From 2bbda9b9483e5963442e1a49581838694cf2a6f2 Mon Sep 17 00:00:00 2001 From: hama Date: Wed, 2 Jul 2014 17:16:30 +0200 Subject: [PATCH] day's work --- rplcore/rpltest.cpp | 37 ++++++- rplcore/rpltest.hpp | 4 +- rplexpr/rplasclasses.cpp | 48 +++++++-- rplexpr/rplasclasses.hpp | 4 +- rplexpr/rplastree.cpp | 117 ++++++++++++++++++--- rplexpr/rplastree.hpp | 54 ++++++---- rplexpr/rpllexer.cpp | 18 ++++ rplexpr/rpllexer.hpp | 2 + rplexpr/rplmfparser.cpp | 183 ++++++++++++++++++++------------- rplexpr/rplmfparser.hpp | 4 +- rplexpr/rplparser.cpp | 9 ++ rplexpr/rplparser.hpp | 4 +- test/rplmfparser/baseTest.txt | 18 ++++ test/rplmfparser/defTest.txt | 20 ++++ unittests/main.cpp | 3 + unittests/rplmfparser_test.cpp | 26 ++++- 16 files changed, 424 insertions(+), 127 deletions(-) create mode 100644 test/rplmfparser/baseTest.txt create mode 100644 test/rplmfparser/defTest.txt diff --git a/rplcore/rpltest.cpp b/rplcore/rpltest.cpp index abb952f..d68bfee 100644 --- a/rplcore/rpltest.cpp +++ b/rplcore/rpltest.cpp @@ -333,6 +333,38 @@ bool RplTest::assertNotNull(const void* ptr, const char* file, int lineNo) { return ptr != NULL; } +/** + * @brief Compares two files line by line. + * + * @param expected the file with the expected content + * @param current the file with the current content + * @return true: the files are equal
+ * false: otherwise + */ +bool RplTest::assertEqualFiles(const char* expected, const char* current, + const char* file, int lineNo) +{ + bool rc = false; + QByteArray expectedContent = RplString::read(expected, true); + QByteArray currentContent = RplString::read(current, true); + if (expectedContent.isEmpty()){ + char buffer[512]; + snprintf(buffer, sizeof buffer, "%s has no content. Does it exist?", + expected); + error(buffer); + } else if (expectedContent.isEmpty()){ + char buffer[512]; + snprintf(buffer, sizeof buffer, "%s has no content. Does it exist?", + current); + error(buffer); + } else { + QList expLines = expectedContent.split('\n'); + QList curLines = expectedContent.split('\n'); + rc = assertEquals(expLines, curLines, file, lineNo); + } + return rc; +} + /** * @brief Writes an info. * @@ -437,7 +469,10 @@ QByteArray RplTest::getTempDir(const char* node, const char* parent, QByteArray RplTest::getTempFile(const char* node, const char* parent, bool deleteIfExists) { QByteArray dir = getTempDir(parent); - QByteArray rc = dir + m_separator + node; + QByteArray rc = dir; + if (! rc.endsWith(m_separator)) + rc += m_separator; + rc += node; struct stat info; if(deleteIfExists && stat(rc.constData(), &info) == 0) unlink(rc.constData()); diff --git a/rplcore/rpltest.hpp b/rplcore/rpltest.hpp index ed7caed..5c7d3ac 100644 --- a/rplcore/rpltest.hpp +++ b/rplcore/rpltest.hpp @@ -42,6 +42,8 @@ public: bool assertFalse(bool condition, const char* file, int lineNo); bool assertNull(const void* ptr, const char* file, int lineNo); bool assertNotNull(const void* ptr, const char* file, int lineNo); + bool assertEqualFiles(const char* expected, const char* current, + const char* file, int lineNo); bool log(const char* message); bool error(const char* message, ...); QByteArray getTempDir(const char* node, const char* parent = NULL, @@ -67,5 +69,5 @@ protected: #define checkF(current) assertFalse(current, __FILE__, __LINE__) #define checkN(current) assertNull(current, __FILE__, __LINE__) #define checkNN(current) assertNotNull(current, __FILE__, __LINE__) - +#define checkFiles(expected, current) assertEqualFiles(expected, current, __FILE__, __LINE__) #endif // RPLTEST_HPP diff --git a/rplexpr/rplasclasses.cpp b/rplexpr/rplasclasses.cpp index 152eba0..9edd340 100644 --- a/rplexpr/rplasclasses.cpp +++ b/rplexpr/rplasclasses.cpp @@ -60,12 +60,11 @@ RplSymbolSpace::RplSymbolSpace(RplSymbolSpace::SymbolSpaceType type, if (type == SST_GLOBAL){ m_classes[RplASInteger::m_instance.name()] = &RplASInteger::m_instance; m_classes[RplASBoolean::m_instance.name()] = &RplASBoolean::m_instance; - m_classes[RplASFloat::m_instance.name()] = &RplASBoolean::m_instance; + m_classes[RplASFloat::m_instance.name()] = &RplASFloat::m_instance; m_classes[RplASString::m_instance.name()] = &RplASString::m_instance; m_classes[RplASList::m_instance.name()] = &RplASList::m_instance; m_classes[RplASMap::m_instance.name()] = &RplASMap::m_instance; } - } /** @@ -89,9 +88,14 @@ RplSymbolSpace::~RplSymbolSpace() * @return NULL: not found
* otherwise: the class */ -RplASClass*RplSymbolSpace::findClass(const QString& name) const +RplASClass* RplSymbolSpace::findClass(const QString& name) const { - return NULL; + RplASClass* rc = NULL; + if (m_classes.contains(name)) + rc = m_classes[name]; + else if (m_parent != NULL) + rc = m_parent->findClass(name); + return rc; } /** @@ -125,7 +129,7 @@ void RplSymbolSpace::dump(FILE* fp, int indent, const char* header) clazz->dump(fp, indent + 1); } - fprintf(fp, "== Variables:\n"); + fprintf(fp, "%s== Variables:\n", tabs); sorted.clear(); sorted.reserve(m_variables.size()); VariableMap::iterator it3; @@ -138,8 +142,9 @@ void RplSymbolSpace::dump(FILE* fp, int indent, const char* header) RplVariable* var = m_variables[*it4]; var->dump(fp, indent + 1); } - fprintf(fp, "== Body:\n"); - m_body->dump(fp, indent + 1); + fprintf(fp, "%s== Body:%s\n", tabs, m_body == NULL ? " " : ""); + if (m_body != NULL) + m_body->dump(fp, indent + 1); } /** @@ -173,6 +178,27 @@ const char*RplSymbolSpace::spaceTypeName(RplSymbolSpace::SymbolSpaceType type) } return rc; } +/** + * @brief Returns the body (a abstract syntax tree) of the symbol space. + * + * @return NULL: no body available
+ * othewise: the body of the instance + */ +RplASItem* RplSymbolSpace::body() const +{ + return m_body; +} + +/** + * @brief Sets the body (a abstract syntax tree) of the symbol space. + * + * @param body the new body + */ +void RplSymbolSpace::setBody(RplASItem* body) +{ + m_body = body; +} + /** * @brief Returns the name of the symbol space. * @@ -194,7 +220,7 @@ QString RplSymbolSpace::name() const * @brief Constructor. */ RplASBoolean::RplASBoolean() : - RplASClass("Boolean") + RplASClass("Bool") { } /** @@ -255,7 +281,7 @@ QString RplASBoolean::toString(void* object, int) const * @brief Constructor. */ RplASFloat::RplASFloat() : - RplASClass("Boolean") + RplASClass("Float") { } @@ -324,7 +350,7 @@ QString RplASFloat::toString(void* object, int) const * @brief Constructor. */ RplASInteger::RplASInteger() : - RplASFloat("Integer") + RplASFloat("Int") { } @@ -365,7 +391,7 @@ QString RplASInteger::toString(void* object, int maxLength) const * @brief Constructor. */ RplASString::RplASString() : - RplASClass("String") + RplASClass("Str") { } /** diff --git a/rplexpr/rplasclasses.hpp b/rplexpr/rplasclasses.hpp index 9221890..5b6481c 100644 --- a/rplexpr/rplasclasses.hpp +++ b/rplexpr/rplasclasses.hpp @@ -47,6 +47,9 @@ public: RplVariable* findVariable(const QString& name) const; RplASClass* findClass(const QString& name) const; void dump(FILE* fp, int indent, const char* header = NULL); + QString name() const; + RplASItem* body() const; + void setBody(RplASItem* body); public: static void initGlobal(RplSymbolSpace& global); static const char* spaceTypeName(SymbolSpaceType type); @@ -59,7 +62,6 @@ private: RplASItem* m_body; public: static RplSymbolSpace m_global; - QString name() const; }; class RplASBoolean : public RplASClass { diff --git a/rplexpr/rplastree.cpp b/rplexpr/rplastree.cpp index c280e31..428304a 100644 --- a/rplexpr/rplastree.cpp +++ b/rplexpr/rplastree.cpp @@ -317,7 +317,7 @@ QString RplASVariant::toString(int maxLength) const rc.sprintf("%d", m_value.m_int); break; case DT_OBJECT: - m_class->toString(m_value.m_object, maxLength); + rc = m_class->toString(m_value.m_object, maxLength); break; default: case DT_UNDEF: @@ -354,7 +354,7 @@ void RplASVariant::setObject(void* object, const RplASClass* clazz) */ RplASItem::RplASItem(RplASItemType type) : m_id(m_nextId++), - m_type(type), + m_nodeType(type), m_flags(0), m_position(NULL) { @@ -443,7 +443,7 @@ void RplASConstant::calc(RplASVariant& value) void RplASConstant::dump(FILE* fp, int indent) { DEFINE_TABS(indent); - fprintf(fp, "%sConstant %d value: %s\n", tabs, m_id, + fprintf(fp, "%sconst id: %d value: %s\n", tabs, m_id, m_value.toString().toUtf8().constData()); } @@ -464,25 +464,40 @@ RplASVariant& RplASConstant::value() /** @class RplASNamedValue rplastree.hpp "rplexpr/rplastree.hpp" * - * @brief Implements the abstract base class of all named values, e.g. variables. + * @brief Implements a named values, a constant or a variable * */ /** * @brief Constructor. * - * @param type the type of the variable * @param name the name of the instance - * @param attr a bitmask of Attribute values, e.g. A_CONST */ -RplASNamedValue::RplASNamedValue(RplASClass* type, - const QString& name, int attributes) : +RplASNamedValue::RplASNamedValue(const QString& name) : RplASNode1(AST_NAMED_VALUE), m_name(name), + m_attributes(0), + m_dataType(NULL) +{ +} + +/** + * @brief Constructor. + * + * @param itemType the node type, e.g. AST_VAR_DEFINITION + * @param dataType the data type (class) + * @param name the name of the variable + * @param attributes the attributes of the variable + */ +RplASNamedValue::RplASNamedValue(RplASItemType itemType, RplASClass* dataType, + const QString& name, int attributes) : + RplASNode1(itemType), + m_name(name), m_attributes(attributes), - m_type(type) + m_dataType(dataType) { } + /** * @brief Returns the name. * @@ -510,7 +525,61 @@ void RplASNamedValue::calc(RplASVariant& value) void RplASNamedValue::dump(FILE* fp, int indent) { DEFINE_TABS(indent); - fprintf(fp, "%sNamedValue %d attr: 0x%x\n", tabs, m_id, m_attributes); + fprintf(fp, "%snamedValue id: %d attr: 0x%x\n", tabs, + m_id, m_attributes); +} + +/** @class RplVarDefinition rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements variable definition for the Abstract Syntax Tree. + */ + +/** + * @brief Constructor. + * + * @param type the data type + * @param name the name of the variable + * @param attributes the attributes of the variable + */ +RplASVarDefinition::RplASVarDefinition(RplASClass* type, const QString& name, + int attributes) : + RplASNamedValue(AST_VAR_DEFINITION, type, name, attributes), + RplASStatement() +{ +} + +/** + * @brief Writes the internals into a file. + * + * @param fp target file + * @param indent nesting level + */ +void RplASVarDefinition::dump(FILE* fp, int indent) +{ + DEFINE_TABS(indent); + QByteArray name = m_name.toUtf8(); + QByteArray className = m_dataType == NULL ? "?" : m_dataType->name().toUtf8(); + fprintf(fp, "%svarDef %s (%s) id: %d succ: %d attr: 0x%x\n", + tabs, name.constData(), className.constData(), + m_id, m_successor == NULL ? 0 : m_successor->id(), m_attributes); + if (m_child != NULL) + m_child->dump(fp, indent + 1); + if (m_successor != NULL) + m_successor->dump(fp, indent); +} + +void RplASVarDefinition::execute() +{ + //@ToDo +} + +/** + * @brief Calculates the initialation value while interpreting. + * + * @param value OUT: ignored + */ +void RplASVarDefinition::calc(RplASVariant&) +{ } /** @class RplASNode1 rplastree.hpp "rplexpr/rplastree.hpp" @@ -896,7 +965,7 @@ const QString& RplASClass::name() const void RplASClass::dump(FILE* fp, int indent) { DEFINE_TABS(indent); - fprintf(fp, "%sClass %s super: %s\n", tabs, m_name.toUtf8().constData(), + fprintf(fp, "%sclass %s super: %s\n", tabs, m_name.toUtf8().constData(), m_superClass == NULL ? "" : m_superClass->name().toUtf8().constData()); QList sorted; @@ -975,10 +1044,9 @@ bool RplASTree::startModule(const QString& name) { bool rc = m_modules.contains(name); if (! rc){ - // m_modules[0] is the "global" symbol space. // freed in ~RplASTree() RplSymbolSpace* space = new RplSymbolSpace(RplSymbolSpace::SST_MODULE, - name, m_symbolSpaces[0]); + name, m_global); m_symbolSpaceHeap[name] = space; m_modules[name] = space; m_symbolSpaces.append(space); @@ -986,6 +1054,18 @@ bool RplASTree::startModule(const QString& name) } return rc; } +/** + * @brief Search for the symbol space of a given module. + * + * @param name the module's name + * @return NULL: not found
+ * otherwise: the symbol space of the module + */ +RplSymbolSpace* RplASTree::findmodule(const QString& name) +{ + RplSymbolSpace* rc = m_modules.contains(name) ? m_modules[name] : NULL; + return rc; +} /** * @brief Handles the end of a module. @@ -1213,13 +1293,15 @@ void RplASBinaryOp::setOperator(int op) void RplASBinaryOp::dump(FILE* fp, int indent) { DEFINE_TABS(indent); - fprintf(fp, "%sBinOp %d op: %d left: %d right: %d\n", tabs, m_id, m_operator, + const QByteArray& opName = RplLexer::m_active->nameOfOp(m_operator); + fprintf(fp, "%sBinOp %d op: %s (%d) left: %d right: %d\n", tabs, m_id, + opName.constData(), m_operator, m_child == NULL ? 0 : m_child->id(), m_child2 == NULL ? 0 : m_child2->id()); if (m_child != NULL) m_child->dump(fp, indent + 1); if (m_child2 != NULL) - m_child->dump(fp, indent + 1); + m_child2->dump(fp, indent + 1); } /** @class RplASMethod rplastree.hpp "rplexpr/rplastree.hpp" @@ -1237,7 +1319,7 @@ void RplASBinaryOp::dump(FILE* fp, int indent) RplASMethod::RplASMethod(const QString& name, RplASClass* type) : RplASNode2(AST_METHOD), m_name(name), - m_type(type) + m_nodeType(type) { } @@ -1251,7 +1333,7 @@ void RplASMethod::dump(FILE* fp, int indent) { DEFINE_TABS(indent); fprintf(fp, "%sMethod %d %s %s(", tabs, m_id, - m_type == NULL ? "" : m_type->name().toUtf8().constData(), + m_nodeType == NULL ? "" : m_nodeType->name().toUtf8().constData(), m_name.toUtf8().constData()); fprintf(fp, ") body: %d args: %d\n", m_child == NULL ? 0 : m_child->id(), m_child2 == NULL ? 0 : m_child2->id()); @@ -1269,3 +1351,4 @@ RplASArgument::RplASArgument() : RplASStatement() { } + diff --git a/rplexpr/rplastree.hpp b/rplexpr/rplastree.hpp index d5a265e..63b08a2 100644 --- a/rplexpr/rplastree.hpp +++ b/rplexpr/rplastree.hpp @@ -14,6 +14,7 @@ enum RplASItemType { AST_UNDEF, AST_CONSTANT, AST_NAMED_VALUE, + AST_VAR_DEFINITION, AST_METHOD, AST_ARGUMENT, AST_INTRINSIC_METHOD, @@ -126,7 +127,7 @@ public: virtual void dump(FILE* fp, int indent) = 0; protected: unsigned int m_id:16; - RplASItemType m_type:8; + RplASItemType m_nodeType:8; int m_flags:5; int m_dataType: 3; const RplSourcePosition* m_position; @@ -213,17 +214,43 @@ public: }; public: - RplASNamedValue(RplASClass* type, const QString& name, - int attributes = A_NONE); + RplASNamedValue(const QString& name); + RplASNamedValue(RplASItemType itemType, RplASClass* dataType, + const QString& name, int attributes); public: QString name() const; public: virtual void calc(RplASVariant& value); void dump(FILE* fp, int indent); -private: +protected: QString m_name; int m_attributes; - RplASClass* m_type; + RplASClass* m_dataType; +}; + +class RplASStatement +{ +public: + RplASStatement(); + virtual ~RplASStatement(); +public: + virtual void execute() = 0; + RplASItem* successor() const; + void setSuccessor(RplASItem* successor); +protected: + RplASItem* m_successor; +}; + + +class RplASVarDefinition : public RplASNamedValue, public RplASStatement +{ +public: + RplASVarDefinition(RplASClass* type, const QString& name, + int attributes = A_NONE); +public: + virtual void execute(); + virtual void calc(RplASVariant& value); + void dump(FILE* fp, int indent); }; class RplASUnaryOp : public RplASNode1 @@ -249,20 +276,6 @@ private: int m_operator; }; -class RplASStatement -{ -public: - RplASStatement(); - virtual ~RplASStatement(); -public: - virtual void execute() = 0; - RplASItem* successor() const; - void setSuccessor(RplASItem* successor); - -private: - RplASItem* m_successor; -}; - class RplASCondition : public RplASNode1, public RplASCalculable { public: @@ -338,7 +351,7 @@ public: void dump(FILE* fp, int indent); private: QString m_name; - RplASClass* m_type; + RplASClass* m_nodeType; // body is m_child // param1 is m_child2 }; @@ -420,6 +433,7 @@ public: RplASClass* findClass(const QString& name); void clear(); void dump(const char* filename, int flags = DMP_ALL); + RplSymbolSpace*findmodule(const QString& name); protected: void init(); void destroy(); diff --git a/rplexpr/rpllexer.cpp b/rplexpr/rpllexer.cpp index a1c4922..82a0f8a 100644 --- a/rplexpr/rpllexer.cpp +++ b/rplexpr/rpllexer.cpp @@ -11,6 +11,8 @@ #define CHAR_INFO_SIZE (int(sizeof m_charInfo / sizeof m_charInfo[0])) +RplLexer* RplLexer::m_active = NULL; + /** @class RplToken rpllexer.hpp "rplexpr/rpllexer.hpp" * * @brief Implements specific exception for the lexer. @@ -362,12 +364,16 @@ RplLexer::RplLexer(RplSource* source, charClassToCharInfo(restCharsId, CC_REST_ID, m_charInfo); initializeComments(comments); m_input.reserve(m_maxTokenLength*2); + if (m_active == NULL) + m_active = this; } /** * @brief Destructor. */ RplLexer::~RplLexer() { + if (m_active == this) + m_active = NULL; } /** @@ -1017,6 +1023,18 @@ int RplLexer::prioOfOp(int op) const return rc; } +/** + * @brief Returns the name of an operator. + * + * @param op the operator id + * @return the name of the operator + */ +const QByteArray&RplLexer::nameOfOp(int op) const +{ + const QByteArray& rc = m_opNames.at(op); + return rc; +} + /** * @brief Returns whether an operator is right associative * @param op op to test diff --git a/rplexpr/rpllexer.hpp b/rplexpr/rpllexer.hpp index c624986..d11720b 100644 --- a/rplexpr/rpllexer.hpp +++ b/rplexpr/rpllexer.hpp @@ -228,6 +228,8 @@ protected: char m_prioOfOp[128]; char m_assocOfOp[128]; QList m_opNames; +public: + static RplLexer* m_active; }; diff --git a/rplexpr/rplmfparser.cpp b/rplexpr/rplmfparser.cpp index 8306aae..f3a4203 100644 --- a/rplexpr/rplmfparser.cpp +++ b/rplexpr/rplmfparser.cpp @@ -15,12 +15,13 @@ enum MFLocations{ L_PARSE_OPERAND_RPARENTH_FUNC, L_TERM_WRONG_STRING, L_TERM_WRONG_NUMBER, - L_PARSE_OPERAND_WRONG = 2005, + L_PARSE_OPERAND_WRONG = 2005, L_DEFINITION_NO_ID, L_DEFINITION_WRONG_ID, L_DEFINITION_UNKNOWN_CLASS, L_DEFINITION_MISSING_ID, - L_DEFINITION_NO_OP + L_DEFINITION_NO_OP = 2010, + L_DEFINITION_NO_SEMICOLON }; @@ -99,10 +100,10 @@ RplASItem* RplMFParser::parseFor() * @param clazz NULL or the type of the variable * @param attribute 0 or attribute of the variable: K_CONST or K_LAZY */ -RplASItem* RplMFParser::parseDefinition(Keyword attribute) +RplASItem* RplMFParser::parseVarDefinition(Keyword attribute) { RplASNamedValue::Attributes attr = RplASNamedValue::A_NONE; - RplToken* token; + RplToken* token = m_lexer.currentToken(); while(attribute == K_CONST || attribute == K_LAZY){ switch(attribute){ case K_CONST: @@ -118,7 +119,7 @@ RplASItem* RplMFParser::parseDefinition(Keyword attribute) attribute = token->isTokenType(TOKEN_KEYWORD) ? (Keyword) token->id() : K_UNDEF; } - if (token->isTokenType(TOKEN_ID)) + if (! token->isTokenType(TOKEN_ID)) syntaxError(L_DEFINITION_NO_ID, "class name expected, but no id found"); if (! token->isCapitalizedId()) syntaxError(L_DEFINITION_WRONG_ID, @@ -129,7 +130,8 @@ RplASItem* RplMFParser::parseDefinition(Keyword attribute) token = m_lexer.nextNonSpaceToken(); if (! token->isTokenType(TOKEN_ID)) syntaxError(L_DEFINITION_MISSING_ID, "variable name expected"); - RplASNamedValue* rc = new RplASNamedValue(clazz, token->toString(), attr); + RplASNamedValue* rc = new RplASVarDefinition(clazz, token->toString(), attr); + rc->setPosition(m_lexer.currentPosition()); token = m_lexer.nextNonSpaceToken(); if (! token->isOperator(O_ASSIGN, O_SEMICOLON)) syntaxError(L_DEFINITION_NO_OP, "'=' or ';' expected"); @@ -138,8 +140,8 @@ RplASItem* RplMFParser::parseDefinition(Keyword attribute) rc->setChild(value); token = m_lexer.currentToken(); } - if (token->isOperator(O_SEMICOLON)){ - syntaxError(L_DEFINITION_NO_OP, "';' expected"); + if (! token->isOperator(O_SEMICOLON)){ + syntaxError(L_DEFINITION_NO_SEMICOLON, "';' expected"); } return rc; } @@ -181,6 +183,7 @@ RplASItem* RplMFParser::parseOperand(int level) case TOKEN_REAL: { RplASConstant* constant = new RplASConstant(); + constant->setPosition(m_lexer.currentPosition()); rc = constant; switch(token->tokenType()){ case TOKEN_STRING: @@ -260,11 +263,11 @@ RplASItem* RplMFParser::parseTerm(int depth){ switch(tokenType){ case TOKEN_OPERATOR: { - if (IS_BINARY_OP(tokenType)){ + Operator opId = (Operator) token->id(); + if (IS_BINARY_OP(opId)){ RplASBinaryOp* op = new RplASBinaryOp(); - int opId = token->id(); - op->setOperator(opId); op->setPosition(m_lexer.currentPosition()); + op->setOperator(opId); int prio = m_lexer.prioOfOp(token->id()); if (prio < lastPrio @@ -277,6 +280,7 @@ RplASItem* RplMFParser::parseTerm(int depth){ op->setChild(top2->child2()); top2->setChild2(op); } + lastPrio = prio; op->setChild2(parseOperand(depth)); } else again = false; @@ -327,72 +331,102 @@ RplASItem* RplMFParser::parseBody() RplASStatement* lastStatement = NULL; bool again = true; while(again) { - switch(token->tokenType()) - { - case TOKEN_STRING: - case TOKEN_NUMBER: - case TOKEN_REAL: - case TOKEN_OPERATOR: - m_lexer.undoLastToken(); - item = parseExpr(); - break; - case TOKEN_KEYWORD: - switch (token->id()){ - case K_IF: - item = parseIf(); - break; - case K_WHILE: - item = parseWhile(); - break; - case K_REPEAT: - item = parseRepeat(); - break; - case K_FOR: - item = parseFor(); - break; - case K_CLASS: - item = parseClass(); - break; - case K_FUNCTION: - case K_GENERATOR: - item = parseMethodDefinition(); + token = m_lexer.currentToken(); + try { + switch(token->tokenType()) + { + case TOKEN_STRING: + case TOKEN_NUMBER: + case TOKEN_REAL: + case TOKEN_OPERATOR: + m_lexer.undoLastToken(); + item = parseExpr(); break; - case K_IMPORT: - parseImport(); + case TOKEN_KEYWORD: + switch (token->id()){ + case K_IF: + item = parseIf(); + break; + case K_WHILE: + item = parseWhile(); + break; + case K_REPEAT: + item = parseRepeat(); + break; + case K_FOR: + item = parseFor(); + break; + case K_CLASS: + item = parseClass(); + break; + case K_FUNCTION: + case K_GENERATOR: + item = parseMethodDefinition(); + break; + case K_IMPORT: + parseImport(); + break; + case K_CONST: + case K_LAZY: + item = parseVarDefinition((Keyword) token->id()); + break; + default: + break; + } break; - case K_CONST: - case K_LAZY: - item = parseDefinition(NULL, (Keyword) token->id()); + case TOKEN_ID: + { + if (token->isCapitalizedId()){ + item = parseVarDefinition(K_UNDEF); + } else { + m_lexer.undoLastToken(); + item = parseExpr(); + } break; - case K_BOOL: - parseDefinition(&RplASBoolean::m_instance, K_UNDEF); + } + case TOKEN_END_OF_SOURCE: + again = false; break; default: break; } - break; - case TOKEN_ID: - { - if (token->isCapitalizedId()){ - item = parseDefinition(K_UNDEF); - } else { - m_lexer.undoLastToken(); - item = parseExpr(); + if (! token->isTokenType(TOKEN_END_OF_SOURCE)){ + if (body == NULL){ + body = item; + } else { + lastStatement->setSuccessor(item); + } + lastStatement = dynamic_cast(item); + if (lastStatement == NULL) + assert("wrong item type" == NULL); + token = m_lexer.nextNonSpaceToken(); } - break; - } - case TOKEN_END_OF_SOURCE: - again = false; - break; - default: - break; - } - if (body == NULL){ - body = item; - } else { - lastStatement->setSuccessor(item); + } catch(RplSyntaxError exc){ + // we look for the end of the statement: + token = m_lexer.currentToken(); + RplTokenType type; + Operator op; + Keyword key; + while( (type = token->tokenType()) != TOKEN_END_OF_SOURCE) + if (type == TOKEN_OPERATOR + && ((op = Operator(token->id())) == O_SEMICOLON + || op == O_SEMI_SEMICOLON)) + break; + else if (type == TOKEN_KEYWORD){ + key = Keyword(token->id()); + if (key == K_ENDC || key == K_ENDF){ + // we need the token again! + m_lexer.undoLastToken(); + break; + } else if (key == K_FI || key == K_OD){ + break; + } else { + token = m_lexer.nextNonSpaceToken(); + } + } else { + token = m_lexer.nextNonSpaceToken(); + } } - lastStatement = dynamic_cast(item); } return body; } @@ -445,7 +479,18 @@ RplASItem* RplMFParser::parseModule(const QString& name) */ void RplMFParser::parse() { - + RplSource* source = m_lexer.source(); + RplSourceUnit* mainModule = source->currentReader()->currentSourceUnit(); + QString mainModuleName = mainModule->name(); + m_tree.startModule(mainModuleName); + try { + RplASItem* body = parseBody(); + RplSymbolSpace* module = m_tree.findmodule(mainModuleName); + if (module != NULL) + module->setBody(body); + } catch(RplParserStop exc){ + printf("compiling stopped: %s\n", exc.reason()); + } } /** diff --git a/rplexpr/rplmfparser.hpp b/rplexpr/rplmfparser.hpp index d2fa98c..c888be0 100644 --- a/rplexpr/rplmfparser.hpp +++ b/rplexpr/rplmfparser.hpp @@ -74,7 +74,7 @@ public: RplASItem* parseWhile(); RplASItem* parseRepeat(); RplASItem* parseFor(); - RplASItem* parseDefinition(Keyword attribute); + RplASItem* parseVarDefinition(Keyword attribute); RplASItem* parseExpr(); RplASItem* parseBody(); RplASItem* parseMethodDefinition(); @@ -87,6 +87,8 @@ protected: RplASItem* parseOperand(int level); RplASItem* parseTerm(int depth); private: + ///syntax token builder. + /// Note: the super class contains a reference with the same name RplLexer m_lexer; }; diff --git a/rplexpr/rplparser.cpp b/rplexpr/rplparser.cpp index ff68682..52b389a 100644 --- a/rplexpr/rplparser.cpp +++ b/rplexpr/rplparser.cpp @@ -31,6 +31,15 @@ RplSyntaxError::RplSyntaxError(const char* reason) : m_reason(reason) { } +/** + * @brief Returns the description of the exception. + * + * @return the reason + */ +const char* RplSyntaxError::reason() const +{ + return m_reason; +} /** @class RplParserStop rplparser.hpp "rplexpr/rplparser.hpp" * diff --git a/rplexpr/rplparser.hpp b/rplexpr/rplparser.hpp index 333b2b0..e9334ac 100644 --- a/rplexpr/rplparser.hpp +++ b/rplexpr/rplparser.hpp @@ -14,6 +14,8 @@ class RplSyntaxError { public: RplSyntaxError(const char* reason); +public: + const char* reason() const; private: const char* m_reason; }; @@ -36,7 +38,7 @@ protected: void addMessage(char prefix, int location, const char* format, va_list varList); protected: - RplLexer m_lexer; + RplLexer& m_lexer; RplASTree& m_tree; MessageList m_messages; int m_errors; diff --git a/test/rplmfparser/baseTest.txt b/test/rplmfparser/baseTest.txt new file mode 100644 index 0000000..0fe593c --- /dev/null +++ b/test/rplmfparser/baseTest.txt @@ -0,0 +1,18 @@ +=== Globals: += global (global) parent: +== Classes: + class Boolean super: + class Boolean super: + class Integer super: Float + class List super: + class Map super: + class String super: +== Variables: +== Body: + = (module) parent: global + == Classes: + == Variables: + == Body: + BinOp 2 op: + (26) left: 1 right: 3 + Constant 1 value: 2 + Constant 3 value: 3 diff --git a/test/rplmfparser/defTest.txt b/test/rplmfparser/defTest.txt new file mode 100644 index 0000000..8863957 --- /dev/null +++ b/test/rplmfparser/defTest.txt @@ -0,0 +1,20 @@ +=== Globals: += global (global) parent: +== Classes: + class Bool super: + class Float super: + class Int super: Float + class List super: + class Map super: + class Str super: +== Variables: +== Body: + = (module) parent: global + == Classes: + == Variables: + == Body: + varDef i (Int) id: 1 succ: 3 attr: 0x0 + const id: 2 value: 3 + varDef s (Str) id: 3 succ: 5 attr: 0x10 + const id: 4 value: 'Hi' + varDef l (List) id: 5 succ: 0 attr: 0x2 diff --git a/unittests/main.cpp b/unittests/main.cpp index 274a93c..e9fc42b 100644 --- a/unittests/main.cpp +++ b/unittests/main.cpp @@ -22,6 +22,9 @@ void testCore(){ } void testExpr(){ + extern void testRplMFParser(); + testRplMFParser(); + extern void testRplASTree(); testRplASTree(); extern void testRplSource(); diff --git a/unittests/rplmfparser_test.cpp b/unittests/rplmfparser_test.cpp index 5d7d7cb..6fe622e 100644 --- a/unittests/rplmfparser_test.cpp +++ b/unittests/rplmfparser_test.cpp @@ -10,7 +10,7 @@ #include "rplexpr/rplexpr.hpp" #include "rplcore/rpltest.hpp" -class TestRplMFParser : public RplTest, public RplMFParser{ +class TestRplMFParser : public RplTest{ private: RplSource m_source; RplASTree m_tree; @@ -18,7 +18,6 @@ private: public: TestRplMFParser() : RplTest("RplMFParser"), - RplMFParser(m_source, m_tree), m_source(), m_tree(), m_reader(m_source) @@ -31,6 +30,21 @@ protected: m_tree.clear(); m_source.clear(); m_reader.replaceSource("", content); + m_source.addReader(&m_reader); + m_source.addSourceUnit(m_reader.currentSourceUnit()); + } + +private: + void checkAST(const char* fileExpected, int lineNo){ + QByteArray fnExpected = "test"; + fnExpected += QDir::separator().toLatin1(); + fnExpected += "rplmfparser"; + fnExpected += (char) QDir::separator().toLatin1(); + fnExpected += fileExpected; + QByteArray fnCurrent = getTempFile(fileExpected, "rplmfparser"); + m_tree.dump(fnCurrent); + assertEqualFiles(fnExpected.constData(), fnCurrent.constData(), + __FILE__, lineNo); } public: @@ -38,17 +52,19 @@ public: setSource("2+3*4"); RplMFParser parser(m_source, m_tree); parser.parse(); + checkAST("baseTest.txt", __LINE__); } void defTest(){ - setSource("int i = 3; Str s = 'Hi; List l = [3, 'v4'];"); + setSource("Int i = 3; const lazy Str s = 'Hi'; const List l;"); RplMFParser parser(m_source, m_tree); parser.parse(); + checkAST("defTest.txt", __LINE__); } virtual void doIt(void) { - //baseTest(); - //defTest(); + defTest(); + baseTest(); } }; void testRplMFParser() { -- 2.39.5