From e11d5a0ae36f78d7272dac3f072945458164f16b Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Tue, 11 Jun 2019 21:32:54 +0200 Subject: [PATCH 1/7] Added Contextual Analysis final checks --- AST/CommandAssign.cpp | 4 ++ AST/CommandAssign.h | 2 + AST/CommandIf.cpp | 4 ++ AST/CommandIf.h | 2 + AST/Expression.cpp | 28 +++++++++++++ AST/Expression.h | 5 +++ AST/Operate.cpp | 4 ++ AST/Operate.h | 2 + AST/PrimaryExpression.cpp | 4 ++ AST/PrimaryExpression.h | 3 ++ AST/PrimaryExpressionVar.cpp | 28 ++++++++++++- AST/PrimaryExpressionVar.h | 11 ++++- AST/PrimaryExpression_Expression.cpp | 4 ++ AST/PrimaryExpression_Expression.h | 2 + Parser.cpp | 62 +++++++++++++++++++++++++--- Scanner.h | 2 +- 16 files changed, 157 insertions(+), 10 deletions(-) diff --git a/AST/CommandAssign.cpp b/AST/CommandAssign.cpp index fd61644..246bb60 100644 --- a/AST/CommandAssign.cpp +++ b/AST/CommandAssign.cpp @@ -17,3 +17,7 @@ CommandAssign::CommandAssign(VarName *varName, Expression *expression): Expressi std::string CommandAssign::describe() { return Command::describe() + " " + Expression::describe() + " " + VarName::describe(); } + +std::string CommandAssign::getType() { + return Expression::getType(); +} diff --git a/AST/CommandAssign.h b/AST/CommandAssign.h index 2c35877..583639a 100644 --- a/AST/CommandAssign.h +++ b/AST/CommandAssign.h @@ -15,6 +15,8 @@ class CommandAssign: public Command, public Expression, public VarName { CommandAssign(VarName *varName, Expression *expression); std::string describe() override; + + std::string getType() override; }; diff --git a/AST/CommandIf.cpp b/AST/CommandIf.cpp index 6dd863d..e48a685 100644 --- a/AST/CommandIf.cpp +++ b/AST/CommandIf.cpp @@ -20,3 +20,7 @@ CommandIf::~CommandIf() { delete falseCommand; delete primaryExpression; } + +std::string CommandIf::getType() { + return "BOOL"; +} diff --git a/AST/CommandIf.h b/AST/CommandIf.h index d072034..74b1cdb 100644 --- a/AST/CommandIf.h +++ b/AST/CommandIf.h @@ -21,6 +21,8 @@ class CommandIf : public Command, public PrimaryExpression { virtual ~CommandIf(); std::string describe() override; + + std::string getType() override; }; diff --git a/AST/Expression.cpp b/AST/Expression.cpp index bc6ff67..e5cbf5c 100644 --- a/AST/Expression.cpp +++ b/AST/Expression.cpp @@ -22,3 +22,31 @@ Expression::~Expression() { std::string Expression::describe() { return P1->describe() + " " + O->describe() + " " + P2->describe(); } + +std::string Expression::getType() { + return ""; +} +// // Get expression types +// std::string p1 = P1->getType(); +// std::string p2 = P2->getType(); +// +// // Check if any types are null, this should be first in checking order +// if(p1.length() == 0) { +// fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\'\n" ANSI_COLOR_RESET, P1->describe().data()); +// return ""; +// } +// if( p2.length() == 0){ +// fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\'\n" ANSI_COLOR_RESET, P2->describe().data()); +// return ""; +// } +// +// // Check incompatible types +// if(p1 != p2) { +// fprintf(stderr, ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, P1->describe().data(), p1.data(), P2->describe().data(), p2.data()); +// return ""; +// } +// +// // If i +// fprintf(stderr, ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s\nP2: %s\n" ANSI_COLOR_RESET,) +// return ""; +//} diff --git a/AST/Expression.h b/AST/Expression.h index dbd42e9..024aa2f 100644 --- a/AST/Expression.h +++ b/AST/Expression.h @@ -5,6 +5,9 @@ #ifndef COMPILER_EXPRESSION_H #define COMPILER_EXPRESSION_H +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_RESET "\x1b[0m" #include "AST.h" #include "PrimaryExpression.h" @@ -18,6 +21,8 @@ class Expression : public AST { explicit Expression(Expression *expression); virtual ~Expression(); std::string describe() override; + + virtual std::string getType(); }; diff --git a/AST/Operate.cpp b/AST/Operate.cpp index dc60395..ebd8a7d 100644 --- a/AST/Operate.cpp +++ b/AST/Operate.cpp @@ -5,3 +5,7 @@ #include "Operate.h" Operate::Operate(const std::string &spelling) : Terminal(spelling) {} + +std::string Operate::describe() { + return spelling; +} diff --git a/AST/Operate.h b/AST/Operate.h index 3e275c7..a771bba 100644 --- a/AST/Operate.h +++ b/AST/Operate.h @@ -11,6 +11,8 @@ class Operate: public Terminal { public: explicit Operate(const std::string &spelling); + + std::string describe() override; }; diff --git a/AST/PrimaryExpression.cpp b/AST/PrimaryExpression.cpp index 05d9b9b..3d3c382 100644 --- a/AST/PrimaryExpression.cpp +++ b/AST/PrimaryExpression.cpp @@ -7,3 +7,7 @@ std::string PrimaryExpression::describe() { return AST::describe(); } + +PrimaryExpression::~PrimaryExpression() { + +} diff --git a/AST/PrimaryExpression.h b/AST/PrimaryExpression.h index 8799183..b5049d2 100644 --- a/AST/PrimaryExpression.h +++ b/AST/PrimaryExpression.h @@ -9,7 +9,10 @@ class PrimaryExpression: public AST { public: + virtual ~PrimaryExpression(); + std::string describe() override; + virtual std::string getType() = 0; }; diff --git a/AST/PrimaryExpressionVar.cpp b/AST/PrimaryExpressionVar.cpp index c85c954..1e2cd4a 100644 --- a/AST/PrimaryExpressionVar.cpp +++ b/AST/PrimaryExpressionVar.cpp @@ -1,4 +1,5 @@ #include +#include // // Created by cybex on 2019/06/04. @@ -6,4 +7,29 @@ #include "PrimaryExpressionVar.h" -PrimaryExpressionVar::PrimaryExpressionVar(const std::string &spelling) : VarName(spelling) {} +PrimaryExpressionVar::PrimaryExpressionVar(const std::string &spelling) : Identifier(spelling) {} + +std::string PrimaryExpressionVar::getType() { + if (std::regex_match(spelling, std::regex(("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?")))) { +#ifdef _INT + return "INT"; +#elif + fprintf(stderr, ANSI_COLOR_RED "Unknown Type \'%s\' [Type int recognized]" ANSI_COLOR_RESET, spelling.data()); + return ""; +#endif + } else if (std::regex_match(spelling, std::regex(("^[a-zA-Z]+$")))) { +#ifdef _INT + return "STRING"; +#elif + fprintf(stderr, ANSI_COLOR_RED "Unknown Type \'%s\' [Type std::string recognized]" ANSI_COLOR_RESET, spelling.data()); + return ""; +#endif + }; + + fprintf(stderr, ANSI_COLOR_RED "Unknown Type \'%s\'" ANSI_COLOR_RESET, spelling.data()); + return ""; +} + +std::string PrimaryExpressionVar::describe() { + return spelling; +} diff --git a/AST/PrimaryExpressionVar.h b/AST/PrimaryExpressionVar.h index 6f9ef63..33c8d60 100644 --- a/AST/PrimaryExpressionVar.h +++ b/AST/PrimaryExpressionVar.h @@ -5,13 +5,20 @@ #ifndef COMPILER_PRIMARYEXPRESSIONVAR_H #define COMPILER_PRIMARYEXPRESSIONVAR_H +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_RESET "\x1b[0m" + #include #include "PrimaryExpression.h" #include "VarName.h" -class PrimaryExpressionVar: public PrimaryExpression, public VarName { +class PrimaryExpressionVar: public PrimaryExpression, public Identifier { public: - PrimaryExpressionVar(const std::string &spelling); + explicit PrimaryExpressionVar(const std::string &spelling); + std::string getType() override; + + std::string describe() override; }; diff --git a/AST/PrimaryExpression_Expression.cpp b/AST/PrimaryExpression_Expression.cpp index 6ad6e3c..ea5e3e8 100644 --- a/AST/PrimaryExpression_Expression.cpp +++ b/AST/PrimaryExpression_Expression.cpp @@ -12,3 +12,7 @@ PrimaryExpression_Expression::PrimaryExpression_Expression(PrimaryExpression *p1 : Expression(p1, p2, o) { } + +std::string PrimaryExpression_Expression::getType() { + return Expression::getType(); +} diff --git a/AST/PrimaryExpression_Expression.h b/AST/PrimaryExpression_Expression.h index 5fb2176..c01a19c 100644 --- a/AST/PrimaryExpression_Expression.h +++ b/AST/PrimaryExpression_Expression.h @@ -14,6 +14,8 @@ class PrimaryExpression_Expression : public Expression, public PrimaryExpression public: PrimaryExpression_Expression(PrimaryExpression *p1, PrimaryExpression *p2, Operate *o); explicit PrimaryExpression_Expression(Expression *expression); + + std::string getType() override; }; diff --git a/Parser.cpp b/Parser.cpp index 631985d..e07a2ce 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -5,6 +5,10 @@ #include "AST/CommandAssign.h" #include "AST/CommandLet.h" + +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_RESET "\x1b[0m" + // // Created by cybex on 2019/05/03. // @@ -21,9 +25,9 @@ Parser::Parser(std::string sentence) { int Parser::compile() { Scanner *s = new Scanner(std::move(sentence)); if (_VERBOSITY > 2) { - fprintf(stdout, "Building Token List...\n"); + fprintf(stdout, ANSI_COLOR_CYAN "Building Token List...\n" ANSI_COLOR_RESET); } else if (_VERBOSITY <= 2) { - fprintf(stdout, "Building Token List..."); + fprintf(stdout, ANSI_COLOR_CYAN "Building Token List..." ANSI_COLOR_RESET); } int success = s->buildTokenList(); if (success == 1) { @@ -39,9 +43,9 @@ int Parser::compile() { // Build AST if (_VERBOSITY > 2) { - fprintf(stdout, "Building AST...\n"); + fprintf(stdout, ANSI_COLOR_CYAN "Building AST...\n" ANSI_COLOR_RESET); } else if (_VERBOSITY <= 2) { - fprintf(stdout, "Building AST..."); + fprintf(stdout, ANSI_COLOR_CYAN "Building AST..." ANSI_COLOR_RESET); } buildAST(); @@ -193,9 +197,54 @@ Command *Parser::parseCommand() { } Expression *Parser::parseExpression() { + // parse primary expression PrimaryExpression *p1 = parsePrimaryExpression(); + std::string p1Type = p1->getType(); + if (p1Type.length() == 0) { + fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, p1->describe().data(), p1Type.data()); + exit(1); + } + // Check if variable/expression defined in vartable, then change data type appropriately + if (p1Type == "STRING" || p1Type == "CHAR") { + if(checkVarExists(p1->describe())) { + p1Type = var_table.find(p1->describe())->second.type->describe(); + } + } + p1Type = Scanner::toUpper(p1Type); + + // parse operator Operate *o1 = parseOperator(); + + // parse primary expression PrimaryExpression *p2 = parsePrimaryExpression(); + std::string p2Type = p2->getType(); + if (p2Type.length() == 0) { + fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, p2->describe().data(), p2Type.data()); + exit(1); + } + // Check if variable/expression defined in vartable, then change data type appropriately + if (p2Type == "STRING" || p2Type == "CHAR") { + if(checkVarExists(p2->describe())) { + p2Type = var_table.find(p2->describe())->second.type->describe(); + } + } + p2Type = Scanner::toUpper(p2Type); + + // Check + is used with strings or chars + if (p2Type == p1Type && (p1Type == "STRING" || p1Type == "CHAR") && o1->describe() != "+") { + fprintf(stderr, ANSI_COLOR_RED "FATAL: Invalid Operator \'%s\' for Strings \'%s\' and \'%s\'\n" ANSI_COLOR_RESET, o1->describe().data(), p1->describe().data(), p2->describe().data()); + exit(1); + } + + // Check incompatible types + if(p1Type != p2Type) { + fprintf(stderr, ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, p1->describe().data(), p1Type.data(), p2->describe().data(), p2Type.data()); + exit(1); + } + + if (_VERBOSITY >= 3) { + fprintf(stdout, ANSI_COLOR_GREEN "\t = PrimaryExpression1 [Type \'%s\'] matches PrimaryExpression2 [Type \'%s\']\n" ANSI_COLOR_RESET, p1Type.data(), p2Type.data()); + } // Build Expression return new Expression(p1, p2, o1); @@ -248,13 +297,14 @@ Declaration *Parser::parseDeclaration() { VarName *id = parseVarName(); nextToken(TokenType::DeclConstToken); Expression *expression = parseExpression(); + TypeDenoter *typeDenoter = new TypeDenoter(expression->getType()); // Create var_table reference - vardef_t *vardef = new vardef_t{ + auto *vardef = new vardef_t{ .name = id, .defined = false, .isConst = true, - .type = new TypeDenoter("const") + .type = typeDenoter }; // Add to var_table diff --git a/Scanner.h b/Scanner.h index 377d293..3a51ff9 100644 --- a/Scanner.h +++ b/Scanner.h @@ -24,7 +24,7 @@ class Scanner { int buildTokenList(); int buildTokenList(std::string sentence); std::string buildNextToken(); - std::string toUpper(const std::string& str); + static std::string toUpper(const std::string& str); TokenType findType(const std::string& spelling); std::vector getTokenList(); virtual ~Scanner(); From c75cb18b3f714d869a7a5f843cf3414d16f37606 Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Tue, 11 Jun 2019 21:33:13 +0200 Subject: [PATCH 2/7] Added test cases - code --- main.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 814c181..d474be7 100644 --- a/main.cpp +++ b/main.cpp @@ -27,8 +27,29 @@ int main() { #ifndef _TESTING std::getline(std::cin, code); #else - code = "let var t: int in t := t + 1"; -// code = "if(true*true) then x:=x+1 else z:=z+2"; + // syntax issue - missing in keyword +// code = "let var t: int t := t + t"; + + // test string <-> int incompatibility +// code = "let var t: int in t := t + abc"; + + // test int <-> int incompatibility, assignment, let command +// code = "let var t: int in t := t + 111"; + + // shows breaking of language example if statement requires Commands (i.e. assiignemnt, ifstatement or let) +// code = "if(true+true) then x+x else z+z"; + + // shows nested commands +// code = "if(true+true) then let var e: int in e:=e+1 else let var z: int in z:=z+2"; + + // shows nested let commands with duplicate variables +// code = "let var e: int in let var e: int in e:=e+e"; + + // shows incompatible data types +// code = "if(true+true) then x:=x+1 else z:=z+2"; + + // shows undeclared variable +// code = "if(true+true) then x:=z+z else z:=z+2"; #endif } From 0d2ca25011d66a8aad9be89ea147a5a76bdb0a96 Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Tue, 11 Jun 2019 22:38:34 +0200 Subject: [PATCH 3/7] Added const type checking with tests --- AST/Expression.cpp | 9 +++++++- Parser.cpp | 51 +++++++++++++++++++++++++++++++++++----------- main.cpp | 17 +++++++++++++--- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/AST/Expression.cpp b/AST/Expression.cpp index e5cbf5c..18171a6 100644 --- a/AST/Expression.cpp +++ b/AST/Expression.cpp @@ -24,7 +24,14 @@ std::string Expression::describe() { } std::string Expression::getType() { - return ""; + if (!P1->getType().empty() && !P2->getType().empty() && P1->getType() == P2->getType()) { + return P1->getType(); + } + + fprintf(stderr, + ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, + P1->describe().data(), P1->getType().data(), P2->describe().data(), P2->getType().data()); + exit(1); } // // Get expression types // std::string p1 = P1->getType(); diff --git a/Parser.cpp b/Parser.cpp index e07a2ce..5a12167 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -173,11 +173,12 @@ Command *Parser::parseCommand() { int c = var_table.count(varName->describe()); if (c == 0) { - fprintf(stdout, ANSI_COLOR_RED "Undeclared variable: %s in:\n%s\n" ANSI_COLOR_RESET, + fprintf(stdout, ANSI_COLOR_RED "FATAL: Undeclared variable: %s in:\n%s\n" ANSI_COLOR_RESET, varName->describe().data(), expression->describe().data()); exit(1); } + // Check is defined auto mapIterator = var_table.find(varName->describe()); if (!mapIterator->second.defined) { fprintf(stdout, ANSI_COLOR_RED "Undefined variable: %s in:\n%s\n" ANSI_COLOR_RESET, @@ -185,6 +186,25 @@ Command *Parser::parseCommand() { exit(1); } + // Get pointer to variable + vardef_t *var = &mapIterator->second; + + // Check data type match + if (var->type->describe() != expression->getType()) { + fprintf(stderr, + ANSI_COLOR_RED "FATAL: Incompatible types: \nVariable \'%s\' [Type \'%s\']\nExpression: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, + var->name->describe().data(), var->type->describe().data(), expression->describe().data(), expression->getType().data()); + exit(1); + } + + // Check if const + if (var->isConst) { + fprintf(stdout, + ANSI_COLOR_RED "FATAL: \'%s\' is a constant. You cannot alter it once declared.\n" ANSI_COLOR_RESET, + varName->describe().data()); + exit(1); + } + // Build variable assignment return new CommandAssign(varName, expression); } @@ -201,12 +221,13 @@ Expression *Parser::parseExpression() { PrimaryExpression *p1 = parsePrimaryExpression(); std::string p1Type = p1->getType(); if (p1Type.length() == 0) { - fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, p1->describe().data(), p1Type.data()); + fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, + p1->describe().data(), p1Type.data()); exit(1); } // Check if variable/expression defined in vartable, then change data type appropriately if (p1Type == "STRING" || p1Type == "CHAR") { - if(checkVarExists(p1->describe())) { + if (checkVarExists(p1->describe())) { p1Type = var_table.find(p1->describe())->second.type->describe(); } } @@ -219,12 +240,13 @@ Expression *Parser::parseExpression() { PrimaryExpression *p2 = parsePrimaryExpression(); std::string p2Type = p2->getType(); if (p2Type.length() == 0) { - fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, p2->describe().data(), p2Type.data()); + fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, + p2->describe().data(), p2Type.data()); exit(1); } // Check if variable/expression defined in vartable, then change data type appropriately if (p2Type == "STRING" || p2Type == "CHAR") { - if(checkVarExists(p2->describe())) { + if (checkVarExists(p2->describe())) { p2Type = var_table.find(p2->describe())->second.type->describe(); } } @@ -232,18 +254,24 @@ Expression *Parser::parseExpression() { // Check + is used with strings or chars if (p2Type == p1Type && (p1Type == "STRING" || p1Type == "CHAR") && o1->describe() != "+") { - fprintf(stderr, ANSI_COLOR_RED "FATAL: Invalid Operator \'%s\' for Strings \'%s\' and \'%s\'\n" ANSI_COLOR_RESET, o1->describe().data(), p1->describe().data(), p2->describe().data()); + fprintf(stderr, + ANSI_COLOR_RED "FATAL: Invalid Operator \'%s\' for Strings \'%s\' and \'%s\'\n" ANSI_COLOR_RESET, + o1->describe().data(), p1->describe().data(), p2->describe().data()); exit(1); } // Check incompatible types - if(p1Type != p2Type) { - fprintf(stderr, ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, p1->describe().data(), p1Type.data(), p2->describe().data(), p2Type.data()); + if (p1Type != p2Type) { + fprintf(stderr, + ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, + p1->describe().data(), p1Type.data(), p2->describe().data(), p2Type.data()); exit(1); } if (_VERBOSITY >= 3) { - fprintf(stdout, ANSI_COLOR_GREEN "\t = PrimaryExpression1 [Type \'%s\'] matches PrimaryExpression2 [Type \'%s\']\n" ANSI_COLOR_RESET, p1Type.data(), p2Type.data()); + fprintf(stdout, + ANSI_COLOR_GREEN "\t = PrimaryExpression1 [Type \'%s\'] matches PrimaryExpression2 [Type \'%s\']\n" ANSI_COLOR_RESET, + p1Type.data(), p2Type.data()); } // Build Expression @@ -293,7 +321,6 @@ Declaration *Parser::parseDeclaration() { * 4. Expression */ loadNextToken(); - nextToken(TokenType::ConstToken); VarName *id = parseVarName(); nextToken(TokenType::DeclConstToken); Expression *expression = parseExpression(); @@ -472,7 +499,7 @@ void Parser::openScope(vardef_t *vardef) { var_table.insert(std::pair(vardef->name->describe(), *vardef)); } -void Parser::closeScope(const std::string& varName) { +void Parser::closeScope(const std::string &varName) { // We check if variable exists, if not, we have a problem if (!checkVarExists(varName)) { fprintf(stdout, ANSI_COLOR_RED "\tUndefined variable [%s].\n" ANSI_COLOR_RESET, @@ -490,7 +517,7 @@ void Parser::closeScope(const std::string& varName) { var_table.erase(varName); } -bool Parser::checkVarExists(const std::string& varName) { +bool Parser::checkVarExists(const std::string &varName) { return (var_table.count(varName) != 0); } diff --git a/main.cpp b/main.cpp index d474be7..7e57a26 100644 --- a/main.cpp +++ b/main.cpp @@ -27,6 +27,12 @@ int main() { #ifndef _TESTING std::getline(std::cin, code); #else + // normal - let command + code = "let var t: int in t := t + t"; + + // normal - if statement with let statements +// code = "if(true+true) then let var e: int in e:=e+1 else let var z: int in z:=z+2"; + // syntax issue - missing in keyword // code = "let var t: int t := t + t"; @@ -39,9 +45,6 @@ int main() { // shows breaking of language example if statement requires Commands (i.e. assiignemnt, ifstatement or let) // code = "if(true+true) then x+x else z+z"; - // shows nested commands -// code = "if(true+true) then let var e: int in e:=e+1 else let var z: int in z:=z+2"; - // shows nested let commands with duplicate variables // code = "let var e: int in let var e: int in e:=e+e"; @@ -50,9 +53,17 @@ int main() { // shows undeclared variable // code = "if(true+true) then x:=z+z else z:=z+2"; + + // shows incompatible data types +// code = "let const e ~ 1+1 in e:=x+y"; + + // shows editing of const + code = "let const e ~ 1+1 in e:=2+2"; #endif } + fprintf(stdout, "Compiling:\n---------------------------------------------\n\t" ANSI_COLOR_MAGENTA " \'%s\'" ANSI_COLOR_RESET " \n---------------------------------------------\n", code.data()); + if (_VERBOSITY >= 1) fprintf(stdout, ANSI_COLOR_BLUE "Compiling, please wait...\n" ANSI_COLOR_RESET); p = new Parser(code); From 162f150ec9e6873b5d86801e4fd7fc45afe41bbb Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Tue, 11 Jun 2019 22:43:49 +0200 Subject: [PATCH 4/7] Fixed Variable Datatype check [case issue] --- Parser.cpp | 2 +- main.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Parser.cpp b/Parser.cpp index 5a12167..bca98a4 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -437,7 +437,7 @@ TypeDenoter *Parser::parseTypeDenoter() { Token::tokenDesc(TokenType::IdentifierToken).data(), type.data()); } - return new TypeDenoter(type); + return new TypeDenoter(Scanner::toUpper(type)); } fprintf(stderr, "FATAL: Unknown data type \'%s\' is not defined!", type.data()); diff --git a/main.cpp b/main.cpp index 7e57a26..ae949db 100644 --- a/main.cpp +++ b/main.cpp @@ -28,7 +28,7 @@ int main() { std::getline(std::cin, code); #else // normal - let command - code = "let var t: int in t := t + t"; + code = "let var t: int in t := 1+1"; // normal - if statement with let statements // code = "if(true+true) then let var e: int in e:=e+1 else let var z: int in z:=z+2"; @@ -58,7 +58,7 @@ int main() { // code = "let const e ~ 1+1 in e:=x+y"; // shows editing of const - code = "let const e ~ 1+1 in e:=2+2"; +// code = "let const e ~ 1+1 in e:=2+2"; #endif } From e34a308f665aa82f003ee6c4b9bad0a059f426e3 Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Thu, 13 Jun 2019 14:32:06 +0200 Subject: [PATCH 5/7] Added application arg flags Added flags for compiler: - Test - List-CodeExamples - Verbosity (not working - see redefining macros) - Help --- CMakeLists.txt | 5 +- Parser.cpp | 22 ++++--- main.cpp | 165 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 141 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2be4174..ae71101 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,4 @@ +#!/usr/bin/cmake -P cmake_minimum_required(VERSION 3.14) project(Compiler) @@ -20,7 +21,7 @@ set(INT 1) set(LONG 1) # Uncomment for testing -add_compile_definitions("_TESTING") +#add_compile_definitions("_TESTING") # DO NOT TOUCH! set(CMAKE_CXX_STANDARD 20) @@ -59,4 +60,4 @@ if (LONG EQUAL 1) endif () # Compilation of sources to object to binary -add_executable(Compiler main.cpp AST/Token.cpp AST/Token.h Scanner.cpp Scanner.h Parser.cpp Parser.h AST/AST.h AST/Operate.cpp AST/Operate.h AST/Expression.cpp AST/Expression.h AST/PrimaryExpression_Expression.cpp AST/PrimaryExpression_Expression.h AST/Terminal.cpp AST/Terminal.h AST/Identifier.cpp AST/Identifier.h AST/AST.cpp AST/Program.cpp AST/Program.h AST/Command.cpp AST/Command.h AST/CommandIf.cpp AST/CommandIf.h AST/CommandAssign.cpp AST/CommandAssign.h AST/CommandLet.cpp AST/CommandLet.h AST/Declaration.cpp AST/Declaration.h AST/DeclarationConst.cpp AST/DeclarationConst.h AST/DeclarationVar.cpp AST/DeclarationVar.h AST/TypeDenoter.cpp AST/TypeDenoter.h AST/VarName.cpp AST/VarName.h AST/PrimaryExpressionVar.cpp AST/PrimaryExpressionVar.h AST/PrimaryExpression.cpp AST/PrimaryExpression.h) \ No newline at end of file +add_executable(Compiler main.cpp AST/Token.cpp AST/Token.h Scanner.cpp Scanner.h Parser.cpp Parser.h AST/AST.h AST/Operate.cpp AST/Operate.h AST/Expression.cpp AST/Expression.h AST/PrimaryExpression_Expression.cpp AST/PrimaryExpression_Expression.h AST/Terminal.cpp AST/Terminal.h AST/Identifier.cpp AST/Identifier.h AST/AST.cpp AST/Program.cpp AST/Program.h AST/Command.cpp AST/Command.h AST/CommandIf.cpp AST/CommandIf.h AST/CommandAssign.cpp AST/CommandAssign.h AST/CommandLet.cpp AST/CommandLet.h AST/Declaration.cpp AST/Declaration.h AST/DeclarationConst.cpp AST/DeclarationConst.h AST/DeclarationVar.cpp AST/DeclarationVar.h AST/TypeDenoter.cpp AST/TypeDenoter.h AST/VarName.cpp AST/VarName.h AST/PrimaryExpressionVar.cpp AST/PrimaryExpressionVar.h AST/PrimaryExpression.cpp AST/PrimaryExpression.h) diff --git a/Parser.cpp b/Parser.cpp index bca98a4..2f7a578 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -84,7 +84,7 @@ void Parser::nextToken(TokenType type) { if (currentToken->matchesType(type)) { loadNextToken(); } else { - fprintf(stdout, ANSI_COLOR_RED "FATAL: Compilation Error: Expected \'%s\' but found \'%s\' \n" ANSI_COLOR_RESET, + fprintf(stdout, ANSI_COLOR_RED "\nFATAL: Compilation Error: Expected \'%s\' but found \'%s\' \n" ANSI_COLOR_RESET, Token::tokenDesc(type).data(), Token::tokenDesc(currentToken->getType()).data()); exit(1); } @@ -173,7 +173,7 @@ Command *Parser::parseCommand() { int c = var_table.count(varName->describe()); if (c == 0) { - fprintf(stdout, ANSI_COLOR_RED "FATAL: Undeclared variable: %s in:\n%s\n" ANSI_COLOR_RESET, + fprintf(stdout, ANSI_COLOR_RED "\nFATAL: Undeclared variable: %s in:\n%s\n" ANSI_COLOR_RESET, varName->describe().data(), expression->describe().data()); exit(1); } @@ -192,7 +192,7 @@ Command *Parser::parseCommand() { // Check data type match if (var->type->describe() != expression->getType()) { fprintf(stderr, - ANSI_COLOR_RED "FATAL: Incompatible types: \nVariable \'%s\' [Type \'%s\']\nExpression: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, + ANSI_COLOR_RED "\nFATAL: Incompatible types: \nVariable \'%s\' [Type \'%s\']\nExpression: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, var->name->describe().data(), var->type->describe().data(), expression->describe().data(), expression->getType().data()); exit(1); } @@ -200,7 +200,7 @@ Command *Parser::parseCommand() { // Check if const if (var->isConst) { fprintf(stdout, - ANSI_COLOR_RED "FATAL: \'%s\' is a constant. You cannot alter it once declared.\n" ANSI_COLOR_RESET, + ANSI_COLOR_RED "\nFATAL: \'%s\' is a constant. You cannot alter it once declared.\n" ANSI_COLOR_RESET, varName->describe().data()); exit(1); } @@ -221,7 +221,7 @@ Expression *Parser::parseExpression() { PrimaryExpression *p1 = parsePrimaryExpression(); std::string p1Type = p1->getType(); if (p1Type.length() == 0) { - fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, + fprintf(stderr, ANSI_COLOR_RED "\nFATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, p1->describe().data(), p1Type.data()); exit(1); } @@ -240,7 +240,7 @@ Expression *Parser::parseExpression() { PrimaryExpression *p2 = parsePrimaryExpression(); std::string p2Type = p2->getType(); if (p2Type.length() == 0) { - fprintf(stderr, ANSI_COLOR_RED "FATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, + fprintf(stderr, ANSI_COLOR_RED "\nFATAL: Unknown type for variable: \'%s\' [Type \'%s\']\n" ANSI_COLOR_RESET, p2->describe().data(), p2Type.data()); exit(1); } @@ -255,7 +255,7 @@ Expression *Parser::parseExpression() { // Check + is used with strings or chars if (p2Type == p1Type && (p1Type == "STRING" || p1Type == "CHAR") && o1->describe() != "+") { fprintf(stderr, - ANSI_COLOR_RED "FATAL: Invalid Operator \'%s\' for Strings \'%s\' and \'%s\'\n" ANSI_COLOR_RESET, + ANSI_COLOR_RED "\nFATAL: Invalid Operator \'%s\' for Strings \'%s\' and \'%s\'\n" ANSI_COLOR_RESET, o1->describe().data(), p1->describe().data(), p2->describe().data()); exit(1); } @@ -263,7 +263,7 @@ Expression *Parser::parseExpression() { // Check incompatible types if (p1Type != p2Type) { fprintf(stderr, - ANSI_COLOR_RED "FATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, + ANSI_COLOR_RED "\nFATAL: Incompatible types: \nP1: %s [Type \'%s\']\nP2: %s [Type \'%s\']\n" ANSI_COLOR_RESET, p1->describe().data(), p1Type.data(), p2->describe().data(), p2Type.data()); exit(1); } @@ -440,7 +440,7 @@ TypeDenoter *Parser::parseTypeDenoter() { return new TypeDenoter(Scanner::toUpper(type)); } - fprintf(stderr, "FATAL: Unknown data type \'%s\' is not defined!", type.data()); + fprintf(stderr, "\nFATAL: Unknown data type \'%s\' is not defined!", type.data()); exit(1); } @@ -478,7 +478,9 @@ Operate *Parser::parseOperator() { Parser::~Parser() { delete currentToken; - delete program; + if (program != nullptr) { + delete program; + } } void Parser::openScope(vardef_t *vardef) { diff --git a/main.cpp b/main.cpp index ae949db..8ce3042 100644 --- a/main.cpp +++ b/main.cpp @@ -11,9 +11,77 @@ #define ANSI_COLOR_CYAN "\x1b[36m" #define ANSI_COLOR_RESET "\x1b[0m" -int main() { +void loadExamples(std::vector *container); + +void showHelp(); + +void printCodeExamples(std::vector vector); + +int main(int argc, char *argv[]) { Parser *p = nullptr; std::string code; + std::vector codeExamples; + + // Check if we go to interactive mode. + if (argc != 1) { + + // Load testing examples + loadExamples(&codeExamples); + + for (int v = 1; v < argc; ++v) { + std::string param(argv[v]); + if (param == "-v" || param == "--verbosity") { + v += 1; + int verb(atoi(argv[v])); + printf(ANSI_COLOR_GREEN "Verbosity Set: %d\n" ANSI_COLOR_RESET, verb); + + switch (verb) { + case 0: + case 1: { +#ifdef _VERBOSITY +#undef _VERBOSITY +#define _VERBOSITY 1 +#endif + break; + } + + case 2: { +#ifdef _VERBOSITY +#undef _VERBOSITY +#define _VERBOSITY 2 +#endif + break; + } + + default: { +#ifdef _VERBOSITY +#undef _VERBOSITY +#define _VERBOSITY 3 +#endif + break; + } + } + } else if (param == "-h" || param == "--help") { + showHelp(); + exit(0); + } else if (param == "-l" || param == "--list-examples") { + printCodeExamples(codeExamples); + exit(0); + } else if (param == "-t" || param == "--test") { + v += 1; + int index = atoi(argv[v]) - 1; + if(index > codeExamples.size() || index < 0) { + fprintf(stderr, ANSI_COLOR_RED "Invalid Test Code. Please select in the range [0-%lu]\n", codeExamples.size()); + } else { + fprintf(stderr, ANSI_COLOR_RED ANSI_COLOR_CYAN "Testing Code String [%d]\n" ANSI_COLOR_RESET, index); + code = codeExamples[index]; + } + } else { + code += param + " "; + } + } + } + std::printf("=====================\nRobot Compiler (%d.%d)\n=====================\n", _VERSION_MAJOR, _VERSION_MINOR); @@ -24,45 +92,12 @@ int main() { while (!std::regex_match(code, regex)) { while (code.empty()) { std::printf("\n>>> "); -#ifndef _TESTING std::getline(std::cin, code); -#else - // normal - let command - code = "let var t: int in t := 1+1"; - - // normal - if statement with let statements -// code = "if(true+true) then let var e: int in e:=e+1 else let var z: int in z:=z+2"; - - // syntax issue - missing in keyword -// code = "let var t: int t := t + t"; - - // test string <-> int incompatibility -// code = "let var t: int in t := t + abc"; - - // test int <-> int incompatibility, assignment, let command -// code = "let var t: int in t := t + 111"; - - // shows breaking of language example if statement requires Commands (i.e. assiignemnt, ifstatement or let) -// code = "if(true+true) then x+x else z+z"; - - // shows nested let commands with duplicate variables -// code = "let var e: int in let var e: int in e:=e+e"; - - // shows incompatible data types -// code = "if(true+true) then x:=x+1 else z:=z+2"; - - // shows undeclared variable -// code = "if(true+true) then x:=z+z else z:=z+2"; - - // shows incompatible data types -// code = "let const e ~ 1+1 in e:=x+y"; - - // shows editing of const -// code = "let const e ~ 1+1 in e:=2+2"; -#endif } - fprintf(stdout, "Compiling:\n---------------------------------------------\n\t" ANSI_COLOR_MAGENTA " \'%s\'" ANSI_COLOR_RESET " \n---------------------------------------------\n", code.data()); + fprintf(stdout, + "Compiling:\n---------------------------------------------\n\t" ANSI_COLOR_MAGENTA " \'%s\'" ANSI_COLOR_RESET " \n---------------------------------------------\n", + code.data()); if (_VERBOSITY >= 1) fprintf(stdout, ANSI_COLOR_BLUE "Compiling, please wait...\n" ANSI_COLOR_RESET); @@ -78,10 +113,62 @@ int main() { fprintf(stderr, ANSI_COLOR_RED "\nFailed!\n" ANSI_COLOR_RESET); } code.clear(); -#ifdef _TESTING + exit(0); -#endif } return 0; +} + +void printCodeExamples(std::vector vector) { + fprintf(stdout, "Code Test Examples:\n"); + for (int i = 0; i < vector.size(); ++i) { + fprintf(stdout, "[%d] \t" ANSI_COLOR_YELLOW " %s\n" ANSI_COLOR_RESET, i, vector.at(i).data()); + } +} + + +void loadExamples(std::vector *container) { + // normal - let command + container->emplace_back("let var t: int in t := 1+1"); + + // normal - if statement with let statements + container->emplace_back("if(true+true) then let var e: int in e:=e+1 else let var z: int in z:=z+2"); + + // syntax issue - missing in keyword + container->emplace_back("let var t: int t := t + t"); + + // test string <-> int incompatibility + container->emplace_back("let var t: int in t := t + abc"); + + // test int <-> int incompatibility, assignment, let command + container->emplace_back("let var t: int in t := t + 111"); + + // shows breaking of language example if statement requires Commands (i.e. assiignemnt, ifstatement or let) + container->emplace_back("if(true+true) then x+x else z+z"); + + // shows nested let commands with duplicate variables + container->emplace_back("let var e: int in let var e: int in e:=e+e"); + + // shows incompatible data types + container->emplace_back("if(true+true) then x:=x+1 else z:=z+2"); + + // shows undeclared variable + container->emplace_back("if(true+true) then x:=z+z else z:=z+2"); + + // shows incompatible data types + container->emplace_back("let const e ~ 1+1 in e:=x+y"); + + // shows editing of const + container->emplace_back("let const e ~ 1+1 in e:=2+2"); + +} + + +void showHelp() { + fprintf(stdout, "Robot Compiler - Help\n\n"); + fprintf(stdout, "\t-h | --help\t\t\tDisplay this help list\n"); + fprintf(stdout, "\t-t | --test [id]\t\t\tTest a specific code string. To find test code strings, use '--list-examples' flag'\n"); + fprintf(stdout, "\t-l | --list-examples\tShows a list of test code strings to run with the compiler.\n"); + fprintf(stdout, "\t-v | --verbosity [value]\t\tSet the debug verbosity\n\t\t1\tBasic messages\n\t\t2\tCompiler Process Messages\n\t\t3\tAll compiler messages including token processing."); } \ No newline at end of file From 8148627d6504ed052a734b2a8126515a02509e1d Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Sun, 23 Jun 2019 21:05:07 +0200 Subject: [PATCH 6/7] Create ReadMe.md --- ReadMe.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 ReadMe.md diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..ad9aafc --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,71 @@ +# Robot Compiler + +## What is it? +A simple compiler that can compile an single line application using Pascal syntax. + +### Examples lines to compile: + +* Let Statement +``` +let + var t: int +in + t := 1+1 +``` +* If Statement +``` + if(true+true) then + let + var e: int + in + e:=e+1 + else + let + var z: int + in + z:=z+2 +``` + +## Under the hood + +The compiler implements: +* Syntax Analysis: reads the input code (list of tokens), scans and parses these tokens to build the [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) +* Contextual Analysis: A very simple implementation for scope checking of variables + +**Program does not require spaces between tokens** + +## Production Rules Implemented +*Production rules is shown in [Backus-Naur Form (BNF)](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form)* + +| Non-Terminal Symbols | | Expressions | +|------------------|-----|-----------------------------------------------------| +| Program | ::= | Command | +| Command | ::= | if PrimaryExpression then Command else Command | +| | | V-name := Expression | +| | | let Declaration in Command | +| Expression | ::= | PrimaryExpression Operator PrimaryExpression | +| Declaration | ::= | var Identifier: Type-Denoter | +| | | const Identifier ~ Expression | +| PrimaryExpression| ::= | V-name | +| | ::= | ( Expression ) | +| Type-Denoter | ::= | Identifier | +| V-name | ::= | Identifier | +| Identifier | ::= | Literal | +| | | Digit Literal | +| | | Literal Digit | +| Operator | ::= | + | +| | | - | +| | | * | +| | | / | + +*Note: Identifier implemented as std::string (no distinction made between digits and chars)* + +## Future Work +* Add more detailed production rules looking at literals and digits +* Add visual representation of tree built +* Add more completed production rules allowing for multi-line code blocks +* Add files to pass in and compile +* Add ability to run compiled files using Code Generation + +## Contributing? +You are more than welcome to submit PR or collaborate :) From e0ed32e0fc913f702c18db18f4be5b4e66a30177 Mon Sep 17 00:00:00 2001 From: Charles Dyason Date: Sun, 23 Jun 2019 21:05:54 +0200 Subject: [PATCH 7/7] Update ReadMe.md --- ReadMe.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ReadMe.md b/ReadMe.md index ad9aafc..ffca567 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,5 +1,7 @@ # Robot Compiler +What is with the name? I have no idea. If you have a better one, please let me know :) + ## What is it? A simple compiler that can compile an single line application using Pascal syntax.