ADD: basic function3

main
bog 2023-09-01 22:38:12 +02:00
parent d72660e6de
commit 620818c42a
30 changed files with 858 additions and 120 deletions

View File

@ -3,6 +3,7 @@ PROG ::= (INSTR (EOI+ INSTR)*)?
INSTR ::= EXPR INSTR ::= EXPR
| assert EXPR | assert EXPR
| assert_static_fail INSTR | assert_static_fail INSTR
| return EXPR
| VARDECL | VARDECL
| CONSTDECL | CONSTDECL
| ASSIGN | ASSIGN
@ -30,4 +31,14 @@ FACTOR ::= UNOP ((mul | div | mod) UNOP)*
UNOP ::= (add | sub | not)? POW UNOP ::= (add | sub | not)? POW
POW ::= GROUP (pow GROUP)? POW ::= GROUP (pow GROUP)?
GROUP ::= BASE | opar EXPR cpar GROUP ::= BASE | opar EXPR cpar
BASE ::= int | bool | ident BASE ::= int | bool | ident | FUN | CALL
CALL ::= obrace ident expr* cbrace
ARGS ::= expr*
FUN ::= fun opar PARAMS cpar RET BODY end
PARAMS ::= (PARAM (comma PARAM))?
PARAM ::= ident (as TYPE)
RET ::= arrow TYPE |
BODY ::= INSTR*
TYPE ::= type | fun lt (type (arrow type)*)? gt

View File

