ADD: assert and assert_static_fail instructions.
parent
50f66409b2
commit
66283e23bb
|
@ -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)?
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
G(NODE_INT), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), \
|
||||
G(NODE_DIV), G(NODE_MOD), G(NODE_POW), G(NODE_OPAR), \
|
||||
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_GE)
|
||||
G(NODE_GE), G(NODE_ASSERT), G(NODE_ASSERT_STATIC_FAIL)
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
12
lib/VM.cpp
12
lib/VM.cpp
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
88
src/main.cpp
88
src/main.cpp
|
@ -33,51 +33,61 @@ int main(int argc, char** argv)
|
|||
|
||||
if (args.inputs().size() > 0)
|
||||
{
|
||||
auto source = loader.load(args.inputs()[0]);
|
||||
roza::SrcLoc loc {args.inputs()[0]};
|
||||
roza::StatusLog log;
|
||||
|
||||
auto lexer = std::make_shared<roza::Lexer>(log, loc);
|
||||
auto parser = std::make_shared<roza::Parser>(*lexer, log);
|
||||
auto static_pass = std::make_shared<roza::StaticPass>(log);
|
||||
auto compiler = std::make_shared<roza::Compiler>(log);
|
||||
auto vm = std::make_shared<roza::VM>(log);
|
||||
|
||||
lexer->scan(source);
|
||||
|
||||
if (args.get("--tokens"))
|
||||
try
|
||||
{
|
||||
std::cout << "Tokens:" << std::endl;
|
||||
for (size_t i=0; i<lexer->size(); i++)
|
||||
auto source = loader.load(args.inputs()[0]);
|
||||
roza::SrcLoc loc {args.inputs()[0]};
|
||||
roza::StatusLog log;
|
||||
|
||||
auto lexer = std::make_shared<roza::Lexer>(log, loc);
|
||||
auto parser = std::make_shared<roza::Parser>(*lexer, log);
|
||||
auto static_pass = std::make_shared<roza::StaticPass>(log);
|
||||
auto compiler = std::make_shared<roza::Compiler>(log);
|
||||
auto vm = std::make_shared<roza::VM>(log);
|
||||
|
||||
lexer->scan(source);
|
||||
|
||||
if (args.get("--tokens"))
|
||||
{
|
||||
std::cout << "\t" << lexer->get_or_nullptr(i)->string() << std::endl;
|
||||
std::cout << "Tokens:" << std::endl;
|
||||
for (size_t i=0; i<lexer->size(); i++)
|
||||
{
|
||||
std::cout << "\t" << lexer->get_or_nullptr(i)->string() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
auto root = parser->parse();
|
||||
|
||||
if (args.get("--ast"))
|
||||
{
|
||||
std::cout << "AST:" << std::endl;
|
||||
std::cout << root->string() << std::endl;
|
||||
}
|
||||
|
||||
static_pass->check(root);
|
||||
auto prog = compiler->compile(root);
|
||||
|
||||
if (args.get("--code"))
|
||||
{
|
||||
std::cout << "Bytecode:" << std::endl;
|
||||
std::cout << prog->string() << std::endl;
|
||||
}
|
||||
|
||||
vm->exec(prog);
|
||||
|
||||
if (args.get("--stack"))
|
||||
{
|
||||
std::cout << vm->string() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto root = parser->parse();
|
||||
|
||||
if (args.get("--ast"))
|
||||
catch(std::exception const& err)
|
||||
{
|
||||
std::cout << "AST:" << std::endl;
|
||||
std::cout << root->string() << std::endl;
|
||||
std::cerr << err.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static_pass->check(root);
|
||||
auto prog = compiler->compile(root);
|
||||
|
||||
if (args.get("--code"))
|
||||
{
|
||||
std::cout << "Bytecode:" << std::endl;
|
||||
std::cout << prog->string() << std::endl;
|
||||
}
|
||||
|
||||
vm->exec(prog);
|
||||
|
||||
if (args.get("--stack"))
|
||||
{
|
||||
std::cout << vm->string() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -65,30 +65,32 @@ 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));
|
||||
REQUIRE("OR" == get_str(1));
|
||||
REQUIRE("NOT" == get_str(2));
|
||||
REQUIRE("BOOL[true]" == get_str(3));
|
||||
REQUIRE("BOOL[false]" == get_str(4));
|
||||
REQUIRE("IMP" == get_str(5));
|
||||
REQUIRE("" == get_str(6));
|
||||
}
|
||||
m_lexer.scan("and or not true false =>");
|
||||
REQUIRE("AND" == get_str(0));
|
||||
REQUIRE("OR" == get_str(1));
|
||||
REQUIRE("NOT" == get_str(2));
|
||||
REQUIRE("BOOL[true]" == get_str(3));
|
||||
REQUIRE("BOOL[false]" == get_str(4));
|
||||
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));
|
||||
REQUIRE("NE" == get_str(1));
|
||||
REQUIRE("LT" == get_str(2));
|
||||
REQUIRE("GT" == get_str(3));
|
||||
REQUIRE("LE" == get_str(4));
|
||||
REQUIRE("GE" == get_str(5));
|
||||
REQUIRE("" == get_str(6));
|
||||
}
|
||||
m_lexer.scan("== != < > <= >=");
|
||||
REQUIRE("EQ" == get_str(0));
|
||||
REQUIRE("NE" == get_str(1));
|
||||
REQUIRE("LT" == get_str(2));
|
||||
REQUIRE("GT" == get_str(3));
|
||||
REQUIRE("LE" == get_str(4));
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue