├── README.md ├── examples ├── compiled-monte-carlo │ ├── Makefile │ ├── NR-ran2.cpp │ ├── NR-ran2.hpp │ ├── montecarlo.cpp │ └── montecarlo.sla └── new-instructions │ ├── Makefile │ ├── main.cpp │ └── montecarlo.sla ├── lib ├── Makefile ├── NR-ran2.cpp ├── NR-ran2.hpp ├── SlashA.cpp ├── SlashA.hpp └── SlashA_DIS.hpp └── slash ├── Makefile ├── examples ├── add.sla ├── montecarlo.sla ├── montecarlo_nogotos.sla ├── power4.sla └── rand.sla └── main.cpp /README.md: -------------------------------------------------------------------------------- 1 | # Slash/A 2 | 3 | A programming language and C++ library for (quantitative) linear genetic programming. 4 | 5 | Copyright (C) 2004-2011 Artur B Adib. 6 | 7 | Licensed under GNU version 3. 8 | http://www.gnu.org/licenses 9 | 10 | ## Introduction 11 | 12 | Slash/A is programming language and C++ library for quantitative applications of linear genetic programming (GP). Genetic programming is a machine learning method for randomly 'evolving' computer programs until they perform a given desired task [1,2]. Linear means that codes are expressed as a simple string of instructions [1] as opposed to the more complex tree structure originally adopted by GP practitioners [2]. 13 | 14 | When expressed in plain text (as opposed to Bytecode), a Slash/A program consists of a series atomic instructions separated by slashes, like this program that takes two user inputs and adds them: 15 | 16 | input/0/save/input/add/output/. 17 | 18 | Or, more intelligibly, 19 | 20 | input/ # gets an input from user and saves it to register F 21 | 0/ # sets register I = 0 22 | save/ # saves content of F into data vector D[I] (i.e. D[0] := F) 23 | input/ # gets another input, saves to F 24 | add/ # adds to F current data pointed to by I (i.e. D[0] := F) 25 | output/. # outputs result from F 26 | 27 | The instructions are atomic in that they don't need any arguments (unlike some [x86 assembly](http://en.wikipedia.org/wiki/Assembly_language) instructions, for example), so **any random sequence of Slash/A instructions is a semantically correct program**. This is important in genetic programming as it enables the free mutation of any instruction without worrying about its number and types of arguments. Instruction argumentation is effectively done through fixed registers (see below). 28 | 29 | When expressed as Bytecodes, a Slash/A program is represented by a simple C++ vector of unsigned numbers, each of which corresponds to an instruction. A mutation operation is thus a simple replacement of a number in such a vector by another random integer, while a crossing-over operation can be accomplished by simply cutting-and-pasting the appropriate vector segments into another vector. 30 | 31 | Because one can map any instruction from a Turing-complete language (C, Pascal, etc) into a Slash/A code, **Slash/A is Turing-complete**, though it needn't be so (as one can pick and choose which instructions to enable). 32 | 33 | [1] [Genetic Programming: An Introduction](http://www.amazon.com/Genetic-Programming-Introduction-Artificial-Intelligence/dp/155860510X) 34 | [2] [Genetic Programming: On the Programming of Computers by Means of Natural Selection](http://www.amazon.com/Genetic-Programming-Computers-Selection-Adaptive/dp/0262111705) 35 | 36 | ## Features 37 | 38 | - **Optimized for speed.** Slash/A's interpreter introduces a highly optimized Bytecode interpreter that rivals compiled code. With a Monte Carlo example, Slash/A's interpreter runs _only 2x_ slower than an analogous code written in pure optimized C++ code. 39 | - **No function argumentation.** Because the code is expressed as a string of atomic instructions, any randomly generated code is semantically correct. Inspired by [Avida](http://avida.devosoft.org/); 40 | - **Extensible instruction set.** Slash/A comes with a Default Instruction Set (DIS) covering most elementary instructions for flow control, mathematics, etc (see below), but users can introduce any number of custom instructions with simple C++ classes; 41 | 42 | ## Getting started 43 | 44 | Slash/A only needs `g++` and its standard libraries. There are no other dependencies. 45 | 46 | Compile the Slash/A library `libslasha.a` first: 47 | 48 | $ cd lib 49 | $ make 50 | 51 | The source `slash/main.cpp` contains a simple application of the Slash/A library (a command-line interpreter). Take a look at it as a first example on how to use the library (< 100 lines). To compile it: 52 | 53 | $ cd slash 54 | $ make 55 | 56 | To test the interpreter, run one of the examples under `slash/examples`: 57 | 58 | $ cd slash 59 | $ ./slash examples/add.sla 60 | slash -- An interpreter for the Slash/A language 61 | Slash/A Revision 10, Copyright (C) Artur B. Adib 62 | 63 | Enter input #1: 3.14 64 | Enter input #2: 5 65 | Output #1: 8.14 66 | 67 | Total number of operations: 6 68 | Total number of invalid operations: 0 69 | Total number of inputs before an output: 2 70 | 71 | ## Memory resources 72 | 73 | The Slash/A interpreter exposes two registers: one integer, `I`, and one floating-point, `F`. All other data is stored in a floating-point vector `D[i]`. 74 | 75 | An intuitive depiction of the memory resources available to Slash/A programs is sketched below: 76 | 77 | _________________ 78 | D[i]: | | | | | | | ... << Data vector D[i] 79 | |__|__|__|__|__|__| 80 | i : 1 2 3 4 5 6 81 | ^ 82 | | 83 | \--[I=3] << Integer register. Load/save operations would access D[3]. 84 | 85 | [F=3.1415] << Floating-point register 86 | 87 | 88 | ## Current limitations 89 | 90 | - It is not possible to enter floating-point constants directly into the code. It is expected that if a floating-point constant is important to solve the problem at hand, the evolving codes will find their own way to construct them (see examples below). 91 | 92 | ## Default instruction set (DIS) 93 | 94 | _NOTE: For performance reasons, all DIS instructions are implemented inline in `lib/SlashA_DIS.hpp`. (The compiler will inline the code whenever possible, avoiding a round-trip to a new function)._ 95 | 96 | **Numeric instructions** 97 | 98 | * `0-(?)`: sets I := 0,1,2,...,(?), where the maximum number (?) is determined at runtime by the user; 99 | 100 | **Register-Register commands** 101 | 102 | * `itof`: copies I into F (F := I); 103 | * `ftoi`: copies the absolute value of F into I, rounding off to the nearest integer ( I := round(abs(F)) ); 104 | * `inc`: increments register F (F := F + 1); 105 | * `dec`: decrements register F (F := F - 1); 106 | 107 | **Memory-Register commands** 108 | 109 | * `load`: loads data at position I into F (F := D[I]); 110 | * `save`: reverse of the above (D[I] := F); 111 | * `swap`: swaps the contents of F and D[I]; 112 | * `cmp`: returns F := 0 if F == D[I], and F := -1 otherwise. 113 | 114 | **Flow control** 115 | 116 | * `label`: next code position (x+1) gets the label I; 117 | * `gotoifp`: if F >= 0, goes to the code position with the previously defined label I; 118 | * `jumpifn`: if F<0, jumps to the instruction following the corresponding jumphere instruction; 119 | * `jumphere`: see above; 120 | * `loop`: will loop I times the following block (block ends at the next endloop); 121 | * `endloop`: see above. 122 | 123 | **I/O** 124 | 125 | * `input`: sets F to the next float number in the input buffer; 126 | * `output`: writes the content of F to the output buffer; 127 | 128 | **Mathematics** 129 | 130 | * `add`: adds number at data vector position I to F (F := F + D[I]); 131 | * `sub`: F := F - D[I]; 132 | * `mul`: F := F * D[I]; 133 | * `div`: F := F / D[I] - division by zero is protected; 134 | * `abs`: F := |F|; 135 | * `sign`: F := -F; 136 | * `exp`: F := e ^ F; 137 | * `log`: natural logarithm, F := log(F); 138 | * `sin`: F := sin(F); 139 | * `pow`: F := F ^ D[I]; 140 | * `ran`: returns a random number in F between 0 and 1 (F := ran(0,1)); 141 | 142 | **Other** 143 | 144 | * `nop`: no operation; 145 | 146 | **Source-code interpreter** 147 | 148 | _(These 'instructions' are only implemented in the `source2ByteCode()` function; there are no Bytecode instructions corresponding to them)_ 149 | 150 | * `/`: interprets and executes the word before its appearance as an instruction - only way to separate instructions; 151 | * `#`: end-of-line - will cause the interpreter to ignore the rest of the current line (including slashes); 152 | * `.`: end-of-code - will cause the interpreter to stop interpreting the rest of the code. 153 | 154 | ## Error handling 155 | 156 | Every invalid operation is ignored during program execution (e.g. going to an undefined label, reading from an unsaved variable, etc). 157 | 158 | ## Examples 159 | 160 | _Throughout the examples, capital letters such as **X**, **Y**, etc stand for input values._ 161 | 162 | **Arithmetics: X+Y** 163 | 164 | input/0/save/input/add/output/. 165 | 166 | **Power: X^4** 167 | 168 | * by direct exponentiation 169 | 170 | 4/itof/0/save/input/pow/output/. 171 | 172 | * by manual iteration 173 | 174 | input/0/save/mul/mul/mul/output/. 175 | 176 | * by automatic iteration 177 | 178 | input/0/save/1/save/# D[0] = X, D[1] = X 179 | 2/itof/save/# D[2] contains the loop counter (=2) 180 | 0/label/# main loop 181 | 1/load/0/mul/1/save/# F := F * X, D[1] := X^n 182 | 2/load/dec/save/# decreases loop counter 183 | 0/gotoifp/# loops three times 184 | 1/load/output/. 185 | 186 | **Polynomial X^3 + X^2 + X** 187 | 188 | input/0/save/mul/1/save/0/mul/2/save/# D[0] := X, D[1] := X^2, D[2] := X^3 189 | 1/add/0/add/output/. 190 | 191 | **Random number generation (prints 10 random numbers between 0 and 1)** 192 | 193 | 9/itof/0/save/# D[0] is the counter 194 | 0/label/# 195 | ran/output/# 196 | 0/load/dec/save/# 197 | 0/gotoifp/. 198 | 199 | **Area of circle of radius X (illustrates how to construct floating-point constants)** 200 | 201 | * method 1 (PI = 31415 * 10^-4) 202 | 203 | input/0/save/mul/save/# D[0] := X^2 204 | 31415/itof/1/save/# D[1] := 31415 205 | 4/itof/sign/2/save/# D[2] := -4 206 | 10/itof/2/pow/# F := 10^(-4) 207 | 1/mul/# F := 3.1415 208 | 0/mul/output/.# output is 3.1415 * X^2 209 | 210 | * method 2 (PI = 31415 / (100 * 100) ) 211 | 212 | 100/itof/0/save/mul/save/# D[0] := 10^4 213 | 31415/itof/0/div/save/# D[0] := 3.1415 214 | input/1/save/mul/# F := X^2 215 | 0/mul/output/.# output is 3.1415 * X^2 216 | 217 | **Equality test using `jumpifn` (outputs 1 if two inputs are identical, 0 otherwise)** 218 | 219 | input/0/save/# 220 | input/0/sub/abs/sign/save/# D[0] < 0 only if inputs are different 221 | 0/itof/1/save/# default answer is 1 (in D[1]) 222 | 0/load/ 223 | jumpifn/# 224 | 1/itof/1/save/# changes default answer if inputs are identical 225 | jumphere/# 226 | 1/load/output/. 227 | 228 | **Integer/Fractional number classification (outputs 1 if X is integer, 0 otherwise)** 229 | 230 | input/0/save/ftoi/itof/# D[0] := X, F := round(X) 231 | 0/div/save/# D[0] == 1 if X is an integer, D[0] != 1 otherwise 232 | 1/itof/0/sub/abs/sign/0/save/# D[0] == 0 if X is an integer, D[0] > 0 otherwise 233 | 0/itof/1/save/# D[1] := 0 234 | 0/load/# F < 0 if X is fractional 235 | jumpifn/# 236 | 1/itof/save/# D[1] := 1 237 | jumphere/# 238 | 1/load/output/. 239 | 240 | **Integer/Fractional classification with nested jumpifn instructions (outputs 1 if both X and Y are integers, 0 otherwise)** 241 | 242 | input/0/save/input/1/save/# D[0] := X, D[1] := Y 243 | 0/load/ftoi/itof/# F := round(X) 244 | 0/div/save/# D[0] == 1 if X is an integer, D[0] != 1 otherwise 245 | 1/itof/0/sub/abs/sign/0/save/# D[0] == 0 if X is an integer, D[0] < 0 otherwise 246 | 0/itof/2/save/# default answer is 0 (either X or Y is not an integer) 247 | 0/load/# 248 | jumpifn/# will jump if X is not an integer 249 | 1/load/ftoi/itof/# F := round(Y) 250 | 1/div/save/# D[1] == 1 if Y is an integer, D[1] != 1 otherwise 251 | 1/itof/1/sub/abs/sign/1/save/# D[1] == 0 if X is an integer, D[1] < 0 otherwise 252 | jumpifn/# 253 | 1/itof/2/save/# X and Y are integers! 254 | jumphere/# 255 | jumphere/# 256 | 2/load/output/. 257 | 258 | **Positive/Negative classification loop using an If-Then-Else construction (outputs 1 if X is positive, 0 otherwise)** 259 | 260 | 0/label/# 261 | input/0/save/# 262 | jumpifn/# If positive... 263 | 1/itof/output/# ...print 1 264 | jumphere/ 265 | 0/load/sign/# 266 | jumpifn/# Else... 267 | 0/itof/output/# ...print 0 268 | jumphere/# 269 | 0/itof/gotoifp/. infinite loop (use Ctrl+C to abort) 270 | 271 | **Factorial function** 272 | 273 | input/0/save/dec/1/save/0/mul/save/# D[0] := X*(X-1), D[1] := X-1 274 | dec/dec/dec/ 275 | jumpifn/# we're not interested in 2!, 1!, or 0! ... 276 | 0/label/# 277 | 1/load/dec/save/0/mul/save/# D[1] decreases from X to 1, D[0] := X*(X-1)*...*D[1] 278 | 1/load/dec/dec/# makes sure we exit the loop if D[1] == 1 279 | 0/gotoifp/# 280 | 0/load/output/# 281 | jumphere/. 282 | 283 | **Monte Carlo (hit-and-miss) evaluation of the area of a circle of unit radius (=3.1415...)** 284 | 285 | 7/itof/0/save/10/itof/0/pow/save/# D[0] is the number of MC points (here 10^7) 286 | 0/itof/1/save/# D[1] contains the total number of points so far 287 | 0/itof/2/save/# D[2] contains the number of hits inside a quadrant 288 | 0/label/# 289 | ran/3/save/mul/save/# D[3] := x^2 290 | ran/4/save/mul/# F := y^2 291 | 3/add/save/# D[3] := x^2 + y^2 292 | 1/itof/3/sub/# F < 0 if missed 293 | jumpifn/ 294 | 2/load/inc/save/# D[2] := D[2] + 1 only if it's a hit 295 | jumphere/ 296 | 1/load/inc/save/# D[1] := D[1] + 1 always 297 | 0/load/dec/save/# decrease counter 298 | 0/gotoifp/# 299 | 2/load/1/div/save/# D[1] := hits / total 300 | 4/itof/1/mul/# F := 4 * (area of quadrant) 301 | output/. 302 | -------------------------------------------------------------------------------- /examples/compiled-monte-carlo/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Simple Makefile 3 | 4 | CC=g++ 5 | CFLAGS=-O3 -Wall -I../ 6 | LFLAGS=-L../ 7 | LIBS=-lm 8 | DBGFLAGS=-DDEBUG -g 9 | 10 | C_FILES=montecarlo.cpp NR-ran2.cpp 11 | O_FILES=$(C_FILES:.cc=.o) 12 | 13 | all: 14 | $(CC) -c $(CFLAGS) $(C_FILES) 15 | $(CC) $(LFLAGS) $(O_FILES) -o montecarlo $(LIBS) 16 | 17 | debug: 18 | $(CC) -c $(DBGFLAGS) $(C_FILES) 19 | $(CC) $(LFLAGS) $(O_FILES) -o montecarlo $(LIBS) 20 | 21 | clean: 22 | rm -f *.o core a.out *~ montecarlo 23 | 24 | -------------------------------------------------------------------------------- /examples/compiled-monte-carlo/NR-ran2.cpp: -------------------------------------------------------------------------------- 1 | namespace NumericalRecipes 2 | { 3 | 4 | #define IM1 2147483563 5 | #define IM2 2147483399 6 | #define AM (1.0/IM1) 7 | #define IMM1 (IM1-1) 8 | #define IA1 40014 9 | #define IA2 40692 10 | #define IQ1 53668 11 | #define IQ2 52774 12 | #define IR1 12211 13 | #define IR2 3791 14 | #define NTAB 32 15 | #define NDIV (1+IMM1/NTAB) 16 | #define EPS 1.2e-7 17 | #define RNMX (1.0-EPS) 18 | 19 | float ran2(long *idum) 20 | { 21 | int j; 22 | long k; 23 | static long idum2=123456789; 24 | static long iy=0; 25 | static long iv[NTAB]; 26 | float temp; 27 | 28 | if (*idum <= 0) { 29 | if (-(*idum) < 1) *idum=1; 30 | else *idum = -(*idum); 31 | idum2=(*idum); 32 | for (j=NTAB+7;j>=0;j--) { 33 | k=(*idum)/IQ1; 34 | *idum=IA1*(*idum-k*IQ1)-k*IR1; 35 | if (*idum < 0) *idum += IM1; 36 | if (j < NTAB) iv[j] = *idum; 37 | } 38 | iy=iv[0]; 39 | } 40 | k=(*idum)/IQ1; 41 | *idum=IA1*(*idum-k*IQ1)-k*IR1; 42 | if (*idum < 0) *idum += IM1; 43 | k=idum2/IQ2; 44 | idum2=IA2*(idum2-k*IQ2)-k*IR2; 45 | if (idum2 < 0) idum2 += IM2; 46 | j=iy/NDIV; 47 | iy=iv[j]-idum2; 48 | iv[j] = *idum; 49 | if (iy < 1) iy += IMM1; 50 | if ((temp=AM*iy) > RNMX) return RNMX; 51 | else return temp; 52 | } 53 | #undef IM1 54 | #undef IM2 55 | #undef AM 56 | #undef IMM1 57 | #undef IA1 58 | #undef IA2 59 | #undef IQ1 60 | #undef IQ2 61 | #undef IR1 62 | #undef IR2 63 | #undef NTAB 64 | #undef NDIV 65 | #undef EPS 66 | #undef RNMX 67 | /* (C) Copr. 1986-92 Numerical Recipes Software . */ 68 | 69 | } // end of NumericalRecipes 70 | -------------------------------------------------------------------------------- /examples/compiled-monte-carlo/NR-ran2.hpp: -------------------------------------------------------------------------------- 1 | 2 | namespace NumericalRecipes { 3 | float ran2(long *idum); 4 | }; 5 | -------------------------------------------------------------------------------- /examples/compiled-monte-carlo/montecarlo.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // This C++ program implements the Slash/A language as inlines and performs the 3 | // Monte Carlo calculation (montecarlo.sla) in compiled form. This is intended to 4 | // compare interpreted vs. compiled runtimes. 5 | // 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "NR-ran2.hpp" 12 | 13 | using namespace std; 14 | 15 | const unsigned nvar = 32768; 16 | 17 | double D[nvar]; 18 | bool D_saved[nvar]; 19 | double F = 0; 20 | unsigned I = 0; 21 | long rseed = -2237; 22 | 23 | // Necessary instructions from DIS 24 | inline void seti(unsigned arg) { I = arg; }; 25 | inline void itof() { F = (double)I; }; 26 | inline void mul() { if (D_saved[I]) F = F*D[I]; }; 27 | inline void add() { if (D_saved[I]) F = F+D[I]; }; 28 | inline void sub() { if (D_saved[I]) F = F-D[I]; }; 29 | inline void ran() { F = NumericalRecipes::ran2(&rseed); } 30 | inline void inc() { F = F+1; } 31 | inline void dec() { F = F-1; } 32 | inline void pow() { F = pow(F,D[I]); } 33 | inline void div() 34 | { 35 | if (I < nvar) { 36 | if ( D_saved[I] ) { 37 | if ( D[I] == 0. ) { 38 | F = 0; 39 | } 40 | else 41 | F = F/D[I]; 42 | } 43 | } 44 | } 45 | inline void save() 46 | { 47 | if (I=0) goto label0; 85 | seti(2); load(); seti(1); div(); save(); 86 | seti(4); itof(); seti(1); mul(); 87 | output(); 88 | } 89 | -------------------------------------------------------------------------------- /examples/compiled-monte-carlo/montecarlo.sla: -------------------------------------------------------------------------------- 1 | 7/itof/0/save/10/itof/0/pow/save/# D[0] is the number of MC points (here 10^7) 2 | 0/itof/1/save/# D[1] contains the total number of points so far 3 | 0/itof/2/save/# D[2] contains the number of hits inside a quadrant 4 | 0/label/# 5 | ran/3/save/mul/save/# D[3] := x^2 6 | ran/4/save/mul/# F := y^2 7 | 3/add/save/# D[3] := x^2 + y^2 8 | 1/itof/3/sub/# F < 0 if missed 9 | jumpifn/ 10 | 2/load/inc/save/# D[2] := D[2] + 1 only if it's a hit 11 | jumphere/ 12 | 1/load/inc/save/# D[1] := D[1] + 1 always 13 | 0/load/dec/save/# decrease counter 14 | 0/gotoifp/# 15 | 2/load/1/div/save/# D[1] := hits / total 16 | 4/itof/1/mul/# F := 4 * (area of quadrant) 17 | output/. 18 | 19 | -------------------------------------------------------------------------------- /examples/new-instructions/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Simple Makefile 3 | 4 | SLASHPATH=../../lib 5 | 6 | CC=g++ 7 | CFLAGS=-O3 -Wall -I$(SLASHPATH) 8 | LFLAGS=-L$(SLASHPATH) 9 | LIBS=-lm -lslasha 10 | DBGFLAGS=-DDEBUG -g 11 | 12 | C_FILES=main.cpp 13 | O_FILES=$(C_FILES:.cpp=.o) 14 | 15 | all: 16 | $(CC) -c $(CFLAGS) $(C_FILES) 17 | $(CC) $(LFLAGS) $(O_FILES) -o new-slash $(LIBS) 18 | 19 | debug: 20 | $(CC) -c $(DBGFLAGS) $(C_FILES) 21 | $(CC) $(LFLAGS) $(O_FILES) -o new-slash $(LIBS) 22 | 23 | clean: 24 | rm -f *.o core a.out *~ new-slash 25 | 26 | -------------------------------------------------------------------------------- /examples/new-instructions/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * new-slash 4 | * 5 | * A command-line utility illustrating the use of the Slash/A library with user-defined instructions. 6 | * 7 | * Copyright (C) 2004-2011 Artur B Adib 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | */ 23 | 24 | using namespace std; 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "SlashA.hpp" 31 | 32 | inline double p2(double x) { return x*x; } 33 | 34 | // 35 | // Defines new instruction 36 | // 37 | class Dist : public SlashA::Instruction 38 | { 39 | public: 40 | Dist() : Instruction() { name="DIST"; } 41 | ~Dist() {} 42 | inline void code(SlashA::MemCore& core, SlashA::InstructionSet& iset) 43 | { 44 | core.setF( sqrt(p2(core.getF()) + p2(core.D[core.I])) ); // returns the distance from the origin; with x=F, y=D[I]. 45 | } 46 | }; 47 | 48 | 49 | int main(int argc, char** argv) 50 | { 51 | cout << SlashA::getHeader() << endl << endl; 52 | 53 | if (argc<2) { 54 | cout << "Usage:\n"; 55 | cout << " slash \n\n"; 56 | exit(1); 57 | } 58 | 59 | string source = ""; 60 | ifstream f(argv[1]); 61 | 62 | if (!f) { 63 | cout << "Cannot open file " << argv[1] << ".\n\n"; 64 | exit(1); 65 | } 66 | 67 | while (!f.eof()) 68 | source += f.get(); 69 | 70 | f.close(); 71 | 72 | try 73 | { 74 | vector input, output; 75 | SlashA::ByteCode bc; 76 | SlashA::InstructionSet iset(32768); // initializes the Default Instruction Set with 32768 numeric instructions 77 | SlashA::MemCore memcore(10, // length of the data tape D[] 78 | 10, // length of the label tape L[] 79 | input, // input buffer (will use keyboard if empty) 80 | output); // output buffer 81 | 82 | iset.insert_DIS_full(); 83 | Dist* dist = new Dist(); // allocates the new instruction 84 | iset.insert(dist); // inserts the new instruction in the instruction set iset 85 | 86 | source2ByteCode(source, bc, iset); // translates "source" into "bc" using the instruction set "iset" 87 | 88 | bool failed = SlashA::runByteCode(iset, // instruction set pointer 89 | memcore, // memory core pointer 90 | bc, // ByteCoded program to be run (pointer) 91 | 2237, // random seed for random number instructions 92 | 0, // max run time in seconds (0 for no limit) 93 | -1); // loop depth limit 94 | 95 | if (failed) 96 | cout << "Program failed (time-out, max loop depth, etc)!" << endl; 97 | 98 | cout << endl; 99 | cout << "Total number of invalid operations: " << iset.getTotalInvops() << endl; 100 | } 101 | catch(string& s) 102 | { 103 | cout << s << endl << endl; 104 | exit(1); 105 | } 106 | 107 | cout << endl; 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /examples/new-instructions/montecarlo.sla: -------------------------------------------------------------------------------- 1 | 7/itof/0/save/10/itof/0/pow/save/# D[0] is the number of MC points (here 10^7) 2 | 0/itof/1/save/# D[1] contains the total number of points so far 3 | 0/itof/2/save/# D[2] contains the number of hits inside a quadrant 4 | 0/label/# 5 | ran/3/save/# D[3] := x 6 | ran/4/save/# F := y 7 | 3/DIST/save/# D[3] := sqrt(x^2 + y^2) <---- this line contains the call to the new instruction 8 | 1/itof/3/sub/# F < 0 if missed 9 | jumpifn/ 10 | 2/load/inc/save/# D[2] := D[2] + 1 only if it's a hit 11 | jumphere/ 12 | 1/load/inc/save/# D[1] := D[1] + 1 always 13 | 0/load/dec/save/# decrease counter 14 | 0/gotoifp/# 15 | 2/load/1/div/save/# D[1] := hits / total 16 | 4/itof/1/mul/# F := 4 * (area of quadrant) 17 | output/. 18 | 19 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Simple Makefile 3 | 4 | CC=g++ 5 | CFLAGS=-O3 -Wall 6 | LIBOUTPUT=libslasha.a 7 | DBGFLAGS=-DDEBUG -g 8 | 9 | C_FILES=SlashA.cpp NR-ran2.cpp 10 | O_FILES=$(C_FILES:.cpp=.o) 11 | 12 | all: 13 | $(CC) -c $(CFLAGS) $(C_FILES) 14 | ar rc $(LIBOUTPUT) $(O_FILES) 15 | ranlib $(LIBOUTPUT) 16 | 17 | debug: 18 | $(CC) -c $(DBGFLAGS) $(C_FILES) 19 | ar rc $(LIBOUTPUT) $(O_FILES) 20 | ranlib $(LIBOUTPUT) 21 | 22 | clean: 23 | rm -f *.a *.o core a.out *~ 24 | -------------------------------------------------------------------------------- /lib/NR-ran2.cpp: -------------------------------------------------------------------------------- 1 | namespace NumericalRecipes 2 | { 3 | 4 | #define IM1 2147483563 5 | #define IM2 2147483399 6 | #define AM (1.0/IM1) 7 | #define IMM1 (IM1-1) 8 | #define IA1 40014 9 | #define IA2 40692 10 | #define IQ1 53668 11 | #define IQ2 52774 12 | #define IR1 12211 13 | #define IR2 3791 14 | #define NTAB 32 15 | #define NDIV (1+IMM1/NTAB) 16 | #define EPS 1.2e-7 17 | #define RNMX (1.0-EPS) 18 | 19 | float ran2(long *idum) 20 | { 21 | int j; 22 | long k; 23 | static long idum2=123456789; 24 | static long iy=0; 25 | static long iv[NTAB]; 26 | float temp; 27 | 28 | if (*idum <= 0) { 29 | if (-(*idum) < 1) *idum=1; 30 | else *idum = -(*idum); 31 | idum2=(*idum); 32 | for (j=NTAB+7;j>=0;j--) { 33 | k=(*idum)/IQ1; 34 | *idum=IA1*(*idum-k*IQ1)-k*IR1; 35 | if (*idum < 0) *idum += IM1; 36 | if (j < NTAB) iv[j] = *idum; 37 | } 38 | iy=iv[0]; 39 | } 40 | k=(*idum)/IQ1; 41 | *idum=IA1*(*idum-k*IQ1)-k*IR1; 42 | if (*idum < 0) *idum += IM1; 43 | k=idum2/IQ2; 44 | idum2=IA2*(idum2-k*IQ2)-k*IR2; 45 | if (idum2 < 0) idum2 += IM2; 46 | j=iy/NDIV; 47 | iy=iv[j]-idum2; 48 | iv[j] = *idum; 49 | if (iy < 1) iy += IMM1; 50 | if ((temp=AM*iy) > RNMX) return RNMX; 51 | else return temp; 52 | } 53 | #undef IM1 54 | #undef IM2 55 | #undef AM 56 | #undef IMM1 57 | #undef IA1 58 | #undef IA2 59 | #undef IQ1 60 | #undef IQ2 61 | #undef IR1 62 | #undef IR2 63 | #undef NTAB 64 | #undef NDIV 65 | #undef EPS 66 | #undef RNMX 67 | /* (C) Copr. 1986-92 Numerical Recipes Software . */ 68 | 69 | } // end of NumericalRecipes 70 | -------------------------------------------------------------------------------- /lib/NR-ran2.hpp: -------------------------------------------------------------------------------- 1 | 2 | namespace NumericalRecipes { 3 | float ran2(long *idum); 4 | }; 5 | -------------------------------------------------------------------------------- /lib/SlashA.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * SlashA.cpp 4 | * 5 | * Copyright (C) 2004-2011 Artur B Adib 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include // contains the alarm() function used for timing-out the interpreter 26 | #include // contains the signal() function to handle the alarm 27 | #include "SlashA.hpp" 28 | #include "SlashA_DIS.hpp" 29 | 30 | using namespace std; 31 | 32 | namespace SlashA 33 | { 34 | 35 | 36 | /* 37 | * 38 | * Global variables and functions 39 | * 40 | */ 41 | 42 | volatile bool timedout; // volatile tells the compiler the variable might be changed from outside the normal program flow 43 | void alarm_handler(int sign) { timedout=true; }; // alarm for the runByteCode() function 44 | 45 | 46 | /* 47 | * 48 | * Class methods 49 | * 50 | */ 51 | 52 | // 53 | // Class: InstructionSet 54 | // 55 | 56 | 57 | void InstructionSet::insert_DIS_IO() 58 | { 59 | Instruction* setiptr; 60 | // input/output 61 | setiptr = new DIS::Input(); insert(setiptr); 62 | setiptr = new DIS::Output(); insert(setiptr); 63 | } 64 | 65 | void InstructionSet::insert_DIS_memreg() 66 | { 67 | Instruction* setiptr; 68 | 69 | // memory-register commands 70 | setiptr = new DIS::Load(); insert(setiptr); 71 | setiptr = new DIS::Save(); insert(setiptr); 72 | setiptr = new DIS::Swap(); insert(setiptr); 73 | setiptr = new DIS::Cmp(); insert(setiptr); 74 | }; 75 | 76 | void InstructionSet::insert_DIS_regreg() 77 | { 78 | Instruction* setiptr; 79 | // register-register commands 80 | setiptr = new DIS::Inc(); insert(setiptr); 81 | setiptr = new DIS::Dec(); insert(setiptr); 82 | setiptr = new DIS::ItoF(); insert(setiptr); 83 | setiptr = new DIS::FtoI(); insert(setiptr); 84 | } 85 | 86 | void InstructionSet::insert_DIS_gotos() 87 | { 88 | Instruction* setiptr; 89 | // flow control: gotos 90 | setiptr = new DIS::Label(); insert(setiptr); 91 | setiptr = new DIS::GotoIfP(); insert(setiptr); 92 | } 93 | 94 | void InstructionSet::insert_DIS_jumps() 95 | { 96 | Instruction* setiptr; 97 | // flow control: jumps 98 | setiptr = new DIS::JumpIfN(); insert(setiptr); 99 | setiptr = new DIS::JumpHere(); insert(setiptr); 100 | } 101 | 102 | void InstructionSet::insert_DIS_loops() 103 | { 104 | Instruction* setiptr; 105 | // flow control: loops 106 | setiptr = new DIS::Loop(); insert(setiptr); 107 | setiptr = new DIS::EndLoop(); insert(setiptr); 108 | } 109 | 110 | void InstructionSet::insert_DIS_basicmath() 111 | { 112 | Instruction* setiptr; 113 | // basic math 114 | setiptr = new DIS::Add(); insert(setiptr); 115 | setiptr = new DIS::Sub(); insert(setiptr); 116 | setiptr = new DIS::Mul(); insert(setiptr); 117 | setiptr = new DIS::Div(); insert(setiptr); 118 | } 119 | 120 | void InstructionSet::insert_DIS_advmath() 121 | { 122 | Instruction* setiptr; 123 | // advanced math 124 | setiptr = new DIS::Abs(); insert(setiptr); 125 | setiptr = new DIS::Sign(); insert(setiptr); 126 | setiptr = new DIS::Exp(); insert(setiptr); 127 | setiptr = new DIS::Log(); insert(setiptr); 128 | setiptr = new DIS::Sin(); insert(setiptr); 129 | setiptr = new DIS::Pow(); insert(setiptr); 130 | setiptr = new DIS::Ran(); insert(setiptr); 131 | } 132 | 133 | void InstructionSet::insert_DIS_misc() 134 | { 135 | Instruction* setiptr; 136 | // misc 137 | setiptr = new DIS::Nop(); insert(setiptr); 138 | } 139 | 140 | void InstructionSet::insert_DIS_numeric(ByteCode_Type n_num) 141 | { 142 | Instruction* setiptr; 143 | 144 | for (ByteCode_Type i=0;iisDIS()) // only deallocates memory if it's a DIS instruction (users must deallocate their own instructions!!) 179 | delete set[i]; 180 | } 181 | 182 | // 183 | // Class: MemCore 184 | // 185 | 186 | // Constructor 187 | MemCore::MemCore(const unsigned _Dsize, 188 | const unsigned _Lsize, 189 | vector& _input, 190 | vector& _output) 191 | { 192 | D_size = _Dsize; 193 | L_size = _Lsize; 194 | input = &_input; 195 | output = &_output; 196 | output_executed = false; 197 | 198 | F = I = c = 0; 199 | D = new double[D_size]; 200 | D_saved = new bool[D_size]; 201 | L = new unsigned[L_size]; 202 | L_saved = new bool[L_size]; 203 | ran_ptr = new long; 204 | 205 | for (unsigned i=0;i maxWordLen) 298 | throw (string)"Instruction word is too large: " + instr; 299 | } // ifs 300 | 301 | } // for 302 | 303 | } // Source2ByteCode() 304 | 305 | 306 | void bytecode2Source( ByteCode& bc, 307 | string& src, 308 | InstructionSet& iset ) 309 | { 310 | src.clear(); 311 | for (unsigned i=0;i. 19 | * 20 | */ 21 | 22 | #ifndef SLASHA_INCLUDED // duplicate protection 23 | #define SLASHA_INCLUDED 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace SlashA 30 | { 31 | 32 | const std::string revNumber = "10"; 33 | 34 | const unsigned maxWordLen = 32; // Maximum length of each instruction word 35 | 36 | typedef unsigned ByteCode_Type; 37 | typedef std::vector ByteCode; 38 | 39 | /* Classes */ 40 | 41 | class MemCore 42 | { 43 | private: 44 | double F; // F-register 45 | public: 46 | unsigned I; // I-register 47 | ByteCode* C; // program tape 48 | unsigned c; // program tape position 49 | double* D; // data tape 50 | unsigned D_size; 51 | bool* D_saved; // saved/unsaved flag for each Data element 52 | 53 | unsigned* L; // label tape 54 | unsigned L_size; 55 | bool* L_saved; // analogous to D_saved 56 | 57 | std::vector L_table_addr; // Loop-table containing the addresses of the corresponding EndLoop instructions 58 | std::vector L_table_count; // Loop-table containing the loop counters 59 | 60 | std::vector* input; // input buffer 61 | std::vector* output; // output buffer 62 | bool output_executed; // a flag that tells if any output instruction has been executed so far 63 | 64 | long* ran_ptr; 65 | 66 | // Methods: 67 | MemCore(const unsigned _Dsize, 68 | const unsigned _Lsize, 69 | std::vector& _input, 70 | std::vector& _output); 71 | ~MemCore(); 72 | 73 | inline double getF() { return F; } 74 | inline bool setF(double f) // protects F against assignment of invalid values 75 | { 76 | if (std::isnan(f) || std::isinf(f)) 77 | return false; 78 | else 79 | F = f; 80 | return true; 81 | } 82 | }; 83 | 84 | class InstructionSet; // prototype 85 | 86 | class Instruction 87 | { 88 | protected: 89 | std::string name; // to be defined in the derived classes 90 | bool DIS_flag; // is this a DIS instruction? (Default Instruction Set) 91 | unsigned n_ops; // number of operations executed with this instruction 92 | unsigned n_invops; // number of invalid operations executed with the given instruction 93 | unsigned n_inputs; // number of executed input instructions 94 | unsigned n_outputs; // number of executed output instructions 95 | unsigned n_inputs_bf_output; // number of executed input instructions before the first output instruction 96 | public: 97 | Instruction() { clearCounters(); DIS_flag = false; } 98 | virtual ~Instruction() {} 99 | 100 | virtual inline void code(MemCore& core, InstructionSet& iset) { throw (std::string)"Instruction not properly initialized! (method code() undefined)"; } // to be defined in the derived class (i.e. specific instruction) 101 | 102 | bool isDIS() { return DIS_flag; } 103 | std::string getName() { return name; } 104 | unsigned getOps() { return n_ops; } 105 | unsigned getInvops() { return n_invops; } 106 | unsigned getInputs() { return n_inputs; } 107 | unsigned getOutputs() { return n_outputs; } 108 | unsigned getInputsBeforeOutput() { return n_inputs_bf_output; } 109 | void clearCounters() { n_ops=0; n_invops=0; n_inputs=0; n_outputs=0; n_inputs_bf_output=0; } 110 | virtual void clear() {}; 111 | void clearAll() { clearCounters(); clear(); } 112 | }; 113 | 114 | class InstructionSet 115 | { 116 | private: 117 | std::vector set; 118 | void insert_DIS_numeric(ByteCode_Type n_num); 119 | void remove_DIS(); 120 | unsigned n_numericinst; 121 | int maxloopdepth; 122 | public: 123 | InstructionSet(ByteCode_Type n_num) { maxloopdepth=-1; n_numericinst=n_num; insert_DIS_numeric(n_num); } 124 | ~InstructionSet() { remove_DIS(); } 125 | 126 | void insert_DIS_IO(); // input/output commands 127 | void insert_DIS_memreg(); // memory-register commands 128 | void insert_DIS_regreg(); // register-register commands 129 | void insert_DIS_gotos(); // flow-control: goto commands 130 | void insert_DIS_jumps(); // flow-control: jump commands 131 | void insert_DIS_loops(); // flow-control: loop commands 132 | void insert_DIS_basicmath(); // basic math operations 133 | void insert_DIS_advmath(); // advanced math functions 134 | void insert_DIS_misc(); // everything else 135 | void insert_DIS_full(); // inserts all of the above (with the exception of _DIS_numeric) 136 | void insert_DIS_full_minus_Gotos(); // avoids infinite loops 137 | 138 | void insert(Instruction* inst) { set.push_back(inst); } // inserts a user-defined instruction 139 | void exec(unsigned inst_num, MemCore& core) { set[inst_num]->code(core, (*this)); } 140 | 141 | std::string listAll() { std::string s = ""; for (unsigned i=0;igetName()+'/'; return s + '.'; } 142 | std::string getName(int inst_num) { return set[inst_num]->getName(); } 143 | unsigned getOps(int inst_num) { return set[inst_num]->getOps(); } 144 | unsigned getInvops(int inst_num) { return set[inst_num]->getInvops(); } 145 | unsigned getTotalOps() 146 | { unsigned n=0; for (unsigned i=0;igetOps(); return n; }; 147 | unsigned getTotalInvops() 148 | { unsigned n=0; for (unsigned i=0;igetInvops(); return n; }; 149 | unsigned getTotalInputs() 150 | { unsigned n=0; for (unsigned i=0;igetInputs(); return n; }; 151 | unsigned getTotalOutputs() 152 | { unsigned n=0; for (unsigned i=0;igetOutputs(); return n; }; 153 | unsigned getTotalInputsBFOutput() 154 | { unsigned n=0; for (unsigned i=0;igetInputsBeforeOutput(); return n; }; 155 | void clear() { for (unsigned i=0;iclearAll(); } 156 | unsigned size() { return (ByteCode_Type)set.size(); } 157 | unsigned numericInstructions() { return n_numericinst; } 158 | int getMaxLoopDepth() { return maxloopdepth; } 159 | void setMaxLoopDepth(unsigned ldepth) { maxloopdepth=ldepth; } 160 | }; 161 | 162 | /* Functions */ 163 | 164 | std::string getHeader(); 165 | 166 | bool runByteCode(InstructionSet& iset, 167 | MemCore& core, 168 | ByteCode& bc, 169 | long randseed, 170 | long max_rtime, 171 | int max_loop_depth); 172 | 173 | ByteCode_Type instruction2ByteCode( std::string inst, 174 | InstructionSet& iset ); 175 | 176 | void source2ByteCode( std::string src, 177 | ByteCode& bc, 178 | InstructionSet& iset ); 179 | 180 | void bytecode2Source( ByteCode& bc, 181 | std::string& src, 182 | InstructionSet& iset ); 183 | 184 | }; // namespace SlashA 185 | 186 | #endif // SLASHA_INCLUDED 187 | -------------------------------------------------------------------------------- /lib/SlashA_DIS.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Slash/A's Default Instruction Set (DIS) 4 | * 5 | * Copyright (C) 2004-2011 Artur B Adib 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include "NR-ran2.hpp" 25 | 26 | namespace SlashA 27 | { 28 | 29 | namespace DIS 30 | { 31 | 32 | class SetI : public Instruction 33 | { 34 | private: 35 | ByteCode_Type num; 36 | public: 37 | SetI(ByteCode_Type n) : Instruction() 38 | { 39 | std::ostringstream nstr; 40 | nstr << n; 41 | name=nstr.str(); 42 | DIS_flag = true; 43 | num = n; 44 | }; 45 | ~SetI() {}; 46 | inline void code(MemCore& core, InstructionSet& iset) { core.I = num; n_ops++; } 47 | }; 48 | 49 | class ItoF : public Instruction 50 | { 51 | public: 52 | ItoF() : Instruction() { name="itof"; DIS_flag = true; }; 53 | ~ItoF() {}; 54 | inline void code(MemCore& core, InstructionSet& iset) 55 | { 56 | n_ops++; 57 | if (!core.setF((double)core.I)) 58 | n_invops++; 59 | } 60 | }; 61 | 62 | class FtoI : public Instruction 63 | { 64 | public: 65 | FtoI() : Instruction() { name="ftoi"; DIS_flag = true; }; 66 | ~FtoI() {}; 67 | inline void code(MemCore& core, InstructionSet& iset) 68 | { 69 | n_ops++; 70 | core.I = (unsigned)rint(core.getF()); 71 | } 72 | }; 73 | 74 | class Inc : public Instruction 75 | { 76 | public: 77 | Inc() : Instruction() { name="inc"; DIS_flag = true; }; 78 | ~Inc() {}; 79 | inline void code(MemCore& core, InstructionSet& iset) 80 | { 81 | n_ops++; 82 | if ( !core.setF(core.getF()+1.0) ) 83 | n_invops++; 84 | } 85 | }; 86 | 87 | class Dec : public Instruction 88 | { 89 | public: 90 | Dec() : Instruction() { name="dec"; DIS_flag = true; }; 91 | ~Dec() {}; 92 | inline void code(MemCore& core, InstructionSet& iset) 93 | { 94 | n_ops++; 95 | if ( !core.setF(core.getF()-1.0) ) 96 | n_invops++; 97 | } 98 | }; 99 | 100 | class Cmp : public Instruction 101 | { 102 | public: 103 | Cmp() : Instruction() { name="cmp"; DIS_flag = true; }; 104 | ~Cmp() {}; 105 | inline void code(MemCore& core, InstructionSet& iset) 106 | { 107 | double retvalue=0; 108 | 109 | n_ops++; 110 | if (core.I=0) 213 | core.c=core.L[core.I]; 214 | } 215 | else 216 | n_invops++; 217 | } 218 | else 219 | n_invops++; 220 | } 221 | }; 222 | 223 | class JumpIfN : public Instruction 224 | { 225 | private: 226 | bool never_called; 227 | std::vector J_table; // jump-table 228 | 229 | /* 230 | * The present implementation of JumpIfN uses a "jump table". In Revision 1, upon every call to jumpifn 231 | * (if indeed F<0) the instruction implementation would search for the corresponding jumphere. This is 232 | * a very time-consuming task, especially when such jumps appear within long loops. 233 | * 234 | * With a jump-table formalism, we perform a one-time search for all the jumpifn's and their corresponding 235 | * jumphere's, feeding that information to the jump-table. Later calls to jumpifn simply jump to the 236 | * corresponding point stored at the table, without any additional searches. 237 | * 238 | * Because JumpHere are dummy instructions, all of the implementation of JumpsIfN can be confined to here. 239 | * 240 | */ 241 | 242 | inline void build_J_table(MemCore& core, InstructionSet& iset) 243 | { 244 | const unsigned C_size=(*core.C).size(); 245 | unsigned curr_c=0; // starting from c=0 IS important! 246 | // other flow control instructions, including another jumpifn, might cause the first 247 | // occurrence of jumpifn in the code to be bypassed 248 | unsigned searching_c; 249 | unsigned n_openjumps; // n_openjumps counts the number of jumps without a corresponding jumphere 250 | 251 | J_table.clear(); 252 | 253 | for (unsigned i=0;i0) && (searching_c0) 270 | J_table[curr_c]=0; // could not find a corresponding jumphere! 271 | else 272 | J_table[curr_c]=searching_c-1; // points to the jumphere instruction (interpreter will execute next one) 273 | } // if name is jumpifn 274 | curr_c++; 275 | } 276 | never_called=false; 277 | }; 278 | 279 | public: 280 | void clear() { never_called = true; } 281 | JumpIfN() : Instruction() { name="jumpifn"; DIS_flag=true; clear(); }; 282 | ~JumpIfN() {}; 283 | inline void code(MemCore& core, InstructionSet& iset) 284 | { 285 | n_ops++; 286 | if (core.getF()<0) 287 | { 288 | if (never_called) 289 | build_J_table(core, iset); // builds the jump-table on first call 290 | 291 | if (J_table[core.c]) // only jumps if a corresponding jumphere exists 292 | core.c = J_table[core.c]; 293 | else 294 | n_invops++; 295 | } 296 | } 297 | }; 298 | 299 | class JumpHere : public Instruction 300 | { 301 | public: 302 | JumpHere() : Instruction() { name="jumphere"; DIS_flag = true; }; 303 | ~JumpHere() {}; 304 | inline void code(MemCore& core, InstructionSet& iset) { n_ops++; } 305 | }; 306 | 307 | class Loop : public Instruction 308 | { 309 | inline void build_L_table(MemCore& core, InstructionSet& iset) 310 | { 311 | const unsigned C_size=(*core.C).size(); 312 | unsigned curr_c=0; 313 | unsigned searching_c; 314 | unsigned n_openloops; // counts the number of repeats without a corresponding endloop 315 | int depth, max_depth=0; 316 | 317 | core.L_table_addr.clear(); 318 | core.L_table_count.clear(); 319 | 320 | for (unsigned i=0;i0) && (searching_cmax_depth) 343 | max_depth=depth; 344 | core.L_table_addr[curr_c]=searching_c-1; 345 | core.L_table_addr[searching_c-1]=curr_c; // points to the occurrence of "loop" 346 | } 347 | } 348 | curr_c++; 349 | } // end while 350 | 351 | if (iset.getMaxLoopDepth()>=0) 352 | if (max_depth>iset.getMaxLoopDepth()) 353 | throw 0; // program has depth greater than allowed 354 | }; 355 | 356 | public: 357 | Loop() : Instruction() { name="loop"; DIS_flag=true; }; 358 | ~Loop() {}; 359 | inline void code(MemCore& core, InstructionSet& iset) 360 | { 361 | n_ops++; 362 | if (core.L_table_addr.size()==0) 363 | build_L_table(core, iset); // builds the loop-table on first call 364 | 365 | if (core.L_table_addr[core.c]) // does this loop instruction have a corresponding endloop? 366 | { 367 | if (core.I==0) // were we asked to jump the loop block? 368 | core.c=core.L_table_addr[core.c]; // points c to the corresponding endloop address (interpreter will execute the following instruction) 369 | else 370 | core.L_table_count[core.c]=core.I; // we're in a loop -- set the loop counter to core.I 371 | } 372 | else 373 | n_invops++; // could not find endloop for this loop! 374 | } 375 | }; 376 | 377 | class EndLoop : public Instruction 378 | { 379 | public: 380 | EndLoop() : Instruction() { name="endloop"; DIS_flag = true; }; 381 | ~EndLoop() {}; 382 | inline void code(MemCore& core, InstructionSet& iset) 383 | { 384 | n_ops++; 385 | if (core.L_table_addr.size()>0) // does L_table_* exist? 386 | { 387 | if (core.L_table_addr[core.c]) // does this EndLoop have a corresponding Loop? 388 | { 389 | const unsigned loop_addr = core.L_table_addr[core.c]; 390 | if (core.L_table_count[loop_addr]>1) // do we have any more loops left? 391 | { 392 | core.c = loop_addr; // points c to the corresponding "loop" 393 | core.L_table_count[loop_addr] -= 1; // decreases the loop counter 394 | } 395 | } 396 | else 397 | n_invops++; 398 | } 399 | else 400 | n_invops++; 401 | } 402 | }; 403 | 404 | class Input : public Instruction 405 | { 406 | public: 407 | Input() : Instruction() { name="input"; DIS_flag = true; }; 408 | ~Input() {}; 409 | inline void code(MemCore& core, InstructionSet& iset) { 410 | n_ops++; 411 | if ( (*core.input).size() == 0 ) 412 | { 413 | double finput; 414 | std::cout << "Enter input #" << n_inputs+1 << ": "; 415 | std::cin >> finput; 416 | core.setF(finput); 417 | } 418 | else 419 | { 420 | if ( n_inputs < (*core.input).size() ) 421 | core.setF( (*core.input)[n_inputs] ); 422 | } 423 | 424 | n_inputs++; 425 | if (!core.output_executed) 426 | n_inputs_bf_output++; 427 | } 428 | }; 429 | 430 | class Output : public Instruction 431 | { 432 | public: 433 | Output() : Instruction() { name="output"; DIS_flag = true; }; 434 | ~Output() {}; 435 | inline void code(MemCore& core, InstructionSet& iset) { 436 | n_ops++; 437 | if ( (*core.input).size() == 0 ) 438 | std::cout << "Output #" << n_outputs+1 << ": " << core.getF() << std::endl; 439 | else 440 | (*core.output).push_back(core.getF()); 441 | 442 | n_outputs++; 443 | core.output_executed=true; 444 | } 445 | }; 446 | 447 | class Abs : public Instruction 448 | { 449 | public: 450 | Abs() : Instruction() { name="abs"; DIS_flag = true; }; 451 | ~Abs() {}; 452 | inline void code(MemCore& core, InstructionSet& iset) { core.setF( fabs(core.getF()) ); n_ops++; } 453 | }; 454 | 455 | class Sign : public Instruction 456 | { 457 | public: 458 | Sign() : Instruction() { name="sign"; DIS_flag = true; }; 459 | ~Sign() {}; 460 | inline void code(MemCore& core, InstructionSet& iset) { core.setF( -core.getF() ); n_ops++; } 461 | }; 462 | 463 | class Exp : public Instruction 464 | { 465 | public: 466 | Exp() : Instruction() { name="exp"; DIS_flag = true; }; 467 | ~Exp() {}; 468 | inline void code(MemCore& core, InstructionSet& iset) 469 | { 470 | n_ops++; 471 | core.setF( exp(core.getF()) ); 472 | } 473 | }; 474 | 475 | class Log : public Instruction 476 | { 477 | public: 478 | Log() : Instruction() { name="log"; DIS_flag = true; }; 479 | ~Log() {}; 480 | inline void code(MemCore& core, InstructionSet& iset) 481 | { 482 | n_ops++; 483 | if ( !core.setF( log(core.getF()) ) ) 484 | n_invops++; 485 | } 486 | }; 487 | 488 | class Sin : public Instruction 489 | { 490 | public: 491 | Sin() : Instruction() { name="sin"; DIS_flag = true; }; 492 | ~Sin() {}; 493 | inline void code(MemCore& core, InstructionSet& iset) 494 | { 495 | if ( !core.setF(sin(core.getF())) ) 496 | n_ops++; 497 | } 498 | }; 499 | 500 | class Add : public Instruction 501 | { 502 | public: 503 | Add() : Instruction() { name="add"; DIS_flag = true; }; 504 | ~Add() {}; 505 | inline void code(MemCore& core, InstructionSet& iset) 506 | { 507 | n_ops++; 508 | if (core.I < core.D_size) 509 | { 510 | if ( core.D_saved[core.I] ) 511 | { 512 | if ( !core.setF( core.getF()+core.D[core.I] ) ) 513 | n_invops++; 514 | } 515 | else 516 | n_invops++; // variable D[core.I] hasn't been saved 517 | } 518 | else 519 | n_invops++; // variable D[core.I] is out of range 520 | } 521 | }; 522 | 523 | class Sub : public Instruction 524 | { 525 | public: 526 | Sub() : Instruction() { name="sub"; DIS_flag = true; }; 527 | ~Sub() {}; 528 | inline void code(MemCore& core, InstructionSet& iset) { 529 | n_ops++; 530 | if (core.I < core.D_size) { 531 | if ( core.D_saved[core.I] ) 532 | { 533 | if ( !core.setF( core.getF()-core.D[core.I] ) ) 534 | n_invops++; 535 | } 536 | else 537 | n_invops++; // variable D[core.I] hasn't been saved 538 | } 539 | else 540 | n_invops++; // variable D[core.I] is out of range 541 | } 542 | }; 543 | 544 | class Mul : public Instruction 545 | { 546 | public: 547 | Mul() : Instruction() { name="mul"; DIS_flag = true; }; 548 | ~Mul() {}; 549 | inline void code(MemCore& core, InstructionSet& iset) { 550 | n_ops++; 551 | if (core.I < core.D_size) { 552 | if ( core.D_saved[core.I] ) 553 | { 554 | if ( !core.setF( core.getF()*core.D[core.I] ) ) 555 | n_invops++; 556 | } 557 | else 558 | n_invops++; // variable D[core.I] hasn't been saved 559 | } 560 | else 561 | n_invops++; // variable D[core.I] is out of range 562 | } 563 | }; 564 | 565 | class Div : public Instruction 566 | { 567 | public: 568 | Div() : Instruction() { name="div"; DIS_flag = true; }; 569 | ~Div() {}; 570 | inline void code(MemCore& core, InstructionSet& iset) { 571 | n_ops++; 572 | if (core.I < core.D_size) { 573 | if ( core.D_saved[core.I] ) 574 | { 575 | if ( !core.setF( core.getF()/core.D[core.I] ) ) 576 | n_invops++; 577 | } 578 | else 579 | n_invops++; // variable D[core.I] hasn't been saved 580 | } 581 | else 582 | n_invops++; // variable D[core.I] is out of range 583 | } 584 | }; 585 | 586 | class Pow : public Instruction 587 | { 588 | public: 589 | Pow() : Instruction() { name="pow"; DIS_flag = true; }; 590 | ~Pow() {}; 591 | inline void code(MemCore& core, InstructionSet& iset) { 592 | n_ops++; 593 | if (core.I < core.D_size) { 594 | if ( core.D_saved[core.I] ) 595 | { 596 | if ( !core.setF(pow(core.getF(),core.D[core.I])) ) 597 | n_invops++; 598 | } 599 | else 600 | n_invops++; // variable D[core.I] hasn't been saved 601 | } 602 | else 603 | n_invops++; // variable D[core.I] is out of range 604 | } 605 | }; 606 | 607 | class Ran : public Instruction 608 | { 609 | public: 610 | Ran() : Instruction() { name="ran"; DIS_flag = true; }; 611 | ~Ran() {}; 612 | inline void code(MemCore& core, InstructionSet& iset) 613 | { 614 | if ( !core.setF( NumericalRecipes::ran2(core.ran_ptr) ) ) 615 | n_invops++; 616 | n_ops++; 617 | } 618 | }; 619 | 620 | class Nop : public Instruction 621 | { 622 | public: 623 | Nop() : Instruction() { name="nop"; DIS_flag = true; }; 624 | ~Nop() {}; 625 | inline void code(MemCore& core, InstructionSet& iset) { n_ops++; } 626 | }; 627 | 628 | } // namespace DIS 629 | 630 | } // namespace SlashA 631 | 632 | -------------------------------------------------------------------------------- /slash/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Simple Makefile 3 | 4 | SLASHPATH=../lib 5 | 6 | CC=g++ 7 | CFLAGS=-O3 -Wall -I$(SLASHPATH) 8 | LFLAGS=-L$(SLASHPATH) 9 | LIBS=-lm -lslasha 10 | DBGFLAGS=-DDEBUG -g 11 | 12 | C_FILES=main.cpp 13 | O_FILES=$(C_FILES:.cpp=.o) 14 | 15 | all: 16 | $(CC) -c $(CFLAGS) $(C_FILES) 17 | $(CC) $(LFLAGS) $(O_FILES) -o slash $(LIBS) 18 | 19 | debug: 20 | $(CC) -c $(DBGFLAGS) $(C_FILES) 21 | $(CC) $(LFLAGS) $(O_FILES) -o slash $(LIBS) 22 | 23 | clean: 24 | rm -f *.o core a.out *~ slash 25 | 26 | -------------------------------------------------------------------------------- /slash/examples/add.sla: -------------------------------------------------------------------------------- 1 | # hello world (adds two numbers) 2 | 3 | input/ # gets an input from user and saves it to register F 4 | 0/ # sets register I = 0 5 | save/ # saves content of F into data vector D[I] (i.e. D[0]) 6 | input/ # gets another input, saves to F 7 | add/ # adds to F current data pointed to by I (i.e. D[0]) 8 | output/. # outputs result from F 9 | 10 | -------------------------------------------------------------------------------- /slash/examples/montecarlo.sla: -------------------------------------------------------------------------------- 1 | 7/itof/0/save/10/itof/0/pow/save/ # D[0] is the number of MC points (here 10^7) 2 | 0/itof/1/save/ # D[1] contains the total number of points so far 3 | 0/itof/2/save/ # D[2] contains the number of hits inside a quadrant 4 | 0/label/ # 5 | ran/3/save/mul/save/ # D[3] := x^2 6 | ran/4/save/mul/ # F := y^2 7 | 3/add/save/ # D[3] := x^2 + y^2 8 | 1/itof/3/sub/ # F < 0 if missed 9 | jumpifn/ 10 | 2/load/inc/save/ # D[2] := D[2] + 1 only if it's a hit 11 | jumphere/ 12 | 1/load/inc/save/ # D[1] := D[1] + 1 always 13 | 0/load/dec/save/ # decrease counter 14 | 0/gotoifp/ # 15 | 2/load/1/div/save/ # D[1] := hits / total 16 | 4/itof/1/mul/ # F := 4 * (area of quadrant) 17 | output/. 18 | 19 | -------------------------------------------------------------------------------- /slash/examples/montecarlo_nogotos.sla: -------------------------------------------------------------------------------- 1 | 7/itof/0/save/10/itof/0/pow/save/ # D[0] is the number of MC points (here 10^7) 2 | 0/itof/1/save/ # D[1] contains the total number of points so far 3 | 0/itof/2/save/ # D[2] contains the number of hits inside a quadrant 4 | 0/load/ftoi/ # loads loop counter 5 | loop/ # 6 | ran/3/save/mul/save/ # D[3] := x^2 7 | ran/4/save/mul/ # F := y^2 8 | 3/add/save/ # D[3] := x^2 + y^2 9 | 1/itof/3/sub/ # F < 0 if missed 10 | jumpifn/ 11 | 2/load/inc/save/ # D[2] := D[2] + 1 only if it's a hit 12 | jumphere/ 13 | 1/load/inc/save/ # D[1] := D[1] + 1 always 14 | endloop/ # 15 | 2/load/1/div/save/ # D[1] := hits / total 16 | 4/itof/1/mul/ # F := 4 * (area of quadrant) 17 | output/. 18 | -------------------------------------------------------------------------------- /slash/examples/power4.sla: -------------------------------------------------------------------------------- 1 | # raises an input to the power of 4 2 | 3 | 4/itof/0/save/input/pow/output/. 4 | 5 | -------------------------------------------------------------------------------- /slash/examples/rand.sla: -------------------------------------------------------------------------------- 1 | # prints 10 random numbers 2 | 3 | 9/itof/0/save/# D[0] is the counter 4 | 0/label/# 5 | ran/output/# 6 | 0/load/dec/save/# 7 | 0/gotoifp/. 8 | 9 | -------------------------------------------------------------------------------- /slash/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * slash - A command-line Slash/A interpreter illustrating the use of the Default Instruction Set (DIS). 4 | * 5 | * Copyright (C) 2004-2011 Artur B Adib 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "SlashA.hpp" 26 | 27 | using namespace std; 28 | 29 | int main(int argc, char** argv) 30 | { 31 | cout << "slash -- An interpreter for the Slash/A language" << endl; 32 | cout << SlashA::getHeader() << endl << endl; 33 | 34 | if (argc<2) { 35 | cout << "Usage:\n"; 36 | cout << " slash \n\n"; 37 | exit(1); 38 | } 39 | 40 | string source = ""; 41 | ifstream f(argv[1]); 42 | 43 | if (!f) { 44 | cout << "Cannot open file " << argv[1] << ".\n\n"; 45 | exit(1); 46 | } 47 | 48 | while (!f.eof()) 49 | source += f.get(); 50 | 51 | f.close(); 52 | 53 | try 54 | { 55 | vector input, output; 56 | SlashA::ByteCode bc; 57 | SlashA::InstructionSet iset(32768); // initializes the Default Instruction Set with 32768 numeric instructions 58 | SlashA::MemCore memcore(10, // length of the data tape D[] 59 | 10, // length of the label tape L[] 60 | input, // input buffer (will use keyboard if empty) 61 | output); // output buffer 62 | 63 | iset.insert_DIS_full(); 64 | 65 | SlashA::source2ByteCode(source, bc, iset); // Translates "source" into "bc" using the instruction set "iset" 66 | 67 | bool failed = SlashA::runByteCode(iset, // instruction set pointer 68 | memcore, // memory core pointer 69 | bc, // ByteCoded program to be run (pointer) 70 | -2237, // random seed for random number instructions 71 | 0, // max run time in seconds (0 for no limit) 72 | -1); // max loop depth 73 | 74 | if (failed) 75 | cout << "Program failed (time-out, loop depth, etc)!" << endl; 76 | 77 | cout << endl; 78 | cout << "Total number of operations: " << iset.getTotalOps() << endl; 79 | cout << "Total number of invalid operations: " << iset.getTotalInvops() << endl; 80 | cout << "Total number of inputs before an output: " << iset.getTotalInputsBFOutput() << endl; 81 | } 82 | catch(string& s) 83 | { 84 | cout << s << endl << endl; 85 | exit(1); 86 | } 87 | 88 | cout << endl; 89 | 90 | return 0; 91 | } 92 | --------------------------------------------------------------------------------