From: hama Date: Sat, 26 Jul 2014 11:13:58 +0000 (+0200) Subject: dayly work X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=56e2fc6f9b16e2e955772ef43ba71c06dc5dbf81;p=reqt dayly work --- diff --git a/rplexpr/rplastree.cpp b/rplexpr/rplastree.cpp index dffb1de..2df3291 100644 --- a/rplexpr/rplastree.cpp +++ b/rplexpr/rplastree.cpp @@ -2026,6 +2026,9 @@ void RplASBinaryOp::dump(FILE* fp, int indent) * @brief Implements a method definition for the Abstract Syntax Tree. * * The special case "function" (a method without class) is included. + * + * m_child: body
+ * m_child2: parameter list (or NULL) */ /** * @brief Constructor. @@ -2033,10 +2036,11 @@ void RplASBinaryOp::dump(FILE* fp, int indent) * @param name the method name * @param type the method result type */ -RplASMethod::RplASMethod(const QString& name, RplASClass* type) : +RplASMethod::RplASMethod(const QString& name) : RplASNode2(AST_METHOD), m_name(name), - m_nodeType(type) + m_resultType(NULL), + m_parent(NULL) { } @@ -2050,13 +2054,37 @@ void RplASMethod::dump(FILE* fp, int indent) { DEFINE_TABS(indent); char buffer[256]; - fprintf(fp, "%sMethod %d %s %s(", tabs, m_id, - m_nodeType == NULL ? "" : m_nodeType->name().toUtf8().constData(), + fprintf(fp, "%sMethod %s %s(", tabs, + m_resultType == NULL ? "" : m_resultType->name().toUtf8().constData(), m_name.toUtf8().constData()); - fprintf(fp, ") body: %d args: %d %s\n", m_child == NULL ? 0 : m_child->id(), + fprintf(fp, ") id: %d parent: %d args: %d body: %d %s\n", m_id, + m_parent == NULL ? 0 : m_parent->id(), m_child2 == NULL ? 0 : m_child2->id(), + m_child->id(), positionStr(buffer, sizeof buffer)); + if (m_child2 != NULL) + m_child2->dump(fp, indent + 1); + m_child->dump(fp, indent + 1); +} +/** + * @brief Returns the parent of the method: a class or a method. + * @return NULL: this is a function, not a method
+ * otherwise: the parent: a class or a method/func + */ +RplASItem* RplASMethod::parent() const +{ + return m_parent; } +/** + * @brief Sets the parent of the method. + * + * @param parent the parent: NULL or a class or a method + */ +void RplASMethod::setParent(RplASItem* parent) +{ + m_parent = parent; +} + /** @class RplASArgument rplastree.hpp "rplexpr/rplastree.hpp" * diff --git a/rplexpr/rplastree.hpp b/rplexpr/rplastree.hpp index 59e2087..fbb8cc5 100644 --- a/rplexpr/rplastree.hpp +++ b/rplexpr/rplastree.hpp @@ -289,7 +289,9 @@ public: /// the variable/constant is found in the module namespace, not in a method A_MODULE_STATIC = 1<<3, /// the evaluation should be lazy - A_LAZY = 1<<4 + A_LAZY = 1<<4, + /// parameter of a method + A_PARAM = 1<<5 }; public: @@ -475,15 +477,17 @@ class RplASClass; class RplASMethod : public RplASNode2 { public: - RplASMethod(const QString& name, RplASClass* type); + RplASMethod(const QString& name); public: void execute(); void dump(FILE* fp, int indent); + RplASItem* parent() const; + void setParent(RplASItem* parent); + private: QString m_name; - RplASClass* m_nodeType; - // body is m_child - // param1 is m_child2 + RplASClass* m_resultType; + RplASItem* m_parent; }; class RplASClass { diff --git a/rplexpr/rplmfparser.cpp b/rplexpr/rplmfparser.cpp index bbdfebb..e15e695 100644 --- a/rplexpr/rplmfparser.cpp +++ b/rplexpr/rplmfparser.cpp @@ -45,7 +45,17 @@ enum MFLocations{ L_PARSE_ARGS_NO_COMMA_OR_PARENT, L_PARSE_OPERAND_NO_FIELD2, L_PARSE_OPERAND_NO_BRACKET2 = 2035, - L_PARSE_OPERAND_NO_FIELD + L_PARSE_OPERAND_NO_FIELD, + L_PARSE_METH_NO_CLASS, + L_PARSE_METH_NO_CLASS2, + L_PARSE_METH_NO_NAME, + L_PARSE_METH_NO_NAME2 = 2040, + L_PARSE_METH_NO_LPARENTH, + L_PARSE_METH_NO_COLON, + L_PARSE_PARAMLIST_NO_PARENTH, + L_PARSE_PARAMLIST_NO_PARENTH2, + L_PARSE_METH_NO_END = 2045, + L_PARSE_METH_NO_END2 }; /** @class RplMFParser rpllexer.hpp "rplexpr/rplmfparser.hpp" @@ -67,14 +77,6 @@ RplMFParser::RplMFParser(RplSource& source, RplASTree& abstractSyntaxTree) : { } -/** - * @brief Parses a function or a generator. - */ -RplASItem* RplMFParser::parseFunc() -{ - RplASItem*rc = NULL; - return rc; -} /** * @brief Parses an if statement. */ @@ -199,32 +201,29 @@ RplASItem* RplMFParser::parseFor() * @brief Parses a variable definition. * * Syntax: - * Variable: { "const" | "lazy" }* id [ = ] ";" + * Variable: { "const" | "lazy" }* id [ = ] [ ";" ] * Parameter: { "const" | "lazy" }* id [ = ] { "," | ")" } * - * AST: RplASNamedValue - * - * @param clazz NULL or the type of the variable - * @param attribute 0 or attribute of the variable: K_CONST or K_LAZY + * @pre first token of the definition is read + * @post token behind the definition is read: ';', ',', ')' + * @param attribute attribute of the variable: A_PARAM... */ -RplASItem* RplMFParser::parseVarDefinition(Keyword attribute) +RplASItem* RplMFParser::parseVarDefinition(RplASNamedValue::Attributes attribute) { - RplASNamedValue::Attributes attr = RplASNamedValue::A_NONE; + int attributes = attribute; RplToken* token = m_lexer.currentToken(); - while(attribute == K_CONST || attribute == K_LAZY){ - switch(attribute){ + while(token->isKeyword(K_CONST, K_LAZY)){ + switch(token->id()){ case K_CONST: - attr = RplASNamedValue::A_CONST; + attributes += RplASNamedValue::A_CONST; break; case K_LAZY: - attr = RplASNamedValue::A_LAZY; + attributes += RplASNamedValue::A_LAZY; break; default: break; } token = m_lexer.nextNonSpaceToken(); - attribute = token->isTokenType(TOKEN_KEYWORD) - ? (Keyword) token->id() : K_UNDEF; } if (! token->isTokenType(TOKEN_ID)) syntaxError(L_DEFINITION_NO_ID, "class name expected, but no id found"); @@ -238,23 +237,18 @@ RplASItem* RplMFParser::parseVarDefinition(Keyword attribute) if (! token->isTokenType(TOKEN_ID)) syntaxError(L_DEFINITION_MISSING_ID, "variable name expected"); // freed in the destructor of the nodes: - RplASNamedValue* namedValue = new RplASNamedValue(clazz, token->toString(), attr); + RplASNamedValue* namedValue = new RplASNamedValue(clazz, token->toString(), + attributes); namedValue->setPosition(m_lexer.currentPosition()); RplASVarDefinition* rc = new RplASVarDefinition(); rc->setPosition(m_lexer.currentPosition()); rc->setChild2(namedValue); token = m_lexer.nextNonSpaceToken(); - if (! token->isOperator(O_ASSIGN, O_SEMICOLON)) - syntaxError(L_DEFINITION_NO_OP, "'=' or ';' expected"); if (token->id() == O_ASSIGN){ RplASItem* value = parseExpr(0); rc->setChild3(value); token = m_lexer.currentToken(); } - if (! token->isOperator(O_SEMICOLON)){ - syntaxError(L_DEFINITION_NO_SEMICOLON, "';' expected"); - } - m_lexer.nextNonSpaceToken(); return rc; } @@ -539,7 +533,6 @@ RplASItem* RplMFParser::parseOperand(int level, RplASItem* parent) rc = parseExpr(level + 1); token = m_lexer.currentToken(); if(! token->isOperator(O_RPARENT)){ - char buffer[256]; // this call never comes back (exception!) syntaxError(L_PARSE_OPERAND_RPARENTH, "')' expected", "(", startPosition); @@ -736,6 +729,11 @@ RplASItem* RplMFParser::parseExprStatement(bool eatSemicolon) return statement; } +RplASItem* RplMFParser::parseLocalVar(){ + RplASItem* rc = parseVarDefinition(RplASNamedValue::A_NONE); + return rc; +} + /** * @brief Parses the body. * @@ -798,14 +796,14 @@ RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, break; case K_FUNCTION: case K_GENERATOR: - item = parseMethodDefinition(); + item = parseMethodDefinition(NULL); break; case K_IMPORT: parseImport(); break; case K_CONST: case K_LAZY: - item = parseVarDefinition((Keyword) token->id()); + item = parseLocalVar(); break; default: if (token->isKeyword(keywordStop, keywordStop2)) @@ -816,7 +814,7 @@ RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, case TOKEN_ID: { if (token->isCapitalizedId()){ - item = parseVarDefinition(K_UNDEF); + item = parseLocalVar(); } else { m_lexer.undoLastToken(); item = parseExprStatement(); @@ -876,13 +874,84 @@ RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, return body; } +/** + * @brief Parses a parameter list of a method/function. + * + * @pre token behind '(' is read + * @post token behind ')' is read + * @return + */ +RplASExprStatement* RplMFParser::parseParameterList(){ + RplASExprStatement* rc = NULL; + RplASExprStatement* last = NULL; + const RplSourcePosition* startPos = m_lexer.currentPosition(); + RplASItem* definition; + do { + definition = parseVarDefinition(RplASNamedValue::A_PARAM); + RplASExprStatement *current = new RplASExprStatement(); + current->setChild2(definition); + if (rc == NULL){ + rc = current; + } else { + current->setChild(last); + last->setChild(current); + } + last = current; + } while(m_lexer.currentToken()->isOperator(O_COMMA)); + if (! m_lexer.currentToken()->isOperator(O_RPARENT)) + syntaxError(L_PARSE_PARAMLIST_NO_PARENTH, ") expected", ")", startPos); + m_lexer.nextNonSpaceToken(); + return rc; +} + /** * @brief Parses a class definition. + * + * @pre token "func" is read + * @post token behind "endf" is read * @return the node in an abstract syntax tree */ -RplASItem*RplMFParser::parseMethodDefinition() +RplASMethod*RplMFParser::parseMethodDefinition(RplASItem* parent) { - RplASItem* rc = NULL; + RplASMethod* rc = NULL; + const RplSourcePosition* startPos = m_lexer.currentPosition(); + RplToken* token = m_lexer.nextNonSpaceToken(); + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_PARSE_METH_NO_CLASS, "type name expected"); + QString type = token->toString(); + if (! type.at(0).isUpper()) + syntaxError(L_PARSE_METH_NO_CLASS2, + "type name expected (must start with an upper case character)"); + token = m_lexer.nextNonSpaceToken(); + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_PARSE_METH_NO_NAME, "method name expected"); + QString name = token->toString(); + if (! type.at(0).isUpper()) + syntaxError(L_PARSE_METH_NO_CLASS2, + "method name expected (must start with an lower case character)"); + token = m_lexer.nextNonSpaceToken(); + if (! token->isOperator(O_LPARENTH, O_COLON)) + syntaxError(L_PARSE_METH_NO_LPARENTH, "'(' or ':' expected"); + RplASExprStatement* parameterList; + if (token->isOperator(O_LPARENTH)){ + token = m_lexer.nextNonSpaceToken(); + if (token->isOperator(O_RPARENT)){ + token = m_lexer.nextNonSpaceToken(); + } else { + parameterList = parseParameterList(); + } + } + if (! token->isOperator(O_COLON)) + syntaxError(L_PARSE_METH_NO_COLON, "':' expected"); + rc = new RplASMethod(name); + rc->setParent(parent); + rc->setPosition(startPos); + rc->setChild2(parameterList); + rc->setChild(parseBody(K_ENDF)); + if (! m_lexer.currentToken()->isKeyword(K_ENDF)) + syntaxError(L_PARSE_METH_NO_END, "end of function not found", "endf", + startPos); + m_lexer.nextNonSpaceToken(); return rc; } diff --git a/rplexpr/rplmfparser.hpp b/rplexpr/rplmfparser.hpp index 3d581b2..dcee6ec 100644 --- a/rplexpr/rplmfparser.hpp +++ b/rplexpr/rplmfparser.hpp @@ -46,8 +46,7 @@ public: }; #define IS_BINARY_OP(op) (Operator(op) >= O_ASSIGN && Operator(op) <= O_DOT) #define IS_UNARY_OP(op) (op==O_PLUS || op==O_MINUS || (op>=O_NOT && op<=O_DEC)) -#define IS_OP_BEHIND_EXPR(o) (o==O_RBRACKET||o==O_RBRACE||o==O_SEMICOLON \ - ||o==O_COMMA||o==O_SEMI_SEMICOLON||o==O_QUESTION) + /// \n separates the priority classes #define MF_OPERATORS ";; ; , :\n" \ "= += -= /= *= %= **= |= &= <<= >>= >>>=\n" \ @@ -68,16 +67,15 @@ public: public: RplMFParser(RplSource& source, RplASTree& ast); public: - RplASItem* parseFunc(); RplASItem* parseIf(); RplASItem* parseWhile(); RplASItem* parseRepeat(); RplASItem* parseFor(); - RplASItem* parseVarDefinition(Keyword attribute); + RplASItem* parseVarDefinition(RplASNamedValue::Attributes attribute); RplASItem* parseExpr(int depth); RplASItem* parseBody(Keyword keywordStop, Keyword keywordStop2 = K_UNDEF, Operator opStop = O_UNDEF, Operator opStop2 = O_UNDEF); - RplASItem* parseMethodDefinition(); + RplASMethod*parseMethodDefinition(RplASItem* parent); RplASItem* parseClass(); void parseImport(); RplASItem* parseModule(const QString& name); @@ -94,6 +92,8 @@ protected: RplASItem* buildVarOrField(const QString& name, const RplSourcePosition* position, RplASItem* parent); + RplASExprStatement*parseParameterList(); + RplASItem* parseLocalVar(); private: ///syntax token builder. /// Note: the super class contains a reference with the same name diff --git a/rplexpr/rplparser.cpp b/rplexpr/rplparser.cpp index c39570c..bc9bcd6 100644 --- a/rplexpr/rplparser.cpp +++ b/rplexpr/rplparser.cpp @@ -53,7 +53,6 @@ const char* RplSyntaxError::reason() const /** * @brief Constructor. * @param reason the reason of the exception - * @return */ RplParserStop::RplParserStop(const char* reason) : RplSyntaxError(reason) diff --git a/test/rplmfparser/defTest.txt b/test/rplmfparser/defTest.txt index b766452..8d941fa 100644 --- a/test/rplmfparser/defTest.txt +++ b/test/rplmfparser/defTest.txt @@ -7,7 +7,7 @@ varDef i id: 2 namedValue: 1 value: 3 succ: 5 :1:4 namedValue i id: 1 attr: 0x0 :1:4 const id: 3 value: 3 :1:8 varDef s id: 5 namedValue: 4 value: 6 succ: 8 :1:26 - namedValue s id: 4 attr: 0x10 :1:26 + namedValue s id: 4 attr: 0x12 :1:26 const id: 6 value: 'Hi' :1:30 varDef l id: 8 namedValue: 7 value: 0 succ: 0 :1:47 namedValue l id: 7 attr: 0x2 :1:47 diff --git a/test/rplmfparser/meth1.txt b/test/rplmfparser/meth1.txt new file mode 100644 index 0000000..70435a3 --- /dev/null +++ b/test/rplmfparser/meth1.txt @@ -0,0 +1,7 @@ +func Float pi: 3.1415; endf func Str delim(): '/' endf; += (module) parent: global +== Classes: +== Variables: +== Body: +Method 1 pi() body: 4 args: 36816 :0:0 +Method 4 delim() body: 0 args: 36816 :1:28 diff --git a/test/rplmfparser/meth2.txt b/test/rplmfparser/meth2.txt new file mode 100644 index 0000000..ef65275 --- /dev/null +++ b/test/rplmfparser/meth2.txt @@ -0,0 +1,8 @@ +func Int fac(const Int n): +Int rc; if rc <= 1 then rc = 1 else rc = n*fac(n-1) fi +rc endf += (module) parent: global +== Classes: +== Variables: +== Body: +Method 4 fac() body: 0 args: 3 :0:55 diff --git a/unittests/rplmfparser_test.cpp b/unittests/rplmfparser_test.cpp index 18abe4d..8b2424d 100644 --- a/unittests/rplmfparser_test.cpp +++ b/unittests/rplmfparser_test.cpp @@ -139,14 +139,25 @@ public: checkAST("methc1.txt", __LINE__); } void fieldTest(){ - //setSource("a.trunc;"); setSource("file.find('*.c')[0].name;\n[1,2,3].join(' ');\n3.14.trunc;"); RplMFParser parser(m_source, m_tree); parser.parse(); checkAST("field1.txt", __LINE__); } + void methodTest(){ + setSource("func Float pi: 3.1415; endf func Str delim(): '/' endf;"); + RplMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("meth1.txt", __LINE__); + setSource("func Int fac(const Int n):\n" + "Int rc; if rc <= 1 then rc = 1 else rc = n*fac(n-1) fi\n" + "rc endf"); + parser.parse(); + checkAST("meth2.txt", __LINE__); + } virtual void doIt(void) { + methodTest(); fieldTest(); methodCallTest(); mapTest();