Browse Source

Added tests for error messages on all build system known failure

- Added pieces of code to handle interpreter shenanigans
  - Must add error handling
  - must test the syscalls
- Added tests for errors
- Changed error messages
- Better exceptions/messages
master
Ludovic 'Archivist' Lagouardette 1 year ago
parent
commit
e33b26a276
15 changed files with 701 additions and 94 deletions
  1. +1
    -0
      .gitignore
  2. +9
    -0
      .idea/codeStyles/Project.xml
  3. +2
    -0
      CMakeLists.txt
  4. +35
    -12
      include/molasses/parser_primitives.h
  5. +30
    -20
      src/main.cpp
  6. +298
    -62
      src/molasses/generator_primitives_x86_64_linux.cpp
  7. +1
    -0
      src/molasses/lexer.cpp
  8. +5
    -0
      src/molasses/parser_primitives.cpp
  9. +33
    -0
      tests/004.exp
  10. +5
    -0
      tests/004/incomplete-07.mol
  11. +7
    -0
      tests/004/incomplete-08.mol
  12. +75
    -0
      tests/005.exp
  13. +7
    -0
      tests/005/exit-with-3.mol
  14. +35
    -0
      tests/005/library.mol
  15. +158
    -0
      tests/006.exp

+ 1
- 0
.gitignore View File

@ -1 +1,2 @@
/cmake-build-debug
/cmake-build-debug-coverage

+ 9
- 0
.idea/codeStyles/Project.xml View File

@ -1,6 +1,10 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<Objective-C>
<option name="CLASS_CONSTRUCTOR_INIT_LIST_COMMA_ON_NEXT_LINE" value="true" />
<option name="SUPERCLASS_LIST_COMMA_ON_NEXT_LINE" value="true" />
</Objective-C>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
@ -44,5 +48,10 @@
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
<codeStyleSettings language="ObjectiveC">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

+ 2
- 0
CMakeLists.txt View File

@ -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)

+ 35
- 12
include/molasses/parser_primitives.h View File

