From 8f7c3cda1b2246322095d6a8014231b97a8899fa Mon Sep 17 00:00:00 2001 From: hama Date: Sat, 19 Jul 2014 00:22:15 +0200 Subject: [PATCH] dayly work --- rplexpr/rplasclasses.cpp | 16 +- rplexpr/rplastree.cpp | 100 +++++++++- rplexpr/rplastree.hpp | 3 +- rplexpr/rpllexer.cpp | 6 +- rplexpr/rpllexer.hpp | 1 - rplexpr/rplmfparser.cpp | 345 +++++++++++++++++++++++---------- rplexpr/rplmfparser.hpp | 18 +- test/rplmfparser/map1.txt | 8 + test/rplmfparser/map2.txt | 12 ++ unittests/main.cpp | 4 +- unittests/rplmfparser_test.cpp | 16 +- 11 files changed, 398 insertions(+), 131 deletions(-) create mode 100644 test/rplmfparser/map1.txt create mode 100644 test/rplmfparser/map2.txt diff --git a/rplexpr/rplasclasses.cpp b/rplexpr/rplasclasses.cpp index e780bd0..246b241 100644 --- a/rplexpr/rplasclasses.cpp +++ b/rplexpr/rplasclasses.cpp @@ -590,12 +590,12 @@ RplASMap::RplASMap() : */ void* RplASMap::newValueInstance(void* source) const { - RplASMapValueType* rc = new RplASMapValueType(); + RplASMapOfVariants* rc = new RplASMapOfVariants(); if (source != NULL){ - RplASMapValueType* source2 = - static_cast(source); + RplASMapOfVariants* source2 = + static_cast(source); // rc->reserve(source2->size()); - RplASMapValueType::iterator it; + RplASMapOfVariants::iterator it; for (it = source2->begin(); it != source2->end(); it++){ // deleting in destroyValue(): const QString& key = it.key(); @@ -615,7 +615,7 @@ void* RplASMap::newValueInstance(void* source) const */ void RplASMap::destroyValueInstance(void* object) const { - delete (RplASMapValueType*) object; + delete (RplASMapOfVariants*) object; } /** @@ -628,7 +628,7 @@ bool RplASMap::boolValueOf(void* object) const { bool rc = false; if (object != NULL){ - RplASMapValueType* map = reinterpret_cast(object); + RplASMapOfVariants* map = reinterpret_cast(object); if (map == NULL) throw RplException("RplASMap.boolValueOf(): not a map"); rc = map->empty() > 0; @@ -647,8 +647,8 @@ QString RplASMap::toString(void* object, int maxLength) const QString rc; rc.reserve(maxLength); rc += "["; - RplASMapValueType* map = reinterpret_cast(object); - RplASMapValueType::iterator it; + RplASMapOfVariants* map = reinterpret_cast(object); + RplASMapOfVariants::iterator it; bool first = true; for(it = map->begin(); it != map->end(); it++){ if (first) diff --git a/rplexpr/rplastree.cpp b/rplexpr/rplastree.cpp index fd74edb..a496d41 100644 --- a/rplexpr/rplastree.cpp +++ b/rplexpr/rplastree.cpp @@ -16,6 +16,35 @@ unsigned int RplASItem::m_nextId = 1; memset(tabs, '\t', sizeof tabs); \ tabs[(unsigned) indent < sizeof tabs ? indent : sizeof tabs - 1] = '\0' +/** + * @brief Writes a map into a file. + * + * The output is sorted by key. + * + * @param fp target file + * @param map map to dump + * @param withEndOfLine true: '\n' will be written at the end + */ +void dumpMap(FILE* fp, RplASMapOfVariants& map, bool withEndOfLine) +{ + QList sorted; + sorted.reserve(map.size()); + RplASMapOfVariants::iterator it; + for (it = map.begin(); it != map.end(); it++){ + sorted.append(it.key()); + } + qSort(sorted.begin(), sorted.end(), qLess()); + QList::iterator it2; + bool first = true; + for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ + RplASVariant* value = map[*it2]; + fprintf(fp, "%c'%s':%s", first ? '{' : ',', (*it2).toUtf8().constData(), + value->toString().toUtf8().constData()); + first = false; + } + fprintf(fp, "%s}%s", first ? "{" : "", withEndOfLine ? "\n" : ""); +} + /** @class RplASException rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements a specific exception for the Abstract Syntax Tree. @@ -583,11 +612,74 @@ RplASVariant& RplASListConstant::value() return m_value; } +/** @class RplASMapConstant rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a hash map for constant list entries. + * + */ +/** + * @brief RplASMapConstant::RplASMapConstant + */ +RplASMapConstant::RplASMapConstant() : + RplASNode1(AST_MAP_CONSTANT), + RplASCalculable(), + m_value() +{ + m_value.setObject(new RplASMapOfVariants, &RplASMap::m_instance); +} + +/** + * @brief Calculates the value. + * + * @param value OUT: the value of the instance + */ +void RplASMapConstant::calc(RplASVariant&) +{ + +} +/** + * @brief Writes the internals into a file. + * + * @param fp target file + * @param indent nesting level + */ +void RplASMapConstant::dump(FILE* fp, int indent) +{ + DEFINE_TABS(indent); + char buffer[256]; + fprintf(fp, "%smapConst id: %d %s\n%s", tabs, + m_id, positionStr(buffer, sizeof buffer), tabs); + dumpMap(fp, *map(), true); +} + + +/** + * @brief Returns the value of the constant, containing a map. + * + * @return the variant value + */ +RplASVariant& RplASMapConstant::value() +{ + return m_value; +} + +/** + * @brief Returns the (low level) map of the constant. + * + * @return the map of the constant + */ +RplASMapOfVariants* RplASMapConstant::map() +{ + RplASMapOfVariants* rc = static_cast( + m_value.asObject(NULL)); + return rc; +} /** @class RplASNamedValue rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements a named values, a constant or a variable * + * m_child: NULL or the index expression (map/list) */ /** @@ -634,7 +726,7 @@ QString RplASNamedValue::name() const * * @param value OUT: the value of the instance */ -void RplASNamedValue::calc(RplASVariant& value) +void RplASNamedValue::calc(RplASVariant& ) { } @@ -738,7 +830,7 @@ void RplASExprStatement::execute() * * @param value OUT: the calculated value */ -void RplASExprStatement::calc(RplASVariant& value) +void RplASExprStatement::calc(RplASVariant& ) { } @@ -1308,7 +1400,7 @@ void RplASForCounted::execute() if (m_child2 != NULL){ var = dynamic_cast(m_child2); } - for(int ii = start; ii <= end; ii++){ + for(int ii = start; ii <= end; ii += step){ body->execute(); } } @@ -1888,3 +1980,5 @@ RplASArgument::RplASArgument() : RplASNode2(AST_ARGUMENT) { } + + diff --git a/rplexpr/rplastree.hpp b/rplexpr/rplastree.hpp index 64537af..776bf85 100644 --- a/rplexpr/rplastree.hpp +++ b/rplexpr/rplastree.hpp @@ -232,7 +232,7 @@ protected: RplASItem* m_child5; }; typedef QList RplASListOfVariants; -typedef QMap RplASMapValueType; +typedef QMap RplASMapOfVariants; class RplASListConstant : public RplASNode1, public RplASCalculable { @@ -256,6 +256,7 @@ public: public: virtual void dump(FILE* fp, int indent); RplASVariant& value(); + RplASMapOfVariants* map(); private: RplASVariant m_value; }; diff --git a/rplexpr/rpllexer.cpp b/rplexpr/rpllexer.cpp index 414e394..0080407 100644 --- a/rplexpr/rpllexer.cpp +++ b/rplexpr/rpllexer.cpp @@ -543,10 +543,6 @@ int RplLexer::findInVector(int tokenLength, const StringList& vector) } return id; } -const char* RplLexer::nextText(int& length, bool &isLast) -{ - return NULL; -} /** * @brief Reads data until enough data are available for one token. * @@ -578,7 +574,7 @@ bool RplLexer::fillInput() /** * @brief Finds a token with an id: TOKEN_OP, TOKEN_KEYWORD, ... * - * @postcond the token is removed from the input + * @post the token is removed from the input * * @param tokenType the token type * @param flag2 the flag of the 2nd char diff --git a/rplexpr/rpllexer.hpp b/rplexpr/rpllexer.hpp index 4c385f5..5d23de7 100644 --- a/rplexpr/rpllexer.hpp +++ b/rplexpr/rpllexer.hpp @@ -189,7 +189,6 @@ private: void initializeComments(const char* comments); bool fillInput(); int findInVector(int tokenLength, const StringList& vector); - const char* nextText(int& length, bool &isLast); RplToken* findTokenWithId(RplTokenType tokenType, int flag2, StringList& names); RplToken* scanNumber(); diff --git a/rplexpr/rplmfparser.cpp b/rplexpr/rplmfparser.cpp index 202dffa..23239a8 100644 --- a/rplexpr/rplmfparser.cpp +++ b/rplexpr/rplmfparser.cpp @@ -31,8 +31,17 @@ enum MFLocations{ L_PARSE_REPEAT_NO_SEMI, L_PARSE_BODY_WRONG_ITEM, L_PARSE_FOR_NO_TO = 2020, - L_PARSE_LIST_NO_COMMA - + L_PARSE_LIST_NO_COMMA, + L_PARSE_MAP_BOOL, + L_PARSE_MAP_NONE, + L_PARSE_MAP_NUMERIC, + L_PARSE_MAP_EXPR = 2025, + L_PARSE_MAP_EXPR2, + L_PARSE_MAP_NO_COLON, + L_PARSE_MAP_NO_COMMA, + L_PARSE_OPERAND_NOT_OPERAND = 2030, + L_PARSE_BODY_NO_START, + L_PARSE_OPERAND_NO_BRACKET }; /** @class RplMFParser rpllexer.hpp "rplexpr/rplmfparser.hpp" @@ -84,6 +93,7 @@ RplASItem* RplMFParser::parseIf() } if (! m_lexer.currentToken()->isKeyword(K_FI)) syntaxError(L_PARSE_IF_NO_FI, "'fi' expected"); + m_lexer.nextNonSpaceToken(); return rc; } /** @@ -103,6 +113,7 @@ RplASItem* RplMFParser::parseWhile() rc->setChild(body); if (! m_lexer.currentToken()->isKeyword(K_OD)) syntaxError(L_PARSE_WHILE_NO_OD, "'od' expected"); + m_lexer.nextNonSpaceToken(); return rc; } @@ -123,6 +134,7 @@ RplASItem* RplMFParser::parseRepeat() if (! m_lexer.currentToken()->isOperator(O_SEMICOLON)) syntaxError(L_PARSE_REPEAT_NO_SEMI, "';' expected"); rc->setChild2(condition); + m_lexer.nextNonSpaceToken(); return rc; } @@ -135,6 +147,8 @@ RplASItem* RplMFParser::parseRepeat() * * for in do od * + * @post the token behind the do is read + * @return the abstract syntax tree of the for statement */ RplASItem* RplMFParser::parseFor() { @@ -160,17 +174,18 @@ RplASItem* RplMFParser::parseFor() if (token->isKeyword(K_FROM)){ node->setChild3(parseExpr(0)); } - if (! token->isKeyword(K_TO)){ + if (! m_lexer.currentToken()->isKeyword(K_TO)){ syntaxError(L_PARSE_FOR_NO_TO, "'to' expected"); } node->setChild4(parseExpr(0)); - if (token->isKeyword(K_STEP)){ + if (m_lexer.currentToken()->isKeyword(K_STEP)){ node->setChild5(parseExpr(0)); } } - if (! token->isKeyword(K_DO)) + if (! m_lexer.currentToken()->isKeyword(K_DO)) syntaxError(L_PARSE_FOR_NO_TO, "'to' expected"); rc->setChild(parseBody(K_OD)); + m_lexer.nextNonSpaceToken(); return rc; } @@ -229,17 +244,102 @@ RplASItem* RplMFParser::parseVarDefinition(Keyword attribute) if (! token->isOperator(O_SEMICOLON)){ syntaxError(L_DEFINITION_NO_SEMICOLON, "';' expected"); } + m_lexer.nextNonSpaceToken(); return rc; } +/** + * @brief Reads the current tokens as an formula and returns the variant. + * + * @post the token behind the formula is read + * @param parent the parent for the formula: because of the destroying + * the new expression is chained into the parent + * @return the variant containing the formula + */ +RplASVariant* RplMFParser::createFormula(RplASNode1* parent) +{ + RplASVariant* variant = NULL; + m_lexer.undoLastToken2(); + RplASExprStatement* expr = dynamic_cast + (parseExprStatement(false)); + if (expr != NULL){ + // chaining per m_successor is for freeing while destruction: + expr->setSuccessor(parent->child()); + parent->setChild(expr); + // freed in the destructor of varList (~RplASVariant()): + variant = new RplASVariant(); + variant->setObject(expr, &RplASFormula::m_instance); + } + return variant; +} + +/** + * @brief Converts the current expression into a RplASVariant. + * + * If the expression is a constant, the constant value will be the content + * of the variant. Otherwise a formula will be stored. + * + * @pre the first token of the variant expression is read + * @post the token behind the variant expression is read + * @param token the token to convert + * @param endsWithComma true: the 2nd token is a ',' + * @param parent the parent node of the expression + * @return the variant with the token's content + */ +RplASVariant* RplMFParser::tokenToVariant(RplToken* token, + bool endsWithComma, RplASNode1* parent) +{ + RplASVariant* variant = NULL; + if (endsWithComma){ + switch(token->tokenType()){ + case TOKEN_NUMBER: + // freed in the destructor of varList (~RplASVariant()): + variant = new RplASVariant(); + variant->setInt(token->asInteger()); + break; + case TOKEN_STRING: + // freed in the destructor of varList (~RplASVariant()): + variant = new RplASVariant(); + variant->setString(token->toString()); + break; + case TOKEN_REAL: + // freed in the destructor of varList (~RplASVariant()): + variant = new RplASVariant(); + variant->setFloat(token->asReal()); + break; + case TOKEN_KEYWORD: + switch(token->id()){ + case K_TRUE: + case K_FALSE: + // freed in the destructor of varList (~RplASVariant()): + variant = new RplASVariant(); + variant->setBool(token->id() == K_TRUE); + break; + case K_NONE: + // freed in the destructor of varList (~RplASVariant()): + variant = new RplASVariant(); + break; + default: + break; + } + break; + default: + break; + } + } + if (variant == NULL) + variant = createFormula(parent); + return variant; +} + /** * @brief Parses a list. * * Syntax:
* '[' [ [ ',' ...]] ']' * - * @precondition '[' is the current token - * @postcondition the token behind the ']' is read + * @pre '[' is the current token + * @post the token behind the ']' is read * @return a node of the abstract syntax tree */ RplASItem* RplMFParser::parseList() @@ -253,69 +353,23 @@ RplASItem* RplMFParser::parseList() bool again = true; RplToken* token; RplToken* token2; - while(again){ - token = m_lexer.nextNonSpaceToken(); - if (token->isOperator(O_RBRACKET)) - again = false; - else{ - variant = NULL; + // read the token behind '[': + token = m_lexer.nextNonSpaceToken(); + if (token->isOperator(O_RBRACKET)){ + // read token behind ']': + m_lexer.nextNonSpaceToken(); + } else{ + while(again){ m_lexer.saveLastToken(); token2 = m_lexer.nextNonSpaceToken(); - if (token2->isOperator(O_COMMA)){ - switch(token->tokenType()){ - case TOKEN_NUMBER: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setInt(token->asInteger()); - break; - case TOKEN_STRING: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setString(token->toString()); - break; - case TOKEN_REAL: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setFloat(token->asReal()); - break; - case TOKEN_KEYWORD: - switch(token->id()){ - case K_TRUE: - case K_FALSE: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setBool(token->id() == K_TRUE); - break; - case K_NONE: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - break; - default: - break; - } - break; - default: - break; - } - } - if (variant == NULL && ! token->isOperator(O_RBRACKET)){ - m_lexer.undoLastToken2(); - RplASExprStatement* expr = dynamic_cast - (parseExprStatement()); - if (expr != NULL){ - // chaining per m_successor is for freeing in destruction: - expr->setSuccessor(rc->child()); - rc->setChild(expr); - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setObject(expr, &RplASFormula::m_instance); - token = m_lexer.currentToken(); - if (token->isOperator(O_RBRACKET)) - again = false; - else if (! token->isOperator(O_COMMA)) - syntaxError(L_PARSE_LIST_NO_COMMA, "',' or ']' expected"); - } - } + variant = tokenToVariant(token, token2->isOperator(O_COMMA), rc); + token = m_lexer.currentToken(); + if (token->isOperator(O_RBRACKET)) + again = false; + else if (! token->isOperator(O_COMMA)) + syntaxError(L_PARSE_LIST_NO_COMMA, "',' or ']' expected"); + // read token behind ',' or ']' + token = m_lexer.nextNonSpaceToken(); if (variant != NULL) list->append(variant); } @@ -323,19 +377,87 @@ RplASItem* RplMFParser::parseList() return rc; } + /** * @brief Parses a map. * * Syntax:
* '{' [ ':' [ ',' ': ...]] '}' * - * @precondition '{' is the current token - * @postcondition the token behind the '}' is read + * @pre '{' is the current token + * @post the token behind the '}' is read * @return a node of the abstract syntax tree */ RplASItem* RplMFParser::parseMap() { - return NULL; + RplASMapConstant* rc = new RplASMapConstant(); + RplASVariant& varMap = rc->value(); + RplASMapOfVariants* map = static_cast + (varMap.asObject(NULL)); + rc->setPosition(m_lexer.currentPosition()); + RplASVariant* variant; + bool again = true; + RplToken* token; + RplToken* token2; + QString key; + while(again){ + token = m_lexer.nextNonSpaceToken(); + if (token->isOperator(O_RBRACE)) + again = false; + else{ + key.clear(); + switch(token->tokenType()){ + case TOKEN_STRING: + // freed in the destructor of varList (~RplASVariant()): + key = token->toString(); + break; + case TOKEN_KEYWORD: + switch(token->id()){ + case K_TRUE: + case K_FALSE: + syntaxError(L_PARSE_MAP_BOOL, + "boolean value not allowed as key type. Use a string"); + break; + case K_NONE: + syntaxError(L_PARSE_MAP_NONE, + "'none' is not allowed as key type. Use a string"); + break; + default: + syntaxError(L_PARSE_MAP_EXPR, + "a non constant expression is not allowed as key type. Use a string"); + break; + } + break; + case TOKEN_NUMBER: + case TOKEN_REAL: + syntaxError(L_PARSE_MAP_NUMERIC, + "numeric values not allowed as key type. Use a string"); + break; + default: + syntaxError(L_PARSE_MAP_EXPR2, + "a non constant expression is not allowed as key type. Use a string"); + break; + } + token = m_lexer.nextNonSpaceToken(); + if (! token->isOperator(O_COLON)){ + syntaxError(L_PARSE_MAP_NO_COLON, "':' expected"); + } else { + token = m_lexer.nextNonSpaceToken(); + m_lexer.saveLastToken(); + token2 = m_lexer.nextNonSpaceToken(); + variant = tokenToVariant(token, token2->isOperator(O_COMMA), rc); + (*map)[key] = variant; + variant = NULL; + token = m_lexer.currentToken(); + if (token->isOperator(O_RBRACE)) + again = false; + else if (! token->isOperator(O_COMMA)) + syntaxError(L_PARSE_MAP_NO_COMMA, "',' or '}' expected"); + } + } + } + m_lexer.nextNonSpaceToken(); + return rc; } /** @@ -344,6 +466,7 @@ RplASItem* RplMFParser::parseMap() * An operand is the first and the third part of a binary operation. * Examples: constant, variable, method call, an expression in parentheses * + * @post the token behind the operand is read * @return the node with the operand */ RplASItem* RplMFParser::parseOperand(int level) @@ -351,14 +474,17 @@ RplASItem* RplMFParser::parseOperand(int level) RplToken* token = m_lexer.nextNonSpaceToken(); const RplSourcePosition* startPosition = m_lexer.currentPosition(); RplASItem* rc = NULL; + bool readNext = true; switch(token->tokenType()){ case TOKEN_OPERATOR: { Operator opId = (Operator) token->id(); if (opId == O_LBRACKET){ rc = parseList(); + readNext = false; } else if (opId == O_LBRACE){ rc = parseMap(); + readNext = false; } else if (opId == O_LPARENTH){ rc = parseExpr(level + 1); token = m_lexer.currentToken(); @@ -373,8 +499,11 @@ RplASItem* RplMFParser::parseOperand(int level) RplASUnaryOp* op = new RplASUnaryOp(token->id(), AST_PRE_UNARY_OP); op->setPosition(m_lexer.currentPosition()); op->setChild(parseOperand(level)); + readNext = false; rc = op; - } + } else + syntaxError(L_PARSE_OPERAND_NOT_OPERAND, + "operand expected, not an operator"); break; } case TOKEN_STRING: @@ -407,40 +536,46 @@ RplASItem* RplMFParser::parseOperand(int level) if (token->tokenType() != TOKEN_OPERATOR){ RplASNamedValue* var = new RplASNamedValue(name); rc = var; - m_lexer.undoLastToken(); + readNext = false; } else { if (token->id() == O_LPARENTH){ - RplASArgument* args = parseArguments(); RplASMethodCall* call = new RplASMethodCall(); + call->setPosition(m_lexer.currentPosition()); rc = call; + RplASArgument* args = parseArguments(); call->setArg1(args); token = m_lexer.nextNonSpaceToken(); - if (token->tokenType() != TOKEN_OPERATOR - || token->id() != O_RPARENT){ + if (! token->isOperator(O_RPARENT)){ QByteArray pos = startPosition->toString().toUtf8(); // this call never comes back (exception!) syntaxError(L_PARSE_OPERAND_RPARENTH_FUNC, "')' expected. '(' is at %s", pos.constData()); } - } else if (token->id() == O_LBRACKET){ - } else { RplASNamedValue* var = new RplASNamedValue(name); var->setPosition(startPosition); rc = var; - if (token->id() == O_INC || token->id() == O_DEC){ - RplASUnaryOp* op = new RplASUnaryOp(token->id(), - AST_POST_UNARY_OP); - op->setChild(var); - rc = op; + if (token->id() == O_LBRACKET){ + RplASItem indexExpr = parseExpr(0); + if (! m_lexer.currentToken()->isOperator(O_RBRACKET)) + syntaxError(L_PARSE_OPERAND_NO_BRACKET, "']' expected"); + var->setChild(indexExpr); } else { - m_lexer.undoLastToken(); + if (token->id() == O_INC || token->id() == O_DEC){ + RplASUnaryOp* op = new RplASUnaryOp(token->id(), + AST_POST_UNARY_OP); + op->setChild(var); + rc = op; + } else { + readNext = false; + } } } } break; } case TOKEN_END_OF_SOURCE: + readNext = false; break; default: // this call never comes back (exception!) @@ -448,6 +583,8 @@ RplASItem* RplMFParser::parseOperand(int level) "unexpected symbol detected. Operand expected"); break; } + if (readNext) + m_lexer.nextNonSpaceToken(); return rc; } @@ -462,11 +599,11 @@ RplASItem* RplMFParser::parseOperand(int level) * expr with level 1: 3*7-2
* expr with level 0: a + expr1
* - * @precond the nextNonSpaceToken() will return the first token of the expr. - * @postcond the next token behind the expression is read + * @pre the nextNonSpaceToken() will return the first token of the expr. + * @post the token behind the expression is read * - * @param depth the level of the parenthesis - * @return the abstract syntax tree representing the parsed expression + * @param depth the level of the parenthesis + * @return the abstract syntax tree representing the parsed expression */ RplASItem* RplMFParser::parseExpr(int depth){ RplToken* token; @@ -475,9 +612,8 @@ RplASItem* RplMFParser::parseExpr(int depth){ int lastPrio = INT_MAX; bool again = true; do { - token = m_lexer.nextNonSpaceToken(); - RplTokenType tokenType = token->tokenType(); - switch(tokenType){ + token = m_lexer.currentToken(); + switch(token->tokenType()){ case TOKEN_OPERATOR: { Operator opId = (Operator) token->id(); @@ -527,12 +663,13 @@ RplASItem* RplMFParser::parseExpr(int depth){ /** * @brief Parses an expression as a statement. * - * @precond the nextNonSpaceToken() will return the first token of the expr. - * @postcond all tokens belonging to the expr are read (not more!) - * - * @return the abstract syntax tree of the expression statement + * @pre the nextNonSpaceToken() will return the first token of the expr.
+ * Note: a ';' is part of the expression statement + * @post the token behind the expression is read + * @param eatSemicolon true: a trailing ';' will be read + * @return the abstract syntax tree of the expression statement */ -RplASItem* RplMFParser::parseExprStatement() +RplASItem* RplMFParser::parseExprStatement(bool eatSemicolon) { RplASItem* item = parseExpr(0); RplASExprStatement* statement = NULL; @@ -541,6 +678,8 @@ RplASItem* RplMFParser::parseExprStatement() statement->setPosition(item->position()); statement->setChild(item); } + if (eatSemicolon && m_lexer.currentToken()->isOperator(O_SEMICOLON)) + m_lexer.nextNonSpaceToken(); return statement; } @@ -563,8 +702,12 @@ RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, RplASItem* body = NULL; RplASStatement* lastStatement = NULL; bool again = true; - while(again) { + const RplSourcePosition* lastPos = NULL; + do { token = m_lexer.currentToken(); + if (lastPos == m_lexer.currentPosition()) + syntaxError(L_PARSE_BODY_NO_START, "no statement starts with this symbol"); + lastPos = m_lexer.currentPosition(); // eat a superflous ';' if (token->isOperator(O_SEMICOLON)) token = m_lexer.nextNonSpaceToken(); @@ -649,8 +792,6 @@ RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, || (opStop != O_UNDEF && token->isOperator(opStop, opStop2))) again = false; - else - token = m_lexer.nextNonSpaceToken(); } } catch(RplSyntaxError exc){ // we look for the end of the statement: @@ -678,7 +819,7 @@ RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, token = m_lexer.nextNonSpaceToken(); } } - } + } while(again); return body; } @@ -713,8 +854,8 @@ void RplMFParser::parseImport() /** * @brief Parses a module. * - * @precond the first char of the module is the next char to read. - * @postcond the total module is read + * @pre the first char of the module is the next char to read. + * @post the total module is read * * @param name the name of the module (without path) */ diff --git a/rplexpr/rplmfparser.hpp b/rplexpr/rplmfparser.hpp index 49429aa..e0637fa 100644 --- a/rplexpr/rplmfparser.hpp +++ b/rplexpr/rplmfparser.hpp @@ -14,11 +14,14 @@ class RplMFParser : public RplParser { public: enum Keyword { - K_UNDEF, K_IF, K_THEN, K_ELSE, K_FI, K_WHILE, K_DO, K_OD, K_REPEAT, K_UNTIL, - K_FOR, K_FROM, K_TO, K_STEP, K_IN, K_CASE, K_OF, K_ESAC, K_LEAVE, K_CONTINUE, K_PASS, - K_CLASS, K_ENDC, K_ENDF, K_FUNCTION, K_GENERATOR, K_IMPORT, - K_CONST, K_LAZY, K_NONE, K_TRUE, K_FALSE - }; + K_UNDEF, K_IF, K_THEN, K_ELSE, K_FI, K_WHILE, // 5 + K_DO, K_OD, K_REPEAT, K_UNTIL, K_FOR, // 10 + K_FROM, K_TO, K_STEP, K_IN, K_CASE, // 15 + K_OF, K_ESAC, K_LEAVE, K_CONTINUE, K_PASS, // 20 + K_CLASS, K_ENDC, K_ENDF, K_FUNCTION, K_GENERATOR, // 25 + K_IMPORT, K_CONST, K_LAZY, K_NONE, K_TRUE, // 30 + K_FALSE + }; #define MF_KEYWORDS "if then else fi while do od repeat until" \ " for from to step in case of esac leave continue pass" \ " class endc endf func generator import" \ @@ -79,12 +82,15 @@ public: void parseImport(); RplASItem* parseModule(const QString& name); void parse(); - RplASItem*parseExprStatement(); + RplASItem*parseExprStatement(bool eatSemicolon = true); RplASItem*parseList(); RplASItem*parseMap(); protected: RplASArgument* parseArguments(); RplASItem* parseOperand(int level); + RplASVariant* tokenToVariant(RplToken* token, bool endsWithComma, + RplASNode1* parent); + RplASVariant*createFormula(RplASNode1* parent); private: ///syntax token builder. /// Note: the super class contains a reference with the same name diff --git a/test/rplmfparser/map1.txt b/test/rplmfparser/map1.txt new file mode 100644 index 0000000..c0ac61a --- /dev/null +++ b/test/rplmfparser/map1.txt @@ -0,0 +1,8 @@ +Map a = {}; += (module) parent: global +== Classes: +== Variables: +== Body: +varDef a (Map) id: 1 succ: 0 attr: 0x0 :1:4 + mapConst id: 2 :1:8 + {} diff --git a/test/rplmfparser/map2.txt b/test/rplmfparser/map2.txt new file mode 100644 index 0000000..1e24831 --- /dev/null +++ b/test/rplmfparser/map2.txt @@ -0,0 +1,12 @@ +Map a = {'a': 2+3,'bcd':3.14,'ccc':7, 'hi':'world'}; +Map b = {}; += (module) parent: global +== Classes: +== Variables: +== Body: +varDef a (Map) id: 1 succ: 9 attr: 0x0 :1:4 + mapConst id: 2 :1:8 + {'a':,'bcd':3.140000,'ccc':7,'hi':} +varDef b (Map) id: 9 succ: 0 attr: 0x0 :2:4 + mapConst id: 10 :2:8 + {} diff --git a/unittests/main.cpp b/unittests/main.cpp index e9fc42b..bee689c 100644 --- a/unittests/main.cpp +++ b/unittests/main.cpp @@ -34,11 +34,11 @@ void testExpr(){ } void testStandard(){ + testExpr(); + testCore(); extern void testRplMatrix(); testRplMatrix(); - testExpr(); - testCore(); } int main(int argc, char *argv[]) diff --git a/unittests/rplmfparser_test.cpp b/unittests/rplmfparser_test.cpp index 533b369..d183689 100644 --- a/unittests/rplmfparser_test.cpp +++ b/unittests/rplmfparser_test.cpp @@ -114,19 +114,29 @@ public: checkAST("forIt1.txt", __LINE__); } void listTest(){ - setSource("List b = [];"); RplMFParser parser(m_source, m_tree); + setSource("List b = [];"); parser.parse(); checkAST("list1.txt", __LINE__); setSource("List a = [2+3, 3.14, 7, 'hi', a]; List b = [];"); parser.parse(); checkAST("list2.txt", __LINE__); } + void mapTest(){ + setSource("Map a = {};"); + RplMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("map1.txt", __LINE__); + setSource("Map a = {'a': 2+3,'bcd':3.14,'ccc':7, 'hi':'world'};\nMap b = {};"); + parser.parse(); + checkAST("map2.txt", __LINE__); + } virtual void doIt(void) { - listTest(); - forCTest(); + mapTest(); forItTest(); + forCTest(); + listTest(); opTest(); ifTest(); whileTest(); -- 2.39.5