diff --git a/CMakeLists.txt b/CMakeLists.txt index 62407e3..1c96707 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,3 +30,4 @@ endfunction() 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) diff --git a/include/molasses/parser_primitives.h b/include/molasses/parser_primitives.h index 864ccf0..b7abd52 100644 --- a/include/molasses/parser_primitives.h +++ b/include/molasses/parser_primitives.h @@ -141,7 +141,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 { @@ -157,7 +157,7 @@ namespace molasses { // 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 +168,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 +180,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/molasses/parser_primitives.cpp b/src/molasses/parser_primitives.cpp index f0e1a33..7b92323 100644 --- a/src/molasses/parser_primitives.cpp +++ b/src/molasses/parser_primitives.cpp @@ -152,47 +152,57 @@ namespace molasses { do{if(it == tokens.symbols.end()) { \ throw expecting_token_error(expected, context); \ }}while(false) - - if(*it != PROC_KW) { + + decltype(it) last_valid; + + 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(tokens.dictionary[PROC_KW], details::concatenate_builder("In top level, file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end("Procedure-Name", details::concatenate_builder("In top level, file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); std::string name = tokens.dictionary.at(*it); auto& name_symbol = *it; + last_valid = it; ++it; - check_for_unexpected_stream_end("Procedure-Name", details::concatenate_builder("In top level, file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end(tokens.dictionary[SEPARATOR_KW], details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); // Process arguments list std::vector argument_types; while(*it != SEPARATOR_KW) { argument_types.emplace_back(tokens.dictionary.at(*it)); + last_valid = it; ++it; - check_for_unexpected_stream_end(tokens.dictionary[SEPARATOR_KW], details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end("Procedure-Argument-List to end", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); } + last_valid = it; ++it; - check_for_unexpected_stream_end("Procedure-Argument-List", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end("Procedure-Argument-List to be followed by a return list or a __DO__", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); argument_types = compact_type_modifiers(argument_types); // Process return types list std::vector return_types; while(*it != DO_KW) { return_types.emplace_back(tokens.dictionary.at(*it)); + last_valid = it; ++it; - check_for_unexpected_stream_end(tokens.dictionary[DO_KW], details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end("Procedure-Return-List to end", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); } + last_valid = it; ++it; - check_for_unexpected_stream_end("Procedure-Return-List", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end("__DO__ block needs a matching __END__", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); return_types = compact_type_modifiers(return_types); - // Process return types list + // Process body std::vector body; while(*it != END_KW) { body.emplace_back(*it); + last_valid = it; ++it; - check_for_unexpected_stream_end(tokens.dictionary[END_KW], details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",it->file_name, ":", it->line, ":", it->column)); + check_for_unexpected_stream_end("__DO__ block needs a matching __END__", details::concatenate_builder("Parsing procedure ",tokens.dictionary.at(name_symbol),", file ",last_valid->file_name, ":", last_valid->line, ":", last_valid->column)); } + last_valid = it; ++it; return std::make_pair(it, std::make_shared(name, argument_types, return_types, body)); diff --git a/tests/004.exp b/tests/004.exp new file mode 100644 index 0000000..0b58287 --- /dev/null +++ b/tests/004.exp @@ -0,0 +1,71 @@ +#!/usr/bin/expect + +set SUGAR_EXECUTABLE $::env(SUGAR_EXECUTABLE) + +proc abort {reason} { + puts "test failed $reason" + exit 1 +} + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-01.mol lex parse +expect { + eof { abort "should display failure in incomplete-01" } + Procedure-Name +} +expect eof + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-02.mol lex parse +expect { + eof { abort "should display failure in incomplete-02" } + __--__ +} +expect { + eof { abort "should display failure procedure" } + main +} +expect eof + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-03.mol lex parse +expect { + eof { abort "should display failure in incomplete-03" } + {Expected Procedure-Argument-List to end} +} +expect { + eof { abort "should display failure line" } + {incomplete-03.mol:2} +} +expect eof + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-04.mol lex parse +expect { + eof { abort "should display failure in incomplete-04" } + {Expected Procedure-Argument-List to be followed by a return list or a __DO__} +} +expect { + eof { abort "should display failure line" } + {incomplete-04.mol:3} +} +expect eof + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-05.mol lex parse +expect { + eof { abort "should display failure in incomplete-05" } + {Expected Procedure-Return-List to end} +} +expect { + eof { abort "should display failure line" } + {incomplete-05.mol:4} +} +expect eof + +spawn -noecho $SUGAR_EXECUTABLE tests/004/incomplete-06.mol lex parse +expect { + eof { abort "should display failure in incomplete-06" } + {Expected __DO__ block needs a matching __END__} +} +expect { + eof { abort "should display failure line" } + {incomplete-06.mol:5} +} +expect eof + diff --git a/tests/004/incomplete-01.mol b/tests/004/incomplete-01.mol new file mode 100644 index 0000000..1fefcab --- /dev/null +++ b/tests/004/incomplete-01.mol @@ -0,0 +1 @@ +__PROC__ \ No newline at end of file diff --git a/tests/004/incomplete-02.mol b/tests/004/incomplete-02.mol new file mode 100644 index 0000000..fd06b0d --- /dev/null +++ b/tests/004/incomplete-02.mol @@ -0,0 +1 @@ +__PROC__ main \ No newline at end of file diff --git a/tests/004/incomplete-03.mol b/tests/004/incomplete-03.mol new file mode 100644 index 0000000..c95097b --- /dev/null +++ b/tests/004/incomplete-03.mol @@ -0,0 +1,2 @@ +__PROC__ main +i64 \ No newline at end of file diff --git a/tests/004/incomplete-04.mol b/tests/004/incomplete-04.mol new file mode 100644 index 0000000..8a613a6 --- /dev/null +++ b/tests/004/incomplete-04.mol @@ -0,0 +1,3 @@ +__PROC__ main +i64 +__--__ \ No newline at end of file diff --git a/tests/004/incomplete-05.mol b/tests/004/incomplete-05.mol new file mode 100644 index 0000000..340c0f8 --- /dev/null +++ b/tests/004/incomplete-05.mol @@ -0,0 +1,4 @@ +__PROC__ main +i64 +__--__ +i64 \ No newline at end of file diff --git a/tests/004/incomplete-06.mol b/tests/004/incomplete-06.mol new file mode 100644 index 0000000..667780b --- /dev/null +++ b/tests/004/incomplete-06.mol @@ -0,0 +1,5 @@ +__PROC__ main +i64 +__--__ +i64 +__DO__ \ No newline at end of file