From f565918f4cf9618202f75f28b94669de29f9cfe9 Mon Sep 17 00:00:00 2001 From: dantheking-crypto Date: Thu, 29 Oct 2020 11:16:32 -0700 Subject: [PATCH 01/19] Update c-cpp.yml --- .github/workflows/c-cpp.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 9801aa5..dcbfc7c 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -3,6 +3,8 @@ name: C/C++ CI on: pull_request: branches: [ master, develop, feature/* ] + push: + branches: [master] jobs: compile: From e67742a1aabb7cafb26f80278c450d50ab49874a Mon Sep 17 00:00:00 2001 From: dantheking-crypto Date: Thu, 29 Oct 2020 11:21:49 -0700 Subject: [PATCH 02/19] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 257293e..2b01287 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Head over to the "Actions" tab at the top of the repo. Make sure that your branch is set to "master" for the latest release build. In other words, you should enter this into the searchbar:
`workflow:C/C++ branch:master`
Click on the link at the top.
-In the Artifacts section, click on "spark". You may get a warning, but I assure you, this code has no viruses. Press the arrow on the right and press "Keep". Open the zip file - you should see a .exe file called "spark". Drag this into the folder where you want your spark projects to be.
+In the Artifacts section, click on the os that you use: a .zip file should start downloading. You may get a warning, but I assure you, this code has no viruses. Press the arrow on the right and press "Keep". Open the zip file and drag the file in the zip folder into the folder where you want your spark projects to be.
### Running: Windows -Navigate to your project folder and do Shift+Right-click. Press "Open PowerShell window here." Now run ./spark \. Voila! +Navigate to your project folder and do Shift+Right-click. Press "Open PowerShell window here." Now run ./spark-windows \. Voila! +### Running: Mac +Open Terminal and drag and drop your project folder into the terminal. This should navigate the terminal to your project folder. Now, just run ./spark-macos-latest \, and it should run your code. From 5f48da1943e33c5402994b2f73fcc388d46cbef3 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Thu, 29 Oct 2020 13:04:36 -0700 Subject: [PATCH 03/19] Updated workflow --- .github/workflows/c-cpp.yml | 2 +- .github/workflows/spark.yml | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/spark.yml diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 1dc2fe6..c7d4744 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -1,4 +1,4 @@ -name: C/C++ CI +name: Compile and Run Spark Program on: pull_request: diff --git a/.github/workflows/spark.yml b/.github/workflows/spark.yml new file mode 100644 index 0000000..1dc2fe6 --- /dev/null +++ b/.github/workflows/spark.yml @@ -0,0 +1,50 @@ +name: C/C++ CI + +on: + pull_request: + branches: [ master, develop, feature/* ] + push: + branches: [master] + +jobs: + windows: + name: run on windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: ilammy/msvc-dev-cmd@v1.4.1 + - name: compile + run: | + cl main.cpp /Fe:spark-windows /std:c++latest + - name: run + run: | + spark-windows.exe test.spark + shell: cmd + - name: upload executable + uses: actions/upload-artifact@v2 + with: + name: windows + path: | + spark-windows.exe + mac-and-ubuntu: + runs-on: ${{matrix.os}} + strategy: + matrix: + os: [macos-latest, ubuntu-latest] + name: ${{matrix.os}} + steps: + - uses: actions/checkout@v2 + - name: compile + run: | + g++ -std=c++17 main.cpp -o spark-${{matrix.os}} + - name: run + run: | + ./spark-${{matrix.os}} test.spark + shell: bash + - name: upload executable + uses: actions/upload-artifact@v2 + with: + name: ${{matrix.os}} + path: | + spark-${{matrix.os}} + From 2756007744bf570b74370ad6a3e4b758183a8ada Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Thu, 29 Oct 2020 13:05:50 -0700 Subject: [PATCH 04/19] Removed extraneous file --- .github/workflows/c-cpp.yml | 50 ------------------------------------- .github/workflows/spark.yml | 2 +- 2 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml deleted file mode 100644 index c7d4744..0000000 --- a/.github/workflows/c-cpp.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Compile and Run Spark Program - -on: - pull_request: - branches: [ master, develop, feature/* ] - push: - branches: [master] - -jobs: - windows: - name: run on windows - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: ilammy/msvc-dev-cmd@v1.4.1 - - name: compile - run: | - cl main.cpp /Fe:spark-windows /std:c++latest - - name: run - run: | - spark-windows.exe test.spark - shell: cmd - - name: upload executable - uses: actions/upload-artifact@v2 - with: - name: windows - path: | - spark-windows.exe - mac-and-ubuntu: - runs-on: ${{matrix.os}} - strategy: - matrix: - os: [macos-latest, ubuntu-latest] - name: ${{matrix.os}} - steps: - - uses: actions/checkout@v2 - - name: compile - run: | - g++ -std=c++17 main.cpp -o spark-${{matrix.os}} - - name: run - run: | - ./spark-${{matrix.os}} test.spark - shell: bash - - name: upload executable - uses: actions/upload-artifact@v2 - with: - name: ${{matrix.os}} - path: | - spark-${{matrix.os}} - diff --git a/.github/workflows/spark.yml b/.github/workflows/spark.yml index 1dc2fe6..c7d4744 100644 --- a/.github/workflows/spark.yml +++ b/.github/workflows/spark.yml @@ -1,4 +1,4 @@ -name: C/C++ CI +name: Compile and Run Spark Program on: pull_request: From 9c4bc4c7377c261d40ac2447b775fcf9a5b6c1c4 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Thu, 29 Oct 2020 16:07:47 -0700 Subject: [PATCH 05/19] Updated .gitignore and parser Now, functions can take formal parameters (that don't do anything yet). --- .gitignore | 4 +++- Parser/astnodes.h | 4 +++- Parser/parser.h | 16 ++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 3a8c69e..83b4018 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ Debug/* .vs/* *.sln *.vcxproj.* -*.vcxproj \ No newline at end of file +*.vcxproj +spark.exe +*.obj \ No newline at end of file diff --git a/Parser/astnodes.h b/Parser/astnodes.h index 262ef19..eee2dff 100644 --- a/Parser/astnodes.h +++ b/Parser/astnodes.h @@ -171,9 +171,11 @@ class Block : public AstNode { class ProcedureDecl : public AstNode { public: Block block; + std::vector params; std::string name; - ProcedureDecl(std::string n, Block b) : block(b) { + ProcedureDecl(std::string n, Block b, std::vector p) : block(b) { name = n; + params = p; } std::string print() { return "ProcedureDecl"; diff --git a/Parser/parser.h b/Parser/parser.h index 5b10e54..f6b49f8 100644 --- a/Parser/parser.h +++ b/Parser/parser.h @@ -70,12 +70,25 @@ class Parser { while (currentToken.type == INT || currentToken.type == REAL || currentToken.type == STRING) { std::vector line = parseVarDeclarations(); + eat(Semicolon); decl.insert(decl.end(), line.begin(), line.end()); } } return decl; } + std::vector parseFormalParameters() { + std::vector decl; + while (currentToken.type == INT || currentToken.type == REAL || currentToken.type == STRING) + { + std::vector line = parseVarDeclarations(); + decl.insert(decl.end(), line.begin(), line.end()); + if (currentToken.type != Semicolon) break; + else eat(Semicolon); + } + + return decl; + } std::vector parseFunctions() { std::vector declarations; while (currentToken.type == FUNCTION) { @@ -85,7 +98,7 @@ class Parser { eat(LeftParenthesis); eat(RightParenthesis); Block block = parseBlock(); - ProcedureDecl* proc_decl = new ProcedureDecl(proc_name, block); + ProcedureDecl* proc_decl = new ProcedureDecl(proc_name, block, parseFormalParameters()); declarations.push_back(proc_decl); } return declarations; @@ -101,7 +114,6 @@ class Parser { declarations.push_back(Var(currentToken)); eat(Identifier); } - eat(Semicolon); std::vector var_decl(declarations.size()); for (unsigned int i = 0; i < var_decl.size(); i++) { VarDecl varDecl(type, declarations[i]); From e510cb105ea6a507c0856137742c0ece7912282c Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Fri, 30 Oct 2020 08:29:37 -0700 Subject: [PATCH 06/19] Renamed pipeline --- .github/workflows/{spark.yml => compile.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{spark.yml => compile.yml} (100%) diff --git a/.github/workflows/spark.yml b/.github/workflows/compile.yml similarity index 100% rename from .github/workflows/spark.yml rename to .github/workflows/compile.yml From a3aeeca66814212770027c212a2f10fef3871312 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Fri, 30 Oct 2020 08:30:32 -0700 Subject: [PATCH 07/19] Updated pipeline --- .github/workflows/compile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index c7d4744..831d232 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -1,4 +1,4 @@ -name: Compile and Run Spark Program +name: Compile Spark on: pull_request: From 318b1906f01b08b670adf355c6460c41a6f148d7 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Fri, 30 Oct 2020 10:32:44 -0700 Subject: [PATCH 08/19] Updated parseFunction() Parse params between the parentheses, not after. --- Parser/parser.h | 3 ++- test.spark | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Parser/parser.h b/Parser/parser.h index f6b49f8..f9feff3 100644 --- a/Parser/parser.h +++ b/Parser/parser.h @@ -96,9 +96,10 @@ class Parser { std::string proc_name = currentToken.value; eat(Identifier); eat(LeftParenthesis); + std::vector params = parseFormalParameters(); eat(RightParenthesis); Block block = parseBlock(); - ProcedureDecl* proc_decl = new ProcedureDecl(proc_name, block, parseFormalParameters()); + ProcedureDecl* proc_decl = new ProcedureDecl(proc_name, block, params); declarations.push_back(proc_decl); } return declarations; diff --git a/test.spark b/test.spark index 2c2eb20..9f626b0 100644 --- a/test.spark +++ b/test.spark @@ -3,7 +3,7 @@ int: a, c, d; real: b; string: test, multi; - def foo () { + def foo (int: bar) { program: } program: #this is a comment# From ffad627902c37470f94e0183adef713eecb3359a Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Sat, 31 Oct 2020 17:09:28 -0800 Subject: [PATCH 09/19] Updated code to use "Program" type Instead of interpreting the program as a block, I created a specific type called program. --- Interpreter/Symbols.h | 6 ++++++ Interpreter/interpreter.h | 7 ++++--- Parser/astnodes.h | 9 +++++++++ Parser/parser.h | 6 +++--- main.cpp | 14 +++++++------- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index 2f7e9b1..194a195 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -33,6 +33,12 @@ class VarSymbol : public Symbol private: +}; +class ProcedureSymbol : public Symbol { +public: + std::vector params; + ProcedureSymbol(std::string name, std::vector p) : Symbol(name), params(p) { + } }; class ScopedSymbolTable { private: diff --git a/Interpreter/interpreter.h b/Interpreter/interpreter.h index 38705e9..9ca2f66 100644 --- a/Interpreter/interpreter.h +++ b/Interpreter/interpreter.h @@ -13,6 +13,7 @@ namespace interpreter { private: Parser parser; ScopedSymbolTable symTab; + ScopedSymbolTable& currentSymTab = symTab; public: std::map> GLOBAL_SCOPE; Interpreter(std::string input) : parser(input), symTab("global", 1) { @@ -248,14 +249,14 @@ namespace interpreter { } void visit_NoOp() {} void interpret() { - Block block = parser.parseProgram(); + Program p = parser.parseProgram(); SymbolTableBuilder symtabBuilder("global", 1); //std::cout << "Building symtab...\n"; - symtabBuilder.visit(&block); + symtabBuilder.visit(&p.block); //std::cout << "Finished building symtab...\n"; symTab = symtabBuilder.symtab; //std::cout << "Interpreting...\n"; - visit_Block(block); + visit_Block(p.block); } }; } \ No newline at end of file diff --git a/Parser/astnodes.h b/Parser/astnodes.h index eee2dff..193b7af 100644 --- a/Parser/astnodes.h +++ b/Parser/astnodes.h @@ -168,6 +168,15 @@ class Block : public AstNode { return s; } }; +class Program : public AstNode { +public: + Block block; + Program(Block b) : block(b) {} + Program() {} + std::string print() { + return "Program"; + } +}; class ProcedureDecl : public AstNode { public: Block block; diff --git a/Parser/parser.h b/Parser/parser.h index f9feff3..f1c7bcb 100644 --- a/Parser/parser.h +++ b/Parser/parser.h @@ -17,12 +17,12 @@ class Parser { if (currentToken.type == type) currentToken = lexer.nextToken(); else error(type, currentToken.type); } - Block parseProgram() { + Program parseProgram() { eat(MAIN); eat(LeftParenthesis); eat(RightParenthesis); - return parseBlock(); - + Program p(parseBlock()); + return p; } Block parseBlock() { eat(LeftBracket); diff --git a/main.cpp b/main.cpp index 508d7d3..0bcc16b 100644 --- a/main.cpp +++ b/main.cpp @@ -26,14 +26,14 @@ int interpret(string input) { vector tokens = lexer.allTokens(); // tokens.size() is a long unsigned int, use to prevent -Wall or -Wextra warnings - cout << "Lexer tokens:" << endl; + /* cout << "Lexer tokens:" << endl; for (long unsigned int i = 0; i < tokens.size(); i++) { cout << tokens[i]; } Parser parser(input); - Block block; + Program program; try { - block = parser.parseProgram(); + program = parser.parseProgram(); } catch (string error) { cout << error << "\n"; @@ -42,9 +42,9 @@ int interpret(string input) { cout << "Parser blocks:" << endl; for (int i = 0; i < block.size(); i++) { cout << block[i]->print() << endl; - } + }*/ Interpreter interpreter(input); - cout << "Interpreting...\n"; + // cout << "Interpreting...\n"; try { interpreter.interpret(); } @@ -53,7 +53,7 @@ int interpret(string input) { return 1; } - cout << "Variables: " << endl; + /*cout << "Variables: " << endl; ScopedSymbolTable symtab = interpreter.getSymTab(); cout << symtab.print(); for (auto const& pair : interpreter.GLOBAL_SCOPE) { @@ -69,7 +69,7 @@ int interpret(string input) { cout << std::get(pair.second); } cout << endl; - } + }*/ return 0; } int main (int argc, char** argv) { From e0097d3617249e9df7889864d17c7ee6258b889a Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Sun, 1 Nov 2020 16:16:49 -0800 Subject: [PATCH 10/19] Added scopes! Procedures will now have a separate scope from the global one. --- Interpreter/Symbols.h | 56 ++++++++++++++++++++++++++++++++------- Interpreter/interpreter.h | 9 +++++-- test.spark | 1 + 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index 194a195..dc4b4e2 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -36,8 +36,10 @@ class VarSymbol : public Symbol }; class ProcedureSymbol : public Symbol { public: - std::vector params; - ProcedureSymbol(std::string name, std::vector p) : Symbol(name), params(p) { + std::vector params; + ProcedureSymbol(std::string name, std::vector p) : Symbol(name), params(p) { + } + ProcedureSymbol(std::string name) : Symbol(name) { } }; class ScopedSymbolTable { @@ -45,6 +47,7 @@ class ScopedSymbolTable { std::string scope_name; int scope_level; public: + ScopedSymbolTable* enclosingScope = NULL; std::map symbols; ScopedSymbolTable(std::string name, int level) { define(new BuiltinTypeSymbol("int")); @@ -82,20 +85,53 @@ class ScopedSymbolTable { class SymbolTableBuilder { public: ScopedSymbolTable symtab; - SymbolTableBuilder(std::string name, int level) : symtab(name, level) { - + ScopedSymbolTable& currentScope; + SymbolTableBuilder(std::string name, int level) : symtab("global", 1), currentScope(symtab) { } void visit(AstNode * node) { - if (node->print() == "Block") visit_Block(*static_cast(node)); + if (node->print() == "Program") visit_Program(*static_cast(node)); + else if (node->print() == "ProcedureDecl") visit_ProcedureDecl(*static_cast(node)); + else if (node->print() == "Block") visit_Block(*static_cast(node)); else if (node->print() == "UnOp") visit_UnOp(*static_cast(node)); else if (node->print() == "Var") visit_Var(*static_cast(node)); else if (node->print() == "BinOp") return visit_BinOp(*static_cast(node)); else if (node->print() == "Assign") visit_Assign(*static_cast(node)); } + void visit_Program(Program program) { + cout << "Enter scope: GLOBAL\n"; + ScopedSymbolTable globalScope("global", 1); + currentScope = globalScope; + visit_Block(program.block); + cout << currentScope.print(); + cout << "Leave scope: GLOBAL\n"; + } + void visit_ProcedureDecl(ProcedureDecl proc) { + ProcedureSymbol* proc_symbol = new ProcedureSymbol(proc.name); + currentScope.define(proc_symbol); + cout << "Enter scope: " << proc.name << endl; + ScopedSymbolTable proc_scope(proc.name, 2); + ScopedSymbolTable* enclosing = new ScopedSymbolTable(currentScope); + proc_scope.enclosingScope = enclosing; + currentScope = proc_scope; + for (VarDecl param : proc.params) { + BuiltinTypeSymbol* type = static_cast(currentScope.lookup(param.type.type)); + std::string param_name = param.var.value; + Symbol* var = new VarSymbol(param_name, type); + currentScope.define(var); + VarSymbol var_symbol(param_name, type); + proc_symbol->params.push_back(var_symbol); + } + visit_Block(proc.block); + cout << currentScope.print(); + currentScope = *proc_scope.enclosingScope; + } void visit_Block(Block block) { for (VarDecl decl : block.declarations) { visit_VarDecl(decl); } + for (ProcedureDecl* decl : block.procedures) { + visit(decl); + } for (AstNode* node : block.children) { visit(node); } @@ -109,7 +145,7 @@ class SymbolTableBuilder { } void visit_Assign(class Assign assign) { std::string var_name = assign.var.value; - Symbol* var_symbol = symtab.lookup(var_name); + Symbol* var_symbol = currentScope.lookup(var_name); if (var_symbol->name == "") { std::string error = "Error: variable not defined: " + var_name; throw error; @@ -118,7 +154,7 @@ class SymbolTableBuilder { } void visit_Var(Var var) { std::string var_name = var.value; - Symbol* var_symbol = symtab.lookup(var_name); + Symbol* var_symbol = currentScope.lookup(var_name); if (var_symbol->name == "") { std::string error = "Error: variable not defined: " + var_name; throw error; @@ -126,13 +162,13 @@ class SymbolTableBuilder { } void visit_VarDecl(VarDecl varDecl) { std::string type_name = varDecl.type.type; - BuiltinTypeSymbol* type_symbol = static_cast(symtab.lookup(type_name)); + BuiltinTypeSymbol* type_symbol = static_cast(currentScope.lookup(type_name)); std::string var_name = varDecl.var.value; - if (symtab.lookup(var_name)->name != "") { + if (currentScope.lookup(var_name)->name != "") { std::string error("Multiple definition of variable " + var_name); throw error; } VarSymbol* var = new VarSymbol(var_name, type_symbol); - symtab.define(var); + currentScope.define(var); } }; \ No newline at end of file diff --git a/Interpreter/interpreter.h b/Interpreter/interpreter.h index 9ca2f66..258a0f4 100644 --- a/Interpreter/interpreter.h +++ b/Interpreter/interpreter.h @@ -252,9 +252,14 @@ namespace interpreter { Program p = parser.parseProgram(); SymbolTableBuilder symtabBuilder("global", 1); //std::cout << "Building symtab...\n"; - symtabBuilder.visit(&p.block); + try { + symtabBuilder.visit(&p); + } + catch (std::string error) { + cout << error << endl; + } //std::cout << "Finished building symtab...\n"; - symTab = symtabBuilder.symtab; + symTab = symtabBuilder.currentScope; //std::cout << "Interpreting...\n"; visit_Block(p.block); } diff --git a/test.spark b/test.spark index 9f626b0..761fa11 100644 --- a/test.spark +++ b/test.spark @@ -5,6 +5,7 @@ string: test, multi; def foo (int: bar) { program: + bar = 5; } program: #this is a comment# a = 1*1+4//-2; From 1db0ec47981d59aa9df91fb6c25eb8caec2d9971 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Sun, 1 Nov 2020 16:19:56 -0800 Subject: [PATCH 11/19] Scopes with enclosing scope variables Scopes can now access variables from the enclosing scopes. --- Interpreter/Symbols.h | 3 ++- test.spark | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index dc4b4e2..5fdec80 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -66,7 +66,8 @@ class ScopedSymbolTable { return symbols[name]; } else { - return new Symbol(); + if (enclosingScope != NULL) return enclosingScope->lookup(name); + else return new Symbol(); } } std::string print() { diff --git a/test.spark b/test.spark index 761fa11..8766923 100644 --- a/test.spark +++ b/test.spark @@ -4,8 +4,11 @@ real: b; string: test, multi; def foo (int: bar) { - program: - bar = 5; + def proc (int: asdf) { + program: asdf = a; + } + program: + bar = a; } program: #this is a comment# a = 1*1+4//-2; From 3d0748e281da376533aaae7f072a4a0bb01a01e7 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Sun, 1 Nov 2020 16:38:45 -0800 Subject: [PATCH 12/19] Updated error handling --- Interpreter/interpreter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Interpreter/interpreter.h b/Interpreter/interpreter.h index 258a0f4..114c03c 100644 --- a/Interpreter/interpreter.h +++ b/Interpreter/interpreter.h @@ -257,6 +257,7 @@ namespace interpreter { } catch (std::string error) { cout << error << endl; + return; } //std::cout << "Finished building symtab...\n"; symTab = symtabBuilder.currentScope; From fda1c74518a4780c38fbe53b3644adea63b98403 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Sun, 1 Nov 2020 18:01:23 -0800 Subject: [PATCH 13/19] Cleaned up output and updated pipeline --- .github/workflows/compile.yml | 10 +++++----- Interpreter/Symbols.h | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 831d232..64ea13e 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -15,7 +15,7 @@ jobs: - uses: ilammy/msvc-dev-cmd@v1.4.1 - name: compile run: | - cl main.cpp /Fe:spark-windows /std:c++latest + cl main.cpp /Fe:spark /std:c++latest - name: run run: | spark-windows.exe test.spark @@ -25,7 +25,7 @@ jobs: with: name: windows path: | - spark-windows.exe + spark.exe mac-and-ubuntu: runs-on: ${{matrix.os}} strategy: @@ -36,15 +36,15 @@ jobs: - uses: actions/checkout@v2 - name: compile run: | - g++ -std=c++17 main.cpp -o spark-${{matrix.os}} + g++ -std=c++17 main.cpp -o spark - name: run run: | - ./spark-${{matrix.os}} test.spark + ./spark test.spark shell: bash - name: upload executable uses: actions/upload-artifact@v2 with: name: ${{matrix.os}} path: | - spark-${{matrix.os}} + spark diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index 5fdec80..19b0a7b 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -99,17 +99,17 @@ class SymbolTableBuilder { else if (node->print() == "Assign") visit_Assign(*static_cast(node)); } void visit_Program(Program program) { - cout << "Enter scope: GLOBAL\n"; + // cout << "Enter scope: GLOBAL\n"; ScopedSymbolTable globalScope("global", 1); currentScope = globalScope; visit_Block(program.block); - cout << currentScope.print(); - cout << "Leave scope: GLOBAL\n"; + // cout << currentScope.print(); + // cout << "Leave scope: GLOBAL\n"; } void visit_ProcedureDecl(ProcedureDecl proc) { ProcedureSymbol* proc_symbol = new ProcedureSymbol(proc.name); currentScope.define(proc_symbol); - cout << "Enter scope: " << proc.name << endl; + // cout << "Enter scope: " << proc.name << endl; ScopedSymbolTable proc_scope(proc.name, 2); ScopedSymbolTable* enclosing = new ScopedSymbolTable(currentScope); proc_scope.enclosingScope = enclosing; @@ -123,7 +123,7 @@ class SymbolTableBuilder { proc_symbol->params.push_back(var_symbol); } visit_Block(proc.block); - cout << currentScope.print(); + // cout << currentScope.print(); currentScope = *proc_scope.enclosingScope; } void visit_Block(Block block) { From 2292a055c9e178436877c5bff4b35916292b1758 Mon Sep 17 00:00:00 2001 From: dantheking-crypto Date: Sun, 1 Nov 2020 18:04:57 -0800 Subject: [PATCH 14/19] Update compile.yml --- .github/workflows/compile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 64ea13e..ccdda99 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -18,7 +18,7 @@ jobs: cl main.cpp /Fe:spark /std:c++latest - name: run run: | - spark-windows.exe test.spark + spark.exe test.spark shell: cmd - name: upload executable uses: actions/upload-artifact@v2 From aae648ebdacd8171199baa542f98468913f3080e Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Sun, 1 Nov 2020 18:06:49 -0800 Subject: [PATCH 15/19] Updated scope-level initialization --- Interpreter/Symbols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index 19b0a7b..7572d9b 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -45,8 +45,8 @@ class ProcedureSymbol : public Symbol { class ScopedSymbolTable { private: std::string scope_name; - int scope_level; public: + int scope_level; ScopedSymbolTable* enclosingScope = NULL; std::map symbols; ScopedSymbolTable(std::string name, int level) { @@ -110,7 +110,7 @@ class SymbolTableBuilder { ProcedureSymbol* proc_symbol = new ProcedureSymbol(proc.name); currentScope.define(proc_symbol); // cout << "Enter scope: " << proc.name << endl; - ScopedSymbolTable proc_scope(proc.name, 2); + ScopedSymbolTable proc_scope(proc.name, currentScope.scope_level + 1); ScopedSymbolTable* enclosing = new ScopedSymbolTable(currentScope); proc_scope.enclosingScope = enclosing; currentScope = proc_scope; From 039bb191c49767e82dbf46839b508a4bad79ca67 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Mon, 9 Nov 2020 10:49:47 -0800 Subject: [PATCH 16/19] Added support for function calls However, the function calls don't do anything...yet. --- Interpreter/Symbols.h | 12 +++++++++--- Interpreter/interpreter.h | 10 +++++++++- Lexer/Lexer.h | 3 +++ Parser/astnodes.h | 24 +++++++++++++++++++----- Parser/parser.h | 26 ++++++++++++++++++++++---- test.spark | 4 +++- 6 files changed, 65 insertions(+), 14 deletions(-) diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index 7572d9b..fa759a7 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -91,13 +91,19 @@ class SymbolTableBuilder { } void visit(AstNode * node) { if (node->print() == "Program") visit_Program(*static_cast(node)); - else if (node->print() == "ProcedureDecl") visit_ProcedureDecl(*static_cast(node)); + else if (node->print() == "FunctionDecl") visit_ProcedureDecl(*static_cast(node)); + else if (node->print() == "FunctionCall") visit_FunctionCall(*static_cast(node)); else if (node->print() == "Block") visit_Block(*static_cast(node)); else if (node->print() == "UnOp") visit_UnOp(*static_cast(node)); else if (node->print() == "Var") visit_Var(*static_cast(node)); else if (node->print() == "BinOp") return visit_BinOp(*static_cast(node)); else if (node->print() == "Assign") visit_Assign(*static_cast(node)); } + void visit_FunctionCall(FunctionCall functionCall) { + for (AstNode* param : functionCall.params) { + visit(param); + } + } void visit_Program(Program program) { // cout << "Enter scope: GLOBAL\n"; ScopedSymbolTable globalScope("global", 1); @@ -106,7 +112,7 @@ class SymbolTableBuilder { // cout << currentScope.print(); // cout << "Leave scope: GLOBAL\n"; } - void visit_ProcedureDecl(ProcedureDecl proc) { + void visit_ProcedureDecl(FunctionDecl proc) { ProcedureSymbol* proc_symbol = new ProcedureSymbol(proc.name); currentScope.define(proc_symbol); // cout << "Enter scope: " << proc.name << endl; @@ -130,7 +136,7 @@ class SymbolTableBuilder { for (VarDecl decl : block.declarations) { visit_VarDecl(decl); } - for (ProcedureDecl* decl : block.procedures) { + for (FunctionDecl* decl : block.procedures) { visit(decl); } for (AstNode* node : block.children) { diff --git a/Interpreter/interpreter.h b/Interpreter/interpreter.h index 114c03c..7b817c1 100644 --- a/Interpreter/interpreter.h +++ b/Interpreter/interpreter.h @@ -47,6 +47,7 @@ namespace interpreter { else if (node->print() == "Assign") visit_Assign(*static_cast(node)); else if (node->print() == "NoOp") visit_NoOp(); else if (node->print() == "Print") visit_Print(*static_cast(node)); + else if (node->print() == "FunctionCall"); else { std::string error = "Error: void operation not recognized"; throw error; @@ -249,7 +250,14 @@ namespace interpreter { } void visit_NoOp() {} void interpret() { - Program p = parser.parseProgram(); + Program p; + try { + p = parser.parseProgram(); + } + catch (std::string error) { + cout << error << endl; + return; + } SymbolTableBuilder symtabBuilder("global", 1); //std::cout << "Building symtab...\n"; try { diff --git a/Lexer/Lexer.h b/Lexer/Lexer.h index 7fc8b3e..9f00042 100644 --- a/Lexer/Lexer.h +++ b/Lexer/Lexer.h @@ -36,6 +36,9 @@ class Lexer { int getCol () { return column; } + char getChar() { + return input[position]; + } vector allTokens () { vector tokens; Token currentToken; diff --git a/Parser/astnodes.h b/Parser/astnodes.h index 193b7af..71a7d89 100644 --- a/Parser/astnodes.h +++ b/Parser/astnodes.h @@ -146,12 +146,12 @@ class VarDecl : public AstNode { return s; } }; -class ProcedureDecl; +class FunctionDecl; class Block : public AstNode { public: std::vector children; std::vector declarations; - std::vector procedures; + std::vector procedures; Block () {} Block(std::vector d) : declarations(d) {} void append(AstNode* node) { @@ -177,17 +177,31 @@ class Program : public AstNode { return "Program"; } }; -class ProcedureDecl : public AstNode { +class FunctionDecl : public AstNode { public: Block block; std::vector params; std::string name; - ProcedureDecl(std::string n, Block b, std::vector p) : block(b) { + FunctionDecl(std::string n, Block b, std::vector p) : block(b) { name = n; params = p; } std::string print() { - return "ProcedureDecl"; + return "FunctionDecl"; + } +}; +class FunctionCall : public AstNode { +public: + std::string proc_name; + std::vector params; + Token token; + FunctionCall(std::string proc_name, std::vector params, Token token) { + this->proc_name = proc_name; + this->params = params; + this->token = token; + } + std::string print() { + return "FunctionCall"; } }; diff --git a/Parser/parser.h b/Parser/parser.h index f1c7bcb..e752602 100644 --- a/Parser/parser.h +++ b/Parser/parser.h @@ -89,8 +89,8 @@ class Parser { return decl; } - std::vector parseFunctions() { - std::vector declarations; + std::vector parseFunctions() { + std::vector declarations; while (currentToken.type == FUNCTION) { eat(FUNCTION); std::string proc_name = currentToken.value; @@ -99,11 +99,28 @@ class Parser { std::vector params = parseFormalParameters(); eat(RightParenthesis); Block block = parseBlock(); - ProcedureDecl* proc_decl = new ProcedureDecl(proc_name, block, params); + FunctionDecl* proc_decl = new FunctionDecl(proc_name, block, params); declarations.push_back(proc_decl); } return declarations; } + FunctionCall parseFunctionCall() { + Token token = currentToken; + std::string proc_name = token.value; + eat(Identifier); + eat(LeftParenthesis); + vector params; + if (currentToken.type != RightParenthesis) { + params.push_back(parseExpression()); + } + while (currentToken.type == Comma) { + eat(Comma); + params.push_back(parseExpression()); + } + eat(RightParenthesis); + FunctionCall procCall(proc_name, params, token); + return procCall; + } std::vector parseVarDeclarations() { Type type = parseType(); eat(Colon); @@ -141,7 +158,8 @@ class Parser { return node; } if (currentToken.type == Identifier) { - node = new class Assign(parseAssignment()); + if (lexer.getChar() == '(') node = new FunctionCall(parseFunctionCall()); + else node = new class Assign(parseAssignment()); return node; } else { diff --git a/test.spark b/test.spark index 8766923..079ea44 100644 --- a/test.spark +++ b/test.spark @@ -5,12 +5,14 @@ string: test, multi; def foo (int: bar) { def proc (int: asdf) { - program: asdf = a; + program: a = asdf; } program: bar = a; + proc(5); } program: #this is a comment# + foo(a); a = 1*1+4//-2; b = -13+4.5; test = 'The value of a is {a}, and the value of b is {b}'; From f00ef51adea10cb2d305be97837294e85aeb2daf Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Mon, 9 Nov 2020 10:56:16 -0800 Subject: [PATCH 17/19] Added check for parameter size vs function parameter size --- Interpreter/Symbols.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index fa759a7..cb6e29c 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -100,6 +100,10 @@ class SymbolTableBuilder { else if (node->print() == "Assign") visit_Assign(*static_cast(node)); } void visit_FunctionCall(FunctionCall functionCall) { + if (functionCall.params.size() != static_cast(symtab.lookup(functionCall.proc_name))->params.size()) { + std::string error("Function call parameters do not match function parameters."); + throw error; + } for (AstNode* param : functionCall.params) { visit(param); } From 6c3eb9c159db4919e1aa2326d3052f3df288c3d8 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Mon, 9 Nov 2020 20:21:30 -0800 Subject: [PATCH 18/19] Added activation records! --- Interpreter/Stack.h | 38 ++++++++++++++++++++++++++++++++++++++ Interpreter/interpreter.h | 26 +++++++++++++++++--------- 2 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 Interpreter/Stack.h diff --git a/Interpreter/Stack.h b/Interpreter/Stack.h new file mode 100644 index 0000000..c19d9f6 --- /dev/null +++ b/Interpreter/Stack.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include +#include +enum class ARType { + PROGRAM +}; +using value = std::variant; +class ActivationRecord { +public: + std::string name; + ARType type; + int nesting_level; + std::map members; + ActivationRecord(ARType type, int nesting_level, std::string name = "") { + this->name = name; + this->type = type; + this->nesting_level = nesting_level; + this->members = members; + } + value& operator [] (std::string index) { + return members[index]; + } +}; +class CallStack { +public: + std::vector records; + void push(ActivationRecord& ar) { + records.push_back(ar); + } + void pop() { + records.pop_back(); + } + ActivationRecord& peek() { + return records.back(); + } +}; \ No newline at end of file diff --git a/Interpreter/interpreter.h b/Interpreter/interpreter.h index 7b817c1..4fbec4e 100644 --- a/Interpreter/interpreter.h +++ b/Interpreter/interpreter.h @@ -7,6 +7,7 @@ #include #include #include +#include "./Stack.h" namespace interpreter { class Interpreter { @@ -15,7 +16,7 @@ namespace interpreter { ScopedSymbolTable symTab; ScopedSymbolTable& currentSymTab = symTab; public: - std::map> GLOBAL_SCOPE; + CallStack stack; Interpreter(std::string input) : parser(input), symTab("global", 1) { } @@ -192,16 +193,18 @@ namespace interpreter { void visit_Assign(class Assign assign) { std::string var_name = assign.var.value; std::string type = symTab.lookup(var_name)->type->name; + ActivationRecord& ar = stack.peek(); if (type == "int") { - GLOBAL_SCOPE[var_name] = visit(assign.right); + ar[var_name] = visit(assign.right); return; } else if (type == "real") { - GLOBAL_SCOPE[var_name] = visit(assign.right); + ar[var_name] = visit(assign.right); return; } else if (type == "string") { - GLOBAL_SCOPE[var_name] = visit_String(*static_cast(assign.right)); + ar[var_name] = visit_String(*static_cast(assign.right)); + return; } else { std::string error("Var type not supported"); @@ -210,10 +213,11 @@ namespace interpreter { } template T visit_Var(Var var) { - if (GLOBAL_SCOPE.find(var.value) != GLOBAL_SCOPE.end()) { + ActivationRecord& ar = stack.peek(); + if (ar.members.find(var.value) != ar.members.end()) { if constexpr (std::is_same_v) { std::string type = symTab.lookup(var.value)->type->name; - if (type == "int") return (T)std::get(GLOBAL_SCOPE[var.value]); + if (type == "int") return (T)std::get(ar[var.value]); else { std::string error("Wanted integer, got " + type); throw error; @@ -221,9 +225,9 @@ namespace interpreter { } else if constexpr (std::is_same_v) { std::string type = symTab.lookup(var.value)->type->name; - if (type == "real") return (T)std::get(GLOBAL_SCOPE[var.value]); + if (type == "real") return (T)std::get(ar[var.value]); else if (type == "int") { - return (T)std::get(GLOBAL_SCOPE[var.value]); + return (T)std::get(ar[var.value]); } else { std::string error("Wanted real, got " + type); @@ -232,7 +236,7 @@ namespace interpreter { } else if constexpr (std::is_same_v) { std::string type = symTab.lookup(var.value)->type->name; - if (type == "string") return std::get(GLOBAL_SCOPE[var.value]); + if (type == "string") return std::get(ar[var.value]); else { std::string error("Wanted string, got " + type); throw error; @@ -270,7 +274,11 @@ namespace interpreter { //std::cout << "Finished building symtab...\n"; symTab = symtabBuilder.currentScope; //std::cout << "Interpreting...\n"; + ARType t = ARType::PROGRAM; + ActivationRecord ar(t, 1); + stack.push(ar); visit_Block(p.block); + stack.pop(); } }; } \ No newline at end of file From 8ba8fec6a268d5f6e0277b63febd49abdbd5bc50 Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Wed, 11 Nov 2020 15:16:44 -0800 Subject: [PATCH 19/19] Added support for function calls! (No return values yet though) --- Interpreter/Stack.h | 3 +- Interpreter/SymbolTable.h | 153 ++++++++++++++++++++++++++++++++++++++ Interpreter/Symbols.h | 152 ------------------------------------- Interpreter/interpreter.h | 66 +++++++++++++--- Parser/astnodes.h | 13 ++++ Parser/parser.h | 2 +- main.cpp | 28 +++---- test.spark | 11 ++- 8 files changed, 243 insertions(+), 185 deletions(-) create mode 100644 Interpreter/SymbolTable.h diff --git a/Interpreter/Stack.h b/Interpreter/Stack.h index c19d9f6..267a4d9 100644 --- a/Interpreter/Stack.h +++ b/Interpreter/Stack.h @@ -4,7 +4,8 @@ #include #include enum class ARType { - PROGRAM + PROGRAM, + FUNCTION }; using value = std::variant; class ActivationRecord { diff --git a/Interpreter/SymbolTable.h b/Interpreter/SymbolTable.h new file mode 100644 index 0000000..7ba8739 --- /dev/null +++ b/Interpreter/SymbolTable.h @@ -0,0 +1,153 @@ +#pragma once +#include +#include +#include +#include "../Parser/astnodes.h" +#include "./Symbols.h" +class ScopedSymbolTable { +private: + std::string scope_name; +public: + int scope_level; + ScopedSymbolTable* enclosingScope = NULL; + std::map symbols; + ScopedSymbolTable(std::string name, int level) { + define(new BuiltinTypeSymbol("int")); + define(new BuiltinTypeSymbol("real")); + define(new BuiltinTypeSymbol("string")); + scope_name = name; + scope_level = level; + } + void define(Symbol* symbol) { + // std::// cout << "Define: " << symbol->print() << std::endl; + symbols[symbol->name] = symbol; + } + Symbol* lookup(std::string name) { + // std::// cout << "Lookup: " << name << "\n"; + if (symbols.count(name)) { + return symbols[name]; + } + else { + if (enclosingScope != NULL) return enclosingScope->lookup(name); + else return new Symbol(); + } + } + std::string print() { + std::string rep(""); + rep += "SCOPE (SCOPED SYMBOL TABLE)\n==================\n"; + rep += "Scope name: " + scope_name + "\n"; + rep += "Scope level: " + std::to_string(scope_level) + "\n"; + rep += "Contents:\n...................\n"; + for (auto pair : symbols) { + rep += pair.first + ": " + pair.second->print() + "\n"; + } + rep += "====================\n"; + return rep; + } +}; +class SymbolTableBuilder { +public: + ScopedSymbolTable symtab; + ScopedSymbolTable& currentScope; + std::map types; + SymbolTableBuilder(std::string name, int level) : symtab("global", 1), currentScope(symtab) { + } + void visit(AstNode* node) { + if (node->print() == "Program") visit_Program(*static_cast(node)); + else if (node->print() == "FunctionDecl") visit_ProcedureDecl(*static_cast(node)); + else if (node->print() == "FunctionCall") visit_FunctionCall(*static_cast(node)); + else if (node->print() == "Block") visit_Block(*static_cast(node)); + else if (node->print() == "UnOp") visit_UnOp(*static_cast(node)); + else if (node->print() == "Var") visit_Var(*static_cast(node)); + else if (node->print() == "BinOp") return visit_BinOp(*static_cast(node)); + else if (node->print() == "Assign") visit_Assign(*static_cast(node)); + } + void visit_FunctionCall(FunctionCall& functionCall) { + if (functionCall.params.size() != static_cast(symtab.lookup(functionCall.proc_name))->params.size()) { + std::string error("Function call parameters do not match function parameters."); + throw error; + } + for (AstNode* param : functionCall.params) { + visit(param); + } + ProcedureSymbol procSymbol = *static_cast(symtab.lookup(functionCall.proc_name)); + functionCall.procSymbol = procSymbol; + } + void visit_Program(Program program) { + // // cout << "Enter scope: GLOBAL\n"; + ScopedSymbolTable globalScope("global", 1); + currentScope = globalScope; + visit_Block(program.block); + // // cout << currentScope.print(); + // // cout << "Leave scope: GLOBAL\n"; + } + void visit_ProcedureDecl(FunctionDecl proc) { + ProcedureSymbol* proc_symbol = new ProcedureSymbol(proc.name); + currentScope.define(proc_symbol); + // // cout << "Enter scope: " << proc.name << endl; + ScopedSymbolTable proc_scope(proc.name, currentScope.scope_level + 1); + ScopedSymbolTable* enclosing = new ScopedSymbolTable(currentScope); + proc_scope.enclosingScope = enclosing; + currentScope = proc_scope; + for (VarDecl param : proc.params) { + BuiltinTypeSymbol* type = static_cast(currentScope.lookup(param.type.type)); + std::string param_name = param.var.value; + Symbol* var = new VarSymbol(param_name, type); + currentScope.define(var); + VarSymbol var_symbol(param_name, type); + proc_symbol->params.push_back(var_symbol); + types[param_name] = param.type.type; + } + visit_Block(proc.block); + // // cout << currentScope.print(); + currentScope = *proc_scope.enclosingScope; + proc_symbol->blockAst = *new Block(proc.block); + } + void visit_Block(Block block) { + for (VarDecl decl : block.declarations) { + visit_VarDecl(decl); + } + for (FunctionDecl* decl : block.procedures) { + visit(decl); + } + for (AstNode* node : block.children) { + visit(node); + } + } + void visit_UnOp(UnOp unOp) { + visit(unOp.expr); + } + void visit_BinOp(BinOp binOp) { + visit(binOp.left); + visit(binOp.right); + } + void visit_Assign(class Assign assign) { + std::string var_name = assign.var.value; + Symbol* var_symbol = currentScope.lookup(var_name); + if (var_symbol->name == "") { + std::string error = "Error: variable not defined: " + var_name; + throw error; + } + visit(assign.right); + } + void visit_Var(Var var) { + std::string var_name = var.value; + Symbol* var_symbol = currentScope.lookup(var_name); + if (var_symbol->name == "") { + std::string error = "Error: variable not defined: " + var_name; + throw error; + } + } + void visit_VarDecl(VarDecl varDecl) { + std::string type_name = varDecl.type.type; + BuiltinTypeSymbol* type_symbol = static_cast(currentScope.lookup(type_name)); + std::string var_name = varDecl.var.value; + if (currentScope.lookup(var_name)->name != "") { + std::string error("Multiple definition of variable " + var_name); + throw error; + } + VarSymbol* var = new VarSymbol(var_name, type_symbol); + currentScope.define(var); + types[var_name] = type_name; + } +}; \ No newline at end of file diff --git a/Interpreter/Symbols.h b/Interpreter/Symbols.h index cb6e29c..d3d1bd7 100644 --- a/Interpreter/Symbols.h +++ b/Interpreter/Symbols.h @@ -1,8 +1,5 @@ #pragma once #include -#include -#include -#include "../Parser/astnodes.h" class Symbol { private: @@ -34,152 +31,3 @@ class VarSymbol : public Symbol private: }; -class ProcedureSymbol : public Symbol { -public: - std::vector params; - ProcedureSymbol(std::string name, std::vector p) : Symbol(name), params(p) { - } - ProcedureSymbol(std::string name) : Symbol(name) { - } -}; -class ScopedSymbolTable { -private: - std::string scope_name; -public: - int scope_level; - ScopedSymbolTable* enclosingScope = NULL; - std::map symbols; - ScopedSymbolTable(std::string name, int level) { - define(new BuiltinTypeSymbol("int")); - define(new BuiltinTypeSymbol("real")); - define(new BuiltinTypeSymbol("string")); - scope_name = name; - scope_level = level; - } - void define(Symbol* symbol) { - // std::cout << "Define: " << symbol->print() << std::endl; - symbols[symbol->name] = symbol; - } - Symbol* lookup(std::string name) { - // std::cout << "Lookup: " << name << "\n"; - if (symbols.count(name)) { - return symbols[name]; - } - else { - if (enclosingScope != NULL) return enclosingScope->lookup(name); - else return new Symbol(); - } - } - std::string print() { - std::string rep(""); - rep += "SCOPE (SCOPED SYMBOL TABLE)\n==================\n"; - rep += "Scope name: " + scope_name + "\n"; - rep += "Scope level: " + std::to_string(scope_level) + "\n"; - rep += "Contents:\n...................\n"; - for (auto pair : symbols) { - rep += pair.first + ": " + pair.second->print() + "\n"; - } - rep += "====================\n"; - return rep; - } -}; -class SymbolTableBuilder { -public: - ScopedSymbolTable symtab; - ScopedSymbolTable& currentScope; - SymbolTableBuilder(std::string name, int level) : symtab("global", 1), currentScope(symtab) { - } - void visit(AstNode * node) { - if (node->print() == "Program") visit_Program(*static_cast(node)); - else if (node->print() == "FunctionDecl") visit_ProcedureDecl(*static_cast(node)); - else if (node->print() == "FunctionCall") visit_FunctionCall(*static_cast(node)); - else if (node->print() == "Block") visit_Block(*static_cast(node)); - else if (node->print() == "UnOp") visit_UnOp(*static_cast(node)); - else if (node->print() == "Var") visit_Var(*static_cast(node)); - else if (node->print() == "BinOp") return visit_BinOp(*static_cast(node)); - else if (node->print() == "Assign") visit_Assign(*static_cast(node)); - } - void visit_FunctionCall(FunctionCall functionCall) { - if (functionCall.params.size() != static_cast(symtab.lookup(functionCall.proc_name))->params.size()) { - std::string error("Function call parameters do not match function parameters."); - throw error; - } - for (AstNode* param : functionCall.params) { - visit(param); - } - } - void visit_Program(Program program) { - // cout << "Enter scope: GLOBAL\n"; - ScopedSymbolTable globalScope("global", 1); - currentScope = globalScope; - visit_Block(program.block); - // cout << currentScope.print(); - // cout << "Leave scope: GLOBAL\n"; - } - void visit_ProcedureDecl(FunctionDecl proc) { - ProcedureSymbol* proc_symbol = new ProcedureSymbol(proc.name); - currentScope.define(proc_symbol); - // cout << "Enter scope: " << proc.name << endl; - ScopedSymbolTable proc_scope(proc.name, currentScope.scope_level + 1); - ScopedSymbolTable* enclosing = new ScopedSymbolTable(currentScope); - proc_scope.enclosingScope = enclosing; - currentScope = proc_scope; - for (VarDecl param : proc.params) { - BuiltinTypeSymbol* type = static_cast(currentScope.lookup(param.type.type)); - std::string param_name = param.var.value; - Symbol* var = new VarSymbol(param_name, type); - currentScope.define(var); - VarSymbol var_symbol(param_name, type); - proc_symbol->params.push_back(var_symbol); - } - visit_Block(proc.block); - // cout << currentScope.print(); - currentScope = *proc_scope.enclosingScope; - } - void visit_Block(Block block) { - for (VarDecl decl : block.declarations) { - visit_VarDecl(decl); - } - for (FunctionDecl* decl : block.procedures) { - visit(decl); - } - for (AstNode* node : block.children) { - visit(node); - } - } - void visit_UnOp(UnOp unOp) { - visit(unOp.expr); - } - void visit_BinOp(BinOp binOp) { - visit(binOp.left); - visit(binOp.right); - } - void visit_Assign(class Assign assign) { - std::string var_name = assign.var.value; - Symbol* var_symbol = currentScope.lookup(var_name); - if (var_symbol->name == "") { - std::string error = "Error: variable not defined: " + var_name; - throw error; - } - visit(assign.right); - } - void visit_Var(Var var) { - std::string var_name = var.value; - Symbol* var_symbol = currentScope.lookup(var_name); - if (var_symbol->name == "") { - std::string error = "Error: variable not defined: " + var_name; - throw error; - } - } - void visit_VarDecl(VarDecl varDecl) { - std::string type_name = varDecl.type.type; - BuiltinTypeSymbol* type_symbol = static_cast(currentScope.lookup(type_name)); - std::string var_name = varDecl.var.value; - if (currentScope.lookup(var_name)->name != "") { - std::string error("Multiple definition of variable " + var_name); - throw error; - } - VarSymbol* var = new VarSymbol(var_name, type_symbol); - currentScope.define(var); - } -}; \ No newline at end of file diff --git a/Interpreter/interpreter.h b/Interpreter/interpreter.h index 4fbec4e..5018b12 100644 --- a/Interpreter/interpreter.h +++ b/Interpreter/interpreter.h @@ -8,6 +8,7 @@ #include #include #include "./Stack.h" +#include "./SymbolTable.h" namespace interpreter { class Interpreter { @@ -15,6 +16,7 @@ namespace interpreter { Parser parser; ScopedSymbolTable symTab; ScopedSymbolTable& currentSymTab = symTab; + std::map types; public: CallStack stack; Interpreter(std::string input) : parser(input), symTab("global", 1) { @@ -28,8 +30,11 @@ namespace interpreter { else return "real"; } else if (node->print() == "Var") { - Var* var = static_cast(node); - return symTab.lookup(var->value)->type->name; + // cout << "Getting type of Var..." << endl; + Var* var = dynamic_cast(node); + // cout << var->value << endl; + // cout << types[var->value]; + return types[var->value]; } else if (node->print() == "Num") { Num* num = static_cast(node); @@ -48,13 +53,15 @@ namespace interpreter { else if (node->print() == "Assign") visit_Assign(*static_cast(node)); else if (node->print() == "NoOp") visit_NoOp(); else if (node->print() == "Print") visit_Print(*static_cast(node)); - else if (node->print() == "FunctionCall"); + else if (node->print() == "FunctionDecl") visit_FunctionDecl(*static_cast(node)); + else if (node->print() == "FunctionCall") visit_FunctionCall(*static_cast(node)); else { std::string error = "Error: void operation not recognized"; throw error; } } else if constexpr (std::is_same_v || std::is_same_v) { + // cout << "Visiting number: " << node->print() << endl; if (node->print() == "BinOp") { BinOp binOp = *static_cast(node); return visit_BinOp(binOp); @@ -81,7 +88,33 @@ namespace interpreter { throw error; } } + void visit_FunctionDecl(FunctionDecl functionDecl) { + + } + void visit_FunctionCall(FunctionCall functionCall) { + std::string proc_name = functionCall.proc_name; + // cout << proc_name << endl; + ActivationRecord ar(ARType::FUNCTION, 2, proc_name); + ProcedureSymbol proc_symbol = functionCall.procSymbol; + // // cout << proc_symbol.print() << endl; + std::vector formal_params = proc_symbol.params; + std::vector actual_params = functionCall.params; + for (int i = 0; i < formal_params.size(); i++) { + if (formal_params[i].type->name == "int") ar[formal_params[i].name] = visit(actual_params[i]); + else if (formal_params[i].type->name == "real") ar[formal_params[i].name] = visit(actual_params[i]); + else if (formal_params[i].type->name == "string") ar[formal_params[i].name] = visit(actual_params[i]); + } + stack.push(ar); + ar = stack.peek(); + // cout << std::get(ar["bar"]) << endl; + // cout << "AR Done!" << endl; + // cout << std::get(ar["bar"]) << endl; + visit_Block(proc_symbol.blockAst); + // cout << "Block done!" << endl; + stack.pop(); + } void visit_Print(Print p) { + // cout << p.str.raw << endl; cout << visit_String(p.str); cout << endl; } @@ -89,10 +122,14 @@ namespace interpreter { int expr_index = 0; std::string result = ""; for (int i = 0; i < str.raw.size(); i++) { + // cout << str.raw[i]; if (str.raw[i] == '{') { AstNode* node = str.expr[expr_index]; + // cout << boolalpha << (node == nullptr) << endl << node->print() << endl; expr_index++; + // cout << getType(node) << endl; if (getType(node) == "int") { + // cout << visit(node) << endl; result += std::to_string(visit(node)); } else if (getType(node) == "real") { @@ -105,6 +142,7 @@ namespace interpreter { } else result += str.raw[i]; } + // cout << result << endl; return result; } template @@ -186,13 +224,18 @@ namespace interpreter { } } void visit_Block(Block block) { + ActivationRecord ar = stack.peek(); + // cout << std::get(ar["bar"]) << endl; + // cout << "Visiting block..." << endl; for (AstNode* child : block.children) { + // cout << "Visiting child..." << endl; + // cout << child->print() << endl; visit(child); } } void visit_Assign(class Assign assign) { std::string var_name = assign.var.value; - std::string type = symTab.lookup(var_name)->type->name; + std::string type = types[var_name]; ActivationRecord& ar = stack.peek(); if (type == "int") { ar[var_name] = visit(assign.right); @@ -214,9 +257,10 @@ namespace interpreter { template T visit_Var(Var var) { ActivationRecord& ar = stack.peek(); + // if (ar.name == "foo") cout << std::get(ar["bar"]) << endl; if (ar.members.find(var.value) != ar.members.end()) { if constexpr (std::is_same_v) { - std::string type = symTab.lookup(var.value)->type->name; + std::string type = types[var.value]; if (type == "int") return (T)std::get(ar[var.value]); else { std::string error("Wanted integer, got " + type); @@ -224,7 +268,7 @@ namespace interpreter { } } else if constexpr (std::is_same_v) { - std::string type = symTab.lookup(var.value)->type->name; + std::string type = types[var.value]; if (type == "real") return (T)std::get(ar[var.value]); else if (type == "int") { return (T)std::get(ar[var.value]); @@ -235,7 +279,7 @@ namespace interpreter { } } else if constexpr (std::is_same_v) { - std::string type = symTab.lookup(var.value)->type->name; + std::string type = types[var.value]; if (type == "string") return std::get(ar[var.value]); else { std::string error("Wanted string, got " + type); @@ -263,7 +307,7 @@ namespace interpreter { return; } SymbolTableBuilder symtabBuilder("global", 1); - //std::cout << "Building symtab...\n"; + //std::// cout << "Building symtab...\n"; try { symtabBuilder.visit(&p); } @@ -271,9 +315,9 @@ namespace interpreter { cout << error << endl; return; } - //std::cout << "Finished building symtab...\n"; - symTab = symtabBuilder.currentScope; - //std::cout << "Interpreting...\n"; + //std::// cout << "Finished building symtab...\n"; + types = symtabBuilder.types; + //std::// cout << "Interpreting...\n"; ARType t = ARType::PROGRAM; ActivationRecord ar(t, 1); stack.push(ar); diff --git a/Parser/astnodes.h b/Parser/astnodes.h index 71a7d89..6387911 100644 --- a/Parser/astnodes.h +++ b/Parser/astnodes.h @@ -2,6 +2,7 @@ #include #include #include "../Lexer/tokens.h" +#include "../Interpreter/Symbols.h" class AstNode { private: std::string type; @@ -147,6 +148,7 @@ class VarDecl : public AstNode { } }; class FunctionDecl; + class Block : public AstNode { public: std::vector children; @@ -168,6 +170,16 @@ class Block : public AstNode { return s; } }; +class ProcedureSymbol : public Symbol { +public: + std::vector params; + ProcedureSymbol(std::string name, std::vector p) : Symbol(name), params(p) { + } + ProcedureSymbol(std::string name) : Symbol(name) { + } + ProcedureSymbol() {} + Block blockAst; +}; class Program : public AstNode { public: Block block; @@ -203,6 +215,7 @@ class FunctionCall : public AstNode { std::string print() { return "FunctionCall"; } + ProcedureSymbol procSymbol; }; diff --git a/Parser/parser.h b/Parser/parser.h index e752602..deb3ebe 100644 --- a/Parser/parser.h +++ b/Parser/parser.h @@ -10,7 +10,7 @@ class Parser { currentToken = lexer.nextToken(); } void error (TokenType expected, TokenType received) { - std::string error = "Invalid syntax: Expected " + std::to_string(expected) + ", got " + std::to_string(received); + std::string error = "Invalid syntax: Expected " + std::to_string(expected) + ", got " + std::to_string(received) + " " + currentToken.value; throw error; } void eat (TokenType type) { diff --git a/main.cpp b/main.cpp index 0bcc16b..8a2aa89 100644 --- a/main.cpp +++ b/main.cpp @@ -26,9 +26,9 @@ int interpret(string input) { vector tokens = lexer.allTokens(); // tokens.size() is a long unsigned int, use to prevent -Wall or -Wextra warnings - /* cout << "Lexer tokens:" << endl; + /* // cout << "Lexer tokens:" << endl; for (long unsigned int i = 0; i < tokens.size(); i++) { - cout << tokens[i]; + // cout << tokens[i]; } Parser parser(input); Program program; @@ -36,39 +36,39 @@ int interpret(string input) { program = parser.parseProgram(); } catch (string error) { - cout << error << "\n"; + // cout << error << "\n"; return 1; } - cout << "Parser blocks:" << endl; + // cout << "Parser blocks:" << endl; for (int i = 0; i < block.size(); i++) { - cout << block[i]->print() << endl; + // cout << block[i]->print() << endl; }*/ Interpreter interpreter(input); - // cout << "Interpreting...\n"; + // // cout << "Interpreting...\n"; try { interpreter.interpret(); } catch (std::string error) { - cout << error << "\n"; + // cout << error << "\n"; return 1; } - /*cout << "Variables: " << endl; + /*// cout << "Variables: " << endl; ScopedSymbolTable symtab = interpreter.getSymTab(); - cout << symtab.print(); + // cout << symtab.print(); for (auto const& pair : interpreter.GLOBAL_SCOPE) { - cout << pair.first << " "; + // cout << pair.first << " "; std::string type = symtab.lookup(pair.first)->type->name; if (type == "int") { - cout << std::get(pair.second); + // cout << std::get(pair.second); } else if (type == "real") { - cout << std::get(pair.second); + // cout << std::get(pair.second); } else if (type == "string") { - cout << std::get(pair.second); + // cout << std::get(pair.second); } - cout << endl; + // cout << endl; }*/ return 0; } diff --git a/test.spark b/test.spark index 079ea44..b07f59b 100644 --- a/test.spark +++ b/test.spark @@ -4,16 +4,15 @@ real: b; string: test, multi; def foo (int: bar) { - def proc (int: asdf) { - program: a = asdf; - } + def sub (int: asdf) { + program: + print('{asdf}'); } program: - bar = a; - proc(5); + sub(bar); } program: #this is a comment# - foo(a); a = 1*1+4//-2; + foo(a); b = -13+4.5; test = 'The value of a is {a}, and the value of b is {b}'; multi = 'a*b={a*b}';