Commit 259cb45a authored by Peter Eastman's avatar Peter Eastman
Browse files

Creating expression parsing library for use in custom forces

parent 65452671
#ifndef LEPTON_EXCEPTION_H_
#define LEPTON_EXCEPTION_H_
/* -------------------------------------------------------------------------- *
* 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 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 <exception>
#include <string>
namespace Lepton {
/**
* This class is used for all exceptions thrown by Lepton.
*/
class Exception : public std::exception {
public:
Exception(const std::string& message) : message(message) {
}
~Exception() throw() {
}
const char* what() const throw() {
return message.c_str();
}
private:
std::string message;
};
} // namespace Lepton
#endif /*LEPTON_EXCEPTION_H_*/
#ifndef LEPTON_EXPRESSION_TREE_NODE_H_
#define LEPTON_EXPRESSION_TREE_NODE_H_
/* -------------------------------------------------------------------------- *
* 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 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 "windowsIncludes.h"
#include <string>
#include <vector>
namespace Lepton {
class Operation;
class LEPTON_EXPORT ExpressionTreeNode {
public:
ExpressionTreeNode(Operation* operation, const std::vector<ExpressionTreeNode>& children);
ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child1, const ExpressionTreeNode& child2);
ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child);
ExpressionTreeNode(Operation* operation);
ExpressionTreeNode(const ExpressionTreeNode& node);
ExpressionTreeNode();
~ExpressionTreeNode();
ExpressionTreeNode& operator=(const ExpressionTreeNode& node);
const Operation& getOperation() const;
const std::vector<ExpressionTreeNode>& getChildren() const;
private:
Operation* operation;
std::vector<ExpressionTreeNode> children;
};
} // namespace Lepton
#endif /*LEPTON_EXPRESSION_TREE_NODE_H_*/
#ifndef LEPTON_OPERATION_H_
#define LEPTON_OPERATION_H_
/* -------------------------------------------------------------------------- *
* 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 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 "windowsIncludes.h"
#include <cmath>
#include <map>
#include <string>
#include <vector>
#include <sstream>
namespace Lepton {
class LEPTON_EXPORT Operation {
public:
enum Id {CONSTANT, VARIABLE, CUSTOM, ADD, SUBTRACT, MULTIPLY, DIVIDE, POWER, NEGATE, SQRT, EXP, LOG,
SIN, COS, SEC, CSC, TAN, COT, ASIN, ACOS, ATAN};
virtual std::string getName() const = 0;
virtual Id getId() const = 0;
virtual int getNumArguments() const = 0;
virtual Operation* clone() const = 0;
virtual double evaluate(double* args, const std::map<std::string, double>& variables) const = 0;
class Constant;
class Variable;
class Custom;
class Add;
class Subtract;
class Multiply;
class Divide;
class Power;
class Negate;
class Sqrt;
class Exp;
class Log;
class Sin;
class Cos;
class Sec;
class Csc;
class Tan;
class Cot;
class Asin;
class Acos;
class Atan;
};
class Operation::Constant : public Operation {
public:
Constant(double value) : value(value) {
}
std::string getName() const {
std::stringstream name;
name << value;
return name.str();
}
Id getId() const {
return CONSTANT;
}
int getNumArguments() const {
return 0;
}
Operation* clone() const {
return new Constant(value);
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return value;
}
private:
double value;
};
class Operation::Variable : public Operation {
public:
Variable(const std::string& name) : name(name) {
}
std::string getName() const {
return name;
}
Id getId() const {
return VARIABLE;
}
int getNumArguments() const {
return 0;
}
Operation* clone() const {
return new Variable(name);
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
std::map<std::string, double>::const_iterator iter = variables.find(name);
if (iter == variables.end())
throw std::exception();
return iter->second;
}
private:
std::string name;
};
class Operation::Custom : public Operation {
public:
Custom(const std::string& name, int arguments) : name(name), arguments(arguments) {
}
std::string getName() const {
return name;
}
Id getId() const {
return CUSTOM;
}
int getNumArguments() const {
return arguments;
}
Operation* clone() const {
return new Custom(name, arguments);
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return 0.0;
}
private:
std::string name;
int arguments;
};
class Operation::Add : public Operation {
public:
Add() {
}
std::string getName() const {
return "+";
}
Id getId() const {
return ADD;
}
int getNumArguments() const {
return 2;
}
Operation* clone() const {
return new Add();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return args[0]+args[1];
}
};
class Operation::Subtract : public Operation {
public:
Subtract() {
}
std::string getName() const {
return "-";
}
Id getId() const {
return SUBTRACT;
}
int getNumArguments() const {
return 2;
}
Operation* clone() const {
return new Subtract();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return args[0]-args[1];
}
};
class Operation::Multiply : public Operation {
public:
Multiply() {
}
std::string getName() const {
return "*";
}
Id getId() const {
return MULTIPLY;
}
int getNumArguments() const {
return 2;
}
Operation* clone() const {
return new Multiply();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return args[0]*args[1];
}
};
class Operation::Divide : public Operation {
public:
Divide() {
}
std::string getName() const {
return "/";
}
Id getId() const {
return DIVIDE;
}
int getNumArguments() const {
return 2;
}
Operation* clone() const {
return new Divide();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return args[0]/args[1];
}
};
class Operation::Power : public Operation {
public:
Power() {
}
std::string getName() const {
return "^";
}
Id getId() const {
return POWER;
}
int getNumArguments() const {
return 2;
}
Operation* clone() const {
return new Power();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::pow(args[0], args[1]);
}
};
class Operation::Negate : public Operation {
public:
Negate() {
}
std::string getName() const {
return "-";
}
Id getId() const {
return NEGATE;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Negate();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return -args[0];
}
};
class Operation::Sqrt : public Operation {
public:
Sqrt() {
}
std::string getName() const {
return "sqrt";
}
Id getId() const {
return SQRT;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Sqrt();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::sqrt(args[0]);
}
};
class Operation::Exp : public Operation {
public:
Exp() {
}
std::string getName() const {
return "exp";
}
Id getId() const {
return EXP;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Exp();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::exp(args[0]);
}
};
class Operation::Log : public Operation {
public:
Log() {
}
std::string getName() const {
return "log";
}
Id getId() const {
return SQRT;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Log();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::log(args[0]);
}
};
class Operation::Sin : public Operation {
public:
Sin() {
}
std::string getName() const {
return "sin";
}
Id getId() const {
return LOG;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Sin();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::sin(args[0]);
}
};
class Operation::Cos : public Operation {
public:
Cos() {
}
std::string getName() const {
return "cos";
}
Id getId() const {
return COS;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Cos();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::cos(args[0]);
}
};
class Operation::Sec : public Operation {
public:
Sec() {
}
std::string getName() const {
return "sec";
}
Id getId() const {
return SEC;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Sec();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return 1.0/std::cos(args[0]);
}
};
class Operation::Csc : public Operation {
public:
Csc() {
}
std::string getName() const {
return "csc";
}
Id getId() const {
return CSC;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Csc();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return 1.0/std::sin(args[0]);
}
};
class Operation::Tan : public Operation {
public:
Tan() {
}
std::string getName() const {
return "tan";
}
Id getId() const {
return TAN;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Tan();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::tan(args[0]);
}
};
class Operation::Cot : public Operation {
public:
Cot() {
}
std::string getName() const {
return "cot";
}
Id getId() const {
return COT;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Cot();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return 1.0/std::tan(args[0]);
}
};
class Operation::Asin : public Operation {
public:
Asin() {
}
std::string getName() const {
return "asin";
}
Id getId() const {
return ASIN;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Asin();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::asin(args[0]);
}
};
class Operation::Acos : public Operation {
public:
Acos() {
}
std::string getName() const {
return "acos";
}
Id getId() const {
return ACOS;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Acos();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::acos(args[0]);
}
};
class Operation::Atan : public Operation {
public:
Atan() {
}
std::string getName() const {
return "atan";
}
Id getId() const {
return ATAN;
}
int getNumArguments() const {
return 1;
}
Operation* clone() const {
return new Atan();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::atan(args[0]);
}
};
} // namespace Lepton
#endif /*LEPTON_OPERATION_H_*/
#ifndef LEPTON_PARSED_EXPRESSION_H_
#define LEPTON_PARSED_EXPRESSION_H_
/* -------------------------------------------------------------------------- *
* 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 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 "ExpressionTreeNode.h"
#include "windowsIncludes.h"
#include <map>
#include <string>
namespace Lepton {
class LEPTON_EXPORT ParsedExpression {
public:
ParsedExpression(ExpressionTreeNode rootNode);
const ExpressionTreeNode& getRootNode() const;
double evaluate() const;
double evaluate(const std::map<std::string, double>& variables) const;
private:
double evaluate(const ExpressionTreeNode& node, const std::map<std::string, double>& variables) const;
ExpressionTreeNode rootNode;
};
} // namespace Lepton
#endif /*LEPTON_PARSED_EXPRESSION_H_*/
#ifndef LEPTON_PARSER_H_
#define LEPTON_PARSER_H_
/* -------------------------------------------------------------------------- *
* 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 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 "windowsIncludes.h"
#include <string>
#include <vector>
namespace Lepton {
class ExpressionTreeNode;
class Operation;
class ParsedExpression;
class ParseToken;
/**
* This class provides the main interface for parsing expressions.
*/
class LEPTON_EXPORT Parser {
public:
static ParsedExpression parse(std::string expression);
private:
static std::vector<ParseToken> tokenize(std::string expression);
static ParseToken getNextToken(std::string expression, int start);
static ExpressionTreeNode parsePrecedence(const std::vector<ParseToken>& tokens, int& pos, int precedence);
static Operation* getOperatorOperation(const std::string& name);
static Operation* getFunctionOperation(const std::string& name, int arguments);
};
} // namespace Lepton
#endif /*LEPTON_PARSER_H_*/
#ifndef LEPTON_WINDOW_INCLUDE_H_
#define LEPTON_WINDOW_INCLUDE_H_
/*
* Shared libraries are messy in Visual Studio. We have to distinguish three
* cases:
* (1) this header is being used to build the Lepton shared library
* (dllexport)
* (2) this header is being used by a *client* of the Lepton shared
* library (dllimport)
* (3) we are building the Lepton static library, or the client is
* being compiled with the expectation of linking with the
* Lepton static library (nothing special needed)
* In the CMake script for building this library, we define one of the symbols
* Lepton_BUILDING_{SHARED|STATIC}_LIBRARY
* Client code normally has no special symbol defined, in which case we'll
* assume it wants to use the shared library. However, if the client defines
* the symbol LEPTON_USE_STATIC_LIBRARIES we'll suppress the dllimport so
* that the client code can be linked with static libraries. Note that
* the client symbol is not library dependent, while the library symbols
* affect only the Lepton library, meaning that other libraries can
* be clients of this one. However, we are assuming all-static or all-shared.
*/
#ifdef _MSC_VER
// We don't want to hear about how sprintf is "unsafe".
#pragma warning(disable:4996)
#if defined(LEPTON_BUILDING_SHARED_LIBRARY)
#define LEPTON_EXPORT __declspec(dllexport)
// Keep MS VC++ quiet about lack of dll export of private members.
#pragma warning(disable:4251)
#elif defined(LEPTON_BUILDING_STATIC_LIBRARY) || defined(LEPTON_USE_STATIC_LIBRARIES)
#define LEPTON_EXPORT
#else
#define LEPTON_EXPORT __declspec(dllimport) // i.e., a client of a shared library
#endif
#else
#define LEPTON_EXPORT // Linux, Mac
#endif
#endif // LEPTON_WINDOW_INCLUDE_H_
/* -------------------------------------------------------------------------- *
* 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 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 "ExpressionTreeNode.h"
#include "Exception.h"
#include "Operation.h"
using namespace Lepton;
using namespace std;
ExpressionTreeNode::ExpressionTreeNode(Operation* operation, const vector<ExpressionTreeNode>& children) : operation(operation), children(children) {
if (operation->getNumArguments() != children.size())
throw Exception("Parse error: wrong number of arguments to function");
}
ExpressionTreeNode::ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child1, const ExpressionTreeNode& child2) : operation(operation) {
children.push_back(child1);
children.push_back(child2);
if (operation->getNumArguments() != children.size())
throw Exception("Parse error: wrong number of arguments to function");
}
ExpressionTreeNode::ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child) : operation(operation) {
children.push_back(child);
if (operation->getNumArguments() != children.size())
throw Exception("Parse error: wrong number of arguments to function");
}
ExpressionTreeNode::ExpressionTreeNode(Operation* operation) : operation(operation) {
if (operation->getNumArguments() != children.size())
throw Exception("Parse error: wrong number of arguments to function");
}
ExpressionTreeNode::ExpressionTreeNode(const ExpressionTreeNode& node) : operation(node.getOperation().clone()), children(node.getChildren()) {
}
ExpressionTreeNode::ExpressionTreeNode() : operation(NULL) {
}
ExpressionTreeNode::~ExpressionTreeNode() {
if (operation != NULL)
delete operation;
}
ExpressionTreeNode& ExpressionTreeNode::operator=(const ExpressionTreeNode& node) {
if (operation != NULL)
delete operation;
operation = node.getOperation().clone();
children = node.getChildren();
return *this;
}
const Operation& ExpressionTreeNode::getOperation() const {
return *operation;
}
const vector<ExpressionTreeNode>& ExpressionTreeNode::getChildren() const {
return children;
}
/* -------------------------------------------------------------------------- *
* 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 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 "ParsedExpression.h"
#include "Operation.h"
#include <vector>
using namespace Lepton;
using namespace std;
ParsedExpression::ParsedExpression(ExpressionTreeNode rootNode) : rootNode(rootNode) {
}
const ExpressionTreeNode& ParsedExpression::getRootNode() const {
return rootNode;
}
double ParsedExpression::evaluate() const {
return evaluate(rootNode, map<string, double>());
}
double ParsedExpression::evaluate(const std::map<std::string, double>& variables) const {
return evaluate(rootNode, variables);
}
double ParsedExpression::evaluate(const ExpressionTreeNode& node, const map<string, double>& variables) const {
vector<double> args(node.getChildren().size());
for (int i = 0; i < args.size(); i++)
args[i] = evaluate(node.getChildren()[i], variables);
return node.getOperation().evaluate(&args[0], variables);
}
/* -------------------------------------------------------------------------- *
* 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 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 "Exception.h"
#include "ExpressionTreeNode.h"
#include "Operation.h"
#include "ParsedExpression.h"
#include <iostream>
using namespace Lepton;
using namespace std;
static const string Digits = "0123456789";
static const 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(string text, Type type) : text(text), type(type) {
}
const string& getText() const {
return text;
}
Type getType() const {
return type;
}
private:
string text;
Type type;
};
ParseToken Parser::getNextToken(string expression, int 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) != string::npos)
return ParseToken(string(1, c), ParseToken::Operator);
if (c == ' ') {
// White space
for (int pos = start+1; pos < expression.size(); pos++) {
if (expression[pos] != ' ')
return ParseToken(expression.substr(start, pos-start), ParseToken::Whitespace);
}
return ParseToken(expression.substr(start, string::npos), ParseToken::Whitespace);
}
if (c == '.' || Digits.find(c) != string::npos) {
// A number
bool foundDecimal = (c == '.');
bool foundExp = false;
int pos;
for (pos = start+1; pos < expression.size(); pos++) {
c = expression[pos];
if (Digits.find(c) != 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] == '-')
pos++;
continue;
}
break;
}
return ParseToken(expression.substr(start, pos-start), ParseToken::Number);
}
// A variable, function, or left parenthesis
for (int 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) != string::npos || c == ',' || c == ')')
return ParseToken(expression.substr(start, pos-start), ParseToken::Variable);
}
return ParseToken(expression.substr(start, string::npos), ParseToken::Variable);
}
vector<ParseToken> Parser::tokenize(string expression) {
vector<ParseToken> tokens;
int 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(string expression) {
vector<ParseToken> tokens = tokenize(expression);
int pos = 0;
ExpressionTreeNode result = parsePrecedence(tokens, pos, 0);
if (pos != tokens.size())
throw Exception("Parse error: unexpected text at end of expression");
return ParsedExpression(result);
}
ExpressionTreeNode Parser::parsePrecedence(const vector<ParseToken>& tokens, int& pos, 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;
stringstream(token.getText()) >> value;
result = ExpressionTreeNode(new Operation::Constant(value));
pos++;
}
else if (token.getType() == ParseToken::Variable) {
Operation* op = new Operation::Variable(token.getText());
result = ExpressionTreeNode(op);
pos++;
}
else if (token.getType() == ParseToken::LeftParen) {
pos++;
result = parsePrecedence(tokens, pos, 0);
if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen)
throw Exception("Parse error: unbalanced parentheses");
pos++;
}
else if (token.getType() == ParseToken::Function) {
pos++;
vector<ExpressionTreeNode> args;
bool moreArgs;
do {
args.push_back(parsePrecedence(tokens, pos, 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++;
result = ExpressionTreeNode(getFunctionOperation(token.getText(), args.size()), args);
}
else if (token.getType() == ParseToken::Operator && token.getText() == "-") {
pos++;
ExpressionTreeNode toNegate = parsePrecedence(tokens, pos, 2);
result = ExpressionTreeNode(new Operation::Negate(), toNegate);
}
else
throw Exception("Parse error: unexpected token");
// Now deal with the next binary operator.
while (pos < tokens.size() && tokens[pos].getType() == ParseToken::Operator) {
token = tokens[pos];
int op = Operators.find(token.getText());
int opPrecedence = Precedence[op];
if (opPrecedence < precedence)
return result;
pos++;
ExpressionTreeNode arg = parsePrecedence(tokens, pos, LeftAssociative[op] ? opPrecedence+1 : opPrecedence);
result = ExpressionTreeNode(getOperatorOperation(token.getText()), result, arg);
}
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, int arguments) {
static map<string, Operation::Id> opMap;
if (opMap.size() == 0) {
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;
}
string trimmed = name.substr(0, name.size()-1);
map<string, Operation::Id>::const_iterator iter = opMap.find(trimmed);
if (iter == opMap.end())
return new Operation::Custom(trimmed, arguments);
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();
default:
throw Exception("Parse error: unknown function");
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment