From ce6469cdd74bdebb1ebc7a5dc137a5184b07680b Mon Sep 17 00:00:00 2001 From: Antonin Portelli Date: Thu, 6 Feb 2014 18:07:27 +0000 Subject: [PATCH] general cleaning/consolidation of the math interpreter --- examples/Makefile.am | 15 +- examples/exCompiledDoubleFunction.cpp | 31 ++ examples/exMathInterpreter.cpp | 75 ++++ latan/CompiledFunction.cpp | 69 ++++ latan/CompiledFunction.hpp | 39 ++ latan/Exceptions.cpp | 21 +- latan/Exceptions.hpp | 21 +- latan/Function.cpp | 4 +- latan/Function.hpp | 4 +- latan/Makefile.am | 46 +-- latan/MathInterpreter.cpp | 534 ++++++++++++++++++++++++++ latan/MathInterpreter.hpp | 251 ++++++++++++ latan/MathLexer.lpp | 8 +- latan/MathParser.ypp | 6 +- 14 files changed, 1067 insertions(+), 57 deletions(-) create mode 100644 examples/exCompiledDoubleFunction.cpp create mode 100644 examples/exMathInterpreter.cpp create mode 100644 latan/CompiledFunction.cpp create mode 100644 latan/CompiledFunction.hpp create mode 100644 latan/MathInterpreter.cpp create mode 100644 latan/MathInterpreter.hpp diff --git a/examples/Makefile.am b/examples/Makefile.am index 9c5d47c..34353ed 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -14,11 +14,16 @@ if CXX_INTEL endif endif -noinst_PROGRAMS = \ - exMathCompiler +noinst_PROGRAMS = \ + exMathInterpreter \ + exCompiledDoubleFunction -exMathCompiler_SOURCES = exMathCompiler.cpp -exMathCompiler_CFLAGS = -g -O2 -exMathCompiler_LDFLAGS = -L../latan/.libs -llatan +exMathInterpreter_SOURCES = exMathInterpreter.cpp +exMathInterpreter_CFLAGS = -g -O2 +exMathInterpreter_LDFLAGS = -L../latan/.libs -llatan + +exCompiledDoubleFunction_SOURCES = exCompiledDoubleFunction.cpp +exCompiledDoubleFunction_CFLAGS = -g -O2 +exCompiledDoubleFunction_LDFLAGS = -L../latan/.libs -llatan ACLOCAL_AMFLAGS = -I .buildutils/m4 diff --git a/examples/exCompiledDoubleFunction.cpp b/examples/exCompiledDoubleFunction.cpp new file mode 100644 index 0000000..300095e --- /dev/null +++ b/examples/exCompiledDoubleFunction.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +using namespace std; +using namespace Latan; + +int main(int argc, char* argv[]) +{ + string source; + + if (argc != 2) + { + cerr << "usage: " << argv[0] << " " << endl; + + return EXIT_FAILURE; + } + source = argv[1]; + + CompiledDoubleFunction f(1, source); + + cout << "-- Program:" << endl << f << endl; + cout << "-- Values:" << endl; + for (double x = 0.0; x < 10.0; x += 0.5) + { + cout << "f(" << right << setw(6) << strFrom(x) << ")= " + << f(x) << endl; + } + + return EXIT_SUCCESS; +} diff --git a/examples/exMathInterpreter.cpp b/examples/exMathInterpreter.cpp new file mode 100644 index 0000000..42df050 --- /dev/null +++ b/examples/exMathInterpreter.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +using namespace std; +using namespace Latan; + +int main(int argc, char* argv[]) +{ + string source; + + if (argc != 2) + { + cerr << "usage: " << argv[0] << " " << endl; + + return EXIT_FAILURE; + } + source = argv[1]; + + MathInterpreter interpreter(source); + RunContext context; + + cout << "-- Source code:" << endl << source << endl << endl; + interpreter.compile(); + cout << "-- Abstract Syntax Tree:" << endl; + if (interpreter.getAST()) + { + cout << *interpreter.getAST() << endl; + } + else + { + cout << "" << endl << endl; + } + cout << "-- Program:" << endl << interpreter << endl; + StdMath::addStdMathFunc(context.fTable); + interpreter(context); + if (!context.dStack.empty()) + { + cout << "-- Result: " << context.dStack.top() << endl; + } + + return EXIT_SUCCESS; +} + +/*int main(void) +{ + ASCIIFile F; + DMat A,B; + + F.Open("foo.boot",FileMode::Read); + A = F.Read("bla"); + B = F.Read("bli"); + cout << A << endl; + cout << B << endl; + cout << A*B << endl; + + return EXIT_SUCCESS; +}*/ + +/* +int main(void) +{ + DMat m(2,2); + + m(0,6) = 3; + m(1,0) = 2.5; + m(0,1) = -1; + m(1,1) = m(1,0) + m(0,1); + cout << "Here is the matrix m:\n" << m << endl; + DVec v(2); + v(0) = 4; + v(1) = v(0) - 1; + cout << "Here is the vector v:\n" << v << endl; +} +*/ diff --git a/latan/CompiledFunction.cpp b/latan/CompiledFunction.cpp new file mode 100644 index 0000000..5f4d589 --- /dev/null +++ b/latan/CompiledFunction.cpp @@ -0,0 +1,69 @@ +#include +#include +#include + +using namespace std; +using namespace Latan; + +/****************************************************************************** + * Compiled double function implementation * + ******************************************************************************/ +// constructor/destructor ////////////////////////////////////////////////////// +CompiledDoubleFunction::CompiledDoubleFunction(const unsigned nArg) +: DoubleFunction(nArg) +{} + +CompiledDoubleFunction::CompiledDoubleFunction(const unsigned nArg, + const string &code) +: DoubleFunction(nArg) +{ + setCode(code); +} + +CompiledDoubleFunction::~CompiledDoubleFunction(void) +{} + +// access ////////////////////////////////////////////////////////////////////// +void CompiledDoubleFunction::setCode(const string &code) +{ + interpreter_.setCode(code); + StdMath::addStdMathFunc(context_.fTable); +} + +// function call /////////////////////////////////////////////////////////////// +double CompiledDoubleFunction::operator()(vector &arg) +{ + double result; + + if (arg.size() != getNArg()) + { + LATAN_ERROR(Size, "function argument vector has a wrong size (got " + + strFrom(arg.size()) + ", expected " + strFrom(getNArg()) + + ")"); + } + for (unsigned int i = 0; i < getNArg(); ++i) + { + context_.vTable["x_" + strFrom(i)] = arg[i]; + } + interpreter_(context_); + if (!context_.dStack.empty()) + { + result = context_.dStack.top(); + context_.dStack.pop(); + } + else + { + result = 0.0; + LATAN_ERROR(Program, "program execution resulted in an empty stack"); + } + + return result; +} + +// IO ////////////////////////////////////////////////////////////////////////// +ostream &Latan::operator<<(ostream &out, CompiledDoubleFunction &f) +{ + out << f.interpreter_; + + return out; +} diff --git a/latan/CompiledFunction.hpp b/latan/CompiledFunction.hpp new file mode 100644 index 0000000..b55b304 --- /dev/null +++ b/latan/CompiledFunction.hpp @@ -0,0 +1,39 @@ +#ifndef LATAN_COMPILED_FUNCTION_HPP_ +#define LATAN_COMPILED_FUNCTION_HPP_ + +#include +#include +#include +#include +#include +#include + +LATAN_BEGIN_CPPDECL + +/****************************************************************************** + * Compiled double function class * + ******************************************************************************/ +class CompiledDoubleFunction: public DoubleFunction +{ +public: + // constructor/destructor + explicit CompiledDoubleFunction(const unsigned nArg); + explicit CompiledDoubleFunction(const unsigned nArg, + const std::string &code); + virtual ~CompiledDoubleFunction(void); + // access + void setCode(const std::string &code); + // function call + using DoubleFunction::operator(); + virtual double operator()(std::vector &arg); + // IO + friend std::ostream &operator<<(std::ostream &out, + CompiledDoubleFunction &f); +private: + MathInterpreter interpreter_; + RunContext context_; +}; + +LATAN_END_CPPDECL + +#endif diff --git a/latan/Exceptions.cpp b/latan/Exceptions.cpp index dd518ae..46efe5b 100644 --- a/latan/Exceptions.cpp +++ b/latan/Exceptions.cpp @@ -8,7 +8,7 @@ #define ERR_SUFF " (" + loc + ")" #endif -#define CONST_EXC(name,init) \ +#define CONST_EXC(name, init) \ name::name(string msg, string loc)\ :init\ {} @@ -18,13 +18,16 @@ using namespace Latan; using namespace Exceptions; // logic errors -CONST_EXC(Logic,logic_error(ERR_PREF+msg+ERR_SUFF)) -CONST_EXC(Implementation,Logic("implementation error: "+msg,loc)) -CONST_EXC(Range,Logic("range error: "+msg,loc)) +CONST_EXC(Logic, logic_error(ERR_PREF+msg+ERR_SUFF)) +CONST_EXC(Definition, Logic("definition error: "+msg,loc)) +CONST_EXC(Implementation, Logic("implementation error: "+msg,loc)) +CONST_EXC(Range, Logic("range error: "+msg,loc)) +CONST_EXC(Size, Logic("size error: "+msg,loc)) // runtime errors -CONST_EXC(Runtime,runtime_error(ERR_PREF+msg+ERR_SUFF)) -CONST_EXC(Compilation,Runtime("compilation error: "+msg,loc)) -CONST_EXC(Io,Runtime("IO error: "+msg,loc)) -CONST_EXC(Parsing,Runtime(msg,loc)) -CONST_EXC(Syntax,Runtime("syntax error: "+msg,loc)) +CONST_EXC(Runtime, runtime_error(ERR_PREF+msg+ERR_SUFF)) +CONST_EXC(Compilation, Runtime("compilation error: "+msg,loc)) +CONST_EXC(Io, Runtime("IO error: "+msg,loc)) +CONST_EXC(Parsing, Runtime(msg,loc)) +CONST_EXC(Program, Runtime(msg,loc)) +CONST_EXC(Syntax, Runtime("syntax error: "+msg,loc)) diff --git a/latan/Exceptions.hpp b/latan/Exceptions.hpp index 3515dce..781585f 100644 --- a/latan/Exceptions.hpp +++ b/latan/Exceptions.hpp @@ -10,7 +10,7 @@ + strFrom(__FILE__) + ":" + strFrom(__LINE__) #define LATAN_ERROR(exc,msg) throw(Exceptions::exc(msg,SRC_LOC)) -#define DECL_EXC(name,base) \ +#define DECL_EXC(name, base) \ class name: public base\ {\ public:\ @@ -22,15 +22,18 @@ LATAN_BEGIN_CPPDECL namespace Exceptions { // logic errors - DECL_EXC(Logic,std::logic_error); - DECL_EXC(Implementation,Logic); - DECL_EXC(Range,Logic); + DECL_EXC(Logic, std::logic_error); + DECL_EXC(Definition, Logic); + DECL_EXC(Implementation, Logic); + DECL_EXC(Range, Logic); + DECL_EXC(Size, Logic); // runtime errors - DECL_EXC(Runtime,std::runtime_error); - DECL_EXC(Compilation,Runtime); - DECL_EXC(Io,Runtime); - DECL_EXC(Parsing,Runtime); - DECL_EXC(Syntax,Runtime); + DECL_EXC(Runtime, std::runtime_error); + DECL_EXC(Compilation, Runtime); + DECL_EXC(Io, Runtime); + DECL_EXC(Parsing, Runtime); + DECL_EXC(Program, Runtime); + DECL_EXC(Syntax, Runtime); } LATAN_END_CPPDECL diff --git a/latan/Function.cpp b/latan/Function.cpp index 9c27c2d..5c694fc 100644 --- a/latan/Function.cpp +++ b/latan/Function.cpp @@ -34,7 +34,7 @@ Function::~Function(void) {} // access ////////////////////////////////////////////////////////////////////// -unsigned int Function::getNArg(void) +unsigned int Function::getNArg(void) const { return nArg_; } @@ -77,5 +77,3 @@ double DoubleFunction::operator()(const double x0, ...) return (*this)(buffer_); } - - diff --git a/latan/Function.hpp b/latan/Function.hpp index de122cf..2010075 100644 --- a/latan/Function.hpp +++ b/latan/Function.hpp @@ -16,9 +16,9 @@ class Function public: // constructor/destructor explicit Function(const unsigned nArg); - ~Function(void); + virtual ~Function(void); // access - unsigned int getNArg(void); + unsigned int getNArg(void) const; private: const unsigned int nArg_; }; diff --git a/latan/Makefile.am b/latan/Makefile.am index 3fb1a75..255a996 100644 --- a/latan/Makefile.am +++ b/latan/Makefile.am @@ -24,30 +24,32 @@ BUILT_SOURCES = IoAsciiParser.hpp MathParser.hpp lib_LTLIBRARIES = liblatan.la -liblatan_la_SOURCES = \ - Exceptions.cpp \ - Function.cpp \ - Global.cpp \ - includes.hpp \ - Io.cpp \ - IoAsciiParser.ypp \ - IoAsciiLexer.lpp \ - Mat.cpp \ - Math.cpp \ - MathCompiler.cpp \ - MathParser.ypp \ - MathLexer.lpp \ - Sample.cpp \ +liblatan_la_SOURCES = \ + CompiledFunction.cpp\ + Exceptions.cpp \ + Function.cpp \ + Global.cpp \ + includes.hpp \ + Io.cpp \ + IoAsciiParser.ypp \ + IoAsciiLexer.lpp \ + Mat.cpp \ + Math.cpp \ + MathInterpreter.cpp \ + MathParser.ypp \ + MathLexer.lpp \ + Sample.cpp \ ../config.h liblatan_ladir = $(pkgincludedir) -liblatan_la_HEADERS = \ - Function.hpp \ - Global.hpp \ - Io.hpp \ - IoObject.hpp \ - Mat.hpp \ - Math.hpp \ - MathCompiler.hpp \ +liblatan_la_HEADERS = \ + CompiledFunction.hpp\ + Function.hpp \ + Global.hpp \ + Io.hpp \ + IoObject.hpp \ + Mat.hpp \ + Math.hpp \ + MathInterpreter.hpp \ Sample.hpp liblatan_la_CFLAGS = $(COM_CFLAGS) liblatan_la_CXXFLAGS = $(COM_CXXFLAGS) diff --git a/latan/MathInterpreter.cpp b/latan/MathInterpreter.cpp new file mode 100644 index 0000000..bdae517 --- /dev/null +++ b/latan/MathInterpreter.cpp @@ -0,0 +1,534 @@ +#include +#include + +using namespace std; +using namespace Latan; + +/****************************************************************************** + * MathNode implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +MathNode::MathNode(const string &name, const unsigned int type) +: name_(name) +, type_(type) +, parent_(NULL) +{} + +MathNode::MathNode(const std::string &name, const unsigned int type,\ + const unsigned int nArg, ...) +: name_(name) +, type_(type) +, arg_(nArg) +, parent_(NULL) +{ + va_list va; + + va_start(va, nArg); + for (unsigned int i = 0; i < nArg; ++i) + { + arg_[i] = va_arg(va, MathNode *); + arg_[i]->parent_ = this; + } + va_end(va); +} + +// destructor ////////////////////////////////////////////////////////////////// +MathNode::~MathNode(void) +{ + vector::iterator i; + + for (i = arg_.begin(); i != arg_.end(); ++i) + { + delete *i; + } +} + +// access ////////////////////////////////////////////////////////////////////// +const string &MathNode::getName(void) const +{ + return name_; +} + +unsigned int MathNode::getType(void) const +{ + return type_; +} + +unsigned int MathNode::getNArg(void) const +{ + return static_cast(arg_.size()); +} + +const MathNode * MathNode::getParent(void) const +{ + return parent_; +} + +unsigned int MathNode::getLevel(void) const +{ + if (getParent()) + { + return getParent()->getLevel() + 1; + } + else + { + return 0; + } +} + +void MathNode::setName(const std::string &name) +{ + name_ = name; +} + +void MathNode::pushArg(MathNode *node) +{ + arg_.push_back(node); +} + +// operators /////////////////////////////////////////////////////////////////// +const MathNode &MathNode::operator[](const unsigned int i) const +{ + return *arg_[i]; +} + +ostream &Latan::operator<<(ostream &out, const MathNode &n) +{ + unsigned int level = n.getLevel(); + + for (unsigned int i = 0; i <= level; ++i) + { + if (i == level) + { + out << "_"; + } + else if (i == level - 1) + { + out << "|"; + } + else + { + out << " "; + } + } + out << " " << n.getName() << " (type " << n.getType() << ")" << endl; + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + out << n[i]; + } + + return out; +} + +/****************************************************************************** + * Instruction set * + ******************************************************************************/ +#define CODE_WIDTH 6 +#define CODE_MOD setw(CODE_WIDTH) << left + +Instruction::~Instruction(void) +{} + +ostream &Latan::operator<<(ostream& out, const Instruction& ins) +{ + ins.print(out); + + return out; +} + +Push::Push(const double val) +: type_(ArgType::Constant) +, val_(val) +, name_("") +{} + +Push::Push(const string &name) +: type_(ArgType::Variable) +, val_(0.0) +, name_(name) +{} + +void Push::operator()(RunContext &context) const +{ + if (type_ == ArgType::Constant) + { + context.dStack.push(val_); + } + else + { + if (keyExists(name_, context.vTable)) + { + context.dStack.push(context.vTable[name_]); + } + else + { + LATAN_ERROR(Range, "unknown variable '" + name_ + "'"); + } + } + context.insIndex++; +} + +void Push::print(std::ostream &out) const +{ + out << CODE_MOD << "push"; + if (type_ == ArgType::Constant) + { + out << CODE_MOD << val_; + } + else + { + out << CODE_MOD << name_; + } +} + +Pop::Pop(const string &name) +: name_(name) +{} + +void Pop::operator()(RunContext &context) const +{ + if (!name_.empty()) + { + context.vTable[name_] = context.dStack.top(); + } + context.dStack.pop(); + context.insIndex++; +} + +void Pop::print(std::ostream &out) const +{ + out << CODE_MOD << "pop" << CODE_MOD << name_; +} + +Store::Store(const string &name) +: name_(name) +{} + +void Store::operator()(RunContext &context) const +{ + if (!name_.empty()) + { + context.vTable[name_] = context.dStack.top(); + } + context.insIndex++; +} + +void Store::print(std::ostream &out) const +{ + out << CODE_MOD << "store" << CODE_MOD << name_; +} + +Call::Call(const string &name) +: name_(name) +{} + +void Call::operator()(RunContext &context) const +{ + if (keyExists(name_, context.fTable)) + { + context.dStack.push((*context.fTable[name_])(context.dStack)); + } + else + { + LATAN_ERROR(Range, "unknown function '" + name_ + "'"); + } + context.insIndex++; +} + +void Call::print(std::ostream &out) const +{ + out << CODE_MOD << "call" << CODE_MOD << name_; +} + +#define DEF_OP(name, nArg, exp, insName)\ +void name::operator()(RunContext &context) const\ +{\ + double x[nArg];\ + for (int i = 0; i < nArg; ++i)\ + {\ + x[nArg-1-i] = context.dStack.top();\ + context.dStack.pop();\ + }\ + context.dStack.push(exp);\ + context.insIndex++;\ +}\ +void name::print(std::ostream &out) const\ +{\ + out << CODE_MOD << insName;\ +} + +DEF_OP(Neg, 1, -x[0], "neg") +DEF_OP(Add, 2, x[0] + x[1], "add") +DEF_OP(Sub, 2, x[0] - x[1], "sub") +DEF_OP(Mul, 2, x[0]*x[1], "mul") +DEF_OP(Div, 2, x[0]/x[1], "div") +DEF_OP(Pow, 2, pow(x[0],x[1]), "pow") + +/****************************************************************************** + * MathInterpreter implementation * + ******************************************************************************/ +// MathParserState constructor ///////////////////////////////////////////////// +MathInterpreter::MathParserState::MathParserState(istream *stream, string *name, + MathNode **data) +: ParserState(stream, name, data) +{ + initScanner(); +} + +// MathParserState destructor ////////////////////////////////////////////////// +MathInterpreter::MathParserState::~MathParserState(void) +{ + destroyScanner(); +} + +// constructors //////////////////////////////////////////////////////////////// +MathInterpreter::MathInterpreter(void) +: code_(NULL) +, codeName_("") +, state_(NULL) +, root_(NULL) +, gotReturn_(false) +, status_(Status::none) +{} + +MathInterpreter::MathInterpreter(const std::string &code) +: code_(NULL) +, codeName_("") +, state_(NULL) +, root_(NULL) +, gotReturn_(false) +, status_(Status::none) +{ + setCode(code); +} + +// destructor ////////////////////////////////////////////////////////////////// +MathInterpreter::~MathInterpreter(void) +{ + reset(); +} + +// access ////////////////////////////////////////////////////////////////////// +const Instruction * MathInterpreter::operator[](const unsigned int i) const +{ + return program_[i]; +} + +const MathNode * MathInterpreter::getAST(void) const +{ + if (root_) + { + return root_; + } + else + { + return NULL; + } +} + +void MathInterpreter::push(const Instruction *i) +{ + program_.push_back(i); +} + +// initialization ////////////////////////////////////////////////////////////// +void MathInterpreter::setCode(const std::string &code) +{ + if (status_) + { + reset(); + } + code_ = new stringstream(code); + codeName_ = ""; + state_ = new MathParserState(code_, &codeName_, &root_); + status_ = Status::initialised; +} + +void MathInterpreter::reset(void) +{ + InstructionContainer::iterator i; + + delete code_; + codeName_ = ""; + delete state_; + delete root_; + for (i = program_.begin(); i != program_.end(); ++i) + { + delete *i; + } + program_.clear(); + status_ = 0; +} + +// parser ////////////////////////////////////////////////////////////////////// + +// Bison/Flex parser declaration +int _math_parse(MathInterpreter::MathParserState* state); + +void MathInterpreter::parse(void) +{ + _math_parse(state_); +} + +// interpreter ///////////////////////////////////////////////////////////////// +void MathInterpreter::compile(void) +{ + if (!(status_ & Status::parsed)) + { + parse(); + status_ |= Status::parsed; + status_ -= status_ & Status::compiled; + } + gotReturn_ = false; + if (root_) + { + compileNode(*root_); + } + if (!root_||!gotReturn_) + { + LATAN_ERROR(Syntax, "expected 'return' in program '" + codeName_ + "'"); + } + status_ |= Status::compiled; +} + +#define IFNODE(name, nArg) if ((n.getName() == (name))&&(n.getNArg() == nArg)) +#define ELIFNODE(name, nArg) else IFNODE(name, nArg) +#define ELSE else + +void MathInterpreter::compileNode(const MathNode& n) +{ + if (!gotReturn_) + { + switch (n.getType()) + { + case MathNode::Type::cst: + program_.push_back(new Push(strTo(n.getName()))); + break; + case MathNode::Type::var: + program_.push_back(new Push(n.getName())); + break; + case MathNode::Type::op: + // semicolon + if (n.getName() == ";") + { + // compile relevant statements + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + bool isAssign = + ((n[i].getType() == MathNode::Type::op)&& + (n[i].getName() == "=")); + bool isSemiColumn = + ((n[i].getType() == MathNode::Type::op)&& + (n[i].getName() == ";")); + bool isKeyword = + (n[i].getType() == MathNode::Type::keyw); + + if (isAssign||isSemiColumn||isKeyword) + { + compileNode(n[i]); + } + } + } + // assignment + else if (n.getName() == "=") + { + // variable assignement + if (n[0].getType() == MathNode::Type::var) + { + bool hasSemicolonParent = ((n.getParent() != NULL) && + (n.getParent()->getType() + == MathNode::Type::op)&& + (n.getParent()->getName() + == ";")); + // compile the RHS + compileNode(n[1]); + // pop instruction if at the end of a statement + if (hasSemicolonParent) + { + program_.push_back(new Pop(n[0].getName())); + } + // store instruction else + else + { + program_.push_back(new Store(n[0].getName())); + } + } + else + { + LATAN_ERROR(Compilation, "invalid LHS for '='"); + } + } + // arithmetic operators + else + { + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + compileNode(n[i]); + } + IFNODE("-", 1) program_.push_back(new Neg); + ELIFNODE("+", 2) program_.push_back(new Add); + ELIFNODE("-", 2) program_.push_back(new Sub); + ELIFNODE("*", 2) program_.push_back(new Mul); + ELIFNODE("/", 2) program_.push_back(new Div); + ELIFNODE("^", 2) program_.push_back(new Pow); + ELSE LATAN_ERROR(Compilation, "unknown operator '" + + n.getName() + "'"); + } + break; + case MathNode::Type::keyw: + if (n.getName() == "return") + { + compileNode(n[0]); + gotReturn_ = true; + } + else + { + LATAN_ERROR(Compilation, "unknown keyword '" + n.getName() + + "'"); + } + break; + case MathNode::Type::func: + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + compileNode(n[i]); + } + program_.push_back(new Call(n.getName())); + break; + default: + LATAN_ERROR(Compilation, + "unknown node type (node named '" + n.getName() + + "')"); + break; + } + } +} + +// execution /////////////////////////////////////////////////////////////////// +void MathInterpreter::operator()(RunContext &context) +{ + if (!(status_ & Status::compiled)) + { + compile(); + } + execute(context); +} + +void MathInterpreter::execute(RunContext &context) const +{ + context.insIndex = 0; + while (context.insIndex != program_.size()) + { + (*(program_[context.insIndex]))(context); + } +} + +// IO ////////////////////////////////////////////////////////////////////////// +ostream &Latan::operator<<(ostream &out, const MathInterpreter &program) +{ + for (unsigned int i = 0; i < program.program_.size(); ++i) + { + out << *(program.program_[i]) << endl; + } + + return out; +} diff --git a/latan/MathInterpreter.hpp b/latan/MathInterpreter.hpp new file mode 100644 index 0000000..abbdfd0 --- /dev/null +++ b/latan/MathInterpreter.hpp @@ -0,0 +1,251 @@ +#ifndef LATAN_MATHCOMPILER_HPP_ +#define LATAN_MATHCOMPILER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXIDLENGTH 256 + +LATAN_BEGIN_CPPDECL + +/****************************************************************************** + * Parser classes * + ******************************************************************************/ +class MathNode +{ +public: + class Type + { + public: + enum + { + undef = -1, + cst = 0, + op = 1, + var = 2, + keyw = 3, + func = 4 + }; + }; +public: + // constructor + MathNode(const std::string &name, const unsigned int type); + MathNode(const std::string &name, const unsigned int type, + const unsigned int nArg, ...); + // destructor + virtual ~MathNode(); + // access + const std::string& getName(void) const; + unsigned int getType(void) const; + unsigned int getNArg(void) const; + const MathNode * getParent(void) const; + unsigned int getLevel(void) const; + void setName(const std::string &name); + void pushArg(MathNode *node); + // operator + const MathNode &operator[](const unsigned int i) const; +private: + std::string name_; + unsigned int type_; + std::vector arg_; + const MathNode * parent_; +}; + +std::ostream &operator<<(std::ostream &out, const MathNode &n); + +/****************************************************************************** + * Instruction classes * + ******************************************************************************/ +typedef std::map VarTable; +typedef std::map FunctionTable; + +struct RunContext +{ + unsigned int insIndex; + std::stack dStack; + VarTable vTable; + FunctionTable fTable; +}; + +// Abstract base +class Instruction +{ +public: + virtual ~Instruction(); + // instruction execution + virtual void operator()(RunContext &context) const = 0; + friend std::ostream& operator<<(std::ostream &out, const Instruction &ins); +private: + virtual void print(std::ostream &out) const = 0; +}; + +// Push +class Push: public Instruction +{ +private: + class ArgType + { + public: + enum + { + Constant = 0, + Variable = 1 + }; + }; +public: + //constructors + explicit Push(const double val); + explicit Push(const std::string &name); + // instruction execution + virtual void operator()(RunContext &context) const; +private: + virtual void print(std::ostream& out) const; +private: + unsigned int type_; + double val_; + std::string name_; +}; + +// Pop +class Pop: public Instruction +{ +public: + //constructor + explicit Pop(const std::string &name); + // instruction execution + virtual void operator()(RunContext &context) const; +private: + virtual void print(std::ostream& out) const; +private: + std::string name_; +}; + +// Store +class Store: public Instruction +{ +public: + //constructor + explicit Store(const std::string &name); + // instruction execution + virtual void operator()(RunContext &context) const; +private: + virtual void print(std::ostream& out) const; +private: + std::string name_; +}; + +// Call function +class Call: public Instruction +{ +public: + //constructor + explicit Call(const std::string &name); + // instruction execution + virtual void operator()(RunContext &context) const; +private: + virtual void print(std::ostream& out) const; +private: + std::string name_; +}; + +// Floating point operations +#define DECL_OP(name)\ +class name: public Instruction\ +{\ +public:\ +virtual void operator()(RunContext &context) const;\ +private:\ + virtual void print(std::ostream &out) const;\ +} + +DECL_OP(Neg); +DECL_OP(Add); +DECL_OP(Sub); +DECL_OP(Mul); +DECL_OP(Div); +DECL_OP(Pow); + +/****************************************************************************** + * Compiler class * + ******************************************************************************/ +class MathInterpreter +{ + +public: + // parser state + class MathParserState: public ParserState + { + public: + // constructor + explicit MathParserState(std::istream *stream, std::string *name, + MathNode **data); + // destructor + virtual ~MathParserState(void); + private: + // allocation/deallocation functions defined in MathLexer.lpp + virtual void initScanner(void); + virtual void destroyScanner(void); + }; +private: + // status flags + class Status + { + public: + enum + { + none = 0, + initialised = 1 << 0, + parsed = 1 << 1, + compiled = 1 << 2 + }; + }; + // instruction container + typedef std::vector InstructionContainer; +public: + // constructors + MathInterpreter(void); + MathInterpreter(const std::string &code); + // destructor + ~MathInterpreter(void); + // access + const Instruction * operator[](const unsigned int i) const; + const MathNode * getAST(void) const; + // initialization + void setCode(const std::string &code); + // interpreter + void compile(void); + // execution + void operator()(RunContext &context); + // IO + friend std::ostream &operator<<(std::ostream &out, + const MathInterpreter &program); +private: + // initialization + void reset(void); + // access + void push(const Instruction *i); + // parser + void parse(void); + // interpreter + void compileNode(const MathNode &node); + // execution + void execute(RunContext &context) const; +private: + std::istream *code_; + std::string codeName_; + MathParserState *state_; + MathNode *root_; + bool gotReturn_; + InstructionContainer program_; + unsigned int status_; +}; + +LATAN_END_CPPDECL + +#endif diff --git a/latan/MathLexer.lpp b/latan/MathLexer.lpp index ec63062..da326ed 100644 --- a/latan/MathLexer.lpp +++ b/latan/MathLexer.lpp @@ -7,7 +7,7 @@ %{ #include - #include + #include #include #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wunused-function" @@ -16,7 +16,7 @@ using namespace std; using namespace Latan; - #define YY_EXTRA_TYPE MathCompiler::MathParserState * + #define YY_EXTRA_TYPE MathInterpreter::MathParserState * #define YY_USER_ACTION \ yylloc->first_line = yylloc->last_line = yylineno;\ yylloc->first_column = yylloc->last_column + 1;\ @@ -62,13 +62,13 @@ return {RETTOK(RETURN);} %% -void MathCompiler::MathParserState::initScanner() +void MathInterpreter::MathParserState::initScanner() { yylex_init(&scanner); yyset_extra(this, scanner); } -void MathCompiler::MathParserState::destroyScanner() +void MathInterpreter::MathParserState::destroyScanner() { yylex_destroy(scanner); } diff --git a/latan/MathParser.ypp b/latan/MathParser.ypp index bd30537..e2d38a5 100644 --- a/latan/MathParser.ypp +++ b/latan/MathParser.ypp @@ -3,7 +3,7 @@ #include #include #include - #include + #include using namespace std; using namespace Latan; @@ -14,7 +14,7 @@ %locations %defines %error-verbose -%parse-param { Latan::MathCompiler::MathParserState *state } +%parse-param { Latan::MathInterpreter::MathParserState *state } %initial-action {yylloc.last_column = 0;} %lex-param { void* scanner } @@ -42,7 +42,7 @@ %{ int _math_lex(YYSTYPE *lvalp, YYLTYPE *llocp, void *scanner); - void _math_error(YYLTYPE *locp, MathCompiler::MathParserState *state, + void _math_error(YYLTYPE *locp, MathInterpreter::MathParserState *state, const char *err) { stringstream buf;