├── .gitignore ├── images └── test.jpg ├── Makefile ├── LICENSE ├── README.md ├── expr.h ├── test_expr.cpp └── expr.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /images/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/6502/expr/master/images/test.jpg -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | 3 | ifeq ($(DEBUG), 1) 4 | CCOPTS = -Wall -g -O0 5 | else 6 | CCOPTS = -Wall -O3 7 | endif 8 | 9 | CCOPTS_11 = $(CCOPTS) -std=c++0x 10 | 11 | all: test_expr test_expr_11 test 12 | 13 | clean: 14 | rm -f test_expr test_expr_11 test*.pgm *.gcov *.gcda *.gcno 15 | 16 | test_expr: test_expr.cpp expr.cpp expr.h 17 | $(CC) $(CCOPTS) -otest_expr test_expr.cpp expr.cpp 18 | 19 | test_expr_11: test_expr.cpp expr.cpp expr.h 20 | $(CC) $(CCOPTS_11) -otest_expr_11 test_expr.cpp expr.cpp 21 | 22 | test: test_expr test_expr_11 23 | ./test_expr 24 | ./test_expr_11 25 | 26 | coverage: 27 | g++ -Wall -O0 -g -coverage -otest_expr test_expr.cpp expr.cpp 28 | ./test_expr 29 | gcov test_expr 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Andrea Griffini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | expr 2 | ==== 3 | 4 | ![test image](https://raw.github.com/6502/expr/master/images/test.jpg) 5 | 6 | Tiny library for run-time evaluation of expressions for C++. 7 | 8 | The code is just one include file and one cpp file (about 500 lines 9 | in total, no dependencies besides standard C++ library) providing 10 | only one class `Expr`. 11 | 12 | Usage 13 | ----- 14 | You can instantiate an `Expr` object by parsing an expression from a 15 | `const char *`: 16 | 17 | Expr e = Expr::parse("1/(1 + x)", vars); 18 | 19 | or also 20 | 21 | Expr e("1/(1 + x)", vars); 22 | 23 | where `vars` is an `std::map` instance containing 24 | variables. Referencing undefined variables is a parse time error. 25 | 26 | To evaluate the expression with current variable values just call `e.eval()`. 27 | 28 | Warning 29 | ------- 30 | For efficiency reasons the variables are stored in parsed expressions 31 | as `double *`. This is fine for `std::map` 32 | because the address of an item inside a map is guaranteed to never 33 | change when other items are added or if item value is changed. The map 34 | variable used in the expression however must not be removed when an 35 | `Expr` instance depending on them is stil alive because calling `eval` 36 | in that case would be undefined behavior. 37 | 38 | `Expr` can be copied but note that the copy refers to the same 39 | variables in memory. 40 | 41 | An `Expr` instance can be initialized with a single `double` value 42 | and in that case is a constant expression returning that value. 43 | 44 | Default constructor returns a constant expr evaluating to 0.0 and 45 | an `Expr` instance can also be implicitly converted to a double 46 | (it calls `eval`). 47 | 48 | Partial parsing 49 | --------------- 50 | It's also possible to parse an expression without giving an error if 51 | extra characters are present. This is done using `Expr::parsePartial` 52 | that requires a `const char *&` instead of a `const char *`. The 53 | character pointer will be on the first character not used for parsing 54 | the expression and not being on the terminating `NUL` is not 55 | considered an error. 56 | 57 | Functions 58 | --------- 59 | Using `Expr::addFunction(name, f)` it's possible to add external 60 | functions of 0, 1 or 2 parameters. Predefined functions from 61 | `` are: **floor**, **fabs**, **sqrt**, **sin**, **cos**, 62 | **tan**, **atan**, **atan2**, **pow** and there is also **random** 63 | taking no parameters and returning a number between 0 and 1 64 | implemented as `double(rand()) / RAND_MAX`. 65 | 66 | Syntax 67 | ------ 68 | C syntax is used; implemented operators (in order of precedence) are: 69 | 70 | - (unary) (sign change) 71 | * / (multiplication/division) 72 | + - (addition/subtraction) 73 | 74 | << >> (left and right shift) 75 | & (bitwise and) 76 | | ^ (bitwise or / xor) 77 | 78 | < <= > >= == != (comparison, result is 0 or 1) 79 | && (logical and) 80 | || (logical or) 81 | 82 | **NOTE**: precedence is not the same as in C because C precendence for 83 | bitwise operations is just wrong. 84 | 85 | variables and function names are parsed with `[a-zA-Z_][a-zA-Z0-9_]*`. 86 | 87 | Comments can be included: characters from `;` to the end of a line 88 | are ignored during parsing. 89 | 90 | Errors 91 | ------ 92 | Parsing errors throw an instance of `Expr::Error` (that derives from 93 | `std::runtime_error`). 94 | 95 | Speed 96 | ----- 97 | `Expr` compiles the expression into bytecode for a register based 98 | virtual machine. In the expression used in test_expr image generation: 99 | 100 | ((128 + sin(((x-320)*(x-320) + (y-240)*(y-240))*k)*127) ^ 101 | (255 * ((floor(x/128)+floor(y/96)) & 1))) + random()*32-16 102 | 103 | the evaluation is about 3 times slower than optimized C++ equivalent for 104 | the same. Currently there are no optimizations of any kind implemented. 105 | -------------------------------------------------------------------------------- /expr.h: -------------------------------------------------------------------------------- 1 | #if !defined(EXPR_H_INCLUDED) 2 | #define EXPR_H_INCLUDED 3 | 4 | /* 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2014 Andrea Griffini 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | class Expr { 37 | 38 | public: 39 | struct Error : std::runtime_error { 40 | int position; 41 | Error(const std::string& msg, int position = -1) 42 | : std::runtime_error(msg), position(position) 43 | {} 44 | }; 45 | 46 | double eval() const; 47 | 48 | static void skipsp(const char *& s) { 49 | for(;;) { 50 | while (*s && isspace(*s)) s++; 51 | if (*s == ';') { 52 | while (*s && *s != '\n') s++; 53 | } else break; 54 | } 55 | } 56 | 57 | static Expr partialParse(const char *& s, std::map& vars); 58 | 59 | static Expr parse(const char *s, std::map& vars) { 60 | const char *s0 = s; 61 | Expr expr = partialParse(s, vars); 62 | if (*s) throw Error("Unexpected extra characters\n", s - s0); 63 | return expr; 64 | } 65 | 66 | Expr(double x = 0.0) { 67 | resreg = 0; 68 | wrk.push_back(x); 69 | } 70 | 71 | void swap(Expr& other) { 72 | std::swap(resreg, other.resreg); 73 | code.swap(other.code); 74 | wrk.swap(other.wrk); 75 | variables.swap(other.variables); 76 | } 77 | 78 | Expr(const char *s, std::map& m) { 79 | Expr e = parse(s, m); 80 | swap(e); 81 | } 82 | 83 | operator double() const { 84 | return eval(); 85 | } 86 | 87 | static void addFunction(const char *name, double (*f)()) { 88 | func0.push_back(f); 89 | functions[name] = std::make_pair(func0.size()-1, 0); 90 | } 91 | 92 | static void addFunction(const char *name, double (*f)(double)) { 93 | func1.push_back(f); 94 | functions[name] = std::make_pair(func1.size()-1, 1); 95 | } 96 | 97 | static void addFunction(const char *name, double (*f)(double, double)) { 98 | func2.push_back(f); 99 | functions[name] = std::make_pair(func2.size()-1, 2); 100 | } 101 | 102 | std::string disassemble() const; 103 | 104 | private: 105 | enum { MOVE, LOAD, 106 | NEG, NOT, 107 | ADD, SUB, MUL, DIV, LT, LE, GT, GE, EQ, NE, AND, OR, 108 | B_SHL, B_SHR, B_AND, B_OR, B_XOR, 109 | FSIN, FCOS, FFLOOR, FABS, FSQRT, FTAN, FATAN, FLOG, FEXP, FATAN2, FPOW, 110 | FUNC0, FUNC1, FUNC2 }; 111 | 112 | int resreg; 113 | std::vector code; 114 | mutable std::vector wrk; 115 | std::vector variables; 116 | 117 | struct Operator { 118 | const char *name; 119 | int level; 120 | int opcode; 121 | }; 122 | 123 | static std::map operators; 124 | static int max_level; 125 | static std::map > inlined; 126 | 127 | static std::map > functions; 128 | static std::vector func0; 129 | static std::vector func1; 130 | static std::vector func2; 131 | 132 | class Init; 133 | friend class Init; 134 | 135 | enum { READONLY = 0x4000000 }; 136 | 137 | int reg(std::vector& regs) { 138 | if (regs.size() == 0) { 139 | wrk.resize(1 + wrk.size()); 140 | regs.push_back(wrk.size()-1); 141 | } 142 | int r = regs.back(); 143 | regs.pop_back(); 144 | return r; 145 | } 146 | 147 | int compile(std::vector& regs, 148 | const char *& s, std::map& vars, int level); 149 | 150 | }; 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /test_expr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "expr.h" 5 | 6 | double myrandom() { 7 | return double(rand()) / RAND_MAX; 8 | } 9 | 10 | double sqr(double x) { 11 | return x*x; 12 | } 13 | 14 | double len2(double a, double b) { 15 | return sqrt(a*a + b*b); 16 | } 17 | 18 | int main() { 19 | Expr::addFunction("sqr", sqr); 20 | Expr::addFunction("len2", len2); 21 | 22 | struct Test { const char *expr; int err; double result; } tests[] = { 23 | {"1", -1, 1.0}, 24 | {"1+1", -1, 2.0}, 25 | {"3 * 4", -1, 12.0}, 26 | {"((1))", -1, 1.0}, 27 | {"3*--3", -1, 9.0}, 28 | {"1+2*(3+4)", -1, 15.0}, 29 | {"1+2*(3+4*(5+6.5))", -1, 99.0}, 30 | {"x1", -1, 100.0}, 31 | {"x1+y1*2", -1, 500}, 32 | {"1--1", -1, 2}, 33 | {"1/(8--8) ; this comment is ignored", -1, 0.0625}, 34 | {"1/;internal comment\n(8--8)", -1, 0.0625}, 35 | {"1<2", -1, 1.0}, 36 | {"1<=2", -1, 1.0}, 37 | {"1>2", -1, 0.0}, 38 | {"1>=2", -1, 0.0}, 39 | {"1==2", -1, 0.0}, 40 | {"1!=2", -1, 1.0}, 41 | {"1!=2", -1, 1.0}, 42 | {"!(2<1)", -1, 1.0}, 43 | {"!!2", -1, 1.0}, 44 | {"1<2 && 3<4", -1, 1.0}, 45 | {"1<2 && 3>4", -1, 0.0}, 46 | {"1<2 || 3<4", -1, 1.0}, 47 | {"1<2 || 3>4", -1, 1.0}, 48 | {"(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1)!=1.0", -1, 1.0}, // Approximation! 49 | 50 | {"1 | 2 == 3", -1, 1.0}, 51 | {"3 ^ 2 == 1", -1, 1.0}, 52 | {"3 ^ 1 == 2", -1, 1.0}, 53 | {"-1 & 7 == 7", -1, 1.0}, 54 | {"-1 >> 1 == -1", -1, 1.0}, 55 | {"-1 << 1 == -2", -1, 1.0}, 56 | {"(1 << 16)-1 >> 8 == 255", -1, 1.0}, 57 | 58 | {"abs(-1)", -1, 1.0}, 59 | {"cos(1-1)", -1, 1.0}, 60 | {"abs(cos(3.141592654 * 0.5)) < 1E-6", -1, 1.0}, 61 | {"cos()", 4, -1}, 62 | {"atan2(12)", 8, -1}, 63 | {"atan2(0, 1)", -1, 0}, 64 | {"abs(atan(1)-3.141592654/4) < 1E-6", -1, 1.0}, 65 | {"random() != 1.2", -1, 1.0}, 66 | {"abs(log(1024)/log(2) - 10) < 1E-6", -1, 1.0}, 67 | {"floor(pow(2, 8) + 0.5) == 256", -1, 1.0}, 68 | {"abs(sqrt(2)*sqrt(2) - 2) < 1E-6", -1, 1.0}, 69 | {"abs(tan(3.141592654/4)-1) < 1E-6", -1, 1.0}, 70 | {"abs(log(exp(13)) - 13) < 1E-6", -1, 1.0}, 71 | {"abs(len2(3,4) - 5) < 1E-6", -1, 1.0}, 72 | {"sqr(3) == 9", -1, 1.0}, 73 | 74 | {"1+z2*4", 4, -1}, 75 | {"1+2*", 4, -1}, 76 | {"1+(2*3", 6, -1}, 77 | {"1+2()", 3, -1}, 78 | {"+1", 0, -1}, 79 | }; 80 | 81 | std::map vars; 82 | vars["x0"] = 3.14; 83 | vars["y0"] = 2.718; 84 | vars["x1"] = 100; 85 | vars["y1"] = 200; 86 | 87 | int errors = 0; 88 | int ntests = sizeof(tests)/sizeof(tests[0]); 89 | for (int i=0; i res=%0.3f\n%s (position=%i)\n\n", 102 | tests[i].expr, tests[i].err, tests[i].result, 103 | res, err.what(), err.position); 104 | } 105 | } 106 | } 107 | printf("%i errors on %i tests\n", errors, ntests); 108 | 109 | int w=640, h=480; 110 | vars["k"] = 10*3.141592654 / ((w*w+h*h)/4); 111 | double& y = vars["y"]; 112 | double& x = vars["x"]; 113 | std::vector img(w*h); 114 | Expr e("((128 + sin(((x-320)*(x-320) + (y-240)*(y-240))*k)*127) ^" 115 | " (255 * ((floor(x/128)+floor(y/96)) & 1))) + random()*32-16", vars); 116 | printf("Expression compiled code:\n%s\n", e.disassemble().c_str()); 117 | clock_t start = clock(); 118 | for (int rep=0; rep<10; rep++) { 119 | int i = 0; 120 | for (y=0; y 255) ie = 255; 124 | img[i++] = ie; 125 | } 126 | } 127 | } 128 | clock_t stop = clock(); 129 | printf("Test image generated in %0.3fms (%.0f pixels/sec)\n", 130 | (stop - start)*100.0/CLOCKS_PER_SEC, 131 | double(w*h*10)*CLOCKS_PER_SEC/(stop-start+1)); 132 | FILE *f = fopen("test.pgm", "wb"); 133 | if (f) { 134 | fprintf(f, "P5\n%i %i 255\n", w, h); 135 | fwrite(&img[0], 1, w*h, f); 136 | fclose(f); 137 | } else { 138 | fprintf(stderr, "Error generating test.pgm\n"); 139 | } 140 | 141 | clock_t start2 = clock(); 142 | double k = vars["k"]; 143 | for (int rep=0; rep<10; rep++) { 144 | int i = 0; 145 | for (y=0; y 255) ie = 255; 151 | img[i++] = ie; 152 | } 153 | } 154 | } 155 | clock_t stop2 = clock(); 156 | printf("Test image generated natively in %0.3fms (%.0f pixels/sec)\n", 157 | (stop2 - start2)*100.0/CLOCKS_PER_SEC, 158 | double(w*h*10)*CLOCKS_PER_SEC/(stop2-start2+1)); 159 | f = fopen("test_native.pgm", "wb"); 160 | if (f) { 161 | fprintf(f, "P5\n%i %i 255\n", w, h); 162 | fwrite(&img[0], 1, w*h, f); 163 | fclose(f); 164 | } else { 165 | fprintf(stderr, "Error generating test_native.pgm\n"); 166 | } 167 | 168 | return errors != 0; 169 | } 170 | -------------------------------------------------------------------------------- /expr.cpp: -------------------------------------------------------------------------------- 1 | #include "expr.h" 2 | #include 3 | #include 4 | 5 | /* 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Andrea Griffini 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | 29 | std::map > Expr::functions; 30 | std::vector Expr::func0; 31 | std::vector Expr::func1; 32 | std::vector Expr::func2; 33 | 34 | double Expr::eval() const { 35 | double *wp = &wrk[0]; 36 | const int *cp = &code[0], *ce = cp+code.size(); 37 | while (cp != ce) { 38 | switch(cp[0]) { 39 | case MOVE: wp[cp[1]] = wp[cp[2]]; cp+=3; break; 40 | case LOAD: wp[cp[1]] = *variables[cp[2]]; cp+=3; break; 41 | case NEG: wp[cp[1]] = -wp[cp[1]]; cp+=2; break; 42 | case NOT: wp[cp[1]] = !wp[cp[1]]; cp+=2; break; 43 | case ADD: wp[cp[1]] += wp[cp[2]]; cp+=3; break; 44 | case SUB: wp[cp[1]] -= wp[cp[2]]; cp+=3; break; 45 | case MUL: wp[cp[1]] *= wp[cp[2]]; cp+=3; break; 46 | case DIV: wp[cp[1]] /= wp[cp[2]]; cp+=3; break; 47 | case LT: wp[cp[1]] = (wp[cp[1]] < wp[cp[2]]); cp+=3; break; 48 | case LE: wp[cp[1]] = (wp[cp[1]] <= wp[cp[2]]); cp+=3; break; 49 | case GT: wp[cp[1]] = (wp[cp[1]] > wp[cp[2]]); cp+=3; break; 50 | case GE: wp[cp[1]] = (wp[cp[1]] >= wp[cp[2]]); cp+=3; break; 51 | case EQ: wp[cp[1]] = (wp[cp[1]] == wp[cp[2]]); cp+=3; break; 52 | case NE: wp[cp[1]] = (wp[cp[1]] != wp[cp[2]]); cp+=3; break; 53 | case AND: wp[cp[1]] = (wp[cp[1]] && wp[cp[2]]); cp+=3; break; 54 | case OR: wp[cp[1]] = (wp[cp[1]] || wp[cp[2]]); cp+=3; break; 55 | case B_OR: wp[cp[1]] = (int(wp[cp[1]]) | int(wp[cp[2]])); cp+=3; break; 56 | case B_AND: wp[cp[1]] = (int(wp[cp[1]]) & int(wp[cp[2]])); cp+=3; break; 57 | case B_XOR: wp[cp[1]] = (int(wp[cp[1]]) ^ int(wp[cp[2]])); cp+=3; break; 58 | case B_SHL: wp[cp[1]] = (int(wp[cp[1]]) << int(wp[cp[2]])); cp+=3; break; 59 | case B_SHR: wp[cp[1]] = (int(wp[cp[1]]) >> int(wp[cp[2]])); cp+=3; break; 60 | case FFLOOR: wp[cp[1]] = floor(wp[cp[1]]); cp+=2; break; 61 | case FABS: wp[cp[1]] = fabs(wp[cp[1]]); cp+=2; break; 62 | case FSIN: wp[cp[1]] = sin(wp[cp[1]]); cp+=2; break; 63 | case FCOS: wp[cp[1]] = cos(wp[cp[1]]); cp+=2; break; 64 | case FSQRT: wp[cp[1]] = sqrt(wp[cp[1]]); cp+=2; break; 65 | case FTAN: wp[cp[1]] = tan(wp[cp[1]]); cp+=2; break; 66 | case FATAN: wp[cp[1]] = atan(wp[cp[1]]); cp+=2; break; 67 | case FLOG: wp[cp[1]] = log(wp[cp[1]]); cp+=2; break; 68 | case FEXP: wp[cp[1]] = exp(wp[cp[1]]); cp+=2; break; 69 | case FATAN2: wp[cp[1]] = atan2(wp[cp[1]], wp[cp[2]]); cp+=3; break; 70 | case FPOW: wp[cp[1]] = pow(wp[cp[1]], wp[cp[2]]); cp+=3; break; 71 | case FUNC0: wp[cp[2]] = func0[cp[1]](); cp+=3; break; 72 | case FUNC1: wp[cp[2]] = func1[cp[1]](wp[cp[2]]); cp+=3; break; 73 | case FUNC2: wp[cp[2]] = func2[cp[1]](wp[cp[2]], wp[cp[3]]); cp+=4; break; 74 | } 75 | } 76 | return wp[resreg]; 77 | } 78 | 79 | Expr Expr::partialParse(const char *& s, std::map& vars) { 80 | Expr result; 81 | std::vector regs; 82 | const char *s0 = s; 83 | try { 84 | result.resreg = result.compile(regs, s, vars, -1)&~READONLY; 85 | skipsp(s); 86 | } catch (const Error& re) { 87 | throw Error(re.what(), s - s0); 88 | } 89 | //printf("s0 = \"%s\":\n%s\n\n", s0, result.disassemble().c_str()); 90 | return result; 91 | } 92 | 93 | int Expr::compile(std::vector& regs, 94 | const char *& s, std::map& vars, int level) { 95 | if (level == -1) level = max_level; 96 | if (level == 0) { 97 | skipsp(s); 98 | if (*s == '(') { 99 | s++; 100 | int res = compile(regs, s, vars, -1); 101 | skipsp(s); 102 | if (*s != ')') throw Error("')' expected"); 103 | s++; 104 | return res; 105 | } else if (isdigit((unsigned char)*s) || (*s=='-' && isdigit((unsigned char)s[1]))) { 106 | char *ss = 0; 107 | double v = strtod(s, &ss); 108 | if (ss && ss!=s) { 109 | int x = wrk.size(); wrk.push_back(v); 110 | s = (const char *)ss; 111 | return x | READONLY; 112 | } else { 113 | throw Error("Invalid number"); 114 | } 115 | } else if (*s == '-') { 116 | s++; 117 | int res = compile(regs, s, vars, 0); 118 | if (res & READONLY) { 119 | int r1 = reg(regs); 120 | code.push_back(MOVE); code.push_back(r1); code.push_back(res&~READONLY); 121 | res = r1; 122 | } 123 | code.push_back(NEG); code.push_back(res); 124 | return res; 125 | } else if (*s == '!') { 126 | s++; 127 | int res = compile(regs, s, vars, 0); 128 | if (res & READONLY) { 129 | int r1 = reg(regs); 130 | code.push_back(MOVE); code.push_back(r1); code.push_back(res&~READONLY); 131 | res = r1; 132 | } 133 | code.push_back(NOT); code.push_back(res); 134 | return res; 135 | } else if (*s && (*s == '_' || isalpha((unsigned char)*s))) { 136 | const char *s0 = s; 137 | while (*s && (isalpha((unsigned char)*s) || isdigit((unsigned char)*s) || *s == '_')) s++; 138 | std::string name(s0, s); 139 | if (*s == '(') { 140 | bool ii = false; 141 | std::map >::iterator it = functions.find(name); 142 | if (it == functions.end()) { 143 | ii = true; 144 | it = inlined.find(name); 145 | if (it == inlined.end()) throw Error(std::string("Unknown function '" + name + "'")); 146 | } 147 | s++; 148 | std::vector args; 149 | int id = it->second.first; 150 | int arity = it->second.second; 151 | for (int a=0; a::iterator it = vars.find(name); 184 | if (it != vars.end()) { 185 | int target = reg(regs); 186 | variables.push_back(&it->second); 187 | code.push_back(LOAD); 188 | code.push_back(target); 189 | code.push_back(variables.size()-1); 190 | return target; 191 | } else { 192 | throw Error(std::string("Unknown variable '" + name + "'")); 193 | } 194 | } 195 | } else { 196 | throw Error("Syntax error"); 197 | } 198 | } 199 | int res = compile(regs, s, vars, level-1); 200 | while (skipsp(s), *s) { 201 | std::map::iterator it = operators.find(std::string(s, s+2)); 202 | if (it == operators.end()) it = operators.find(std::string(s, s+1)); 203 | if (it == operators.end() || it->second.level != level) break; 204 | s += it->first.size(); 205 | int x = compile(regs, s, vars, level-1); 206 | if (res & READONLY) { 207 | int r1 = reg(regs); 208 | code.push_back(MOVE); code.push_back(r1); code.push_back(res&~READONLY); 209 | res = r1; 210 | } 211 | code.push_back(it->second.opcode); 212 | code.push_back(res); 213 | code.push_back(x&~READONLY); 214 | if (!(x&READONLY)) { 215 | regs.push_back(x); 216 | } 217 | } 218 | return res; 219 | } 220 | 221 | std::string Expr::disassemble() const { 222 | const char *opnames[] = { "MOVE", "LOAD", 223 | "NEG", 224 | "ADD", "SUB", "MUL", "DIV", "LT", "LE", "GT", "GE", "EQ", "NE", "AND", "OR", 225 | "B_SHL", "B_SHR", "B_AND", "B_OR", "B_XOR", 226 | "FSIN", "FCOS", "FFLOOR", "FABS", "FSQRT", "FTAN", "FATAN", "FLOG", "FEXP", 227 | "FATAN2", "FPOW", 228 | "FUNC0", "FUNC1", "FUNC2" }; 229 | std::string result; 230 | char buf[30]; 231 | const char *fn = "?"; 232 | for (int i=0,n=code.size(); i %i\n", code[i+1], code[i+2], code[i+1]); 261 | i += 2; 262 | break; 263 | case FUNC0: 264 | case FUNC1: 265 | case FUNC2: 266 | fn = "?"; 267 | for (std::map >::iterator it=functions.begin(); 268 | it!=functions.end(); ++it) { 269 | if (it->second.second == code[i]-FUNC0 && it->second.first == code[i+1]) { 270 | fn=it->first.c_str(); 271 | } 272 | } 273 | switch(code[i]) { 274 | case FUNC0: sprintf(buf, " %p=%s() -> %i\n", 275 | func0[code[i+1]], fn, code[i+2]); 276 | i+=2; break; 277 | case FUNC1: sprintf(buf, " %p=%s(%i) -> %i\n", 278 | func1[code[i+1]], fn, code[i+2], code[i+2]); 279 | i+=2; break; 280 | case FUNC2: sprintf(buf, " %p=%s(%i, %i) -> %i\n", 281 | func2[code[i+1]], fn, code[i+2], code[i+3], code[i+2]); 282 | i+=3; break; 283 | } 284 | break; 285 | default: 286 | sprintf(buf, "(%i, %i) -> %i\n", code[i+1], code[i+2], code[i+1]); 287 | i += 2; 288 | break; 289 | } 290 | result += buf; 291 | } 292 | return result; 293 | } 294 | 295 | std::map > Expr::inlined; 296 | 297 | int Expr::max_level; 298 | std::map Expr::operators; 299 | 300 | class Expr::Init { 301 | static double random() { 302 | return double(rand()) / RAND_MAX; 303 | } 304 | 305 | public: 306 | Init() { 307 | const Expr::Operator ops[] = 308 | {{"*", 1, MUL}, {"/", 1, DIV}, 309 | {"+", 2, ADD}, {"-", 2, SUB}, 310 | 311 | {"<<", 3, B_SHL}, {">>", 3, B_SHR}, 312 | {"&", 4, B_AND}, 313 | {"|", 5, B_OR}, {"^", 5, B_XOR}, 314 | 315 | {"<", 6, LT}, {">", 6, GT}, {"<=", 6, LE}, {">=", 6, GE}, {"==", 6, EQ}, {"!=", 6, NE}, 316 | 317 | {"&&", 7, AND}, 318 | 319 | {"||", 8, OR}}; 320 | int n = sizeof(ops) / sizeof(ops[0]); 321 | for (int i=0; i