@ -1,5 +1,6 @@
#include "Compiler.hpp" #include "Compiler.hpp"
#include "lib/Node.hpp" #include "lib/Node.hpp"
#include "lib/TypeResolver.hpp"
#include "lib/opcodes.hpp" #include "lib/opcodes.hpp"
#include "StaticPass.hpp" #include "StaticPass.hpp"
@ -14,20 +15,64 @@ namespace roza
{ {
} }
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> root) std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> root,
std::shared_ptr<SymTable> sym)
{ {
auto program = std::make_shared<Program>(); auto program = std::make_shared<Program>();
compile_node(root, program); compile_node(root, program, sym);
return program; return program;
} }
void Compiler::compile_node(std::shared_ptr<Node> root, std::shared_ptr<Program> prog) void Compiler::compile_node(std::shared_ptr<Node> root,
std::shared_ptr<Program> prog,
std::shared_ptr<SymTable> sym)
{ {
switch (root->type()) switch (root->type())
{ {
case NODE_FUN: {
auto params = root->child(0);
auto ret = root->child(1);
auto body = root->child(2);
TypeResolver resolver {m_log};
auto ty = resolver.find(root, *sym);
auto fun = std::make_shared<Fun>(ty);
for (size_t i=0; i<params->size(); i++)
{
auto param = params->child(i);
std::string name = param->child(0)->repr();
auto node = param->child(1);
fun->sym()->declare_mut(name, node);
}
m_fun_stack.push_back(fun);
compile_node(body, fun->program(), fun->sym());
m_fun_stack.pop_back();
auto value = std::make_shared<Value>(fun, root->loc());
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
} break;
case NODE_RETURN: {
compile_node(root->child(0), prog, sym);
prog->push_instr(OP_RET);
} break;
case NODE_CALL: {
auto fun_addr = sym->find(root->child(0)->repr()).addr;
prog->push_instr(OP_PUSH_CONST, fun_addr);
compile_children(root->child(1), prog, sym);
prog->push_instr(OP_CALL, root->child(1)->size());
} break;
case NODE_IF: { case NODE_IF: {
m_sym.enter_scope(); sym->enter_scope();
std::vector<size_t> to_end; std::vector<size_t> to_end;
@ -36,11 +81,11 @@ namespace roza
auto cond = n->child(0); auto cond = n->child(0);
auto then = n->child(1); auto then = n->child(1);
compile_node(cond, prog); compile_node(cond, prog, sym);
size_t cond_addr = prog->size(); size_t cond_addr = prog->size();
prog->push_instr(OP_BRF, 0); prog->push_instr(OP_BRF, 0);
compile_node(then, prog); compile_node(then, prog, sym);
to_end.push_back(prog->size()); to_end.push_back(prog->size());
prog->push_instr(OP_BR, 0); prog->push_instr(OP_BR, 0);
@ -50,7 +95,7 @@ namespace roza
if (n->size() > 2) if (n->size() > 2)
{ {
auto next = n->child(2); auto next = n->child(2);
compile_node(next, prog); compile_node(next, prog, sym);
} }
}; };
@ -61,17 +106,18 @@ namespace roza
prog->set_param(addr, prog->size()); prog->set_param(addr, prog->size());
} }
m_sym.leave_scope(); sym->leave_scope();
} break; } break;
case NODE_BODY:
case NODE_ELSE: case NODE_ELSE:
case NODE_THEN: { case NODE_THEN: {
compile_children(root, prog); compile_children(root, prog, sym);
} break; } break;
case NODE_ASSERT: { case NODE_ASSERT: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_ASSERT); prog->push_instr(OP_ASSERT);
} break; } break;
@ -80,10 +126,10 @@ namespace roza
try try
{ {
StaticPass static_pass {m_log, m_sym}; StaticPass static_pass {m_log, *sym};
static_pass.check_children(root); static_pass.check_children(root);
compile_children(root, prog); compile_children(root, prog, sym);
failed = true; failed = true;
} }
@ -99,30 +145,64 @@ namespace roza
} break; } break;
case NODE_IDENT: { case NODE_IDENT: {
SymEntry const& entry = m_sym.find(root->repr()); SymEntry const& entry = sym->find(root->repr());
prog->push_instr(OP_LOAD_GLOBAL, entry.addr);
if (m_fun_stack.empty())
{
prog->push_instr(OP_LOAD_GLOBAL, entry.addr);
}
else
{
prog->push_instr(OP_LOAD_LOCAL, entry.addr);
}
} break; } break;
case NODE_VARDECL: { case NODE_VARDECL: {
std::string name = root->child(0)->repr(); std::string name = root->child(0)->repr();
compile_node(root->child(1), prog); compile_node(root->child(1), prog, sym);
int addr = m_sym.declare_mut(name, root->child(1)); int addr = sym->declare_mut(name, root->child(1));
prog->push_instr(OP_STORE_GLOBAL, addr);
if (m_fun_stack.empty())
{
prog->push_instr(OP_STORE_GLOBAL, addr);
}
else
{
prog->push_instr(OP_STORE_LOCAL, addr);
}
} break; } break;
case NODE_CONSTDECL: { case NODE_CONSTDECL: {
std::string name = root->child(0)->repr(); std::string name = root->child(0)->repr();
compile_node(root->child(1), prog); compile_node(root->child(1), prog, sym);
int addr = m_sym.declare(name, root->child(1)); int addr = sym->declare(name, root->child(1));
prog->push_instr(OP_STORE_GLOBAL, addr);
if (m_fun_stack.empty())
{
prog->push_instr(OP_STORE_GLOBAL, addr);
}
else
{
prog->push_instr(OP_STORE_LOCAL, addr);
}
} break; } break;
case NODE_ASSIGN: { case NODE_ASSIGN: {
int addr = m_sym.find(root->child(0)->repr()).addr; int addr = sym->find(root->child(0)->repr()).addr;
compile_node(root->child(1), prog); compile_node(root->child(1), prog, sym);
prog->push_instr(OP_STORE_GLOBAL, addr);
if (m_fun_stack.empty())
{
prog->push_instr(OP_STORE_GLOBAL, addr);
}
else
{
prog->push_instr(OP_STORE_LOCAL, addr);
}
} break; } break;
case NODE_INT: { case NODE_INT: {
@ -136,100 +216,100 @@ namespace roza
} break; } break;
case NODE_EQ: { case NODE_EQ: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_EQ); prog->push_instr(OP_EQ);
} break; } break;
case NODE_NE: { case NODE_NE: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_EQ); prog->push_instr(OP_EQ);
prog->push_instr(OP_NOT); prog->push_instr(OP_NOT);
} break; } break;
case NODE_LT: { case NODE_LT: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_ILT); prog->push_instr(OP_ILT);
} break; } break;
case NODE_GT: { case NODE_GT: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IGT); prog->push_instr(OP_IGT);
} break; } break;
case NODE_LE: { case NODE_LE: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IGT); prog->push_instr(OP_IGT);
prog->push_instr(OP_NOT); prog->push_instr(OP_NOT);
} break; } break;
case NODE_GE: { case NODE_GE: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_ILT); prog->push_instr(OP_ILT);
prog->push_instr(OP_NOT); prog->push_instr(OP_NOT);
} break; } break;
case NODE_IMP: { case NODE_IMP: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IMP); prog->push_instr(OP_IMP);
} break; } break;
case NODE_AND: { case NODE_AND: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_AND); prog->push_instr(OP_AND);
} break; } break;
case NODE_OR: { case NODE_OR: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_OR); prog->push_instr(OP_OR);
} break; } break;
case NODE_NOT: { case NODE_NOT: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_NOT); prog->push_instr(OP_NOT);
} break; } break;
case NODE_ADD: { case NODE_ADD: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IADD); prog->push_instr(OP_IADD);
} break; } break;
case NODE_SUB: { case NODE_SUB: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_ISUB); prog->push_instr(OP_ISUB);
} break; } break;
case NODE_MUL: { case NODE_MUL: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IMUL); prog->push_instr(OP_IMUL);
} break; } break;
case NODE_DIV: { case NODE_DIV: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IDIV); prog->push_instr(OP_IDIV);
} break; } break;
case NODE_MOD: { case NODE_MOD: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IMOD); prog->push_instr(OP_IMOD);
} break; } break;
case NODE_POW: { case NODE_POW: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IPOW); prog->push_instr(OP_IPOW);
} break; } break;
case NODE_UADD: { case NODE_UADD: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IUADD); prog->push_instr(OP_IUADD);
} break; } break;
case NODE_USUB: { case NODE_USUB: {
compile_children(root, prog); compile_children(root, prog, sym);
prog->push_instr(OP_IUSUB); prog->push_instr(OP_IUSUB);
} break; } break;
case NODE_PROG: { case NODE_PROG: {
compile_children(root, prog); compile_children(root, prog, sym);
} break; } break;
default: default:
@ -237,11 +317,13 @@ namespace roza
} }
} }
void Compiler::compile_children(std::shared_ptr<Node> root, std::shared_ptr<Program> prog) void Compiler::compile_children(std::shared_ptr<Node> root,
std::shared_ptr<Program> prog,
std::shared_ptr<SymTable> sym)
{ {
for (size_t i=0; i<root->size(); i++) for (size_t i=0; i<root->size(); i++)
{ {
compile_node(root->child(i), prog); compile_node(root->child(i), prog, sym);
} }
} }
} }

View File

@ -7,6 +7,7 @@
#include "opcodes.hpp" #include "opcodes.hpp"
#include "StatusLog.hpp" #include "StatusLog.hpp"
#include "SymTable.hpp" #include "SymTable.hpp"
#include "Fun.hpp"
namespace roza namespace roza
{ {
@ -16,13 +17,20 @@ namespace roza
explicit Compiler(StatusLog& log); explicit Compiler(StatusLog& log);
virtual ~Compiler(); virtual ~Compiler();
std::shared_ptr<Program> compile(std::shared_ptr<Node> root); std::shared_ptr<Program> compile(std::shared_ptr<Node> root,
void compile_node(std::shared_ptr<Node> root, std::shared_ptr<Program> prog); std::shared_ptr<SymTable> sym);
void compile_children(std::shared_ptr<Node> root, std::shared_ptr<Program> prog);
void compile_node(std::shared_ptr<Node> root,
std::shared_ptr<Program> prog,
std::shared_ptr<SymTable> sym);
void compile_children(std::shared_ptr<Node> root,
std::shared_ptr<Program> prog,
std::shared_ptr<SymTable> sym);
private: private:
StatusLog& m_log; StatusLog& m_log;
SymTable m_sym; std::vector<std::shared_ptr<Fun>> m_fun_stack;
}; };
} }

13
lib/Fun.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "Fun.hpp"
namespace roza
{
/*explicit*/ Fun::Fun(std::shared_ptr<Type> type)
: m_type { type }
{
}
/*virtual*/ Fun::~Fun()
{
}
}

28
lib/Fun.hpp Normal file
View File

@ -0,0 +1,28 @@
#ifndef roza_FUN_HPP
#define roza_FUN_HPP
#include "commons.hpp"
#include "FunTy.hpp"
#include "Program.hpp"
#include "SymTable.hpp"
namespace roza
{
class Fun
{
public:
explicit Fun(std::shared_ptr<Type> type);
virtual ~Fun();
std::shared_ptr<Type> type() const { return m_type; }
std::shared_ptr<SymTable> sym() const { return m_sym; }
std::shared_ptr<Program> program() const { return m_program; }
private:
std::shared_ptr<Type> m_type;
std::shared_ptr<Program> m_program = std::make_shared<Program>();
std::shared_ptr<SymTable> m_sym = std::make_shared<SymTable>();
};
}
#endif

61
lib/FunTy.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "FunTy.hpp"
namespace roza
{
/*explicit*/ FunTy::FunTy()
: Type(TY_FUN)
{
}
/*virtual*/ FunTy::~FunTy()
{
}
void FunTy::add_input(std::shared_ptr<Type> ty)
{
m_inputs.push_back(ty);
}
void FunTy::set_output(std::shared_ptr<Type> ty)
{
m_output = ty;
}
std::string FunTy::string() const /*override*/
{
std::stringstream ss;
ss << "FUN";
ss << "<";
if (m_inputs.empty() && m_output->base() == TY_NIL)
{
ss << ">";
return ss.str();
}
std::string sep;
for (auto ty: m_inputs)
{
ss << sep << ty->string();
sep = " -> ";
}
ss << " -> " << m_output->string();
ss << ">";
return ss.str();
}
bool FunTy::equals(BaseType base_type) const /*override*/
{
return base_type == TY_FUN;
}
bool FunTy::equals(Type const&) const /*override*/
{
return false;
}
}

29
lib/FunTy.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef roza_FUNTY_HPP
#define roza_FUNTY_HPP
#include "Type.hpp"
namespace roza
{
class FunTy: public Type
{
public:
explicit FunTy();
virtual ~FunTy();
std::shared_ptr<Type> get_output() const { return m_output; }
void add_input(std::shared_ptr<Type> ty);
void set_output(std::shared_ptr<Type> ty);
std::string string() const override;
bool equals(BaseType rhs) const override;
bool equals(Type const& rhs) const override;
private:
std::vector<std::shared_ptr<Type>> m_inputs;
std::shared_ptr<Type> m_output = std::make_shared<Type>(TY_NIL);
};
}
#endif

View File

