Nicolas Winkler 7 năm trước cách đây
mục cha
commit
e3c4d41179
5 tập tin đã thay đổi với 193 bổ sung71 xóa
  1. 62 21
      src/Ast.h
  2. 21 1
      src/Semantic.cpp
  3. 3 0
      src/Semantic.h
  4. 44 6
      src/lexer.l
  5. 63 43
      src/parser.y

+ 62 - 21
src/Ast.h

@@ -32,6 +32,8 @@
 
 namespace qlow
 {
+    struct CodePosition;
+    
     class StructureVisitor;
     namespace ast
     {
@@ -76,9 +78,26 @@ namespace qlow
 }
 
 
+/*!
+ * \brief bison-compatible location struct
+ */
+struct qlow::CodePosition
+{
+    int first_line;
+    int last_line;
+    int first_column;
+    int last_column;
+};
+
+
 struct qlow::ast::AstObject :
     public Visitable<std::unique_ptr<sem::SemanticObject>, const sem::SymbolTable<sem::Class>, StructureVisitor>
 {
+    CodePosition pos;
+    
+    inline AstObject(const CodePosition& cp) :
+        pos{ cp } {}
+        
     virtual ~AstObject(void);
 };
 
@@ -88,7 +107,8 @@ struct qlow::ast::Class : public AstObject
     std::string name;
     List<FeatureDeclaration> features;
     
-    inline Class(const std::string& name, List<FeatureDeclaration>& features) :
+    inline Class(const std::string& name, List<FeatureDeclaration>& features, const CodePosition& cp) :
+        AstObject{ cp },
         name{ name }, features(std::move(features))
     {
     }
@@ -102,7 +122,8 @@ struct qlow::ast::FeatureDeclaration : public AstObject
     std::string name;
     std::string type;
 
-    inline FeatureDeclaration(const std::string& type, const std::string& name) :
+    inline FeatureDeclaration(const std::string& type, const std::string& name, const CodePosition& cp) :
+        AstObject{ cp },
         name(name), type(type)
     {
     }
@@ -113,8 +134,8 @@ struct qlow::ast::FeatureDeclaration : public AstObject
 
 struct qlow::ast::FieldDeclaration : public FeatureDeclaration
 {
-    inline FieldDeclaration(const std::string& type, const std::string& name) :
-        FeatureDeclaration(type, name)
+    inline FieldDeclaration(const std::string& type, const std::string& name, const CodePosition& cp) :
+        FeatureDeclaration(type, name, cp)
     {
     }
 
@@ -128,16 +149,16 @@ struct qlow::ast::MethodDefinition : public FeatureDeclaration
     std::unique_ptr<DoEndBlock> body;
 
     inline MethodDefinition(const std::string& type, const std::string& name,
-            std::unique_ptr<DoEndBlock> body) :
-        FeatureDeclaration(type, name),
+            std::unique_ptr<DoEndBlock> body, const CodePosition& cp) :
+        FeatureDeclaration(type, name, cp),
         body(std::move(body))
     {
     }
 
 
     inline MethodDefinition(const std::string& type, const std::string& name,
-            List<ArgumentDeclaration>&& arguments, std::unique_ptr<DoEndBlock> body) :
-        FeatureDeclaration(type, name),
+            List<ArgumentDeclaration>&& arguments, std::unique_ptr<DoEndBlock> body, const CodePosition& cp) :
+        FeatureDeclaration(type, name, cp),
         arguments(std::move(arguments)),
         body(std::move(body))
     {
@@ -151,7 +172,8 @@ struct qlow::ast::VariableDeclaration  : public AstObject
 {
     std::string type;
     std::string name;
-    inline VariableDeclaration(const std::string& type, const std::string& name) :
+    inline VariableDeclaration(const std::string& type, const std::string& name, const CodePosition& cp) :
+        AstObject{ cp },
         type(type), name(name)
     {
     }
@@ -163,8 +185,8 @@ struct qlow::ast::VariableDeclaration  : public AstObject
 struct qlow::ast::ArgumentDeclaration :
     public VariableDeclaration
 {
-    inline ArgumentDeclaration(const std::string& type, const std::string& name) :
-        VariableDeclaration(type, name)
+    inline ArgumentDeclaration(const std::string& type, const std::string& name, const CodePosition& cp) :
+        VariableDeclaration(type, name, cp)
     {
     }
 
@@ -176,7 +198,8 @@ struct qlow::ast::DoEndBlock : public AstObject
 {
     List<Statement> statements;
     
-    inline DoEndBlock(List<Statement>&& statements) :
+    inline DoEndBlock(List<Statement>&& statements, const CodePosition& cp) :
+        AstObject{ cp },
         statements(std::move(statements))
     {
     }
@@ -187,6 +210,9 @@ struct qlow::ast::DoEndBlock : public AstObject
 
 struct qlow::ast::Statement : public virtual AstObject
 {
+    inline Statement(const CodePosition& cp) :
+        AstObject{ cp } {}
+        
     virtual ~Statement(void);
 
     std::unique_ptr<sem::SemanticObject> accept(StructureVisitor& v, const sem::SymbolTable<sem::Class>&);
@@ -195,6 +221,9 @@ struct qlow::ast::Statement : public virtual AstObject
 
 struct qlow::ast::Expression : public virtual AstObject
 {
+    inline Expression(const CodePosition& cp) :
+        AstObject{ cp } {}
+        
     std::unique_ptr<sem::SemanticObject> accept(StructureVisitor& v, const sem::SymbolTable<sem::Class>&);
 };
 
@@ -205,14 +234,18 @@ struct qlow::ast::FeatureCall : public Expression, public Statement
     std::string name;
     List<Expression> arguments;
 
-    inline FeatureCall(std::unique_ptr<Expression> target, const std::string& name) :
+    inline FeatureCall(std::unique_ptr<Expression> target, const std::string& name, const CodePosition& cp) :
+        AstObject{ cp },
+        Expression{ cp }, Statement{ cp },
         target(std::move(target)), name(name)
     {
     }
 
 
     inline FeatureCall(std::unique_ptr<Expression> target, const std::string& name,
-            List<Expression>&& arguments) :
+            List<Expression>&& arguments, const CodePosition& cp) :
+        AstObject{ cp },
+        Expression{ cp }, Statement{ cp },
         target(std::move(target)), name(name), arguments(std::move(arguments))
     {
     }
@@ -226,7 +259,9 @@ struct qlow::ast::AssignmentStatement : public Statement
     std::string target;
     std::unique_ptr<Expression> expr;
 
-    inline AssignmentStatement(const std::string& target, std::unique_ptr<Expression> expr) :
+    inline AssignmentStatement(const std::string& target, std::unique_ptr<Expression> expr, const CodePosition& cp) :
+        AstObject{ cp },
+        Statement{ cp },
         target(target), expr(std::move(expr))
     {
     }
@@ -239,7 +274,9 @@ struct qlow::ast::NewVariableStatement : public Statement
 {
     std::string name;
     std::string type;
-    inline NewVariableStatement(const std::string& name, const std::string& type) :
+    inline NewVariableStatement(const std::string& name, const std::string& type, const CodePosition& cp) :
+        AstObject{ cp },
+        Statement{ cp },
        name(name), type(type)
     {
     } 
@@ -255,7 +292,9 @@ struct qlow::ast::Operation : public Expression
     };
     Operator op;
 
-    inline Operation(Operator op) :
+    inline Operation(Operator op, const CodePosition& cp) :
+        AstObject{ cp },
+        Expression{ cp },
         op(op)
     {
     }
@@ -273,8 +312,9 @@ struct qlow::ast::UnaryOperation : public Operation
     Side side;
     std::unique_ptr<Expression> expr;
 
-    inline UnaryOperation(std::unique_ptr<Expression> expr, Side side, Operator op) :
-        Operation(op),
+    inline UnaryOperation(std::unique_ptr<Expression> expr, Side side, Operator op, const CodePosition& cp) :
+        AstObject{ cp },
+        Operation(op, cp),
         side(side),
         expr(std::move(expr))
     {
@@ -289,8 +329,9 @@ struct qlow::ast::BinaryOperation : public Operation
     std::unique_ptr<Expression> left;
     std::unique_ptr<Expression> right;
 
-    inline BinaryOperation(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right, Operator op) :
-        Operation(op),
+    inline BinaryOperation(std::unique_ptr<Expression> left, std::unique_ptr<Expression> right, Operator op, const CodePosition& cp) :
+        AstObject{ cp },
+        Operation(op, cp),
         left(std::move(left)), right(std::move(right))
     {
     }

+ 21 - 1
src/Semantic.cpp

@@ -44,12 +44,27 @@ SymbolTable<qlow::sem::Class>
     // create all methods and fields
     for (auto& [name, semClass] : semClasses) {
         for (auto& feature : semClass->astNode->features) {
+            
             if (auto* field = dynamic_cast<qlow::ast::FieldDeclaration*> (feature.get()); field) {
+                if (semClass->fields.find(field->name) != semClass->fields.end()) // throw, if field already exists
+                    throw SemanticException(SemanticException::DUPLICATE_FIELD_DECLARATION, field->name);
+                
+                // otherwise add to the fields list
                 semClass->fields[field->name] = unique_dynamic_cast<Field>(av.visit(*field, semClasses));
             }
-            if (auto* method = dynamic_cast<qlow::ast::MethodDefinition*> (feature.get()); method) {
+            else if (auto* method = dynamic_cast<qlow::ast::MethodDefinition*> (feature.get()); method) {
+                if (semClass->methods.find(method->name) != semClass->methods.end()) // throw, if method already exists
+                    throw SemanticException(SemanticException::DUPLICATE_METHOD_DEFINITION, method->name);
+                
+                // otherwise add to the methods list
                 semClass->methods[method->name] = unique_dynamic_cast<Method>(av.visit(*method, semClasses));
             }
+            else {
+                // if a feature is neither a method nor a field, something went horribly wrong
+                throw "internal error";
+            }
+            
+            
         }
     }
     return semClasses;
@@ -77,6 +92,11 @@ std::string Class::toString(void) const
         val += field.second->toString() + ", ";
     if (!fields.empty())
         val = val.substr(0, val.length() - 2);
+    val += " (";
+    val += std::to_string(this->astNode->pos.first_line) + ", ";
+    val += std::to_string(this->astNode->pos.first_column);
+    val += " )";
+    
     return val + "]";
 }
 

+ 3 - 0
src/Semantic.h

@@ -93,6 +93,9 @@ public:
     enum ErrorCode
     {
         UNKNOWN_TYPE,
+        DUPLICATE_CLASS_DEFINITION,
+        DUPLICATE_FIELD_DECLARATION,
+        DUPLICATE_METHOD_DEFINITION,
     };
     
     

+ 44 - 6
src/lexer.l

@@ -33,25 +33,63 @@ extern "C" int yywrap()
     return 1; /* do not continue on EOF */
 }
 
+int commentDepth;
+
+size_t offset;
+extern QLOW_PARSER_LTYPE qlow_parser_lloc;
+
+#define YY_USER_ACTION                          \
+  do {                                          \
+    qlow_parser_lloc.first_line = yylineno;     \
+    qlow_parser_lloc.first_column = offset;     \
+    offset += yyleng;                           \
+    qlow_parser_lloc.last_line = yylineno;      \
+    qlow_parser_lloc.last_column = offset;      \
+  } while(0);
+
 %}
 
 
 %option prefix="qlow_parser_"
+%option yylineno
+%option stack
+
+%x COMMENT
+%x LINE_COMMENT
+%x STRING
+%s METHOD
 
 %%
 
+<COMMENT>"/*"           commentDepth++;
+<COMMENT>"*/"           if ((--commentDepth) == 0) { BEGIN(INITIAL); };
+<COMMENT>\n             ;
+<COMMENT>.              ; // inside comment, ignore everything
+
+<LINE_COMMENT>\n        yy_push_state(INITIAL);
+<LINE_COMMENT>.         ; // inside comment, ignore everything
+
+<STRING>"\""            yy_pop_state();
+<STRING>[^\"^]*         printf("%s\n", std::string(yytext, yyleng).c_str());
+
+<INITIAL>"/*"           yy_push_state(COMMENT); commentDepth = 1;
+<INITIAL>"//"           yy_push_state(LINE_COMMENT);
+<INITIAL>"\""           yy_push_state(STRING);
+
+
 [\t ]                   ; // Space or tab ignored
-\/\*[.\n]*\*\/          ; // comment
-\n                      ; //return SET_TOKEN(NEW_LINE);
+<METHOD>\n              offset = 0; return SET_TOKEN(NEW_LINE);
+\n                      offset = 0; //return SET_TOKEN(NEW_LINE);
 
 "class"                 return SET_TOKEN(CLASS);
-"do"                    return SET_TOKEN(DO);
-"end"                   return SET_TOKEN(END);
+"do"                    yy_push_state(METHOD); return SET_TOKEN(DO);
+<METHOD>"end"           yy_pop_state(); return SET_TOKEN(END);
+<INITIAL>"end"          return SET_TOKEN(END);
 "if"                    return SET_TOKEN(IF);
 
 ":"                     return SET_TOKEN(COLON);
-","                     return SET_TOKEN(COMMA);
 ";"                     return SET_TOKEN(SEMICOLON);
+","                     return SET_TOKEN(COMMA);
 ":="                    return SET_TOKEN(ASSIGN);
 "."                     return SET_TOKEN(DOT);
 
@@ -65,7 +103,7 @@ extern "C" int yywrap()
 
 
 [a-zA-Z_][a-zA-Z0-9_]*  SET_STRING; return IDENTIFIER;
-.                       printf("Unknown token!\n"); yyterminate();
+.                       printf("Unexpected symbol %s.\n", std::string(yytext, yyleng).c_str()); yyterminate();
 
 %%
 

+ 63 - 43
src/parser.y

@@ -32,28 +32,40 @@ using namespace qlow::ast;
 
 extern int qlow_parser_lex();
 
+void yy_pop_state(void);
+
 int qlow_parser_error(const char*)
 {
     throw "syntax error";
 }
 
+#define QLOW_PARSER_LTYPE_IS_DECLARED
+typedef qlow::CodePosition QLOW_PARSER_LTYPE;
+
+/*
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+*/
+
 using ClassList = std::vector<std::unique_ptr<qlow::ast::Class>>;
 std::unique_ptr<ClassList> parsedClasses;
 
 %}
 
+
 %define api.prefix {qlow_parser_}
 
-/*
-%skeleton "lalr1.cc" // generate C++ parser
-//%define api.namespace {uetli::parser}
-//%define api.value.type {struct semantic_type}
-//%define parser_class_name {Parser}
+%locations
+%code requires { #include "Ast.h" }
+
+//%define api.location.type {qlow::CodePosition}
 
-//%name-prefix "uetli_parser_"
-*/
 %union {
-    //using qlow::ast::Class;
     std::vector<std::unique_ptr<qlow::ast::Class>>* classes;
     qlow::ast::Class* classDefinition;
     qlow::ast::FeatureDeclaration* featureDeclaration;
@@ -79,7 +91,7 @@ std::unique_ptr<ClassList> parsedClasses;
     const char* cString;
     std::string* string;
     int token;
-};
+}
 
 
 %token <string> IDENTIFIER
@@ -113,14 +125,6 @@ std::unique_ptr<ClassList> parsedClasses;
 
 %%
 
-/* possible newline characters
-pnl:
-    {
-    }
-    |
-    pnl NEW_LINE {
-    };
-*/
 
 /* list of class definitions */
 classes:
@@ -135,7 +139,7 @@ classes:
 
 classDefinition:
     CLASS IDENTIFIER featureList END {
-        $$ = new Class(*$2, *$3);
+        $$ = new Class(*$2, *$3, @$);
         delete $2; delete $3; $2 = 0; $3 = 0;
     };
 
@@ -163,30 +167,30 @@ featureDeclaration:
 
 fieldDeclaration:
     IDENTIFIER COLON IDENTIFIER {
-        $$ = new FieldDeclaration(*$3, *$1);
+        $$ = new FieldDeclaration(*$3, *$1, @$);
         delete $3; delete $1; $1 = $3 = 0;
     };
 
 
 methodDefinition:
     IDENTIFIER COLON IDENTIFIER doEndBlock {
-        $$ = new MethodDefinition(*$3, *$1, std::move(std::unique_ptr<DoEndBlock>($4)));
+        $$ = new MethodDefinition(*$3, *$1, std::move(std::unique_ptr<DoEndBlock>($4)), @$);
         delete $3; delete $1; $1 = $3 = 0;
     }
     |
     IDENTIFIER doEndBlock {
-        $$ = new MethodDefinition("", *$1, std::move(std::unique_ptr<DoEndBlock>($2)));
+        $$ = new MethodDefinition("", *$1, std::move(std::unique_ptr<DoEndBlock>($2)), @$);
         delete $1; $1 = 0;
     }
     |
     IDENTIFIER
         ROUND_LEFT argumentList ROUND_RIGHT COLON IDENTIFIER doEndBlock {
-        $$ = new MethodDefinition(*$6, *$1, std::move(*$3), std::move(std::unique_ptr<DoEndBlock>($7)));
+        $$ = new MethodDefinition(*$6, *$1, std::move(*$3), std::move(std::unique_ptr<DoEndBlock>($7)), @$);
         delete $6; delete $1; delete $3; $1 = $6 = nullptr; $3 = nullptr;
     }
     |
     IDENTIFIER ROUND_LEFT argumentList ROUND_RIGHT doEndBlock {
-        $$ = new MethodDefinition("", *$1, std::move(*$3), std::move(std::unique_ptr<DoEndBlock>($5)));
+        $$ = new MethodDefinition("", *$1, std::move(*$3), std::move(std::unique_ptr<DoEndBlock>($5)), @$);
         delete $1; delete $3; $1 = nullptr; $3 = nullptr;
     };
 
@@ -205,62 +209,78 @@ argumentList:
 
 argumentDeclaration:
     IDENTIFIER COLON IDENTIFIER {
-        $$ = new ArgumentDeclaration(*$3, *$1);
+        $$ = new ArgumentDeclaration(*$3, *$1, @$);
         delete $3; delete $1; $1 = $3 = 0;
     };
 
 
 doEndBlock:
     DO statements END {
-        $$ = new DoEndBlock(std::move(*$2));
+        $$ = new DoEndBlock(std::move(*$2), @$);
         delete $2; $2 = 0;
     };
 
 
 statements:
-    /* empty */ {
+    pnl {
         $$ = new std::vector<std::unique_ptr<Statement>>();
     }
     |
-    statements statement {
+    statements statement pnl {
         $$ = $1;
-        $$->push_back(std::move(std::unique_ptr<Statement>($2)));
+        $$->push_back(std::unique_ptr<Statement>($2));
     };
 
+/*!
+ * hacky way to allow for multiple empty lines between statements
+ */
+pnl:
+    /* empty */ {
+    }
+    |
+    pnl NEW_LINE {
+    }
+    ;
 
 statement:
-    featureCall SEMICOLON {
+    featureCall statementEnd {
         $$ = $1;
     }
     |
-    assignmentStatement SEMICOLON {
+    assignmentStatement statementEnd {
         $$ = $1;
     }
     |
-    newVariableStatement SEMICOLON {
+    newVariableStatement statementEnd {
         $$ = $1;
     };
+    
+statementEnd:
+    SEMICOLON {}
+    |
+    NEW_LINE {}
+    ;
 
 
 featureCall:
     IDENTIFIER {
-        $$ = new FeatureCall(nullptr, *$1);
+        $$ = new FeatureCall(nullptr, *$1, @$);
         delete $1; $1 = 0;
     }
     |
     IDENTIFIER ROUND_LEFT expressionList ROUND_RIGHT {
-        $$ = new FeatureCall(nullptr, *$1, std::move(*$3));
+        $$ = new FeatureCall(nullptr, *$1, std::move(*$3), @$);
         delete $1; delete $3; $1 = 0; $3 = 0;
     }
     |
     expression DOT IDENTIFIER {
-        $$ = new FeatureCall(std::move(std::unique_ptr<Expression>($1)), *$3);
+        $$ = new FeatureCall(std::unique_ptr<Expression>($1), *$3, @$);
         delete $3; $3 = 0;
     }
     |
     expression DOT IDENTIFIER ROUND_LEFT expressionList ROUND_RIGHT {
-        $$ = new FeatureCall(std::move(std::unique_ptr<Expression>($1)), *$3,
-            std::move(*$5));
+        $$ = new FeatureCall(std::unique_ptr<Expression>($1), *$3,
+            std::move(*$5), @$);
         delete $3; $3 = 0; delete $5; $5 = 0;
     };
 
@@ -269,12 +289,12 @@ featureCall:
 expressionList:
     expression {
         $$ = new std::vector<std::unique_ptr<Expression>>();
-        $$->push_back(std::move(std::unique_ptr<Expression>($1)));
+        $$->push_back(std::unique_ptr<Expression>($1));
     }
     |
     expressionList COMMA expression {
         $$ = $1;
-        $$->push_back(std::move(std::unique_ptr<Expression>($3)));
+        $$->push_back(std::unique_ptr<Expression>($3));
     };
 
 
@@ -305,19 +325,19 @@ operationExpression:
 binaryOperation:
     expression operator expression {
         $$ = new BinaryOperation(std::unique_ptr<Expression>($1), 
-            std::unique_ptr<Expression>($3), $2);
+            std::unique_ptr<Expression>($3), $2, @$);
     };
 
 
 unaryOperation:
     expression operator {
         $$ = new UnaryOperation(std::unique_ptr<Expression>($1),
-            UnaryOperation::SUFFIX, $2);
+            UnaryOperation::SUFFIX, $2, @$);
     }
     |
     operator expression {
         $$ = new UnaryOperation(std::unique_ptr<Expression>($2),
-            UnaryOperation::PREFIX, $1);
+            UnaryOperation::PREFIX, $1, @$);
     };
 
 
@@ -339,14 +359,14 @@ paranthesesExpression:
 
 assignmentStatement:
     IDENTIFIER ASSIGN expression {
-        $$ = new AssignmentStatement(std::move(*$1), std::unique_ptr<Expression>($3));
+        $$ = new AssignmentStatement(std::move(*$1), std::unique_ptr<Expression>($3), @$);
         delete $1; $1 = 0;
     };
 
 
 newVariableStatement:
     IDENTIFIER COLON IDENTIFIER {
-        $$ = new NewVariableStatement(*$3, *$1);
+        $$ = new NewVariableStatement(*$3, *$1, @$);
         delete $3; delete $1; $3 = 0; $1 = 0;
     };