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 @@
+
+
+
+
@@ -44,5 +48,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