@ -8,7 +8,9 @@ namespace roza
, m_loc { loc } , m_loc { loc }
{ {
std::vector<std::tuple<std::string, NodeType, bool>> texts = { std::vector<std::tuple<std::string, NodeType, bool>> texts = {
{"->", NODE_ARROW, false},
{";", NODE_EOI, false}, {";", NODE_EOI, false},
{",", NODE_COMMA, false},
{"==", NODE_EQ, false}, {"==", NODE_EQ, false},
{"!=", NODE_NE, false}, {"!=", NODE_NE, false},
{"<=", NODE_LE, false}, {"<=", NODE_LE, false},
@ -24,10 +26,18 @@ namespace roza
{"^", NODE_POW, false}, {"^", NODE_POW, false},
{"(", NODE_OPAR, false}, {"(", NODE_OPAR, false},
{")", NODE_CPAR, false}, {")", NODE_CPAR, false},
{"{", NODE_OBRACE, false},
{"}", NODE_CBRACE, false},
{"=", NODE_ASSIGN, false}, {"=", NODE_ASSIGN, false},
}; };
std::vector<std::tuple<std::string, NodeType, bool>> keywords = { std::vector<std::tuple<std::string, NodeType, bool>> keywords = {
{"return", NODE_RETURN, true},
{"int", NODE_TYPE, true},
{"bool", NODE_TYPE, true},
{"nil", NODE_TYPE, true},
{"fun", NODE_FUN, false},
{"as", NODE_AS, false},
{"if", NODE_IF, false}, {"if", NODE_IF, false},
{"else", NODE_ELSE, false}, {"else", NODE_ELSE, false},
{"end", NODE_END, false}, {"end", NODE_END, false},

View File

@ -14,7 +14,10 @@
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_IDENT), G(NODE_ASSIGN), G(NODE_LET), G(NODE_LET_MUT), \
G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_IF), G(NODE_ELSE), \ G(NODE_VARDECL), G(NODE_CONSTDECL), G(NODE_IF), G(NODE_ELSE), \
G(NODE_THEN), G(NODE_END) G(NODE_THEN), G(NODE_END), G(NODE_FUN), G(NODE_AS), G(NODE_TYPE), \
G(NODE_PARAMS), G(NODE_RET), G(NODE_BODY), G(NODE_ARROW), \
G(NODE_COMMA), G(NODE_PARAM), G(NODE_RETURN), G(NODE_OBRACE), \
G(NODE_CBRACE), G(NODE_CALL), G(NODE_ARGS)
namespace roza namespace roza
{ {

View File

@ -141,6 +141,13 @@ namespace roza
root->add_child(parse_instr()); root->add_child(parse_instr());
return root; return root;
} }
else if (type_is(NODE_RETURN))
{
auto root = consume();
root->add_child(parse_expr());
ensure(NODE_EOI);
return root;
}
else if (type_is(NODE_LET)) else if (type_is(NODE_LET))
{ {
auto root = parse_constdecl(); auto root = parse_constdecl();
@ -453,6 +460,16 @@ namespace roza
return consume(); return consume();
} }
if (type_is(NODE_FUN))
{
return parse_fun();
}
if (type_is(NODE_OBRACE))
{
return parse_call();
}
m_log.fatal(node()->loc(), m_log.fatal(node()->loc(),
"cannot parse unknown node '" "cannot parse unknown node '"
+ node()->string() + node()->string()
@ -461,9 +478,133 @@ namespace roza
return nullptr; return nullptr;
} }
std::shared_ptr<Node> Parser::parse_call()
{
auto root = std::make_shared<Node>(NODE_CALL, "", node()->loc());
consume(NODE_OBRACE);
root->add_child(consume(NODE_IDENT));
root->add_child(parse_args());
consume(NODE_CBRACE);
return root;
}
std::shared_ptr<Node> Parser::parse_args()
{
auto root = std::make_shared<Node>(NODE_ARGS, "", node()->loc());
while (!type_is(NODE_CBRACE))
{
root->add_child(parse_expr());
}
return root;
}
std::shared_ptr<Node> Parser::parse_int() std::shared_ptr<Node> Parser::parse_int()
{ {
auto root = consume(NODE_INT); auto root = consume(NODE_INT);
return root; return root;
} }
std::shared_ptr<Node> Parser::parse_fun()
{
auto root = consume(NODE_FUN);
consume(NODE_OPAR);
root->add_child(parse_params());
consume(NODE_CPAR);
root->add_child(parse_ret());
root->add_child(parse_body());
consume(NODE_END);
return root;
}
std::shared_ptr<Node> Parser::parse_params()
{
auto root = std::make_shared<Node>(NODE_PARAMS, "", m_lexer.loc());
if (type_is(NODE_CPAR))
{
return root;
}
root->add_child(parse_param());
while (type_is(NODE_COMMA))
{
consume();
root->add_child(parse_param());
}
return root;
}
std::shared_ptr<Node> Parser::parse_param()
{
auto root = std::make_shared<Node>(NODE_PARAM, "", m_lexer.loc());
root->add_child(consume(NODE_IDENT));
if(type_is(NODE_AS))
{
consume();
root->add_child(parse_type());
}
return root;
}
std::shared_ptr<Node> Parser::parse_ret()
{
auto root = std::make_shared<Node>(NODE_RET, "", m_lexer.loc());
if (type_is(NODE_ARROW))
{
consume();
root->add_child(parse_type());
}
return root;
}
std::shared_ptr<Node> Parser::parse_body()
{
auto root = std::make_shared<Node>(NODE_BODY, "", m_lexer.loc());
while (!type_is(NODE_END))
{
root->add_child(parse_instr());
}
return root;
}
std::shared_ptr<Node> Parser::parse_type()
{
if (type_is(NODE_FUN))
{
auto root = std::make_shared<Node>(NODE_TYPE, "fun", node()->loc());
consume();
consume(NODE_LT);
while (!type_is(NODE_GT))
{
root->add_child(parse_type());
if (type_is(NODE_ARROW))
{
consume();
}
}
consume(NODE_GT);
return root;
}
return consume(NODE_TYPE);
}
} }

View File

@ -52,7 +52,15 @@ namespace roza
std::shared_ptr<Node> parse_pow(); std::shared_ptr<Node> parse_pow();
std::shared_ptr<Node> parse_group(); std::shared_ptr<Node> parse_group();
std::shared_ptr<Node> parse_base(); std::shared_ptr<Node> parse_base();
std::shared_ptr<Node> parse_call();
std::shared_ptr<Node> parse_args();
std::shared_ptr<Node> parse_int(); std::shared_ptr<Node> parse_int();
std::shared_ptr<Node> parse_fun();
std::shared_ptr<Node> parse_params();
std::shared_ptr<Node> parse_param();
std::shared_ptr<Node> parse_ret();
std::shared_ptr<Node> parse_body();
std::shared_ptr<Node> parse_type();
}; };
} }

View File

@ -26,6 +26,66 @@ namespace roza
switch (root->type()) switch (root->type())
{ {
case NODE_FUN: {
//SymTable fun_sym;
//StaticPass pass {m_log, fun_sym};
m_sym.enter_scope();
auto params = root->child(0);
for (size_t i=0; i<params->size(); i++)
{
auto name = params->child(i)->child(0)->repr();
auto node = params->child(i)->child(1);
m_sym.declare_mut(name, node);
}
m_outer_fun_ret = root->child(1)->child(0);
check(root->child(2)->child(0));
m_outer_fun_ret = nullptr;
m_sym.leave_scope();
} break;
case NODE_RETURN: {
check_children(root);
auto actual_ty = resolver.find(root->child(0), m_sym);
auto fun_ty = resolver.find(m_outer_fun_ret, m_sym);
check_types(root, fun_ty, actual_ty);
} break;
case NODE_CALL: {
check_children(root);
std::string fname = root->child(0)->repr();
auto args = root->child(1);
auto entry = m_sym.find(fname);
auto params = entry.node->child(0);
if (args->size() != params->size())
{
std::stringstream ss;
ss << "function '"<< fname << "' expects " << params->size();
ss << " arguments,";
ss << " got " << args->size();
m_log.fatal(root->loc(), ss.str());
}
for (size_t i=0; i<args->size(); i++)
{
auto arg = args->child(i);
auto param = params->child(i);
auto arg_ty = resolver.find(arg, m_sym);
auto param_ty = resolver.find(param->child(1), m_sym);
check_types(root, param_ty, arg_ty);
}
} break;
case NODE_IF: { case NODE_IF: {
m_sym.enter_scope(); m_sym.enter_scope();
auto cond_type = resolver.find(root->child(0), m_sym); auto cond_type = resolver.find(root->child(0), m_sym);
@ -34,6 +94,7 @@ namespace roza
m_sym.leave_scope(); m_sym.leave_scope();
} break; } break;
case NODE_ARGS:
case NODE_THEN: case NODE_THEN:
case NODE_ELSE: { case NODE_ELSE: {
check_children(root); check_children(root);

View File

@ -22,6 +22,7 @@ namespace roza
private: private:
StatusLog& m_log; StatusLog& m_log;
SymTable m_sym; SymTable m_sym;
std::shared_ptr<Node> m_outer_fun_ret;
void check_types(std::shared_ptr<Node> root, void check_types(std::shared_ptr<Node> root,
std::shared_ptr<Type> lhs, std::shared_ptr<Type> lhs,

View File

@ -2,8 +2,6 @@
namespace roza namespace roza
{ {
/*static*/ int SymTable::addr = 0;
/*explicit*/ SymTable::SymTable() /*explicit*/ SymTable::SymTable()
{ {
} }
@ -48,13 +46,13 @@ namespace roza
m_entries.push_back(SymEntry { m_entries.push_back(SymEntry {
name, name,
SymTable::addr++, m_addr++,
m_scope, m_scope,
node, node,
false false
}); });
return SymTable::addr - 1; return m_addr - 1;
} }
int SymTable::declare_mut(std::string const& name, std::shared_ptr<Node> node) int SymTable::declare_mut(std::string const& name, std::shared_ptr<Node> node)
@ -137,4 +135,16 @@ namespace roza
return false; return false;
} }
std::string SymTable::string() const
{
std::stringstream ss;
for (size_t i=0; i<m_entries.size(); i++)
{
ss << m_entries[i].addr << "\t" << m_entries[i].name << std::endl;
}
return ss.str();
}
} }

View File

@ -34,8 +34,10 @@ namespace roza
bool exists(std::string const& name) const; bool exists(std::string const& name) const;
bool exists_in_scope(std::string const& name) const; bool exists_in_scope(std::string const& name) const;
std::string string() const;
private: private:
static int addr; int m_addr = 0;
std::vector<SymEntry> m_entries; std::vector<SymEntry> m_entries;
int m_scope = 0; int m_scope = 0;
}; };

View File

@ -11,18 +11,18 @@ namespace roza
{ {
} }
std::string Type::string() const /*virtual*/ std::string Type::string() const
{ {
return std::string(BaseTypeStr[m_base]) return std::string(BaseTypeStr[m_base])
.substr(std::string("TY_").size()); .substr(std::string("TY_").size());
} }
bool Type::equals(BaseType rhs) const /*virtual*/ bool Type::equals(BaseType rhs) const
{ {
return m_base == rhs; return m_base == rhs;
} }
bool Type::equals(Type const& rhs) const /*virtual*/ bool Type::equals(Type const& rhs) const
{ {
return m_base == rhs.m_base; return m_base == rhs.m_base;
} }

View File

@ -4,7 +4,7 @@
#include "commons.hpp" #include "commons.hpp"
#define BASE_TYPE(G) \ #define BASE_TYPE(G) \
G(TY_INT), G(TY_BOOL) G(TY_INT), G(TY_BOOL), G(TY_FUN), G(TY_NIL)
namespace roza namespace roza
{ {
@ -18,10 +18,10 @@ namespace roza
BaseType base() const { return m_base; } BaseType base() const { return m_base; }
std::string string() const; virtual std::string string() const;
bool equals(BaseType rhs) const; virtual bool equals(BaseType rhs) const;
bool equals(Type const& rhs) const; virtual bool equals(Type const& rhs) const;
private: private:
BaseType m_base; BaseType m_base;

View File

@ -1,4 +1,5 @@
#include "TypeResolver.hpp" #include "TypeResolver.hpp"
#include "FunTy.hpp"
namespace roza namespace roza
{ {
@ -20,6 +21,61 @@ namespace roza
return find(root->child(root->size() - 1), sym); return find(root->child(root->size() - 1), sym);
} break; } break;
case NODE_CALL: {
auto fun = sym.find(root->child(0)->repr()).node;
return find(fun->child(1), sym);
} break;
case NODE_RET: {
return find(root->child(0), sym);
} break;
case NODE_FUN: {
auto params = root->child(0);
auto ret = root->child(1);
auto ty = std::make_shared<FunTy>();
for (size_t i=0; i<params->size(); i++)
{
ty->add_input(find(params->child(i)->child(1), sym));
}
if (ret->size() > 0)
{
ty->set_output(find(ret->child(0), sym));
}
return ty;
} break;
case NODE_TYPE: {
if (root->repr() == "nil") { return std::make_shared<Type>(TY_NIL); }
if (root->repr() == "int") { return std::make_shared<Type>(TY_INT); }
if (root->repr() == "bool") { return std::make_shared<Type>(TY_BOOL); }
if (root->repr() == "fun")
{
auto ty = std::make_shared<FunTy>();
if (root->size() > 0)
{
for (size_t i=0; i<root->size() - 1; i++)
{
ty->add_input(find(root->child(i), sym));
}
}
if (root->size() > 0)
{
ty->set_output(find(root->child(root->size()-1), sym));
}
return ty;
}
m_log.fatal(root->loc(), "cannot find type of '" + root->repr() + "'");
} break;
case NODE_IDENT: { case NODE_IDENT: {
std::string name = root->repr(); std::string name = root->repr();
SymEntry const& entry = sym.find(name); SymEntry const& entry = sym.find(name);

View File

@ -1,10 +1,12 @@
#include "VM.hpp" #include "VM.hpp"
#include "lib/opcodes.hpp" #include "lib/opcodes.hpp"
#include "Fun.hpp"
namespace roza namespace roza
{ {
/*explicit*/ VM::VM(StatusLog& log) /*explicit*/ VM::VM(StatusLog& log)
: m_log { log } : m_log { log }
, m_sp { 0 }
{ {
} }
@ -20,6 +22,76 @@ namespace roza
switch (program->opcode(m_pc)) switch (program->opcode(m_pc))
{ {
case OP_CALL: {
size_t arity = *program->param(m_pc);
std::vector<param_t> args;
for (size_t i=0; i<arity; i++)
{
param_t val = pop();
args.insert(std::begin(args), val);
}
auto fun = program->value(pop())->as_fun();
var_map vars;
for (size_t i=0; i<args.size(); i++)
{
if (program->has_value(args[i]))
{
vars.insert({i, program->value(args[i])});
}
}
// Prepare
Frame frame {
vars,
m_pc,
m_sp,
m_bp
};
m_frames.push_back(frame);
m_pc = 0;
m_bp = m_sp;
// Execute
exec(fun->program());
// Clear
size_t sz = m_sp - m_bp;
std::vector<param_t> rets;
for (size_t i=0; i<sz; i++)
{
rets.insert(rets.begin(), pop());
}
// Restore
m_pc = m_frames.back().pc;
m_sp = m_frames.back().sp;
m_bp = m_frames.back().bp;
auto ty = std::static_pointer_cast<FunTy>(fun->type());
if (ty->get_output()->base() != TY_NIL)
{
push(program->push_value(fun->program()->value(rets.back())));
}
m_frames.pop_back();
m_pc++;
} break;
case OP_RET: {
m_pc = program->size();
} break;
case OP_BRF: { case OP_BRF: {
auto value = program->value(pop()); auto value = program->value(pop());
@ -54,6 +126,23 @@ namespace roza
m_pc++; m_pc++;
} break; } break;
case OP_LOAD_LOCAL: {
int addr = *program->param(m_pc);
auto value = m_frames.back().locals[addr];
push(program->push_value(value));
m_pc++;
} break;
case OP_STORE_LOCAL: {
auto value = program->value(pop());
int addr = *program->param(m_pc);
m_frames.back().locals[addr] = value;
m_pc++;
} break;
case OP_ASSERT: { case OP_ASSERT: {
auto value = program->value(pop()); auto value = program->value(pop());
@ -199,7 +288,7 @@ namespace roza
{ {
std::stringstream ss; std::stringstream ss;
for (size_t i=0; i<m_stack.size(); i++) for (size_t i=0; i<m_sp; i++)
{ {
ss << i << "\t"; ss << i << "\t";
@ -222,15 +311,16 @@ namespace roza
void VM::push(param_t param) void VM::push(param_t param)
{ {
m_stack.push_back(param); m_stack[m_sp] = param;
m_sp++;
} }
param_t VM::pop() param_t VM::pop()
{ {
assert(m_stack.size() > 0); assert(m_stack.size() > 0);
param_t res = m_stack.back(); param_t res = m_stack[m_sp - 1];
m_stack.pop_back(); m_sp--;
return res; return res;
} }

View File

@ -14,6 +14,15 @@ namespace roza
(std::shared_ptr<Value>, (std::shared_ptr<Value>,
std::shared_ptr<Value>)>; std::shared_ptr<Value>)>;
using var_map = std::unordered_map<int, std::shared_ptr<Value>>;
struct Frame {
var_map locals;
size_t pc;
size_t sp;
size_t bp;
};
class VM class VM
{ {
public: public:
@ -25,13 +34,17 @@ namespace roza
private: private:
StatusLog& m_log; StatusLog& m_log;
std::vector<param_t> m_stack; std::array<param_t, 4096> m_stack;
std::shared_ptr<Program> m_last_program; std::shared_ptr<Program> m_last_program;
std::unordered_map<int, std::shared_ptr<Value>> m_globals; var_map m_globals;
std::vector<Frame> m_frames;
size_t m_sp = 0;
size_t m_bp = 0;
size_t m_pc = 0; size_t m_pc = 0;
void push(param_t param); void push(param_t param);
param_t pop(); param_t pop();
void apply_binop(std::shared_ptr<Program> program, binop_t op); void apply_binop(std::shared_ptr<Program> program, binop_t op);
void apply_unop(std::shared_ptr<Program> program, unop_t op); void apply_unop(std::shared_ptr<Program> program, unop_t op);
}; };

View File

@ -1,4 +1,5 @@
#include "Value.hpp" #include "Value.hpp"
#include "Fun.hpp"
namespace roza namespace roza
{ {
@ -16,6 +17,13 @@ namespace roza
{ {
} }
/*explicit*/ Value::Value(std::shared_ptr<Fun> value, SrcLoc loc)
: m_type { value->type() }
, m_fun_val { value }
, m_loc { loc }
{
}
/*virtual*/ Value::~Value() /*virtual*/ Value::~Value()
{ {
} }
@ -32,6 +40,11 @@ namespace roza
return m_bool_val ? "true" : "false"; return m_bool_val ? "true" : "false";
} }
if (m_type->equals(TY_FUN))
{
return "function: " + m_fun_val->type()->string();
}
assert("cannot stringify unknown value " && 0); assert("cannot stringify unknown value " && 0);
} }

View File

@ -7,17 +7,21 @@
namespace roza namespace roza
{ {
class Fun;
class Value class Value
{ {
public: public:
explicit Value(int value, SrcLoc loc); explicit Value(int value, SrcLoc loc);
explicit Value(bool value, SrcLoc loc); explicit Value(bool value, SrcLoc loc);
explicit Value(std::shared_ptr<Fun> value, SrcLoc loc);
virtual ~Value(); virtual ~Value();
SrcLoc loc() const { return m_loc; } SrcLoc loc() const { return m_loc; }
int as_int() const { return m_int_val; } int as_int() const { return m_int_val; }
bool as_bool() const { return m_bool_val; } bool as_bool() const { return m_bool_val; }
std::shared_ptr<Fun> as_fun() const { return m_fun_val; }
std::shared_ptr<Type> type() const { return m_type; } std::shared_ptr<Type> type() const { return m_type; }
std::string string() const; std::string string() const;
@ -27,7 +31,10 @@ namespace roza
std::shared_ptr<Type> m_type; std::shared_ptr<Type> m_type;
int m_int_val; int m_int_val;
bool m_bool_val; bool m_bool_val;
std::shared_ptr<Fun> m_fun_val;
SrcLoc m_loc; SrcLoc m_loc;
}; };
} }

View File

@ -10,6 +10,7 @@
#include <optional> #include <optional>
#include <sstream> #include <sstream>
#include <cmath> #include <cmath>
#include <array>
#include "mutils.hpp" #include "mutils.hpp"
#endif #endif

View File

@ -9,7 +9,8 @@
G(OP_IADD), G(OP_ISUB), G(OP_IMUL), G(OP_IDIV), G(OP_IMOD), G(OP_IPOW), \ 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_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_IMP), G(OP_EQ), G(OP_ILT), G(OP_IGT), G(OP_ASSERT), G(OP_STORE_GLOBAL), \
G(OP_LOAD_GLOBAL), G(OP_BRF), G(OP_BR) G(OP_LOAD_GLOBAL), G(OP_BRF), G(OP_BR), G(OP_RET), G(OP_CALL), \
G(OP_LOAD_LOCAL), G(OP_STORE_LOCAL)
namespace roza namespace roza
{ {

View File

@ -23,6 +23,8 @@ roza_lib = static_library(
'lib/Value.cpp', 'lib/Value.cpp',
'lib/TypeResolver.cpp', 'lib/TypeResolver.cpp',
'lib/SymTable.cpp', 'lib/SymTable.cpp',
'lib/FunTy.cpp',
'lib/Fun.cpp',
] ]
) )

6
roza_tests/test_fun.roza Normal file
View File

@ -0,0 +1,6 @@
let a = fun (x as int, y as int) -> int
return x + y
end
assert 12 == {a 9 3}
assert 6 == {a {a 1 2} 3}

View File

@ -1,6 +1,7 @@
#include <iostream> #include <iostream>
#include "Args.hpp" #include "Args.hpp"
#include "Loader.hpp" #include "Loader.hpp"
#include "../lib/SymTable.hpp"
#include "../lib/Lexer.hpp" #include "../lib/Lexer.hpp"
#include "../lib/Parser.hpp" #include "../lib/Parser.hpp"
#include "../lib/StaticPass.hpp" #include "../lib/StaticPass.hpp"
@ -20,6 +21,7 @@ int main(int argc, char** argv)
std::cerr << "\t--ast, show the AST" << std::endl; std::cerr << "\t--ast, show the AST" << std::endl;
std::cerr << "\t--code, show the bytecode" << std::endl; std::cerr << "\t--code, show the bytecode" << std::endl;
std::cerr << "\t--stack, show the call stack" << std::endl; std::cerr << "\t--stack, show the call stack" << std::endl;
std::cerr << "\t--throws, throws exception on error" << std::endl;
return 0; return 0;
} }
@ -33,59 +35,70 @@ int main(int argc, char** argv)
if (args.inputs().size() > 0) if (args.inputs().size() > 0)
{ {
try auto do_stuff = [&](){
auto source = loader.load(args.inputs()[0]);
roza::SrcLoc loc {args.inputs()[0]};
roza::StatusLog log;
auto sym = std::make_shared<roza::SymTable>();
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 << "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, sym);
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;
};
if (args.get("--throws"))
{ {
auto source = loader.load(args.inputs()[0]); do_stuff();
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 << "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;
} }
catch(std::exception const& err) else
{ {
std::cerr << err.what() << std::endl; try
return -1; {
do_stuff();
}
catch(std::exception const& err)
{
std::cerr << err.what() << std::endl;
return -1;
}
} }
return 0; return 0;

View File

@ -18,10 +18,11 @@ public:
roza::Lexer lexer {log, loc}; roza::Lexer lexer {log, loc};
roza::Parser parser {lexer, log}; roza::Parser parser {lexer, log};
roza::Compiler compiler {log}; roza::Compiler compiler {log};
auto sym = std::make_shared<roza::SymTable>();
lexer.scan(source); lexer.scan(source);
auto node = parser.parse(); auto node = parser.parse();
return compiler.compile(node); return compiler.compile(node, sym);
} }
void compile_err(std::string const& source) void compile_err(std::string const& source)

View File

@ -112,3 +112,28 @@ TEST_CASE_METHOD(LexerTest, "Lexer_if")
REQUIRE("END" == get_str(2)); REQUIRE("END" == get_str(2));
REQUIRE("" == get_str(3)); REQUIRE("" == get_str(3));
} }
TEST_CASE_METHOD(LexerTest, "Lexer_types")
{
m_lexer.scan(" int bool ");
REQUIRE("TYPE[int]" == get_str(0));
REQUIRE("TYPE[bool]" == get_str(1));
REQUIRE("" == get_str(2));
}
TEST_CASE_METHOD(LexerTest, "Lexer_fun")
{
m_lexer.scan(" fun as -> ");
REQUIRE("FUN" == get_str(0));
REQUIRE("AS" == get_str(1));
REQUIRE("ARROW" == get_str(2));
REQUIRE("" == get_str(3));
}
TEST_CASE_METHOD(LexerTest, "Lexer_call")
{
m_lexer.scan(" {} ");
REQUIRE("OBRACE" == get_str(0));
REQUIRE("CBRACE" == get_str(1));
REQUIRE("" == get_str(2));
}

View File

@ -137,3 +137,45 @@ TEST_CASE_METHOD(ParserTest, "Parser_if")
"ELSE(INT[3])))))", "ELSE(INT[3])))))",
"if true 0; else if false 1; else if true 2; else 3; end"); "if true 0; else if false 1; else if true 2; else 3; end");
} }
TEST_CASE_METHOD(ParserTest, "Parser_fun")
{
test_node("PROG(FUN(PARAMS,RET,BODY))",
"fun () end");
test_node("PROG(FUN(PARAMS("
"PARAM(IDENT[x],TYPE[int]),"
"PARAM(IDENT[y],TYPE[bool])"
"),RET,BODY))",
"fun (x as int, y as bool) end");
test_node("PROG(FUN(PARAMS,RET,BODY(INT[0],INT[1],INT[2])))",
"fun () 0; 1; 2; end");
test_node("PROG(FUN(PARAMS,RET(TYPE[int]),BODY(INT[0],INT[1],INT[2])))",
"fun () -> int 0; 1; 2; end");
}
TEST_CASE_METHOD(ParserTest, "Parser_type")
{
test_node("PROG(FUN(PARAMS,RET(TYPE[int]),BODY))",
"fun () -> int end");
test_node("PROG(FUN(PARAMS,RET(TYPE[nil]),BODY))",
"fun () -> nil end");
test_node("PROG(FUN(PARAMS,RET(TYPE[fun](TYPE[int],TYPE[bool])),BODY))",
"fun () -> fun<int -> bool> end");
test_node("PROG(FUN(PARAMS,RET(TYPE[fun](TYPE[int],TYPE[fun]("
"TYPE[bool],TYPE[bool]"
"))),BODY))",
"fun () -> fun< int -> fun<bool -> bool> > end");
}
TEST_CASE_METHOD(ParserTest, "Parser_call")
{
test_node("PROG(CALL(IDENT[f],ARGS(IDENT[a],IDENT[b],IDENT[c])))",
"{f a b c}");
}