diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 1d8b4f4..8593054 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -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 diff --git a/lib/Compiler.cpp b/lib/Compiler.cpp index 624a8e9..76715fe 100644 --- a/lib/Compiler.cpp +++ b/lib/Compiler.cpp @@ -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 Compiler::compile(std::shared_ptr root) + std::shared_ptr Compiler::compile(std::shared_ptr root, + std::shared_ptr sym) { auto program = std::make_shared(); - compile_node(root, program); + compile_node(root, program, sym); return program; } - void Compiler::compile_node(std::shared_ptr root, std::shared_ptr prog) + void Compiler::compile_node(std::shared_ptr root, + std::shared_ptr prog, + std::shared_ptr 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(ty); + + for (size_t i=0; isize(); 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(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 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 root, std::shared_ptr prog) + void Compiler::compile_children(std::shared_ptr root, + std::shared_ptr prog, + std::shared_ptr sym) { for (size_t i=0; isize(); i++) { - compile_node(root->child(i), prog); + compile_node(root->child(i), prog, sym); } } } diff --git a/lib/Compiler.hpp b/lib/Compiler.hpp index 89bec7a..2e54935 100644 --- a/lib/Compiler.hpp +++ b/lib/Compiler.hpp @@ -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 compile(std::shared_ptr root); - void compile_node(std::shared_ptr root, std::shared_ptr prog); - void compile_children(std::shared_ptr root, std::shared_ptr prog); + std::shared_ptr compile(std::shared_ptr root, + std::shared_ptr sym); + + void compile_node(std::shared_ptr root, + std::shared_ptr prog, + std::shared_ptr sym); + + void compile_children(std::shared_ptr root, + std::shared_ptr prog, + std::shared_ptr sym); private: StatusLog& m_log; - SymTable m_sym; + std::vector> m_fun_stack; }; } diff --git a/lib/Fun.cpp b/lib/Fun.cpp new file mode 100644 index 0000000..16197e0 --- /dev/null +++ b/lib/Fun.cpp @@ -0,0 +1,13 @@ +#include "Fun.hpp" + +namespace roza +{ + /*explicit*/ Fun::Fun(std::shared_ptr type) + : m_type { type } + { + } + + /*virtual*/ Fun::~Fun() + { + } +} diff --git a/lib/Fun.hpp b/lib/Fun.hpp new file mode 100644 index 0000000..69c240e --- /dev/null +++ b/lib/Fun.hpp @@ -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); + virtual ~Fun(); + + std::shared_ptr type() const { return m_type; } + std::shared_ptr sym() const { return m_sym; } + std::shared_ptr program() const { return m_program; } + + private: + std::shared_ptr m_type; + std::shared_ptr m_program = std::make_shared(); + std::shared_ptr m_sym = std::make_shared(); + }; +} + +#endif diff --git a/lib/FunTy.cpp b/lib/FunTy.cpp new file mode 100644 index 0000000..5c60ae2 --- /dev/null +++ b/lib/FunTy.cpp @@ -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 ty) + { + m_inputs.push_back(ty); + } + + void FunTy::set_output(std::shared_ptr 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; + } + +} diff --git a/lib/FunTy.hpp b/lib/FunTy.hpp new file mode 100644 index 0000000..5fc8ad3 --- /dev/null +++ b/lib/FunTy.hpp @@ -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 get_output() const { return m_output; } + + void add_input(std::shared_ptr ty); + void set_output(std::shared_ptr ty); + + std::string string() const override; + bool equals(BaseType rhs) const override; + bool equals(Type const& rhs) const override; + + private: + std::vector> m_inputs; + std::shared_ptr m_output = std::make_shared(TY_NIL); + }; +} + +#endif diff --git a/lib/Lexer.cpp b/lib/Lexer.cpp index f831f7b..f2839a0 100644 --- a/lib/Lexer.cpp +++ b/lib/Lexer.cpp @@ -8,7 +8,9 @@ namespace roza , m_loc { loc } { std::vector> 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> 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}, diff --git a/lib/Node.hpp b/lib/Node.hpp index d341802..6a114cd 100644 --- a/lib/Node.hpp +++ b/lib/Node.hpp @@ -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 { diff --git a/lib/Parser.cpp b/lib/Parser.cpp index 483b6a5..262b57e 100644 --- a/lib/Parser.cpp +++ b/lib/Parser.cpp @@ -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 Parser::parse_call() + { + auto root = std::make_shared(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 Parser::parse_args() + { + auto root = std::make_shared(NODE_ARGS, "", node()->loc()); + + while (!type_is(NODE_CBRACE)) + { + root->add_child(parse_expr()); + } + + return root; + } + std::shared_ptr Parser::parse_int() { auto root = consume(NODE_INT); return root; } + + std::shared_ptr 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 Parser::parse_params() + { + auto root = std::make_shared(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 Parser::parse_param() + { + auto root = std::make_shared(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 Parser::parse_ret() + { + auto root = std::make_shared(NODE_RET, "", m_lexer.loc()); + + if (type_is(NODE_ARROW)) + { + consume(); + root->add_child(parse_type()); + } + + return root; + } + + std::shared_ptr Parser::parse_body() + { + auto root = std::make_shared(NODE_BODY, "", m_lexer.loc()); + + while (!type_is(NODE_END)) + { + root->add_child(parse_instr()); + } + + return root; + } + + std::shared_ptr Parser::parse_type() + { + if (type_is(NODE_FUN)) + { + auto root = std::make_shared(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); + } } diff --git a/lib/Parser.hpp b/lib/Parser.hpp index 07e27e2..9f71329 100644 --- a/lib/Parser.hpp +++ b/lib/Parser.hpp @@ -52,7 +52,15 @@ namespace roza std::shared_ptr parse_pow(); std::shared_ptr parse_group(); std::shared_ptr parse_base(); + std::shared_ptr parse_call(); + std::shared_ptr parse_args(); std::shared_ptr parse_int(); + std::shared_ptr parse_fun(); + std::shared_ptr parse_params(); + std::shared_ptr parse_param(); + std::shared_ptr parse_ret(); + std::shared_ptr parse_body(); + std::shared_ptr parse_type(); }; } diff --git a/lib/StaticPass.cpp b/lib/StaticPass.cpp index 2845531..100d090 100644 --- a/lib/StaticPass.cpp +++ b/lib/StaticPass.cpp @@ -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; isize(); 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; isize(); 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); diff --git a/lib/StaticPass.hpp b/lib/StaticPass.hpp index 3c38104..641503d 100644 --- a/lib/StaticPass.hpp +++ b/lib/StaticPass.hpp @@ -22,6 +22,7 @@ namespace roza private: StatusLog& m_log; SymTable m_sym; + std::shared_ptr m_outer_fun_ret; void check_types(std::shared_ptr root, std::shared_ptr lhs, diff --git a/lib/SymTable.cpp b/lib/SymTable.cpp index 15e5530..2663d5e 100644 --- a/lib/SymTable.cpp +++ b/lib/SymTable.cpp @@ -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) @@ -137,4 +135,16 @@ namespace roza return false; } + + std::string SymTable::string() const + { + std::stringstream ss; + + for (size_t i=0; i m_entries; int m_scope = 0; }; diff --git a/lib/Type.cpp b/lib/Type.cpp index 3b6325d..3e27278 100644 --- a/lib/Type.cpp +++ b/lib/Type.cpp @@ -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; } diff --git a/lib/Type.hpp b/lib/Type.hpp index efbbb6d..b7e8a12 100644 --- a/lib/Type.hpp +++ b/lib/Type.hpp @@ -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; diff --git a/lib/TypeResolver.cpp b/lib/TypeResolver.cpp index d80ed5d..8b16898 100644 --- a/lib/TypeResolver.cpp +++ b/lib/TypeResolver.cpp @@ -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(); + + for (size_t i=0; isize(); 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(TY_NIL); } + if (root->repr() == "int") { return std::make_shared(TY_INT); } + if (root->repr() == "bool") { return std::make_shared(TY_BOOL); } + + if (root->repr() == "fun") + { + auto ty = std::make_shared(); + + if (root->size() > 0) + { + for (size_t i=0; isize() - 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); diff --git a/lib/VM.cpp b/lib/VM.cpp index bff580c..a17417a 100644 --- a/lib/VM.cpp +++ b/lib/VM.cpp @@ -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 args; + + for (size_t i=0; ivalue(pop())->as_fun(); + + var_map vars; + + for (size_t i=0; ihas_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 rets; + + for (size_t i=0; i(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 0); - param_t res = m_stack.back(); - m_stack.pop_back(); + param_t res = m_stack[m_sp - 1]; + m_sp--; return res; } diff --git a/lib/VM.hpp b/lib/VM.hpp index 334d4ef..1bae82d 100644 --- a/lib/VM.hpp +++ b/lib/VM.hpp @@ -14,6 +14,15 @@ namespace roza (std::shared_ptr, std::shared_ptr)>; + using var_map = std::unordered_map>; + + 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 m_stack; + std::array m_stack; std::shared_ptr m_last_program; - std::unordered_map> m_globals; + var_map m_globals; + std::vector 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, binop_t op); void apply_unop(std::shared_ptr program, unop_t op); }; diff --git a/lib/Value.cpp b/lib/Value.cpp index b9ca749..46a2f98 100644 --- a/lib/Value.cpp +++ b/lib/Value.cpp @@ -1,4 +1,5 @@ #include "Value.hpp" +#include "Fun.hpp" namespace roza { @@ -16,6 +17,13 @@ namespace roza { } + /*explicit*/ Value::Value(std::shared_ptr 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); } diff --git a/lib/Value.hpp b/lib/Value.hpp index 3d4d786..916051e 100644 --- a/lib/Value.hpp +++ b/lib/Value.hpp @@ -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 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 as_fun() const { return m_fun_val; } std::shared_ptr type() const { return m_type; } std::string string() const; @@ -27,7 +31,10 @@ namespace roza std::shared_ptr m_type; int m_int_val; bool m_bool_val; + std::shared_ptr m_fun_val; + SrcLoc m_loc; + }; } diff --git a/lib/commons.hpp b/lib/commons.hpp index 91d5a75..bf0a759 100644 --- a/lib/commons.hpp +++ b/lib/commons.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "mutils.hpp" #endif diff --git a/lib/opcodes.hpp b/lib/opcodes.hpp index ae97cc1..fbbc775 100644 --- a/lib/opcodes.hpp +++ b/lib/opcodes.hpp @@ -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 { diff --git a/meson.build b/meson.build index aaa641a..cdb2046 100644 --- a/meson.build +++ b/meson.build @@ -23,6 +23,8 @@ roza_lib = static_library( 'lib/Value.cpp', 'lib/TypeResolver.cpp', 'lib/SymTable.cpp', + 'lib/FunTy.cpp', + 'lib/Fun.cpp', ] ) diff --git a/roza_tests/test_fun.roza b/roza_tests/test_fun.roza new file mode 100644 index 0000000..737c83b --- /dev/null +++ b/roza_tests/test_fun.roza @@ -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} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 89ba862..85bb5ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include #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(); + auto lexer = std::make_shared(log, loc); + auto parser = std::make_shared(*lexer, log); + auto static_pass = std::make_shared(log); + auto compiler = std::make_shared(log); + auto vm = std::make_shared(log); + + lexer->scan(source); + + if (args.get("--tokens")) + { + std::cout << "Tokens:" << std::endl; + for (size_t i=0; isize(); 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(log, loc); - auto parser = std::make_shared(*lexer, log); - auto static_pass = std::make_shared(log); - auto compiler = std::make_shared(log); - auto vm = std::make_shared(log); - - lexer->scan(source); - - if (args.get("--tokens")) - { - std::cout << "Tokens:" << std::endl; - for (size_t i=0; isize(); 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; diff --git a/tests/Compiler.cpp b/tests/Compiler.cpp index 96d1bc0..45e1a93 100644 --- a/tests/Compiler.cpp +++ b/tests/Compiler.cpp @@ -18,10 +18,11 @@ public: roza::Lexer lexer {log, loc}; roza::Parser parser {lexer, log}; roza::Compiler compiler {log}; + auto sym = std::make_shared(); lexer.scan(source); auto node = parser.parse(); - return compiler.compile(node); + return compiler.compile(node, sym); } void compile_err(std::string const& source) diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index af81f4f..718116f 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -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)); +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index abb8d82..ded67be 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -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 bool> end"); + + test_node("PROG(FUN(PARAMS,RET(TYPE[fun](TYPE[int],TYPE[fun](" + "TYPE[bool],TYPE[bool]" + "))),BODY))", + "fun () -> fun< int -> fun 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}"); +}