ADD: assert and assert_static_fail instructions.

main
bog 2023-08-31 14:41:43 +02:00
parent 50f66409b2
commit 66283e23bb
14 changed files with 273 additions and 66 deletions

View File

@ -1,5 +1,9 @@
PROG ::= (INSTR (EOI INSTR)*)? PROG ::= (INSTR (EOI+ INSTR)*)?
INSTR ::= EXPR INSTR ::= EXPR
| assert EXPR
| assert_static_fail EXPR
EXPR ::= IMP EXPR ::= IMP
IMP ::= OR (imp OR)? IMP ::= OR (imp OR)?

View File

@ -1,5 +1,7 @@
#include "Compiler.hpp" #include "Compiler.hpp"
#include "lib/Node.hpp"
#include "lib/opcodes.hpp" #include "lib/opcodes.hpp"
#include "StaticPass.hpp"
namespace roza namespace roza
{ {
@ -23,6 +25,33 @@ namespace roza
{ {
switch (root->type()) switch (root->type())
{ {
case NODE_ASSERT: {
compile_children(root, prog);
prog->push_instr(OP_ASSERT);
} break;
case NODE_ASSERT_STATIC_FAIL: {
bool failed = false;
try
{
StaticPass static_pass {m_log};
static_pass.check_children(root);
compile_children(root, prog);
failed = true;
}
catch(...)
{
}
if (failed)
{
m_log.fatal(root->loc(), "static assertion failed");
}
} break;
case NODE_INT: { case NODE_INT: {
auto value = std::make_shared<Value>(std::stoi(root->repr()), root->loc()); auto value = std::make_shared<Value>(std::stoi(root->repr()), root->loc());
prog->push_instr(OP_PUSH_CONST, prog->push_value(value)); prog->push_instr(OP_PUSH_CONST, prog->push_value(value));

View File

@ -31,6 +31,8 @@ namespace roza
{"and", NODE_AND, false}, {"and", NODE_AND, false},
{"or", NODE_OR, false}, {"or", NODE_OR, false},
{"not", NODE_NOT, false}, {"not", NODE_NOT, false},
{"assert_static_fail", NODE_ASSERT_STATIC_FAIL, false},
{"assert", NODE_ASSERT, false},
}; };

View File

@ -11,7 +11,7 @@
G(NODE_CPAR), G(NODE_UADD), G(NODE_USUB), G(NODE_BOOL), \ G(NODE_CPAR), G(NODE_UADD), G(NODE_USUB), G(NODE_BOOL), \
G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IMP), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IMP), \
G(NODE_EQ), G(NODE_NE), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ G(NODE_EQ), G(NODE_NE), G(NODE_LT), G(NODE_LE), G(NODE_GT), \
G(NODE_GE) G(NODE_GE), G(NODE_ASSERT), G(NODE_ASSERT_STATIC_FAIL)
namespace roza namespace roza
{ {

View File

@ -1,5 +1,6 @@
#include "Parser.hpp" #include "Parser.hpp"
#include "lib/Node.hpp" #include "lib/Node.hpp"
#include <memory>
namespace roza namespace roza
{ {
@ -67,6 +68,7 @@ namespace roza
+ "', got '" + "', got '"
+ NodeTypeStr[type()] + NodeTypeStr[type()]
+ "'"); + "'");
return nullptr; return nullptr;
} }
@ -118,7 +120,21 @@ namespace roza
std::shared_ptr<Node> Parser::parse_instr() std::shared_ptr<Node> Parser::parse_instr()
{ {
consume_all(NODE_EOI); consume_all(NODE_EOI);
auto root = parse_expr();
std::shared_ptr<Node> root;
if (type_is(NODE_ASSERT)
|| type_is(NODE_ASSERT_STATIC_FAIL))
{
root = consume();
root->add_child(parse_expr());
}
else
{
root = parse_expr();
}
consume_or_nullptr(NODE_EOI); consume_or_nullptr(NODE_EOI);
consume_all(NODE_EOI); consume_all(NODE_EOI);

View File

@ -19,6 +19,7 @@ namespace roza
switch (root->type()) switch (root->type())
{ {
case NODE_ASSERT_STATIC_FAIL:
case NODE_INT: case NODE_INT:
case NODE_BOOL: case NODE_BOOL:
break; break;
@ -41,6 +42,7 @@ namespace roza
check_types(root, lhs, rhs); check_types(root, lhs, rhs);
} break; } break;
case NODE_ASSERT:
case NODE_NOT: { case NODE_NOT: {
check_children(root); check_children(root);
auto lhs = resolver.find(root->child(0)); auto lhs = resolver.find(root->child(0));
@ -67,6 +69,8 @@ namespace roza
case NODE_UADD: case NODE_UADD:
case NODE_USUB: { case NODE_USUB: {
check_children(root); check_children(root);
auto lhs = resolver.find(root->child(0));
check_types(root, lhs, std::make_shared<Type>(TY_INT));
} break; } break;
case NODE_PROG: { case NODE_PROG: {

View File

@ -19,6 +19,18 @@ namespace roza
{ {
switch (program->opcode(m_pc)) switch (program->opcode(m_pc))
{ {
case OP_ASSERT: {
auto value = program->value(pop());
if (!value->as_bool())
{
m_log.fatal(value->loc(), "assertion failed");
}
m_pc++;
} break;
case OP_PUSH_CONST: { case OP_PUSH_CONST: {
param_t param = *program->param(m_pc); param_t param = *program->param(m_pc);
push(param); push(param);

View File

@ -8,7 +8,7 @@
G(OP_POP), \ G(OP_POP), \
G(OP_IADD), G(OP_ISUB), G(OP_IMUL), G(OP_IDIV), G(OP_IMOD), G(OP_IPOW), \ G(OP_IADD), G(OP_ISUB), G(OP_IMUL), G(OP_IDIV), G(OP_IMOD), G(OP_IPOW), \
G(OP_MOD), G(OP_IUADD), G(OP_IUSUB), G(OP_AND), G(OP_OR), G(OP_NOT), \ G(OP_MOD), G(OP_IUADD), G(OP_IUSUB), G(OP_AND), G(OP_OR), G(OP_NOT), \
G(OP_IMP), G(OP_EQ), G(OP_ILT), G(OP_IGT) G(OP_IMP), G(OP_EQ), G(OP_ILT), G(OP_IGT), G(OP_ASSERT)
namespace roza namespace roza
{ {

34
roza_tests/run.sh Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
OK=0
KO=0
TOTAL=0
for file in $(find . -name "test_*.roza" | sort)
do
roza $file
RET="$?"
NAME=$(basename "$file" | cut -d '.' -f1)
echo -en "\e[34m$NAME...\e[0m"
if [ "$RET" == 0 ]
then
echo -e " \e[32mok\e[0m"
OK=$(($OK + 1))
else
echo -e " \e[31mko\e[0m"
KO=$(($KO + 1))
fi
TOTAL=$(($TOTAL + 1))
done
if [ $OK -eq $TOTAL ]
then
echo -e "\e[32m=== all tests passed ($OK) ===\e[0m"
exit 0
else
echo -e "\e[31m=== some tests failed ($KO) ===\e[0m"
exit -1
fi

30
roza_tests/test_bool.roza Normal file
View File

@ -0,0 +1,30 @@
assert true == true
assert false == false
assert true == (true and true)
assert false == (true and false)
assert false == (false and true)
assert false == (false and false)
assert true == (true or true)
assert true == (true or false)
assert true == (false or true)
assert false == (false or false)
assert false == not true
assert true == not false
assert true == (true => true)
assert false == (true => false)
assert true == (false => true)
assert true == (false => false)
assert (false or true) == (not (true and false))
assert_static_fail 1 and true
assert_static_fail false and 7
assert_static_fail 1 or true
assert_static_fail false or 7
assert_static_fail not 9
assert_static_fail 3 => true
assert_static_fail true => 7

54
roza_tests/test_int.roza Normal file
View File

@ -0,0 +1,54 @@
assert 5 == 5
assert 3 != 5
assert -9 == -10 + 1
assert -4 == 3 - 7
assert 4 == 7 - 3
assert 21 == 3 * 7
assert 3 == 21 / 7
assert 2 == 21 / 8
assert 3 == 7 % 4
assert 128 == 2 ^ 7
assert -128 == -2 ^ 7
assert 3 == +3
assert 0 - 7 == -7
assert 7 == 1 + 2 * 3
assert 9 == (1 + 2) * 3
assert true == 5 < 6
assert false == 5 < 5
assert false == 5 < 4
assert true == 5 <= 6
assert true == 5 <= 5
assert false == 5 <= 4
assert false == 5 > 6
assert false == 5 > 5
assert true == 5 > 4
assert false == 5 >= 6
assert true == 5 >= 5
assert true == 5 >= 4
assert_static_fail 5 + true
assert_static_fail 5 - true
assert_static_fail 5 * true
assert_static_fail 5 / true
assert_static_fail 5 % true
assert_static_fail 5 ^ true
assert_static_fail -true
assert_static_fail +true
assert_static_fail true + 9
assert_static_fail true - (4 + 1)
assert_static_fail true * 7
assert_static_fail true / 3
assert_static_fail true % 15
assert_static_fail true ^ 2
assert_static_fail true > 1
assert_static_fail 2 > false
assert_static_fail true < 1
assert_static_fail 2 < false
assert_static_fail true >= 1
assert_static_fail 2 >= false
assert_static_fail true <= 1
assert_static_fail 2 <= false

View File

@ -32,6 +32,8 @@ int main(int argc, char** argv)
Loader loader; Loader loader;
if (args.inputs().size() > 0) if (args.inputs().size() > 0)
{
try
{ {
auto source = loader.load(args.inputs()[0]); auto source = loader.load(args.inputs()[0]);
roza::SrcLoc loc {args.inputs()[0]}; roza::SrcLoc loc {args.inputs()[0]};
@ -77,7 +79,15 @@ int main(int argc, char** argv)
{ {
std::cout << vm->string() << std::endl; std::cout << vm->string() << std::endl;
} }
return 0;
}
catch(std::exception const& err)
{
std::cerr << err.what() << std::endl;
return -1;
} }
return 0; return 0;
}
} }

View File

@ -65,8 +65,6 @@ TEST_CASE_METHOD(LexerTest, "Lexer_keywords")
TEST_CASE_METHOD(LexerTest, "Lexer_bool") TEST_CASE_METHOD(LexerTest, "Lexer_bool")
{ {
SECTION("nominal cases")
{
m_lexer.scan("and or not true false =>"); m_lexer.scan("and or not true false =>");
REQUIRE("AND" == get_str(0)); REQUIRE("AND" == get_str(0));
REQUIRE("OR" == get_str(1)); REQUIRE("OR" == get_str(1));
@ -75,13 +73,10 @@ TEST_CASE_METHOD(LexerTest, "Lexer_bool")
REQUIRE("BOOL[false]" == get_str(4)); REQUIRE("BOOL[false]" == get_str(4));
REQUIRE("IMP" == get_str(5)); REQUIRE("IMP" == get_str(5));
REQUIRE("" == get_str(6)); REQUIRE("" == get_str(6));
}
} }
TEST_CASE_METHOD(LexerTest, "Lexer_comparisons") TEST_CASE_METHOD(LexerTest, "Lexer_comparisons")
{ {
SECTION("nominal cases")
{
m_lexer.scan("== != < > <= >="); m_lexer.scan("== != < > <= >=");
REQUIRE("EQ" == get_str(0)); REQUIRE("EQ" == get_str(0));
REQUIRE("NE" == get_str(1)); REQUIRE("NE" == get_str(1));
@ -90,5 +85,12 @@ TEST_CASE_METHOD(LexerTest, "Lexer_comparisons")
REQUIRE("LE" == get_str(4)); REQUIRE("LE" == get_str(4));
REQUIRE("GE" == get_str(5)); REQUIRE("GE" == get_str(5));
REQUIRE("" == get_str(6)); REQUIRE("" == get_str(6));
} }
TEST_CASE_METHOD(LexerTest, "Lexer_asserts")
{
m_lexer.scan(" assert assert_static_fail ");
REQUIRE("ASSERT" == get_str(0));
REQUIRE("ASSERT_STATIC_FAIL" == get_str(1));
REQUIRE("" == get_str(2));
} }

View File

@ -100,3 +100,13 @@ TEST_CASE_METHOD(ParserTest, "Parser_comparisons")
test_node("PROG(EQ(LE(INT[5],INT[3]),BOOL[false]))", test_node("PROG(EQ(LE(INT[5],INT[3]),BOOL[false]))",
"5 <= 3 == false"); "5 <= 3 == false");
} }
TEST_CASE_METHOD(ParserTest, "Parser_assertions")
{
test_node("PROG(ASSERT(EQ(ADD(INT[1],INT[1]),INT[2])))",
"assert 1 + 1 == 2");
test_node("PROG(ASSERT_STATIC_FAIL(EQ(ADD(INT[1],INT[1]),INT[2])))",
"assert_static_fail 1 + 1 == 2");
}