/**
* @brief Constructor.
*
+ * @param type the type of the variable
* @param name the name of the instance
* @param attr a bitmask of <code>Attribute</code> values, e.g. A_CONST
*/
-RplASNamedValue::RplASNamedValue(const QString& name, int attributes) :
- RplASItem(AST_NAMED_VALUE),
+RplASNamedValue::RplASNamedValue(RplASClass* type,
+ const QString& name, int attributes) :
+ RplASNode1(AST_NAMED_VALUE),
m_name(name),
- m_attributes(attributes)
+ m_attributes(attributes),
+ m_type(type)
{
}
/**
RplASVariant m_value;
};
-class RplASNamedValue : public RplASItem, public RplASCalculable
-{
-public:
- enum Attributes {
- A_NONE,
- /// the value cannot be changed.
- A_CONST = 1<<1,
- /// the variable/constant is found in the global namespace, not in a method
- A_GLOBAL = 1<<2,
- /// the variable/constant is found in the module namespace, not in a method
- A_MODULE_STATIC = 1<<3
- };
-
-public:
- RplASNamedValue(const QString& name, int attributes = A_NONE);
- QString name() const;
-public:
- virtual void calc(RplASVariant& value);
- void dump(FILE* fp, int indent);
-private:
- QString m_name;
- int m_attributes;
-};
-
class RplASNode1 : public RplASItem
{
public:
RplASItem* m_child4;
};
+class RplASNamedValue : public RplASNode1, public RplASCalculable
+{
+public:
+ enum Attributes {
+ A_NONE,
+ /// the value cannot be changed.
+ A_CONST = 1<<1,
+ /// the variable/constant is found in the global namespace, not in a method
+ A_GLOBAL = 1<<2,
+ /// 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
+ };
+
+public:
+ RplASNamedValue(RplASClass* type, const QString& name,
+ int attributes = A_NONE);
+public:
+ QString name() const;
+public:
+ virtual void calc(RplASVariant& value);
+ void dump(FILE* fp, int indent);
+private:
+ QString m_name;
+ int m_attributes;
+ RplASClass* m_type;
+};
+
class RplASUnaryOp : public RplASNode1
{
public:
return m_tokenType;
}
+/**
+ * @brief Checks whether the instance has a given token type.
+ *
+ * @param expected the token type to compare
+ *
+ * @return true: the expected type is the current<br>
+ * false: otherwise
+ */
+bool RplToken::isTokenType(RplTokenType expected) const
+{
+ return m_tokenType == expected;
+}
+
+/**
+ * @brief Checks whether the instance is a given operator.
+ *
+ * @param expected the token type to compare
+ *
+ * @return true: the instance is an operator and the expected<br>
+ * false: otherwise
+ */
+bool RplToken::isOperator(int expected, int alternative) const
+{
+ return m_tokenType == TOKEN_OPERATOR && (m_value.m_id == expected
+ || m_value.m_id == alternative);
+}
+
/**
* @brief Makes all members undefined.
*/
m_value.m_integer = 0;
}
+/**
+ * @brief Returns whether the token is a capitalized id
+ *
+ * @return true: the token is an id and the first char is an upper case char<br>
+ * false: otherwise
+ */
+bool RplToken::isCapitalizedId() const
+{
+ bool rc = m_tokenType == TOKEN_ID && m_string.at(0).isUpper()
+ && (m_string.length() == 1 || m_string.at(1).isLower());
+ return rc;
+}
+
/** @class RplLexer rpllexer.hpp "rplexpr/rpllexer.hpp"
*
m_currentCol(0),
m_hasMoreInput(false),
m_stringFeatures(stringFeatures),
- m_storageFlags(storageFlags)
+ m_storageFlags(storageFlags),
// m_prioOfOp
+ // m_assocOfOp
+ m_opNames()
{
memset(m_prioOfOp, 0, sizeof m_prioOfOp);
+ memset(m_assocOfOp, 0, sizeof m_assocOfOp);
m_currentPosition->setSourceUnit(source->currentReader()
->currentSourceUnit());
memset(m_charInfo, 0, sizeof m_charInfo);
m_input.remove(0, length);
m_currentCol += length;
}
+/**
+ * @brief Returns the last read token.
+ *
+ * @return the current token
+ */
+RplToken* RplLexer::currentToken() const
+{
+ return m_currentToken;
+}
+
/**
* @brief Returns the current position.
*
return rc;
}
+/**
+ * @brief Returns whether an operator is right associative
+ * @param op op to test
+ * @return true: the operator is right associative<br>
+ * false: otherwise
+ */
+bool RplLexer::isRightAssociative(int op) const
+{
+ bool rc = false;
+ if (op >= 0 && (unsigned) op < sizeof m_assocOfOp / sizeof m_assocOfOp[0]){
+ rc = m_assocOfOp[op];
+ }
+ return rc;
+}
+
const QString& rawString() const;
int id() const;
RplTokenType tokenType() const;
+ bool isTokenType(RplTokenType expected) const;
+ bool isOperator(int expected, int alternative = 0) const;
void clear();
+ bool isCapitalizedId() const;
protected:
RplTokenType m_tokenType;
QString m_string;
const QByteArray& nameOfOp(int op) const;
bool isRightAssociative(int op) const;
RplSourcePosition* currentPosition() const;
+ RplToken* currentToken() const;
private:
void prepareOperators(const char* operators, const char* rightAssociatives);
#include "rplexpr/rplexpr.hpp"
enum MFLocations{
- L_PARSE_OPERAND_RPARENTH = 2000,
+ L_PARSE_OPERAND_RPARENTH = 2001,
L_PARSE_OPERAND_RPARENTH_FUNC,
- L_PARSE_OPERAND_WRONG,
- L_TERM_NO_OP,
- L_TERM_NO_OP2,
- L_TERM_WRONG_KEYWORD,
- L_TERM_WRONG_ID
+ L_TERM_WRONG_STRING,
+ L_TERM_WRONG_NUMBER,
+ 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
};
/**
* @brief Parses a variable definition.
*
+ * Syntax:
+ * Variable: { "const" | "lazy" }* <type> id [ = <expr> ] ";"
+ * Parameter: { "const" | "lazy" }* <type> id [ = <expr> ] { "," | ")" }
+ *
+ * AST: RplASNamedValue
+ *
* @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(RplASClass* clazz, Keyword attribute)
+RplASItem* RplMFParser::parseDefinition(Keyword attribute)
{
- RplASItem*rc = NULL;
+ RplASNamedValue::Attributes attr = RplASNamedValue::A_NONE;
+ RplToken* token;
+ while(attribute == K_CONST || attribute == K_LAZY){
+ switch(attribute){
+ case K_CONST:
+ attr = RplASNamedValue::A_CONST;
+ break;
+ case K_LAZY:
+ attr = 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");
+ if (! token->isCapitalizedId())
+ syntaxError(L_DEFINITION_WRONG_ID,
+ "a class name must start with an upper case character");
+ RplASClass* clazz = m_tree.currentSpace()->findClass(token->toString());
+ if (clazz == NULL)
+ syntaxError(L_DEFINITION_UNKNOWN_CLASS, "unknown class");
+ 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);
+ 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();
+ rc->setChild(value);
+ token = m_lexer.currentToken();
+ }
+ if (token->isOperator(O_SEMICOLON)){
+ syntaxError(L_DEFINITION_NO_OP, "';' expected");
+ }
return rc;
}
* second term: a + term1<br>
*
* @param depth the level of the parenthesis
- * @return
+ * @return the abstract syntax tree representing the parsed expression
*/
RplASItem* RplMFParser::parseTerm(int depth){
RplToken* token;
- RplSourcePosition* start = m_lexer.currentPosition();
- RplASItem* top = NULL;
- RplASItem* item = NULL;
- int lastPrio = -1;
+ RplASItem* top = parseOperand(depth);
+ int lastPrio = INT_MAX;
bool again = true;
do {
- item = parseOperand(depth);
token = m_lexer.nextNonSpaceToken();
RplTokenType tokenType = token->tokenType();
- int tokenId;
switch(tokenType){
case TOKEN_OPERATOR:
{
RplASBinaryOp* op = new RplASBinaryOp();
int opId = token->id();
op->setOperator(opId);
+ op->setPosition(m_lexer.currentPosition());
+
int prio = m_lexer.prioOfOp(token->id());
if (prio < lastPrio
- || prio == lastPrio && m_lexer.isRightAssociative(opId)){
- op->setChild(item);
- op->setPosition(m_lexer.currentPosition());
- op->setChild2(parseOperand(depth));
- } else {
- op->setChild(item);
- op->setPosition(m_lexer.currentPosition());
- op->setChild2(parseOperand(depth));
+ || (prio == lastPrio
+ && ! m_lexer.isRightAssociative(opId))){
+ op->setChild(top);
+ top = op;
+ } else{
+ RplASBinaryOp* top2 = dynamic_cast<RplASBinaryOp*>(op);
+ op->setChild(top2->child2());
+ top2->setChild2(op);
}
- } else if ( (tokenId = token->id()) == O_RPARENT
- || IS_OP_BEHIND_EXPR(tokenId))
+ op->setChild2(parseOperand(depth));
+ } else
again = false;
- else
- syntaxError(L_TERM_NO_OP, "Operator expected");
break;
}
case TOKEN_STRING:
+ syntaxError(L_TERM_WRONG_STRING, "Operator expected, not a string");
+ break;
case TOKEN_NUMBER:
case TOKEN_REAL:
- syntaxError(L_TERM_NO_OP2, "Operator expected");
+ syntaxError(L_TERM_WRONG_NUMBER, "Operator expected, not a number");
break;
case TOKEN_KEYWORD:
- tokenId = token->id();
- if (IS_KEYWORD_BEHIND_EXPR(tokenId))
- again = false;
- else
- syntaxError(L_TERM_WRONG_KEYWORD, "unexpected keyword found");
- break;
case TOKEN_ID:
- syntaxError(L_TERM_WRONG_ID, "unexpected id found");
- break;
case TOKEN_END_OF_SOURCE:
- again = false;
- break;
default:
+ again = false;
break;
}
} while(again);
case K_LAZY:
item = parseDefinition(NULL, (Keyword) token->id());
break;
- case K_INT:
- item = parseDefinition(&RplASInteger::m_instance, K_UNDEF);
- break;
- case K_FLOAT:
- parseDefinition(&RplASFloat::m_instance, K_UNDEF);
- break;
case K_BOOL:
parseDefinition(&RplASBoolean::m_instance, K_UNDEF);
break;
break;
case TOKEN_ID:
{
- RplASClass* clazz = m_tree.currentSpace()->findClass(token->toString());
- if (clazz != NULL){
- item = parseDefinition(clazz, K_UNDEF);
+ if (token->isCapitalizedId()){
+ item = parseDefinition(K_UNDEF);
} else {
m_lexer.undoLastToken();
item = parseExpr();
O_INC, O_DEC,
O_LPARENTH, O_RPARENT, O_LBRACKET, O_RBRACKET, O_LBRACE, O_RBRACE
};
-#define IS_BINARY_OP(op) ((op) >= O_ASSIGN && op <= O_DOT)
+#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)
RplASItem* parseWhile();
RplASItem* parseRepeat();
RplASItem* parseFor();
- RplASItem* parseDefinition(RplASClass* clazz, Keyword attribute);
+ RplASItem* parseDefinition(Keyword attribute);
RplASItem* parseExpr();
RplASItem* parseBody();
RplASItem* parseMethodDefinition();