From f73927b60594464bb2a93628586fc46042108ce6 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 8 Aug 2021 00:36:50 +0200 Subject: [PATCH 1/9] Add break and continue statements to grammar --- compiler/src/grammar/Spice.g4 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/grammar/Spice.g4 b/compiler/src/grammar/Spice.g4 index 67aaa4147..135e4a2ab 100644 --- a/compiler/src/grammar/Spice.g4 +++ b/compiler/src/grammar/Spice.g4 @@ -12,11 +12,13 @@ ifStmt: IF assignment LBRACE stmtLst RBRACE; stmtLst: (stmt | forLoop | /*foreachLoop |*/ whileLoop | ifStmt)*; paramLstDef: (declStmt | assignment) (COMMA (declStmt | assignment))*; paramLstCall: assignment (COMMA assignment)*; -stmt: (declStmt | assignment | functionCall | assignment | importStmt | returnStmt | printfStmt) SEMICOLON; +stmt: (declStmt | assignment | functionCall | assignment | importStmt | returnStmt | breakStmt | continueStmt | printfStmt) SEMICOLON; declStmt: CONST? dataType IDENTIFIER; functionCall: IDENTIFIER LPAREN paramLstCall? RPAREN; importStmt: IMPORT STRING; returnStmt: RETURN assignment; +breakStmt: BREAK INTEGER?; +continueStmt: CONTINUE; printfStmt: PRINTF LPAREN STRING (COMMA assignment)* RPAREN; assignment: ((declStmt | IDENTIFIER) (ASSIGN_OP | PLUS_EQUAL | MINUS_EQUAL | MUL_EQUAL | DIV_EQUAL))? ternary; @@ -48,6 +50,8 @@ FOR: 'for'; WHILE: 'while'; CONST: 'const'; IMPORT: 'import'; +BREAK: 'break'; +CONTINUE: 'continue'; RETURN: 'return'; MAIN: 'main'; PRINTF: 'printf'; From 6926e1e0aa25dd4738292ab9d724822ef5d7be95 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 8 Aug 2021 11:49:23 +0200 Subject: [PATCH 2/9] Add semantic analysis passes to break and printf stmts --- compiler/src/analyzer/AnalyzerVisitor.cpp | 56 +++++++++++++++++++++++ compiler/src/analyzer/AnalyzerVisitor.h | 2 + compiler/src/exception/SemanticError.h | 8 ++++ media/test.spice | 2 +- 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/compiler/src/analyzer/AnalyzerVisitor.cpp b/compiler/src/analyzer/AnalyzerVisitor.cpp index bc873c8dc..d085af430 100644 --- a/compiler/src/analyzer/AnalyzerVisitor.cpp +++ b/compiler/src/analyzer/AnalyzerVisitor.cpp @@ -242,7 +242,63 @@ antlrcpp::Any AnalyzerVisitor::visitReturnStmt(SpiceParser::ReturnStmtContext* c return returnType; } +antlrcpp::Any AnalyzerVisitor::visitBreakStmt(SpiceParser::BreakStmtContext* ctx) { + if (ctx->INTEGER()) { + int breakCount = std::stoi(ctx->INTEGER()->toString()); + if (breakCount < 1) + throw SemanticError(INVALID_BREAK_NUMBER, "Break count was: " + ctx->INTEGER()->toString()); + } + return TYPE_INT; +} + +antlrcpp::Any AnalyzerVisitor::visitContinueStmt(SpiceParser::ContinueStmtContext* ctx) { + return SpiceBaseVisitor::visitContinueStmt(ctx); +} + antlrcpp::Any AnalyzerVisitor::visitPrintfStmt(SpiceParser::PrintfStmtContext* ctx) { + std::string templateString = ctx->STRING()->toString(); + templateString = templateString.substr(1, templateString.size() - 2); + + // Check if assignment types match placeholder types + std::size_t index = templateString.find_first_of('%'); + int placeholderCount = 0; + while (index != std::string::npos) { + switch (templateString[index + 1]) { + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'c': { + SymbolType assignmentType = visit(ctx->assignment()[placeholderCount]).as(); + if (assignmentType != TYPE_INT && assignmentType != TYPE_BOOL) + throw SemanticError(PRINTF_TYPE_ERROR, "Template string expects an int or a bool here"); + placeholderCount++; + break; + } + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': { + SymbolType assignmentType = visit(ctx->assignment()[placeholderCount]).as(); + if (assignmentType != TYPE_DOUBLE) + throw SemanticError(PRINTF_TYPE_ERROR, "Template string expects a double here"); + placeholderCount++; + break; + } + case 's': { + SymbolType assignmentType = visit(ctx->assignment()[placeholderCount]).as(); + if (assignmentType != TYPE_STRING) + throw SemanticError(PRINTF_TYPE_ERROR, "Template string expects a string here"); + placeholderCount++; + break; + } + } + index = templateString.find_first_of('%', index + 1); + } + return SpiceBaseVisitor::visitPrintfStmt(ctx); } diff --git a/compiler/src/analyzer/AnalyzerVisitor.h b/compiler/src/analyzer/AnalyzerVisitor.h index 75c93922a..9ec70d3ce 100644 --- a/compiler/src/analyzer/AnalyzerVisitor.h +++ b/compiler/src/analyzer/AnalyzerVisitor.h @@ -26,6 +26,8 @@ class AnalyzerVisitor : public SpiceBaseVisitor { antlrcpp::Any visitFunctionCall(SpiceParser::FunctionCallContext* ctx) override; antlrcpp::Any visitImportStmt(SpiceParser::ImportStmtContext* ctx) override; antlrcpp::Any visitReturnStmt(SpiceParser::ReturnStmtContext* ctx) override; + antlrcpp::Any visitBreakStmt(SpiceParser::BreakStmtContext* ctx) override; + antlrcpp::Any visitContinueStmt(SpiceParser::ContinueStmtContext* ctx) override; antlrcpp::Any visitPrintfStmt(SpiceParser::PrintfStmtContext* ctx) override; antlrcpp::Any visitAssignment(SpiceParser::AssignmentContext* ctx) override; antlrcpp::Any visitTernary(SpiceParser::TernaryContext* ctx) override; diff --git a/compiler/src/exception/SemanticError.h b/compiler/src/exception/SemanticError.h index bf5d72686..d47a547e4 100644 --- a/compiler/src/exception/SemanticError.h +++ b/compiler/src/exception/SemanticError.h @@ -16,6 +16,8 @@ enum SemanticErrorType { CONDITION_MUST_BE_BOOL, MISSING_MAIN_FUNCTION, FCT_PARAM_IS_TYPE_DYN, + INVALID_BREAK_NUMBER, + PRINTF_TYPE_ERROR }; class SemanticError : public std::exception { @@ -54,6 +56,12 @@ class SemanticError : public std::exception { case FCT_PARAM_IS_TYPE_DYN: messagePrefix = "Parameter type dyn not valid in function/procedure definition without default value"; break; + case INVALID_BREAK_NUMBER: + messagePrefix = "Invalid number of breaks"; + break; + case PRINTF_TYPE_ERROR: + messagePrefix = "Types of printf call not matching"; + break; } errorMessage = messagePrefix + ": " + message; } diff --git a/media/test.spice b/media/test.spice index 895d8c55c..083d7c77a 100644 --- a/media/test.spice +++ b/media/test.spice @@ -2,6 +2,6 @@ f main() { int i = 0; while i < 10 { i += 1; - printf("i is now at: %f\n", i); + printf("i is now at: %d\n", i); } } \ No newline at end of file From fdfbc4f98086e28b063674195a2bbb1d60877422 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 8 Aug 2021 15:36:59 +0200 Subject: [PATCH 3/9] Print code loc when throwing a SemanticError --- compiler/src/analyzer/AnalyzerVisitor.cpp | 157 +++++++++++---------- compiler/src/analyzer/AnalyzerVisitor.h | 2 - compiler/src/analyzer/SymbolTableEntry.cpp | 3 +- compiler/src/exception/SemanticError.cpp | 30 +++- compiler/src/exception/SemanticError.h | 47 +----- media/test.spice | 2 +- 6 files changed, 120 insertions(+), 121 deletions(-) diff --git a/compiler/src/analyzer/AnalyzerVisitor.cpp b/compiler/src/analyzer/AnalyzerVisitor.cpp index d085af430..ecd9794ac 100644 --- a/compiler/src/analyzer/AnalyzerVisitor.cpp +++ b/compiler/src/analyzer/AnalyzerVisitor.cpp @@ -9,7 +9,8 @@ antlrcpp::Any AnalyzerVisitor::visitEntry(SpiceParser::EntryContext* ctx) { visitChildren(ctx); // Check if the visitor got a main function - if (!hasMainFunction) throw SemanticError(MISSING_MAIN_FUNCTION, "No main function found."); + if (!hasMainFunction) + throw SemanticError(*ctx->start, MISSING_MAIN_FUNCTION, "No main function found."); // Post traversing actions @@ -66,7 +67,7 @@ antlrcpp::Any AnalyzerVisitor::visitFunctionDef(SpiceParser::FunctionDefContext* visit(ctx->stmtLst()); // Check if return variable is now initialized if (currentScope->lookup(RETURN_VARIABLE_NAME)->getState() == DECLARED) - throw SemanticError(FUNCTION_WITHOUT_RETURN_STMT, "Function without return statement"); + throw SemanticError(*ctx->start, FUNCTION_WITHOUT_RETURN_STMT,"Function without return statement"); // Return to old scope currentScope = currentScope->getParent(); return returnType; @@ -109,7 +110,8 @@ antlrcpp::Any AnalyzerVisitor::visitForLoop(SpiceParser::ForLoopContext* ctx) { // Visit condition in new scope SymbolType conditionType = visit(ctx->assignment()[1]).as(); if (conditionType != TYPE_BOOL) - throw SemanticError(CONDITION_MUST_BE_BOOL, "For loop condition must be of type bool"); + throw SemanticError(*ctx->assignment()[1]->start, CONDITION_MUST_BE_BOOL, + "For loop condition must be of type bool"); // Visit incrementer in new scope visit(ctx->assignment()[2]); // Visit statement list in new scope @@ -119,12 +121,6 @@ antlrcpp::Any AnalyzerVisitor::visitForLoop(SpiceParser::ForLoopContext* ctx) { return TYPE_BOOL; } -/*antlrcpp::Any AnalyzerVisitor::visitForeachLoop(SpiceParser::ForeachLoopContext* ctx) { - // Create a new scope - std::string scopeId = ScopeIdUtil::getScopeId(ctx); - currentScope = currentScope->createChildBlock(scopeId); -}*/ - antlrcpp::Any AnalyzerVisitor::visitWhileLoop(SpiceParser::WhileLoopContext* ctx) { // Create a new scope std::string scopeId = ScopeIdUtil::getScopeId(ctx); @@ -132,7 +128,8 @@ antlrcpp::Any AnalyzerVisitor::visitWhileLoop(SpiceParser::WhileLoopContext* ctx // Visit condition SymbolType conditionType = visit(ctx->assignment()).as(); if (conditionType != TYPE_BOOL) - throw SemanticError(CONDITION_MUST_BE_BOOL, "While loop condition must be of type bool"); + throw SemanticError(*ctx->assignment()->start, CONDITION_MUST_BE_BOOL, + "While loop condition must be of type bool"); // Visit statement list in new scope visit(ctx->stmtLst()); // Return to old scope @@ -147,7 +144,8 @@ antlrcpp::Any AnalyzerVisitor::visitIfStmt(SpiceParser::IfStmtContext* ctx) { // Visit condition SymbolType conditionType = visit(ctx->assignment()).as(); if (conditionType != TYPE_BOOL) - throw SemanticError(CONDITION_MUST_BE_BOOL, "If condition must be of type bool"); + throw SemanticError(*ctx->assignment()->start, CONDITION_MUST_BE_BOOL, + "If condition must be of type bool"); // Visit statement list in new scope visit(ctx->stmtLst()); // Return to old scope @@ -161,7 +159,8 @@ antlrcpp::Any AnalyzerVisitor::visitParamLstDef(SpiceParser::ParamLstDefContext SymbolType paramType = visit(param).as(); std::string paramName = param->IDENTIFIER()->toString(); if (paramType == TYPE_DYN) - throw SemanticError(FCT_PARAM_IS_TYPE_DYN, "Type of parameter '" + paramName + "' is invalid"); + throw SemanticError(*param->start, FCT_PARAM_IS_TYPE_DYN, + "Type of parameter '" + paramName + "' is invalid"); paramTypes.push_back(paramType); } for (auto& param : ctx->assignment()) { // Parameters with default value @@ -175,7 +174,7 @@ antlrcpp::Any AnalyzerVisitor::visitDeclStmt(SpiceParser::DeclStmtContext* ctx) std::string variableName = ctx->IDENTIFIER()->toString(); // Check if symbol already exists in the symbol table if (currentScope->lookup(variableName)) - throw SemanticError(VARIABLE_DECLARED_TWICE, + throw SemanticError(*ctx->start, VARIABLE_DECLARED_TWICE, "The variable '" + variableName + "' was declared more than once"); // Insert variable name to symbol table SymbolType type = getSymbolTypeFromDataType(ctx->dataType()); @@ -195,7 +194,7 @@ antlrcpp::Any AnalyzerVisitor::visitFunctionCall(SpiceParser::FunctionCallContex FunctionSignature signature = FunctionSignature(functionName, paramTypes); SymbolTableEntry* entry = currentScope->lookup(signature.toString()); if (!entry) - throw SemanticError(REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE, + throw SemanticError(*ctx->start, REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE, "Function/Procedure '" + signature.toString() + "' could not be found"); // Add function call to the signature queue of the current scope currentScope->pushSignature(signature); @@ -204,7 +203,7 @@ antlrcpp::Any AnalyzerVisitor::visitFunctionCall(SpiceParser::FunctionCallContex SymbolTable* symbolTable = currentScope; while (!symbolTable->hasChild(signature.toString())) { if (!symbolTable->getParent()) - throw SemanticError(REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE, + throw SemanticError(*ctx->start, REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE, "Could not find child symbol table for function/procedure '" + signature.toString() +"'"); symbolTable = symbolTable->getParent(); } @@ -227,7 +226,8 @@ antlrcpp::Any AnalyzerVisitor::visitReturnStmt(SpiceParser::ReturnStmtContext* c // Check if return variable is in the symbol table SymbolTableEntry* returnVariable = currentScope->lookup(RETURN_VARIABLE_NAME); if (!returnVariable) - throw SemanticError(RETURN_STMT_WITHOUT_FUNCTION, "Cannot assign return statement to a function"); + throw SemanticError(*ctx->start, RETURN_STMT_WITHOUT_FUNCTION, + "Cannot assign return statement to a function"); // Check data type of return statement if (returnVariable->getType() == TYPE_DYN) { // Set explicit return type to the return variable @@ -235,7 +235,8 @@ antlrcpp::Any AnalyzerVisitor::visitReturnStmt(SpiceParser::ReturnStmtContext* c } else { // Check if return type matches with function definition if (returnType != returnVariable->getType()) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, "Passed wrong data type to return statement"); + throw SemanticError(*ctx->assignment()->start, OPERATOR_WRONG_DATA_TYPE, + "Passed wrong data type to return statement"); } // Set the return variable to initialized returnVariable->updateState(INITIALIZED); @@ -246,7 +247,8 @@ antlrcpp::Any AnalyzerVisitor::visitBreakStmt(SpiceParser::BreakStmtContext* ctx if (ctx->INTEGER()) { int breakCount = std::stoi(ctx->INTEGER()->toString()); if (breakCount < 1) - throw SemanticError(INVALID_BREAK_NUMBER, "Break count was: " + ctx->INTEGER()->toString()); + throw SemanticError(*ctx->INTEGER()->getSymbol(), INVALID_BREAK_NUMBER, + "Break count was: " + ctx->INTEGER()->toString()); } return TYPE_INT; } @@ -264,34 +266,42 @@ antlrcpp::Any AnalyzerVisitor::visitPrintfStmt(SpiceParser::PrintfStmtContext* c int placeholderCount = 0; while (index != std::string::npos) { switch (templateString[index + 1]) { + case 'c': case 'd': case 'i': case 'o': case 'x': - case 'X': - case 'c': { - SymbolType assignmentType = visit(ctx->assignment()[placeholderCount]).as(); + case 'X': { + auto assignment = ctx->assignment()[placeholderCount]; + SymbolType assignmentType = visit(assignment).as(); if (assignmentType != TYPE_INT && assignmentType != TYPE_BOOL) - throw SemanticError(PRINTF_TYPE_ERROR, "Template string expects an int or a bool here"); + throw SemanticError(*assignment->start, PRINTF_TYPE_ERROR, + "Template string expects an int or a bool here"); placeholderCount++; break; } + case 'a': + case 'A': case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': { - SymbolType assignmentType = visit(ctx->assignment()[placeholderCount]).as(); + auto assignment = ctx->assignment()[placeholderCount]; + SymbolType assignmentType = visit(assignment).as(); if (assignmentType != TYPE_DOUBLE) - throw SemanticError(PRINTF_TYPE_ERROR, "Template string expects a double here"); + throw SemanticError(*assignment->start, PRINTF_TYPE_ERROR, + "Template string expects a double here"); placeholderCount++; break; } case 's': { - SymbolType assignmentType = visit(ctx->assignment()[placeholderCount]).as(); + auto assignment = ctx->assignment()[placeholderCount]; + SymbolType assignmentType = visit(assignment).as(); if (assignmentType != TYPE_STRING) - throw SemanticError(PRINTF_TYPE_ERROR, "Template string expects a string here"); + throw SemanticError(*assignment->start, PRINTF_TYPE_ERROR, + "Template string expects a string here"); placeholderCount++; break; } @@ -307,19 +317,22 @@ antlrcpp::Any AnalyzerVisitor::visitAssignment(SpiceParser::AssignmentContext* c if (ctx->children.size() > 1) { std::string variableName; SymbolType leftType; + antlr4::Token* token; // Take a look on the left side if (ctx->declStmt()) { // Variable was declared in this line visit(ctx->declStmt()); variableName = ctx->declStmt()->IDENTIFIER()->toString(); + token = ctx->declStmt()->IDENTIFIER()->getSymbol(); } else { // Variable was declared before and is referenced here variableName = ctx->IDENTIFIER()[0].toString(); + token = ctx->IDENTIFIER()[0].getSymbol(); } // Retrieve the left type from the symbol table SymbolTableEntry* symbolTableEntry = currentScope->lookup(variableName); if (symbolTableEntry == nullptr) - throw SemanticError(REFERENCED_UNDEFINED_VARIABLE, "Variable " + variableName + - " was referenced before declared."); + throw SemanticError(*token, REFERENCED_UNDEFINED_VARIABLE, + "Variable " + variableName + " was referenced before declared."); leftType = symbolTableEntry->getType(); // Take a look on the right side @@ -333,35 +346,33 @@ antlrcpp::Any AnalyzerVisitor::visitAssignment(SpiceParser::AssignmentContext* c // Take a look at the operator if (ctx->ASSIGN_OP()) { if (leftType != rightType) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->ASSIGN_OP()->getSymbol(), OPERATOR_WRONG_DATA_TYPE, "Cannot apply the assign operator to different data types"); } else if (ctx->PLUS_EQUAL()) { if (!(leftType == TYPE_DOUBLE && rightType == TYPE_DOUBLE) && !(leftType == TYPE_INT && rightType == TYPE_INT) && !(leftType == TYPE_STRING && rightType == TYPE_STRING)) { - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->PLUS_EQUAL()->getSymbol(), OPERATOR_WRONG_DATA_TYPE, "Cannot apply += operator on this combination of types"); } } else if (ctx->MINUS_EQUAL()) { if (!(leftType == TYPE_DOUBLE && rightType == TYPE_DOUBLE) && !(leftType == TYPE_INT && rightType == TYPE_INT)) { - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->MINUS_EQUAL()->getSymbol(), OPERATOR_WRONG_DATA_TYPE, "Cannot apply -= operator on this combination of types"); } } else if (ctx->MUL_EQUAL()) { if (!(leftType == TYPE_DOUBLE && rightType == TYPE_DOUBLE) && !(leftType == TYPE_INT && rightType == TYPE_INT)) { - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->MUL_EQUAL()->getSymbol(), OPERATOR_WRONG_DATA_TYPE, "Cannot apply *= operator on this combination of types"); } } else if (ctx->DIV_EQUAL()) { if (!(leftType == TYPE_DOUBLE && rightType == TYPE_DOUBLE) && !(leftType == TYPE_INT && rightType == TYPE_INT)) { - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->DIV_EQUAL()->getSymbol(), OPERATOR_WRONG_DATA_TYPE, "Cannot apply /= operator on this combination of types"); } - } else { - throw std::runtime_error("Internal compiler error. Fell through all assign ops"); } // Update variable in symbol table symbolTableEntry->updateState(INITIALIZED); @@ -373,15 +384,17 @@ antlrcpp::Any AnalyzerVisitor::visitAssignment(SpiceParser::AssignmentContext* c antlrcpp::Any AnalyzerVisitor::visitTernary(SpiceParser::TernaryContext* ctx) { // Check if there is a ternary operator applied if (ctx->children.size() > 1) { - SymbolType conditionType = visit(ctx->logicalOrExpr()[0]).as(); + auto condition = ctx->logicalOrExpr()[0]; + SymbolType conditionType = visit(condition).as(); SymbolType trueType = visit(ctx->logicalOrExpr()[1]).as(); SymbolType falseType = visit(ctx->logicalOrExpr()[2]).as(); // Check if the condition evaluates to boolean if (conditionType != TYPE_BOOL) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, "Condition operand in ternary must be a bool"); + throw SemanticError(*condition->start, OPERATOR_WRONG_DATA_TYPE, + "Condition operand in ternary must be a bool"); // Check if trueType and falseType are matching if (trueType != falseType) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, "True operand and false operand in ternary must be from same data type"); return trueType; } @@ -399,7 +412,8 @@ antlrcpp::Any AnalyzerVisitor::visitLogicalOrExpr(SpiceParser::LogicalOrExprCont continue; } // Every other combination is invalid - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, "Can only apply logical or to booleans"); + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, + "Can only apply logical or to booleans"); } return leftType; } @@ -417,7 +431,8 @@ antlrcpp::Any AnalyzerVisitor::visitLogicalAndExpr(SpiceParser::LogicalAndExprCo continue; } // Every other combination is invalid - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, "Can only apply logical and to booleans"); + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, + "Can only apply logical and to booleans"); } return leftType; } @@ -439,7 +454,7 @@ antlrcpp::Any AnalyzerVisitor::visitBitwiseOrExpr(SpiceParser::BitwiseOrExprCont continue; } // Every other combination is invalid - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, "Can only apply bitwise or to booleans and integers"); } return leftType; @@ -462,7 +477,7 @@ antlrcpp::Any AnalyzerVisitor::visitBitwiseAndExpr(SpiceParser::BitwiseAndExprCo continue; } // Every other combination is invalid - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, "Can only apply bitwise and to booleans and integers"); } return leftType; @@ -482,7 +497,7 @@ antlrcpp::Any AnalyzerVisitor::visitEqualityExpr(SpiceParser::EqualityExprContex if (leftType == TYPE_STRING && rightType == TYPE_STRING) return TYPE_BOOL; // Can compare string with string if (leftType == TYPE_BOOL && rightType == TYPE_BOOL) return TYPE_BOOL; // Can compare bool with bool // Every other combination is invalid - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, "Can only compare some type combinations with an equality operator"); } return visit(ctx->relationalExpr()[0]); @@ -498,7 +513,7 @@ antlrcpp::Any AnalyzerVisitor::visitRelationalExpr(SpiceParser::RelationalExprCo if (leftType == TYPE_INT && rightType == TYPE_DOUBLE) return TYPE_BOOL; // Can compare int with double if (leftType == TYPE_INT && rightType == TYPE_INT) return TYPE_BOOL; // Can compare int with int // Every other combination is invalid - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->start, OPERATOR_WRONG_DATA_TYPE, "Can only compare doubles or ints with one another with a relational operator"); } return visit(ctx->additiveExpr()[0]); @@ -510,7 +525,8 @@ antlrcpp::Any AnalyzerVisitor::visitAdditiveExpr(SpiceParser::AdditiveExprContex antlrcpp::Any currentType = visit(ctx->multiplicativeExpr()[0]); // Check if data types are compatible for (int i = 1; i < ctx->multiplicativeExpr().size(); i++) { - antlrcpp::Any nextType = visit(ctx->multiplicativeExpr()[i]); + auto next = ctx->multiplicativeExpr()[i]; + antlrcpp::Any nextType = visit(next); // Check all combinations if (currentType.as() == TYPE_DOUBLE) { if (nextType.as() == TYPE_DOUBLE) { // e.g.: 4.3 + 6.1 @@ -520,7 +536,7 @@ antlrcpp::Any AnalyzerVisitor::visitAdditiveExpr(SpiceParser::AdditiveExprContex } else if (nextType.as() == TYPE_STRING) { // e.g.: 4.3 + "Test" currentType = TYPE_STRING; } else if (nextType.as() == TYPE_BOOL) { // e.g.: 4.3 + true - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands double and bool for additive operator"); } } else if (currentType.as() == TYPE_INT) { @@ -531,7 +547,7 @@ antlrcpp::Any AnalyzerVisitor::visitAdditiveExpr(SpiceParser::AdditiveExprContex } else if (nextType.as() == TYPE_STRING) { // e.g.: 4 + "Test" currentType = TYPE_STRING; } else if (nextType.as() == TYPE_BOOL) { // e.g.: 4 + true - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands int and bool for additive operator"); } } else if (currentType.as() == TYPE_STRING) { @@ -542,21 +558,21 @@ antlrcpp::Any AnalyzerVisitor::visitAdditiveExpr(SpiceParser::AdditiveExprContex } else if (nextType.as() == TYPE_STRING) { // e.g.: "Test" + "Test" currentType = TYPE_STRING; } else if (nextType.as() == TYPE_BOOL) { // e.g.: "Test" + true - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands string and bool for additive operator"); } } else if (currentType.as() == TYPE_BOOL) { if (nextType.as() == TYPE_DOUBLE) { // e.g.: true + 6.1 - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands bool and double for additive operator"); } else if (nextType.as() == TYPE_INT) { // e.g.: true + 5 - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands bool and int for additive operator"); } else if (nextType.as() == TYPE_STRING) { // e.g.: true + "Test" - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands string and string for additive operator"); } else if (nextType.as() == TYPE_BOOL) { // e.g.: true + false - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands bool and bool for additive operator"); } } @@ -572,7 +588,8 @@ antlrcpp::Any AnalyzerVisitor::visitMultiplicativeExpr(SpiceParser::Multiplicati antlrcpp::Any currentType = visit(ctx->prefixUnary()[0]); // Check if data types are compatible for (int i = 1; i < ctx->prefixUnary().size(); i++) { - antlrcpp::Any nextType = visit(ctx->prefixUnary()[i]); + auto next = ctx->prefixUnary()[i]; + antlrcpp::Any nextType = visit(next); // Check all combinations if (currentType.as() == TYPE_DOUBLE) { if (nextType.as() == TYPE_DOUBLE) { // e.g.: 4.3 * 6.1 @@ -580,10 +597,10 @@ antlrcpp::Any AnalyzerVisitor::visitMultiplicativeExpr(SpiceParser::Multiplicati } else if (nextType.as() == TYPE_INT) { // e.g.: 4.3 * 4 currentType = TYPE_DOUBLE; } else if (nextType.as() == TYPE_STRING) { // e.g.: 4.3 * "Test" - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands double and string for multiplicative operator"); } else if (nextType.as() == TYPE_BOOL) { // e.g.: 4.3 * true - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands double and bool for multiplicative operator"); } } else if (currentType.as() == TYPE_INT) { @@ -594,34 +611,34 @@ antlrcpp::Any AnalyzerVisitor::visitMultiplicativeExpr(SpiceParser::Multiplicati } else if (nextType.as() == TYPE_STRING) { // e.g.: 4 * "Test" currentType = TYPE_STRING; } else if (nextType.as() == TYPE_BOOL) { // e.g.: 4 * true - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands int and bool for multiplicative operator"); } } else if (currentType.as() == TYPE_STRING) { if (nextType.as() == TYPE_DOUBLE) { // e.g.: "Test" * 6.1 - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands string and double for multiplicative operator"); } else if (nextType.as() == TYPE_INT) { // e.g.: "Test" * 5 currentType = TYPE_STRING; } else if (nextType.as() == TYPE_STRING) { // e.g.: "Test" * "Test" - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands string and string for multiplicative operator"); } else if (nextType.as() == TYPE_BOOL) { // e.g.: "Test" * true - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands string and bool for multiplicative operator"); } } else if (currentType.as() == TYPE_BOOL) { if (nextType.as() == TYPE_DOUBLE) { // e.g.: true * 6.1 - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands bool and double for multiplicative operator"); } else if (nextType.as() == TYPE_INT) { // e.g.: true * 5 - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands bool and int for multiplicative operator"); } else if (nextType.as() == TYPE_STRING) { // e.g.: true * "Test" - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands string and string for multiplicative operator"); } else if (nextType.as() == TYPE_BOOL) { // e.g.: true * false - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*next->start, OPERATOR_WRONG_DATA_TYPE, "Incompatible operands bool and bool for multiplicative operator"); } } @@ -637,7 +654,7 @@ antlrcpp::Any AnalyzerVisitor::visitPrefixUnary(SpiceParser::PrefixUnaryContext* // Ensure integer when '++' or '--' is applied if ((ctx->PLUS_PLUS() || ctx->MINUS_MINUS()) && (prefixUnary.as() != TYPE_INT || !ctx->postfixUnary()->atomicExpr()->value()->IDENTIFIER())) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->postfixUnary()->start, OPERATOR_WRONG_DATA_TYPE, "Prefix '++' or '--' only can be applied to an identifier of type integer"); // Ensure right return type if not is applied @@ -658,18 +675,12 @@ antlrcpp::Any AnalyzerVisitor::visitPostfixUnary(SpiceParser::PostfixUnaryContex // Ensure integer when '++' or '--' is applied if ((ctx->PLUS_PLUS() || ctx->MINUS_MINUS()) && (atomicExpr.as() != TYPE_INT || !ctx->atomicExpr()->value()->IDENTIFIER())) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, + throw SemanticError(*ctx->atomicExpr()->start, OPERATOR_WRONG_DATA_TYPE, "Postfix '++' or '--' only can be applied to an identifier of type integer"); return atomicExpr; } -antlrcpp::Any AnalyzerVisitor::visitAtomicExpr(SpiceParser::AtomicExprContext* ctx) { - if (ctx->LPAREN()) // Rule: '(' assignment ')' - return visit(ctx->assignment()); - return visit(ctx->value()); // Rule: value -} - antlrcpp::Any AnalyzerVisitor::visitValue(SpiceParser::ValueContext* ctx) { if (ctx->DOUBLE()) return TYPE_DOUBLE; if (ctx->INTEGER()) return TYPE_INT; @@ -679,8 +690,8 @@ antlrcpp::Any AnalyzerVisitor::visitValue(SpiceParser::ValueContext* ctx) { std::string variableName = ctx->IDENTIFIER()->toString(); SymbolTableEntry* entry = currentScope->lookup(variableName); if (entry == nullptr) - throw SemanticError(REFERENCED_UNDEFINED_VARIABLE, "Variable " + variableName + - " was referenced before initialized"); + throw SemanticError(*ctx->IDENTIFIER()->getSymbol(), REFERENCED_UNDEFINED_VARIABLE, + "Variable " + variableName + " was referenced before initialized"); return entry->getType(); } return visit(ctx->functionCall()); diff --git a/compiler/src/analyzer/AnalyzerVisitor.h b/compiler/src/analyzer/AnalyzerVisitor.h index 9ec70d3ce..9f8827252 100644 --- a/compiler/src/analyzer/AnalyzerVisitor.h +++ b/compiler/src/analyzer/AnalyzerVisitor.h @@ -18,7 +18,6 @@ class AnalyzerVisitor : public SpiceBaseVisitor { antlrcpp::Any visitFunctionDef(SpiceParser::FunctionDefContext* ctx) override; antlrcpp::Any visitProcedureDef(SpiceParser::ProcedureDefContext* ctx) override; antlrcpp::Any visitForLoop(SpiceParser::ForLoopContext* ctx) override; - /*antlrcpp::Any visitForeachLoop(SpiceParser::ForeachLoopContext* ctx) override;*/ antlrcpp::Any visitWhileLoop(SpiceParser::WhileLoopContext* ctx) override; antlrcpp::Any visitIfStmt(SpiceParser::IfStmtContext* ctx) override; antlrcpp::Any visitParamLstDef(SpiceParser::ParamLstDefContext *ctx) override; @@ -41,7 +40,6 @@ class AnalyzerVisitor : public SpiceBaseVisitor { antlrcpp::Any visitMultiplicativeExpr(SpiceParser::MultiplicativeExprContext* ctx) override; antlrcpp::Any visitPrefixUnary(SpiceParser::PrefixUnaryContext* ctx) override; antlrcpp::Any visitPostfixUnary(SpiceParser::PostfixUnaryContext* ctx) override; - antlrcpp::Any visitAtomicExpr(SpiceParser::AtomicExprContext* ctx) override; antlrcpp::Any visitValue(SpiceParser::ValueContext* ctx) override; private: // Members diff --git a/compiler/src/analyzer/SymbolTableEntry.cpp b/compiler/src/analyzer/SymbolTableEntry.cpp index 8664b1cf0..1f2af83f6 100644 --- a/compiler/src/analyzer/SymbolTableEntry.cpp +++ b/compiler/src/analyzer/SymbolTableEntry.cpp @@ -14,8 +14,7 @@ void SymbolTableEntry::updateState(SymbolState newState) { if (state == INITIALIZED && isConstant) throw SemanticError(REASSIGN_CONST_VARIABLE, "Not re-assignable variable '" + name + "'"); if (newState == INITIALIZED && type == TYPE_DYN) - throw SemanticError(OPERATOR_WRONG_DATA_TYPE, - "Internal compiler error: could not determine type of variable '" + name + "'"); + throw std::runtime_error("Internal compiler error: could not determine type of variable '" + name + "'"); state = newState; } diff --git a/compiler/src/exception/SemanticError.cpp b/compiler/src/exception/SemanticError.cpp index 944d82cdc..f9c286409 100644 --- a/compiler/src/exception/SemanticError.cpp +++ b/compiler/src/exception/SemanticError.cpp @@ -2,6 +2,34 @@ #include "SemanticError.h" +SemanticError::SemanticError(const antlr4::Token& token, SemanticErrorType type, const std::string &message) { + auto codeLoc = std::to_string(token.getLine()) + ":" + std::to_string(token.getCharPositionInLine() + 1); + errorMessage = "Semantic error at " + codeLoc + ": " + getMessagePrefix(type) + ": " + message; +} + +SemanticError::SemanticError(SemanticErrorType type, const std::string &message) { + errorMessage = "Semantic error at - " + getMessagePrefix(type) + ": " + message; +} + const char *SemanticError::what() const noexcept { return errorMessage.c_str(); -} \ No newline at end of file +} + +std::string SemanticError::getMessagePrefix(SemanticErrorType type) { + std::string messagePrefix; + switch (type) { + case REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE: return "Referenced undefined function"; + case REFERENCED_UNDEFINED_VARIABLE: return "Referenced undefined variable"; + case VARIABLE_DECLARED_TWICE: return "Multiple declarations of the same variable"; + case FUNCTION_WITHOUT_RETURN_STMT: return "Missing return statement"; + case RETURN_STMT_WITHOUT_FUNCTION: return "Return statement outside function"; + case OPERATOR_WRONG_DATA_TYPE: return "Wrong data type for operator"; + case REASSIGN_CONST_VARIABLE: return "Cannot re-assign constant variable"; + case CONDITION_MUST_BE_BOOL: return "Condition must be bool"; + case MISSING_MAIN_FUNCTION: return "Spice programs must contain a main function"; + case FCT_PARAM_IS_TYPE_DYN: return "Parameter type dyn not valid in function/procedure definition without default value"; + case INVALID_BREAK_NUMBER: return "Invalid number of breaks"; + case PRINTF_TYPE_ERROR: return "Types of printf call not matching"; + } + return "Unknown error"; +} diff --git a/compiler/src/exception/SemanticError.h b/compiler/src/exception/SemanticError.h index d47a547e4..c05023b66 100644 --- a/compiler/src/exception/SemanticError.h +++ b/compiler/src/exception/SemanticError.h @@ -4,6 +4,8 @@ #include #include +#include +#include enum SemanticErrorType { REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE, @@ -23,52 +25,13 @@ enum SemanticErrorType { class SemanticError : public std::exception { public: // Constructors - explicit SemanticError(SemanticErrorType type, const std::string& message) { - std::string messagePrefix; - switch (type) { - case REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE: - messagePrefix = "Referenced undefined function"; - break; - case REFERENCED_UNDEFINED_VARIABLE: - messagePrefix = "Referenced undefined variable"; - break; - case VARIABLE_DECLARED_TWICE: - messagePrefix = "Multiple declarations of the same variable"; - break; - case FUNCTION_WITHOUT_RETURN_STMT: - messagePrefix = "Missing return statement"; - break; - case RETURN_STMT_WITHOUT_FUNCTION: - messagePrefix = "Return statement outside function"; - break; - case OPERATOR_WRONG_DATA_TYPE: - messagePrefix = "Wrong data type for operator"; - break; - case REASSIGN_CONST_VARIABLE: - messagePrefix = "Cannot re-assign constant variable"; - break; - case CONDITION_MUST_BE_BOOL: - messagePrefix = "Condition must be bool"; - break; - case MISSING_MAIN_FUNCTION: - messagePrefix = "Spice programs must contain a main function"; - break; - case FCT_PARAM_IS_TYPE_DYN: - messagePrefix = "Parameter type dyn not valid in function/procedure definition without default value"; - break; - case INVALID_BREAK_NUMBER: - messagePrefix = "Invalid number of breaks"; - break; - case PRINTF_TYPE_ERROR: - messagePrefix = "Types of printf call not matching"; - break; - } - errorMessage = messagePrefix + ": " + message; - } + explicit SemanticError(const antlr4::Token& token, SemanticErrorType type, const std::string& message); + explicit SemanticError(SemanticErrorType type, const std::string& message); // Public methods const char * what() const noexcept override; private: // Members std::string errorMessage {}; + static std::string getMessagePrefix(SemanticErrorType); }; diff --git a/media/test.spice b/media/test.spice index 083d7c77a..c6053ee4f 100644 --- a/media/test.spice +++ b/media/test.spice @@ -1,4 +1,4 @@ -f main() { +f main1() { int i = 0; while i < 10 { i += 1; From 6808ca89a08f463ca51a6e91ab3f0bb00edc747e Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 8 Aug 2021 15:57:40 +0200 Subject: [PATCH 4/9] Print code loc when throwing an IRError --- compiler/src/exception/IRError.cpp | 23 ++++++++++++++ compiler/src/exception/IRError.h | 34 +++------------------ compiler/src/exception/SemanticError.cpp | 1 - compiler/src/generator/GeneratorVisitor.cpp | 18 +++++------ 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/compiler/src/exception/IRError.cpp b/compiler/src/exception/IRError.cpp index 5674424e1..2ec1919ae 100644 --- a/compiler/src/exception/IRError.cpp +++ b/compiler/src/exception/IRError.cpp @@ -2,6 +2,29 @@ #include "IRError.h" +IRError::IRError(const antlr4::Token& token, IRErrorType type, const std::string& message) { + auto codeLoc = std::to_string(token.getLine()) + ":" + std::to_string(token.getCharPositionInLine() + 1); + errorMessage = "Internal compiler error at " + codeLoc + " - " + getMessagePrefix(type) + ": " + message; +} + +IRError::IRError(IRErrorType type, const std::string& message) { + errorMessage = "Internal compiler error - " + getMessagePrefix(type) + ": " + message; +} + const char *IRError::what() const noexcept { return errorMessage.c_str(); } + +std::string IRError::getMessagePrefix(IRErrorType type) { + switch (type) { + case TARGET_NOT_AVAILABLE: return "Selected target not available"; + case CANT_OPEN_OUTPUT_FILE: return "Could not open output file"; + case WRONG_TYPE: return "Wrong type of output file"; + case UNEXPECTED_DYN_TYPE: return "Unexpected type of dyn. Symbol table incomplete"; + case PRINTF_NULL_TYPE: return "Printf has null type"; + case VARIABLE_NOT_FOUND: return "Variable not found"; + case INVALID_FUNCTION: return "Invalid function"; + case INVALID_MODULE: return "Invalid module"; + } + return "Unknown error"; +} diff --git a/compiler/src/exception/IRError.h b/compiler/src/exception/IRError.h index 59e611cda..cbdaf5106 100644 --- a/compiler/src/exception/IRError.h +++ b/compiler/src/exception/IRError.h @@ -4,6 +4,7 @@ #include #include +#include enum IRErrorType { TARGET_NOT_AVAILABLE, @@ -19,40 +20,13 @@ enum IRErrorType { class IRError : public std::exception { public: // Constructors - explicit IRError(IRErrorType type, const std::string& message) { - std::string messagePrefix; - switch (type) { - case TARGET_NOT_AVAILABLE: - messagePrefix = "Selected target not available"; - break; - case CANT_OPEN_OUTPUT_FILE: - messagePrefix = "Could not open output file"; - break; - case WRONG_TYPE: - messagePrefix = "Wrong type of output file"; - break; - case UNEXPECTED_DYN_TYPE: - messagePrefix = "Unexpected type of dyn. Symbol table incomplete"; - break; - case PRINTF_NULL_TYPE: - messagePrefix = "Printf has null type"; - break; - case VARIABLE_NOT_FOUND: - messagePrefix = "Variable not found"; - break; - case INVALID_FUNCTION: - messagePrefix = "Invalid function"; - break; - case INVALID_MODULE: - messagePrefix = "Invalid module"; - break; - } - errorMessage = "Internal compiler error - " + messagePrefix + ": " + message; - } + explicit IRError(const antlr4::Token&, IRErrorType, const std::string&); + explicit IRError(IRErrorType, const std::string&); // Public methods const char * what() const noexcept override; private: // Members std::string errorMessage {}; + static std::string getMessagePrefix(IRErrorType); }; \ No newline at end of file diff --git a/compiler/src/exception/SemanticError.cpp b/compiler/src/exception/SemanticError.cpp index f9c286409..125965050 100644 --- a/compiler/src/exception/SemanticError.cpp +++ b/compiler/src/exception/SemanticError.cpp @@ -16,7 +16,6 @@ const char *SemanticError::what() const noexcept { } std::string SemanticError::getMessagePrefix(SemanticErrorType type) { - std::string messagePrefix; switch (type) { case REFERENCED_UNDEFINED_FUNCTION_OR_PROCEDURE: return "Referenced undefined function"; case REFERENCED_UNDEFINED_VARIABLE: return "Referenced undefined variable"; diff --git a/compiler/src/generator/GeneratorVisitor.cpp b/compiler/src/generator/GeneratorVisitor.cpp index 6fed3c466..91863cc2f 100644 --- a/compiler/src/generator/GeneratorVisitor.cpp +++ b/compiler/src/generator/GeneratorVisitor.cpp @@ -119,7 +119,7 @@ antlrcpp::Any GeneratorVisitor::visitEntry(SpiceParser::EntryContext* ctx) { // Verify module to detect IR code bugs std::string output; llvm::raw_string_ostream oss(output); - if (llvm::verifyModule(*module, &oss)) throw IRError(INVALID_MODULE, oss.str()); + if (llvm::verifyModule(*module, &oss)) throw IRError(*ctx->start, INVALID_MODULE, oss.str()); return result; } @@ -156,7 +156,7 @@ antlrcpp::Any GeneratorVisitor::visitMainFunctionDef(SpiceParser::MainFunctionDe // Verify function std::string output; llvm::raw_string_ostream oss(output); - if (llvm::verifyFunction(*fct, &oss)) throw IRError(INVALID_FUNCTION, oss.str()); + if (llvm::verifyFunction(*fct, &oss)) throw IRError(*ctx->start, INVALID_FUNCTION, oss.str()); // Add function to function list functions.push_back(fct); @@ -234,7 +234,7 @@ antlrcpp::Any GeneratorVisitor::visitFunctionDef(SpiceParser::FunctionDefContext // Verify function std::string output; llvm::raw_string_ostream oss(output); - if (llvm::verifyFunction(*fct, &oss)) throw IRError(INVALID_FUNCTION, oss.str()); + if (llvm::verifyFunction(*fct, &oss)) throw IRError(*ctx->start, INVALID_FUNCTION, oss.str()); // Add function to function list functions.push_back(fct); @@ -302,7 +302,7 @@ antlrcpp::Any GeneratorVisitor::visitProcedureDef(SpiceParser::ProcedureDefConte // Verify procedure std::string output; llvm::raw_string_ostream oss(output); - if (llvm::verifyFunction(*proc, &oss)) throw IRError(INVALID_FUNCTION, oss.str()); + if (llvm::verifyFunction(*proc, &oss)) throw IRError(*ctx->start, INVALID_FUNCTION, oss.str()); // Add function to function list functions.push_back(proc); @@ -481,7 +481,7 @@ antlrcpp::Any GeneratorVisitor::visitPrintfStmt(SpiceParser::PrintfStmtContext* printfArgs.push_back(builder->CreateGlobalStringPtr(stringTemplate)); for (auto &arg : ctx->assignment()) { auto argVal = visit(arg).as(); - if (argVal == nullptr) throw IRError(PRINTF_NULL_TYPE, "'" + arg->getText() + "' is null"); + if (argVal == nullptr) throw IRError(*arg->start, PRINTF_NULL_TYPE, "'" + arg->getText() + "' is null"); printfArgs.push_back(argVal); } return builder->CreateCall(printf, printfArgs); @@ -790,8 +790,8 @@ antlrcpp::Any GeneratorVisitor::visitValue(SpiceParser::ValueContext* ctx) { std::string variableName = ctx->IDENTIFIER()->toString(); currentSymbolType = currentScope->lookup(variableName)->getType(); llvm::Value* var = namedValues[variableName]; - if (!var) throw IRError(VARIABLE_NOT_FOUND, "Internal compiler error - Variable '" + variableName + - "' not found in code generation step"); + if (!var) throw IRError(*ctx->IDENTIFIER()->getSymbol(), VARIABLE_NOT_FOUND, + "Internal compiler error - Variable '" + variableName + "' not found in code generation step"); return (llvm::Value*) builder->CreateLoad(var->getType()->getPointerElementType(), var); } @@ -833,8 +833,8 @@ antlrcpp::Any GeneratorVisitor::visitDataType(SpiceParser::DataTypeContext* ctx) case TYPE_INT: return (llvm::Type*) llvm::Type::getInt32Ty(*context); case TYPE_STRING: return (llvm::Type*) llvm::Type::getInt8Ty(*context)->getPointerTo(); case TYPE_BOOL: return (llvm::Type*) llvm::Type::getInt1Ty(*context); - default: throw IRError(UNEXPECTED_DYN_TYPE, "Dyn was " + - std::to_string(symbolTableEntry->getType())); + default: throw IRError(*ctx->TYPE_DYN()->getSymbol(), UNEXPECTED_DYN_TYPE, + "Dyn was " + std::to_string(symbolTableEntry->getType())); } } From 07f8a653c1a49cb841d67b78502e85a0ec4cf681 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 8 Aug 2021 16:21:39 +0200 Subject: [PATCH 5/9] Fix failing tests --- compiler/src/exception/SemanticError.cpp | 2 +- compiler/test/AnalyzerTest.cpp | 58 ++++++++++--------- .../source.spice | 5 ++ .../generator/success-while-loop/ir-code.ll | 2 +- .../generator/success-while-loop/source.spice | 2 +- 5 files changed, 39 insertions(+), 30 deletions(-) create mode 100644 compiler/test/test-files/analyzer/error-printf-type-incompatibility/source.spice diff --git a/compiler/src/exception/SemanticError.cpp b/compiler/src/exception/SemanticError.cpp index 125965050..438aab301 100644 --- a/compiler/src/exception/SemanticError.cpp +++ b/compiler/src/exception/SemanticError.cpp @@ -8,7 +8,7 @@ SemanticError::SemanticError(const antlr4::Token& token, SemanticErrorType type, } SemanticError::SemanticError(SemanticErrorType type, const std::string &message) { - errorMessage = "Semantic error at - " + getMessagePrefix(type) + ": " + message; + errorMessage = "Semantic error - " + getMessagePrefix(type) + ": " + message; } const char *SemanticError::what() const noexcept { diff --git a/compiler/test/AnalyzerTest.cpp b/compiler/test/AnalyzerTest.cpp index b91c3c9ef..e22fcee05 100644 --- a/compiler/test/AnalyzerTest.cpp +++ b/compiler/test/AnalyzerTest.cpp @@ -17,110 +17,114 @@ const AnalyzerParams ANALYZER_TEST_PARAMETERS[] = { // Failing tests { // 0 "error-assignment-same-type", - "Wrong data type for operator: Cannot apply the assign operator to different data types" + "Semantic error at 2:14: Wrong data type for operator: Cannot apply the assign operator to different data types" }, { // 1 "error-if-condition-bool", - "Condition must be bool: If condition must be of type bool" + "Semantic error at 2:8: Condition must be bool: If condition must be of type bool" }, { // 2 "error-while-condition-bool", - "Condition must be bool: While loop condition must be of type bool" + "Semantic error at 2:11: Condition must be bool: While loop condition must be of type bool" }, { // 3 "error-for-condition-bool", - "Condition must be bool: For loop condition must be of type bool" + "Semantic error at 2:20: Condition must be bool: For loop condition must be of type bool" }, { // 4 "error-ternary-condition-bool", - "Wrong data type for operator: Condition operand in ternary must be a bool" + "Semantic error at 2:25: Wrong data type for operator: Condition operand in ternary must be a bool" }, { // 5 "error-ternary-types-match", - "Wrong data type for operator: True operand and false operand in ternary must be from same data type" + "Semantic error at 2:25: Wrong data type for operator: True operand and false operand in ternary must be from same data type" }, { // 6 "error-variable-declared-before-referenced", - "Referenced undefined variable: Variable test was referenced before initialized" + "Semantic error at 2:26: Referenced undefined variable: Variable test was referenced before initialized" }, { // 7 "error-variable-declared-only-once", - "Multiple declarations of the same variable: The variable 'i' was declared more than once" + "Semantic error at 3:5: Multiple declarations of the same variable: The variable 'i' was declared more than once" }, { // 8 "error-variable-const-not-modified", - "Cannot re-assign constant variable: Not re-assignable variable 'variable'" + "Semantic error - Cannot re-assign constant variable: Not re-assignable variable 'variable'" }, { // 9 "error-functions-defined-before-called", - "Referenced undefined function: Function/Procedure 'testFunction(int)' could not be found" + "Semantic error at 7:25: Referenced undefined function: Function/Procedure 'testFunction(int)' could not be found" }, { // 10 "error-cannot-call-variables", - "Referenced undefined function: Function/Procedure 'variable()' could not be found" + "Semantic error at 3:22: Referenced undefined function: Function/Procedure 'variable()' could not be found" }, { // 11 "error-function-params-match-declaration", - "Referenced undefined function: Function/Procedure 'testFunction()' could not be found" + "Semantic error at 6:5: Referenced undefined function: Function/Procedure 'testFunction()' could not be found" }, { // 12 "error-function-param-types-match-declaration", - "Referenced undefined function: Function/Procedure 'testFunction(string)' could not be found" + "Semantic error at 6:5: Referenced undefined function: Function/Procedure 'testFunction(string)' could not be found" }, { // 13 "error-return-only-inside-function", - "Return statement outside function: Cannot assign return statement to a function" + "Semantic error at 1:1: Return statement outside function: Cannot assign return statement to a function" }, { // 14 "error-return-type-matches-def", - "Wrong data type for operator: Passed wrong data type to return statement" + "Semantic error at 2:12: Wrong data type for operator: Passed wrong data type to return statement" }, { // 15 "error-logical-operators-are-booleans", - "Wrong data type for operator: Can only apply logical or to booleans" + "Semantic error at 2:9: Wrong data type for operator: Can only apply logical or to booleans" }, { // 16 "error-bitwise-operators-are-booleans-or-integers", - "Wrong data type for operator: Can only apply bitwise or to booleans and integers" + "Semantic error at 2:22: Wrong data type for operator: Can only apply bitwise or to booleans and integers" }, { // 17 "error-equality-operators-some-combinations", - "Wrong data type for operator: Can only compare some type combinations with an equality operator" + "Semantic error at 2:9: Wrong data type for operator: Can only compare some type combinations with an equality operator" }, { // 18 "error-relational-operators-are-doubles-or-integers", - "Wrong data type for operator: Can only compare doubles or ints with one another with a relational operator" + "Semantic error at 3:12: Wrong data type for operator: Can only compare doubles or ints with one another with a relational operator" }, { // 19 "error-additive-operators-some-combinations", - "Wrong data type for operator: Incompatible operands string and string for additive operator" + "Semantic error at 2:29: Wrong data type for operator: Incompatible operands string and string for additive operator" }, { // 20 "error-multiplicative-operators-some-combinations", - "Wrong data type for operator: Incompatible operands string and string for multiplicative operator" + "Semantic error at 2:29: Wrong data type for operator: Incompatible operands string and string for multiplicative operator" }, { // 21 "error-prefix-unary-only-integer-identifiers", - "Wrong data type for operator: Prefix '++' or '--' only can be applied to an identifier of type integer" + "Semantic error at 2:7: Wrong data type for operator: Prefix '++' or '--' only can be applied to an identifier of type integer" }, { // 22 "error-postfix-unary-only-integer-identifiers", - "Wrong data type for operator: Postfix '++' or '--' only can be applied to an identifier of type integer" + "Semantic error at 2:5: Wrong data type for operator: Postfix '++' or '--' only can be applied to an identifier of type integer" }, { // 23 "error-must-contain-main-function", - "Spice programs must contain a main function: No main function found." + "Semantic error at 1:1: Spice programs must contain a main function: No main function found." }, { // 24 "error-function-arg-decl-type-dyn", - "Parameter type dyn not valid in function/procedure definition without default value: Type of parameter 'arg2' is invalid" + "Semantic error at 1:51: Parameter type dyn not valid in function/procedure definition without default value: Type of parameter 'arg2' is invalid" }, { // 25 "error-dyn-return-types-not-matching", - "Wrong data type for operator: Passed wrong data type to return statement" + "Semantic error at 3:12: Wrong data type for operator: Passed wrong data type to return statement" + }, // 26 + { + "error-printf-type-incompatibility", + "Semantic error at 4:36: Types of printf call not matching: Template string expects an int or a bool here" }, // Successful tests - { // 26 + { // 27 "success-fibonacci", "" } diff --git a/compiler/test/test-files/analyzer/error-printf-type-incompatibility/source.spice b/compiler/test/test-files/analyzer/error-printf-type-incompatibility/source.spice new file mode 100644 index 000000000..9603929bb --- /dev/null +++ b/compiler/test/test-files/analyzer/error-printf-type-incompatibility/source.spice @@ -0,0 +1,5 @@ +f main() { + printf("This is a double: %f", 5.6); + printf("This is an int: %d", 5); + printf("This is a string: %c", "test"); +} \ No newline at end of file diff --git a/compiler/test/test-files/generator/success-while-loop/ir-code.ll b/compiler/test/test-files/generator/success-while-loop/ir-code.ll index f6be7b8e1..48ff07886 100644 --- a/compiler/test/test-files/generator/success-while-loop/ir-code.ll +++ b/compiler/test/test-files/generator/success-while-loop/ir-code.ll @@ -1,7 +1,7 @@ ; ModuleID = 'Module' source_filename = "Module" -@0 = private unnamed_addr constant [16 x i8] c"i is now at: %f\00", align 1 +@0 = private unnamed_addr constant [16 x i8] c"i is now at: %d\00", align 1 declare i32 @printf(i8*, ...) diff --git a/compiler/test/test-files/generator/success-while-loop/source.spice b/compiler/test/test-files/generator/success-while-loop/source.spice index 9984ec9e0..58add9b7f 100644 --- a/compiler/test/test-files/generator/success-while-loop/source.spice +++ b/compiler/test/test-files/generator/success-while-loop/source.spice @@ -2,6 +2,6 @@ f main() { int i = 0; while i < 10 { i += 1; - printf("i is now at: %f", i); + printf("i is now at: %d", i); } } \ No newline at end of file From dd86328da52f9a44548b7d8abdc3aa6efb070c3d Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Sun, 8 Aug 2021 23:30:29 +0200 Subject: [PATCH 6/9] Add break and continue support to generator --- compiler/.run/Spice_run.run.xml | 2 +- compiler/src/analyzer/AnalyzerVisitor.cpp | 28 +++++++- compiler/src/analyzer/AnalyzerVisitor.h | 1 + compiler/src/analyzer/SymbolTable.cpp | 34 +++++++--- compiler/src/analyzer/SymbolTable.h | 7 ++ compiler/src/exception/SemanticError.cpp | 3 +- compiler/src/exception/SemanticError.h | 1 + compiler/src/generator/GeneratorVisitor.cpp | 74 ++++++++++++++++++--- compiler/src/generator/GeneratorVisitor.h | 4 +- compiler/src/grammar/Spice.g4 | 2 +- media/test.spice | 6 +- 11 files changed, 134 insertions(+), 28 deletions(-) diff --git a/compiler/.run/Spice_run.run.xml b/compiler/.run/Spice_run.run.xml index b346b8ef6..3f09787fd 100644 --- a/compiler/.run/Spice_run.run.xml +++ b/compiler/.run/Spice_run.run.xml @@ -1,5 +1,5 @@ - + diff --git a/compiler/src/analyzer/AnalyzerVisitor.cpp b/compiler/src/analyzer/AnalyzerVisitor.cpp index ecd9794ac..6cbceff22 100644 --- a/compiler/src/analyzer/AnalyzerVisitor.cpp +++ b/compiler/src/analyzer/AnalyzerVisitor.cpp @@ -115,7 +115,9 @@ antlrcpp::Any AnalyzerVisitor::visitForLoop(SpiceParser::ForLoopContext* ctx) { // Visit incrementer in new scope visit(ctx->assignment()[2]); // Visit statement list in new scope + nestedLoopCounter++; visit(ctx->stmtLst()); + nestedLoopCounter--; // Return to old scope currentScope = currentScope->getParent(); return TYPE_BOOL; @@ -131,7 +133,9 @@ antlrcpp::Any AnalyzerVisitor::visitWhileLoop(SpiceParser::WhileLoopContext* ctx throw SemanticError(*ctx->assignment()->start, CONDITION_MUST_BE_BOOL, "While loop condition must be of type bool"); // Visit statement list in new scope + nestedLoopCounter++; visit(ctx->stmtLst()); + nestedLoopCounter--; // Return to old scope currentScope = currentScope->getParent(); return TYPE_BOOL; @@ -244,17 +248,35 @@ antlrcpp::Any AnalyzerVisitor::visitReturnStmt(SpiceParser::ReturnStmtContext* c } antlrcpp::Any AnalyzerVisitor::visitBreakStmt(SpiceParser::BreakStmtContext* ctx) { + int breakCount = 1; if (ctx->INTEGER()) { - int breakCount = std::stoi(ctx->INTEGER()->toString()); + // Check if the stated number is valid + breakCount = std::stoi(ctx->INTEGER()->toString()); if (breakCount < 1) throw SemanticError(*ctx->INTEGER()->getSymbol(), INVALID_BREAK_NUMBER, - "Break count was: " + ctx->INTEGER()->toString()); + "Break count must be >= 1: " + ctx->INTEGER()->toString()); } + // Check if we can break this often + if (breakCount > nestedLoopCounter) + throw SemanticError(*ctx->INTEGER()->getSymbol(), INVALID_BREAK_NUMBER, + "We only can break " + std::to_string(nestedLoopCounter) + " time(s) here"); return TYPE_INT; } antlrcpp::Any AnalyzerVisitor::visitContinueStmt(SpiceParser::ContinueStmtContext* ctx) { - return SpiceBaseVisitor::visitContinueStmt(ctx); + int continueCount = 1; + if (ctx->INTEGER()) { + // Check if the stated number is valid + continueCount = std::stoi(ctx->INTEGER()->toString()); + if (continueCount < 1) + throw SemanticError(*ctx->INTEGER()->getSymbol(), INVALID_CONTINUE_NUMBER, + "Continue count must be >= 1: " + ctx->INTEGER()->toString()); + } + // Check if we can continue this often + if (continueCount > nestedLoopCounter) + throw SemanticError(*ctx->INTEGER()->getSymbol(), INVALID_CONTINUE_NUMBER, + "We only can continue " + std::to_string(nestedLoopCounter) + " time(s) here"); + return TYPE_INT; } antlrcpp::Any AnalyzerVisitor::visitPrintfStmt(SpiceParser::PrintfStmtContext* ctx) { diff --git a/compiler/src/analyzer/AnalyzerVisitor.h b/compiler/src/analyzer/AnalyzerVisitor.h index 9f8827252..d26c59c19 100644 --- a/compiler/src/analyzer/AnalyzerVisitor.h +++ b/compiler/src/analyzer/AnalyzerVisitor.h @@ -46,6 +46,7 @@ class AnalyzerVisitor : public SpiceBaseVisitor { SymbolTable* currentScope = new SymbolTable(nullptr); bool parameterMode = false; bool hasMainFunction = false; + int nestedLoopCounter = 0; // Private functions static SymbolType getSymbolTypeFromDataType(SpiceParser::DataTypeContext*); diff --git a/compiler/src/analyzer/SymbolTable.cpp b/compiler/src/analyzer/SymbolTable.cpp index f122d906f..652b3440e 100644 --- a/compiler/src/analyzer/SymbolTable.cpp +++ b/compiler/src/analyzer/SymbolTable.cpp @@ -61,15 +61,6 @@ bool SymbolTable::hasChild(const std::string& scopeId) { return children.find(scopeId) != children.end(); } -std::string SymbolTable::toString() { - std::string symbolsString, childrenString; - for (auto& symbol : symbols) - symbolsString.append("(" + symbol.second.toString() + ")\n"); - for (auto& child : children) - childrenString.append(child.first + ": " + child.second.toString() + "\n"); - return "SymbolTable(\n" + symbolsString + ") {\n" + childrenString + "}"; -} - void SymbolTable::pushSignature(const FunctionSignature& signature) { functionSignatures.push(signature); } @@ -79,3 +70,28 @@ FunctionSignature SymbolTable::popSignature() { functionSignatures.pop(); return signature; } + +llvm::BasicBlock* SymbolTable::getContinueBlock() const { + return continueBlock; +} + +void SymbolTable::setContinueBlock(llvm::BasicBlock* block) { + continueBlock = block; +} + +llvm::BasicBlock* SymbolTable::getBreakBlock() const { + return breakBlock; +} + +void SymbolTable::setBreakBlock(llvm::BasicBlock* block) { + breakBlock = block; +} + +std::string SymbolTable::toString() { + std::string symbolsString, childrenString; + for (auto& symbol : symbols) + symbolsString.append("(" + symbol.second.toString() + ")\n"); + for (auto& child : children) + childrenString.append(child.first + ": " + child.second.toString() + "\n"); + return "SymbolTable(\n" + symbolsString + ") {\n" + childrenString + "}"; +} \ No newline at end of file diff --git a/compiler/src/analyzer/SymbolTable.h b/compiler/src/analyzer/SymbolTable.h index f066069a3..c5a3deb52 100644 --- a/compiler/src/analyzer/SymbolTable.h +++ b/compiler/src/analyzer/SymbolTable.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "SymbolTableEntry.h" class SymbolTable { @@ -26,6 +27,10 @@ class SymbolTable { bool hasChild(const std::string&); void pushSignature(const FunctionSignature&); FunctionSignature popSignature(); + llvm::BasicBlock* getContinueBlock() const; + void setContinueBlock(llvm::BasicBlock*); + llvm::BasicBlock* getBreakBlock() const; + void setBreakBlock(llvm::BasicBlock*); std::string toString(); private: // Members @@ -34,4 +39,6 @@ class SymbolTable { std::map symbols; std::vector paramNames; std::queue functionSignatures; + llvm::BasicBlock* continueBlock = nullptr; + llvm::BasicBlock* breakBlock = nullptr; }; \ No newline at end of file diff --git a/compiler/src/exception/SemanticError.cpp b/compiler/src/exception/SemanticError.cpp index 438aab301..6e3c510b5 100644 --- a/compiler/src/exception/SemanticError.cpp +++ b/compiler/src/exception/SemanticError.cpp @@ -27,7 +27,8 @@ std::string SemanticError::getMessagePrefix(SemanticErrorType type) { case CONDITION_MUST_BE_BOOL: return "Condition must be bool"; case MISSING_MAIN_FUNCTION: return "Spice programs must contain a main function"; case FCT_PARAM_IS_TYPE_DYN: return "Parameter type dyn not valid in function/procedure definition without default value"; - case INVALID_BREAK_NUMBER: return "Invalid number of breaks"; + case INVALID_BREAK_NUMBER: return "Invalid number of break calls"; + case INVALID_CONTINUE_NUMBER: return "Invalid number of continue calls"; case PRINTF_TYPE_ERROR: return "Types of printf call not matching"; } return "Unknown error"; diff --git a/compiler/src/exception/SemanticError.h b/compiler/src/exception/SemanticError.h index c05023b66..4ebee8557 100644 --- a/compiler/src/exception/SemanticError.h +++ b/compiler/src/exception/SemanticError.h @@ -19,6 +19,7 @@ enum SemanticErrorType { MISSING_MAIN_FUNCTION, FCT_PARAM_IS_TYPE_DYN, INVALID_BREAK_NUMBER, + INVALID_CONTINUE_NUMBER, PRINTF_TYPE_ERROR }; diff --git a/compiler/src/generator/GeneratorVisitor.cpp b/compiler/src/generator/GeneratorVisitor.cpp index 91863cc2f..f973784e3 100644 --- a/compiler/src/generator/GeneratorVisitor.cpp +++ b/compiler/src/generator/GeneratorVisitor.cpp @@ -325,10 +325,12 @@ antlrcpp::Any GeneratorVisitor::visitForLoop(SpiceParser::ForLoopContext* ctx) { // Change scope std::string scopeId = ScopeIdUtil::getScopeId(ctx); currentScope = currentScope->getChild(scopeId); + currentScope->setContinueBlock(bCond); + currentScope->setBreakBlock(bLoopEnd); // Execute pre-loop stmts visit(ctx->assignment()[0]); - // Jump in condition block + // Jump into condition block createBr(bCond); // Fill condition block @@ -345,7 +347,7 @@ antlrcpp::Any GeneratorVisitor::visitForLoop(SpiceParser::ForLoopContext* ctx) { visit(ctx->stmtLst()); // Run post-loop actions visit(ctx->assignment()[2]); - // Jump in condition block + // Jump into condition block createBr(bCond); // Fill loop end block @@ -360,30 +362,35 @@ antlrcpp::Any GeneratorVisitor::visitForLoop(SpiceParser::ForLoopContext* ctx) { } antlrcpp::Any GeneratorVisitor::visitWhileLoop(SpiceParser::WhileLoopContext* ctx) { - auto conditionValue = visit(ctx->assignment()).as(); auto parentFct = builder->GetInsertBlock()->getParent(); // Create blocks + auto bCond = llvm::BasicBlock::Create(*context, "while_cond"); auto bLoop = llvm::BasicBlock::Create(*context, "while"); auto bLoopEnd = llvm::BasicBlock::Create(*context, "while_end"); - // Check if entering the loop is necessary - createCondBr(conditionValue, bLoop, bLoopEnd); - // Change scope std::string scopeId = ScopeIdUtil::getScopeId(ctx); currentScope = currentScope->getChild(scopeId); + currentScope->setContinueBlock(bCond); + currentScope->setBreakBlock(bLoopEnd); + + // Jump into condition block + createBr(bCond); + + // Fill condition block + parentFct->getBasicBlockList().push_back(bCond); + moveInsertPointToBlock(bCond); + auto conditionValue = visit(ctx->assignment()).as(); + createCondBr(conditionValue, bLoop, bLoopEnd); // Fill loop block parentFct->getBasicBlockList().push_back(bLoop); moveInsertPointToBlock(bLoop); // Generate IR for nested statements visit(ctx->stmtLst()); - // Visit condition again - conditionValue = visit(ctx->assignment()).as(); - // Check if condition is now false - bLoop = builder->GetInsertBlock(); - createCondBr(conditionValue, bLoop, bLoopEnd); + // Jump into condition block + createBr(bCond); // Fill loop end block parentFct->getBasicBlockList().push_back(bLoopEnd); @@ -396,6 +403,13 @@ antlrcpp::Any GeneratorVisitor::visitWhileLoop(SpiceParser::WhileLoopContext* ct return llvm::ConstantInt::get(llvm::Type::getInt1Ty(*context), 1); } +antlrcpp::Any GeneratorVisitor::visitStmtLst(SpiceParser::StmtLstContext* ctx) { + for (auto& child : ctx->children) { + if (!blockAlreadyTerminated) visit(child); + } + return nullptr; +} + antlrcpp::Any GeneratorVisitor::visitIfStmt(SpiceParser::IfStmtContext* ctx) { auto conditionValue = visit(ctx->assignment()).as(); auto parentFct = builder->GetInsertBlock()->getParent(); @@ -473,6 +487,44 @@ antlrcpp::Any GeneratorVisitor::visitReturnStmt(SpiceParser::ReturnStmtContext* return returnValue; } +antlrcpp::Any GeneratorVisitor::visitBreakStmt(SpiceParser::BreakStmtContext* ctx) { + // Get number, how many loops we want to break + int breakCount = 1; + if (ctx->INTEGER()) breakCount = std::stoi(ctx->INTEGER()->toString()); + + // Get destination block + SymbolTable* scope = currentScope; + while (!scope->getBreakBlock()) scope = scope->getParent(); + for (int i = 1; i < breakCount; i++) { + scope = scope->getParent(); + while (!scope->getBreakBlock()) scope = scope->getParent(); + } + llvm::BasicBlock* destinationBlock = scope->getBreakBlock(); + + // Jump to destination block + createBr(destinationBlock); + return nullptr; +} + +antlrcpp::Any GeneratorVisitor::visitContinueStmt(SpiceParser::ContinueStmtContext* ctx) { + // Get number, how many loops we want to continue + int continueCount = 1; + if (ctx->INTEGER()) continueCount = std::stoi(ctx->INTEGER()->toString()); + + // Get destination block + SymbolTable* scope = currentScope; + while (!scope->getBreakBlock()) scope = scope->getParent(); + for (int i = 1; i < continueCount; i++) { + scope = scope->getParent(); + while (!scope->getBreakBlock()) scope = scope->getParent(); + } + llvm::BasicBlock* destinationBlock = scope->getContinueBlock(); + + // Jump to destination block + createBr(destinationBlock); + return nullptr; +} + antlrcpp::Any GeneratorVisitor::visitPrintfStmt(SpiceParser::PrintfStmtContext* ctx) { auto printf = module->getFunction("printf"); std::vector printfArgs; diff --git a/compiler/src/generator/GeneratorVisitor.h b/compiler/src/generator/GeneratorVisitor.h index 8c8cfb30c..171f425e4 100644 --- a/compiler/src/generator/GeneratorVisitor.h +++ b/compiler/src/generator/GeneratorVisitor.h @@ -52,12 +52,14 @@ class GeneratorVisitor : public SpiceBaseVisitor { antlrcpp::Any visitFunctionDef(SpiceParser::FunctionDefContext* ctx) override; antlrcpp::Any visitProcedureDef(SpiceParser::ProcedureDefContext* ctx) override; antlrcpp::Any visitForLoop(SpiceParser::ForLoopContext* ctx) override; - /*antlrcpp::Any visitForeachLoop(SpiceParser::ForeachLoopContext* ctx) override;*/ antlrcpp::Any visitWhileLoop(SpiceParser::WhileLoopContext* ctx) override; + antlrcpp::Any visitStmtLst(SpiceParser::StmtLstContext* ctx) override; antlrcpp::Any visitIfStmt(SpiceParser::IfStmtContext* ctx) override; antlrcpp::Any visitDeclStmt(SpiceParser::DeclStmtContext* ctx) override; antlrcpp::Any visitFunctionCall(SpiceParser::FunctionCallContext* ctx) override; antlrcpp::Any visitReturnStmt(SpiceParser::ReturnStmtContext* ctx) override; + antlrcpp::Any visitBreakStmt(SpiceParser::BreakStmtContext *ctx) override; + antlrcpp::Any visitContinueStmt(SpiceParser::ContinueStmtContext* ctx) override; antlrcpp::Any visitPrintfStmt(SpiceParser::PrintfStmtContext* ctx) override; antlrcpp::Any visitAssignment(SpiceParser::AssignmentContext* ctx) override; antlrcpp::Any visitTernary(SpiceParser::TernaryContext* ctx) override; diff --git a/compiler/src/grammar/Spice.g4 b/compiler/src/grammar/Spice.g4 index 135e4a2ab..ea94dcbc4 100644 --- a/compiler/src/grammar/Spice.g4 +++ b/compiler/src/grammar/Spice.g4 @@ -18,7 +18,7 @@ functionCall: IDENTIFIER LPAREN paramLstCall? RPAREN; importStmt: IMPORT STRING; returnStmt: RETURN assignment; breakStmt: BREAK INTEGER?; -continueStmt: CONTINUE; +continueStmt: CONTINUE INTEGER?; printfStmt: PRINTF LPAREN STRING (COMMA assignment)* RPAREN; assignment: ((declStmt | IDENTIFIER) (ASSIGN_OP | PLUS_EQUAL | MINUS_EQUAL | MUL_EQUAL | DIV_EQUAL))? ternary; diff --git a/media/test.spice b/media/test.spice index c6053ee4f..7018b3b0c 100644 --- a/media/test.spice +++ b/media/test.spice @@ -1,7 +1,11 @@ -f main1() { +f main() { int i = 0; while i < 10 { i += 1; printf("i is now at: %d\n", i); + if i > 5 { + continue; + } + printf("test"); } } \ No newline at end of file From 2cbbbcdb59e0f3394922f653a2151249a3232669 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Mon, 9 Aug 2021 00:17:59 +0200 Subject: [PATCH 7/9] Fix generator test for while loop --- compiler/src/generator/GeneratorVisitor.cpp | 2 +- .../generator/success-while-loop/ir-code.ll | 15 ++++++++------- media/test.spice | 4 ---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/src/generator/GeneratorVisitor.cpp b/compiler/src/generator/GeneratorVisitor.cpp index f973784e3..9ec2b28d2 100644 --- a/compiler/src/generator/GeneratorVisitor.cpp +++ b/compiler/src/generator/GeneratorVisitor.cpp @@ -529,7 +529,7 @@ antlrcpp::Any GeneratorVisitor::visitPrintfStmt(SpiceParser::PrintfStmtContext* auto printf = module->getFunction("printf"); std::vector printfArgs; auto stringTemplate = ctx->STRING()->toString(); - stringTemplate = stringTemplate.erase(stringTemplate.size() -1).erase(0, 1); + stringTemplate = stringTemplate.substr(1, stringTemplate.size() - 2); printfArgs.push_back(builder->CreateGlobalStringPtr(stringTemplate)); for (auto &arg : ctx->assignment()) { auto argVal = visit(arg).as(); diff --git a/compiler/test/test-files/generator/success-while-loop/ir-code.ll b/compiler/test/test-files/generator/success-while-loop/ir-code.ll index 48ff07886..a5b3a8355 100644 --- a/compiler/test/test-files/generator/success-while-loop/ir-code.ll +++ b/compiler/test/test-files/generator/success-while-loop/ir-code.ll @@ -11,21 +11,22 @@ main_entry: %result = alloca i32, align 4 store i32 0, i32* %result, align 4 store i32 0, i32* %i, align 4 + br label %while_cond + +while_cond: ; preds = %while, %main_entry %0 = load i32, i32* %i, align 4 %lt = icmp slt i32 %0, 10 br i1 %lt, label %while, label %while_end -while: ; preds = %while, %main_entry +while: ; preds = %while_cond %1 = load i32, i32* %i, align 4 %ple = add i32 %1, 1 store i32 %ple, i32* %i, align 4 %2 = load i32, i32* %i, align 4 %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @0, i32 0, i32 0), i32 %2) - %4 = load i32, i32* %i, align 4 - %lt1 = icmp slt i32 %4, 10 - br i1 %lt1, label %while, label %while_end + br label %while_cond -while_end: ; preds = %while, %main_entry - %5 = load i32, i32* %result, align 4 - ret i32 %5 +while_end: ; preds = %while_cond + %4 = load i32, i32* %result, align 4 + ret i32 %4 } diff --git a/media/test.spice b/media/test.spice index 7018b3b0c..083d7c77a 100644 --- a/media/test.spice +++ b/media/test.spice @@ -3,9 +3,5 @@ f main() { while i < 10 { i += 1; printf("i is now at: %d\n", i); - if i > 5 { - continue; - } - printf("test"); } } \ No newline at end of file From ff9707eade9dacdcb2a256bd78a561b54b713d2b Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Mon, 9 Aug 2021 10:00:24 +0200 Subject: [PATCH 8/9] Install alpine version of build-essentials in Docker container --- .github/dependabot.yml | 14 ++++++++++++++ Dockerfile | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3a58f6970..4295a5d14 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,20 @@ updates: timezone: Europe/Berlin open-pull-requests-limit: 15 target-branch: main + reviewers: + - chillibits/compiler-team + assignees: + - chillibits/compiler-team + + # Dockerfile + - package-ecosystem: docker + directory: / + schedule: + interval: daily + time: "04:00" + timezone: Europe/Berlin + open-pull-requests-limit: 15 + target-branch: main reviewers: - chillibits/compiler-team assignees: diff --git a/Dockerfile b/Dockerfile index e153cf52b..37f8f45a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14.0 +FROM alpine:3.14.1 WORKDIR /spice/out ENV TERM="xterm-256color" @@ -6,7 +6,7 @@ ENV SPICE_DOCKERIZED=1 ARG COMPILER_PATH=linux-amd64 -RUN apk update && apk add --no-cache libc6-compat libstdc++ && rm -rf /var/cache/apk/* +RUN apk update && apk add --no-cache alpine-sdk && rm -rf /var/cache/apk/* COPY spice /usr/bin/spice COPY bin/spicec-${COMPILER_PATH}/ /usr/lib/spice/ From babd7b8abfc74e6363115ded099aae352bad3765 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Mon, 9 Aug 2021 13:24:51 +0200 Subject: [PATCH 9/9] Add tests for break and continue --- compiler/test/AnalyzerTest.cpp | 22 ++++++++++++++++--- .../source.spice | 12 ++++++++++ .../error-break-count-valid/source.spice | 12 ++++++++++ .../source.spice | 12 ++++++++++ .../error-continue-count-valid/source.spice | 12 ++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 compiler/test/test-files/analyzer/error-break-count-not-too-high/source.spice create mode 100644 compiler/test/test-files/analyzer/error-break-count-valid/source.spice create mode 100644 compiler/test/test-files/analyzer/error-continue-count-not-too-high/source.spice create mode 100644 compiler/test/test-files/analyzer/error-continue-count-valid/source.spice diff --git a/compiler/test/AnalyzerTest.cpp b/compiler/test/AnalyzerTest.cpp index e22fcee05..7466bb496 100644 --- a/compiler/test/AnalyzerTest.cpp +++ b/compiler/test/AnalyzerTest.cpp @@ -118,13 +118,29 @@ const AnalyzerParams ANALYZER_TEST_PARAMETERS[] = { { // 25 "error-dyn-return-types-not-matching", "Semantic error at 3:12: Wrong data type for operator: Passed wrong data type to return statement" - }, // 26 - { + }, + { // 26 "error-printf-type-incompatibility", "Semantic error at 4:36: Types of printf call not matching: Template string expects an int or a bool here" }, - // Successful tests { // 27 + "error-break-count-valid", + "Semantic error at 7:23: Invalid number of break calls: Break count must be >= 1: -10" + }, + { // 28 + "error-break-count-not-too-high", + "Semantic error at 7:23: Invalid number of break calls: We only can break 2 time(s) here" + }, + { // 29 + "error-continue-count-valid", + "Semantic error at 7:26: Invalid number of continue calls: Continue count must be >= 1: -10" + }, + { // 30 + "error-continue-count-not-too-high", + "Semantic error at 7:26: Invalid number of continue calls: We only can continue 2 time(s) here" + }, + // Successful tests + { // 31 "success-fibonacci", "" } diff --git a/compiler/test/test-files/analyzer/error-break-count-not-too-high/source.spice b/compiler/test/test-files/analyzer/error-break-count-not-too-high/source.spice new file mode 100644 index 000000000..b28ae0f64 --- /dev/null +++ b/compiler/test/test-files/analyzer/error-break-count-not-too-high/source.spice @@ -0,0 +1,12 @@ +f main() { + for int i = 0; i < 15; i++ { + if i > 6 { + int j = 6; + while j > 3 { + printf("Test"); + break 4; + j--; + } + } + } +} \ No newline at end of file diff --git a/compiler/test/test-files/analyzer/error-break-count-valid/source.spice b/compiler/test/test-files/analyzer/error-break-count-valid/source.spice new file mode 100644 index 000000000..883c65e95 --- /dev/null +++ b/compiler/test/test-files/analyzer/error-break-count-valid/source.spice @@ -0,0 +1,12 @@ +f main() { + for int i = 0; i < 15; i++ { + if i > 6 { + int j = 6; + while j > 3 { + printf("Test"); + break -10; + j--; + } + } + } +} \ No newline at end of file diff --git a/compiler/test/test-files/analyzer/error-continue-count-not-too-high/source.spice b/compiler/test/test-files/analyzer/error-continue-count-not-too-high/source.spice new file mode 100644 index 000000000..ace17a080 --- /dev/null +++ b/compiler/test/test-files/analyzer/error-continue-count-not-too-high/source.spice @@ -0,0 +1,12 @@ +f main() { + for int i = 0; i < 15; i++ { + if i > 6 { + int j = 6; + while j > 3 { + printf("Test"); + continue 3; + j--; + } + } + } +} \ No newline at end of file diff --git a/compiler/test/test-files/analyzer/error-continue-count-valid/source.spice b/compiler/test/test-files/analyzer/error-continue-count-valid/source.spice new file mode 100644 index 000000000..225b6790e --- /dev/null +++ b/compiler/test/test-files/analyzer/error-continue-count-valid/source.spice @@ -0,0 +1,12 @@ +f main() { + for int i = 0; i < 15; i++ { + if i > 6 { + int j = 6; + while j > 3 { + printf("Test"); + continue -10; + j--; + } + } + } +} \ No newline at end of file