ADD: if-then-else statement.
parent
b9072c580f
commit
d72660e6de
|
@ -6,10 +6,14 @@ INSTR ::= EXPR
|
|||
| VARDECL
|
||||
| CONSTDECL
|
||||
| ASSIGN
|
||||
| IF
|
||||
|
||||
VARDECL ::= let_mut ident assign EXPR
|
||||
CONSTDECL ::= let ident assign EXPR
|
||||
ASSIGN ::= ident assign EXPR
|
||||
IF ::= if EXPR THEN (else (IF | ELSE))?
|
||||
THEN ::= INSTR*
|
||||
ELSE ::= INSTR*
|
||||
|
||||
EXPR ::= IMP
|
||||
|
||||
|
|
|
@ -25,6 +25,51 @@ namespace roza
|
|||
{
|
||||
switch (root->type())
|
||||
{
|
||||
|
||||
case NODE_IF: {
|
||||
m_sym.enter_scope();
|
||||
|
||||
std::vector<size_t> to_end;
|
||||
|
||||
std::function<void(std::shared_ptr<Node>)>
|
||||
f = [&](std::shared_ptr<Node> n){
|
||||
auto cond = n->child(0);
|
||||
auto then = n->child(1);
|
||||
|
||||
compile_node(cond, prog);
|
||||
size_t cond_addr = prog->size();
|
||||
prog->push_instr(OP_BRF, 0);
|
||||
|
||||
compile_node(then, prog);
|
||||
|
||||
to_end.push_back(prog->size());
|
||||
prog->push_instr(OP_BR, 0);
|
||||
|
||||
prog->set_param(cond_addr, prog->size());
|
||||
|
||||
if (n->size() > 2)
|
||||
{
|
||||
auto next = n->child(2);
|
||||
compile_node(next, prog);
|
||||
}
|
||||
};
|
||||
|
||||
f(root);
|
||||
|
||||
for (auto addr: to_end)
|
||||
{
|
||||
prog->set_param(addr, prog->size());
|
||||
}
|
||||
|
||||
m_sym.leave_scope();
|
||||
|
||||
} break;
|
||||
|
||||
case NODE_ELSE:
|
||||
case NODE_THEN: {
|
||||
compile_children(root, prog);
|
||||
} break;
|
||||
|
||||
case NODE_ASSERT: {
|
||||
compile_children(root, prog);
|
||||
prog->push_instr(OP_ASSERT);
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace roza
|
|||
, m_loc { loc }
|
||||
{
|
||||
std::vector<std::tuple<std::string, NodeType, bool>> texts = {
|
||||
{";", NODE_EOI, false},
|
||||
{"==", NODE_EQ, false},
|
||||
{"!=", NODE_NE, false},
|
||||
{"<=", NODE_LE, false},
|
||||
|
@ -27,6 +28,9 @@ namespace roza
|
|||
};
|
||||
|
||||
std::vector<std::tuple<std::string, NodeType, bool>> keywords = {
|
||||
{"if", NODE_IF, false},
|
||||
{"else", NODE_ELSE, false},
|
||||
{"end", NODE_END, false},
|
||||
{"let!", NODE_LET_MUT, false},
|
||||
{"let", NODE_LET, false},
|
||||
{"true", NODE_BOOL, true},
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
G(NODE_EQ), G(NODE_NE), G(NODE_LT), G(NODE_LE), G(NODE_GT), \
|
||||
G(NODE_GE), G(NODE_ASSERT), G(NODE_ASSERT_STATIC_FAIL), \
|
||||
G(NODE_IDENT), G(NODE_ASSIGN), G(NODE_LET), G(NODE_LET_MUT), \
|
||||
G(NODE_VARDECL), G(NODE_CONSTDECL)
|
||||
G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_IF), G(NODE_ELSE), \
|
||||
G(NODE_THEN), G(NODE_END)
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
|
|
@ -159,6 +159,12 @@ namespace roza
|
|||
ensure(NODE_EOI);
|
||||
return root;
|
||||
}
|
||||
else if (type_is(NODE_IF))
|
||||
{
|
||||
auto root = parse_if();
|
||||
consume_all(NODE_EOI);
|
||||
return root;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto root = parse_expr();
|
||||
|
@ -213,6 +219,55 @@ namespace roza
|
|||
return root;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_if()
|
||||
{
|
||||
auto root = consume(NODE_IF);
|
||||
root->add_child(parse_expr());
|
||||
root->add_child(parse_then());
|
||||
|
||||
if (type_is(NODE_ELSE)
|
||||
&& type_is(NODE_IF, 1))
|
||||
{
|
||||
consume(NODE_ELSE);
|
||||
root->add_child(parse_if());
|
||||
return root;
|
||||
}
|
||||
|
||||
if (type_is(NODE_ELSE))
|
||||
{
|
||||
root->add_child(parse_else());
|
||||
}
|
||||
|
||||
consume(NODE_END);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_then()
|
||||
{
|
||||
auto root = std::make_shared<Node>(NODE_THEN, "", node()->loc());
|
||||
|
||||
while (!type_is(NODE_ELSE)
|
||||
&& !type_is(NODE_END))
|
||||
{
|
||||
root->add_child(parse_instr());
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_else()
|
||||
{
|
||||
auto root = consume(NODE_ELSE);
|
||||
|
||||
while (!type_is(NODE_END))
|
||||
{
|
||||
root->add_child(parse_instr());
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parse_expr()
|
||||
{
|
||||
return parse_imp();
|
||||
|
|
|
@ -36,6 +36,9 @@ namespace roza
|
|||
std::shared_ptr<Node> parse_vardecl();
|
||||
std::shared_ptr<Node> parse_constdecl();
|
||||
std::shared_ptr<Node> parse_assign();
|
||||
std::shared_ptr<Node> parse_if();
|
||||
std::shared_ptr<Node> parse_then();
|
||||
std::shared_ptr<Node> parse_else();
|
||||
|
||||
std::shared_ptr<Node> parse_expr();
|
||||
std::shared_ptr<Node> parse_imp();
|
||||
|
|
|
@ -45,6 +45,12 @@ namespace roza
|
|||
return index < m_values.size();
|
||||
}
|
||||
|
||||
void Program::set_param(size_t index, param_t param)
|
||||
{
|
||||
assert(index < m_instrs.size());
|
||||
m_instrs[index].param = param;
|
||||
}
|
||||
|
||||
std::string Program::string() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace roza
|
|||
std::shared_ptr<Value> value(size_t index) const;
|
||||
bool has_value(size_t index) const;
|
||||
|
||||
void set_param(size_t index, param_t param);
|
||||
|
||||
std::string string() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -26,6 +26,20 @@ namespace roza
|
|||
|
||||
switch (root->type())
|
||||
{
|
||||
case NODE_IF: {
|
||||
m_sym.enter_scope();
|
||||
auto cond_type = resolver.find(root->child(0), m_sym);
|
||||
check_types(root, std::make_shared<Type>(TY_BOOL), cond_type);
|
||||
check_children(root);
|
||||
m_sym.leave_scope();
|
||||
} break;
|
||||
|
||||
case NODE_THEN:
|
||||
case NODE_ELSE: {
|
||||
check_children(root);
|
||||
} break;
|
||||
|
||||
|
||||
case NODE_CONSTDECL: {
|
||||
check(root->child(1));
|
||||
m_sym.declare(root->child(0)->repr(), root->child(1));
|
||||
|
|
111
lib/SymTable.cpp
111
lib/SymTable.cpp
|
@ -11,59 +11,130 @@ namespace roza
|
|||
/*explicit*/ SymTable::SymTable(SymTable const& sym_table)
|
||||
{
|
||||
m_scope = sym_table.m_scope;
|
||||
|
||||
for (auto const& entry: sym_table.m_entries)
|
||||
{
|
||||
m_entries[entry.first] = entry.second;
|
||||
}
|
||||
m_entries = sym_table.m_entries;
|
||||
}
|
||||
|
||||
/*virtual*/ SymTable::~SymTable()
|
||||
{
|
||||
}
|
||||
|
||||
void SymTable::enter_scope()
|
||||
{
|
||||
m_scope++;
|
||||
}
|
||||
|
||||
void SymTable::leave_scope()
|
||||
{
|
||||
auto iter = std::begin(m_entries);
|
||||
|
||||
while (iter != std::end(m_entries))
|
||||
{
|
||||
if (iter->scope >= m_scope)
|
||||
{
|
||||
iter = m_entries.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
m_scope--;
|
||||
}
|
||||
|
||||
int SymTable::declare(std::string const& name, std::shared_ptr<Node> node)
|
||||
{
|
||||
assert(!exists(name));
|
||||
assert(!exists_in_scope(name));
|
||||
|
||||
m_entries.insert({name, SymEntry {
|
||||
m_entries.push_back(SymEntry {
|
||||
name,
|
||||
SymTable::addr++,
|
||||
m_scope,
|
||||
node,
|
||||
false
|
||||
}});
|
||||
});
|
||||
|
||||
return SymTable::addr - 1;
|
||||
}
|
||||
|
||||
int SymTable::declare_mut(std::string const& name, std::shared_ptr<Node> node)
|
||||
{
|
||||
assert(!exists(name));
|
||||
int addr = declare(name, node);
|
||||
m_entries.back().is_mut = true;
|
||||
|
||||
m_entries.insert({name, SymEntry {
|
||||
SymTable::addr++,
|
||||
m_scope,
|
||||
node,
|
||||
true
|
||||
}});
|
||||
|
||||
return SymTable::addr - 1;
|
||||
return addr;
|
||||
}
|
||||
|
||||
SymEntry& SymTable::find(std::string const& name)
|
||||
{
|
||||
assert(exists(name));
|
||||
return m_entries[name];
|
||||
|
||||
size_t idx = 0;
|
||||
int scope = -1;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (auto const& entry: m_entries)
|
||||
{
|
||||
if (entry.name == name && entry.scope > scope)
|
||||
{
|
||||
idx = i;
|
||||
scope = entry.scope;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
assert(scope >= 0);
|
||||
|
||||
return m_entries[idx];
|
||||
}
|
||||
|
||||
SymEntry const& SymTable::find(std::string const& name) const
|
||||
{
|
||||
assert(exists(name));
|
||||
return m_entries.at(name);
|
||||
|
||||
size_t idx = 0;
|
||||
int scope = -1;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (auto const& entry: m_entries)
|
||||
{
|
||||
if (entry.name == name && entry.scope > scope)
|
||||
{
|
||||
idx = i;
|
||||
scope = entry.scope;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
assert(scope >= 0);
|
||||
|
||||
return m_entries[idx];
|
||||
}
|
||||
|
||||
bool SymTable::exists(std::string const& name) const
|
||||
{
|
||||
return m_entries.find(name) != std::end(m_entries);
|
||||
for (auto const& entry: m_entries)
|
||||
{
|
||||
if (entry.name == name) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SymTable::exists_in_scope(std::string const& name) const
|
||||
{
|
||||
for (auto const& entry: m_entries)
|
||||
{
|
||||
if (entry.name == name && entry.scope >= m_scope)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace roza
|
||||
{
|
||||
struct SymEntry {
|
||||
std::string name;
|
||||
int addr;
|
||||
int scope;
|
||||
std::shared_ptr<Node> node;
|
||||
|
@ -21,6 +22,9 @@ namespace roza
|
|||
|
||||
virtual ~SymTable();
|
||||
|
||||
void enter_scope();
|
||||
void leave_scope();
|
||||
|
||||
int declare(std::string const& name, std::shared_ptr<Node> node);
|
||||
int declare_mut(std::string const& name, std::shared_ptr<Node> node);
|
||||
|
||||
|
@ -28,10 +32,11 @@ namespace roza
|
|||
SymEntry const& find(std::string const& name) const;
|
||||
|
||||
bool exists(std::string const& name) const;
|
||||
bool exists_in_scope(std::string const& name) const;
|
||||
|
||||
private:
|
||||
static int addr;
|
||||
std::unordered_map<std::string, SymEntry> m_entries;
|
||||
std::vector<SymEntry> m_entries;
|
||||
int m_scope = 0;
|
||||
};
|
||||
}
|
||||
|
|
18
lib/VM.cpp
18
lib/VM.cpp
|
@ -19,6 +19,24 @@ namespace roza
|
|||
{
|
||||
switch (program->opcode(m_pc))
|
||||
{
|
||||
|
||||
case OP_BRF: {
|
||||
auto value = program->value(pop());
|
||||
|
||||
if (!value->as_bool())
|
||||
{
|
||||
m_pc = *program->param(m_pc);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pc++;
|
||||
}
|
||||
} break;
|
||||
|
||||
case OP_BR: {
|
||||
m_pc = *program->param(m_pc);
|
||||
} break;
|
||||
|
||||
case OP_LOAD_GLOBAL: {
|
||||
int addr = *program->param(m_pc);
|
||||
auto value = m_globals[addr];
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef roza_COMMONS_HPP
|
||||
#define roza_COMMONS_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
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_ASSERT), G(OP_STORE_GLOBAL), \
|
||||
G(OP_LOAD_GLOBAL)
|
||||
G(OP_LOAD_GLOBAL), G(OP_BRF), G(OP_BR)
|
||||
|
||||
namespace roza
|
||||
{
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
let! a = 0
|
||||
|
||||
if true
|
||||
a = 7
|
||||
end
|
||||
|
||||
assert 7 == a
|
||||
|
||||
if false
|
||||
a = 9
|
||||
end
|
||||
|
||||
assert 7 == a
|
||||
|
||||
if true
|
||||
a = 0
|
||||
else
|
||||
a = 1
|
||||
end
|
||||
|
||||
assert 0 == a
|
||||
|
||||
if false
|
||||
a = 0
|
||||
else
|
||||
a = 1
|
||||
end
|
||||
|
||||
assert 1 == a
|
||||
|
||||
if true
|
||||
a = 1
|
||||
else if true
|
||||
a = 2
|
||||
else if true
|
||||
a = 3
|
||||
else
|
||||
a = 4
|
||||
end
|
||||
|
||||
assert 1 == a
|
||||
|
||||
if false
|
||||
a = 1
|
||||
else if true
|
||||
a = 2
|
||||
else if true
|
||||
a = 3
|
||||
else
|
||||
a = 4
|
||||
end
|
||||
|
||||
assert 2 == a
|
||||
|
||||
if false
|
||||
a = 1
|
||||
else if false
|
||||
a = 2
|
||||
else if true
|
||||
a = 3
|
||||
else
|
||||
a = 4
|
||||
end
|
||||
|
||||
assert 3 == a
|
||||
|
||||
if false
|
||||
a = 1
|
||||
else if false
|
||||
a = 2
|
||||
else if false
|
||||
a = 3
|
||||
else
|
||||
a = 4
|
||||
end
|
||||
|
||||
assert 4 == a
|
||||
|
||||
let! b = 54
|
||||
let! c = 33
|
||||
|
||||
if true
|
||||
let! b = 18
|
||||
c = 7
|
||||
assert 18 == b
|
||||
end
|
||||
|
||||
assert 54 == b
|
||||
assert 7 == c
|
||||
|
||||
|
||||
assert_static_fail if 4 0; end
|
|
@ -103,3 +103,12 @@ TEST_CASE_METHOD(LexerTest, "Lexer_declarations")
|
|||
REQUIRE("ASSIGN" == get_str(3));
|
||||
REQUIRE("" == get_str(4));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(LexerTest, "Lexer_if")
|
||||
{
|
||||
m_lexer.scan(" if else end ");
|
||||
REQUIRE("IF" == get_str(0));
|
||||
REQUIRE("ELSE" == get_str(1));
|
||||
REQUIRE("END" == get_str(2));
|
||||
REQUIRE("" == get_str(3));
|
||||
}
|
||||
|
|
|
@ -122,3 +122,18 @@ TEST_CASE_METHOD(ParserTest, "Parser_vardecl")
|
|||
test_node("PROG(CONSTDECL(IDENT[_coucou],MUL(INT[6],INT[7])))",
|
||||
"let _coucou = 6 * 7");
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(ParserTest, "Parser_if")
|
||||
{
|
||||
test_node("PROG(IF(BOOL[true],THEN(ASSIGN(IDENT[x],INT[4]))))",
|
||||
"if true x = 4; end");
|
||||
|
||||
test_node("PROG(IF(BOOL[true],THEN(INT[1]),ELSE(INT[4])))",
|
||||
"if true 1; else 4; end");
|
||||
|
||||
test_node("PROG(IF(BOOL[true],THEN(INT[0]),"
|
||||
"IF(BOOL[false],THEN(INT[1]),"
|
||||
"IF(BOOL[true],THEN(INT[2]),"
|
||||
"ELSE(INT[3])))))",
|
||||
"if true 0; else if false 1; else if true 2; else 3; end");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue