]> gitweb.hamatoma.de Git - reqt/commitdiff
dayly work
authorhama <hama@siduction.net>
Fri, 18 Jul 2014 22:22:15 +0000 (00:22 +0200)
committerhama <hama@siduction.net>
Fri, 18 Jul 2014 22:22:15 +0000 (00:22 +0200)
rplexpr/rplasclasses.cpp
rplexpr/rplastree.cpp
rplexpr/rplastree.hpp
rplexpr/rpllexer.cpp
rplexpr/rpllexer.hpp
rplexpr/rplmfparser.cpp
rplexpr/rplmfparser.hpp
test/rplmfparser/map1.txt [new file with mode: 0644]
test/rplmfparser/map2.txt [new file with mode: 0644]
unittests/main.cpp
unittests/rplmfparser_test.cpp

index e780bd0f872b3210c7176b76c2aa07e417862358..246b2413ada91a0faa49441c35817a9340188525 100644 (file)
@@ -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<RplASMapValueType*>(source);
+        RplASMapOfVariants* source2 =
+                static_cast<RplASMapOfVariants*>(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<RplASMapValueType*>(object);
+        RplASMapOfVariants* map = reinterpret_cast<RplASMapOfVariants*>(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<RplASMapValueType*>(object);
-    RplASMapValueType::iterator it;
+    RplASMapOfVariants* map = reinterpret_cast<RplASMapOfVariants*>(object);
+    RplASMapOfVariants::iterator it;
     bool first = true;
     for(it = map->begin(); it != map->end(); it++){
         if (first)
index fd74edb9fb86f8f134c41553bd74a49d7a8452ec..a496d415bd79b5bf75e8cd479046f41466eba205 100644 (file)
@@ -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<QString> 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<QString>());
+    QList<QString>::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<RplASMapOfVariants*>(
+                m_value.asObject(NULL));
+    return rc;
+}
 
 /** @class RplASNamedValue rplastree.hpp "rplexpr/rplastree.hpp"
  *
  * @brief Implements a named values, a constant or a variable
  *
+ * <code>m_child</code>: 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<RplASNamedValue*>(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)
 {
 }
+
+
index 64537afe299a2a969c2d1c620cf51538542edc66..776bf85199a23cc0212ba6d10ccf5e753254aad7 100644 (file)
@@ -232,7 +232,7 @@ protected:
     RplASItem* m_child5;
 };
 typedef QList<RplASVariant*> RplASListOfVariants;
-typedef QMap<QString, RplASVariant*> RplASMapValueType;
+typedef QMap<QString, RplASVariant*> 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;
 };
index 414e39437920d6297464bdc7f1f51d9e56e1925b..0080407ee42eea97a0c4f047d6ade008f17a99b0 100644 (file)
@@ -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
index 4c385f52e852d7d9aae4430e9d31e6f51fc68f33..5d23de7a168d0a2617beb986521686f932062357 100644 (file)
@@ -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();
index 202dffa6b5839ab81f36c72d5999a4f6599fc2cb..23239a86d0f79b1dce913c46126add42991ff0f5 100644 (file)
@@ -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 <var> in <iterable_expr> do <body> 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<RplASExprStatement*>
+            (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 <code>RplASVariant</code>.
+ *
+ * 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:<br>
  * '[' [<expr_1> [ ',' <expr_2> ...]] ']'
  *
- * @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<RplASExprStatement*>
-                        (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:<br>
  * '{' [ <string_expr1> ':' <expr_1> [ ',' <string_expr1> ': <expr_2> ...]] '}'
  *
- * @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<RplASMapOfVariants*>
+            (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<br>
  * expr with level 0: a + expr1<br>
  *
- * @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.<br>
+ *          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)
  */
index 49429aac57f2cd04cae959d0dd8ac5c74add80d9..e0637fad4054d60c543bd60760c5132d7e2249cb 100644 (file)
@@ -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 (file)
index 0000000..c0ac61a
--- /dev/null
@@ -0,0 +1,8 @@
+Map a = {};
+= <test> (module) parent: global
+== Classes:
+== Variables:
+== Body:
+varDef a (Map) id: 1 succ: 0 attr: 0x0 <test>:1:4
+       mapConst id: 2 <test>:1:8
+       {}
diff --git a/test/rplmfparser/map2.txt b/test/rplmfparser/map2.txt
new file mode 100644 (file)
index 0000000..1e24831
--- /dev/null
@@ -0,0 +1,12 @@
+Map a = {'a': 2+3,'bcd':3.14,'ccc':7, 'hi':'world'};
+Map b = {};
+= <test> (module) parent: global
+== Classes:
+== Variables:
+== Body:
+varDef a (Map) id: 1 succ: 9 attr: 0x0 <test>:1:4
+       mapConst id: 2 <test>:1:8
+       {'a':<formula 6>,'bcd':3.140000,'ccc':7,'hi':<formula 8>}
+varDef b (Map) id: 9 succ: 0 attr: 0x0 <test>:2:4
+       mapConst id: 10 <test>:2:8
+       {}
index e9fc42b85e05d624b74f6b7aeee33d333b819dd1..bee689c4f1d324c542210cb1f252cd58deeeca58 100644 (file)
@@ -34,11 +34,11 @@ void testExpr(){
 }
 
 void testStandard(){
+    testExpr();
+    testCore();
     extern void testRplMatrix();
     testRplMatrix();
 
-    testExpr();
-    testCore();
 }
 
 int main(int argc, char *argv[])
index 533b3698e9dbee313b1fcf0194d780702475e8bb..d1836890cc6e93846c64a835cdf3a71e4573924b 100644 (file)
@@ -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();