Compare commits
2 Commits
3eefed351c
...
12cc10720e
Author | SHA1 | Date |
---|---|---|
bog | 12cc10720e | |
bog | 14e693c946 |
|
@ -3,4 +3,8 @@ EXPR ::=
|
||||||
| int | float | bool | string
|
| int | float | bool | string
|
||||||
| ident
|
| ident
|
||||||
| CALL
|
| CALL
|
||||||
CALL opar ident EXPR* cpar
|
| LAMBDA
|
||||||
|
CALL ::= opar EXPR EXPR* cpar
|
||||||
|
LAMBDA ::= opar rarrow opar PARAMS cpar BODY cpar
|
||||||
|
PARAMS ::= ident*
|
||||||
|
BODY ::= EXPR*
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
($ a 2)
|
||||||
|
|
||||||
|
(:
|
||||||
|
(assert= 2 a)
|
||||||
|
($ a 3)
|
||||||
|
(assert= 3 a)
|
||||||
|
|
||||||
|
(:
|
||||||
|
($ a 7)
|
||||||
|
(assert= 7 a)
|
||||||
|
(assert-static-fail ($ a 2))
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert= 3 a)
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert= 2 a)
|
||||||
|
|
||||||
|
($ b (: 2 6 9))
|
||||||
|
|
||||||
|
(assert= 9 b)
|
||||||
|
|
||||||
|
($ c (: 1 2 (: 3 4 (: 5 6 7))))
|
||||||
|
(assert= 7 c)
|
|
@ -0,0 +1,33 @@
|
||||||
|
;; CALL LAMBDA
|
||||||
|
;; ===========
|
||||||
|
|
||||||
|
(assert= 2 (
|
||||||
|
(-> (x y) x)
|
||||||
|
2 3
|
||||||
|
))
|
||||||
|
|
||||||
|
(assert= 3 (
|
||||||
|
(-> (x y) y)
|
||||||
|
2 3
|
||||||
|
))
|
||||||
|
|
||||||
|
(assert= 7 (
|
||||||
|
(-> (x)
|
||||||
|
($ a 39)
|
||||||
|
x)
|
||||||
|
7))
|
||||||
|
|
||||||
|
;; CALL IDENT
|
||||||
|
;; ==========
|
||||||
|
|
||||||
|
($ a (-> (x y) y))
|
||||||
|
|
||||||
|
(assert= 9 (a 3 9))
|
||||||
|
|
||||||
|
;; RETURN LAMBDA
|
||||||
|
;; =============
|
||||||
|
|
||||||
|
($ b (-> () (-> () 32)))
|
||||||
|
(assert= 32 ((b)))
|
||||||
|
|
||||||
|
(assert-static-fail ((b 7)))
|
|
@ -0,0 +1,10 @@
|
||||||
|
(assert-static-fail a)
|
||||||
|
($ a 12)
|
||||||
|
(assert-static-fail ($ a 3))
|
||||||
|
|
||||||
|
($ b 13)
|
||||||
|
($ c a)
|
||||||
|
|
||||||
|
(assert= 12 a)
|
||||||
|
(assert= 13 b)
|
||||||
|
(assert= 12 c)
|
|
@ -8,4 +8,6 @@ extern "C" void lib(Module& mod)
|
||||||
mod.register_function("println", fkstd::println);
|
mod.register_function("println", fkstd::println);
|
||||||
|
|
||||||
mod.register_macro("assert-static-fail", fkstd::assert_static_fail);
|
mod.register_macro("assert-static-fail", fkstd::assert_static_fail);
|
||||||
|
mod.register_macro(":", fkstd::block);
|
||||||
|
mod.register_macro("$", fkstd::decl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
|
#include "src/Compiler.hpp"
|
||||||
|
|
||||||
namespace fkstd
|
namespace fkstd
|
||||||
{
|
{
|
||||||
|
@ -22,4 +23,43 @@ namespace fkstd
|
||||||
ss << "assertion failed";
|
ss << "assertion failed";
|
||||||
node->loc().error<assert_error>(LOG_ERROR, ss.str());
|
node->loc().error<assert_error>(LOG_ERROR, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void decl(Compiler& compiler,
|
||||||
|
std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> program)
|
||||||
|
{
|
||||||
|
static addr_t addr = 0;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
std::string ident = node->child(1)->repr();
|
||||||
|
auto rhs = node->child(2);
|
||||||
|
|
||||||
|
compiler.compile_prog(rhs, program);
|
||||||
|
|
||||||
|
auto entry = compiler.sym()->declare_local(ident,
|
||||||
|
addr,
|
||||||
|
node->loc())
|
||||||
|
.set_node(rhs);
|
||||||
|
|
||||||
|
program->add(OP_STORE_LOCAL, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block(Compiler& compiler,
|
||||||
|
std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> program)
|
||||||
|
{
|
||||||
|
compiler.sym()->enter_scope();
|
||||||
|
|
||||||
|
for (size_t i=1; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compiler.compile_prog(node->child(i), program);
|
||||||
|
|
||||||
|
if (i != node->size() - 1)
|
||||||
|
{
|
||||||
|
program->add(OP_POP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler.sym()->leave_scope();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,14 @@ namespace fkstd
|
||||||
void assert_static_fail(Compiler& compiler,
|
void assert_static_fail(Compiler& compiler,
|
||||||
std::shared_ptr<Node> node,
|
std::shared_ptr<Node> node,
|
||||||
std::shared_ptr<Program> program);
|
std::shared_ptr<Program> program);
|
||||||
|
|
||||||
|
void decl(Compiler& compiler,
|
||||||
|
std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> program);
|
||||||
|
|
||||||
|
void block(Compiler& compiler,
|
||||||
|
std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> program);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,6 +29,7 @@ fakir_cpp = [
|
||||||
'src/Parser.cpp',
|
'src/Parser.cpp',
|
||||||
'src/Constant.cpp',
|
'src/Constant.cpp',
|
||||||
'src/NativeFunction.cpp',
|
'src/NativeFunction.cpp',
|
||||||
|
'src/Lambda.cpp',
|
||||||
'src/NativeMacro.cpp',
|
'src/NativeMacro.cpp',
|
||||||
'src/SymTable.cpp',
|
'src/SymTable.cpp',
|
||||||
'src/SymEntry.cpp',
|
'src/SymEntry.cpp',
|
||||||
|
@ -53,6 +54,7 @@ fakir_hpp = [
|
||||||
'src/Program.hpp',
|
'src/Program.hpp',
|
||||||
'src/Module.hpp',
|
'src/Module.hpp',
|
||||||
'src/NativeFunction.hpp',
|
'src/NativeFunction.hpp',
|
||||||
|
'src/Lambda.hpp',
|
||||||
'src/NativeMacro.hpp',
|
'src/NativeMacro.hpp',
|
||||||
'src/opcodes.hpp',
|
'src/opcodes.hpp',
|
||||||
'src/commons.hpp',
|
'src/commons.hpp',
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Compiler.hpp"
|
#include "Compiler.hpp"
|
||||||
|
#include "Lambda.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
@ -37,6 +38,48 @@ namespace fk
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_BODY: {
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile_prog(node->child(i), prog);
|
||||||
|
|
||||||
|
if (i != node->size() - 1)
|
||||||
|
{
|
||||||
|
prog->add(OP_POP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_LAMBDA: {
|
||||||
|
auto params = node->child(0);
|
||||||
|
auto body = node->child(1);
|
||||||
|
|
||||||
|
m_sym->enter_scope(node);
|
||||||
|
|
||||||
|
for (size_t i=0; i<params->size(); i++)
|
||||||
|
{
|
||||||
|
std::string ident = params->child(i)->repr();
|
||||||
|
m_sym->declare_local(ident, i, node->loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
Compiler compiler {m_sym};
|
||||||
|
for (auto e: m_macros)
|
||||||
|
{
|
||||||
|
compiler.add_macro(e.first, e.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto program = compiler.compile(body);
|
||||||
|
program->add(OP_RET);
|
||||||
|
|
||||||
|
m_sym->leave_scope();
|
||||||
|
|
||||||
|
auto constant = std::make_shared<Constant>(TYPE_PROGRAM,
|
||||||
|
program,
|
||||||
|
node->loc());
|
||||||
|
prog->load_const(constant);
|
||||||
|
prog->add(OP_MAKE_FUNCTION, params->size());
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_CALL: {
|
case NODE_CALL: {
|
||||||
std::string ident = node->child(0)->repr();
|
std::string ident = node->child(0)->repr();
|
||||||
|
|
||||||
|
@ -53,7 +96,37 @@ namespace fk
|
||||||
compile_prog(node->child(i), prog);
|
compile_prog(node->child(i), prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
prog->add(OP_CALL_NATIVE, node->size() - 1);
|
if (node->child(0)->type() == NODE_LAMBDA
|
||||||
|
|| node->child(0)->type() == NODE_CALL)
|
||||||
|
{
|
||||||
|
prog->add(OP_CALL_REF, node->size() - 1);
|
||||||
|
}
|
||||||
|
else if (node->child(0)->type() == NODE_IDENT)
|
||||||
|
{
|
||||||
|
if (auto entry = m_sym->find(node->child(0)->repr());
|
||||||
|
entry && entry->is_global() == false)
|
||||||
|
{
|
||||||
|
size_t arity = entry->node()->child(0)->size();
|
||||||
|
if (arity != node->size() - 1)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "arity mismatch: " << node->child(0)->repr()
|
||||||
|
<< " expect '"
|
||||||
|
<< arity
|
||||||
|
<< "' arguments, got '"
|
||||||
|
<< node->size() - 1
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
node->loc().error<compile_error>(LOG_ERROR, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
prog->add(OP_CALL_REF, node->size() - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prog->add(OP_CALL_NATIVE, node->size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -72,10 +145,12 @@ namespace fk
|
||||||
|
|
||||||
if (!entry->is_global())
|
if (!entry->is_global())
|
||||||
{
|
{
|
||||||
throw std::runtime_error { "not implemented yet" };
|
prog->add(OP_LOAD_LOCAL, entry->addr());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prog->add(OP_LOAD_GLOBAL, entry->addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
prog->add(OP_LOAD_GLOBAL, entry->addr());
|
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -114,5 +189,4 @@ namespace fk
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,18 @@ namespace fk
|
||||||
{
|
{
|
||||||
FK_ERROR(compile_error);
|
FK_ERROR(compile_error);
|
||||||
|
|
||||||
|
struct DeclContext {
|
||||||
|
size_t arity;
|
||||||
|
};
|
||||||
|
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Compiler(std::shared_ptr<SymTable> sym);
|
explicit Compiler(std::shared_ptr<SymTable> sym);
|
||||||
virtual ~Compiler();
|
virtual ~Compiler();
|
||||||
|
|
||||||
|
std::shared_ptr<SymTable> sym() const { return m_sym; }
|
||||||
|
|
||||||
void add_macro(std::string const& name,
|
void add_macro(std::string const& name,
|
||||||
std::shared_ptr<NativeMacro> macro);
|
std::shared_ptr<NativeMacro> macro);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Constant.hpp"
|
#include "Constant.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,15 @@ namespace fk
|
||||||
{
|
{
|
||||||
FK_ERROR(constant_error);
|
FK_ERROR(constant_error);
|
||||||
|
|
||||||
using constant_t = std::variant<int, float, bool, std::string, size_t>;
|
class Program;
|
||||||
|
|
||||||
|
using constant_t = std::variant<int,
|
||||||
|
float,
|
||||||
|
bool,
|
||||||
|
std::string,
|
||||||
|
size_t,
|
||||||
|
std::shared_ptr<Program>
|
||||||
|
>;
|
||||||
|
|
||||||
class Constant
|
class Constant
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "Lambda.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ Lambda::Lambda(std::shared_ptr<Program> program, size_t arity)
|
||||||
|
: m_program { program }
|
||||||
|
, m_arity { arity }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Lambda::~Lambda()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef fk_LAMBDA_HPP
|
||||||
|
#define fk_LAMBDA_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
class Lambda
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Lambda(std::shared_ptr<Program> program, size_t arity);
|
||||||
|
virtual ~Lambda();
|
||||||
|
|
||||||
|
size_t arity() const { return m_arity; }
|
||||||
|
|
||||||
|
std::shared_ptr<Program> program() const { return m_program; }
|
||||||
|
std::shared_ptr<SymTable> sym() const { return m_sym; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Program> m_program;
|
||||||
|
size_t m_arity;
|
||||||
|
std::shared_ptr<SymTable> m_sym = std::make_shared<SymTable>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,6 +7,7 @@ namespace fk
|
||||||
: m_loc { loc }
|
: m_loc { loc }
|
||||||
{
|
{
|
||||||
std::vector<std::tuple<NodeType, std::string, bool>> text = {
|
std::vector<std::tuple<NodeType, std::string, bool>> text = {
|
||||||
|
{NODE_RARROW, "->", false},
|
||||||
{NODE_OPAR, "(", false},
|
{NODE_OPAR, "(", false},
|
||||||
{NODE_CPAR, ")", false},
|
{NODE_CPAR, ")", false},
|
||||||
{NODE_BOOL, "true", true},
|
{NODE_BOOL, "true", true},
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
#define NODE_TYPES(G) \
|
#define NODE_TYPES(G) \
|
||||||
G(NODE_MODULE), G(NODE_INT), G(NODE_FLOAT), G(NODE_BOOL), G(NODE_STRING),\
|
G(NODE_MODULE), G(NODE_INT), G(NODE_FLOAT), G(NODE_BOOL), G(NODE_STRING),\
|
||||||
G(NODE_IDENT), G(NODE_OPAR), G(NODE_CPAR), G(NODE_CALL)
|
G(NODE_IDENT), G(NODE_OPAR), G(NODE_CPAR), G(NODE_CALL), G(NODE_LAMBDA),\
|
||||||
|
G(NODE_RARROW), G(NODE_PARAMS), G(NODE_BODY)
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "Parser.hpp"
|
#include "Parser.hpp"
|
||||||
#include "src/Loc.hpp"
|
#include "src/Loc.hpp"
|
||||||
|
#include "src/Node.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
@ -48,12 +49,21 @@ namespace fk
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
{
|
{
|
||||||
if (type_any({NODE_INT, NODE_FLOAT, NODE_BOOL, NODE_STRING}))
|
if (type_any({
|
||||||
|
NODE_INT,
|
||||||
|
NODE_FLOAT,
|
||||||
|
NODE_BOOL,
|
||||||
|
NODE_STRING,
|
||||||
|
NODE_IDENT}))
|
||||||
{
|
{
|
||||||
return consume();
|
return consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type_is(NODE_OPAR))
|
if (type_all({NODE_OPAR, NODE_RARROW}))
|
||||||
|
{
|
||||||
|
return parse_lambda();
|
||||||
|
}
|
||||||
|
else if (type_is(NODE_OPAR))
|
||||||
{
|
{
|
||||||
return parse_call();
|
return parse_call();
|
||||||
}
|
}
|
||||||
|
@ -72,7 +82,7 @@ namespace fk
|
||||||
{
|
{
|
||||||
auto node = make_node(NODE_CALL);
|
auto node = make_node(NODE_CALL);
|
||||||
consume(NODE_OPAR);
|
consume(NODE_OPAR);
|
||||||
node->add_child(consume(NODE_IDENT));
|
node->add_child(parse_expr());
|
||||||
|
|
||||||
while (type_isnt(NODE_CPAR))
|
while (type_isnt(NODE_CPAR))
|
||||||
{
|
{
|
||||||
|
@ -84,6 +94,46 @@ namespace fk
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_lambda()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_LAMBDA);
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
consume(NODE_RARROW);
|
||||||
|
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
node->add_child(parse_params());
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
node->add_child(parse_body());
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_params()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_PARAMS);
|
||||||
|
|
||||||
|
while (type_isnt(NODE_CPAR))
|
||||||
|
{
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_body()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_BODY);
|
||||||
|
|
||||||
|
while (type_isnt(NODE_CPAR))
|
||||||
|
{
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::make_node(NodeType type)
|
std::shared_ptr<Node> Parser::make_node(NodeType type)
|
||||||
{
|
{
|
||||||
return std::make_shared<Node>(type, "", loc());
|
return std::make_shared<Node>(type, "", loc());
|
||||||
|
|
|
@ -23,6 +23,9 @@ namespace fk
|
||||||
std::shared_ptr<Node> parse_module();
|
std::shared_ptr<Node> parse_module();
|
||||||
std::shared_ptr<Node> parse_expr();
|
std::shared_ptr<Node> parse_expr();
|
||||||
std::shared_ptr<Node> parse_call();
|
std::shared_ptr<Node> parse_call();
|
||||||
|
std::shared_ptr<Node> parse_lambda();
|
||||||
|
std::shared_ptr<Node> parse_params();
|
||||||
|
std::shared_ptr<Node> parse_body();
|
||||||
|
|
||||||
std::shared_ptr<Node> make_node(NodeType type);
|
std::shared_ptr<Node> make_node(NodeType type);
|
||||||
Loc loc() const;
|
Loc loc() const;
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
#include "SymEntry.hpp"
|
#include "SymEntry.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
/*explicit*/ SymEntry::SymEntry(std::string const& name,
|
/*explicit*/ SymEntry::SymEntry(std::string const& name,
|
||||||
addr_t addr,
|
addr_t addr,
|
||||||
bool is_global,
|
bool is_global,
|
||||||
|
int scope,
|
||||||
|
std::shared_ptr<Node> parent,
|
||||||
Loc const& loc)
|
Loc const& loc)
|
||||||
: m_name { name }
|
: m_name { name }
|
||||||
, m_addr { addr }
|
, m_addr { addr }
|
||||||
, m_is_global { is_global }
|
, m_is_global { is_global }
|
||||||
|
, m_scope { scope }
|
||||||
|
, m_parent { parent }
|
||||||
, m_loc { loc }
|
, m_loc { loc }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -20,7 +25,9 @@ namespace fk
|
||||||
std::string SymEntry::string() const
|
std::string SymEntry::string() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << m_name << "\t" << m_addr << "\t" << m_is_global;
|
ss << m_name << "\t" << m_addr << "\t" << m_is_global << "\t" << m_scope
|
||||||
|
<< "\t"
|
||||||
|
<< (m_parent ? NodeTypeStr[m_parent->type()] + strlen("NODE_") : "");
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,35 @@
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
class Node;
|
||||||
|
|
||||||
class SymEntry
|
class SymEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit SymEntry(std::string const& name,
|
explicit SymEntry(std::string const& name,
|
||||||
addr_t addr,
|
addr_t addr,
|
||||||
bool is_global,
|
bool is_global,
|
||||||
|
int scope,
|
||||||
|
std::shared_ptr<Node> parent,
|
||||||
Loc const& loc);
|
Loc const& loc);
|
||||||
virtual ~SymEntry();
|
virtual ~SymEntry();
|
||||||
|
|
||||||
std::string name() const { return m_name; }
|
std::string name() const { return m_name; }
|
||||||
addr_t addr() const { return m_addr; }
|
addr_t addr() const { return m_addr; }
|
||||||
bool is_global() const { return m_is_global; }
|
bool is_global() const { return m_is_global; }
|
||||||
|
int scope() const { return m_scope; }
|
||||||
|
std::shared_ptr<Node> parent() const { return m_parent; }
|
||||||
|
std::shared_ptr<Node> node() const { return m_node; }
|
||||||
Loc loc() const { return m_loc; }
|
Loc loc() const { return m_loc; }
|
||||||
|
size_t arity() const { return m_arity; }
|
||||||
|
|
||||||
void set_global(bool global) { m_is_global = global; }
|
SymEntry& set_global(bool global) { m_is_global = global; return *this; }
|
||||||
|
SymEntry& set_addr(addr_t addr) { m_addr = addr; return *this; }
|
||||||
|
SymEntry& set_arity(size_t arity) { m_arity = arity; return *this; }
|
||||||
|
SymEntry& set_parent(std::shared_ptr<Node> parent)
|
||||||
|
{ m_parent = parent; return *this; }
|
||||||
|
SymEntry& set_node(std::shared_ptr<Node> node)
|
||||||
|
{ m_node = node; return *this; }
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
@ -28,7 +42,11 @@ namespace fk
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
addr_t m_addr;
|
addr_t m_addr;
|
||||||
bool m_is_global = false;
|
bool m_is_global = false;
|
||||||
|
int m_scope;
|
||||||
|
std::shared_ptr<Node> m_parent;
|
||||||
|
std::shared_ptr<Node> m_node;
|
||||||
Loc m_loc;
|
Loc m_loc;
|
||||||
|
size_t m_arity = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "SymTable.hpp"
|
#include "SymTable.hpp"
|
||||||
#include "src/Loc.hpp"
|
#include "src/Loc.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
@ -11,13 +12,15 @@ namespace fk
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymTable::declare_local(std::string const& name,
|
SymEntry& SymTable::declare_local(std::string const& name,
|
||||||
addr_t addr,
|
addr_t addr,
|
||||||
Loc const& loc)
|
Loc const& loc)
|
||||||
{
|
{
|
||||||
auto entry = find(name);
|
auto entry = find(name);
|
||||||
|
|
||||||
if (entry)
|
if (entry && entry->scope() == m_scope
|
||||||
|
&& ((entry->parent() == nullptr && m_parents.empty())
|
||||||
|
|| entry->parent() == m_parents.back()))
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "cannot declare existing variable '"
|
ss << "cannot declare existing variable '"
|
||||||
|
@ -27,28 +30,44 @@ namespace fk
|
||||||
entry->loc().error<symbol_error>(LOG_ERROR, ss.str());
|
entry->loc().error<symbol_error>(LOG_ERROR, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_entries.push_back(SymEntry {name, addr, false, loc});
|
SymEntry e {name,
|
||||||
|
addr,
|
||||||
|
false,
|
||||||
|
m_scope,
|
||||||
|
m_parents.empty() ?
|
||||||
|
nullptr : m_parents.back(),
|
||||||
|
loc};
|
||||||
|
|
||||||
|
m_entries.push_back(e);
|
||||||
|
|
||||||
|
return m_entries.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymTable::declare_global(std::string const& name,
|
SymEntry& SymTable::declare_global(std::string const& name,
|
||||||
addr_t addr,
|
addr_t addr,
|
||||||
Loc const& loc)
|
Loc const& loc)
|
||||||
{
|
{
|
||||||
declare_local(name, addr, loc);
|
return declare_local(name, addr, loc).set_global(true);
|
||||||
m_entries.back().set_global(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SymEntry> SymTable::find(std::string const& name)
|
std::optional<SymEntry> SymTable::find(std::string const& name)
|
||||||
{
|
{
|
||||||
|
std::optional<SymEntry> result;
|
||||||
|
|
||||||
for (auto& entry: m_entries)
|
for (auto& entry: m_entries)
|
||||||
{
|
{
|
||||||
if (entry.name() == name)
|
if (entry.name() == name
|
||||||
|
&& entry.scope() <= m_scope
|
||||||
|
&& ((entry.parent() == nullptr && m_parents.empty())
|
||||||
|
|| entry.parent() == m_parents.back())
|
||||||
|
&& (result == std::nullopt ||
|
||||||
|
entry.scope() > result->scope()))
|
||||||
{
|
{
|
||||||
return entry;
|
result = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SymTable::string() const
|
std::string SymTable::string() const
|
||||||
|
@ -56,6 +75,9 @@ namespace fk
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
ss << "======== SymTable ========\n";
|
ss << "======== SymTable ========\n";
|
||||||
|
ss << std::setw(8) << std::left;
|
||||||
|
ss << "name\taddr\tglobal\tscope\tfunction" << std::endl;
|
||||||
|
|
||||||
for (auto const& entry: m_entries)
|
for (auto const& entry: m_entries)
|
||||||
{
|
{
|
||||||
ss << entry.string() << std::endl;
|
ss << entry.string() << std::endl;
|
||||||
|
@ -63,4 +85,16 @@ namespace fk
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SymTable::enter_scope(std::shared_ptr<Node> parent)
|
||||||
|
{
|
||||||
|
m_scope++;
|
||||||
|
m_parents.push_back(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::leave_scope()
|
||||||
|
{
|
||||||
|
m_scope--;
|
||||||
|
m_parents.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,20 +14,25 @@ namespace fk
|
||||||
explicit SymTable();
|
explicit SymTable();
|
||||||
virtual ~SymTable();
|
virtual ~SymTable();
|
||||||
|
|
||||||
void declare_local(std::string const& name,
|
SymEntry& declare_local(std::string const& name,
|
||||||
addr_t addr,
|
addr_t addr,
|
||||||
Loc const& loc);
|
Loc const& loc);
|
||||||
|
|
||||||
void declare_global(std::string const& name,
|
SymEntry& declare_global(std::string const& name,
|
||||||
addr_t addr,
|
addr_t addr,
|
||||||
Loc const& loc);
|
Loc const& loc);
|
||||||
|
|
||||||
std::optional<SymEntry> find(std::string const& name);
|
std::optional<SymEntry> find(std::string const& name);
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
void enter_scope(std::shared_ptr<Node> parent=nullptr);
|
||||||
|
void leave_scope();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<SymEntry> m_entries;
|
std::vector<SymEntry> m_entries;
|
||||||
|
int m_scope;
|
||||||
|
std::vector<std::shared_ptr<Node>> m_parents;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
95
src/VM.cpp
95
src/VM.cpp
|
@ -1,4 +1,6 @@
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
|
#include "src/opcodes.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
@ -25,7 +27,43 @@ namespace fk
|
||||||
|
|
||||||
switch (instr.opcode)
|
switch (instr.opcode)
|
||||||
{
|
{
|
||||||
case OP_CALL_NATIVE: {
|
case OP_MAKE_FUNCTION: {
|
||||||
|
auto p = frame().program->get_const(pop());
|
||||||
|
auto prog = std::get<std::shared_ptr<Program>>(p->value());
|
||||||
|
auto lambda = std::make_shared<Lambda>(prog, instr.param);
|
||||||
|
|
||||||
|
addr_t addr = store_global(lambda);
|
||||||
|
auto ref = std::make_shared<Constant>(TYPE_REF, addr, p->loc());
|
||||||
|
|
||||||
|
push(frame().program->add(ref));
|
||||||
|
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_RET: {
|
||||||
|
m_pc = m_frames.back().ret_addr;
|
||||||
|
size_t sz = m_frames.back().stack_sz;
|
||||||
|
std::optional<addr_t> ret;
|
||||||
|
|
||||||
|
while (m_stack.size() > sz)
|
||||||
|
{
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
ret = pop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto value = m_frames.back().program->get_const(*ret);
|
||||||
|
m_frames.pop_back();
|
||||||
|
push(frame().program->add(value));
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_CALL_REF: {
|
||||||
std::vector<std::shared_ptr<Constant>> args;
|
std::vector<std::shared_ptr<Constant>> args;
|
||||||
|
|
||||||
for (size_t i=0; i<instr.param; i++)
|
for (size_t i=0; i<instr.param; i++)
|
||||||
|
@ -34,6 +72,36 @@ namespace fk
|
||||||
frame().program->get_const(pop()));
|
frame().program->get_const(pop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ref_val = frame().program->get_const(pop());
|
||||||
|
addr_t ref = std::get<addr_t>(ref_val->value());
|
||||||
|
|
||||||
|
auto lambda = std::get<std::shared_ptr<Lambda>>(load_global(ref));
|
||||||
|
|
||||||
|
Frame frame;
|
||||||
|
frame.program = lambda->program();
|
||||||
|
frame.ret_addr = m_pc;
|
||||||
|
frame.stack_sz = m_stack.size();
|
||||||
|
m_frames.push_back(frame);
|
||||||
|
|
||||||
|
for (size_t i=0; i<args.size(); i++)
|
||||||
|
{
|
||||||
|
store_local(i, args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pc = 0;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_CALL_NATIVE: {
|
||||||
|
std::vector<std::shared_ptr<Constant>> args;
|
||||||
|
|
||||||
|
for (size_t i=0; i<instr.param; i++)
|
||||||
|
{
|
||||||
|
addr_t addr = pop();
|
||||||
|
|
||||||
|
args.insert(std::begin(args),
|
||||||
|
frame().program->get_const(addr));
|
||||||
|
}
|
||||||
|
|
||||||
auto ref = std::get<size_t>(frame().program
|
auto ref = std::get<size_t>(frame().program
|
||||||
->get_const(pop())->value());
|
->get_const(pop())->value());
|
||||||
auto fun = std::get<std::shared_ptr<NativeFunction>>
|
auto fun = std::get<std::shared_ptr<NativeFunction>>
|
||||||
|
@ -44,6 +112,21 @@ namespace fk
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OP_LOAD_LOCAL: {
|
||||||
|
auto value = frame().locals[instr.param];
|
||||||
|
assert(value);
|
||||||
|
push(frame().program->add(value));
|
||||||
|
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_STORE_LOCAL: {
|
||||||
|
auto value = frame().program->get_const(top());
|
||||||
|
assert(value);
|
||||||
|
frame().locals[instr.param] = value;
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
case OP_LOAD_GLOBAL: {
|
case OP_LOAD_GLOBAL: {
|
||||||
auto ref = std::make_shared<Constant>(TYPE_REF,
|
auto ref = std::make_shared<Constant>(TYPE_REF,
|
||||||
(size_t) instr.param,
|
(size_t) instr.param,
|
||||||
|
@ -89,6 +172,16 @@ namespace fk
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> VM::load_local(addr_t addr)
|
||||||
|
{
|
||||||
|
return frame().locals[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::store_local(addr_t addr, std::shared_ptr<Constant> constant)
|
||||||
|
{
|
||||||
|
frame().locals[addr] = constant;
|
||||||
|
}
|
||||||
|
|
||||||
std::string VM::string() const
|
std::string VM::string() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
13
src/VM.hpp
13
src/VM.hpp
|
@ -4,13 +4,19 @@
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
#include "NativeFunction.hpp"
|
#include "NativeFunction.hpp"
|
||||||
|
#include "Lambda.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
using global_t = std::variant<std::shared_ptr<NativeFunction>>;
|
using global_t = std::variant<std::shared_ptr<NativeFunction>,
|
||||||
|
std::shared_ptr<Lambda>
|
||||||
|
>;
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
std::shared_ptr<Program> program;
|
std::shared_ptr<Program> program;
|
||||||
|
addr_t ret_addr;
|
||||||
|
size_t stack_sz;
|
||||||
|
std::unordered_map<addr_t, std::shared_ptr<Constant>> locals;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VM
|
class VM
|
||||||
|
@ -19,7 +25,7 @@ namespace fk
|
||||||
explicit VM();
|
explicit VM();
|
||||||
virtual ~VM();
|
virtual ~VM();
|
||||||
|
|
||||||
Frame frame() const { return m_frames.back(); }
|
Frame& frame() { return m_frames.back(); }
|
||||||
|
|
||||||
void mount(std::shared_ptr<Program> program);
|
void mount(std::shared_ptr<Program> program);
|
||||||
void run();
|
void run();
|
||||||
|
@ -28,6 +34,9 @@ namespace fk
|
||||||
void store_global(addr_t addr, global_t value);
|
void store_global(addr_t addr, global_t value);
|
||||||
size_t store_global(global_t value);
|
size_t store_global(global_t value);
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> load_local(addr_t addr);
|
||||||
|
void store_local(addr_t addr, std::shared_ptr<Constant> constant);
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#define fk_OPCODES_HPP
|
#define fk_OPCODES_HPP
|
||||||
|
|
||||||
#define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP), G(OP_CALL_NATIVE), \
|
#define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP), G(OP_CALL_NATIVE), \
|
||||||
G(OP_LOAD_GLOBAL)
|
G(OP_LOAD_GLOBAL), G(OP_LOAD_LOCAL), G(OP_STORE_LOCAL), \
|
||||||
|
G(OP_MAKE_FUNCTION), G(OP_CALL_REF), G(OP_RET)
|
||||||
|
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
|
|
||||||
#define TYPES(G) G(TYPE_INT), G(TYPE_FLOAT), G(TYPE_BOOL), G(TYPE_STRING), \
|
#define TYPES(G) G(TYPE_INT), G(TYPE_FLOAT), G(TYPE_BOOL), G(TYPE_STRING), \
|
||||||
G(TYPE_REF)
|
G(TYPE_REF), G(TYPE_PROGRAM)
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
|
|
@ -88,3 +88,12 @@ TEST_CASE_METHOD(LexerTest, "Lexer_parenthesis")
|
||||||
test_next("CPAR");
|
test_next("CPAR");
|
||||||
test_end();
|
test_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_lambda")
|
||||||
|
{
|
||||||
|
m_lexer.scan(" (->) ");
|
||||||
|
test_next("OPAR");
|
||||||
|
test_next("RARROW");
|
||||||
|
test_next("CPAR");
|
||||||
|
test_end();
|
||||||
|
}
|
||||||
|
|
|
@ -35,3 +35,15 @@ TEST_CASE_METHOD(ParserTest, "Parser_call")
|
||||||
test_parse("MODULE(CALL(IDENT[bim],INT[2],STRING['3'],BOOL[false]))",
|
test_parse("MODULE(CALL(IDENT[bim],INT[2],STRING['3'],BOOL[false]))",
|
||||||
" (bim 2 '3' false) ");
|
" (bim 2 '3' false) ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_lambda")
|
||||||
|
{
|
||||||
|
test_parse("MODULE(LAMBDA(PARAMS,BODY))",
|
||||||
|
" (-> ()) ");
|
||||||
|
|
||||||
|
test_parse("MODULE(LAMBDA(PARAMS(IDENT[x]),BODY))",
|
||||||
|
" (-> (x)) ");
|
||||||
|
|
||||||
|
test_parse("MODULE(LAMBDA(PARAMS(IDENT[x],IDENT[y]),BODY(INT[7])))",
|
||||||
|
" (-> (x y) 7) ");
|
||||||
|
}
|
||||||
|
|
|
@ -14,16 +14,5 @@ protected:
|
||||||
|
|
||||||
TEST_CASE_METHOD(SymTableTest, "SymTable_declare_var")
|
TEST_CASE_METHOD(SymTableTest, "SymTable_declare_var")
|
||||||
{
|
{
|
||||||
REQUIRE(std::nullopt == m_sym.find("hello"));
|
|
||||||
|
|
||||||
m_sym.declare_local("hello", 404, m_loc);
|
|
||||||
auto entry = m_sym.find("hello");
|
|
||||||
|
|
||||||
REQUIRE(std::nullopt != entry);
|
|
||||||
REQUIRE("hello" == entry->name());
|
|
||||||
REQUIRE(404 == entry->addr());
|
|
||||||
REQUIRE(false == entry->is_global());
|
|
||||||
|
|
||||||
REQUIRE_THROWS_AS(m_sym.declare_local("hello", 407, m_loc),
|
|
||||||
fk::symbol_error);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue