ADD: global variable declaration (mutable or not).

main
bog 2023-08-31 21:25:00 +02:00
parent 66283e23bb
commit b9072c580f
22 changed files with 414 additions and 30 deletions

View File

@ -2,7 +2,14 @@ PROG ::= (INSTR (EOI+ INSTR)*)?
INSTR ::= EXPR
| assert EXPR
| assert_static_fail EXPR
| assert_static_fail INSTR
| VARDECL
| CONSTDECL
| ASSIGN
VARDECL ::= let_mut ident assign EXPR
CONSTDECL ::= let ident assign EXPR
ASSIGN ::= ident assign EXPR
EXPR ::= IMP
@ -19,4 +26,4 @@ FACTOR ::= UNOP ((mul | div | mod) UNOP)*
UNOP ::= (add | sub | not)? POW
POW ::= GROUP (pow GROUP)?
GROUP ::= BASE | opar EXPR cpar
BASE ::= int | bool
BASE ::= int | bool | ident

View File

@ -35,8 +35,9 @@ namespace roza
try
{
StaticPass static_pass {m_log};
StaticPass static_pass {m_log, m_sym};
static_pass.check_children(root);
compile_children(root, prog);
failed = true;
@ -52,6 +53,33 @@ namespace roza
} break;
case NODE_IDENT: {
SymEntry const& entry = m_sym.find(root->repr());
prog->push_instr(OP_LOAD_GLOBAL, entry.addr);
} break;
case NODE_VARDECL: {
std::string name = root->child(0)->repr();
compile_node(root->child(1), prog);
int addr = m_sym.declare_mut(name, root->child(1));
prog->push_instr(OP_STORE_GLOBAL, addr);
} break;
case NODE_CONSTDECL: {
std::string name = root->child(0)->repr();
compile_node(root->child(1), prog);
int addr = m_sym.declare(name, root->child(1));
prog->push_instr(OP_STORE_GLOBAL, addr);
} break;
case NODE_ASSIGN: {
int addr = m_sym.find(root->child(0)->repr()).addr;
compile_node(root->child(1), prog);
prog->push_instr(OP_STORE_GLOBAL, addr);
} 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

@ -6,6 +6,7 @@
#include "Node.hpp"
#include "opcodes.hpp"
#include "StatusLog.hpp"
#include "SymTable.hpp"
namespace roza
{
@ -21,6 +22,7 @@ namespace roza
private:
StatusLog& m_log;
SymTable m_sym;
};
}

View File

@ -23,9 +23,12 @@ namespace roza
{"^", NODE_POW, false},
{"(", NODE_OPAR, false},
{")", NODE_CPAR, false},
{"=", NODE_ASSIGN, false},
};
std::vector<std::tuple<std::string, NodeType, bool>> keywords = {
{"let!", NODE_LET_MUT, false},
{"let", NODE_LET, false},
{"true", NODE_BOOL, true},
{"false", NODE_BOOL, true},
{"and", NODE_AND, false},
@ -35,7 +38,6 @@ namespace roza
{"assert", NODE_ASSERT, false},
};
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
for (auto const& entry: keywords)
@ -54,6 +56,7 @@ namespace roza
std::get<2>(entry)));
}
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
}
/*virtual*/ Lexer::~Lexer()
@ -225,4 +228,42 @@ namespace roza
};
}
ScanInfo Lexer::scan_ident() const
{
size_t cursor = m_cursor;
std::string value;
auto is_ident = [](size_t pos, char c){
bool other = false;
if (pos > 0)
{
other =
std::isdigit(c);
}
return c == '_' || std::isalpha(c) || other;
};
size_t pos = 0;
while (cursor < m_source.size()
&& is_ident(pos, m_source[cursor]))
{
value += m_source[cursor];
cursor++;
pos++;
}
if (value.empty() == false)
{
return ScanInfo {
std::make_shared<Node>(NODE_IDENT, value, loc()),
cursor
};
}
return ScanInfo {
};
}
}

View File

@ -48,6 +48,8 @@ namespace roza
ScanInfo scan_keyword(std::string const& keyword,
NodeType type,
bool value=false) const;
ScanInfo scan_ident() const;
};
}

View File

@ -11,7 +11,9 @@
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_ASSERT), G(NODE_ASSERT_STATIC_FAIL)
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)
namespace roza
{

View File

@ -94,6 +94,12 @@ namespace roza
return ret;
}
void Parser::ensure(NodeType type)
{
consume_or_nullptr(type);
consume_all(type);
}
void Parser::next()
{
m_cursor++;
@ -121,22 +127,88 @@ namespace roza
{
consume_all(NODE_EOI);
std::shared_ptr<Node> root;
if (type_is(NODE_ASSERT)
|| type_is(NODE_ASSERT_STATIC_FAIL))
if (type_is(NODE_ASSERT))
{
root = consume();
auto root = consume();
root->add_child(parse_expr());
ensure(NODE_EOI);
return root;
}
else if (type_is(NODE_ASSERT_STATIC_FAIL))
{
auto root = consume();
root->add_child(parse_instr());
return root;
}
else if (type_is(NODE_LET))
{
auto root = parse_constdecl();
ensure(NODE_EOI);
return root;
}
else if (type_is(NODE_LET_MUT))
{
auto root = parse_vardecl();
ensure(NODE_EOI);
return root;
}
else if (type_is(NODE_IDENT) && type_is(NODE_ASSIGN, 1))
{
auto root = parse_assign();
ensure(NODE_EOI);
return root;
}
else
{
root = parse_expr();
auto root = parse_expr();
ensure(NODE_EOI);
return root;
}
m_log.fatal(node()->loc(),
std::string()
+ "unknown instruction '"
+ std::string(NodeTypeStr[node()->type()])
.substr(std::string("NODE_").size())
+ "'");
consume_or_nullptr(NODE_EOI);
consume_all(NODE_EOI);
return nullptr;
}
std::shared_ptr<Node> Parser::parse_vardecl()
{
consume(NODE_LET_MUT);
auto root = std::make_shared<Node>(NODE_VARDECL, "", node()->loc());
auto ident = consume(NODE_IDENT);
consume(NODE_ASSIGN);
auto expr = parse_expr();
root->add_child(ident);
root->add_child(expr);
return root;
}
std::shared_ptr<Node> Parser::parse_constdecl()
{
consume(NODE_LET);
auto root = std::make_shared<Node>(NODE_CONSTDECL, "", node()->loc());
auto ident = consume(NODE_IDENT);
consume(NODE_ASSIGN);
auto expr = parse_expr();
root->add_child(ident);
root->add_child(expr);
return root;
}
std::shared_ptr<Node> Parser::parse_assign()
{
auto lhs = consume(NODE_IDENT);
auto root = consume(NODE_ASSIGN);
root->add_child(lhs);
root->add_child(parse_expr());
return root;
}
@ -320,13 +392,14 @@ namespace roza
std::shared_ptr<Node> Parser::parse_base()
{
if (type_is(NODE_INT)
|| type_is(NODE_BOOL))
|| type_is(NODE_BOOL)
|| type_is(NODE_IDENT))
{
return consume();
}
m_log.fatal(node()->loc(),
"unknown node '"
"cannot parse unknown node '"
+ node()->string()
+ "'");

View File

@ -27,10 +27,16 @@ namespace roza
std::shared_ptr<Node> consume(NodeType type);
void consume_all(NodeType type);
std::shared_ptr<Node> consume();
void ensure(NodeType type);
void next();
std::shared_ptr<Node> parse_prog();
std::shared_ptr<Node> parse_instr();
std::shared_ptr<Node> parse_vardecl();
std::shared_ptr<Node> parse_constdecl();
std::shared_ptr<Node> parse_assign();
std::shared_ptr<Node> parse_expr();
std::shared_ptr<Node> parse_imp();
std::shared_ptr<Node> parse_or();

View File

@ -5,7 +5,13 @@
namespace roza
{
/*explicit*/ StaticPass::StaticPass(StatusLog& log)
: StaticPass (log, SymTable {})
{
}
/*explicit*/ StaticPass::StaticPass(StatusLog& log, SymTable const& sym_table)
: m_log { log }
, m_sym { SymTable(sym_table) }
{
}
@ -16,9 +22,35 @@ namespace roza
void StaticPass::check(std::shared_ptr<Node> root)
{
TypeResolver resolver {m_log};
assert(root);
switch (root->type())
{
case NODE_CONSTDECL: {
check(root->child(1));
m_sym.declare(root->child(0)->repr(), root->child(1));
} break;
case NODE_VARDECL: {
check(root->child(1));
m_sym.declare_mut(root->child(0)->repr(), root->child(1));
} break;
case NODE_ASSIGN: {
auto const& entry = m_sym.find(root->child(0)->repr());
if (!entry.is_mut)
{
m_log.fatal(root->child(0)->loc(),
root->child(0)->repr() + " is not mutable");
}
auto lhs = resolver.find(entry.node, m_sym);
auto rhs = resolver.find(root->child(1), m_sym);
check_types(root, lhs, rhs);
} break;
case NODE_IDENT:
case NODE_ASSERT_STATIC_FAIL:
case NODE_INT:
case NODE_BOOL:
@ -27,8 +59,8 @@ namespace roza
case NODE_EQ:
case NODE_NE: {
check_children(root);
auto lhs = resolver.find(root->child(0));
auto rhs = resolver.find(root->child(1));
auto lhs = resolver.find(root->child(0), m_sym);
auto rhs = resolver.find(root->child(1), m_sym);
check_types(root, lhs, rhs);
} break;
@ -36,8 +68,8 @@ namespace roza
case NODE_OR:
case NODE_AND: {
check_children(root);
auto lhs = resolver.find(root->child(0));
auto rhs = resolver.find(root->child(1));
auto lhs = resolver.find(root->child(0), m_sym);
auto rhs = resolver.find(root->child(1), m_sym);
check_types(root, lhs, std::make_shared<Type>(TY_BOOL));
check_types(root, lhs, rhs);
} break;
@ -45,7 +77,7 @@ namespace roza
case NODE_ASSERT:
case NODE_NOT: {
check_children(root);
auto lhs = resolver.find(root->child(0));
auto lhs = resolver.find(root->child(0), m_sym);
check_types(root, lhs, std::make_shared<Type>(TY_BOOL));
} break;
@ -60,8 +92,9 @@ namespace roza
case NODE_MOD:
case NODE_POW: {
check_children(root);
auto lhs = resolver.find(root->child(0));
auto rhs = resolver.find(root->child(1));
auto lhs = resolver.find(root->child(0), m_sym);
auto rhs = resolver.find(root->child(1), m_sym);
check_types(root, lhs, std::make_shared<Type>(TY_INT));
check_types(root, lhs, rhs);
} break;
@ -69,7 +102,7 @@ namespace roza
case NODE_UADD:
case NODE_USUB: {
check_children(root);
auto lhs = resolver.find(root->child(0));
auto lhs = resolver.find(root->child(0), m_sym);
check_types(root, lhs, std::make_shared<Type>(TY_INT));
} break;
@ -94,6 +127,9 @@ namespace roza
std::shared_ptr<Type> lhs,
std::shared_ptr<Type> rhs)
{
assert(lhs);
assert(rhs);
if (!lhs->equals(*rhs))
{
m_log.fatal(root->loc(),

View File

@ -5,6 +5,7 @@
#include "StatusLog.hpp"
#include "Node.hpp"
#include "Type.hpp"
#include "SymTable.hpp"
namespace roza
{
@ -12,6 +13,7 @@ namespace roza
{
public:
explicit StaticPass(StatusLog& log);
explicit StaticPass(StatusLog& log, SymTable const& sym_table);
virtual ~StaticPass();
void check(std::shared_ptr<Node> root);
@ -19,6 +21,7 @@ namespace roza
private:
StatusLog& m_log;
SymTable m_sym;
void check_types(std::shared_ptr<Node> root,
std::shared_ptr<Type> lhs,

69
lib/SymTable.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "SymTable.hpp"
namespace roza
{
/*static*/ int SymTable::addr = 0;
/*explicit*/ SymTable::SymTable()
{
}
/*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;
}
}
/*virtual*/ SymTable::~SymTable()
{
}
int SymTable::declare(std::string const& name, std::shared_ptr<Node> node)
{
assert(!exists(name));
m_entries.insert({name, SymEntry {
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));
m_entries.insert({name, SymEntry {
SymTable::addr++,
m_scope,
node,
true
}});
return SymTable::addr - 1;
}
SymEntry& SymTable::find(std::string const& name)
{
assert(exists(name));
return m_entries[name];
}
SymEntry const& SymTable::find(std::string const& name) const
{
assert(exists(name));
return m_entries.at(name);
}
bool SymTable::exists(std::string const& name) const
{
return m_entries.find(name) != std::end(m_entries);
}
}

39
lib/SymTable.hpp Normal file
View File

@ -0,0 +1,39 @@
#ifndef roza_SYMTABLE_HPP
#define roza_SYMTABLE_HPP
#include "commons.hpp"
#include "Node.hpp"
namespace roza
{
struct SymEntry {
int addr;
int scope;
std::shared_ptr<Node> node;
bool is_mut;
};
class SymTable
{
public:
explicit SymTable();
explicit SymTable(SymTable const& sym_table);
virtual ~SymTable();
int declare(std::string const& name, std::shared_ptr<Node> node);
int declare_mut(std::string const& name, std::shared_ptr<Node> node);
SymEntry& find(std::string const& name);
SymEntry const& find(std::string const& name) const;
bool exists(std::string const& name) const;
private:
static int addr;
std::unordered_map<std::string, SymEntry> m_entries;
int m_scope = 0;
};
}
#endif

View File

@ -11,12 +11,24 @@ namespace roza
{
}
std::shared_ptr<Type> TypeResolver::find(std::shared_ptr<Node> root)
std::shared_ptr<Type> TypeResolver::find(std::shared_ptr<Node> root,
SymTable const& sym)
{
switch (root->type())
{
case NODE_PROG: {
return find(root->child(root->size() - 1));
return find(root->child(root->size() - 1), sym);
} break;
case NODE_IDENT: {
std::string name = root->repr();
SymEntry const& entry = sym.find(name);
return find(entry.node, sym);
} break;
case NODE_CONSTDECL:
case NODE_VARDECL: {
auto ty = find(root->child(1), sym);
} break;
case NODE_INT: {
@ -45,7 +57,7 @@ namespace roza
case NODE_POW:
case NODE_UADD:
case NODE_USUB:{
return find(root->child(0));
return find(root->child(0), sym);
} break;
default:

View File

@ -5,6 +5,7 @@
#include "Type.hpp"
#include "StatusLog.hpp"
#include "Node.hpp"
#include "SymTable.hpp"
namespace roza
{
@ -14,7 +15,7 @@ namespace roza
explicit TypeResolver(StatusLog& log);
virtual ~TypeResolver();
std::shared_ptr<Type> find(std::shared_ptr<Node> root);
std::shared_ptr<Type> find(std::shared_ptr<Node> root, SymTable const& sym);
private:
StatusLog& m_log;
};

View File

@ -19,6 +19,23 @@ namespace roza
{
switch (program->opcode(m_pc))
{
case OP_LOAD_GLOBAL: {
int addr = *program->param(m_pc);
auto value = m_globals[addr];
push(program->push_value(value));
m_pc++;
} break;
case OP_STORE_GLOBAL: {
auto value = program->value(pop());
int addr = *program->param(m_pc);
m_globals[addr] = value;
m_pc++;
} break;
case OP_ASSERT: {
auto value = program->value(pop());

View File

@ -27,6 +27,7 @@ namespace roza
StatusLog& m_log;
std::vector<param_t> m_stack;
std::shared_ptr<Program> m_last_program;
std::unordered_map<int, std::shared_ptr<Value>> m_globals;
size_t m_pc = 0;
void push(param_t param);

View File

@ -8,7 +8,8 @@
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_ASSERT)
G(OP_IMP), G(OP_EQ), G(OP_ILT), G(OP_IGT), G(OP_ASSERT), G(OP_STORE_GLOBAL), \
G(OP_LOAD_GLOBAL)
namespace roza
{

View File

@ -22,6 +22,7 @@ roza_lib = static_library(
'lib/Type.cpp',
'lib/Value.cpp',
'lib/TypeResolver.cpp',
'lib/SymTable.cpp',
]
)

21
roza_tests/test_var.roza Normal file
View File

@ -0,0 +1,21 @@
let! a = 34
assert 34 == a
assert 102 == 3 * a
a = 9
a = a + 1
assert 11 == a + 1
assert_static_fail a and true
assert_static_fail a = false
let b = 7
assert 7 == b
assert 42 == 6 * b
assert 17 == a + b
assert_static_fail b = 4

View File

@ -58,7 +58,6 @@ TEST_CASE_METHOD(LexerTest, "Lexer_int_arith")
TEST_CASE_METHOD(LexerTest, "Lexer_keywords")
{
REQUIRE_THROWS(m_lexer.scan(" andor "));
REQUIRE_NOTHROW(m_lexer.scan(" and+ "));
REQUIRE_NOTHROW(m_lexer.scan(" (and) "));
}
@ -94,3 +93,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_asserts")
REQUIRE("ASSERT_STATIC_FAIL" == get_str(1));
REQUIRE("" == get_str(2));
}
TEST_CASE_METHOD(LexerTest, "Lexer_declarations")
{
m_lexer.scan(" let let! hello = ");
REQUIRE("LET" == get_str(0));
REQUIRE("LET_MUT" == get_str(1));
REQUIRE("IDENT[hello]" == get_str(2));
REQUIRE("ASSIGN" == get_str(3));
REQUIRE("" == get_str(4));
}

View File

@ -110,3 +110,15 @@ TEST_CASE_METHOD(ParserTest, "Parser_assertions")
test_node("PROG(ASSERT_STATIC_FAIL(EQ(ADD(INT[1],INT[1]),INT[2])))",
"assert_static_fail 1 + 1 == 2");
}
TEST_CASE_METHOD(ParserTest, "Parser_vardecl")
{
test_node("PROG(VARDECL(IDENT[x],INT[34]))",
"let! x = 34");
test_node("PROG(CONSTDECL(IDENT[x],INT[34]))",
"let x = 34");
test_node("PROG(CONSTDECL(IDENT[_coucou],MUL(INT[6],INT[7])))",
"let _coucou = 6 * 7");
}

View File

@ -16,11 +16,12 @@ public:
roza::StatusLog log;
roza::Lexer lexer {log, loc};
roza::Parser parser {lexer, log};
roza::SymTable sym;
roza::TypeResolver resolver {log};
lexer.scan(source);
auto node = parser.parse();
return resolver.find(node);
return resolver.find(node, sym);
}
void test_ty(std::string const& source, std::shared_ptr<roza::Type> ty)