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
| assert EXPR
| assert_static_fail EXPR
EXPR ::= IMP
IMP ::= OR (imp OR)?

View File

@ -1,5 +1,7 @@
#include "Compiler.hpp"
#include "lib/Node.hpp"
#include "lib/opcodes.hpp"
#include "StaticPass.hpp"
namespace roza
{
@ -23,6 +25,33 @@ namespace roza
{
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: {
auto value = std::make_shared<Value>(std::stoi(root->repr()), root->loc());
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));

View File

@ -31,6 +31,8 @@ namespace roza
{"and", NODE_AND, false},
{"or", NODE_OR, 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_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_GE)
G(NODE_GE), G(NODE_ASSERT), G(NODE_ASSERT_STATIC_FAIL)
namespace roza
{

View File

@ -1,5 +1,6 @@
#include "Parser.hpp"
#include "lib/Node.hpp"
#include <memory>
namespace roza
{
@ -67,6 +68,7 @@ namespace roza
+ "', got '"
+ NodeTypeStr[type()]
+ "'");
return nullptr;
}
@ -118,7 +120,21 @@ namespace roza
std::shared_ptr<Node> Parser::parse_instr()
{
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_all(NODE_EOI);

View File

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

View File

@ -19,6 +19,18 @@ namespace roza
{
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: {
param_t param = *program->param(m_pc);
push(param);

View File

@ -8,7 +8,7 @@
G(OP_POP), \
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_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
{

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;
if (args.inputs().size() > 0)
{
try
{
auto source = loader.load(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;
}
return 0;
}
catch(std::exception const& err)
{
std::cerr << err.what() << std::endl;
return -1;
}
return 0;
}
}

View File

@ -64,8 +64,6 @@ TEST_CASE_METHOD(LexerTest, "Lexer_keywords")
}
TEST_CASE_METHOD(LexerTest, "Lexer_bool")
{
SECTION("nominal cases")
{
m_lexer.scan("and or not true false =>");
REQUIRE("AND" == get_str(0));
@ -76,11 +74,8 @@ TEST_CASE_METHOD(LexerTest, "Lexer_bool")
REQUIRE("IMP" == get_str(5));
REQUIRE("" == get_str(6));
}
}
TEST_CASE_METHOD(LexerTest, "Lexer_comparisons")
{
SECTION("nominal cases")
{
m_lexer.scan("== != < > <= >=");
REQUIRE("EQ" == get_str(0));
@ -91,4 +86,11 @@ TEST_CASE_METHOD(LexerTest, "Lexer_comparisons")
REQUIRE("GE" == get_str(5));
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]))",
"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");
}