positionStr(buffer, sizeof buffer));
}
+/** @class RplASIndexedValue rplastree.hpp "rplexpr/rplastree.hpp"
+ *
+ * @brief Implements an indexed values (member of a list)
+ *
+ * <code>m_child</code>: the parent: a list expression
+ * <code>m_child2</code>: the index expression
+ */
+RplASIndexedValue::RplASIndexedValue() :
+ RplASNode2(AST_INDEXED_VALUE)
+{
+}
+
+/**
+ * @brief Writes the internals into a file.
+ *
+ * @param fp target file
+ * @param indent nesting level
+ */
+void RplASIndexedValue::dump(FILE* fp, int indent)
+{
+ DEFINE_TABS(indent);
+ char buffer[256];
+ fprintf(fp, "%sindexedValue id: %d index: %d parent: %d %s\n", tabs,
+ m_id, m_child2->id(), m_child->id(),
+ positionStr(buffer, sizeof buffer));
+ m_child2->dump(fp, indent + 1);
+ m_child->dump(fp, indent + 1);
+}
+
/** @class RplVarDefinition rplastree.hpp "rplexpr/rplastree.hpp"
*
* @brief Implements variable definition for the Abstract Syntax Tree.
*
* <code>m_child</code>: next statement</br>
* <code>m_child2</code>: argument list<br>
+ * <code>m_child3</code>: parent (variable, field ...)
*/
/**
* @brief Constructor.
+ *
+ * @param name name of the method/function
+ * @param parent NULL: it is a function<br>
+ * otherwise: the parent (variable, field ...)
*/
-RplASMethodCall::RplASMethodCall(const QString& name) :
- RplASNode2(AST_METHOD_CALL),
+
+RplASMethodCall::RplASMethodCall(const QString& name, RplASItem* parent) :
+ RplASNode3(AST_METHOD_CALL),
RplASStatement(),
m_name(name),
m_method(NULL)
{
-
+ m_child3 = parent;
}
{
DEFINE_TABS(indent);
char buffer[256];
- fprintf(fp, "%scall %s Id: %d args: %d succ: %d %s\n", tabs,
+ fprintf(fp, "%scall %s Id: %d args: %d parent: %d succ: %d %s\n", tabs,
m_name.toUtf8().constData(), m_id,
m_child2 == NULL ? 0 : m_child2->id(),
+ m_child3 == NULL ? 0 : m_child3->id(),
m_child == NULL ? 0 : m_child->id(),
positionStr(buffer, sizeof buffer));
if (m_child2 != NULL)
m_child2->dump(fp, indent + 1);
+ if (m_child3 != NULL)
+ m_child3->dump(fp, indent + 1);
}
/**
/** @class RplASField rplastree.hpp "rplexpr/rplastree.hpp"
*
- * @brief Implements an argument of a method for the Abstract Syntax Tree.
+ * @brief Implements an class field for the Abstract Syntax Tree.
*
- * <code>m_child</code>: parent (variable, field, method)<br>
+ * <code>m_child</code>: parent (variable, field, method)
*/
/**
{
DEFINE_TABS(indent);
char buffer[256];
- fprintf(fp, "%sfield %s id: %d expr: %d succ: %s\n", tabs,
+ fprintf(fp, "%sfield %s id: %d parent: %d succ: %s\n", tabs,
m_name.toUtf8().constData(), m_id,
- m_child->id(), positionStr(buffer, sizeof buffer));
+ m_child == NULL ? 0 : m_child->id(),
+ positionStr(buffer, sizeof buffer));
m_child->dump(fp, indent + 1);
}
+
+
AST_MAP_CONSTANT,
AST_MAP_ENTRY,
AST_NAMED_VALUE,
+ AST_INDEXED_VALUE,
AST_FIELD,
AST_VAR_DEFINITION,
AST_EXPR_STATEMENT,
RplASClass* m_dataType;
};
+class RplASIndexedValue : public RplASNode2 {
+public:
+ RplASIndexedValue();
+public:
+ void dump(FILE* fp, int indent);
+};
+
class RplASStatement
{
public:
};
class RplASMethod;
-class RplASMethodCall : public RplASNode2, public RplASStatement
+class RplASMethodCall : public RplASNode3, public RplASStatement
{
public:
- RplASMethodCall(const QString& name);
+ RplASMethodCall(const QString& name, RplASItem* parent);
public:
void dump(FILE* fp, int indent);
virtual void execute();
enum MFLocations{
L_PARSE_OPERAND_RPARENTH = 2001,
- L_PARSE_FREE1,
+ L_PARSE_OPERAND_RPARENTH_INFO,
L_TERM_WRONG_STRING,
L_TERM_WRONG_NUMBER,
L_PARSE_OPERAND_WRONG = 2005,
L_PARSE_OPERAND_NOT_OPERAND = 2030,
L_PARSE_BODY_NO_START,
L_PARSE_OPERAND_NO_BRACKET,
- L_PARSE_ARGS_NO_COMMA_OR_PARENT
+ L_PARSE_ARGS_NO_COMMA_OR_PARENT,
+ L_PARSE_OPERAND_NO_FIELD2,
+ L_PARSE_OPERAND_NO_BRACKET2 = 2035,
+ L_PARSE_OPERAND_NO_FIELD
};
/** @class RplMFParser rpllexer.hpp "rplexpr/rplmfparser.hpp"
return rc;
}
+/**
+ * @brief Builds a node of a variable or a field.
+ *
+ * @param name name of the variable/field
+ * @param position source position
+ * @param parent NULL: result is a variable
+ *
+ * @return a <code>RplASNamedValue</code> or a
+ * <code>RplASNamedValue</code> instance
+ */
+RplASItem* RplMFParser::buildVarOrField(const QString& name,
+ const RplSourcePosition* position, RplASItem* parent)
+{
+ RplASItem* rc = NULL;
+ if (parent == NULL){
+ RplASNamedValue* var = new RplASNamedValue(name);
+ var->setPosition(position);
+ rc = var;
+ } else {
+ RplASField* field = new RplASField(name);
+ field->setPosition(position);
+ rc = field;
+ field->setChild(parent);
+ }
+ return rc;
+}
/**
* @brief Parses an operand.
*
* @post the token behind the operand is read
* @return the node with the operand
*/
-RplASItem* RplMFParser::parseOperand(int level)
+RplASItem* RplMFParser::parseOperand(int level, RplASItem* parent)
{
RplToken* token = m_lexer.nextNonSpaceToken();
const RplSourcePosition* startPosition = m_lexer.currentPosition();
case TOKEN_OPERATOR:
{
Operator opId = (Operator) token->id();
+ if (parent != NULL && opId != O_LBRACKET)
+ syntaxError(L_PARSE_OPERAND_NO_FIELD,
+ "field expected (behind a '.')");
if (opId == O_LBRACKET){
- rc = parseList();
- readNext = false;
+ if (parent == NULL){
+ rc = parseList();
+ readNext = false;
+ } else {
+ RplASIndexedValue* value = new RplASIndexedValue();
+ value->setPosition(startPosition);
+ value->setChild(parent);
+ rc = value;
+ value->setChild2(parseExpr(level + 1));
+ if (! m_lexer.currentToken()->isOperator(O_RBRACKET))
+ syntaxError(L_PARSE_OPERAND_NO_BRACKET2,
+ "']' expected");
+ }
} else if (opId == O_LBRACE){
rc = parseMap();
readNext = false;
char buffer[256];
// this call never comes back (exception!)
syntaxError(L_PARSE_OPERAND_RPARENTH,
- "')' expected. '(' is at %s",
- startPosition->utf8(buffer, sizeof buffer));
+ "')' expected", "(", startPosition);
}
} else if (IS_UNARY_OP(opId)){
RplASUnaryOp* op = new RplASUnaryOp(token->id(), AST_PRE_UNARY_OP);
case TOKEN_NUMBER:
case TOKEN_REAL:
{
+ if (parent != NULL)
+ syntaxError(L_PARSE_OPERAND_NO_FIELD2,
+ "field expected (behind a '.')");
RplASConstant* constant = new RplASConstant();
constant->setPosition(m_lexer.currentPosition());
rc = constant;
token = m_lexer.nextNonSpaceToken();
startPosition = m_lexer.currentPosition();
if (token->tokenType() != TOKEN_OPERATOR){
- RplASNamedValue* var = new RplASNamedValue(name);
- rc = var;
+ rc = buildVarOrField(name, startPosition, parent);
readNext = false;
} else {
if (token->id() == O_LPARENTH){
- RplASMethodCall* call = new RplASMethodCall(name);
+ RplASMethodCall* call = new RplASMethodCall(name, parent);
call->setPosition(startPosition);
rc = call;
readNext = false;
}
} else {
- RplASNamedValue* var = new RplASNamedValue(name);
- var->setPosition(startPosition);
- rc = var;
+ rc = buildVarOrField(name, startPosition, parent);
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);
+ dynamic_cast<RplASNode1*>(rc)->setChild(indexExpr);
} else {
if (token->id() == O_INC || token->id() == O_DEC){
RplASUnaryOp* op = new RplASUnaryOp(token->id(),
AST_POST_UNARY_OP);
- op->setChild(var);
+ op->setChild(rc);
rc = op;
} else {
readNext = false;
}
if (readNext)
m_lexer.nextNonSpaceToken();
-// if (m_lexer.currentToken()->isOperator(O_DOT))
-
+ if (m_lexer.currentToken()->isOperator(O_DOT, O_LBRACKET)){
+ if (m_lexer.currentToken()->asInteger() == O_LBRACKET)
+ m_lexer.undoLastToken();
+ rc = parseOperand(level, rc);
+ }
return rc;
}
RplASItem*parseMap();
protected:
RplASArgument* parseArguments();
- RplASItem* parseOperand(int level);
+ RplASItem* parseOperand(int level, RplASItem* parent = NULL);
RplASVariant* tokenToVariant(RplToken* token, bool endsWithComma,
RplASNode1* parent);
RplASVariant*createFormula(RplASNode1* parent);
+ RplASItem* buildVarOrField(const QString& name,
+ const RplSourcePosition* position,
+ RplASItem* parent);
private:
///syntax token builder.
/// Note: the super class contains a reference with the same name
*
* @param prefix first char in the message: 'E' (error) or 'W' (warning)
* @param location unique id of the error/warning message
+ * @param position position of the error/warning
* @param format message with placeholdes like sprintf()
* @param varList the variable argument list
*/
-void RplParser::addMessage(char prefix, int location, const char* format,
- va_list varList){
+void RplParser::addSimpleMessage(LevelTag prefix, int location,
+ const RplSourcePosition* position,
+ const char* message){
char buffer[2048];
QString msg;
- const RplSourcePosition* pos = m_lexer.currentPosition();
snprintf(buffer, sizeof buffer, "%c%04d %s:%d-%d: ", prefix, location,
- pos->sourceUnit()->name().toUtf8().constData(),
- pos->lineNo(), pos->column());
- int length = strlen(buffer);
- vsnprintf(buffer + length, -length + sizeof buffer, format, varList);
+ position->sourceUnit()->name().toUtf8().constData(),
+ position->lineNo(), position->column());
+ int used = strlen(buffer);
+ int length = strlen(message);
+ if (length >= (int) sizeof buffer - used)
+ length = sizeof buffer - used - 1;
+ memcpy(buffer + used, message, length);
+ buffer[used + length] = '\0';
m_messages.append(buffer);
}
+/**
+ * @brief Common actions for the error/warning functions.
+ *
+ * @param prefix first char in the message: 'E' (error) or 'W' (warning)
+ * @param location unique id of the error/warning message
+ * @param position position of the error/warning
+ * @param format message with placeholdes like sprintf()
+ * @param varList the variable argument list
+ */
+void RplParser::addMessage(LevelTag prefix, int location,
+ const RplSourcePosition* position,
+ const char* format, va_list varList){
+ char buffer[2048];
+ vsnprintf(buffer, sizeof buffer, format, varList);
+ addSimpleMessage(prefix, location, position, buffer);
+}
+
/**
* @brief Adds an error message and throws an exception.
*
*
* @param location unique id of the error/warning message
* @param format message with placeholdes like sprintf()
- * @param ... optional: the variable argument list
*/
-void RplParser::syntaxError(int location, const char* format, ...)
+void RplParser::syntaxError(int location, const char* message)
{
- va_list ap;
- va_start(ap, format);
- addMessage('E', location, format, ap);
- va_end(ap);
- throw RplSyntaxError(format);
+ addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message);
+ throw RplSyntaxError(message);
+}
+
+/**
+ * @brief Adds an error message and throws an exception.
+ *
+ * This method is used if a closing symbol (e.g. a ')' or 'end') is missed.
+ * The message contains a %s as an placeholder for the position of the
+ * starting symbol.
+ * The exception will be catched at a position where error recovery
+ * can take place.
+ *
+ * @param location unique id of the error
+ * @param message message describing the error
+ * @param symbol starting symbol corresponding to the missed closing symbol
+ * @param position position of the starting symbol
+ */
+
+void RplParser::syntaxError(int location, const char* message,
+ const char* symbol,
+ const RplSourcePosition* position)
+{
+ char buffer[256];
+ char buffer2[512];
+ snprintf(buffer2, sizeof buffer2,
+ "The starting symbol %s is located here. Missing point: %s",
+ symbol, m_lexer.currentPosition()->utf8(buffer, sizeof buffer));
+
+ addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message);
+ addSimpleMessage(LT_INFO, location + 1, position, buffer2);
+ throw RplSyntaxError(message);
}
/**
{
va_list ap;
va_start(ap, format);
- addMessage('E', location, format, ap);
+ addMessage(LT_ERROR, location, m_lexer.currentPosition(), format, ap);
va_end(ap);
if (++m_errors >= m_maxErrors)
throw RplParserStop("too many errors");
{
va_list ap;
va_start(ap, format);
- addMessage('W', location, format, ap);
+ addMessage(LT_WARNING, location, m_lexer.currentPosition(), format, ap);
va_end(ap);
if (++m_warnings >= m_maxWarnings)
throw RplParserStop("too many warnings");
};
class RplParser {
+public:
+ enum LevelTag {
+ LT_ERROR = 'E',
+ LT_WARNING = 'W',
+ LT_INFO = 'I'
+ };
+
public:
typedef QList<QString> MessageList;
public:
RplParser(RplLexer& lexer, RplASTree& ast);
public:
- void syntaxError(int location, const char* format, ...);
+ void addSimpleMessage(LevelTag prefix, int location,
+ const RplSourcePosition* pos,
+ const char* message);
+ void addMessage(LevelTag prefix, int location,
+ const RplSourcePosition* pos,
+ const char* format, va_list varList);
+ void syntaxError(int location, const char* message);
+ void syntaxError(int location, const char* message, const char* symbol,
+ const RplSourcePosition* position);
void error(int location, const char* format, ...);
void warning(int location, const char* format, ...);
-protected:
- void addMessage(char prefix, int location, const char* format, va_list varList);
protected:
RplLexer& m_lexer;
--- /dev/null
+file.find('*.c')[0].name;
+[1,2,3].join(' ');
+3.14.trunc;
+= <test> (module) parent: global
+== Classes:
+== Variables:
+== Body:
+Expr id: 8 expr: 7 succ: 15 <test>:1:24
+ field name id: 7 parent: 5 succ: <test>:1:24
+ indexedValue id: 5 index: 6 parent: 2 <test>:1:16
+ const id: 6 value: 0 <test>:1:17
+ call find Id: 2 args: 4 parent: 1 succ: 0 <test>:1:9
+ arg 1 id: 4 expr: 3 succ: 0
+ const id: 3 value: '*.c' <test>:1:10
+ namedValue file id: 1 attr: 0x0 <test>:1:4
+Expr id: 15 expr: 12 succ: 18 <test>:2:12
+ call join Id: 12 args: 14 parent: 9 succ: 0 <test>:2:12
+ arg 1 id: 14 expr: 13 succ: 0
+ const id: 13 value: ' ' <test>:2:13
+ listConst id: 9 <test>:1:26
+ [1,2,<formula 11>]
+Expr id: 18 expr: 17 succ: 0 <test>:3:10
+ field trunc id: 17 parent: 16 succ: <test>:3:10
+ const id: 16 value: 3.140000 <test>:2:19
namedValue a id: 1 attr: 0x0 <test>:1:4
forIt id: 4 var: 3 set: 5 body: 9 succ: 0 <test>:1:7
namedValue x id: 3 attr: 0x0 <test>:2:4
- namedValue a id: 5 attr: 0x0
+ namedValue a id: 5 attr: 0x0 <test>:2:11
Expr id: 9 expr: 7 succ: 0 <test>:3:2
BinOp id: 7 op: += (6) left: 6 right: 8 <test>:3:2
namedValue a id: 6 attr: 0x0 <test>:3:2
== Variables:
== Body:
Expr id: 2 expr: 1 succ: 6 <test>:1:4
- call rand Id: 1 args: 0 succ: 0 <test>:1:4
+ call rand Id: 1 args: 0 parent: 0 succ: 0 <test>:1:4
Expr id: 6 expr: 3 succ: 20 <test>:2:3
- call sin Id: 3 args: 5 succ: 0 <test>:2:3
- Arg 1 id: 5 expr: 4 succ: 0
+ call sin Id: 3 args: 5 parent: 0 succ: 0 <test>:2:3
+ arg 1 id: 5 expr: 4 succ: 0
namedValue a id: 4 attr: 0x0 <test>:2:5
Expr id: 20 expr: 7 succ: 0 <test>:3:3
- call max Id: 7 args: 13 succ: 0 <test>:3:3
- Arg 1 id: 13 expr: 9 succ: 19
+ call max Id: 7 args: 13 parent: 0 succ: 0 <test>:3:3
+ arg 1 id: 13 expr: 9 succ: 19
BinOp id: 9 op: + (26) left: 8 right: 11 <test>:3:5
const id: 8 value: 1 <test>:3:4
BinOp id: 11 op: * (30) left: 10 right: 12 <test>:3:7
const id: 10 value: 2 <test>:3:6
const id: 12 value: 3 <test>:3:8
- Arg 2 id: 19 expr: 15 succ: 0
+ arg 2 id: 19 expr: 15 succ: 0
BinOp id: 15 op: ** (31) left: 14 right: 17 <test>:3:11
const id: 14 value: 4 <test>:3:10
BinOp id: 17 op: - (27) left: 16 right: 18 <test>:3:15
parser.parse();
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__);
+ }
virtual void doIt(void) {
+ fieldTest();
methodCallTest();
mapTest();
forItTest();