ADD: if-then-else statement.

main
bog 2023-09-01 01:22:51 +02:00
parent b9072c580f
commit d72660e6de
17 changed files with 381 additions and 36 deletions

View File

@ -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

View File

@ -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);

View File

@ -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},

View File

@ -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
{

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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:

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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];

View File

@ -1,6 +1,7 @@
#ifndef roza_COMMONS_HPP
#define roza_COMMONS_HPP
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>

View File

@ -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
{

92
roza_tests/test_if.roza Normal file
View File

@ -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

View File

@ -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));
}

View File

@ -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");
}