/* -------------------------------------------------------------------------- * * Lepton * * -------------------------------------------------------------------------- * * This is part of the Lepton expression parser originating from * * Simbios, the NIH National Center for Physics-Based Simulation of * * Biological Structures at Stanford, funded under the NIH Roadmap for * * Medical Research, grant U54 GM072970. See https://simtk.org. * * * * Portions copyright (c) 2009-2013 Stanford University and the Authors. * * Authors: Peter Eastman * * Contributors: * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the "Software"), * * to deal in the Software without restriction, including without limitation * * the rights to use, copy, modify, merge, publish, distribute, sublicense, * * and/or sell copies of the Software, and to permit persons to whom the * * Software is furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * * USE OR OTHER DEALINGS IN THE SOFTWARE. * * -------------------------------------------------------------------------- */ #include "Parser.h" #include "CustomFunction.h" #include "ExpressionTreeNode.h" #include "Operation.h" #include "ParsedExpression.h" #include #include #include using namespace Lepton; static const std::string Digits = "0123456789"; static const std::string Operators = "+-*/^"; static const bool LeftAssociative[] = { true, true, true, true, false }; static const int Precedence[] = { 0, 0, 1, 1, 3 }; static const Operation::Id OperationId[] = { Operation::ADD, Operation::SUBTRACT, Operation::MULTIPLY, Operation::DIVIDE, Operation::POWER }; class Lepton::ParseToken { public: enum Type { Number, Operator, Variable, Function, LeftParen, RightParen, Comma, Whitespace }; ParseToken(std::string text, Type type) : text(text), type(type) { } const std::string& getText() const { return text; } Type getType() const { return type; } private: std::string text; Type type; }; std::string Parser::trim(const std::string& expression) { // Remove leading and trailing spaces. size_t start, end; for (start = 0; start < expression.size() && isspace(expression[start]); ++start) { } for (end = expression.size() - 1; end > start && isspace(expression[end]); end--) { } if (start == end && isspace(expression[end])) { return ""; } return expression.substr(start, end - start + 1); } ParseToken Parser::getNextToken(const std::string& expression, size_t start) { char c = expression[start]; if (c == '(') { return ParseToken("(", ParseToken::LeftParen); } if (c == ')') { return ParseToken(")", ParseToken::RightParen); } if (c == ',') { return ParseToken(",", ParseToken::Comma); } if (Operators.find(c) != std::string::npos) { return ParseToken(std::string(1, c), ParseToken::Operator); } if (isspace(c)) { // White space for (size_t pos = start + 1; pos < expression.size(); ++pos) { if (!isspace(expression[pos])) { return ParseToken(expression.substr(start, pos - start), ParseToken::Whitespace); } } return ParseToken(expression.substr(start, std::string::npos), ParseToken::Whitespace); } if (c == '.' || Digits.find(c) != std::string::npos) { // A number bool foundDecimal = (c == '.'); bool foundExp = false; size_t pos = start + 1; for (; pos < expression.size(); ++pos) { c = expression[pos]; if (Digits.find(c) != std::string::npos) { continue; } if (c == '.' && !foundDecimal) { foundDecimal = true; continue; } if ((c == 'e' || c == 'E') && !foundExp) { foundExp = true; if (pos < expression.size() - 1 && (expression[pos + 1] == '-' || expression[pos + 1] == '+')) { pos++; } continue; } break; } return ParseToken(expression.substr(start, pos - start), ParseToken::Number); } // A variable, function, or left parenthesis for (size_t pos = start; pos < expression.size(); ++pos) { c = expression[pos]; if (c == '(') { return ParseToken(expression.substr(start, pos - start + 1), ParseToken::Function); } if (Operators.find(c) != std::string::npos || c == ',' || c == ')' || isspace(c)) { return ParseToken(expression.substr(start, pos - start), ParseToken::Variable); } } return ParseToken(expression.substr(start, std::string::npos), ParseToken::Variable); } std::vector Parser::tokenize(const std::string& expression) { std::vector tokens; size_t pos = 0; while (pos < expression.size()) { ParseToken token = getNextToken(expression, pos); if (token.getType() != ParseToken::Whitespace) { tokens.push_back(token); } pos += token.getText().size(); } return tokens; } ParsedExpression Parser::parse(const std::string& expression) { return parse(expression, std::map()); } ParsedExpression Parser::parse(const std::string& expression, const std::map& customFunctions) { // First split the expression into subexpressions. std::string primaryExpression = expression; std::vector subexpressions; while (true) { std::string::size_type pos = primaryExpression.find_last_of(';'); if (pos == std::string::npos) { break; } std::string sub = trim(primaryExpression.substr(pos + 1)); if (!sub.empty()) { subexpressions.push_back(sub); } primaryExpression = primaryExpression.substr(0, pos); } // Parse the subexpressions. std::map subexpDefs; for (size_t i = 0; i < subexpressions.size(); ++i) { size_t equalsPos = subexpressions[i].find('='); if (equalsPos == std::string::npos) { throw Exception("Parse error: subexpression does not specify a name"); } std::string name = trim(subexpressions[i].substr(0, equalsPos)); if (name.empty()) { throw Exception("Parse error: subexpression does not specify a name"); } std::vector tokens = tokenize(subexpressions[i].substr(equalsPos + 1)); size_t pos = 0; subexpDefs[name] = parsePrecedence(tokens, pos, customFunctions, subexpDefs, 0); if (pos != tokens.size()) { throw Exception("Parse error: unexpected text at end of subexpression: " + tokens[pos].getText()); } } // Now parse the primary expression. std::vector tokens = tokenize(primaryExpression); size_t pos = 0; ExpressionTreeNode result = parsePrecedence(tokens, pos, customFunctions, subexpDefs, 0); if (pos != tokens.size()) { throw Exception("Parse error: unexpected text at end of expression: " + tokens[pos].getText()); } return ParsedExpression(result); } ExpressionTreeNode Parser::parsePrecedence(const std::vector& tokens, size_t& pos, const std::map& customFunctions, const std::map& subexpressionDefs, int precedence) { if (pos == tokens.size()) { throw Exception("Parse error: unexpected end of expression"); } // Parse the next value (number, variable, function, parenthesized expression) ParseToken token = tokens[pos]; ExpressionTreeNode result; if (token.getType() == ParseToken::Number) { double value; std::stringstream(token.getText()) >> value; result = ExpressionTreeNode(new Operation::Constant(value)); pos++; } else if (token.getType() == ParseToken::Variable) { const auto subexp = subexpressionDefs.find(token.getText()); if (subexp == subexpressionDefs.end()) { Operation* op = new Operation::Variable(token.getText()); result = ExpressionTreeNode(op); } else { result = subexp->second; } pos++; } else if (token.getType() == ParseToken::LeftParen) { pos++; result = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 0); if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen) { throw Exception("Parse error: unbalanced parentheses"); } pos++; } else if (token.getType() == ParseToken::Function) { pos++; std::vector args; bool moreArgs; do { args.push_back(parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 0)); moreArgs = (pos < tokens.size() && tokens[pos].getType() == ParseToken::Comma); if (moreArgs) { pos++; } } while (moreArgs); if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen) { throw Exception("Parse error: unbalanced parentheses"); } pos++; Operation* op = getFunctionOperation(token.getText(), customFunctions); try { result = ExpressionTreeNode(op, args); } catch (...) { delete op; throw; } } else if (token.getType() == ParseToken::Operator && token.getText() == "-") { pos++; const ExpressionTreeNode toNegate = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 2); result = ExpressionTreeNode(new Operation::Negate(), toNegate); } else { throw Exception("Parse error: unexpected token: " + token.getText()); } // Now deal with the next binary operator. while (pos < tokens.size() && tokens[pos].getType() == ParseToken::Operator) { token = tokens[pos]; int opIndex = int(Operators.find(token.getText())); int opPrecedence = Precedence[opIndex]; if (opPrecedence < precedence) { return result; } pos++; ExpressionTreeNode arg = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, LeftAssociative[opIndex] ? opPrecedence + 1 : opPrecedence); Operation* op = getOperatorOperation(token.getText()); try { result = ExpressionTreeNode(op, result, arg); } catch (...) { delete op; throw; } } return result; } Operation* Parser::getOperatorOperation(const std::string& name) { switch (OperationId[Operators.find(name)]) { case Operation::ADD: return new Operation::Add(); case Operation::SUBTRACT: return new Operation::Subtract(); case Operation::MULTIPLY: return new Operation::Multiply(); case Operation::DIVIDE: return new Operation::Divide(); case Operation::POWER: return new Operation::Power(); default: throw Exception("Parse error: unknown operator"); } } Operation* Parser::getFunctionOperation(const std::string& name, const std::map& customFunctions) { static std::map opMap; if (opMap.empty()) { opMap["sqrt"] = Operation::SQRT; opMap["exp"] = Operation::EXP; opMap["log"] = Operation::LOG; opMap["sin"] = Operation::SIN; opMap["cos"] = Operation::COS; opMap["sec"] = Operation::SEC; opMap["csc"] = Operation::CSC; opMap["tan"] = Operation::TAN; opMap["cot"] = Operation::COT; opMap["asin"] = Operation::ASIN; opMap["acos"] = Operation::ACOS; opMap["atan"] = Operation::ATAN; opMap["sinh"] = Operation::SINH; opMap["cosh"] = Operation::COSH; opMap["tanh"] = Operation::TANH; opMap["erf"] = Operation::ERF; opMap["erfc"] = Operation::ERFC; opMap["step"] = Operation::STEP; opMap["delta"] = Operation::DELTA; opMap["square"] = Operation::SQUARE; opMap["cube"] = Operation::CUBE; opMap["recip"] = Operation::RECIPROCAL; opMap["min"] = Operation::MIN; opMap["max"] = Operation::MAX; opMap["abs"] = Operation::ABS; } const std::string trimmed = name.substr(0, name.size() - 1); // First check custom functions. const auto custom = customFunctions.find(trimmed); if (custom != customFunctions.end()) { return new Operation::Custom(trimmed, custom->second->clone()); } // Now try standard functions. const auto iter = opMap.find(trimmed); if (iter == opMap.end()) { throw Exception("Parse error: unknown function: " + trimmed); } switch (iter->second) { case Operation::SQRT: return new Operation::Sqrt(); case Operation::EXP: return new Operation::Exp(); case Operation::LOG: return new Operation::Log(); case Operation::SIN: return new Operation::Sin(); case Operation::COS: return new Operation::Cos(); case Operation::SEC: return new Operation::Sec(); case Operation::CSC: return new Operation::Csc(); case Operation::TAN: return new Operation::Tan(); case Operation::COT: return new Operation::Cot(); case Operation::ASIN: return new Operation::Asin(); case Operation::ACOS: return new Operation::Acos(); case Operation::ATAN: return new Operation::Atan(); case Operation::SINH: return new Operation::Sinh(); case Operation::COSH: return new Operation::Cosh(); case Operation::TANH: return new Operation::Tanh(); case Operation::ERF: return new Operation::Erf(); case Operation::ERFC: return new Operation::Erfc(); case Operation::STEP: return new Operation::Step(); case Operation::DELTA: return new Operation::Delta(); case Operation::SQUARE: return new Operation::Square(); case Operation::CUBE: return new Operation::Cube(); case Operation::RECIPROCAL: return new Operation::Reciprocal(); case Operation::MIN: return new Operation::Min(); case Operation::MAX: return new Operation::Max(); case Operation::ABS: return new Operation::Abs(); default: throw Exception("Parse error: unknown function"); } }