├── LICENSE ├── README.md ├── example1.cpp ├── example2.cpp ├── example3.cpp ├── expressions ├── AST.h ├── Evaluator.h ├── Exception.h ├── Generator.h ├── Memory.h ├── Parser.h ├── Tokenizer.h └── expressions.h └── tests.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mark Boorer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | expressions 2 | =========== 3 | 4 | Header only c++ expression parsing library with AST building and GLSL shader generation. 5 | Optional LLVM JIT evaluation for improved performance. 6 | 7 | MIT licensed 8 | 9 | 10 | Syntax 11 | ------ 12 | 13 | The supported expression syntax is similar to C (without bitwise operations). 14 | At present the supported operations are: 15 | 16 | 1. Mathematical: \+, -, *, /, %, ^ 17 | 2. Ternary: ? : 18 | 3. Equality: ==, !=, <, <=, >, >= 19 | 4. Logical: &&, || 20 | 5. Functions: sin, cos, tan, sqrt, ceil, floor, min, max, pow, log, log2, log10 21 | 22 | 23 | -------------------------------------------------------------------------------- /example1.cpp: -------------------------------------------------------------------------------- 1 | // example1.cpp 2 | 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | expr::Parser parser; 9 | expr::Evaluator::VariableMap vm; 10 | vm["pi"] = 3.14159f; 11 | vm["x"] = 10.0f; 12 | 13 | const char * expression = "1.0e2 + x * pi"; 14 | expr::ASTNode* ast = parser.parse(expression); 15 | 16 | expr::Evaluator eval(ast, &vm); 17 | std::cout << eval.evaluate() << std::endl; 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /example2.cpp: -------------------------------------------------------------------------------- 1 | // example2.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "GL/glew.h" 9 | #include "GL/glut.h" 10 | #include "GL/gl.h" 11 | 12 | #include 13 | #include 14 | 15 | 16 | const std::string fragHead( 17 | "#version 330\n" 18 | "uniform float value;\n" 19 | "uniform float x;\n" 20 | "uniform float y;\n" 21 | "uniform float pi;\n" 22 | "out vec4 outputColor;\n" 23 | "\n" 24 | ); 25 | 26 | const std::string fragMain( 27 | "void main()\n" 28 | "{\n" 29 | " if (value == calculate())\n" 30 | " {\n" 31 | " outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);\n" 32 | " }\n" 33 | " else\n" 34 | " {\n" 35 | " outputColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n" 36 | " }\n" 37 | "}\n" 38 | ); 39 | 40 | const float vertexPositions[] = 41 | { 42 | 0.75f, 0.75f, 0.0f, 1.0f, 43 | 0.75f, -0.75f, 0.0f, 1.0f, 44 | -0.75f, -0.75f, 0.0f, 1.0f, 45 | }; 46 | 47 | GLuint program; 48 | GLuint positionBuffer; 49 | 50 | 51 | GLuint CreateShader(GLenum eShaderType, const std::string &strShaderFile) 52 | { 53 | GLuint shader = glCreateShader(eShaderType); 54 | const char *strFileData = strShaderFile.c_str(); 55 | glShaderSource(shader, 1, &strFileData, NULL); 56 | 57 | glCompileShader(shader); 58 | 59 | GLint status; 60 | glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 61 | if (status == GL_FALSE) 62 | { 63 | GLint infoLogLength; 64 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); 65 | 66 | GLchar *strInfoLog = new GLchar[infoLogLength + 1]; 67 | glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog); 68 | 69 | const char *strShaderType = NULL; 70 | switch(eShaderType) 71 | { 72 | case GL_VERTEX_SHADER: strShaderType = "vertex"; break; 73 | case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break; 74 | case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break; 75 | } 76 | 77 | std::cerr << "Compile failure in " << strShaderType << "shader: " << strInfoLog << std::endl; 78 | delete[] strInfoLog; 79 | } 80 | 81 | return shader; 82 | } 83 | 84 | 85 | GLuint CreateProgram(const std::vector &shaderList) 86 | { 87 | GLuint program = glCreateProgram(); 88 | 89 | for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) 90 | glAttachShader(program, shaderList[iLoop]); 91 | 92 | glLinkProgram(program); 93 | 94 | GLint status; 95 | glGetProgramiv (program, GL_LINK_STATUS, &status); 96 | if (status == GL_FALSE) 97 | { 98 | GLint infoLogLength; 99 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); 100 | 101 | GLchar *strInfoLog = new GLchar[infoLogLength + 1]; 102 | glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog); 103 | std::cerr << "Linker failure: " << strInfoLog << std::endl; 104 | delete[] strInfoLog; 105 | } 106 | 107 | for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) 108 | glDetachShader(program, shaderList[iLoop]); 109 | 110 | return program; 111 | } 112 | 113 | 114 | GLuint CompileProgram(std::string fragmentShader) 115 | { 116 | std::vector shaderList; 117 | 118 | shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, fragmentShader)); 119 | 120 | GLuint program = CreateProgram(shaderList); 121 | 122 | std::for_each(shaderList.begin(), shaderList.end(), glDeleteShader); 123 | 124 | return program; 125 | } 126 | 127 | 128 | void initialize(int argc, char *argv[]) 129 | { 130 | glutInit(&argc, argv); 131 | glutCreateWindow("Shader compilation test"); 132 | glewExperimental=true; 133 | GLenum err=glewInit(); 134 | if(err!=GLEW_OK) 135 | { 136 | //Problem: glewInit failed, something is seriously wrong. 137 | throw expr::Exception("glewInit failed, aborting."); 138 | } 139 | } 140 | 141 | 142 | void render() 143 | { 144 | glClearColor(0.0f, 0.0f,0.0f, 0.0f); 145 | glClear(GL_COLOR_BUFFER_BIT); 146 | 147 | glUseProgram(program); 148 | 149 | glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); 150 | glEnableVertexAttribArray(0); 151 | glVertexAttribPointer(0,4, GL_FLOAT, GL_FALSE, 0, 0); 152 | glDrawArrays(GL_TRIANGLES, 0, 3); 153 | glDisableVertexAttribArray(0); 154 | glUseProgram(0); 155 | 156 | glutSwapBuffers(); 157 | } 158 | 159 | int main(int argc, char *argv[]) 160 | { 161 | initialize(argc, argv); 162 | 163 | // Expression to evaluate 164 | const char * expression = "min(y,8) < max(y,8) && x % y == 2 ? (ceil(cos(60*pi/180) + sin(30*pi/180) + tan(45*pi/180)) + sqrt(floor(16.5)) + log2(16)) * log10(100) : 0"; 165 | 166 | // Construct the AST from the given expression 167 | expr::Parser parser; 168 | expr::Evaluator::VariableMap vm; vm["pi"] = 3.14159; vm["x"] = 10; vm["y"] = 4; 169 | 170 | expr::ASTNode* ast = parser.parse(expression); 171 | float value = expr::Evaluator(ast, &vm).evaluate(); 172 | std::cout << "Output value is: " << value << std::endl << std::endl; 173 | 174 | // Generate the GLSL fragment shader code 175 | expr::ShaderGenerator generator; 176 | std::string shader; 177 | shader += fragHead; 178 | shader += "float calculate()\n{\n\treturn "; 179 | shader += generator.generate(ast); 180 | shader += ";\n}\n"; 181 | shader += std::string("\n") += fragMain; 182 | 183 | std::cout << shader << std::endl; 184 | 185 | 186 | // Compile the shader 187 | program = CompileProgram(shader); 188 | 189 | // Assign the uniform value in the shader to the output of the CPU evaluation for comparison 190 | glUseProgram(program); 191 | GLuint valueLocation = glGetUniformLocation(program, "value"); 192 | glUniform1f(valueLocation, value); 193 | glUseProgram(0); 194 | 195 | // Assign uniform values for each of the values in variablemap 196 | glUseProgram(program); 197 | for (expr::Parser::VariableMap::iterator it=vm.begin(); it!= vm.end(); it++) 198 | { 199 | GLuint loc = glGetUniformLocation(program, it->first.c_str()); 200 | glUniform1f(loc, it->second); 201 | } 202 | glUseProgram(0); 203 | 204 | 205 | // Make a pretty triangle for us to see the color on 206 | glGenBuffers(1, &positionBuffer); 207 | glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); 208 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW); 209 | glBindBuffer(GL_ARRAY_BUFFER, 0); 210 | 211 | // render 212 | glutDisplayFunc(render); 213 | glutMainLoop(); 214 | 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /example3.cpp: -------------------------------------------------------------------------------- 1 | // example3.cpp 2 | 3 | // clang++ -g example3.cpp -I. `llvm-config --cppflags --ldflags --libs core jit native` -O3 -o example3 4 | 5 | #include 6 | #include 7 | #define USE_LLVM 8 | #include 9 | 10 | int main() 11 | { 12 | expr::Parser parser; 13 | expr::LLVMEvaluator::VariableMap vm; 14 | vm["pi"] = 3.14159265359f; 15 | vm["x"] = 0.5; 16 | vm["y"] = 0.5; 17 | 18 | const char * expression = "(x + y) * 10"; 19 | expr::ASTNode* ast = parser.parse(expression); 20 | 21 | expr::LLVMEvaluator eval(ast, &vm); 22 | 23 | std::cout << eval.evaluate() << std::endl; 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /expressions/AST.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include "Exception.h" 9 | #include "Memory.h" 10 | 11 | namespace expr 12 | { 13 | 14 | class ASTNode 15 | { 16 | public: 17 | enum ASTNodeType 18 | { 19 | OPERATION, 20 | FUNCTION1, 21 | FUNCTION2, 22 | COMPARISON, 23 | LOGICAL, 24 | BRANCH, 25 | NUMBER, 26 | VARIABLE 27 | }; 28 | 29 | ASTNode(ASTNodeType nodeType) 30 | : m_type(nodeType) 31 | { 32 | } 33 | 34 | virtual ~ASTNode() 35 | { 36 | } 37 | 38 | virtual SHARED_PTR clone() const = 0; 39 | 40 | ASTNodeType type() 41 | { 42 | return m_type; 43 | } 44 | 45 | protected: 46 | ASTNodeType m_type; 47 | 48 | }; 49 | typedef SHARED_PTR ASTNodePtr; 50 | 51 | 52 | class OperationASTNode : public ASTNode 53 | { 54 | public: 55 | enum OperationType 56 | { 57 | PLUS, 58 | MINUS, 59 | MUL, 60 | DIV, 61 | POW, 62 | MOD 63 | }; 64 | 65 | OperationASTNode(OperationType operationType, ASTNodePtr leftNode, ASTNodePtr rightNode) 66 | : ASTNode(ASTNode::OPERATION) 67 | , m_operation(operationType) 68 | , m_left(leftNode) 69 | , m_right(rightNode) 70 | {} 71 | 72 | ~OperationASTNode() 73 | { 74 | } 75 | 76 | virtual ASTNodePtr clone() const 77 | { 78 | ASTNodePtr leftNode = m_left->clone(); 79 | ASTNodePtr rightNode = m_right->clone(); 80 | return ASTNodePtr(new OperationASTNode(m_operation, leftNode, rightNode)); 81 | } 82 | 83 | OperationType operation() 84 | { 85 | return m_operation; 86 | } 87 | 88 | ASTNodePtr left() 89 | { 90 | return m_left; 91 | } 92 | 93 | ASTNodePtr right() 94 | { 95 | return m_right; 96 | } 97 | 98 | protected: 99 | OperationType m_operation; 100 | ASTNodePtr m_left; 101 | ASTNodePtr m_right; 102 | }; 103 | 104 | 105 | class Function1ASTNode : public ASTNode 106 | { 107 | public: 108 | enum Function1Type 109 | { 110 | SIN, 111 | COS, 112 | TAN, 113 | SQRT, 114 | LOG, 115 | LOG2, 116 | LOG10, 117 | CEIL, 118 | FLOOR 119 | }; 120 | 121 | Function1ASTNode(Function1Type functionType, ASTNodePtr leftNode) 122 | : ASTNode(ASTNode::FUNCTION1) 123 | , m_function(functionType) 124 | , m_left(leftNode) 125 | {} 126 | 127 | ~Function1ASTNode() 128 | { 129 | } 130 | 131 | virtual ASTNodePtr clone() const 132 | { 133 | ASTNodePtr leftNode = m_left->clone(); 134 | return ASTNodePtr(new Function1ASTNode(m_function, leftNode)); 135 | } 136 | 137 | Function1Type function() 138 | { 139 | return m_function; 140 | } 141 | 142 | ASTNodePtr left() 143 | { 144 | return m_left; 145 | } 146 | 147 | protected: 148 | Function1Type m_function; 149 | ASTNodePtr m_left; 150 | }; 151 | 152 | 153 | class Function2ASTNode : public ASTNode 154 | { 155 | public: 156 | enum Function2Type 157 | { 158 | MIN, 159 | MAX, 160 | POW 161 | }; 162 | 163 | Function2ASTNode(Function2Type functionType, ASTNodePtr leftNode, ASTNodePtr rightNode) 164 | : ASTNode(ASTNode::FUNCTION2) 165 | , m_function(functionType) 166 | , m_left(leftNode) 167 | , m_right(rightNode) 168 | {} 169 | 170 | ~Function2ASTNode() 171 | { 172 | } 173 | 174 | virtual ASTNodePtr clone() const 175 | { 176 | ASTNodePtr leftNode = m_left->clone(); 177 | ASTNodePtr rightNode = m_right->clone(); 178 | return ASTNodePtr(new Function2ASTNode(m_function, leftNode, rightNode)); 179 | } 180 | 181 | Function2Type function() 182 | { 183 | return m_function; 184 | } 185 | 186 | ASTNodePtr left() 187 | { 188 | return m_left; 189 | } 190 | 191 | ASTNodePtr right() 192 | { 193 | return m_right; 194 | } 195 | 196 | protected: 197 | Function2Type m_function; 198 | ASTNodePtr m_left; 199 | ASTNodePtr m_right; 200 | }; 201 | 202 | 203 | class ComparisonASTNode : public ASTNode 204 | { 205 | public: 206 | enum ComparisonType 207 | { 208 | EQUAL, 209 | NOT_EQUAL, 210 | GREATER_THAN, 211 | GREATER_THAN_EQUAL, 212 | LESS_THAN, 213 | LESS_THAN_EQUAL 214 | }; 215 | 216 | ComparisonASTNode(ComparisonType comparisonType, ASTNodePtr leftNode, ASTNodePtr rightNode) 217 | : ASTNode(ASTNode::COMPARISON) 218 | , m_comparison(comparisonType) 219 | , m_left(leftNode) 220 | , m_right(rightNode) 221 | {} 222 | 223 | ~ComparisonASTNode() 224 | { 225 | } 226 | 227 | virtual ASTNodePtr clone() const 228 | { 229 | ASTNodePtr leftNode = m_left->clone(); 230 | ASTNodePtr rightNode = m_right->clone(); 231 | return ASTNodePtr(new ComparisonASTNode(m_comparison, leftNode, rightNode)); 232 | } 233 | 234 | ComparisonType comparison() 235 | { 236 | return m_comparison; 237 | } 238 | 239 | ASTNodePtr left() 240 | { 241 | return m_left; 242 | } 243 | 244 | ASTNodePtr right() 245 | { 246 | return m_right; 247 | } 248 | 249 | protected: 250 | ComparisonType m_comparison; 251 | ASTNodePtr m_left; 252 | ASTNodePtr m_right; 253 | }; 254 | 255 | class LogicalASTNode : public ASTNode 256 | { 257 | public: 258 | enum OperationType 259 | { 260 | AND, 261 | OR 262 | }; 263 | LogicalASTNode(OperationType operationType, ASTNodePtr leftNode, ASTNodePtr rightNode) 264 | : ASTNode(ASTNode::LOGICAL) 265 | , m_operation(operationType) 266 | , m_left(leftNode) 267 | , m_right(rightNode) 268 | {} 269 | 270 | ~LogicalASTNode() 271 | { 272 | } 273 | 274 | virtual ASTNodePtr clone() const 275 | { 276 | ASTNodePtr leftNode = m_left->clone(); 277 | ASTNodePtr rightNode = m_right->clone(); 278 | return ASTNodePtr(new LogicalASTNode(m_operation, leftNode, rightNode)); 279 | } 280 | 281 | OperationType operation() 282 | { 283 | return m_operation; 284 | } 285 | 286 | ASTNodePtr left() 287 | { 288 | return m_left; 289 | } 290 | 291 | ASTNodePtr right() 292 | { 293 | return m_right; 294 | } 295 | 296 | protected: 297 | OperationType m_operation; 298 | ASTNodePtr m_left; 299 | ASTNodePtr m_right; 300 | }; 301 | 302 | 303 | class BranchASTNode : public ASTNode 304 | { 305 | public: 306 | 307 | BranchASTNode(ASTNodePtr conditionNode, ASTNodePtr yesNode, ASTNodePtr noNode) 308 | : ASTNode(ASTNode::BRANCH) 309 | , m_condition(conditionNode) 310 | , m_yes(yesNode) 311 | , m_no(noNode) 312 | {} 313 | 314 | ~BranchASTNode() 315 | { 316 | } 317 | 318 | virtual ASTNodePtr clone() const 319 | { 320 | ASTNodePtr conditionNode = m_condition->clone(); 321 | ASTNodePtr yesNode = m_yes->clone(); 322 | ASTNodePtr noNode = m_no->clone(); 323 | return ASTNodePtr(new BranchASTNode(conditionNode, yesNode, noNode)); 324 | } 325 | 326 | 327 | ASTNodePtr condition() 328 | { 329 | return m_condition; 330 | } 331 | 332 | ASTNodePtr yes() 333 | { 334 | return m_yes; 335 | } 336 | 337 | ASTNodePtr no() 338 | { 339 | return m_no; 340 | } 341 | 342 | protected: 343 | ASTNodePtr m_condition; 344 | ASTNodePtr m_yes; 345 | ASTNodePtr m_no; 346 | }; 347 | 348 | 349 | 350 | template 351 | class NumberASTNode : public ASTNode 352 | { 353 | public: 354 | NumberASTNode(T val) 355 | : ASTNode(ASTNode::NUMBER) 356 | , m_value(val) 357 | {} 358 | 359 | ~NumberASTNode() 360 | {} 361 | 362 | virtual ASTNodePtr clone() const 363 | { 364 | return ASTNodePtr(new NumberASTNode(m_value)); 365 | } 366 | 367 | T value() 368 | { 369 | return m_value; 370 | } 371 | 372 | protected: 373 | T m_value; 374 | }; 375 | 376 | 377 | template 378 | class VariableASTNode : public ASTNode 379 | { 380 | public: 381 | 382 | VariableASTNode(std::string k) 383 | : ASTNode(ASTNode::VARIABLE) 384 | , m_key(k) 385 | {} 386 | 387 | ~VariableASTNode() 388 | {} 389 | 390 | virtual ASTNodePtr clone() const 391 | { 392 | return ASTNodePtr(new VariableASTNode(m_key)); 393 | } 394 | 395 | std::string variable() 396 | { 397 | return m_key; 398 | } 399 | 400 | protected: 401 | std::string m_key; 402 | }; 403 | 404 | 405 | } // namespace expr 406 | 407 | #endif 408 | -------------------------------------------------------------------------------- /expressions/Evaluator.h: -------------------------------------------------------------------------------- 1 | #ifndef EVALUATOR_H 2 | #define EVALUATOR_H 3 | 4 | 5 | #include "math.h" 6 | #include 7 | 8 | #include "AST.h" 9 | #include "Memory.h" 10 | #include "Exception.h" 11 | 12 | #ifdef USE_LLVM 13 | 14 | #include "llvm/DerivedTypes.h" 15 | #include "llvm/ExecutionEngine/ExecutionEngine.h" 16 | #include "llvm/ExecutionEngine/JIT.h" 17 | #include "llvm/IRBuilder.h" 18 | #include "llvm/LLVMContext.h" 19 | #include "llvm/Module.h" 20 | #include "llvm/Intrinsics.h" 21 | #include "llvm/PassManager.h" 22 | #include "llvm/Analysis/Verifier.h" 23 | #include "llvm/Analysis/Passes.h" 24 | #include "llvm/DataLayout.h" 25 | #include "llvm/Transforms/Scalar.h" 26 | #include "llvm/Support/Mutex.h" 27 | #include "llvm/Support/TargetSelect.h" 28 | 29 | #endif //USE_LLVM 30 | 31 | namespace expr 32 | { 33 | 34 | class EvaluatorException : public Exception 35 | { 36 | public: 37 | EvaluatorException(const char * message) 38 | : Exception(message) 39 | { 40 | } 41 | }; 42 | 43 | 44 | 45 | template 46 | class Evaluator 47 | { 48 | public: 49 | typedef std::map VariableMap; 50 | 51 | Evaluator(ASTNodePtr ast, VariableMap *map=NULL) 52 | : m_ast(ast) 53 | , m_map(map) 54 | { 55 | } 56 | 57 | T evaluate() 58 | { 59 | return evaluateSubtree(m_ast); 60 | } 61 | 62 | private: 63 | T evaluateSubtree(ASTNodePtr ast) 64 | { 65 | if(!ast) 66 | { 67 | throw EvaluatorException("No abstract syntax tree provided"); 68 | } 69 | if(ast->type() == ASTNode::NUMBER) 70 | { 71 | SHARED_PTR > n = STATIC_POINTER_CAST >(ast); 72 | return n->value(); 73 | } 74 | else if(ast->type() == ASTNode::VARIABLE) 75 | { 76 | SHARED_PTR > v = STATIC_POINTER_CAST >(ast); 77 | std::string variable = v->variable(); 78 | if (!m_map) 79 | { 80 | throw EvaluatorException("Variable encountered but no VariableMap provided"); 81 | } 82 | 83 | if (!m_map->count(variable)) 84 | { 85 | std::ostringstream ss; 86 | ss << "No variable '" << variable << "' defined in VariableMap"; 87 | throw Exception(ss.str().c_str()); 88 | } 89 | return m_map->at(variable); 90 | } 91 | else if (ast->type() == ASTNode::OPERATION) 92 | { 93 | SHARED_PTR op = STATIC_POINTER_CAST(ast); 94 | 95 | T v1 = evaluateSubtree(op->right()); // the operators are switched thanks to rpn notation 96 | T v2 = evaluateSubtree(op->left()); 97 | switch(op->operation()) 98 | { 99 | case OperationASTNode::PLUS: return v1 + v2; 100 | case OperationASTNode::MINUS: return v1 - v2; 101 | case OperationASTNode::MUL: return v1 * v2; 102 | case OperationASTNode::DIV: return v1 / v2; 103 | case OperationASTNode::POW: return (T) pow(v1,v2); 104 | case OperationASTNode::MOD: return (T) fmod(v1,v2); 105 | default: throw EvaluatorException("Unknown operator in syntax tree"); 106 | } 107 | } 108 | else if (ast->type() == ASTNode::FUNCTION1) 109 | { 110 | SHARED_PTR f = STATIC_POINTER_CAST(ast); 111 | 112 | T v1 = evaluateSubtree(f->left()); 113 | switch(f->function()) 114 | { 115 | case Function1ASTNode::SIN: return (T) sin(v1); 116 | case Function1ASTNode::COS: return (T) cos(v1); 117 | case Function1ASTNode::TAN: return (T) tan(v1); 118 | case Function1ASTNode::SQRT: return (T) sqrt(v1); 119 | case Function1ASTNode::LOG: return (T) log(v1); 120 | case Function1ASTNode::LOG2: return (T) log2(v1); 121 | case Function1ASTNode::LOG10: return (T) log10(v1); 122 | case Function1ASTNode::CEIL: return (T) ceil(v1); 123 | case Function1ASTNode::FLOOR: return (T) floor(v1); 124 | default: throw EvaluatorException("Unknown function in syntax tree"); 125 | } 126 | } 127 | else if (ast->type() == ASTNode::FUNCTION2) 128 | { 129 | SHARED_PTR f = STATIC_POINTER_CAST(ast); 130 | 131 | T v1 = evaluateSubtree(f->right()); // the operators are switched thanks to rpn notation 132 | T v2 = evaluateSubtree(f->left()); 133 | switch(f->function()) 134 | { 135 | case Function2ASTNode::MIN: return std::min(v1, v2); 136 | case Function2ASTNode::MAX: return std::max(v1, v2); 137 | case Function2ASTNode::POW: return (T) pow(v1, v2); 138 | default: throw EvaluatorException("Unknown function in syntax tree"); 139 | } 140 | } 141 | else if (ast->type() == ASTNode::COMPARISON) 142 | { 143 | SHARED_PTR c = STATIC_POINTER_CAST(ast); 144 | T v1 = evaluateSubtree(c->right()); // the operators are switched thanks to rpn notation 145 | T v2 = evaluateSubtree(c->left()); 146 | switch(c->comparison()) 147 | { 148 | case ComparisonASTNode::EQUAL: return v1 == v2; 149 | case ComparisonASTNode::NOT_EQUAL: return v1 != v2; 150 | case ComparisonASTNode::GREATER_THAN: return v1 > v2; 151 | case ComparisonASTNode::GREATER_THAN_EQUAL: return v1 >= v2; 152 | case ComparisonASTNode::LESS_THAN: return v1 < v2; 153 | case ComparisonASTNode::LESS_THAN_EQUAL: return v1 <= v2; 154 | default: throw EvaluatorException("Unknown comparison in syntax tree"); 155 | } 156 | } 157 | else if (ast->type() == ASTNode::LOGICAL) 158 | { 159 | SHARED_PTR l = STATIC_POINTER_CAST(ast); 160 | T v1 = evaluateSubtree(l->right()); // the operators are switched thanks to rpn notation 161 | T v2 = evaluateSubtree(l->left()); 162 | switch(l->operation()) 163 | { 164 | case LogicalASTNode::AND: return v1 && v2; 165 | case LogicalASTNode::OR: return v1 || v2; 166 | default: throw EvaluatorException("Unknown logical operator in syntax tree"); 167 | } 168 | } 169 | else if (ast->type() == ASTNode::BRANCH) 170 | { 171 | SHARED_PTR b = STATIC_POINTER_CAST(ast); 172 | if ((bool)evaluateSubtree(b->condition()) == true) 173 | { 174 | return evaluateSubtree(b->yes()); 175 | } 176 | else 177 | { 178 | return evaluateSubtree(b->no()); 179 | } 180 | } 181 | 182 | throw EvaluatorException("Incorrect syntax tree!"); 183 | } 184 | 185 | 186 | ASTNodePtr m_ast; 187 | VariableMap *m_map; 188 | }; 189 | 190 | #ifdef USE_LLVM 191 | 192 | class LLVMEvaluator 193 | { 194 | public: 195 | typedef std::map VariableMap; 196 | 197 | LLVMEvaluator(ASTNodePtr ast, VariableMap *map=NULL) 198 | : m_context() 199 | , m_module(new llvm::Module("expression jit", m_context)) 200 | , m_builder(m_context) 201 | , m_engine(NULL) 202 | , m_fpm(m_module) 203 | , m_function(NULL) 204 | , m_map(map) 205 | { 206 | // Need to use a mutex here, because LLVM apparently isn't thread safe? 207 | mutex().acquire(); 208 | 209 | llvm::InitializeNativeTarget(); 210 | 211 | // Set up the JIT compiler 212 | std::string error; 213 | m_engine = llvm::EngineBuilder(m_module).setErrorStr(&error).create(); 214 | if (!m_engine) 215 | { 216 | std::ostringstream ss; 217 | ss << "Could not initialize LLVM JIT, "; 218 | ss << error; 219 | mutex().release(); 220 | throw EvaluatorException(ss.str().c_str()); 221 | } 222 | 223 | 224 | // Set up the optimiser pipeline 225 | // Register how target lays out data structures 226 | m_fpm.add(new llvm::DataLayout(*m_engine->getDataLayout())); 227 | // Provide basic AliasAnalysis support for GVN 228 | m_fpm.add(llvm::createBasicAliasAnalysisPass()); 229 | // Promote allocas to registers. 230 | m_fpm.add(llvm::createPromoteMemoryToRegisterPass()); 231 | // Do simple "peephole" optimisations and bit-twiddling 232 | m_fpm.add(llvm::createInstructionCombiningPass()); 233 | // Reassociate expressions 234 | m_fpm.add(llvm::createReassociatePass()); 235 | // Eliminate common subexpressions 236 | m_fpm.add(llvm::createGVNPass()); 237 | // Simplify the control flow graph 238 | m_fpm.add(llvm::createCFGSimplificationPass()); 239 | 240 | m_fpm.doInitialization(); 241 | 242 | 243 | // Create Function as entry point for LLVM 244 | std::vector Void(0, llvm::Type::getFloatTy(m_context)); 245 | llvm::FunctionType *FT = llvm::FunctionType::get(llvm::Type::getFloatTy(m_context), Void, false); 246 | m_function = llvm::Function::Create(FT, llvm::Function::ExternalLinkage, "", m_module); 247 | 248 | // Create block for code 249 | llvm::BasicBlock *BB = llvm::BasicBlock::Create(m_context, "entry", m_function); 250 | m_builder.SetInsertPoint(BB); 251 | 252 | // Convert AST to LLVM and place into function pointer 253 | try 254 | { 255 | m_builder.CreateRet(generateLLVM(ast)); 256 | } 257 | catch (...) 258 | { 259 | mutex().release(); 260 | throw; 261 | } 262 | 263 | // Verify that the function is well formed 264 | //( fails when intrinsics are used ) 265 | //llvm::verifyFunction(*m_function); 266 | 267 | // Dump the LLVM IR (for debugging) 268 | //m_module->dump(); 269 | 270 | // Set the evaluate function call 271 | void *FPtr = m_engine->getPointerToFunction(m_function); 272 | evaluate = (float (*)()) (intptr_t)FPtr; 273 | 274 | mutex().release(); 275 | } 276 | 277 | ~LLVMEvaluator() 278 | { 279 | delete m_engine; 280 | } 281 | 282 | float (*evaluate)(); 283 | 284 | float getVariable(const char *key) 285 | { 286 | if (!m_map) 287 | { 288 | throw EvaluatorException("Variable encountered, but no variable map provided"); 289 | } 290 | 291 | if (m_map->count(key)) 292 | { 293 | return m_map->at(key); 294 | } 295 | 296 | std::stringstream ss; 297 | ss << "Variable '" << key << "' not defined"; 298 | throw EvaluatorException(ss.str().c_str()); 299 | } 300 | 301 | 302 | private: 303 | 304 | // Convert AST into LLVM 305 | llvm::Value *generateLLVM(ASTNodePtr ast) 306 | { 307 | if(!ast) 308 | { 309 | throw EvaluatorException("No abstract syntax tree provided"); 310 | } 311 | if(ast->type() == ASTNode::NUMBER) 312 | { 313 | SHARED_PTR > n = STATIC_POINTER_CAST >(ast); 314 | return llvm::ConstantFP::get(m_context, llvm::APFloat(n->value())); 315 | } 316 | else if(ast->type() == ASTNode::VARIABLE) 317 | { 318 | SHARED_PTR > v = STATIC_POINTER_CAST >(ast); 319 | std::string variable = v->variable(); 320 | 321 | // Put the memory location of the variable from the map, into an LLVM constant 322 | llvm::Value *location = llvm::ConstantInt::get(llvm::Type::getIntNTy(m_context, sizeof(uintptr_t)*8), (uintptr_t) &m_map->at(variable)); 323 | // Cast it to pointer 324 | llvm::Value *ptr = m_builder.CreateIntToPtr(location, llvm::Type::getFloatPtrTy(m_context)); 325 | // Then dereference and load the pointer value 326 | llvm::Value *gep = m_builder.CreateGEP(ptr, llvm::ConstantInt::get(m_context, llvm::APInt(32, 0)), "geptmp"); 327 | llvm::Value *load = m_builder.CreateLoad(gep, "loadtmp"); 328 | 329 | return load; 330 | } 331 | else if (ast->type() == ASTNode::OPERATION) 332 | { 333 | SHARED_PTR op = STATIC_POINTER_CAST(ast); 334 | 335 | llvm::Value *v1 = generateLLVM(op->right()); // the operators are switched thanks to rpn notation 336 | llvm::Value *v2 = generateLLVM(op->left()); 337 | switch(op->operation()) 338 | { 339 | case OperationASTNode::PLUS: return m_builder.CreateFAdd(v1, v2, "addtmp"); 340 | case OperationASTNode::MINUS: return m_builder.CreateFSub(v1, v2, "subtmp"); 341 | case OperationASTNode::MUL: return m_builder.CreateFMul(v1, v2, "multmp"); 342 | case OperationASTNode::DIV: return m_builder.CreateFDiv(v1, v2, "divtmp"); 343 | case OperationASTNode::POW: 344 | { 345 | // Pow operator is defined in an intrinsic, so implement as a function call 346 | std::vector arg_types(2, llvm::Type::getFloatTy(m_context)); // args are 2 floats 347 | return m_builder.CreateCall2(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::pow, arg_types), v1, v2, "powtmp"); 348 | } 349 | case OperationASTNode::MOD: return m_builder.CreateFRem(v1, v2, "modtmp"); 350 | default: throw EvaluatorException("Unknown operator in syntax tree"); 351 | } 352 | } 353 | else if (ast->type() == ASTNode::FUNCTION1) 354 | { 355 | SHARED_PTR f = STATIC_POINTER_CAST(ast); 356 | 357 | llvm::Value *v1 = generateLLVM(f->left()); 358 | std::vector arg_types(1, llvm::Type::getFloatTy(m_context)); // args are 1 float 359 | switch(f->function()) 360 | { 361 | case Function1ASTNode::SIN: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::sin, arg_types), v1, "sintmp"); 362 | case Function1ASTNode::COS: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::cos, arg_types), v1, "costmp"); 363 | //case Function1ASTNode::TAN: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::tan, arg_types), v1 ,"tantmp"); 364 | case Function1ASTNode::TAN: 365 | { 366 | // No tan operator in LLVM 3.2. Have to use sin/cos 367 | llvm::Value *sin = m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::sin, arg_types), v1, "sintmp"); 368 | llvm::Value *cos = m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::cos, arg_types), v1, "costmp"); 369 | return m_builder.CreateFDiv(sin, cos, "tantmp"); 370 | } 371 | case Function1ASTNode::SQRT: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::sqrt, arg_types), v1, "sqrttmp"); 372 | case Function1ASTNode::LOG: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::log, arg_types), v1, "logtmp"); 373 | case Function1ASTNode::LOG2: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::log2, arg_types), v1, "log2tmp"); 374 | case Function1ASTNode::LOG10: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::log10, arg_types), v1, "log10tmp"); 375 | //case Function1ASTNode::CEIL: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::ceil, arg_types), v1, "ceiltmp"); 376 | case Function1ASTNode::CEIL: 377 | { 378 | // No ceil operator in LLVM 3.2. Have to use floor +1 379 | llvm::Value *floor = m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::floor, arg_types), v1, "floortmp"); 380 | llvm::Value *one = llvm::ConstantFP::get(m_context, llvm::APFloat(1.0f)); 381 | return m_builder.CreateFAdd(floor, one, "ceiltmp"); 382 | } 383 | case Function1ASTNode::FLOOR: return m_builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::floor, arg_types), v1, "floortmp"); 384 | default: throw EvaluatorException("Unknown function in syntax tree"); 385 | } 386 | } 387 | else if (ast->type() == ASTNode::FUNCTION2) 388 | { 389 | SHARED_PTR f = STATIC_POINTER_CAST(ast); 390 | 391 | llvm::Value *v1 = generateLLVM(f->right()); // the operators are switched thanks to rpn notation 392 | llvm::Value *v2 = generateLLVM(f->left()); 393 | switch(f->function()) 394 | { 395 | case Function2ASTNode::MIN: 396 | { 397 | // Min is 2 operations, Ordered Greater Than, then select 398 | llvm::Value *gt = m_builder.CreateFCmpOGT(v1, v2, "fogttmp"); 399 | return m_builder.CreateSelect(gt, v2, v1, "mintmp"); 400 | } 401 | case Function2ASTNode::MAX: 402 | { 403 | // Max is 2 operations, Ordered Greater Than, then select 404 | llvm::Value *gt = m_builder.CreateFCmpOGT(v1, v2, "fogttmp"); 405 | return m_builder.CreateSelect(gt, v1, v2, "maxtmp"); 406 | } 407 | case Function2ASTNode::POW: 408 | { 409 | // Pow operator is defined in an intrinsic, so implement as a function call 410 | std::vector arg_types(2, llvm::Type::getFloatTy(m_context)); // args are 2 floats 411 | return m_builder.CreateCall2(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::pow, arg_types), v1, v2, "powtmp"); 412 | } 413 | default: throw EvaluatorException("Unknown function in syntax tree"); 414 | } 415 | } 416 | else if (ast->type() == ASTNode::COMPARISON) 417 | { 418 | SHARED_PTR c = STATIC_POINTER_CAST(ast); 419 | llvm::Value *v1 = generateLLVM(c->right()); // the operators are switched thanks to rpn notation 420 | llvm::Value *v2 = generateLLVM(c->left()); 421 | switch(c->comparison()) 422 | { 423 | case ComparisonASTNode::EQUAL: return m_builder.CreateFCmpOEQ(v1, v2, "foeqtmp"); 424 | case ComparisonASTNode::NOT_EQUAL: return m_builder.CreateFCmpONE(v1, v2, "fonetmp"); 425 | case ComparisonASTNode::GREATER_THAN: return m_builder.CreateFCmpOGT(v1, v2, "fogttmp"); 426 | case ComparisonASTNode::GREATER_THAN_EQUAL: return m_builder.CreateFCmpOGE(v1, v2, "fogetmp"); 427 | case ComparisonASTNode::LESS_THAN: return m_builder.CreateFCmpOLT(v1, v2, "folttmp"); 428 | case ComparisonASTNode::LESS_THAN_EQUAL: return m_builder.CreateFCmpOLE(v1, v2, "foletmp"); 429 | default: throw EvaluatorException("Unknown comparison in syntax tree"); 430 | } 431 | } 432 | else if (ast->type() == ASTNode::LOGICAL) 433 | { 434 | SHARED_PTR l = STATIC_POINTER_CAST(ast); 435 | llvm::Value *v1 = generateLLVM(l->right()); // the operators are switched thanks to rpn notation 436 | llvm::Value *v2 = generateLLVM(l->left()); 437 | switch(l->operation()) 438 | { 439 | case LogicalASTNode::AND: return m_builder.CreateAnd(v1, v2, "andtmp"); 440 | case LogicalASTNode::OR: return m_builder.CreateOr(v1, v2, "ortmp"); 441 | default: throw EvaluatorException("Unknown logical operator in syntax tree"); 442 | } 443 | } 444 | else if (ast->type() == ASTNode::BRANCH) 445 | { 446 | SHARED_PTR b = STATIC_POINTER_CAST(ast); 447 | 448 | llvm::Value *cond = generateLLVM(b->condition()); 449 | // If condition is a number, coerce it to bool 450 | if (cond->getType() == llvm::Type::getFloatTy(m_context)) 451 | { 452 | cond = m_builder.CreateFCmpONE(cond, llvm::ConstantFP::get(m_context, llvm::APFloat(0.0f)), "ifcond"); 453 | } 454 | llvm::Value *yes = generateLLVM(b->yes()); 455 | llvm::Value *no = generateLLVM(b->no()); 456 | 457 | llvm::Function *fun = m_builder.GetInsertBlock()->getParent(); 458 | llvm::BasicBlock *thenBB = llvm::BasicBlock::Create(m_context, "then", fun); 459 | llvm::BasicBlock *elseBB = llvm::BasicBlock::Create(m_context, "else"); 460 | llvm::BasicBlock *mergeBB = llvm::BasicBlock::Create(m_context, "ifcont"); 461 | 462 | m_builder.CreateCondBr(cond, thenBB, elseBB); 463 | m_builder.SetInsertPoint(thenBB); 464 | 465 | m_builder.CreateBr(mergeBB); 466 | 467 | // emit else block 468 | thenBB = m_builder.GetInsertBlock(); 469 | fun->getBasicBlockList().push_back(elseBB); 470 | m_builder.SetInsertPoint(elseBB); 471 | 472 | m_builder.CreateBr(mergeBB); 473 | elseBB = m_builder.GetInsertBlock(); 474 | 475 | // emit the merge block 476 | fun->getBasicBlockList().push_back(mergeBB); 477 | m_builder.SetInsertPoint(mergeBB); 478 | llvm::PHINode *PN = m_builder.CreatePHI(llvm::Type::getFloatTy(m_context), 2, "iftmp"); 479 | PN->addIncoming(yes, thenBB); 480 | PN->addIncoming(no, elseBB); 481 | return PN; 482 | } 483 | 484 | throw EvaluatorException("Incorrect syntax tree!"); 485 | 486 | } 487 | 488 | static llvm::sys::SmartMutex& mutex() 489 | { 490 | static llvm::sys::SmartMutex m_mutex; return m_mutex; 491 | } 492 | 493 | 494 | llvm::LLVMContext m_context; 495 | llvm::Module *m_module; 496 | llvm::IRBuilder<> m_builder; 497 | llvm::ExecutionEngine *m_engine; 498 | llvm::FunctionPassManager m_fpm; 499 | llvm::Function *m_function; 500 | VariableMap *m_map; 501 | 502 | }; 503 | 504 | #endif // USE_LLVM 505 | 506 | } // namespace expr 507 | 508 | #endif 509 | -------------------------------------------------------------------------------- /expressions/Exception.h: -------------------------------------------------------------------------------- 1 | #ifndef EXCEPTION_H 2 | #define EXCEPTION_H 3 | 4 | #include 5 | #include 6 | 7 | namespace expr 8 | { 9 | 10 | class Exception : public std::runtime_error 11 | { 12 | public: 13 | Exception(const char* message) 14 | : std::runtime_error(message) 15 | { 16 | } 17 | }; 18 | 19 | } //namespace expr 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /expressions/Generator.h: -------------------------------------------------------------------------------- 1 | #ifndef GENERATOR_H 2 | #define GENERATOR_H 3 | 4 | #include 5 | #include 6 | #include "AST.h" 7 | #include "Memory.h" 8 | #include "Exception.h" 9 | 10 | namespace expr 11 | { 12 | 13 | class GeneratorException : public Exception 14 | { 15 | public: 16 | GeneratorException(const char * message) 17 | : Exception(message) 18 | { 19 | } 20 | }; 21 | 22 | template 23 | class ShaderGenerator 24 | { 25 | public: 26 | enum Language 27 | { 28 | 29 | GLSLv1_0, 30 | GLSLv1_3 31 | }; 32 | std::string generate(ASTNodePtr ast, Language lang=GLSLv1_3) 33 | { 34 | m_language = lang; 35 | 36 | if(ast == NULL) 37 | { 38 | throw GeneratorException("Incorrect abstract syntax tree"); 39 | } 40 | 41 | std::string code = generateSubtree(ast); 42 | return code; 43 | } 44 | 45 | private: 46 | std::string type() 47 | { 48 | return "double"; 49 | } 50 | 51 | std::string generateSubtree(ASTNodePtr ast) 52 | { 53 | std::stringstream ss; 54 | 55 | if(ast->type() == ASTNode::NUMBER) 56 | { 57 | SHARED_PTR > n = STATIC_POINTER_CAST >(ast); 58 | ss << n->value(); 59 | if (type() == "float") 60 | { 61 | ss << "f"; 62 | } 63 | else if (type() == "double") 64 | { 65 | ss << "lf"; 66 | } 67 | return ss.str(); 68 | } 69 | else if(ast->type() == ASTNode::VARIABLE) 70 | { 71 | SHARED_PTR > v = STATIC_POINTER_CAST >(ast); 72 | std::string var = v->variable(); 73 | ss << var; 74 | return ss.str(); 75 | } 76 | else if (ast->type() == ASTNode::OPERATION) 77 | { 78 | SHARED_PTR op = STATIC_POINTER_CAST(ast); 79 | 80 | std::string v1 = generateSubtree(op->right()); // the operators are switched thanks to rpn notation 81 | std::string v2 = generateSubtree(op->left()); 82 | switch(op->operation()) 83 | { 84 | case OperationASTNode::PLUS: return std::string("(") + v1 + "+" + v2 + ")"; 85 | case OperationASTNode::MINUS: return std::string("(") + v1 + "-" + v2 + ")"; 86 | case OperationASTNode::MUL: return std::string("(") + v1 + "*" + v2 + ")"; 87 | case OperationASTNode::DIV: return std::string("(") + v1 + "/" + v2 + ")"; 88 | case OperationASTNode::POW: return std::string("pow(") + v1 + "," + v2 + ")"; 89 | case OperationASTNode::MOD: 90 | { 91 | if(m_language == GLSLv1_0) 92 | { 93 | // GLSLv1_0 has no trunc or fmod operator 94 | // operation should return (v1 - v2 * ((v1/v2>0) ? floor(v1/v2) : ceil(v1/v2))) 95 | return std::string("(") + v1 + " - " + v2 + "* ((" + v1 + "/" + v2 + ">0) ? floor(" + v1 + "/" + v2 + ") : ceil(" + v1 + "/" + v2 + ")))"; 96 | } 97 | else if (m_language == GLSLv1_3) 98 | { 99 | // GLSLv1_3 has no fmod operator 100 | // operation should return (v1 - v2 * trunc(v1/v2)) 101 | return std::string("(") + v1 + " - " + v2 + " * trunc(" + v1 + "/" + v2 + "))"; 102 | } 103 | return std::string("fmod(") + v1 + "," + v2 + ")"; 104 | } 105 | default: throw GeneratorException("Unknown operator in syntax tree"); 106 | } 107 | } 108 | else if (ast->type() == ASTNode::FUNCTION1) 109 | { 110 | SHARED_PTR f = STATIC_POINTER_CAST(ast); 111 | 112 | std::string v1 = generateSubtree(f->left()); 113 | switch(f->function()) 114 | { 115 | case Function1ASTNode::SIN: return std::string("sin(") + v1 + ")"; 116 | case Function1ASTNode::COS: return std::string("cos(") + v1 + ")"; 117 | case Function1ASTNode::TAN: return std::string("tan(") + v1 + ")"; 118 | case Function1ASTNode::SQRT: return std::string("sqrt(") + v1 + ")"; 119 | case Function1ASTNode::LOG: return std::string("log(") + v1 + ")"; 120 | case Function1ASTNode::LOG2: return std::string("log2(") + v1 + ")"; 121 | case Function1ASTNode::LOG10: 122 | if (m_language == GLSLv1_0 || m_language == GLSLv1_3) 123 | { 124 | // GLSLv1_0 and GLSLv1_3 have no log10 operator 125 | // operation should return (log(v1)/log(10f)) 126 | return std::string("(log(") + v1 + ")/log(10f))"; 127 | } 128 | return std::string("log10(") + v1 + ")"; 129 | case Function1ASTNode::CEIL: return std::string("ceil(") + v1 + ")"; 130 | case Function1ASTNode::FLOOR: return std::string("floor(") + v1 + ")"; 131 | default: throw GeneratorException("Unknown function in syntax tree"); 132 | } 133 | } 134 | else if (ast->type() == ASTNode::FUNCTION2) 135 | { 136 | SHARED_PTR f = STATIC_POINTER_CAST(ast); 137 | 138 | std::string v1 = generateSubtree(f->right()); // the operators are switched thanks to rpn notation 139 | std::string v2 = generateSubtree(f->left()); 140 | switch(f->function()) 141 | { 142 | case Function2ASTNode::MIN: return std::string("min(") + v1 + "," + v2 + ")"; 143 | case Function2ASTNode::MAX: return std::string("max(") + v1 + "," + v2 + ")"; 144 | case Function2ASTNode::POW: return std::string("pow(") + v1 + "," + v2 + ")"; 145 | default: throw GeneratorException("Unknown function in syntax tree"); 146 | } 147 | } 148 | else if (ast->type() == ASTNode::COMPARISON) 149 | { 150 | SHARED_PTR c = STATIC_POINTER_CAST(ast); 151 | std::string v1 = generateSubtree(c->right()); // the operators are switched thanks to rpn notation 152 | std::string v2 = generateSubtree(c->left()); 153 | switch(c->comparison()) 154 | { 155 | case ComparisonASTNode::EQUAL: return v1 + "==" + v2; 156 | case ComparisonASTNode::NOT_EQUAL: return v1 + "!=" + v2; 157 | case ComparisonASTNode::GREATER_THAN: return v1 + ">" + v2; 158 | case ComparisonASTNode::GREATER_THAN_EQUAL: return v1 + ">=" + v2; 159 | case ComparisonASTNode::LESS_THAN: return v1 + "<" + v2; 160 | case ComparisonASTNode::LESS_THAN_EQUAL: return v1 + "<=" + v2; 161 | default: throw GeneratorException("Unknown comparison in syntax tree"); 162 | } 163 | } 164 | else if (ast->type() == ASTNode::LOGICAL) 165 | { 166 | SHARED_PTR l = STATIC_POINTER_CAST(ast); 167 | std::string v1 = generateSubtree(l->right()); // the operators are switched thanks to rpn notation 168 | std::string v2 = generateSubtree(l->left()); 169 | switch(l->operation()) 170 | { 171 | case LogicalASTNode::AND: return v1 + "&&" + v2; 172 | case LogicalASTNode::OR: return v1 + "||" + v2; 173 | default: throw GeneratorException("Unknown logical operator in syntax tree"); 174 | } 175 | } 176 | else if (ast->type() == ASTNode::BRANCH) 177 | { 178 | SHARED_PTR b = STATIC_POINTER_CAST(ast); 179 | std::string condition = generateSubtree(b->condition()); 180 | std::string yes = generateSubtree(b->yes()); 181 | std::string no = generateSubtree(b->no()); 182 | 183 | return std::string("((bool(") + condition + ")) ? " + yes + ":" + no + ")"; 184 | } 185 | 186 | throw GeneratorException("Incorrect syntax tree!"); 187 | } 188 | 189 | private: 190 | Language m_language; 191 | }; 192 | 193 | template <> std::string ShaderGenerator::type() 194 | { 195 | return "int"; 196 | } 197 | 198 | template <> std::string ShaderGenerator::type() 199 | { 200 | return "float"; 201 | } 202 | 203 | } // namespace expr 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /expressions/Memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | 4 | // shared_ptr / dynamic_pointer_cast 5 | #if EXPRESSIONS_USE_BOOST_PTR 6 | #include 7 | #define SHARED_PTR boost::shared_ptr 8 | #define STATIC_POINTER_CAST boost::static_pointer_cast 9 | #elif defined(_LIBCPP_VERSION) 10 | #include 11 | #define SHARED_PTR std::shared_ptr 12 | #define STATIC_POINTER_CAST std::static_pointer_cast 13 | #elif __GNUC__ >= 4 14 | #include 15 | #define SHARED_PTR std::tr1::shared_ptr 16 | #define STATIC_POINTER_CAST std::tr1::static_pointer_cast 17 | #else 18 | #error Expressions needs gcc 4 or later to get access to (or specify EXPRESSIONS_USE_BOOST_PTR instead) 19 | #endif 20 | 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /expressions/Parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include "AST.h" 5 | #include "Tokenizer.h" 6 | #include "Exception.h" 7 | #include 8 | #include 9 | 10 | namespace expr 11 | { 12 | 13 | class ParserException : public Exception 14 | { 15 | public: 16 | ParserException(const char * message) 17 | : Exception(message) 18 | { 19 | } 20 | }; 21 | 22 | 23 | 24 | template 25 | class Parser 26 | { 27 | public: 28 | 29 | ASTNodePtr parse(const char* text) 30 | { 31 | try 32 | { 33 | std::deque tokens; 34 | Tokenizer(text).tokenize(tokens); 35 | shuntingYard(tokens); 36 | ASTNodePtr node = rpnToAST(tokens); 37 | return node; 38 | } 39 | catch (TokenizerException &e) 40 | { 41 | throw ParserException(e.what()); 42 | } 43 | } 44 | 45 | void print(std::deque &tokens) 46 | { 47 | for (unsigned int i=0; iprint(); 51 | std::cout << " "; 52 | } 53 | std::cout << std::endl; 54 | } 55 | 56 | 57 | private: 58 | 59 | void shuntingYard(std::deque &tokens) 60 | { 61 | // This is an implementation of the shunting yard algorithm 62 | // to convert infix notation into reverse polish notation. 63 | // this ensures that the operator precedence is maintained 64 | // in our AST. 65 | 66 | // Stacks for "shunting" 67 | std::deque output; 68 | std::deque stack; 69 | 70 | // While there are tokens to be read 71 | while (tokens.size() != 0) 72 | { 73 | // Read a token 74 | TokenPtr token = tokens.front(); tokens.pop_front(); 75 | 76 | // If the token is a number, then add it to the output queue. 77 | if (token->getType() == Token::NUMBER || token->getType() == Token::VARIABLE) 78 | { 79 | output.push_back(token); 80 | continue; 81 | } 82 | 83 | // If the token is a function token, then push it onto the stack. 84 | else if (token->getType() == Token::FUNCTION) 85 | { 86 | stack.push_front(token); 87 | continue; 88 | } 89 | 90 | // If the token is a function argument separator (e.g., a comma): 91 | else if (token->getType() == Token::COMMA) 92 | { 93 | // Until the token at the top of the stack is a left parenthesis, 94 | while (true) 95 | { 96 | if (stack.size() != 0) 97 | { 98 | TokenPtr stackToken = stack.front(); 99 | if (stackToken->getType() != Token::OPEN_PARENTHESIS) 100 | { 101 | // pop operators off the stack onto the output queue 102 | output.push_back(stackToken); 103 | stack.pop_front(); 104 | } 105 | else 106 | { 107 | break; 108 | } 109 | } 110 | else 111 | { 112 | // If no left parentheses are encountered, either the separator was misplaced 113 | // or parentheses were mismatched. 114 | std::stringstream ss; 115 | ss << "Misplaced separator or unmatched parenthesis, character: "; 116 | ss << token->getPosition(); 117 | throw ParserException(ss.str().c_str()); 118 | } 119 | } 120 | continue; 121 | } 122 | 123 | // If the token is an operator, o1, then: 124 | else if ( token->getType() == Token::OPERATOR || 125 | token->getType() == Token::UNARY || 126 | token->getType() == Token::CONDITIONAL || 127 | token->getType() == Token::LOGICAL || 128 | token->getType() == Token::TERNARY ) 129 | { 130 | // while there is an operator token, o2, at the top of the stack, 131 | while (stack.size() != 0) 132 | { 133 | TokenPtr stackToken = stack.front(); 134 | if (stackToken->getType() == Token::OPERATOR || 135 | stackToken->getType() == Token::UNARY || 136 | stackToken->getType() == Token::CONDITIONAL || 137 | stackToken->getType() == Token::LOGICAL || 138 | stackToken->getType() == Token::TERNARY ) 139 | { 140 | SHARED_PTR op1 = STATIC_POINTER_CAST(token); 141 | SHARED_PTR op2 = STATIC_POINTER_CAST(stackToken); 142 | // and either o1 is left-associative and its precedence is equal to that of o2, 143 | // or o1 has precedence less than that of o2, 144 | if ( (op1->leftAssociative() && op1->precedence() == op2->precedence()) || 145 | op1->precedence() < op2->precedence()) 146 | { 147 | // pop o2 off the stack, onto the output queue; 148 | output.push_back(stackToken); 149 | stack.pop_front(); 150 | } 151 | else 152 | { 153 | break; 154 | } 155 | } 156 | else 157 | { 158 | break; 159 | } 160 | } 161 | // push o1 onto the stack. 162 | stack.push_front(token); 163 | continue; 164 | } 165 | 166 | // If the token is a left parenthesis, then push it onto the stack. 167 | else if (token->getType() == Token::OPEN_PARENTHESIS) 168 | { 169 | stack.push_front(token); 170 | continue; 171 | } 172 | 173 | // If the token is a right parenthesis: 174 | else if (token->getType() == Token::CLOSE_PARENTHESIS) 175 | { 176 | // Until the token at the top of the stack is a left parenthesis, 177 | while (true) 178 | { 179 | if (stack.size() != 0) 180 | { 181 | TokenPtr stackToken = stack.front(); 182 | if (stackToken->getType() != Token::OPEN_PARENTHESIS) 183 | { 184 | // pop operators off the stack onto the output queue 185 | output.push_back(stackToken); 186 | stack.pop_front(); 187 | } 188 | else 189 | { 190 | break; 191 | } 192 | } 193 | else 194 | { 195 | // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses 196 | std::stringstream ss; 197 | ss << "Mismatched parenthesis, character: "; 198 | ss << token->getPosition(); 199 | throw ParserException(ss.str().c_str()); 200 | } 201 | } 202 | 203 | // Pop the left parenthesis from the stack, but not onto the output queue 204 | stack.pop_front(); 205 | 206 | // If the token at the top of the stack is a function token, 207 | if (stack.size() != 0) 208 | { 209 | if (stack.front()->getType() == Token::FUNCTION) 210 | { 211 | // pop it onto the output queue. 212 | output.push_back(stack.front()); 213 | stack.pop_front(); 214 | } 215 | } 216 | continue; 217 | } 218 | } 219 | 220 | // When there are no more tokens to read 221 | // While there are still operator tokens in the stack 222 | while (stack.size() != 0) 223 | { 224 | TokenPtr stackToken = stack.front(); 225 | if (stackToken->getType() == Token::OPEN_PARENTHESIS || 226 | stackToken->getType() == Token::CLOSE_PARENTHESIS ) 227 | { 228 | throw ParserException("Mismatched parenthesis"); 229 | } 230 | else 231 | { 232 | output.push_back(stackToken); 233 | stack.pop_front(); 234 | } 235 | } 236 | 237 | // Replace the tokens in the original TokVec 238 | tokens = std::deque(output.begin(), output.end()); 239 | } 240 | 241 | ASTNodePtr rpnToAST(std::deque &tokens) 242 | { 243 | // To convert from RPN to AST, just push each number node onto the front of the stack 244 | // and for each operator, pop the required operands, then push the resulting node 245 | // to the front of the stack 246 | 247 | std::deque stack; 248 | for (unsigned int i=0; igetType() == Token::NUMBER) 253 | { 254 | SHARED_PTR > nt = STATIC_POINTER_CAST >(token); 255 | ASTNodePtr n = ASTNodePtr(new NumberASTNode(nt->getValue())); 256 | stack.push_front(n); 257 | continue; 258 | } 259 | 260 | if (token->getType() == Token::VARIABLE) 261 | { 262 | SHARED_PTR v = STATIC_POINTER_CAST(token); 263 | std::string key = v->getValue(); 264 | ASTNodePtr n = ASTNodePtr(new VariableASTNode(key)); 265 | stack.push_front(n); 266 | continue; 267 | } 268 | 269 | 270 | 271 | if (token->getType() == Token::UNARY) 272 | { 273 | if (stack.size() == 0) 274 | { 275 | std::stringstream ss; 276 | ss << "Invalid syntax: unary operator given without variable, character: "; 277 | ss << token->getPosition(); 278 | throw ParserException(ss.str().c_str()); 279 | } 280 | 281 | SHARED_PTR u = STATIC_POINTER_CAST(token); 282 | 283 | if (u->getDirection() == UnaryToken::NEGATIVE) 284 | { 285 | 286 | // Take the number from the top of the stack and make it negative 287 | SHARED_PTR > n = STATIC_POINTER_CAST >(stack.front()); 288 | stack.pop_front(); 289 | 290 | stack.push_front(ASTNodePtr(new NumberASTNode(n->value() * -1))); 291 | } 292 | continue; 293 | } 294 | 295 | if (token->getType() == Token::OPERATOR) 296 | { 297 | if (stack.size() < 2) 298 | { 299 | std::stringstream ss; 300 | ss << "Invalid syntax: operator given with insufficient operands, character: "; 301 | ss << token->getPosition(); 302 | throw ParserException(ss.str().c_str()); 303 | } 304 | 305 | SHARED_PTR opt = STATIC_POINTER_CAST(token); 306 | ASTNodePtr left = stack.front(); stack.pop_front(); 307 | ASTNodePtr right = stack.front(); stack.pop_front(); 308 | OperationASTNode::OperationType type; 309 | switch(opt->getOperator()) 310 | { 311 | case OperatorToken::PLUS: type = OperationASTNode::PLUS; break; 312 | case OperatorToken::MINUS: type = OperationASTNode::MINUS; break; 313 | case OperatorToken::MUL: type = OperationASTNode::MUL; break; 314 | case OperatorToken::DIV: type = OperationASTNode::DIV; break; 315 | case OperatorToken::POW: type = OperationASTNode::POW; break; 316 | case OperatorToken::MOD: type = OperationASTNode::MOD; break; 317 | default: 318 | std::stringstream ss; 319 | ss << "Unknown operator token, character: "; 320 | ss << token->getPosition(); 321 | throw ParserException(ss.str().c_str()); 322 | } 323 | stack.push_front(ASTNodePtr(new OperationASTNode(type, left, right))); 324 | continue; 325 | } 326 | 327 | if (token->getType() == Token::FUNCTION) 328 | { 329 | if (stack.size() == 0) 330 | { 331 | std::stringstream ss; 332 | ss << "Invalid syntax: function given with insufficient operands, character: "; 333 | ss << token->getPosition(); 334 | throw ParserException(ss.str().c_str()); 335 | } 336 | SHARED_PTR f = STATIC_POINTER_CAST(token); 337 | ASTNodePtr left = stack.front(); stack.pop_front(); 338 | 339 | switch(f->getFunction()) 340 | { 341 | case FunctionToken::SIN: 342 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::SIN, left))); 343 | break; 344 | case FunctionToken::COS: 345 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::COS, left))); 346 | break; 347 | case FunctionToken::TAN: 348 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::TAN, left))); 349 | break; 350 | case FunctionToken::SQRT: 351 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::SQRT, left))); 352 | break; 353 | case FunctionToken::LOG: 354 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::LOG, left))); 355 | break; 356 | case FunctionToken::LOG2: 357 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::LOG2, left))); 358 | break; 359 | case FunctionToken::LOG10: 360 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::LOG10, left))); 361 | break; 362 | case FunctionToken::CEIL: 363 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::CEIL, left))); 364 | break; 365 | case FunctionToken::FLOOR: 366 | stack.push_front(ASTNodePtr(new Function1ASTNode(Function1ASTNode::FLOOR, left))); 367 | break; 368 | case FunctionToken::MIN: 369 | { 370 | if (stack.size() == 0) 371 | { 372 | std::stringstream ss; 373 | ss << "Invalid syntax: function given with insuffucient operands, character: "; 374 | ss << token->getPosition(); 375 | throw ParserException(ss.str().c_str()); 376 | } 377 | ASTNodePtr right = stack.front(); stack.pop_front(); 378 | stack.push_front(ASTNodePtr(new Function2ASTNode(Function2ASTNode::MIN, left, right))); 379 | } 380 | break; 381 | case FunctionToken::MAX: 382 | { 383 | if (stack.size() == 0) 384 | { 385 | std::stringstream ss; 386 | ss << "Invalid syntax: function given with insuffucient operands, character: "; 387 | ss << token->getPosition(); 388 | throw ParserException(ss.str().c_str()); 389 | } 390 | ASTNodePtr right = stack.front(); stack.pop_front(); 391 | stack.push_front(ASTNodePtr(new Function2ASTNode(Function2ASTNode::MAX, left, right))); 392 | } 393 | break; 394 | case FunctionToken::POW: 395 | { 396 | if (stack.size() == 0) 397 | { 398 | std::stringstream ss; 399 | ss << "Invalid syntax: function given with insuffucient operands, character: "; 400 | ss << token->getPosition(); 401 | throw ParserException(ss.str().c_str()); 402 | } 403 | ASTNodePtr right = stack.front(); stack.pop_front(); 404 | stack.push_front(ASTNodePtr(new Function2ASTNode(Function2ASTNode::POW, left, right))); 405 | } 406 | break; 407 | default: 408 | std::stringstream ss; 409 | ss << "Unknown function token, character: "; 410 | ss << token->getPosition(); 411 | throw ParserException(ss.str().c_str()); 412 | } 413 | continue; 414 | } 415 | 416 | if (token->getType() == Token::CONDITIONAL) 417 | { 418 | if (stack.size() < 2) 419 | { 420 | std::stringstream ss; 421 | ss << "Invalid syntax: conditional operator given with insuffucient operands, character: "; 422 | ss << token->getPosition(); 423 | throw ParserException(ss.str().c_str()); 424 | } 425 | SHARED_PTR c = STATIC_POINTER_CAST(token); 426 | ASTNodePtr left = stack.front(); stack.pop_front(); 427 | ASTNodePtr right = stack.front(); stack.pop_front(); 428 | ComparisonASTNode::ComparisonType type; 429 | switch(c->getConditional()) 430 | { 431 | case ConditionalToken::EQUAL: 432 | type = ComparisonASTNode::EQUAL; break; 433 | case ConditionalToken::NOT_EQUAL: 434 | type = ComparisonASTNode::NOT_EQUAL; break; 435 | case ConditionalToken::GREATER_THAN: 436 | type = ComparisonASTNode::GREATER_THAN; break; 437 | case ConditionalToken::GREATER_THAN_EQUAL: 438 | type = ComparisonASTNode::GREATER_THAN_EQUAL; break; 439 | case ConditionalToken::LESS_THAN: 440 | type = ComparisonASTNode::LESS_THAN; break; 441 | case ConditionalToken::LESS_THAN_EQUAL: 442 | type = ComparisonASTNode::LESS_THAN_EQUAL; break; 443 | default: 444 | std::stringstream ss; 445 | ss << "Unknown conditional operator token, character: "; 446 | ss << token->getPosition(); 447 | throw ParserException(ss.str().c_str()); 448 | } 449 | stack.push_front(ASTNodePtr(new ComparisonASTNode(type, left, right))); 450 | continue; 451 | } 452 | 453 | if (token->getType() == Token::LOGICAL) 454 | { 455 | if (stack.size() < 2) 456 | { 457 | std::stringstream ss; 458 | ss << "Invalid syntax: logical operator given with insuffucient operands, character: "; 459 | ss << token->getPosition(); 460 | throw ParserException(ss.str().c_str()); 461 | } 462 | SHARED_PTR l = STATIC_POINTER_CAST(token); 463 | ASTNodePtr left = stack.front(); stack.pop_front(); 464 | ASTNodePtr right = stack.front(); stack.pop_front(); 465 | LogicalASTNode::OperationType type; 466 | switch(l->getOperator()) 467 | { 468 | case LogicalToken::AND: 469 | type = LogicalASTNode::AND; break; 470 | case LogicalToken::OR: 471 | type = LogicalASTNode::OR; break; 472 | default: 473 | std::stringstream ss; 474 | ss << "Unknown logical operator token, character: "; 475 | ss << token->getPosition(); 476 | throw ParserException(ss.str().c_str()); 477 | } 478 | stack.push_front(ASTNodePtr(new LogicalASTNode(type, left, right))); 479 | continue; 480 | } 481 | 482 | if (token->getType() == Token::TERNARY) 483 | { 484 | if (stack.size() < 3) 485 | { 486 | std::stringstream ss; 487 | ss << "Invalid syntax: ternary operator given with insuffucient operands, character: "; 488 | ss << token->getPosition(); 489 | throw ParserException(ss.str().c_str()); 490 | } 491 | SHARED_PTR t = STATIC_POINTER_CAST(token); 492 | if (t->getSymbol() == TernaryToken::TERNARY) 493 | { 494 | ASTNodePtr no = stack.front(); stack.pop_front(); 495 | ASTNodePtr yes = stack.front(); stack.pop_front(); 496 | ASTNodePtr condition = stack.front(); stack.pop_front(); 497 | 498 | stack.push_front(ASTNodePtr(new BranchASTNode(condition, yes, no))); 499 | } 500 | continue; 501 | } 502 | } 503 | 504 | if (stack.size() != 0) 505 | { 506 | return stack.front(); 507 | } 508 | 509 | return ASTNodePtr(); 510 | } 511 | }; 512 | 513 | } // namespace expr 514 | 515 | #endif 516 | -------------------------------------------------------------------------------- /expressions/Tokenizer.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKENIZER_H 2 | #define TOKENIZER_H 3 | 4 | #include "ctype.h" // for isspace, isdigit and isalnum 5 | #include "string.h" // for memcpy 6 | #include "Memory.h" 7 | #include "Exception.h" 8 | #include 9 | #include 10 | #include 11 | 12 | namespace expr 13 | { 14 | 15 | class Token 16 | { 17 | public: 18 | enum TokenType 19 | { 20 | UNARY, 21 | OPERATOR, 22 | FUNCTION, 23 | COMMA, 24 | CONDITIONAL, 25 | LOGICAL, 26 | TERNARY, 27 | OPEN_PARENTHESIS, 28 | CLOSE_PARENTHESIS, 29 | NUMBER, 30 | VARIABLE, 31 | ENDOFTEXT 32 | }; 33 | 34 | Token(TokenType type, int pos) 35 | : m_type(type) 36 | , m_pos(pos) 37 | {} 38 | 39 | ~Token() 40 | {} 41 | 42 | TokenType getType() 43 | { 44 | return m_type; 45 | } 46 | 47 | int getPosition() 48 | { 49 | return m_pos; 50 | } 51 | 52 | virtual std::string print() 53 | { 54 | return ""; 55 | } 56 | 57 | protected: 58 | TokenType m_type; 59 | int m_pos; 60 | }; 61 | typedef SHARED_PTR TokenPtr; 62 | 63 | 64 | template 65 | class NumberToken : public Token 66 | { 67 | public: 68 | NumberToken(T value, int pos) 69 | : Token(NUMBER, pos) 70 | , m_value(value) 71 | {} 72 | 73 | ~NumberToken() 74 | {} 75 | 76 | T getValue() 77 | { 78 | return m_value; 79 | } 80 | 81 | std::string print() 82 | { 83 | std::stringstream ss; 84 | ss << m_value; 85 | return ss.str(); 86 | } 87 | 88 | protected: 89 | T m_value; 90 | }; 91 | 92 | class VariableToken : public Token 93 | { 94 | public: 95 | VariableToken(std::string value, int pos) 96 | : Token(VARIABLE, pos) 97 | , m_value(value) 98 | {} 99 | 100 | ~VariableToken() 101 | {} 102 | 103 | std::string getValue() 104 | { 105 | return m_value; 106 | } 107 | 108 | std::string print() 109 | { 110 | return m_value; 111 | } 112 | 113 | protected: 114 | std::string m_value; 115 | }; 116 | 117 | 118 | 119 | class FunctionToken : public Token 120 | { 121 | public: 122 | enum FunctionType 123 | { 124 | SIN, 125 | COS, 126 | TAN, 127 | SQRT, 128 | LOG, 129 | LOG2, 130 | LOG10, 131 | CEIL, 132 | FLOOR, 133 | MIN, 134 | MAX, 135 | POW 136 | }; 137 | FunctionToken(FunctionType function, int pos) 138 | : Token(FUNCTION, pos) 139 | , m_function(function) 140 | {} 141 | 142 | ~FunctionToken() 143 | {} 144 | 145 | FunctionType getFunction() 146 | { 147 | return m_function; 148 | } 149 | 150 | std::string print() 151 | { 152 | switch(m_function) 153 | { 154 | case FunctionToken::SIN: 155 | return "sin"; 156 | case FunctionToken::COS: 157 | return "cos"; 158 | case FunctionToken::TAN: 159 | return "tan"; 160 | case FunctionToken::SQRT: 161 | return "sqrt"; 162 | case FunctionToken::LOG: 163 | return "log"; 164 | case FunctionToken::LOG2: 165 | return "log2"; 166 | case FunctionToken::LOG10: 167 | return "log10"; 168 | case FunctionToken::CEIL: 169 | return "ceil"; 170 | case FunctionToken::FLOOR: 171 | return "floor"; 172 | case FunctionToken::MIN: 173 | return "min"; 174 | case FunctionToken::MAX: 175 | return "max"; 176 | case FunctionToken::POW: 177 | return "pow"; 178 | default: 179 | return ""; 180 | } 181 | } 182 | 183 | protected: 184 | FunctionType m_function; 185 | }; 186 | 187 | 188 | class PrecedenceOperator: public Token 189 | { 190 | public: 191 | PrecedenceOperator(Token::TokenType type, int pos) 192 | : Token(type, pos) 193 | {} 194 | virtual ~PrecedenceOperator() 195 | {} 196 | 197 | virtual int precedence() 198 | { 199 | return 0; 200 | } 201 | 202 | virtual bool leftAssociative() 203 | { 204 | return true; 205 | } 206 | }; 207 | 208 | 209 | class OperatorToken : public PrecedenceOperator 210 | { 211 | public: 212 | enum OperatorType 213 | { 214 | PLUS, 215 | MINUS, 216 | MUL, 217 | DIV, 218 | POW, 219 | MOD 220 | }; 221 | 222 | OperatorToken(OperatorType op, int pos) 223 | : PrecedenceOperator(Token::OPERATOR, pos) 224 | , m_operator(op) 225 | {} 226 | 227 | ~OperatorToken() 228 | {} 229 | 230 | OperatorType getOperator() 231 | { 232 | return m_operator; 233 | } 234 | 235 | std::string print() 236 | { 237 | switch(m_operator) 238 | { 239 | case OperatorToken::PLUS: 240 | return "+"; 241 | case OperatorToken::MINUS: 242 | return "-"; 243 | case OperatorToken::MUL: 244 | return "*"; 245 | case OperatorToken::DIV: 246 | return "/"; 247 | case OperatorToken::POW: 248 | return "^"; 249 | case OperatorToken::MOD: 250 | return "%"; 251 | default: 252 | return ""; 253 | } 254 | 255 | } 256 | 257 | int precedence() 258 | { 259 | switch(m_operator) 260 | { 261 | case PLUS: 262 | case MINUS: 263 | return 20; 264 | case MUL: 265 | case DIV: 266 | case MOD: 267 | return 30; 268 | case POW: 269 | return 40; 270 | default: 271 | break; 272 | } 273 | return 0; 274 | } 275 | 276 | bool leftAssociative() 277 | { 278 | switch(m_operator) 279 | { 280 | case PLUS: 281 | case MINUS: 282 | case MUL: 283 | case DIV: 284 | case MOD: 285 | return true; 286 | case POW: 287 | return false; 288 | default: 289 | break; 290 | } 291 | return false; 292 | } 293 | 294 | protected: 295 | OperatorType m_operator; 296 | }; 297 | 298 | 299 | class ConditionalToken : public PrecedenceOperator 300 | { 301 | public: 302 | enum ConditionalType 303 | { 304 | EQUAL, 305 | NOT_EQUAL, 306 | GREATER_THAN, 307 | GREATER_THAN_EQUAL, 308 | LESS_THAN, 309 | LESS_THAN_EQUAL 310 | }; 311 | 312 | ConditionalToken(ConditionalType conditional, int pos) 313 | : PrecedenceOperator(Token::CONDITIONAL, pos) 314 | , m_conditional(conditional) 315 | {} 316 | 317 | ~ConditionalToken() 318 | {} 319 | 320 | ConditionalType getConditional() 321 | { 322 | return m_conditional; 323 | } 324 | 325 | std::string print() 326 | { 327 | switch(m_conditional) 328 | { 329 | case ConditionalToken::EQUAL: 330 | return "=="; 331 | case ConditionalToken::NOT_EQUAL: 332 | return "!="; 333 | case ConditionalToken::LESS_THAN: 334 | return "<"; 335 | case ConditionalToken::LESS_THAN_EQUAL: 336 | return "<="; 337 | case ConditionalToken::GREATER_THAN: 338 | return ">"; 339 | case ConditionalToken::GREATER_THAN_EQUAL: 340 | return ">="; 341 | default: 342 | return ""; 343 | } 344 | } 345 | 346 | int precedence() 347 | { 348 | switch(m_conditional) 349 | { 350 | case EQUAL: 351 | case NOT_EQUAL: 352 | return 10; 353 | case GREATER_THAN: 354 | case GREATER_THAN_EQUAL: 355 | case LESS_THAN: 356 | case LESS_THAN_EQUAL: 357 | return 15; 358 | default: 359 | break; 360 | } 361 | return 0; 362 | } 363 | 364 | protected: 365 | ConditionalType m_conditional; 366 | }; 367 | 368 | 369 | class LogicalToken : public PrecedenceOperator 370 | { 371 | public: 372 | enum OperatorType 373 | { 374 | AND, 375 | OR 376 | }; 377 | 378 | LogicalToken(OperatorType op, int pos) 379 | : PrecedenceOperator(Token::LOGICAL, pos) 380 | , m_operator(op) 381 | {} 382 | 383 | ~LogicalToken() 384 | {} 385 | 386 | OperatorType getOperator() 387 | { 388 | return m_operator; 389 | } 390 | 391 | std::string print() 392 | { 393 | switch(m_operator) 394 | { 395 | case LogicalToken::AND: 396 | return "&&"; 397 | case LogicalToken::OR: 398 | return "||"; 399 | default: 400 | return ""; 401 | } 402 | } 403 | 404 | int precedence() 405 | { 406 | switch(m_operator) 407 | { 408 | case AND: 409 | return 9; 410 | case OR: 411 | return 8; 412 | default: 413 | break; 414 | } 415 | return 0; 416 | } 417 | 418 | private: 419 | OperatorType m_operator; 420 | }; 421 | 422 | class TernaryToken : public PrecedenceOperator 423 | { 424 | public: 425 | enum SymbolType 426 | { 427 | TERNARY, 428 | COLON 429 | }; 430 | 431 | TernaryToken(SymbolType symbol, int pos) 432 | : PrecedenceOperator(Token::TERNARY, pos) 433 | , m_symbol(symbol) 434 | {} 435 | 436 | ~TernaryToken() 437 | {} 438 | 439 | SymbolType getSymbol() 440 | { 441 | return m_symbol; 442 | } 443 | 444 | std::string print() 445 | { 446 | switch(m_symbol) 447 | { 448 | case TernaryToken::TERNARY: 449 | return "?"; 450 | case TernaryToken::COLON: 451 | return ":"; 452 | default: 453 | return ""; 454 | } 455 | 456 | } 457 | 458 | int precedence() 459 | { 460 | return 5; 461 | } 462 | 463 | bool leftAssociative() 464 | { 465 | return false; 466 | } 467 | 468 | protected: 469 | SymbolType m_symbol; 470 | }; 471 | 472 | 473 | class UnaryToken: public PrecedenceOperator 474 | { 475 | public: 476 | enum UnaryType 477 | { 478 | POSITIVE, 479 | NEGATIVE 480 | }; 481 | UnaryToken(UnaryType direction, int pos) 482 | : PrecedenceOperator(Token::UNARY, pos) 483 | , m_direction(direction) 484 | {} 485 | 486 | ~UnaryToken() 487 | {} 488 | 489 | UnaryType getDirection() 490 | { 491 | return m_direction; 492 | } 493 | 494 | std::string print() 495 | { 496 | switch(m_direction) 497 | { 498 | case UnaryToken::POSITIVE: 499 | return "u+";; 500 | case UnaryToken::NEGATIVE: 501 | return "u-"; 502 | default: 503 | return ""; 504 | } 505 | 506 | } 507 | 508 | int precedence() 509 | { 510 | return 50; 511 | } 512 | 513 | bool leftAssociative() 514 | { 515 | return false; 516 | } 517 | 518 | 519 | protected: 520 | UnaryType m_direction; 521 | }; 522 | 523 | 524 | class TokenizerException : public Exception 525 | { 526 | public: 527 | TokenizerException(const char * message) 528 | : Exception(message) 529 | { 530 | } 531 | }; 532 | 533 | template 534 | class Tokenizer 535 | { 536 | typedef std::deque TokVec; 537 | public: 538 | Tokenizer(const char *text) 539 | : m_text(text) 540 | , m_index(0) 541 | { 542 | } 543 | 544 | ~Tokenizer() 545 | { 546 | } 547 | 548 | void tokenize(TokVec &tokens) 549 | { 550 | while (m_text[m_index] != 0) 551 | { 552 | skipWhitespace(); 553 | if (m_text[m_index] == 0) 554 | { 555 | break; 556 | } 557 | 558 | // Check for numbers 559 | if (isdigit(m_text[m_index]) || m_text[m_index] == '.') 560 | { 561 | tokens.push_back(TokenPtr(new NumberToken(getNumber(), m_index))); 562 | continue; 563 | } 564 | 565 | // Check for single character operators 566 | { 567 | bool match = false; 568 | switch(m_text[m_index]) 569 | { 570 | case '+': 571 | { 572 | TokenPtr token; 573 | 574 | // Attempt to detect unary minus 575 | if (tokens.size() == 0) 576 | { 577 | // No tokens before, so unary 578 | token = TokenPtr(new UnaryToken(UnaryToken::POSITIVE, m_index)); 579 | } 580 | else 581 | { 582 | if (tokens.back()->getType() == Token::CLOSE_PARENTHESIS || 583 | tokens.back()->getType() == Token::NUMBER || 584 | tokens.back()->getType() == Token::VARIABLE ) 585 | { 586 | token = TokenPtr(new OperatorToken(OperatorToken::PLUS, m_index)); 587 | } 588 | else if (tokens.back()->getType() == Token::FUNCTION) 589 | { 590 | std::stringstream ss; 591 | ss << "Invalid syntax: unary positive '+' following function declaration, character: "; 592 | ss << m_index; 593 | throw TokenizerException(ss.str().c_str()); 594 | } 595 | else if (tokens.back()->getType() == Token::UNARY) 596 | { 597 | std::stringstream ss; 598 | ss << "Invalid syntax: unary positive '+' following unary declaration, character: "; 599 | ss << m_index; 600 | throw TokenizerException(ss.str().c_str()); 601 | } 602 | else 603 | { 604 | token = TokenPtr(new UnaryToken(UnaryToken::POSITIVE, m_index)); 605 | } 606 | } 607 | tokens.push_back(token); 608 | match = true; 609 | break; 610 | } 611 | case '-': 612 | { 613 | TokenPtr token; 614 | 615 | // Attempt to detect unary minus 616 | if (tokens.size() == 0) 617 | { 618 | // No tokens before, so unary 619 | token = TokenPtr(new UnaryToken(UnaryToken::NEGATIVE, m_index)); 620 | } 621 | else 622 | { 623 | if (tokens.back()->getType() == Token::CLOSE_PARENTHESIS || 624 | tokens.back()->getType() == Token::NUMBER || 625 | tokens.back()->getType() == Token::VARIABLE ) 626 | { 627 | token = TokenPtr(new OperatorToken(OperatorToken::MINUS, m_index)); 628 | } 629 | else if (tokens.back()->getType() == Token::FUNCTION) 630 | { 631 | std::stringstream ss; 632 | ss << "Invalid syntax: unary negative '-' following function declaration, character: "; 633 | ss << m_index; 634 | throw TokenizerException(ss.str().c_str()); 635 | } 636 | else if (tokens.back()->getType() == Token::UNARY) 637 | { 638 | std::stringstream ss; 639 | ss << "Invalid syntax: unary negative '-' following unary declaration, character: "; 640 | ss << m_index; 641 | throw TokenizerException(ss.str().c_str()); 642 | } 643 | else 644 | { 645 | token = TokenPtr(new UnaryToken(UnaryToken::NEGATIVE, m_index)); 646 | } 647 | } 648 | tokens.push_back(token); 649 | match = true; 650 | break; 651 | } 652 | case '*': 653 | followsExpression("multiplication operator '*'", tokens); 654 | tokens.push_back(TokenPtr(new OperatorToken(OperatorToken::MUL, m_index))); 655 | match = true; 656 | break; 657 | case '/': 658 | followsExpression("division operator '/'", tokens); 659 | tokens.push_back(TokenPtr(new OperatorToken(OperatorToken::DIV, m_index))); 660 | match = true; 661 | break; 662 | case '^': 663 | followsExpression("power operator '^'", tokens); 664 | tokens.push_back(TokenPtr(new OperatorToken(OperatorToken::POW, m_index))); 665 | match = true; 666 | break; 667 | case '%': 668 | followsExpression("modulus operator '%'", tokens); 669 | tokens.push_back(TokenPtr(new OperatorToken(OperatorToken::MOD, m_index))); 670 | match = true; 671 | break; 672 | case '?': 673 | followsExpression("ternary declaration '?'", tokens); 674 | tokens.push_back(TokenPtr(new TernaryToken(TernaryToken::TERNARY, m_index))); 675 | match = true; 676 | break; 677 | case ':': 678 | followsExpression("ternary divider ':'", tokens); 679 | tokens.push_back(TokenPtr(new TernaryToken(TernaryToken::COLON, m_index))); 680 | match = true; 681 | break; 682 | case '(': 683 | tokens.push_back(TokenPtr(new Token(Token::OPEN_PARENTHESIS, m_index))); 684 | match = true; 685 | break; 686 | case ')': 687 | tokens.push_back(TokenPtr(new Token(Token::CLOSE_PARENTHESIS, m_index))); 688 | match = true; 689 | break; 690 | case ',': 691 | followsExpression("comma separator ','", tokens); 692 | tokens.push_back(TokenPtr(new Token(Token::COMMA, m_index))); 693 | match = true; 694 | break; 695 | default: 696 | break; 697 | } 698 | if (match) 699 | { 700 | m_index++; 701 | continue; 702 | } 703 | } 704 | 705 | // Look for known two character keywords 706 | { 707 | bool match = false; 708 | std::string word; 709 | if (m_text[m_index+1] != 0) 710 | { 711 | word = m_text[m_index]; 712 | word += m_text[m_index+1]; 713 | 714 | if (word == "==") 715 | { 716 | followsExpression("equality conditional '=='", tokens); 717 | tokens.push_back(TokenPtr(new ConditionalToken(ConditionalToken::EQUAL, m_index))); 718 | m_index++; 719 | match = true; 720 | } 721 | else if (word == "!=") 722 | { 723 | followsExpression("inequality conditional '!='", tokens); 724 | tokens.push_back(TokenPtr(new ConditionalToken(ConditionalToken::NOT_EQUAL, m_index))); 725 | m_index++; 726 | match = true; 727 | } 728 | else if (word == "<=") 729 | { 730 | followsExpression("less-than-or-equal conditional '<='", tokens); 731 | tokens.push_back(TokenPtr(new ConditionalToken(ConditionalToken::LESS_THAN_EQUAL, m_index))); 732 | m_index++; 733 | match = true; 734 | } 735 | else if (word == ">=") 736 | { 737 | followsExpression("greater-than-or-equal conditional '>='", tokens); 738 | tokens.push_back(TokenPtr(new ConditionalToken(ConditionalToken::GREATER_THAN_EQUAL, m_index))); 739 | m_index++; 740 | match = true; 741 | } 742 | else if (word == "&&") 743 | { 744 | followsExpression("logical and operator '&&'", tokens); 745 | tokens.push_back(TokenPtr(new LogicalToken(LogicalToken::AND, m_index))); 746 | m_index++; 747 | match = true; 748 | } 749 | else if (word == "||") 750 | { 751 | followsExpression("logical or operator '||'", tokens); 752 | tokens.push_back(TokenPtr(new LogicalToken(LogicalToken::OR, m_index))); 753 | m_index++; 754 | match = true; 755 | } 756 | else 757 | { 758 | // Look for single characters that are substrings of the words above 759 | switch(m_text[m_index]) 760 | { 761 | case '<': 762 | followsExpression("less-than conditional '<'", tokens); 763 | tokens.push_back(TokenPtr(new ConditionalToken(ConditionalToken::LESS_THAN, m_index))); 764 | match = true; 765 | break; 766 | case '>': 767 | followsExpression("greater-than conditional '>'", tokens); 768 | tokens.push_back(TokenPtr(new ConditionalToken(ConditionalToken::GREATER_THAN, m_index))); 769 | match = true; 770 | break; 771 | default: 772 | break; 773 | } 774 | 775 | } 776 | } 777 | if (match) 778 | { 779 | m_index++; // 2 character words will have already incremented once 780 | continue; 781 | } 782 | } 783 | 784 | 785 | // Look for multi character keywords or variables 786 | { 787 | bool match = false; 788 | std::string word; 789 | 790 | while (m_text[m_index] != 0) 791 | { 792 | char c = m_text[m_index]; 793 | 794 | // Only proceed if character is alphanumeric or underscore 795 | if (!isalnum(c) && c != '_') 796 | { 797 | break; 798 | } 799 | 800 | word +=c; 801 | m_index++; 802 | 803 | if (word == "sin") 804 | { 805 | expressionAllowed("sin", tokens); 806 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::SIN, m_index))); 807 | match = true; 808 | break; 809 | } 810 | else if (word == "cos") 811 | { 812 | expressionAllowed("cos", tokens); 813 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::COS, m_index))); 814 | match = true; 815 | break; 816 | } 817 | else if (word == "tan") 818 | { 819 | expressionAllowed("tan", tokens); 820 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::TAN, m_index))); 821 | match = true; 822 | break; 823 | } 824 | else if (word == "sqrt") 825 | { 826 | expressionAllowed("sqrt", tokens); 827 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::SQRT, m_index))); 828 | match = true; 829 | break; 830 | } 831 | else if (word == "ceil") 832 | { 833 | expressionAllowed("ceil", tokens); 834 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::CEIL, m_index))); 835 | match = true; 836 | break; 837 | } 838 | else if (word == "floor") 839 | { 840 | expressionAllowed("floor", tokens); 841 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::FLOOR, m_index))); 842 | match = true; 843 | break; 844 | } 845 | else if (word == "min") 846 | { 847 | expressionAllowed("min", tokens); 848 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::MIN, m_index))); 849 | match = true; 850 | break; 851 | } 852 | else if (word == "max") 853 | { 854 | expressionAllowed("max", tokens); 855 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::MAX, m_index))); 856 | match = true; 857 | break; 858 | } 859 | else if (word == "pow") 860 | { 861 | expressionAllowed("pow", tokens); 862 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::POW, m_index))); 863 | match = true; 864 | break; 865 | } 866 | else if (word == "log") 867 | { 868 | // Check for other versions of log 869 | if (m_text[m_index] == '2') 870 | { 871 | expressionAllowed("log2", tokens); 872 | m_index++; 873 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::LOG2, m_index))); 874 | } 875 | else if (m_text[m_index] == '1') 876 | { 877 | if (m_text[m_index + 1] == '0') 878 | { 879 | expressionAllowed("log10", tokens); 880 | m_index+=2; 881 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::LOG10, m_index))); 882 | } 883 | } 884 | else 885 | { 886 | expressionAllowed("log", tokens); 887 | m_index+=2; 888 | tokens.push_back(TokenPtr(new FunctionToken(FunctionToken::LOG, m_index))); 889 | } 890 | match = true; 891 | break; 892 | } 893 | 894 | } 895 | 896 | if (match) 897 | { 898 | continue; 899 | } 900 | else 901 | { 902 | if (word != "") 903 | { 904 | expressionAllowed(std::string("variable '") + word + "'", tokens); 905 | tokens.push_back(TokenPtr(new VariableToken(word, m_index))); 906 | continue; 907 | } 908 | } 909 | } 910 | // Don't know what this token is 911 | std::stringstream ss; 912 | ss << "Unknown token '" << m_text[m_index] << "', character: "; 913 | ss << m_index; 914 | throw TokenizerException(ss.str().c_str()); 915 | 916 | 917 | } 918 | 919 | // add the endoftext token 920 | if (tokens.size() != 0) 921 | { 922 | if (tokens.back()->getType() != Token::NUMBER && 923 | tokens.back()->getType() != Token::VARIABLE && 924 | tokens.back()->getType() != Token::CLOSE_PARENTHESIS ) 925 | { 926 | std::stringstream ss; 927 | ss << "Unexpected end of expression, character: " << m_index; 928 | throw TokenizerException(ss.str().c_str()); 929 | } 930 | } 931 | tokens.push_back(TokenPtr(new Token(Token::ENDOFTEXT, m_index))); 932 | } 933 | 934 | private: 935 | 936 | void skipWhitespace() 937 | { 938 | while(isspace(m_text[m_index])) 939 | { 940 | m_index++; 941 | } 942 | } 943 | 944 | void followsExpression(std::string descriptor, TokVec &tokens) 945 | { 946 | if (tokens.size() == 0) 947 | { 948 | std::stringstream ss; 949 | ss << "Invalid syntax: " << descriptor << " must follow expression, character: "; 950 | ss << m_index +1; 951 | throw TokenizerException(ss.str().c_str()); 952 | } 953 | 954 | if (tokens.back()->getType() != Token::NUMBER && 955 | tokens.back()->getType() != Token::VARIABLE && 956 | tokens.back()->getType() != Token::CLOSE_PARENTHESIS ) 957 | { 958 | std::stringstream ss; 959 | ss << "Invalid syntax: " << descriptor << " must follow expression, character: "; 960 | ss << m_index + 1; 961 | throw TokenizerException(ss.str().c_str()); 962 | } 963 | } 964 | 965 | void expressionAllowed(std::string descriptor, TokVec &tokens) 966 | { 967 | if (tokens.size() != 0) 968 | { 969 | if (tokens.back()->getType() == Token::NUMBER || 970 | tokens.back()->getType() == Token::VARIABLE || 971 | tokens.back()->getType() == Token::FUNCTION || 972 | tokens.back()->getType() == Token::CLOSE_PARENTHESIS ) 973 | { 974 | std::stringstream ss; 975 | ss << "Invalid syntax: " << descriptor << " cannot directly follow another expression without an operator between, character: "; 976 | ss << m_index; 977 | throw TokenizerException(ss.str().c_str()); 978 | } 979 | } 980 | } 981 | 982 | T getNumber() 983 | { 984 | int index = m_index; 985 | std::stringstream ss; 986 | T value; 987 | 988 | // loop through till we find a non digit 989 | while(isdigit(m_text[m_index])) 990 | { 991 | m_index++; 992 | } 993 | // check for decimal point 994 | if(m_text[m_index] == '.') 995 | { 996 | m_index++; 997 | 998 | // Add the digits after the decimal point 999 | while(isdigit(m_text[m_index])) 1000 | { 1001 | m_index++; 1002 | } 1003 | } 1004 | // check for exponent 1005 | if(m_text[m_index] == 'e' || m_text[m_index] == 'E') 1006 | { 1007 | m_index++; 1008 | if (m_text[m_index] == '+' || m_text[m_index] == '-') 1009 | { 1010 | m_index++; 1011 | } 1012 | 1013 | // Add the rest of the digits 1014 | while(isdigit(m_text[m_index])) 1015 | { 1016 | m_index++; 1017 | } 1018 | } 1019 | 1020 | // Convert from char *to T via stringstream 1021 | char *number = new char[m_index - index +1]; 1022 | memcpy(number, &m_text[index], (m_index - index)*sizeof(char)); 1023 | number[m_index-index] = '\0'; 1024 | ss << number; 1025 | ss >> value; 1026 | delete[] number; 1027 | return value; 1028 | 1029 | } 1030 | 1031 | const char * m_text; 1032 | int m_index; 1033 | }; 1034 | 1035 | } // namespace expr 1036 | 1037 | #endif 1038 | -------------------------------------------------------------------------------- /expressions/expressions.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONS_H 2 | #define EXPRESSIONS_H 3 | 4 | 5 | #include "expressions/AST.h" 6 | #include "expressions/Parser.h" 7 | #include "expressions/Evaluator.h" 8 | #include "expressions/Generator.h" 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /tests.cpp: -------------------------------------------------------------------------------- 1 | // tests.cpp 2 | // g++ tests.cpp -o test -I. `llvm-config --cppflags --ldflags --libs core jit native` -Wall -DUSE_LLVM 3 | #include 4 | #include 5 | #include "math.h" 6 | 7 | #include // for fabs 8 | #include // for epsilon 9 | 10 | 11 | 12 | #ifdef USE_LLVM 13 | typedef expr::LLVMEvaluator Evaluator; 14 | typedef expr::LLVMEvaluator::VariableMap VariableMap; 15 | #else 16 | typedef expr::Evaluator Evaluator; 17 | typedef expr::Evaluator::VariableMap VariableMap; 18 | #endif 19 | 20 | // globals 21 | float pi = 3.41459f; 22 | float x = 0; 23 | float y = 0; 24 | 25 | // EvaluationTest object means we don't need to recompile the expression for every test 26 | class EvaluationTest 27 | { 28 | public: 29 | EvaluationTest(const char *expression) 30 | { 31 | m_vm["x"] = 0; 32 | m_vm["y"] = 0; 33 | m_vm["pi"] = pi; 34 | m_eval = new Evaluator(parser.parse(expression), &m_vm); 35 | } 36 | ~EvaluationTest() 37 | { 38 | delete m_eval; 39 | } 40 | 41 | float evaluate(float x, float y) 42 | { 43 | m_vm["x"] = x; 44 | m_vm["y"] = y; 45 | return m_eval->evaluate(); 46 | } 47 | 48 | private: 49 | expr::Parser parser; 50 | VariableMap m_vm; 51 | Evaluator *m_eval; 52 | }; 53 | 54 | typedef std::map TestMap; 55 | TestMap tests; 56 | 57 | 58 | 59 | float execute(const char *expression) 60 | { 61 | EvaluationTest *t; 62 | if (tests.count(std::string(expression))) 63 | { 64 | t = tests[std::string(expression)]; 65 | } 66 | else 67 | { 68 | t = new EvaluationTest(expression); 69 | tests[std::string(expression)] = t; 70 | } 71 | 72 | return t->evaluate(x,y); 73 | } 74 | 75 | 76 | void assertExp(const char *expression, float result) 77 | { 78 | float a = execute(expression); 79 | if (fabs( a - result) > std::numeric_limits::epsilon()*2048) // ridiculously small difference 80 | { 81 | std::cerr << a << " != " << result << " for " << expression << " where x = "<< x << " and y = " << y << std::endl; 82 | } 83 | } 84 | 85 | 86 | void syntaxErrors(const char *expression) 87 | { 88 | bool error = false; 89 | try 90 | { 91 | execute(expression); 92 | } 93 | catch (expr::ParserException &e) 94 | { 95 | error = true; 96 | } 97 | if (!error) 98 | { 99 | std::cerr << "expression \"" << expression << "\" did not result in a syntax error" << std::endl; 100 | } 101 | } 102 | 103 | 104 | void clone() 105 | { 106 | x = 10; 107 | y = 20; 108 | 109 | expr::Parser parser; 110 | VariableMap vm; 111 | vm["pi"] = pi; 112 | vm["x"] = x; 113 | vm["y"] = y; 114 | 115 | 116 | float result; 117 | expr::ASTNodePtr ast = parser.parse("(x + y) * 10"); 118 | 119 | expr::ASTNodePtr ast2 = ast->clone(); 120 | Evaluator eval(ast2, &vm); 121 | result = eval.evaluate(); 122 | 123 | if (result != 300.0f) 124 | { 125 | std::cerr << "cloned expression did not evaluate correctly" << std::endl; 126 | } 127 | } 128 | 129 | 130 | void test() 131 | { 132 | unsigned int count = 0; 133 | 134 | for (x=-10; x< 10; x+= 0.1f) 135 | { 136 | for (y=-10; y< 10; y+= 0.1f) 137 | { 138 | if (x==0 || y == 0) 139 | continue; 140 | assertExp("(y + x)", (y + x)); 141 | assertExp("2 * (y + x)", 2 * (y + x)); 142 | assertExp("(2 * y + 2 * x)", (2 * y + 2 * x)); 143 | assertExp("(y + x / y) * (x - y / x)", (y + x / y) * (x - y / x)); 144 | assertExp("x / ((x + y) * (x - y)) / y", x / ((x + y) * (x - y)) / y); 145 | assertExp("1 - ((x * y) + (y / x)) - 3", 1 - ((x * y) + (y / x)) - 3); 146 | assertExp("sin(2 * x) + cos(pi / y)", sin(2 * x) + cos(pi / y)); 147 | assertExp("1 - sin(2 * x) + cos(pi / y)", 1 - sin(2 * x) + cos(pi / y)); 148 | assertExp("sqrt(1 - sin(2 * x) + cos(pi / y) / 3)", sqrt(1 - sin(2 * x) + cos(pi / y) / 3)); 149 | assertExp("(x^2 / sin(2 * pi / y)) -x / 2", (pow(x,2) / sin(2 * pi / y)) -x / 2); 150 | assertExp("x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y", x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y); 151 | assertExp("min(4,8) < max(4,8) && 10 % 4 == 2 ? (ceil(cos(60*pi/180) + sin(30*pi/180) + tan(45*pi/180)) + sqrt(floor(16.5)) + log2(16)) * log10(100) : 0", std::min(4,8) < std::max(4,8) && 10 % 4 == 2 ? (ceil(cos(60*pi/180) + sin(30*pi/180) + tan(45*pi/180)) + sqrt(floor(16.5)) + log2(16)) * log10(100) : 0); 152 | count+=12; 153 | } 154 | } 155 | 156 | syntaxErrors("x++"); count++; 157 | syntaxErrors("+"); count++; 158 | syntaxErrors("x y"); count++; 159 | syntaxErrors("sin x"); count++; 160 | syntaxErrors("min(x)"); count++; 161 | syntaxErrors("min(,1)"); count++; 162 | syntaxErrors(")))))))+x"); count++; 163 | syntaxErrors("x % "); count++; 164 | syntaxErrors("%x"); count++; 165 | syntaxErrors("1-*2"); count++; 166 | 167 | clone(); count++; 168 | 169 | std::cout << "Ran " << count << " tests successfully" << std::endl;; 170 | } 171 | 172 | int main() 173 | { 174 | test(); 175 | for(TestMap::iterator it=tests.begin(); it!=tests.end(); ++it) 176 | { 177 | delete it->second; 178 | } 179 | tests.clear(); 180 | return 0; 181 | } 182 | 183 | 184 | 185 | 186 | 187 | --------------------------------------------------------------------------------