2 Commits

Author SHA1 Message Date
  Ludovic 'Archivist' Lagouardette ee0243bd8a Added __JUMP_IF__ operation, its generator, and a a test 8 months ago
  Ludovic 'Archivist' Lagouardette 85e7c18bf2 Fixed 2 bugs 8 months ago
16 changed files with 206 additions and 18 deletions
Split View
  1. +1
    -0
      CMakeLists.txt
  2. +7
    -0
      include/molasses/errors.h
  3. +3
    -0
      include/molasses/generator_primitives.h
  4. +10
    -0
      src/molasses/generator_primitives_x86_64_linux.cpp
  5. +4
    -4
      src/molasses/lexer.cpp
  6. +40
    -7
      src/molasses/parser_primitives.cpp
  7. +1
    -1
      tests/001.exp
  8. +1
    -1
      tests/002.exp
  9. +1
    -1
      tests/003.exp
  10. +1
    -1
      tests/005.exp
  11. +1
    -1
      tests/006.exp
  12. +1
    -1
      tests/007.exp
  13. +1
    -1
      tests/007/exit-with-3.mol
  14. +82
    -0
      tests/008.exp
  15. +13
    -0
      tests/008/exit-with-3.mol
  16. +39
    -0
      tests/008/library.mol

+ 1
- 0
CMakeLists.txt View File

@ -37,6 +37,7 @@ add_expect_test(id004 ./tests/004.exp)
add_expect_test(id005 ./tests/005.exp)
add_expect_test(id006 ./tests/006.exp)
add_expect_test(id007 ./tests/007.exp)
add_expect_test(id008 ./tests/008.exp)

+ 7
- 0
include/molasses/errors.h View File

