ADD: booleans.
parent
051b696b26
commit
08fc0d5d19
|
@ -1,11 +1,15 @@
|
|||
PROG ::= (INSTR (EOI INSTR)*)?
|
||||
INSTR ::= EXPR
|
||||
EXPR ::= TERM
|
||||
EXPR ::= IMP
|
||||
|
||||
IMP ::= OR (imp OR)?
|
||||
OR ::= AND (or AND)*
|
||||
AND ::= TERM (and TERM)*
|
||||
|
||||
TERM ::= FACTOR ((add | sub) FACTOR)*
|
||||
FACTOR ::= UNOP ((mul | div | mod) UNOP)*
|
||||
|
||||
UNOP ::= (add | sub)? POW
|
||||
UNOP ::= (add | sub | not)? POW
|
||||
POW ::= GROUP (pow GROUP)?
|
||||
GROUP ::= BASE | opar EXPR cpar
|
||||
BASE ::= int
|
||||
BASE ::= int | bool
|
||||
|
|
|
@ -28,6 +28,31 @@ namespace roza
|
|||
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
||||
} break;
|
||||
|
||||
case NODE_BOOL: {
|
||||
auto value = std::make_shared<Value>(root->repr() == "true", root->loc());
|
||||
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
||||
} break;
|
||||
|
||||
case NODE_IMP: {
|
||||
compile_children(root, prog);
|
||||
prog->push_instr(OP_IMP);
|
||||
} break;
|
||||
|
||||
case NODE_AND: {
|
||||
compile_children(root, prog);
|
||||
prog->push_instr(OP_AND);
|
||||
} break;
|
||||
|
||||
case NODE_OR: {
|
||||
compile_children(root, prog);
|
||||
prog->push_instr(OP_OR);
|
||||
} break;
|
||||
|
||||
case NODE_NOT: {
|
||||
compile_children(root, prog);
|
||||
prog->push_instr(OP_NOT);
|
||||
} break;
|
||||
|
||||
case NODE_ADD: {
|
||||
compile_children(root, prog);
|
||||
prog->push_instr(OP_IADD);
|
||||
|
|
|
@ -7,15 +7,44 @@ namespace roza
|
|||
: m_log { log }
|
||||
, m_loc { loc }
|
||||
{
|
||||
std::vector<std::tuple<std::string, NodeType, bool>> texts = {
|
||||
{"=>", NODE_IMP, false},
|
||||
{"+", NODE_ADD, false},
|
||||
{"-", NODE_SUB, false},
|
||||
{"*", NODE_MUL, false},
|
||||
{"/", NODE_DIV, false},
|
||||
{"%", NODE_MOD, false},
|
||||
{"^", NODE_POW, false},
|
||||
{"(", NODE_OPAR, false},
|
||||
{")", NODE_CPAR, false},
|
||||
};
|
||||
|
||||
std::vector<std::tuple<std::string, NodeType, bool>> keywords = {
|
||||
{"true", NODE_BOOL, true},
|
||||
{"false", NODE_BOOL, true},
|
||||
{"and", NODE_AND, false},
|
||||
{"or", NODE_OR, false},
|
||||
{"not", NODE_NOT, false},
|
||||
};
|
||||
|
||||
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "+", NODE_ADD, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "-", NODE_SUB, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "*", NODE_MUL, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "/", NODE_DIV, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "%", NODE_MOD, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "^", NODE_POW, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "(", NODE_OPAR, false));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this, ")", NODE_CPAR, false));
|
||||
|
||||
for (auto const& entry: keywords)
|
||||
{
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_keyword, this,
|
||||
std::get<0>(entry),
|
||||
std::get<1>(entry),
|
||||
std::get<2>(entry)));
|
||||
}
|
||||
|
||||
for (auto const& entry: texts)
|
||||
{
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_text, this,
|
||||
std::get<0>(entry),
|
||||
std::get<1>(entry),
|
||||
std::get<2>(entry)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -118,6 +147,18 @@ namespace roza
|
|||
return m_nodes.at(index);
|
||||
}
|
||||
|
||||
bool Lexer::is_sep(size_t index) const
|
||||
{
|
||||
if (index >= m_source.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
char c = m_source[index];
|
||||
|
||||
return !std::isalnum(c);
|
||||
}
|
||||
|
||||
ScanInfo Lexer::scan_int() const
|
||||
{
|
||||
size_t cursor = m_cursor;
|
||||
|
@ -160,4 +201,20 @@ namespace roza
|
|||
m_cursor + text.size()
|
||||
};
|
||||
}
|
||||
|
||||
ScanInfo Lexer::scan_keyword(std::string const& keyword,
|
||||
NodeType type,
|
||||
bool value) const
|
||||
{
|
||||
auto info = scan_text(keyword, type, value);
|
||||
|
||||
if (is_sep(info.cursor))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
return ScanInfo {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,10 +38,16 @@ namespace roza
|
|||
std::vector<std::shared_ptr<Node>> m_nodes;
|
||||
std::vector<scanner> m_scanners;
|
||||
|
||||
bool is_sep(size_t index) const;
|
||||
|
||||
ScanInfo scan_int() const;
|
||||
ScanInfo scan_text(std::string const& text,
|
||||
NodeType type,
|
||||
bool value=false) const;
|
||||
|
||||
ScanInfo scan_keyword(std::string const& keyword,
|
||||
NodeType type,
|
||||
bool value=false) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
G(NODE_PROG), G(NODE_INSTR), G(NODE_EOI), \
|
||||
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_CPAR), G(NODE_UADD), G(NODE_USUB), G(NODE_BOOL), \
|
||||
G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IMP)
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
|
|
@ -126,7 +126,52 @@ namespace roza
|
|||
|
||||
std::shared_ptr<Node> Parser::parse_expr()
|
||||
{
|
||||
return parse_term();
|
||||
return parse_imp();
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_imp()
|
||||
{
|
||||
auto lhs = parse_or();
|
||||
|
||||
if (type_is(NODE_IMP))
|
||||
{
|
||||
auto root = consume();
|
||||
root->add_child(lhs);
|
||||
root->add_child(parse_or());
|
||||
lhs = root;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_or()
|
||||
{
|
||||
auto lhs = parse_and();
|
||||
|
||||
while (type_is(NODE_OR))
|
||||
{
|
||||
auto root = consume();
|
||||
root->add_child(lhs);
|
||||
root->add_child(parse_and());
|
||||
lhs = root;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_and()
|
||||
{
|
||||
auto lhs = parse_term();
|
||||
|
||||
while (type_is(NODE_AND))
|
||||
{
|
||||
auto root = consume();
|
||||
root->add_child(lhs);
|
||||
root->add_child(parse_term());
|
||||
lhs = root;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_term()
|
||||
|
@ -181,6 +226,15 @@ namespace roza
|
|||
return root;
|
||||
}
|
||||
|
||||
if (type_is(NODE_NOT))
|
||||
{
|
||||
auto root = std::make_shared<Node>(NODE_NOT, "", m_lexer.loc());
|
||||
next();
|
||||
|
||||
root->add_child(parse_pow());
|
||||
return root;
|
||||
}
|
||||
|
||||
return parse_pow();
|
||||
}
|
||||
|
||||
|
@ -214,7 +268,18 @@ namespace roza
|
|||
|
||||
std::shared_ptr<Node> Parser::parse_base()
|
||||
{
|
||||
return parse_int();
|
||||
if (type_is(NODE_INT)
|
||||
|| type_is(NODE_BOOL))
|
||||
{
|
||||
return consume();
|
||||
}
|
||||
|
||||
m_log.fatal(node()->loc(),
|
||||
"unknown node '"
|
||||
+ node()->string()
|
||||
+ "'");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_int()
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace roza
|
|||
std::shared_ptr<Node> parse_prog();
|
||||
std::shared_ptr<Node> parse_instr();
|
||||
std::shared_ptr<Node> parse_expr();
|
||||
std::shared_ptr<Node> parse_imp();
|
||||
std::shared_ptr<Node> parse_or();
|
||||
std::shared_ptr<Node> parse_and();
|
||||
std::shared_ptr<Node> parse_term();
|
||||
std::shared_ptr<Node> parse_factor();
|
||||
std::shared_ptr<Node> parse_unop();
|
||||
|
|
|
@ -19,7 +19,25 @@ namespace roza
|
|||
|
||||
switch (root->type())
|
||||
{
|
||||
case NODE_INT: break;
|
||||
case NODE_INT:
|
||||
case NODE_BOOL:
|
||||
break;
|
||||
|
||||
case NODE_IMP:
|
||||
case NODE_OR:
|
||||
case NODE_AND: {
|
||||
check_children(root);
|
||||
auto lhs = resolver.find(root->child(0));
|
||||
auto rhs = resolver.find(root->child(1));
|
||||
check_types(root, lhs, std::make_shared<Type>(TY_BOOL));
|
||||
check_types(root, lhs, rhs);
|
||||
} break;
|
||||
|
||||
case NODE_NOT: {
|
||||
check_children(root);
|
||||
auto lhs = resolver.find(root->child(0));
|
||||
check_types(root, lhs, std::make_shared<Type>(TY_BOOL));
|
||||
} break;
|
||||
|
||||
case NODE_ADD:
|
||||
case NODE_SUB:
|
||||
|
@ -27,9 +45,39 @@ namespace roza
|
|||
case NODE_DIV:
|
||||
case NODE_MOD:
|
||||
case NODE_POW: {
|
||||
check_children(root);
|
||||
auto lhs = resolver.find(root->child(0));
|
||||
auto rhs = resolver.find(root->child(1));
|
||||
check_types(root, lhs, std::make_shared<Type>(TY_INT));
|
||||
check_types(root, lhs, rhs);
|
||||
} break;
|
||||
|
||||
case NODE_UADD:
|
||||
case NODE_USUB: {
|
||||
check_children(root);
|
||||
} break;
|
||||
|
||||
case NODE_PROG: {
|
||||
check_children(root);
|
||||
} break;
|
||||
|
||||
default:
|
||||
m_log.fatal(root->loc(), "cannot check node '" + root->string() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
void StaticPass::check_children(std::shared_ptr<Node> root)
|
||||
{
|
||||
for (size_t i=0; i<root->size(); i++)
|
||||
{
|
||||
check(root->child(i));
|
||||
}
|
||||
}
|
||||
|
||||
void StaticPass::check_types(std::shared_ptr<Node> root,
|
||||
std::shared_ptr<Type> lhs,
|
||||
std::shared_ptr<Type> rhs)
|
||||
{
|
||||
if (!lhs->equals(*rhs))
|
||||
{
|
||||
m_log.fatal(root->loc(),
|
||||
|
@ -40,22 +88,31 @@ namespace roza
|
|||
+ rhs->string()
|
||||
+ "'");
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case NODE_UADD:
|
||||
case NODE_USUB: {
|
||||
} break;
|
||||
|
||||
case NODE_PROG: {
|
||||
for (size_t i=0; i<root->size(); i++)
|
||||
void StaticPass::check_types(std::shared_ptr<Node> root,
|
||||
std::shared_ptr<Type> lhs,
|
||||
std::vector<std::shared_ptr<Type>> const& rhs)
|
||||
{
|
||||
check(root->child(i));
|
||||
for (auto const& ty: rhs)
|
||||
{
|
||||
if (lhs->equals(*ty))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
m_log.fatal(root->loc(), "cannot check node '" + root->string() + "'");
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "type mismatch, got '" << lhs->string() << "'";
|
||||
ss << "candidates are:" << std::endl;
|
||||
|
||||
for (auto ty: rhs)
|
||||
{
|
||||
ss << "\t-> " << ty->string() << std::endl;
|
||||
}
|
||||
|
||||
m_log.fatal(root->loc(), ss.str());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "commons.hpp"
|
||||
#include "StatusLog.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Type.hpp"
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
@ -14,9 +15,18 @@ namespace roza
|
|||
virtual ~StaticPass();
|
||||
|
||||
void check(std::shared_ptr<Node> root);
|
||||
void check_children(std::shared_ptr<Node> root);
|
||||
|
||||
private:
|
||||
StatusLog& m_log;
|
||||
|
||||
void check_types(std::shared_ptr<Node> root,
|
||||
std::shared_ptr<Type> lhs,
|
||||
std::shared_ptr<Type> rhs);
|
||||
|
||||
void check_types(std::shared_ptr<Node> root,
|
||||
std::shared_ptr<Type> lhs,
|
||||
std::vector<std::shared_ptr<Type>> const& rhs);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "commons.hpp"
|
||||
|
||||
#define BASE_TYPE(G) \
|
||||
G(TY_INT)
|
||||
G(TY_INT), G(TY_BOOL)
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
|
|
@ -23,6 +23,14 @@ namespace roza
|
|||
return std::make_shared<Type>(BaseType::TY_INT);
|
||||
} break;
|
||||
|
||||
case NODE_IMP:
|
||||
case NODE_AND:
|
||||
case NODE_OR:
|
||||
case NODE_NOT:
|
||||
case NODE_BOOL: {
|
||||
return std::make_shared<Type>(BaseType::TY_BOOL);
|
||||
} break;
|
||||
|
||||
case NODE_ADD:
|
||||
case NODE_SUB:
|
||||
case NODE_MUL:
|
||||
|
|
34
lib/VM.cpp
34
lib/VM.cpp
|
@ -25,6 +25,37 @@ namespace roza
|
|||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OP_IMP: {
|
||||
apply_binop(program, [](auto lhs, auto rhs){
|
||||
bool a = lhs->as_bool();
|
||||
bool b = rhs->as_bool();
|
||||
|
||||
return std::make_shared<Value>(!a || b,
|
||||
lhs->loc());
|
||||
});
|
||||
} break;
|
||||
|
||||
case OP_AND: {
|
||||
apply_binop(program, [](auto lhs, auto rhs){
|
||||
return std::make_shared<Value>(lhs->as_bool() && rhs->as_bool(),
|
||||
lhs->loc());
|
||||
});
|
||||
} break;
|
||||
|
||||
case OP_OR: {
|
||||
apply_binop(program, [](auto lhs, auto rhs){
|
||||
return std::make_shared<Value>(lhs->as_bool() || rhs->as_bool(),
|
||||
lhs->loc());
|
||||
});
|
||||
} break;
|
||||
|
||||
case OP_NOT: {
|
||||
apply_unop(program, [](auto lhs){
|
||||
return std::make_shared<Value>(!lhs->as_bool(),
|
||||
lhs->loc());
|
||||
});
|
||||
} break;
|
||||
|
||||
case OP_IADD: {
|
||||
apply_binop(program, [](auto lhs, auto rhs){
|
||||
return std::make_shared<Value>(lhs->as_int() + rhs->as_int(),
|
||||
|
@ -62,7 +93,8 @@ namespace roza
|
|||
|
||||
case OP_IPOW: {
|
||||
apply_binop(program, [](auto lhs, auto rhs){
|
||||
return std::make_shared<Value>(std::pow(lhs->as_int(), rhs->as_int()),
|
||||
return std::make_shared<Value>(static_cast<int>(std::pow(lhs->as_int(),
|
||||
rhs->as_int())),
|
||||
lhs->loc());
|
||||
});
|
||||
} break;
|
||||
|
|
|
@ -9,6 +9,13 @@ namespace roza
|
|||
{
|
||||
}
|
||||
|
||||
/*explicit*/ Value::Value(bool value, SrcLoc loc)
|
||||
: m_type { std::make_shared<Type>(BaseType::TY_BOOL) }
|
||||
, m_bool_val { value }
|
||||
, m_loc { loc }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Value::~Value()
|
||||
{
|
||||
}
|
||||
|
@ -20,6 +27,11 @@ namespace roza
|
|||
return std::to_string(m_int_val);
|
||||
}
|
||||
|
||||
if (m_type->equals(TY_BOOL))
|
||||
{
|
||||
return m_bool_val ? "true" : "false";
|
||||
}
|
||||
|
||||
assert("cannot stringify unknown value " && 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,16 +11,21 @@ namespace roza
|
|||
{
|
||||
public:
|
||||
explicit Value(int value, SrcLoc loc);
|
||||
explicit Value(bool value, SrcLoc loc);
|
||||
virtual ~Value();
|
||||
|
||||
SrcLoc loc() const { return m_loc; }
|
||||
|
||||
int as_int() const { return m_int_val; }
|
||||
bool as_bool() const { return m_bool_val; }
|
||||
|
||||
std::shared_ptr<Type> type() const { return m_type; }
|
||||
std::string string() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type> m_type;
|
||||
int m_int_val;
|
||||
bool m_bool_val;
|
||||
SrcLoc m_loc;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
G(OP_PUSH_CONST), \
|
||||
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_MOD), G(OP_IUADD), G(OP_IUSUB), G(OP_AND), G(OP_OR), G(OP_NOT), \
|
||||
G(OP_IMP)
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
|
|
@ -55,3 +55,25 @@ TEST_CASE_METHOD(LexerTest, "Lexer_int_arith")
|
|||
REQUIRE("CPAR" == get_str(7));
|
||||
REQUIRE("" == get_str(8));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(LexerTest, "Lexer_keywords")
|
||||
{
|
||||
REQUIRE_THROWS(m_lexer.scan(" andor "));
|
||||
REQUIRE_NOTHROW(m_lexer.scan(" and+ "));
|
||||
REQUIRE_NOTHROW(m_lexer.scan(" (and) "));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,3 +70,24 @@ TEST_CASE_METHOD(ParserTest, "Parser_int_arith")
|
|||
|
||||
test_node("PROG(USUB(POW(INT[2],INT[7])))", " -2^7 ");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ParserTest, "Parser_bool")
|
||||
{
|
||||
test_node("PROG(BOOL[true])",
|
||||
"true");
|
||||
|
||||
test_node("PROG(AND(BOOL[true],BOOL[false]))",
|
||||
"true and false");
|
||||
|
||||
test_node("PROG(OR(BOOL[true],BOOL[false]))",
|
||||
"true or false");
|
||||
|
||||
test_node("PROG(NOT(BOOL[false]))",
|
||||
"not false");
|
||||
|
||||
test_node("PROG(NOT(OR(BOOL[false],BOOL[true])))",
|
||||
"not (false or true)");
|
||||
|
||||
test_node("PROG(IMP(BOOL[true],BOOL[false]))",
|
||||
"true => false");
|
||||
}
|
||||
|
|
|
@ -33,4 +33,5 @@ protected:
|
|||
TEST_CASE_METHOD(StaticPassTest, "StaticPass_integer")
|
||||
{
|
||||
test_ok(" 43 ");
|
||||
test_ko(" 43 + 7 * true");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue