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
| assert EXPR
| assert_static_fail INSTR
| return EXPR
| VARDECL
| CONSTDECL
| ASSIGN
@ -30,4 +31,14 @@ FACTOR ::= UNOP ((mul | div | mod) UNOP)*
UNOP ::= (add | sub | not)? POW
POW ::= GROUP (pow GROUP)?
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 "lib/Node.hpp"
#include "lib/TypeResolver.hpp"
#include "lib/opcodes.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>();
compile_node(root, program);
compile_node(root, program, sym);
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())
{
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: {
m_sym.enter_scope();
sym->enter_scope();
std::vector<size_t> to_end;
@ -36,11 +81,11 @@ namespace roza
auto cond = n->child(0);
auto then = n->child(1);
compile_node(cond, prog);
compile_node(cond, prog, sym);
size_t cond_addr = prog->size();
prog->push_instr(OP_BRF, 0);
compile_node(then, prog);
compile_node(then, prog, sym);
to_end.push_back(prog->size());
prog->push_instr(OP_BR, 0);
@ -50,7 +95,7 @@ namespace roza
if (n->size() > 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());
}
m_sym.leave_scope();
sym->leave_scope();
} break;
case NODE_BODY:
case NODE_ELSE:
case NODE_THEN: {
compile_children(root, prog);
compile_children(root, prog, sym);
} break;
case NODE_ASSERT: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_ASSERT);
} break;
@ -80,10 +126,10 @@ namespace roza
try
{
StaticPass static_pass {m_log, m_sym};
StaticPass static_pass {m_log, *sym};
static_pass.check_children(root);
compile_children(root, prog);
compile_children(root, prog, sym);
failed = true;
}
@ -99,30 +145,64 @@ namespace roza
} break;
case NODE_IDENT: {
SymEntry const& entry = m_sym.find(root->repr());
prog->push_instr(OP_LOAD_GLOBAL, entry.addr);
SymEntry const& entry = sym->find(root->repr());
if (m_fun_stack.empty())
{
prog->push_instr(OP_LOAD_GLOBAL, entry.addr);
}
else
{
prog->push_instr(OP_LOAD_LOCAL, 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);
compile_node(root->child(1), prog, sym);
int addr = sym->declare_mut(name, root->child(1));
if (m_fun_stack.empty())
{
prog->push_instr(OP_STORE_GLOBAL, addr);
}
else
{
prog->push_instr(OP_STORE_LOCAL, 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);
compile_node(root->child(1), prog, sym);
int addr = sym->declare(name, root->child(1));
if (m_fun_stack.empty())
{
prog->push_instr(OP_STORE_GLOBAL, addr);
}
else
{
prog->push_instr(OP_STORE_LOCAL, 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);
int addr = sym->find(root->child(0)->repr()).addr;
compile_node(root->child(1), prog, sym);
if (m_fun_stack.empty())
{
prog->push_instr(OP_STORE_GLOBAL, addr);
}
else
{
prog->push_instr(OP_STORE_LOCAL, addr);
}
} break;
case NODE_INT: {
@ -136,100 +216,100 @@ namespace roza
} break;
case NODE_EQ: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_EQ);
} break;
case NODE_NE: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_EQ);
prog->push_instr(OP_NOT);
} break;
case NODE_LT: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_ILT);
} break;
case NODE_GT: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IGT);
} break;
case NODE_LE: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IGT);
prog->push_instr(OP_NOT);
} break;
case NODE_GE: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_ILT);
prog->push_instr(OP_NOT);
} break;
case NODE_IMP: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IMP);
} break;
case NODE_AND: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_AND);
} break;
case NODE_OR: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_OR);
} break;
case NODE_NOT: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_NOT);
} break;
case NODE_ADD: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IADD);
} break;
case NODE_SUB: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_ISUB);
} break;
case NODE_MUL: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IMUL);
} break;
case NODE_DIV: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IDIV);
} break;
case NODE_MOD: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IMOD);
} break;
case NODE_POW: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IPOW);
} break;
case NODE_UADD: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IUADD);
} break;
case NODE_USUB: {
compile_children(root, prog);
compile_children(root, prog, sym);
prog->push_instr(OP_IUSUB);
} break;
case NODE_PROG: {
compile_children(root, prog);
compile_children(root, prog, sym);
} break;
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++)
{
compile_node(root->child(i), prog);
compile_node(root->child(i), prog, sym);
}
}
}

View File

@ -7,6 +7,7 @@
#include "opcodes.hpp"
#include "StatusLog.hpp"
#include "SymTable.hpp"
#include "Fun.hpp"
namespace roza
{
@ -16,13 +17,20 @@ namespace roza
explicit Compiler(StatusLog& log);
virtual ~Compiler();
std::shared_ptr<Program> compile(std::shared_ptr<Node> root);
void compile_node(std::shared_ptr<Node> root, std::shared_ptr<Program> prog);
void compile_children(std::shared_ptr<Node> root, std::shared_ptr<Program> prog);
std::shared_ptr<Program> compile(std::shared_ptr<Node> root,
std::shared_ptr<SymTable> sym);
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:
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 }
{
std::vector<std::tuple<std::string, NodeType, bool>> texts = {
{"->", NODE_ARROW, false},
{";", NODE_EOI, false},
{",", NODE_COMMA, false},
{"==", NODE_EQ, false},
{"!=", NODE_NE, false},
{"<=", NODE_LE, false},
@ -24,10 +26,18 @@ namespace roza
{"^", NODE_POW, false},
{"(", NODE_OPAR, false},
{")", NODE_CPAR, false},
{"{", NODE_OBRACE, false},
{"}", NODE_CBRACE, false},
{"=", NODE_ASSIGN, false},
};
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},
{"else", NODE_ELSE, false},
{"end", NODE_END, false},

View File

@ -14,7 +14,10 @@
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_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
{

View File

@ -141,6 +141,13 @@ namespace roza
root->add_child(parse_instr());
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))
{
auto root = parse_constdecl();
@ -453,6 +460,16 @@ namespace roza
return consume();
}
if (type_is(NODE_FUN))
{
return parse_fun();
}
if (type_is(NODE_OBRACE))
{
return parse_call();
}
m_log.fatal(node()->loc(),
"cannot parse unknown node '"
+ node()->string()
@ -461,9 +478,133 @@ namespace roza
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()
{
auto root = consume(NODE_INT);
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_group();
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_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())
{
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: {
m_sym.enter_scope();
auto cond_type = resolver.find(root->child(0), m_sym);
@ -34,6 +94,7 @@ namespace roza
m_sym.leave_scope();
} break;
case NODE_ARGS:
case NODE_THEN:
case NODE_ELSE: {
check_children(root);

View File

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

View File

@ -2,8 +2,6 @@
namespace roza
{
/*static*/ int SymTable::addr = 0;
/*explicit*/ SymTable::SymTable()
{
}
@ -48,13 +46,13 @@ namespace roza
m_entries.push_back(SymEntry {
name,
SymTable::addr++,
m_addr++,
m_scope,
node,
false
});
return SymTable::addr - 1;
return m_addr - 1;
}
int SymTable::declare_mut(std::string const& name, std::shared_ptr<Node> node)
@ -137,4 +135,16 @@ namespace roza
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_in_scope(std::string const& name) const;
std::string string() const;
private:
static int addr;
int m_addr = 0;
std::vector<SymEntry> m_entries;
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])
.substr(std::string("TY_").size());
}
bool Type::equals(BaseType rhs) const
/*virtual*/ bool Type::equals(BaseType rhs) const
{
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;
}

View File

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

View File

@ -1,4 +1,5 @@
#include "TypeResolver.hpp"
#include "FunTy.hpp"
namespace roza
{
@ -20,6 +21,61 @@ namespace roza
return find(root->child(root->size() - 1), sym);
} 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: {
std::string name = root->repr();
SymEntry const& entry = sym.find(name);

View File

@ -1,10 +1,12 @@
#include "VM.hpp"
#include "lib/opcodes.hpp"
#include "Fun.hpp"
namespace roza
{
/*explicit*/ VM::VM(StatusLog& log)
: m_log { log }
, m_sp { 0 }
{
}
@ -20,6 +22,76 @@ namespace roza
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: {
auto value = program->value(pop());
@ -54,6 +126,23 @@ namespace roza
m_pc++;
} 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: {
auto value = program->value(pop());
@ -199,7 +288,7 @@ namespace roza
{
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";
@ -222,15 +311,16 @@ namespace roza
void VM::push(param_t param)
{
m_stack.push_back(param);
m_stack[m_sp] = param;
m_sp++;
}
param_t VM::pop()
{
assert(m_stack.size() > 0);
param_t res = m_stack.back();
m_stack.pop_back();
param_t res = m_stack[m_sp - 1];
m_sp--;
return res;
}

View File

@ -14,6 +14,15 @@ namespace roza
(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
{
public:
@ -25,13 +34,17 @@ namespace roza
private:
StatusLog& m_log;
std::vector<param_t> m_stack;
std::array<param_t, 4096> m_stack;
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;
void push(param_t param);
param_t pop();
void apply_binop(std::shared_ptr<Program> program, binop_t op);
void apply_unop(std::shared_ptr<Program> program, unop_t op);
};

View File

@ -1,4 +1,5 @@
#include "Value.hpp"
#include "Fun.hpp"
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()
{
}
@ -32,6 +40,11 @@ namespace roza
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);
}

View File

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

View File

@ -10,6 +10,7 @@
#include <optional>
#include <sstream>
#include <cmath>
#include <array>
#include "mutils.hpp"
#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_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_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
{

View File

@ -23,6 +23,8 @@ roza_lib = static_library(
'lib/Value.cpp',
'lib/TypeResolver.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 "Args.hpp"
#include "Loader.hpp"
#include "../lib/SymTable.hpp"
#include "../lib/Lexer.hpp"
#include "../lib/Parser.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--code, show the bytecode" << std::endl;
std::cerr << "\t--stack, show the call stack" << std::endl;
std::cerr << "\t--throws, throws exception on error" << std::endl;
return 0;
}
@ -33,59 +35,70 @@ int main(int argc, char** argv)
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]);
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;
do_stuff();
}
catch(std::exception const& err)
else
{
std::cerr << err.what() << std::endl;
return -1;
try
{
do_stuff();
}
catch(std::exception const& err)
{
std::cerr << err.what() << std::endl;
return -1;
}
}
return 0;

View File

@ -18,10 +18,11 @@ public:
roza::Lexer lexer {log, loc};
roza::Parser parser {lexer, log};
roza::Compiler compiler {log};
auto sym = std::make_shared<roza::SymTable>();
lexer.scan(source);
auto node = parser.parse();
return compiler.compile(node);
return compiler.compile(node, sym);
}
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("" == 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])))))",
"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}");
}