@ -61,6 +61,13 @@ namespace molasses {
"\tExpected ", expected, "\n", "\tFound ", found, "\n"
)) {}
};
struct duplicate_label_error : public parser_error {
duplicate_label_error(const symbol& sym, const std::string& found)
: parser_error(details::concatenate_builder(
"Unexpected label duplication\n", "\tAt ", sym.file_name, ":", sym.line, ":", sym.column, "\n",
"\tFound ", found, " duplicated label\n"
)) {}
};
struct expecting_token_error : public parser_error {
expecting_token_error(const std::string& expected, const std::string& context)
: parser_error(details::concatenate_builder(

+ 3
- 0
include/molasses/generator_primitives.h View File

@ -14,6 +14,9 @@ namespace molasses {
template<architecture_t Arch = architecture>
std::vector<std::string> generate_goto(const std::string& target);
template<architecture_t Arch = architecture>
std::vector<std::string> generate_jump_if(const std::string& target);
template<architecture_t Arch = architecture>
std::vector<std::string> generate_push_int32(int32_t target);

+ 10
- 0
src/molasses/generator_primitives_x86_64_linux.cpp View File

@ -549,6 +549,16 @@ namespace molasses {
};
}
template<>
std::vector<std::string> generate_jump_if<architecture_t::x86_64_linux>(const std::string& target) {
return {
" popq %rax\n",
" pushq %rax\n",
" cmpq $0, %rax\n"
" jne "+marshal(target)+"\n"
};
}
template<>
std::vector<std::string> generate_return<architecture_t::x86_64_linux>() {
return {

+ 4
- 4
src/molasses/lexer.cpp View File

@ -44,10 +44,6 @@ namespace molasses {
};
for(auto& character : source) {
if(character == '\n') {
line++;
column = 0;
}
column++;
if(state == state_machine_t::string_escape) {
switch(character) {
@ -79,6 +75,10 @@ namespace molasses {
} else {
builder << character;
}
if(character == '\n') {
line++;
column = 0;
}
} else {
if(state == state_machine_t::string_end) {
std::stringstream quoted;

+ 40
- 7
src/molasses/parser_primitives.cpp View File

@ -107,6 +107,13 @@ namespace molasses {
effective_snapshots[idx] = type_stack;
it = ahead;
++idx;
} else if(auto ahead = it; ++ahead != consumed_stream.end() and (lexer_state.dictionary.at(*ahead) == "__JUMP_IF__")) {
if(type_stack.empty()) return false;
if(type_stack.back() != "i64") return false;
//type_stack.pop_back(); // JUMP_IF does not consume its test variable
effective_snapshots[idx] = type_stack;
it = ahead;
++idx;
} else if(symbol.is_string) {
type_stack.emplace_back("u8 ptr");
} else if(auto is_int32 = try_parse_int32(symbol_text); is_int32) {
@ -134,7 +141,8 @@ namespace molasses {
PROC_KW,
END_KW,
LABEL_KW,
GOTO_KW
GOTO_KW,
JUMP_IF_KW
};
lexed_output fake;
@ -144,6 +152,7 @@ namespace molasses {
fake.dictionary[END_KW] = "__END__";
fake.dictionary[LABEL_KW] = "__LABEL__";
fake.dictionary[GOTO_KW] = "__GOTO__";
fake.dictionary[JUMP_IF_KW] = "__JUMP_IF__";
auto tokens = concatenate(fake, lexer_data);
@ -176,12 +185,11 @@ namespace molasses {
throw expecting_token_error(expected, context); \
}}while(false)
decltype(it) last_valid;
decltype(it) last_valid = it;
if(*it != PROC_KW) {
throw unexpected_token_error(*it, tokens.dictionary[*it],tokens.dictionary[PROC_KW]);
}
last_valid = it;
++it;
check_for_unexpected_stream_end("Procedure-Name", details::concatenate_builder("In top level, file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column));
@ -222,13 +230,19 @@ namespace molasses {
std::vector<std::pair<size_t, size_t>> sub_bodies;
std::map<std::string, size_t> found_labels;
std::map<std::string, size_t> found_gotos;
std::map<std::string, size_t> found_jump_ifs;
while(*it != END_KW) {
if(auto ahead = it; ++ahead != tokens.symbols.end() and (*ahead == GOTO_KW or *ahead == LABEL_KW)) {
if(auto ahead = it; ++ahead != tokens.symbols.end() and (*ahead == GOTO_KW or *ahead == JUMP_IF_KW or *ahead == LABEL_KW)) {
if(*ahead == GOTO_KW) {
found_gotos[tokens.dictionary[*it]] = body.size();
} else if(*ahead == JUMP_IF_KW) {
found_jump_ifs[tokens.dictionary[*it]] = body.size();
} else if(*ahead == LABEL_KW) {
found_labels[tokens.dictionary[*it]] = body.size();
// TODO: Handle duplicate labels
auto label_value = tokens.dictionary[*it];
if(found_labels.contains(label_value)) {
throw duplicate_label_error(*it, label_value);
}
found_labels[label_value] = body.size();
}
body.emplace_back(*it);
body.emplace_back(*ahead);
@ -249,6 +263,14 @@ namespace molasses {
throw orphan_goto_error(body[index], dest);
}
sub_bodies.emplace_back(std::min(index, found_labels[dest]), std::max(index, found_labels[dest]));
}
for(auto& [dest, index] : found_jump_ifs) {
if(not found_labels.contains(dest)) {
throw orphan_goto_error(body[index], dest);
}
sub_bodies.emplace_back(std::min(index, found_labels[dest]), std::max(index, found_labels[dest]));
}
@ -288,7 +310,14 @@ namespace molasses {
for(auto it = _body.begin(); it != _body.end(); ++it) {
auto elem = *it;
auto token = lexer_data.dictionary.at(elem);
if(auto ahead = it; ++ahead != _body.end() and (lexer_data.dictionary.at(*ahead) == "__GOTO__" or lexer_data.dictionary.at(*ahead) == "__LABEL__")) {
if(
auto ahead = it;
++ahead != _body.end() and (
lexer_data.dictionary.at(*ahead) == "__GOTO__"
or lexer_data.dictionary.at(*ahead) == "__LABEL__"
or lexer_data.dictionary.at(*ahead) == "__JUMP_IF__"
)
) {
if(lexer_data.dictionary.at(*ahead) == "__GOTO__") {
for(auto&& instruction : generate_goto(name() + " in " + token)) {
ops.push_back(instruction);
@ -297,6 +326,10 @@ namespace molasses {
for(auto&& instruction : generate_label(name() + " in " + token)) {
ops.push_back(instruction);
}
} else if(lexer_data.dictionary.at(*ahead) == "__JUMP_IF__") {
for(auto&& instruction : generate_jump_if(name() + " in " + token)) {
ops.push_back(instruction);
}
}
it = ahead;
} else if(elem.is_string) {

+ 1
- 1
tests/001.exp View File

@ -1,7 +1,7 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 001.
set BUILD_NAME 001
proc abort {reason} {
puts "test failed $reason"

+ 1
- 1
tests/002.exp View File

@ -1,7 +1,7 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 002.
set BUILD_NAME 002
proc abort {reason} {
puts "test failed $reason"

+ 1
- 1
tests/003.exp View File

@ -1,7 +1,7 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 003.
set BUILD_NAME 003
proc abort {reason} {
puts "test failed $reason"

+ 1
- 1
tests/005.exp View File

@ -1,7 +1,7 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 005.
set BUILD_NAME 005
proc abort {reason} {
puts "test failed $reason"

+ 1
- 1
tests/006.exp View File

@ -1,7 +1,7 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 006.
set BUILD_NAME 006
proc abort {reason} {
puts "test failed $reason"

+ 1
- 1
tests/007.exp View File

@ -1,7 +1,7 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 007.
set BUILD_NAME 007
proc abort {reason} {
puts "test failed $reason"

+ 1
- 1
tests/007/exit-with-3.mol View File

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

+ 82
- 0
tests/008.exp View File

@ -0,0 +1,82 @@
#!/usr/bin/expect
set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE)
set BUILD_NAME 008
proc abort {reason} {
puts "test failed $reason"
exit 1
}
spawn -noecho $SUGAR_EXECUTABLE tests/008/exit-with-3.mol tests/008/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/008/exit-with-3.mol tests/008/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/008/exit-with-3.mol tests/008/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/008/exit-with-3.mol lex tests/008/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"
}

+ 13
- 0
tests/008/exit-with-3.mol View File

@ -0,0 +1,13 @@
__PROC__ main
__--__
__DO__
10_i64
"SAUSAGE" __LABEL__
13_i64 "\tHello world\n" write-out
-1_i64 +_i64
"SAUSAGE" __JUMP_IF__ drop_i64
1 2_i32 + i32-to-i64 exit
__END__

+ 39
- 0
tests/008/library.mol View File

@ -0,0 +1,39 @@
__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__
"POTAT" __LABEL__
exit-syscall-number syscall1 drop_i64
60_i64
"POTAT" __GOTO__
drop_i64
__END__
__PROC__ write-out
i64
u8 ptr
__--__
__DO__
u8-ptr_to_i64 stdout-fd write-syscall-number syscall3 drop_i64
__END__

Loading…
Cancel
Save