From af1a50f6fe45f8351c6cdaa0d92ad98b0b2f8192 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Sep 2024 13:32:21 -0400 Subject: [PATCH 01/12] try workflow --- .../workflows/cmake-single-platform-linux.yml | 162 ++++++++++++++++++ build.sh | 2 + 2 files changed, 164 insertions(+) create mode 100644 .github/workflows/cmake-single-platform-linux.yml diff --git a/.github/workflows/cmake-single-platform-linux.yml b/.github/workflows/cmake-single-platform-linux.yml new file mode 100644 index 0000000..8d35ceb --- /dev/null +++ b/.github/workflows/cmake-single-platform-linux.yml @@ -0,0 +1,162 @@ +name: CMake on Linux + +on: + push: + branches: ["ci-linux"] + pull_request: + branches: ["ci-linux"] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Initialize submodules + run: git submodule update --init --recursive + + - name: Install Wasmer + run: curl https://get.wasmer.io -sSfL | WASMER_DIR=lib/wasmer sh + + - name: Upload Wasmer as artifact + uses: actions/upload-artifact@v4 + with: + name: wasmer + path: ${{ github.workspace }}/lib/wasmer + + - name: Cache CMake build + uses: actions/cache@v3 + with: + path: build + key: ${{ runner.os }}-build-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-build- + - name: Cache CMake dependencies + uses: actions/cache@v3 + with: + path: | + .cache + key: ${{ runner.os }}-cmake-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-cmake- + - name: Build project + working-directory: ${{ github.workspace }} + run: ./build.sh + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build + path: ${{ github.workspace }}/build + + lexer-test: + runs-on: macos-latest + needs: build + + steps: + - uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build + path: ${{ github.workspace }}/build + + - name: Download Wasmer artifacts + uses: actions/download-artifact@v4 + with: + name: wasmer + path: ${{ github.workspace }}/lib/wasmer + + - name: Make LexerTest Executable + run: chmod +x ${{ github.workspace }}/build/LexerTest + + - name: Output Wasmer directory + run: ls -laR ${{ github.workspace }}/lib/wasmer + + - name: Run Lexer Test + working-directory: ${{ github.workspace }}/build + run: ./LexerTest + + parser-test: + runs-on: macos-latest + needs: build + + steps: + - uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build + path: ${{ github.workspace }}/build + + - name: Download Wasmer artifacts + uses: actions/download-artifact@v4 + with: + name: wasmer + path: ${{ github.workspace }}/lib/wasmer + + - name: Make ParserTest Executable + run: chmod +x ${{ github.workspace }}/build/ParserTest + + - name: Run Parser Test + working-directory: ${{ github.workspace }}/build + run: ./ParserTest + + typechecker-test: + runs-on: macos-latest + needs: build + + steps: + - uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build + path: ${{ github.workspace }}/build + + - name: Download Wasmer artifacts + uses: actions/download-artifact@v4 + with: + name: wasmer + path: ${{ github.workspace }}/lib/wasmer + + - name: Make TypeCheckerTest Executable + run: chmod +x ${{ github.workspace }}/build/TypeCheckerTest + + - name: Run TypeChecker Test + working-directory: ${{ github.workspace }}/build + run: ./TypeCheckerTest + + codegen-test: + runs-on: macos-latest + needs: build + + steps: + - uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build + path: ${{ github.workspace }}/build + + - name: Download Wasmer artifacts + uses: actions/download-artifact@v4 + with: + name: wasmer + path: ${{ github.workspace }}/lib/wasmer + + - name: Make CodegenTest Executable + run: chmod +x ${{ github.workspace }}/build/CodegenTest + + - name: Run Codegen Test + working-directory: ${{ github.workspace }}/build + run: ./CodegenTest diff --git a/build.sh b/build.sh index a86a269..ecb7f67 100755 --- a/build.sh +++ b/build.sh @@ -2,6 +2,8 @@ set -e +g++ --version + if [ ! -d "build" ]; then mkdir build fi From 97b52646d5a6b4c3780103e9484f267a79422094 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Sep 2024 13:37:12 -0400 Subject: [PATCH 02/12] point action at master --- .github/workflows/cmake-single-platform-linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-single-platform-linux.yml b/.github/workflows/cmake-single-platform-linux.yml index 8d35ceb..29710b1 100644 --- a/.github/workflows/cmake-single-platform-linux.yml +++ b/.github/workflows/cmake-single-platform-linux.yml @@ -2,9 +2,9 @@ name: CMake on Linux on: push: - branches: ["ci-linux"] + branches: ["master"] pull_request: - branches: ["ci-linux"] + branches: ["master"] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) From 7b6718debfb15be439b5d3cf493a20336e3b3887 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Sep 2024 13:48:05 -0400 Subject: [PATCH 03/12] explicitly include optional header --- src/compiler/CodeGen.hpp | 1 + src/compiler/SymbolTable.hpp | 1 + src/compiler/SymbolTableStack.hpp | 1 + 3 files changed, 3 insertions(+) diff --git a/src/compiler/CodeGen.hpp b/src/compiler/CodeGen.hpp index aa5914b..ab97d77 100644 --- a/src/compiler/CodeGen.hpp +++ b/src/compiler/CodeGen.hpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace std; diff --git a/src/compiler/SymbolTable.hpp b/src/compiler/SymbolTable.hpp index c8dfa1b..917fef6 100644 --- a/src/compiler/SymbolTable.hpp +++ b/src/compiler/SymbolTable.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include using namespace std; diff --git a/src/compiler/SymbolTableStack.hpp b/src/compiler/SymbolTableStack.hpp index e666332..6684247 100644 --- a/src/compiler/SymbolTableStack.hpp +++ b/src/compiler/SymbolTableStack.hpp @@ -1,6 +1,7 @@ #pragma once #include "SymbolTable.hpp" +#include #include using namespace std; From 9be7b42d3f5108454c53e8f2fea6e803c0b3c94e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Sep 2024 15:38:22 -0400 Subject: [PATCH 04/12] fix: linux gcc is stricter than osx, fixes nonreturns in non-void functions, addresses temporary array addressing --- src/compiler/CodeGen.cpp | 32 ++++++++++++++++++++------------ src/compiler/CodeGen.hpp | 4 ++-- src/compiler/StandardLibrary.hpp | 4 +++- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/compiler/CodeGen.cpp b/src/compiler/CodeGen.cpp index c1b40f9..ea7975a 100644 --- a/src/compiler/CodeGen.cpp +++ b/src/compiler/CodeGen.cpp @@ -86,7 +86,7 @@ namespace Theta { if (node->getNodeType() == ASTNode::SOURCE) { generateSource(dynamic_pointer_cast(node), module); } else if (node->getNodeType() == ASTNode::CAPSULE) { - return generateCapsule(dynamic_pointer_cast(node), module); + generateCapsule(dynamic_pointer_cast(node), module); } else if (node->getNodeType() == ASTNode::ASSIGNMENT) { return generateAssignment(dynamic_pointer_cast(node), module); } else if (node->getNodeType() == ASTNode::BLOCK) { @@ -123,7 +123,7 @@ namespace Theta { return nullptr; } - BinaryenExpressionRef CodeGen::generateCapsule(shared_ptr capsuleNode, BinaryenModuleRef &module) { + void CodeGen::generateCapsule(shared_ptr capsuleNode, BinaryenModuleRef &module) { vector> capsuleElements = dynamic_pointer_cast(capsuleNode->getValue())->getElements(); hoistCapsuleElements(capsuleElements); @@ -513,7 +513,7 @@ namespace Theta { collectClosureScope(node->getParent(), identifiersToFind, parameters, bodyExpressions); } - BinaryenExpressionRef CodeGen::generateFunctionDeclaration( + void CodeGen::generateFunctionDeclaration( string identifier, shared_ptr fnDeclNode, BinaryenModuleRef &module, @@ -690,18 +690,20 @@ namespace Theta { // If a refIdentifier was passed, that means we have an existing closure // in memory that we want to populate. if (refIdentifier != "") { + BinaryenExpressionRef closureArgs[2] = { + BinaryenLocalGet( + module, + scope.lookup(refIdentifier).value()->getMappedBinaryenIndex(), + BinaryenTypeInt32() + ), + BinaryenConst(module, BinaryenLiteralInt32(addressToPopulate.getAddress())) + }; + expressions.push_back( BinaryenCall( module, "Theta.Function.populateClosure", - (BinaryenExpressionRef[]){ - BinaryenLocalGet( - module, - scope.lookup(refIdentifier).value()->getMappedBinaryenIndex(), - BinaryenTypeInt32() - ), - BinaryenConst(module, BinaryenLiteralInt32(addressToPopulate.getAddress())) - }, + closureArgs, 2, BinaryenTypeNone() ) @@ -1061,10 +1063,12 @@ namespace Theta { throw runtime_error("Invalid operand types for binary operation"); } + BinaryenExpressionRef args[2] = { binaryenLeft, binaryenRight }; + return BinaryenCall( module, "Theta.Math.pow", - (BinaryenExpressionRef[]){ binaryenLeft, binaryenRight }, + args, 2, BinaryenTypeInt64() ); @@ -1153,6 +1157,8 @@ namespace Theta { if (op == Lexemes::GT && dataType == DataTypes::NUMBER) return BinaryenGtSInt64(); if (op == Lexemes::LTEQ && dataType == DataTypes::NUMBER) return BinaryenLeSInt64(); if (op == Lexemes::GTEQ && dataType == DataTypes::NUMBER) return BinaryenGeSInt64(); + + throw runtime_error("No matching WASM opcode for binary operation: " + binOpNode->getOperator()); } BinaryenType CodeGen::getBinaryenTypeFromTypeDeclaration(shared_ptr typeDeclaration) { @@ -1162,6 +1168,8 @@ namespace Theta { // Function references are returned as i32 pointers to a closure in the function table if (typeDeclaration->getType() == DataTypes::FUNCTION) return BinaryenTypeInt32(); + + throw runtime_error("No matching WASM type for TypeDeclaration: " + typeDeclaration->getType()); } BinaryenType CodeGen::getBinaryenStorageTypeFromTypeDeclaration(shared_ptr typeDeclaration) { diff --git a/src/compiler/CodeGen.hpp b/src/compiler/CodeGen.hpp index ab97d77..de4e138 100644 --- a/src/compiler/CodeGen.hpp +++ b/src/compiler/CodeGen.hpp @@ -30,11 +30,11 @@ namespace Theta { public: BinaryenModuleRef generateWasmFromAST(shared_ptr ast); BinaryenExpressionRef generate(shared_ptr node, BinaryenModuleRef &module); - BinaryenExpressionRef generateCapsule(shared_ptr node, BinaryenModuleRef &module); + void generateCapsule(shared_ptr node, BinaryenModuleRef &module); BinaryenExpressionRef generateAssignment(shared_ptr node, BinaryenModuleRef &module); BinaryenExpressionRef generateBlock(shared_ptr node, BinaryenModuleRef &module); BinaryenExpressionRef generateReturn(shared_ptr node, BinaryenModuleRef &module); - BinaryenExpressionRef generateFunctionDeclaration( + void generateFunctionDeclaration( string identifier, shared_ptr node, BinaryenModuleRef &module, diff --git a/src/compiler/StandardLibrary.hpp b/src/compiler/StandardLibrary.hpp index 194d646..db2069e 100644 --- a/src/compiler/StandardLibrary.hpp +++ b/src/compiler/StandardLibrary.hpp @@ -110,10 +110,12 @@ namespace Theta { BinaryenLocalGet(module, 2, BinaryenTypeInt64()) }; + BinaryenType fnTypes[2] = { BinaryenTypeInt64(), BinaryenTypeInt64() }; + BinaryenFunctionRef powFn = BinaryenAddFunction( module, "Theta.Math.pow", - BinaryenTypeCreate((BinaryenType[]){ BinaryenTypeInt64(), BinaryenTypeInt64() }, 2), + BinaryenTypeCreate(fnTypes, 2), BinaryenTypeInt64(), (BinaryenType[]){ BinaryenTypeInt64() }, 1, From 1b9d07ca2a4c437edc1a401a1b3174801e950e4b Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Sep 2024 16:38:52 -0400 Subject: [PATCH 05/12] fix: try using fully qualified name for return enum in astnode --- src/compiler/CodeGen.cpp | 2 +- src/compiler/StandardLibrary.hpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/CodeGen.cpp b/src/compiler/CodeGen.cpp index ea7975a..c6b48fd 100644 --- a/src/compiler/CodeGen.cpp +++ b/src/compiler/CodeGen.cpp @@ -91,7 +91,7 @@ namespace Theta { return generateAssignment(dynamic_pointer_cast(node), module); } else if (node->getNodeType() == ASTNode::BLOCK) { return generateBlock(dynamic_pointer_cast(node), module); - } else if (node->getNodeType() == ASTNode::RETURN) { + } else if (node->getNodeType() == Theta::ASTNode::RETURN) { return generateReturn(dynamic_pointer_cast(node), module); } else if (node->getNodeType() == ASTNode::FUNCTION_DECLARATION) { // The only time we should get here is if we have a function defined inside a function, diff --git a/src/compiler/StandardLibrary.hpp b/src/compiler/StandardLibrary.hpp index db2069e..7e169d7 100644 --- a/src/compiler/StandardLibrary.hpp +++ b/src/compiler/StandardLibrary.hpp @@ -110,14 +110,15 @@ namespace Theta { BinaryenLocalGet(module, 2, BinaryenTypeInt64()) }; - BinaryenType fnTypes[2] = { BinaryenTypeInt64(), BinaryenTypeInt64() }; + BinaryenType paramTypes[2] = { BinaryenTypeInt64(), BinaryenTypeInt64() }; + BinaryenType varTypes[1] = { BinaryenTypeInt64() }; BinaryenFunctionRef powFn = BinaryenAddFunction( module, "Theta.Math.pow", - BinaryenTypeCreate(fnTypes, 2), + BinaryenTypeCreate(paramTypes, 2), BinaryenTypeInt64(), - (BinaryenType[]){ BinaryenTypeInt64() }, + varTypes, 1, BinaryenBlock( module, From 69650e29e7fafbc50647dc90c52c120ffcd00015 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Sep 2024 22:15:46 -0400 Subject: [PATCH 06/12] test file output for binary --- .github/workflows/cmake-single-platform-linux.yml | 4 ++-- src/compiler/CodeGen.cpp | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-single-platform-linux.yml b/.github/workflows/cmake-single-platform-linux.yml index 29710b1..b8a4e5c 100644 --- a/.github/workflows/cmake-single-platform-linux.yml +++ b/.github/workflows/cmake-single-platform-linux.yml @@ -76,8 +76,8 @@ jobs: - name: Make LexerTest Executable run: chmod +x ${{ github.workspace }}/build/LexerTest - - name: Output Wasmer directory - run: ls -laR ${{ github.workspace }}/lib/wasmer + - name: Output LexerText directory + run: file ${{ github.workspace }}/build/LexerTest - name: Run Lexer Test working-directory: ${{ github.workspace }}/build diff --git a/src/compiler/CodeGen.cpp b/src/compiler/CodeGen.cpp index c6b48fd..35db285 100644 --- a/src/compiler/CodeGen.cpp +++ b/src/compiler/CodeGen.cpp @@ -30,6 +30,9 @@ #include #endif +#pragma push_macro("RETURN") +#undef RETURN + namespace Theta { BinaryenModuleRef CodeGen::generateWasmFromAST(shared_ptr ast) { BinaryenModuleRef module = initializeWasmModule(); @@ -1405,3 +1408,5 @@ namespace Theta { return stream.str(); } } + +#pragma pop_macro("RETURN") From 31264de354bd74a00cdc04276a6a170a828f1418 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Sep 2024 23:19:13 -0400 Subject: [PATCH 07/12] add missing header --- src/compiler/CodeGen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/CodeGen.cpp b/src/compiler/CodeGen.cpp index 35db285..37e1467 100644 --- a/src/compiler/CodeGen.cpp +++ b/src/compiler/CodeGen.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From 70833de7548c6a665aa2057918b9a31cd9b32d43 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Sep 2024 23:39:44 -0400 Subject: [PATCH 08/12] put the include in the right place --- src/compiler/CodeGen.cpp | 1 - src/compiler/CodeGen.hpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/CodeGen.cpp b/src/compiler/CodeGen.cpp index 37e1467..35db285 100644 --- a/src/compiler/CodeGen.cpp +++ b/src/compiler/CodeGen.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/src/compiler/CodeGen.hpp b/src/compiler/CodeGen.hpp index de4e138..f32e0eb 100644 --- a/src/compiler/CodeGen.hpp +++ b/src/compiler/CodeGen.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "../parser/ast/ASTNode.hpp" #include "../parser/ast/BinaryOperationNode.hpp" #include "../parser/ast/UnaryOperationNode.hpp" From 7c9c919b6e7744a3be4ccde278fc0baa8dfd2b4b Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 29 Sep 2024 00:14:10 -0400 Subject: [PATCH 09/12] use correct environment for test runners --- .github/workflows/cmake-single-platform-linux.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmake-single-platform-linux.yml b/.github/workflows/cmake-single-platform-linux.yml index b8a4e5c..8d9f996 100644 --- a/.github/workflows/cmake-single-platform-linux.yml +++ b/.github/workflows/cmake-single-platform-linux.yml @@ -55,7 +55,7 @@ jobs: path: ${{ github.workspace }}/build lexer-test: - runs-on: macos-latest + runs-on: ubuntu-latest needs: build steps: @@ -84,7 +84,7 @@ jobs: run: ./LexerTest parser-test: - runs-on: macos-latest + runs-on: ubuntu-latest needs: build steps: @@ -110,7 +110,7 @@ jobs: run: ./ParserTest typechecker-test: - runs-on: macos-latest + runs-on: ubuntu-latest needs: build steps: @@ -136,7 +136,7 @@ jobs: run: ./TypeCheckerTest codegen-test: - runs-on: macos-latest + runs-on: ubuntu-latest needs: build steps: From 3dd78f1aedd7556ba747a52e2ae083b12e04a23d Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 29 Sep 2024 02:08:22 -0400 Subject: [PATCH 10/12] output the parsed ast during parser test for debugging --- test/ParserTest.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/ParserTest.cpp b/test/ParserTest.cpp index 2b339d9..bd89197 100644 --- a/test/ParserTest.cpp +++ b/test/ParserTest.cpp @@ -815,6 +815,9 @@ TEST_CASE("Parser") { parser.parse(lexer.tokens, source, "fakeFile.th", filesByCapsuleName) ); + cout << "THE PARSED AST IS: " << endl; + cout << parsedAST->toJSON() << endl; + REQUIRE(parsedAST->getNodeType() == ASTNode::SOURCE); REQUIRE(parsedAST->getLinks().size() == 0); REQUIRE(parsedAST->getValue()->getNodeType() == ASTNode::ASSIGNMENT); From 0d1c85c487a24698d0daf9826176f5de78eb1463 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 29 Sep 2024 02:55:13 -0400 Subject: [PATCH 11/12] try explicit ordering of parser function calls --- src/parser/Parser.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index 854a295..cfdc0f0 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -338,8 +338,12 @@ namespace Theta { shared_ptr parseControlFlow(shared_ptr parent) { if (match(Token::KEYWORD, Lexemes::IF)) { shared_ptr cfNode = make_shared(parent); + + shared_ptr cnd = parseExpression(cfNode); + shared_ptr expr = parseBlock(cfNode); + vector, shared_ptr>> conditionExpressionPairs = { - make_pair(parseExpression(cfNode), parseBlock(cfNode)) + make_pair(cnd, expr) }; while (match(Token::KEYWORD, Lexemes::ELSE) && match(Token::KEYWORD, Lexemes::IF)) { From 1d4cd6a5047a32450b135dae4f55e954b727e9d1 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 29 Sep 2024 22:18:21 -0400 Subject: [PATCH 12/12] fix: change if statement parsing to correctly parse if-else blocks on linux --- src/parser/Parser.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index cfdc0f0..88ae720 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -347,7 +347,9 @@ namespace Theta { }; while (match(Token::KEYWORD, Lexemes::ELSE) && match(Token::KEYWORD, Lexemes::IF)) { - conditionExpressionPairs.push_back(make_pair(parseExpression(cfNode), parseBlock(cfNode))); + cnd = parseExpression(cfNode); + expr = parseBlock(cfNode); + conditionExpressionPairs.push_back(make_pair(cnd, expr)); } // If we just matched an else but no if afterwards. This way it only matches one else block per control flow