From 3510898ee96faaa45cec855d076a7647ba1b4a6f Mon Sep 17 00:00:00 2001 From: Antonin Portelli Date: Tue, 18 Feb 2014 18:11:08 +0000 Subject: [PATCH] Math interpreter: rewriting of the compiler using AST with polymorphic nodes --- latan/MathInterpreter.cpp | 455 ++++++++++++++++++-------------------- latan/MathInterpreter.hpp | 113 +++++----- latan/MathParser.ypp | 45 ++-- 3 files changed, 308 insertions(+), 305 deletions(-) diff --git a/latan/MathInterpreter.cpp b/latan/MathInterpreter.cpp index 6025501..0fbc892 100644 --- a/latan/MathInterpreter.cpp +++ b/latan/MathInterpreter.cpp @@ -23,125 +23,13 @@ using namespace std; using namespace Latan; -/****************************************************************************** - * MathNode implementation * - ******************************************************************************/ -// constructors //////////////////////////////////////////////////////////////// -MathNode::MathNode(const string &name, const Type type) -: name_(name) -, type_(type) -, parent_(nullptr) -{} - -MathNode::MathNode(const std::string &name, const Type type,\ - const unsigned int nArg, ...) -: name_(name) -, type_(type) -, arg_(nArg) -, parent_(nullptr) -{ - va_list va; - - va_start(va, nArg); - for (unsigned int i = 0; i < nArg; ++i) - { - arg_[i].reset(va_arg(va, MathNode *)); - arg_[i]->parent_ = this; - } - va_end(va); -} - -// destructor ////////////////////////////////////////////////////////////////// -MathNode::~MathNode(void) -{} - -// access ////////////////////////////////////////////////////////////////////// -const string &MathNode::getName(void) const -{ - return name_; -} - -MathNode::Type 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(unique_ptr(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 " << static_cast(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) -{} - +// Instruction operator //////////////////////////////////////////////////////// ostream &Latan::operator<<(ostream& out, const Instruction& ins) { ins.print(out); @@ -149,6 +37,7 @@ ostream &Latan::operator<<(ostream& out, const Instruction& ins) return out; } +// Push constructors /////////////////////////////////////////////////////////// Push::Push(const double val) : type_(ArgType::Constant) , val_(val) @@ -161,6 +50,7 @@ Push::Push(const string &name) , name_(name) {} +// Push execution ////////////////////////////////////////////////////////////// void Push::operator()(RunContext &context) const { if (type_ == ArgType::Constant) @@ -181,6 +71,7 @@ void Push::operator()(RunContext &context) const context.insIndex++; } +// Push print ////////////////////////////////////////////////////////////////// void Push::print(ostream &out) const { out << CODE_MOD << "push"; @@ -194,10 +85,12 @@ void Push::print(ostream &out) const } } +// Pop constructor ///////////////////////////////////////////////////////////// Pop::Pop(const string &name) : name_(name) {} +// Pop execution /////////////////////////////////////////////////////////////// void Pop::operator()(RunContext &context) const { if (!name_.empty()) @@ -208,15 +101,18 @@ void Pop::operator()(RunContext &context) const context.insIndex++; } +// Pop print /////////////////////////////////////////////////////////////////// void Pop::print(ostream &out) const { out << CODE_MOD << "pop" << CODE_MOD << name_; } +// Store constructor /////////////////////////////////////////////////////////// Store::Store(const string &name) : name_(name) {} +// Store execution ///////////////////////////////////////////////////////////// void Store::operator()(RunContext &context) const { if (!name_.empty()) @@ -226,15 +122,18 @@ void Store::operator()(RunContext &context) const context.insIndex++; } +// Store print ///////////////////////////////////////////////////////////////// void Store::print(ostream &out) const { out << CODE_MOD << "store" << CODE_MOD << name_; } +// Call constructor //////////////////////////////////////////////////////////// Call::Call(const string &name) : name_(name) {} +// Call execution ////////////////////////////////////////////////////////////// void Call::operator()(RunContext &context) const { try @@ -248,11 +147,13 @@ void Call::operator()(RunContext &context) const context.insIndex++; } +// Call print ////////////////////////////////////////////////////////////////// void Call::print(ostream &out) const { out << CODE_MOD << "call" << CODE_MOD << name_; } +// Math operations ///////////////////////////////////////////////////////////// #define DEF_OP(name, nArg, exp, insName)\ void name::operator()(RunContext &context) const\ {\ @@ -277,13 +178,199 @@ 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") +/****************************************************************************** + * ExprNode implementation * + ******************************************************************************/ +// ExprNode constructors /////////////////////////////////////////////////////// +ExprNode::ExprNode(const string &name) +: name_(name) +, parent_(nullptr) +{} + +// ExprNode access ///////////////////////////////////////////////////////////// +const string &ExprNode::getName(void) const +{ + return name_; +} + +unsigned int ExprNode::getNArg(void) const +{ + return static_cast(arg_.size()); +} + +const ExprNode * ExprNode::getParent(void) const +{ + return parent_; +} + +unsigned int ExprNode::getLevel(void) const +{ + if (getParent()) + { + return getParent()->getLevel() + 1; + } + else + { + return 0; + } +} + +void ExprNode::setName(const std::string &name) +{ + name_ = name; +} + +void ExprNode::pushArg(ExprNode *node) +{ + if (node) + { + node->parent_ = this; + arg_.push_back(unique_ptr(node)); + } +} + +// ExprNode operators ////////////////////////////////////////////////////////// +const ExprNode &ExprNode::operator[](const unsigned int i) const +{ + return *arg_[i]; +} + +ostream &Latan::operator<<(ostream &out, const ExprNode &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() << endl; + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + out << n[i]; + } + + return out; +} + +#define PUSH_INS(program, type, ...) \ +program.push_back(unique_ptr(new type(__VA_ARGS__))) + +// VarNode compile ///////////////////////////////////////////////////////////// +void VarNode::compile(Program &program) const +{ + PUSH_INS(program, Push, getName()); +} + +// CstNode compile ///////////////////////////////////////////////////////////// +void CstNode::compile(Program &program) const +{ + PUSH_INS(program, Push, strTo(getName())); +} + +// SemicolonNode compile /////////////////////////////////////////////////////// +void SemicolonNode::compile(Program &program) const +{ + auto &n = *this; + + for (unsigned int i = 0; i < getNArg(); ++i) + { + bool isAssign = isDerivedFrom(&n[i]); + bool isSemiColumn = isDerivedFrom(&n[i]); + bool isKeyword = isDerivedFrom(&n[i]); + + if (isAssign||isSemiColumn||isKeyword) + { + n[i].compile(program); + } + } +} + +// AssignNode compile ////////////////////////////////////////////////////////// +void AssignNode::compile(Program &program) const +{ + auto &n = *this; + + if (isDerivedFrom(&n[0])) + { + bool hasSemicolonParent = isDerivedFrom(getParent()); + + n[1].compile(program); + if (hasSemicolonParent) + { + PUSH_INS(program, Pop, n[0].getName()); + } + else + { + PUSH_INS(program, Store, n[0].getName()); + } + } + else + { + LATAN_ERROR(Compilation, "invalid LHS for '='"); + } +} + +// MathOpNode compile ////////////////////////////////////////////////////////// +#define IFNODE(name, nArg) if ((n.getName() == (name))&&(n.getNArg() == nArg)) +#define ELIFNODE(name, nArg) else IFNODE(name, nArg) +#define ELSE else + +void MathOpNode::compile(Program &program) const +{ + auto &n = *this; + + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + n[i].compile(program); + } + IFNODE("-", 1) PUSH_INS(program, Neg,); + ELIFNODE("+", 2) PUSH_INS(program, Add,); + ELIFNODE("-", 2) PUSH_INS(program, Sub,); + ELIFNODE("*", 2) PUSH_INS(program, Mul,); + ELIFNODE("/", 2) PUSH_INS(program, Div,); + ELIFNODE("^", 2) PUSH_INS(program, Pow,); + ELSE LATAN_ERROR(Compilation, "unknown operator '" + getName() + "'"); +} + +// FuncNode compile //////////////////////////////////////////////////////////// +void FuncNode::compile(Program &program) const +{ + auto &n = *this; + + for (unsigned int i = 0; i < n.getNArg(); ++i) + { + n[i].compile(program); + } + PUSH_INS(program, Call, getName()); +} + +// ReturnNode compile //////////////////////////////////////////////////////////// +void ReturnNode::compile(Program &program) const +{ + auto &n = *this; + + n[0].compile(program); + program.push_back(nullptr); +} + /****************************************************************************** * MathInterpreter implementation * ******************************************************************************/ // MathParserState constructor ///////////////////////////////////////////////// MathInterpreter::MathParserState::MathParserState -(istream *stream, string *name, std::unique_ptr *data) -: ParserState>(stream, name, data) +(istream *stream, string *name, std::unique_ptr *data) +: ParserState>(stream, name, data) { initScanner(); } @@ -300,7 +387,6 @@ MathInterpreter::MathInterpreter(void) , codeName_("") , state_(nullptr) , root_(nullptr) -, gotReturn_(false) , status_(Status::none) {} @@ -309,7 +395,6 @@ MathInterpreter::MathInterpreter(const std::string &code) , codeName_("") , state_(nullptr) , root_(nullptr) -, gotReturn_(false) , status_(Status::none) { setCode(code); @@ -325,7 +410,7 @@ const Instruction * MathInterpreter::operator[](const unsigned int i) const return program_[i].get(); } -const MathNode * MathInterpreter::getAST(void) const +const ExprNode * MathInterpreter::getAST(void) const { return root_.get(); } @@ -345,6 +430,7 @@ void MathInterpreter::setCode(const std::string &code) code_.reset(new stringstream(code)); codeName_ = ""; state_.reset(new MathParserState(code_.get(), &codeName_, &root_)); + program_.clear(); status_ = Status::initialised; } @@ -371,139 +457,36 @@ void MathInterpreter::parse(void) // interpreter ///////////////////////////////////////////////////////////////// void MathInterpreter::compile(void) { + bool gotReturn = false; + if (!(status_ & Status::parsed)) { parse(); status_ |= Status::parsed; status_ -= status_ & Status::compiled; } - gotReturn_ = false; if (root_) { - compileNode(*root_); + root_->compile(program_); + for (unsigned int i = 0; i < program_.size(); ++i) + { + if (!program_[i]) + { + gotReturn = true; + program_.resize(i); + program_.shrink_to_fit(); + break; + } + } + } - if (!root_||!gotReturn_) + 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 - -#define PUSH_INS(type, ...) \ -program_.push_back(unique_ptr(new (type)(__VA_ARGS__))) - -void MathInterpreter::compileNode(const MathNode& n) -{ - if (!gotReturn_) - { - switch (n.getType()) - { - case MathNode::Type::cst: - PUSH_INS(Push, strTo(n.getName())); - break; - case MathNode::Type::var: - PUSH_INS(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(unique_ptr(new Pop(n[0].getName()))); - } - // store instruction else - else - { - PUSH_INS(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) PUSH_INS(Neg,); - ELIFNODE("+", 2) PUSH_INS(Add,); - ELIFNODE("-", 2) PUSH_INS(Sub,); - ELIFNODE("*", 2) PUSH_INS(Mul,); - ELIFNODE("/", 2) PUSH_INS(Div,); - ELIFNODE("^", 2) PUSH_INS(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]); - } - PUSH_INS(Call, n.getName()); - break; - default: - LATAN_ERROR(Compilation, - "unknown node type (node named '" + n.getName() - + "')"); - break; - } - } -} - // execution /////////////////////////////////////////////////////////////////// void MathInterpreter::operator()(RunContext &context) { diff --git a/latan/MathInterpreter.hpp b/latan/MathInterpreter.hpp index 39c67d9..b890fc5 100644 --- a/latan/MathInterpreter.hpp +++ b/latan/MathInterpreter.hpp @@ -33,47 +33,6 @@ BEGIN_NAMESPACE -/****************************************************************************** - * Expression node class * - ******************************************************************************/ -class MathNode -{ -public: - enum class Type - { - undef = -1, - cst = 0, - op = 1, - var = 2, - keyw = 3, - func = 4 - }; -public: - // constructors - MathNode(const std::string &name, const Type type); - MathNode(const std::string &name, const Type type, - const unsigned int nArg, ...); - // destructor - virtual ~MathNode(); - // access - const std::string& getName(void) const; - Type 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_; - Type type_; - std::vector> arg_; - const MathNode * parent_; -}; - -std::ostream &operator<<(std::ostream &out, const MathNode &n); - /****************************************************************************** * Instruction classes * ******************************************************************************/ @@ -92,7 +51,8 @@ struct RunContext class Instruction { public: - virtual ~Instruction(); + // destructor + virtual ~Instruction(void) = default; // instruction execution virtual void operator()(RunContext &context) const = 0; friend std::ostream & operator<<(std::ostream &out, const Instruction &ins); @@ -102,6 +62,9 @@ private: std::ostream & operator<<(std::ostream &out, const Instruction &ins); +// Instruction container +typedef std::vector> Program; + // Push class Push: public Instruction { @@ -184,6 +147,59 @@ DECL_OP(Mul); DECL_OP(Div); DECL_OP(Pow); +/****************************************************************************** + * Expression node classes * + ******************************************************************************/ +class ExprNode +{ +public: + // constructors + ExprNode(const std::string &name); + // destructor + virtual ~ExprNode() = default; + // access + const std::string& getName(void) const; + unsigned int getNArg(void) const; + const ExprNode * getParent(void) const; + unsigned int getLevel(void) const; + void setName(const std::string &name); + void pushArg(ExprNode *node); + // operator + const ExprNode &operator[](const unsigned int i) const; + // compile + virtual void compile(Program &program) const = 0; +private: + std::string name_; + std::vector> arg_; + const ExprNode * parent_; +}; + +std::ostream &operator<<(std::ostream &out, const ExprNode &n); + +#define DECL_NODE(base, name) \ +class name: public base\ +{\ +public:\ + using base::base;\ + virtual void compile(Program &program) const;\ +} + +DECL_NODE(ExprNode, VarNode); +DECL_NODE(ExprNode, CstNode); +DECL_NODE(ExprNode, SemicolonNode); +DECL_NODE(ExprNode, AssignNode); +DECL_NODE(ExprNode, MathOpNode); +DECL_NODE(ExprNode, FuncNode); + +class KeywordNode: public ExprNode +{ +public: + using ExprNode::ExprNode; + virtual void compile(Program &program) const = 0; +}; + +DECL_NODE(KeywordNode, ReturnNode); + /****************************************************************************** * Interpreter class * ******************************************************************************/ @@ -192,12 +208,12 @@ class MathInterpreter public: // parser state - class MathParserState: public ParserState> + class MathParserState: public ParserState> { public: // constructor explicit MathParserState(std::istream *stream, std::string *name, - std::unique_ptr *data); + std::unique_ptr *data); // destructor virtual ~MathParserState(void); private: @@ -218,8 +234,6 @@ private: compiled = 1 << 2 }; }; - // instruction container - typedef std::vector> Program; public: // constructors MathInterpreter(void); @@ -228,7 +242,7 @@ public: ~MathInterpreter(void); // access const Instruction * operator[](const unsigned int i) const; - const MathNode * getAST(void) const; + const ExprNode * getAST(void) const; // initialization void setCode(const std::string &code); // interpreter @@ -246,15 +260,14 @@ private: // parser void parse(void); // interpreter - void compileNode(const MathNode &node); + void compileNode(const ExprNode &node); // execution void execute(RunContext &context) const; private: std::unique_ptr code_; std::string codeName_; std::unique_ptr state_; - std::unique_ptr root_; - bool gotReturn_; + std::unique_ptr root_; Program program_; unsigned int status_; }; diff --git a/latan/MathParser.ypp b/latan/MathParser.ypp index c1b834c..a55144a 100644 --- a/latan/MathParser.ypp +++ b/latan/MathParser.ypp @@ -42,7 +42,7 @@ double val_double; char val_char; char val_str[MAXIDLENGTH]; - Latan::MathNode *val_node; + Latan::ExprNode *val_node; } %token ERR @@ -70,6 +70,16 @@ << locp->first_column << ": " << err; LATAN_ERROR(Parsing, buf.str()); } + + void _math_warning(YYLTYPE *locp, MathInterpreter::MathParserState *state, + const char *err) + { + stringstream buf; + + buf << *(state->streamName) << ":" << locp->first_line << ":"\ + << locp->first_column << ": " << err; + LATAN_WARNING(buf.str()); + } #define scanner state->scanner %} @@ -83,16 +93,13 @@ program: stmt: ';' - {$$ = new MathNode(";", MathNode::Type::op, 0);} + {$$ = new SemicolonNode(";");} | expr ';' - {$$ = $1;} + {$$ = nullptr; _math_warning(&yylloc, state, "useless statement removed");} | ID '=' expr ';' - { - $$ = new MathNode("=", MathNode::Type::op, 2, \ - new MathNode($ID,MathNode::Type::var), $3); - } + {$$ = new AssignNode("="); $$->pushArg(new VarNode($ID)); $$->pushArg($3);} | RETURN expr ';' - {$$ = new MathNode("return", MathNode::Type::keyw, 1, $2);} + {$$ = new ReturnNode("return"); $$->pushArg($2);} | '{' stmt_list '}' {$$ = $2;} ; @@ -101,26 +108,26 @@ stmt_list: stmt {$$ = $1;} | stmt_list stmt - {$$ = new MathNode(";", MathNode::Type::op, 2, $1, $2);} + {$$ = new SemicolonNode(";"); $$->pushArg($1); $$->pushArg($2);} ; expr: FLOAT - {$$ = new MathNode($FLOAT, MathNode::Type::cst);} + {$$ = new CstNode($FLOAT);} | ID - {$$ = new MathNode($ID, MathNode::Type::var);} + {$$ = new VarNode($ID);} | '-' expr %prec UMINUS - {$$ = new MathNode("-", MathNode::Type::op, 1, $2);} + {$$ = new MathOpNode("-"); $$->pushArg($2);} | expr '+' expr - {$$ = new MathNode("+", MathNode::Type::op, 2, $1, $3);} + {$$ = new MathOpNode("+"); $$->pushArg($1); $$->pushArg($3);} | expr '-' expr - {$$ = new MathNode("-", MathNode::Type::op, 2, $1, $3);} + {$$ = new MathOpNode("-"); $$->pushArg($1); $$->pushArg($3);} | expr '*' expr - {$$ = new MathNode("*", MathNode::Type::op, 2, $1, $3);} + {$$ = new MathOpNode("*"); $$->pushArg($1); $$->pushArg($3);} | expr '/' expr - {$$ = new MathNode("/", MathNode::Type::op, 2, $1, $3);} + {$$ = new MathOpNode("/"); $$->pushArg($1); $$->pushArg($3);} | expr '^' expr - {$$ = new MathNode("^", MathNode::Type::op, 2, $1, $3);} + {$$ = new MathOpNode("^"); $$->pushArg($1); $$->pushArg($3);} | '(' expr ')' {$$ = $2;} | ID '(' func_args ')' @@ -129,9 +136,9 @@ expr: func_args: /* empty string */ - {$$ = new MathNode("", MathNode::Type::func);} + {$$ = new FuncNode("");} | expr - {$$ = new MathNode("", MathNode::Type::func, 1, $1);} + {$$ = new FuncNode(""); $$->pushArg($1);} | func_args ',' expr {$$ = $1; $$->pushArg($3);} ;