diff --git a/.gitignore b/.gitignore index 3c1fdf8..79d97f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /cmake-build-debug +/cmake-build-debug-coverage diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index fdd45d5..6a2e36a 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,6 +1,10 @@ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c96707..6a868b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,3 +31,5 @@ add_expect_test(id001 ./tests/001.exp) add_expect_test(id002 ./tests/002.exp) add_expect_test(id003 ./tests/003.exp) add_expect_test(id004 ./tests/004.exp) +add_expect_test(id005 ./tests/005.exp) +add_expect_test(id006 ./tests/006.exp) diff --git a/include/molasses/parser_primitives.h b/include/molasses/parser_primitives.h index 800cf68..99e98b2 100644 --- a/include/molasses/parser_primitives.h +++ b/include/molasses/parser_primitives.h @@ -1,12 +1,16 @@ #pragma once -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include "molasses/lexer.h" @@ -71,13 +75,25 @@ namespace molasses { }; struct parser_context; - + + struct ptr_type{ + void* ptr; + std::shared_ptr pointed; + }; + + using stack_element = std::variant; + + using interpreter_stack = std::stack; + + struct generate_context; + struct operation { [[nodiscard]] virtual std::string name() const = 0; [[nodiscard]] virtual std::vector argument_types() const = 0; [[nodiscard]] virtual std::vector return_types() const = 0; [[nodiscard]] virtual std::vector generate(const parser_context&, const lexed_output& lexer_data) const = 0; [[nodiscard]] virtual std::vector emit(const parser_context&) const = 0; + virtual void execute(const generate_context&, interpreter_stack&) const = 0; }; struct primitive_operation : public operation { @@ -85,12 +101,14 @@ namespace molasses { std::vector _args; std::vector _rets; std::vector _instructions; + std::function _executable; - primitive_operation(std::string name, std::vector args, std::vector rets, std::vector body) + primitive_operation(std::string name, std::vector args, std::vector rets, std::vector body, std::function executable) : _name(std::forward(name)) , _args(std::forward>(args)) , _rets(std::forward>(rets)) , _instructions(std::forward>(body)) + , _executable(std::move(executable)) {} [[nodiscard]] std::string name() const final { @@ -108,6 +126,10 @@ namespace molasses { [[nodiscard]] std::vector emit(const parser_context&) const final { return _instructions; } + + void execute(const generate_context& ctx, interpreter_stack& stack) const override { + _executable(ctx, stack); + }; }; struct procedure_operation : public operation { @@ -134,6 +156,7 @@ namespace molasses { } [[nodiscard]] std::vector generate(const parser_context&, const lexed_output& lexer_data) const final; [[nodiscard]] std::vector emit(const parser_context&) const final; + void execute(const generate_context&, interpreter_stack&) const final; }; inline auto operator<=>(const operation& lhs, const operation& rhs) { @@ -141,7 +164,7 @@ namespace molasses { } struct parser_error : public std::runtime_error { - explicit parser_error(const std::string str) : std::runtime_error(str) {} + explicit parser_error(const std::string& str) : std::runtime_error(str) {} }; struct type_input_error : public parser_error { @@ -153,11 +176,11 @@ namespace molasses { // TODO: Better error message }; struct procedure_stack_error : public parser_error { - procedure_stack_error() : parser_error("Expected the stack to look like the return stack upon completion") {} + procedure_stack_error() : parser_error("Expected the stack to look like the return stack upon completion\n") {} // TODO: Better error message }; struct unexpected_token_error : public parser_error { - unexpected_token_error(const symbol sym, const std::string found, const std::string expected) + unexpected_token_error(const symbol& sym, const std::string& found, const std::string& expected) : parser_error ( details::concatenate_builder( "Unexpected token encountered\n", @@ -168,7 +191,7 @@ namespace molasses { ) {} }; struct expecting_token_error : public parser_error { - expecting_token_error(const std::string expected, const std::string context) + expecting_token_error(const std::string& expected, const std::string& context) : parser_error( details::concatenate_builder( "An expected token has not been encountered before the end of the input\n", @@ -180,7 +203,7 @@ namespace molasses { // TODO: Better error message }; struct unknown_token_error : public parser_error { - explicit unknown_token_error(const symbol sym) : parser_error(details::concatenate_builder("An unknown token has been encountered\n", "\tAt ", sym.file_name,":",sym.line,":",sym.column,"\n")) {} + explicit unknown_token_error(const symbol& sym) : parser_error(details::concatenate_builder("An unknown token has been encountered\n", "\tAt ", sym.file_name,":",sym.line,":",sym.column,"\n")) {} // TODO: Better error message }; struct type_expected_with_modifier_error : public parser_error { diff --git a/src/main.cpp b/src/main.cpp index 70a629f..56374f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,12 @@ using compile_element = std::variant; +struct build_system_error : std::runtime_error { + explicit build_system_error(const std::string& message) + : std::runtime_error(message) + {} +}; + int main(int argc, char** argv) { std::vector arguments; while(argc > 1) { @@ -24,10 +30,10 @@ int main(int argc, char** argv) { for(auto elem : arguments) { if(elem == "generate") { - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto filename = std::get(compile_stack.top()); compile_stack.pop(); - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto generator = std::get(compile_stack.top()); compile_stack.pop(); auto assembler = molasses::generate(generator); @@ -37,41 +43,41 @@ int main(int argc, char** argv) { } compile_stack.emplace(filename); } else - throw std::runtime_error("generate expects a parsed output"); + throw build_system_error("generate expects a parsed output\n"); } else - throw std::runtime_error("generate expects a filename"); + throw build_system_error("generate expects a filename\n"); } else if(elem == "parse") { molasses::parser_context ctx; ctx = molasses::register_integers(ctx); ctx = molasses::register_i32_operations(ctx); - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto lexer = std::get(compile_stack.top()); compile_stack.pop(); auto generator = molasses::parse(ctx, lexer); compile_stack.emplace(generator); } else - throw std::runtime_error("parse expects a lexed output"); + throw build_system_error("parse expects a lexed output\n"); } else if(elem == "lex") { - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto filename = std::get(compile_stack.top()); compile_stack.pop(); if(not std::filesystem::exists(filename)) - throw std::runtime_error("file " + filename + " does not exist"); + throw build_system_error("file " + filename + " does not exist\n"); std::ifstream t(filename); std::stringstream buffer; buffer << t.rdbuf(); auto lexed = molasses::lex(filename, buffer.str()); compile_stack.emplace(lexed); } else - throw std::runtime_error("lex expects a filename"); + throw build_system_error("lex expects a filename\n"); } else if(elem == "lex-all") { std::vector lexed_list; while(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto filename = std::get(compile_stack.top()); compile_stack.pop(); if(not std::filesystem::exists(filename)) - throw std::runtime_error("file " + filename + " does not exist"); + throw build_system_error("file " + filename + " does not exist\n"); std::ifstream t(filename); std::stringstream buffer; buffer << t.rdbuf(); @@ -82,19 +88,19 @@ int main(int argc, char** argv) { compile_stack.emplace(std::move(lexed)); } } else if(elem == "merge") { - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto lexer_1 = std::get(compile_stack.top()); compile_stack.pop(); - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto lexer_2 = std::get(compile_stack.top()); compile_stack.pop(); compile_stack.emplace(molasses::concatenate(lexer_1, lexer_2)); } else - throw std::runtime_error("merge expects 2 lexed outputs"); + throw build_system_error("merge expects 2 lexed outputs\n"); } else - throw std::runtime_error("merge expects 2 lexed outputs"); + throw build_system_error("merge expects 2 lexed outputs\n"); } else if(elem == "merge-all") { - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto lexer_1 = std::get(compile_stack.top()); compile_stack.pop(); while(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { @@ -104,9 +110,9 @@ int main(int argc, char** argv) { } compile_stack.emplace(lexer_1); } else - throw std::runtime_error("merge-all expects at least 1 lexed outputs"); + throw build_system_error("merge-all expects at least 1 lexed outputs\n"); } else if(elem == "assemble") { - if(std::holds_alternative(compile_stack.top())) { + if(not compile_stack.empty() and std::holds_alternative(compile_stack.top())) { auto filename = std::get(compile_stack.top()); compile_stack.pop(); std::stringstream compile; @@ -118,7 +124,7 @@ int main(int argc, char** argv) { std::cout << link.str() << std::endl; system(link.str().c_str()); } else - throw std::runtime_error("assemble expects an assembly file"); + throw build_system_error("assemble expects an assembly file\n"); } else if(elem == "help" or elem == "--help") { std::cout << "# Sugar\n\n"; std::cout << "## Commands\n\n"; @@ -158,7 +164,7 @@ int main(int argc, char** argv) { if(not compile_stack.empty()) { if(std::holds_alternative(compile_stack.top())) { auto lexer = std::get(compile_stack.top()); - for(auto elem : lexer.symbols) { + for(const auto& elem : lexer.symbols) { std::cout << elem << " "; } std::cout << "\n\n"; @@ -181,6 +187,10 @@ int main(int argc, char** argv) { } } } catch (molasses::parser_error& error) { - std::cerr << error.what(); + std::cerr << "COMPILER ERROR:\n" << error.what(); + return 1; + } catch (build_system_error& error) { + std::cerr << "BUILD SYSTEM ERROR:\n" << error.what(); + return 1; } } diff --git a/src/molasses/generator_primitives_x86_64_linux.cpp b/src/molasses/generator_primitives_x86_64_linux.cpp index 6c56ca4..1f54e66 100644 --- a/src/molasses/generator_primitives_x86_64_linux.cpp +++ b/src/molasses/generator_primitives_x86_64_linux.cpp @@ -4,6 +4,76 @@ namespace molasses { + namespace unix_system { + + struct syscall1 { + int64_t operator()(int64_t syscall_id, int64_t p1) const { + int64_t ret; + asm volatile("syscall" : "=a"(ret) : "0"(syscall_id), "D"(p1) : "rcx", "r11", "memory"); + return ret; + } + }; + + struct syscall2 { + int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2) const { + int64_t ret; + asm volatile("syscall" : "=a"(ret) : "0"(syscall_id), "D"(p1), "S"(p2) : "rcx", "r11", "memory"); + return ret; + } + }; + + struct syscall3 { + int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3) const { + int64_t ret; + asm volatile("syscall" + : "=a"(ret) + : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3) + : "rcx", "r11", "memory"); + return ret; + } + }; + + struct syscall4 { + int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3, int64_t p4) const { + int64_t ret; + register long r10 asm("r10") = p4; + asm volatile("syscall" + : "=a"(ret) + : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10) + : "rcx", "r11", "memory"); + return ret; + } + }; + + struct syscall5 { + int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3, int64_t p4, int64_t p5) const { + int64_t ret; + register long r10 asm("r10") = p4; + register long r8 asm("r8") = p5; + asm volatile("syscall" + : "=a"(ret) + : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10), "r"(r8) + : "rcx", "r11", "memory"); + return ret; + } + }; + + struct syscall6 { + int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3, int64_t p4, int64_t p5, int64_t p6) const { + int64_t ret; + register long r10 asm("r10") = p4; + register long r8 asm("r8") = p5; + register long r9 asm("r9") = p6; + asm volatile("syscall" + : "=a"(ret) + : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10), "r"(r8), "r"(r9) + : "rcx", "r11", "memory"); + return ret; + } + }; + } + + std::string marshal(const std::string& target) { std::stringstream builder; bool is_first = true; @@ -74,7 +144,15 @@ namespace molasses { " addl %ebx, %eax\n", " andl $0xFFFFFFFF, %eax\n", " pushq %rax\n" - }) + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_a = current_stack.top();current_stack.pop(); + auto value_b = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value_a) or not std::holds_alternative(value_b)) { + // TODO: handle errors + } + current_stack.emplace(get(value_a) + get(value_b)); + } ) ); ctx.operations.emplace_back( @@ -87,7 +165,15 @@ namespace molasses { " popq %rbx\n", " addq %rbx, %rax\n", " pushq %rax\n" - }) + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_a = current_stack.top();current_stack.pop(); + auto value_b = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value_a) or not std::holds_alternative(value_b)) { + // TODO: handle errors + } + current_stack.emplace(get(value_a) + get(value_b)); + } ) ); ctx.operations.emplace_back( @@ -102,7 +188,16 @@ namespace molasses { " pushq %rax\n", " pushq %rdx\n" // TODO: this is actually unsigned division, so it needs improvements on negative numbers - }) + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_a = current_stack.top();current_stack.pop(); + auto value_b = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value_a) or not std::holds_alternative(value_b)) { + // TODO: handle errors + } + current_stack.emplace(get(value_a) / get(value_b)); + current_stack.emplace(get(value_a) % get(value_b)); + } ) ); ctx.operations.emplace_back( @@ -111,7 +206,14 @@ namespace molasses { std::vector({"u8 ptr"}), std::vector({"i64"}), std::vector({ - }) + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value) && not (get(value).pointed->name() == "u8 ptr")) { + // TODO: handle errors + } + current_stack.emplace(intptr_t(get(value).ptr)); + } ) ); ctx.operations.emplace_back( @@ -120,12 +222,20 @@ namespace molasses { std::vector({"i32", "i32"}), std::vector({"i32"}), std::vector({ - " popq %rax\n", - " popq %rbx\n", - " imull %ebx, %eax\n", - " andl $0xFFFFFFFF, %eax\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rbx\n", + " imull %ebx, %eax\n", + " andl $0xFFFFFFFF, %eax\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_a = current_stack.top();current_stack.pop(); + auto value_b = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value_a) or not std::holds_alternative(value_b)) { + // TODO: handle errors + } + current_stack.emplace(get(value_a) * get(value_b)); + } ) ); ctx.operations.emplace_back( @@ -134,12 +244,20 @@ namespace molasses { std::vector({"i32", "i32"}), std::vector({"i32"}), std::vector({ - " popq %rax\n", - " popq %rbx\n", - " subl %ebx, %eax\n", - " andl $0xFFFFFFFF, %eax\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rbx\n", + " subl %ebx, %eax\n", + " andl $0xFFFFFFFF, %eax\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_a = current_stack.top();current_stack.pop(); + auto value_b = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value_a) or not std::holds_alternative(value_b)) { + // TODO: handle errors + } + current_stack.emplace(get(value_a) - get(value_b)); + } ) ); ctx.operations.emplace_back( @@ -148,10 +266,17 @@ namespace molasses { std::vector({"i32"}), std::vector({"i64"}), std::vector({ - " popq %rax\n", - " movslq %eax, %rax\n", - " pushq %rax\n" - }) + " popq %rax\n", + " movslq %eax, %rax\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value)) { + // TODO: handle errors + } + current_stack.emplace(int64_t(get(value))); + } ) ); @@ -162,7 +287,13 @@ namespace molasses { std::vector({}), std::vector({ " popq %rax\n" - }) + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value = current_stack.top();current_stack.pop(); + if(not std::holds_alternative(value)) { + // TODO: handle errors + } + } ) ); @@ -176,7 +307,18 @@ namespace molasses { " popq %rdi\n", " syscall\n", " pushq %rax\n" - }) + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_0 = current_stack.top();current_stack.pop(); + auto value_1 = current_stack.top();current_stack.pop(); + if( + not std::holds_alternative(value_0) or + not std::holds_alternative(value_1) + ) { + // TODO: handle errors + } + unix_system::syscall1{}(get(value_0), get(value_1)); + } ) ); ctx.operations.emplace_back( @@ -185,12 +327,25 @@ namespace molasses { std::vector({"i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ - " popq %rax\n", - " popq %rdi\n", - " popq %rsi\n", - " syscall\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rdi\n", + " popq %rsi\n", + " syscall\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_0 = current_stack.top();current_stack.pop(); + auto value_1 = current_stack.top();current_stack.pop(); + auto value_2 = current_stack.top();current_stack.pop(); + if( + not std::holds_alternative(value_0) or + not std::holds_alternative(value_1) or + not std::holds_alternative(value_2) + ) { + // TODO: handle errors + } + unix_system::syscall2{}(get(value_0), get(value_1), get(value_2)); + } ) ); ctx.operations.emplace_back( @@ -199,13 +354,28 @@ namespace molasses { std::vector({"i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ - " popq %rax\n", - " popq %rdi\n", - " popq %rsi\n", - " popq %rdx\n", - " syscall\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rdi\n", + " popq %rsi\n", + " popq %rdx\n", + " syscall\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_0 = current_stack.top();current_stack.pop(); + auto value_1 = current_stack.top();current_stack.pop(); + auto value_2 = current_stack.top();current_stack.pop(); + auto value_3 = current_stack.top();current_stack.pop(); + if( + not std::holds_alternative(value_0) or + not std::holds_alternative(value_1) or + not std::holds_alternative(value_2) or + not std::holds_alternative(value_3) + ) { + // TODO: handle errors + } + unix_system::syscall3{}(get(value_0), get(value_1), get(value_2), get(value_3)); + } ) ); ctx.operations.emplace_back( @@ -214,14 +384,31 @@ namespace molasses { std::vector({"i64", "i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ - " popq %rax\n", - " popq %rdi\n", - " popq %rsi\n", - " popq %rdx\n", - " popq %r10\n", - " syscall\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rdi\n", + " popq %rsi\n", + " popq %rdx\n", + " popq %r10\n", + " syscall\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_0 = current_stack.top();current_stack.pop(); + auto value_1 = current_stack.top();current_stack.pop(); + auto value_2 = current_stack.top();current_stack.pop(); + auto value_3 = current_stack.top();current_stack.pop(); + auto value_4 = current_stack.top();current_stack.pop(); + if( + not std::holds_alternative(value_0) or + not std::holds_alternative(value_1) or + not std::holds_alternative(value_2) or + not std::holds_alternative(value_3) or + not std::holds_alternative(value_4) + ) { + // TODO: handle errors + } + unix_system::syscall4{}(get(value_0), get(value_1), get(value_2), get(value_3), get(value_4)); + } ) ); ctx.operations.emplace_back( @@ -230,15 +417,34 @@ namespace molasses { std::vector({"i64", "i64", "i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ - " popq %rax\n", - " popq %rdi\n", - " popq %rsi\n", - " popq %rdx\n", - " popq %r10\n", - " popq %r8\n", - " syscall\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rdi\n", + " popq %rsi\n", + " popq %rdx\n", + " popq %r10\n", + " popq %r8\n", + " syscall\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_0 = current_stack.top();current_stack.pop(); + auto value_1 = current_stack.top();current_stack.pop(); + auto value_2 = current_stack.top();current_stack.pop(); + auto value_3 = current_stack.top();current_stack.pop(); + auto value_4 = current_stack.top();current_stack.pop(); + auto value_5 = current_stack.top();current_stack.pop(); + if( + not std::holds_alternative(value_0) or + not std::holds_alternative(value_1) or + not std::holds_alternative(value_2) or + not std::holds_alternative(value_3) or + not std::holds_alternative(value_4) or + not std::holds_alternative(value_5) + ) { + // TODO: handle errors + } + unix_system::syscall5{}(get(value_0), get(value_1), get(value_2), get(value_3), get(value_4), get(value_5)); + } ) ); ctx.operations.emplace_back( @@ -247,16 +453,46 @@ namespace molasses { std::vector({"i64", "i64", "i64", "i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ - " popq %rax\n", - " popq %rdi\n", - " popq %rsi\n", - " popq %rdx\n", - " popq %r10\n", - " popq %r8\n", - " popq %r9\n", - " syscall\n", - " pushq %rax\n" - }) + " popq %rax\n", + " popq %rdi\n", + " popq %rsi\n", + " popq %rdx\n", + " popq %r10\n", + " popq %r8\n", + " popq %r9\n", + " syscall\n", + " pushq %rax\n" + }), + [](const generate_context&, interpreter_stack& current_stack){ + auto value_0 = current_stack.top();current_stack.pop(); + auto value_1 = current_stack.top();current_stack.pop(); + auto value_2 = current_stack.top();current_stack.pop(); + auto value_3 = current_stack.top();current_stack.pop(); + auto value_4 = current_stack.top();current_stack.pop(); + auto value_5 = current_stack.top();current_stack.pop(); + auto value_6 = current_stack.top();current_stack.pop(); + if( + not std::holds_alternative(value_0) or + not std::holds_alternative(value_1) or + not std::holds_alternative(value_2) or + not std::holds_alternative(value_3) or + not std::holds_alternative(value_4) or + not std::holds_alternative(value_5) or + not std::holds_alternative(value_6) + ) { + // TODO: handle errors + } + unix_system::syscall6{}( + get(value_0), + get(value_1), + get(value_2), + get(value_3), + get(value_4), + get(value_5), + get(value_6) + ); + + } ) ); diff --git a/src/molasses/lexer.cpp b/src/molasses/lexer.cpp index d593212..7cbff64 100644 --- a/src/molasses/lexer.cpp +++ b/src/molasses/lexer.cpp @@ -57,6 +57,7 @@ namespace molasses { default: builder << character; } + state = state_machine_t::string; continue; } if(character == '\"') { diff --git a/src/molasses/parser_primitives.cpp b/src/molasses/parser_primitives.cpp index 7b92323..43daa23 100644 --- a/src/molasses/parser_primitives.cpp +++ b/src/molasses/parser_primitives.cpp @@ -297,4 +297,9 @@ namespace molasses { return generated; } + + void procedure_operation::execute(const generate_context& ctx, interpreter_stack& stack) const { + + } } + diff --git a/tests/004.exp b/tests/004.exp index 0b58287..d504d34 100644 --- a/tests/004.exp +++ b/tests/004.exp @@ -7,6 +7,8 @@ proc abort {reason} { exit 1 } +### ------------------------------------------------------------------ + spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-01.mol lex parse expect { eof { abort "should display failure in incomplete-01" } @@ -14,6 +16,8 @@ expect { } expect eof +### ------------------------------------------------------------------ + spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-02.mol lex parse expect { eof { abort "should display failure in incomplete-02" } @@ -25,6 +29,8 @@ expect { } expect eof +### ------------------------------------------------------------------ + spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-03.mol lex parse expect { eof { abort "should display failure in incomplete-03" } @@ -36,6 +42,8 @@ expect { } expect eof +### ------------------------------------------------------------------ + spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-04.mol lex parse expect { eof { abort "should display failure in incomplete-04" } @@ -47,6 +55,8 @@ expect { } expect eof +### ------------------------------------------------------------------ + spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-05.mol lex parse expect { eof { abort "should display failure in incomplete-05" } @@ -58,6 +68,8 @@ expect { } expect eof +### ------------------------------------------------------------------ + spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-06.mol lex parse expect { eof { abort "should display failure in incomplete-06" } @@ -69,3 +81,24 @@ expect { } expect eof +### ------------------------------------------------------------------ + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-07.mol lex parse +expect { + eof { abort "should display failure line" } + {incomplete-07.mol:2} +} +expect { + eof { abort "should display failure in incomplete-07" } + {Expected __PROC__} +} +expect eof + +### ------------------------------------------------------------------ + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-08.mol lex parse +expect { + eof { abort "should display failure in incomplete-08" } + {return stack} +} +expect eof \ No newline at end of file diff --git a/tests/004/incomplete-07.mol b/tests/004/incomplete-07.mol new file mode 100644 index 0000000..5a69ded --- /dev/null +++ b/tests/004/incomplete-07.mol @@ -0,0 +1,5 @@ +main +i64 +__--__ +i64 +__DO__ \ No newline at end of file diff --git a/tests/004/incomplete-08.mol b/tests/004/incomplete-08.mol new file mode 100644 index 0000000..dd0868d --- /dev/null +++ b/tests/004/incomplete-08.mol @@ -0,0 +1,7 @@ +__PROC__ main +i64 +__--__ +i64 +__DO__ +1_i64 2_i64 +__END__ \ No newline at end of file diff --git a/tests/005.exp b/tests/005.exp new file mode 100644 index 0000000..37c12e5 --- /dev/null +++ b/tests/005.exp @@ -0,0 +1,75 @@ +#!/usr/bin/expect + +set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE) +set BUILD_NAME 005. + +proc abort {reason} { + puts "test failed $reason" + exit 1 +} + +spawn -noecho $SUGAR_EXECUTABLE tests/005/exit-with-3.mol tests/005/library.mol lex-all merge-all +expect { + error { abort "failed to parse" } + eof { abort "cannot find the symbol main in lexed output" } + main +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 0} { + abort "compiler crashed" +} + +spawn -noecho $SUGAR_EXECUTABLE tests/005/exit-with-3.mol tests/005/library.mol lex-all merge-all parse +expect { + error { abort "failed to parse" } + eof { abort "cannot find the main procedure" } + main +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 0} { + abort "compiler crashed" +} + +spawn -noecho $SUGAR_EXECUTABLE tests/005/exit-with-3.mol tests/005/library.mol lex-all merge-all parse +expect { + error { abort "failed to parse" } + eof { abort "cannot find the exit-syscall-number procedure" } + exit-syscall-number +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 0} { + abort "compiler crashed" +} + + +spawn -noecho $SUGAR_EXECUTABLE tests/005/exit-with-3.mol lex tests/005/library.mol lex merge parse /tmp/sugar.generated.$BUILD_NAME generate assemble +expect { + error { abort "failed to compile" } + eof { abort "didn't run clang" } + clang +} +expect { + error { abort "failed to link" } + eof { abort "didn't run ld" } + ld +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 0} { + abort "compiler crashed" +} + +spawn -noecho /tmp/sugar.generated.$BUILD_NAME +expect { + error { abort "failed to compile" } + eof { abort "didn't output" } + Hello +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 3} { + abort "executable didn't return exit code 3 but $value instead" +} \ No newline at end of file diff --git a/tests/005/exit-with-3.mol b/tests/005/exit-with-3.mol new file mode 100644 index 0000000..1e3409f --- /dev/null +++ b/tests/005/exit-with-3.mol @@ -0,0 +1,7 @@ +__PROC__ main +__--__ +__DO__ + +11_i64 "\tHello world\n" write-out +1 2_i32 + i32-to-i64 exit +__END__ diff --git a/tests/005/library.mol b/tests/005/library.mol new file mode 100644 index 0000000..0305152 --- /dev/null +++ b/tests/005/library.mol @@ -0,0 +1,35 @@ +__PROC__ write-syscall-number +__--__ +i64 +__DO__ +1_i64 +__END__ + +__PROC__ stdout-fd +__--__ +i64 +__DO__ +1_i64 +__END__ + +__PROC__ exit-syscall-number +__--__ +i64 +__DO__ +60_i64 +__END__ + +__PROC__ exit +i64 +__--__ +__DO__ +exit-syscall-number syscall1 drop_i64 +__END__ + +__PROC__ write-out +i64 +u8 ptr +__--__ +__DO__ +u8-ptr_to_i64 stdout-fd write-syscall-number syscall3 drop_i64 +__END__ \ No newline at end of file diff --git a/tests/006.exp b/tests/006.exp new file mode 100644 index 0000000..fd317b0 --- /dev/null +++ b/tests/006.exp @@ -0,0 +1,158 @@ +#!/usr/bin/expect + +set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE) +set BUILD_NAME 005. + +proc abort {reason} { + puts "test failed $reason" + exit 1 +} + +spawn -noecho $SUGAR_EXECUTABLE lex +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE "fake_01.006.not_a_file" lex +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + "does not exist" +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE "fake_01.006.not_a_file" lex-all +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + "does not exist" +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE "tests/005/library.mol" lex merge +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE merge +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE parse +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE generate +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +file delete "/tmp/006.fake_target" +spawn -noecho $SUGAR_EXECUTABLE "/tmp/006.fake_target" generate +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + "parsed output" +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} +if {[file exists "/tmp/006.fake_target"]} { + abort "created a file even if it failed to compile anything" + file delete "/tmp/006.fake_target" +} + +spawn -noecho $SUGAR_EXECUTABLE merge-all +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE assemble +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + expects +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value == 0} { + abort "compiler didn't crash" +} + +spawn -noecho $SUGAR_EXECUTABLE help +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + Commands +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 0} { + abort "compiler crashed displaying help" +} + +spawn -noecho $SUGAR_EXECUTABLE --help +expect { + error { abort "failed to parse" } + eof { abort "failed silently" } + Commands +} +expect eof +lassign [wait] pid spawnid os_error_flag value +if {$value != 0} { + abort "compiler crashed displaying help" +} \ No newline at end of file