@ -1,12 +1,16 @@
#pragma once
#include <string>
#include <set>
#include <vector>
#include <memory>
#include <optional>
#include <charconv>
#include <concepts>
#include <functional>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <variant>
#include <vector>
#include "molasses/lexer.h"
@ -71,13 +75,25 @@ namespace molasses {
};
struct parser_context;
struct ptr_type{
void* ptr;
std::shared_ptr<type> pointed;
};
using stack_element = std::variant<int32_t, int64_t, ptr_type>;
using interpreter_stack = std::stack<stack_element>;
struct generate_context;
struct operation {
[[nodiscard]] virtual std::string name() const = 0;
[[nodiscard]] virtual std::vector<std::string> argument_types() const = 0;
[[nodiscard]] virtual std::vector<std::string> return_types() const = 0;
[[nodiscard]] virtual std::vector<std::string> generate(const parser_context&, const lexed_output& lexer_data) const = 0;
[[nodiscard]] virtual std::vector<std::string> 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<std::string> _args;
std::vector<std::string> _rets;
std::vector<std::string> _instructions;
std::function<void(const generate_context&, interpreter_stack&)> _executable;
primitive_operation(std::string name, std::vector<std::string> args, std::vector<std::string> rets, std::vector<std::string> body)
primitive_operation(std::string name, std::vector<std::string> args, std::vector<std::string> rets, std::vector<std::string> body, std::function<void(const generate_context&,interpreter_stack&)> executable)
: _name(std::forward<std::string>(name))
, _args(std::forward<std::vector<std::string>>(args))
, _rets(std::forward<std::vector<std::string>>(rets))
, _instructions(std::forward<std::vector<std::string>>(body))
, _executable(std::move(executable))
{}
[[nodiscard]] std::string name() const final {
@ -108,6 +126,10 @@ namespace molasses {
[[nodiscard]] std::vector<std::string> 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<std::string> generate(const parser_context&, const lexed_output& lexer_data) const final;
[[nodiscard]] std::vector<std::string> 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 completione">\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 {

+ 30
- 20
src/main.cpp View File

@ -10,6 +10,12 @@
using compile_element = std::variant<molasses::lexed_output, molasses::generate_context, std::string>;
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<std::string> 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<std::string>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
auto filename = std::get<std::string>(compile_stack.top());
compile_stack.pop();
if(std::holds_alternative<molasses::generate_context>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<molasses::generate_context>(compile_stack.top())) {
auto generator = std::get<molasses::generate_context>(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 f">build_system_error("generate expects a parsed output\n");
} else
throw ">std::runtime_error("generate expects a filename");
throw f">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<molasses::lexed_output>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
auto lexer = std::get<molasses::lexed_output>(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 f">build_system_error("parse expects a lexed output\n");
} else if(elem == "lex") {
if(std::holds_alternative<std::string>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
auto filename = std::get<std::string>(compile_stack.top());
compile_stack.pop();
if(not std::filesystem::exists(filename))
throw ">std::runtime_error("file " + filename + " does not exist");
throw f">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 f">build_system_error("lex expects a filename\n");
} else if(elem == "lex-all") {
std::vector<molasses::lexed_output> lexed_list;
while(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
auto filename = std::get<std::string>(compile_stack.top());
compile_stack.pop();
if(not std::filesystem::exists(filename))
throw ">std::runtime_error("file " + filename + " does not exist");
throw f">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<molasses::lexed_output>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
auto lexer_1 = std::get<molasses::lexed_output>(compile_stack.top());
compile_stack.pop();
if(std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
auto lexer_2 = std::get<molasses::lexed_output>(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 f">build_system_error("merge expects 2 lexed outputs\n");
} else
throw ">std::runtime_error("merge expects 2 lexed outputs");
throw f">build_system_error("merge expects 2 lexed outputs\n");
} else if(elem == "merge-all") {
if(std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(compile_stack.top())) {
auto lexer_1 = std::get<molasses::lexed_output>(compile_stack.top());
compile_stack.pop();
while(not compile_stack.empty() and std::holds_alternative<molasses::lexed_output>(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 f">build_system_error("merge-all expects at least 1 lexed outputs\n");
} else if(elem == "assemble") {
if(std::holds_alternative<std::string>(compile_stack.top())) {
if(not compile_stack.empty() and std::holds_alternative<std::string>(compile_stack.top())) {
auto filename = std::get<std::string>(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 f">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<molasses::lexed_output>(compile_stack.top())) {
auto lexer = std::get<molasses::lexed_output>(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;
}
}

+ 298
- 62
src/molasses/generator_primitives_x86_64_linux.cpp View File

@ -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<int32_t>(value_a) or not std::holds_alternative<int32_t>(value_b)) {
// TODO: handle errors
}
current_stack.emplace(get<int32_t>(value_a) + get<int32_t>(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<int64_t>(value_a) or not std::holds_alternative<int64_t>(value_b)) {
// TODO: handle errors
}
current_stack.emplace(get<int64_t>(value_a) + get<int64_t>(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<int64_t>(value_a) or not std::holds_alternative<int64_t>(value_b)) {
// TODO: handle errors
}
current_stack.emplace(get<int64_t>(value_a) / get<int64_t>(value_b));
current_stack.emplace(get<int64_t>(value_a) % get<int64_t>(value_b));
}
)
);
ctx.operations.emplace_back(
@ -111,7 +206,14 @@ namespace molasses {
std::vector<std::string>({"u8 ptr"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
})
}),
[](const generate_context&, interpreter_stack& current_stack){
auto value = current_stack.top();current_stack.pop();
if(not std::holds_alternative<ptr_type>(value) && not (get<ptr_type>(value).pointed->name() == "u8 ptr")) {
// TODO: handle errors
}
current_stack.emplace(intptr_t(get<ptr_type>(value).ptr));
}
)
);
ctx.operations.emplace_back(
@ -120,12 +222,20 @@ namespace molasses {
std::vector<std::string>({"i32", "i32"}),
std::vector<std::string>({"i32"}),
std::vector<std::string>({
" 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<int32_t>(value_a) or not std::holds_alternative<int32_t>(value_b)) {
// TODO: handle errors
}
current_stack.emplace(get<int32_t>(value_a) * get<int32_t>(value_b));
}
)
);
ctx.operations.emplace_back(
@ -134,12 +244,20 @@ namespace molasses {
std::vector<std::string>({"i32", "i32"}),
std::vector<std::string>({"i32"}),
std::vector<std::string>({
" 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<int32_t>(value_a) or not std::holds_alternative<int32_t>(value_b)) {
// TODO: handle errors
}
current_stack.emplace(get<int32_t>(value_a) - get<int32_t>(value_b));
}
)
);
ctx.operations.emplace_back(
@ -148,10 +266,17 @@ namespace molasses {
std::vector<std::string>({"i32"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
" 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<int32_t>(value)) {
// TODO: handle errors
}
current_stack.emplace(int64_t(get<int32_t>(value)));
}
)
);
@ -162,7 +287,13 @@ namespace molasses {
std::vector<std::string>({}),
std::vector<std::string>({
" popq %rax\n"
})
}),
[](const generate_context&, interpreter_stack& current_stack){
auto value = current_stack.top();current_stack.pop();
if(not std::holds_alternative<int64_t>(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<int64_t>(value_0) or
not std::holds_alternative<int64_t>(value_1)
) {
// TODO: handle errors
}
unix_system::syscall1{}(get<int64_t>(value_0), get<int64_t>(value_1));
}
)
);
ctx.operations.emplace_back(
@ -185,12 +327,25 @@ namespace molasses {
std::vector<std::string>({"i64", "i64", "i64"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
" 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<int64_t>(value_0) or
not std::holds_alternative<int64_t>(value_1) or
not std::holds_alternative<int64_t>(value_2)
) {
// TODO: handle errors
}
unix_system::syscall2{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2));
}
)
);
ctx.operations.emplace_back(
@ -199,13 +354,28 @@ namespace molasses {
std::vector<std::string>({"i64", "i64", "i64", "i64"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
" 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<int64_t>(value_0) or
not std::holds_alternative<int64_t>(value_1) or
not std::holds_alternative<int64_t>(value_2) or
not std::holds_alternative<int64_t>(value_3)
) {
// TODO: handle errors
}
unix_system::syscall3{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2), get<int64_t>(value_3));
}
)
);
ctx.operations.emplace_back(
@ -214,14 +384,31 @@ namespace molasses {
std::vector<std::string>({"i64", "i64", "i64", "i64", "i64"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
" 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<int64_t>(value_0) or
not std::holds_alternative<int64_t>(value_1) or
not std::holds_alternative<int64_t>(value_2) or
not std::holds_alternative<int64_t>(value_3) or
not std::holds_alternative<int64_t>(value_4)
) {
// TODO: handle errors
}
unix_system::syscall4{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2), get<int64_t>(value_3), get<int64_t>(value_4));
}
)
);
ctx.operations.emplace_back(
@ -230,15 +417,34 @@ namespace molasses {
std::vector<std::string>({"i64", "i64", "i64", "i64", "i64", "i64"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
" 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<int64_t>(value_0) or
not std::holds_alternative<int64_t>(value_1) or
not std::holds_alternative<int64_t>(value_2) or
not std::holds_alternative<int64_t>(value_3) or
not std::holds_alternative<int64_t>(value_4) or
not std::holds_alternative<int64_t>(value_5)
) {
// TODO: handle errors
}
unix_system::syscall5{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2), get<int64_t>(value_3), get<int64_t>(value_4), get<int64_t>(value_5));
}
)
);
ctx.operations.emplace_back(
@ -247,16 +453,46 @@ namespace molasses {
std::vector<std::string>({"i64", "i64", "i64", "i64", "i64", "i64", "i64"}),
std::vector<std::string>({"i64"}),
std::vector<std::string>({
" 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<int64_t>(value_0) or
not std::holds_alternative<int64_t>(value_1) or
not std::holds_alternative<int64_t>(value_2) or
not std::holds_alternative<int64_t>(value_3) or
not std::holds_alternative<int64_t>(value_4) or
not std::holds_alternative<int64_t>(value_5) or
not std::holds_alternative<int64_t>(value_6)
) {
// TODO: handle errors
}
unix_system::syscall6{}(
get<int64_t>(value_0),
get<int64_t>(value_1),
get<int64_t>(value_2),
get<int64_t>(value_3),
get<int64_t>(value_4),
get<int64_t>(value_5),
get<int64_t>(value_6)
);
}
)
);

+ 1
- 0
src/molasses/lexer.cpp View File

@ -57,6 +57,7 @@ namespace molasses {
default:
builder << character;
}
state = state_machine_t::string;
continue;
}
if(character == '\"') {

+ 5
- 0
src/molasses/parser_primitives.cpp View File

@ -297,4 +297,9 @@ namespace molasses {
return generated;
}
void procedure_operation::execute(const generate_context& ctx, interpreter_stack& stack) const {
}
}

+ 33
- 0
tests/004.exp View File

@ -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

+ 5
- 0
tests/004/incomplete-07.mol View File

@ -0,0 +1,5 @@
main
i64
__--__
i64
__DO__

+ 7
- 0
tests/004/incomplete-08.mol View File

@ -0,0 +1,7 @@
__PROC__ main
i64
__--__
i64
__DO__
1_i64 2_i64
__END__

+ 75
- 0
tests/005.exp View File

@ -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"
}

+ 7
- 0
tests/005/exit-with-3.mol View File

@ -0,0 +1,7 @@
__PROC__ main
__--__
__DO__
11_i64 "\tHello world\n" write-out
1 2_i32 + i32-to-i64 exit
__END__

+ 35
- 0
tests/005/library.mol View File

@ -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__

+ 158
- 0
tests/006.exp View File

@ -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"
}

Loading…
Cancel
Save