ADD: function literal.
parent
59af9d809e
commit
a5ec909a5a
|
@ -5,5 +5,9 @@ EXPR ::=
|
||||||
| ident
|
| ident
|
||||||
| VARDECL
|
| VARDECL
|
||||||
| FUNCALL
|
| FUNCALL
|
||||||
|
| LAMBDA
|
||||||
VARDECL ::= opar decl ident EXPR cpar
|
VARDECL ::= opar decl ident EXPR cpar
|
||||||
FUNCALL ::= opar ident EXPR* cpar
|
FUNCALL ::= opar EXPR EXPR* cpar
|
||||||
|
LAMBDA ::= opar lambda opar PARAMS cpar BODY cpar
|
||||||
|
PARAMS ::= ident*
|
||||||
|
BODY ::= EXPR*
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
;; declare lambda
|
||||||
|
($ a (-> (x) (* x 2)))
|
||||||
|
(assert-eq? 14 (a 7))
|
||||||
|
|
||||||
|
;; high order function
|
||||||
|
($ b (-> (x y) (x (x y))))
|
||||||
|
(assert-eq? 12 (b a 3))
|
||||||
|
|
||||||
|
;; calling function literal
|
||||||
|
(assert-eq? 7 ( (-> (x y) (+ x y 1)) 2 4 ))
|
11
lib/core.cpp
11
lib/core.cpp
|
@ -195,12 +195,14 @@ extern "C" void lib_bool(grino::Loader& loader)
|
||||||
return grino::Value::make_bool(args[0]->loc(), !args[0]->as_bool());
|
return grino::Value::make_bool(args[0]->loc(), !args[0]->as_bool());
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.add_static("and", [](auto& compiler, auto node, auto& program){
|
loader.add_static("and", [](auto& compiler, auto node,
|
||||||
|
auto& program,
|
||||||
|
auto& sym){
|
||||||
std::vector<size_t> to_false;
|
std::vector<size_t> to_false;
|
||||||
|
|
||||||
for (size_t i=1; i<node->size(); i++)
|
for (size_t i=1; i<node->size(); i++)
|
||||||
{
|
{
|
||||||
compiler.compile(node->child(i).lock(), program);
|
compiler.compile(node->child(i).lock(), program, sym);
|
||||||
to_false.push_back(program.size());
|
to_false.push_back(program.size());
|
||||||
program.push_instr(grino::OPCODE_BRF, 0 /* to false */);
|
program.push_instr(grino::OPCODE_BRF, 0 /* to false */);
|
||||||
}
|
}
|
||||||
|
@ -223,12 +225,13 @@ extern "C" void lib_bool(grino::Loader& loader)
|
||||||
program.set_param(to_end, program.size());
|
program.set_param(to_end, program.size());
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.add_static("or", [](auto& compiler, auto node, auto& program){
|
loader.add_static("or", [](auto& compiler, auto node,
|
||||||
|
auto& program, auto& sym){
|
||||||
std::vector<size_t> to_true;
|
std::vector<size_t> to_true;
|
||||||
|
|
||||||
for (size_t i=1; i<node->size(); i++)
|
for (size_t i=1; i<node->size(); i++)
|
||||||
{
|
{
|
||||||
compiler.compile(node->child(i).lock(), program);
|
compiler.compile(node->child(i).lock(), program, sym);
|
||||||
program.push_instr(grino::OPCODE_NOT);
|
program.push_instr(grino::OPCODE_NOT);
|
||||||
to_true.push_back(program.size());
|
to_true.push_back(program.size());
|
||||||
program.push_instr(grino::OPCODE_BRF, 0 /* to true */);
|
program.push_instr(grino::OPCODE_BRF, 0 /* to true */);
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
/*explicit*/ Compiler::Compiler(Logger& logger, SymTable& sym)
|
/*explicit*/ Compiler::Compiler(Logger& logger)
|
||||||
: m_logger { logger }
|
: m_logger { logger }
|
||||||
, m_sym { sym }
|
|
||||||
{
|
{
|
||||||
|
enter_scope();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ Compiler::~Compiler()
|
/*virtual*/ Compiler::~Compiler()
|
||||||
|
@ -17,18 +17,31 @@ namespace grino
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile(std::shared_ptr<Node> node,
|
void Compiler::compile(std::shared_ptr<Node> node,
|
||||||
Program& program)
|
Program& program,
|
||||||
|
SymTable& sym)
|
||||||
{
|
{
|
||||||
switch (node->type())
|
switch (node->type())
|
||||||
{
|
{
|
||||||
case NODE_MODULE: {
|
case NODE_MODULE: {
|
||||||
for (size_t i=0; i<node->size(); i++)
|
for (size_t i=0; i<node->size(); i++)
|
||||||
{
|
{
|
||||||
compile(node->child(i).lock(), program);
|
compile(node->child(i).lock(), program, sym);
|
||||||
program.push_instr(OPCODE_POP);
|
program.push_instr(OPCODE_POP);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_BODY: {
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile(node->child(i).lock(), program, sym);
|
||||||
|
|
||||||
|
if (i < node->size() - 1)
|
||||||
|
{
|
||||||
|
program.push_instr(OPCODE_POP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_FUNCALL: {
|
case NODE_FUNCALL: {
|
||||||
std::string ident = node->child(0).lock()->repr();
|
std::string ident = node->child(0).lock()->repr();
|
||||||
|
|
||||||
|
@ -36,13 +49,13 @@ namespace grino
|
||||||
itr != std::end(m_statics))
|
itr != std::end(m_statics))
|
||||||
{
|
{
|
||||||
auto fun = itr->second;
|
auto fun = itr->second;
|
||||||
fun->call(*this, node, program);
|
fun->call(*this, node, program, sym);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<node->size(); i++)
|
for (size_t i=0; i<node->size(); i++)
|
||||||
{
|
{
|
||||||
compile(node->child(i).lock(), program);
|
compile(node->child(i).lock(), program, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
program.push_instr(OPCODE_CALL, node->size() - 1);
|
program.push_instr(OPCODE_CALL, node->size() - 1);
|
||||||
|
@ -50,6 +63,32 @@ namespace grino
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_LAMBDA: {
|
||||||
|
auto params = node->child(0).lock();
|
||||||
|
|
||||||
|
enter_scope();
|
||||||
|
|
||||||
|
for (size_t i=0; i<params->size(); i++)
|
||||||
|
{
|
||||||
|
auto param = params->child(i).lock();
|
||||||
|
std::string ident = param->repr();
|
||||||
|
sym.declare(param->loc(), ident, get_local_address(),
|
||||||
|
m_scope.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prog = std::make_shared<Program>();
|
||||||
|
auto body = node->child(1).lock();
|
||||||
|
compile(body, *prog, sym);
|
||||||
|
|
||||||
|
prog->push_instr(OPCODE_RET);
|
||||||
|
|
||||||
|
leave_scope();
|
||||||
|
sym.purge(m_scope.size());
|
||||||
|
|
||||||
|
program.push_value(Value::make_program(node->loc(), prog));
|
||||||
|
program.push_instr(OPCODE_MK_FUN);
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_BOOL: {
|
case NODE_BOOL: {
|
||||||
std::string repr = node->repr();
|
std::string repr = node->repr();
|
||||||
auto value = Value::make_bool(node->loc(), repr == "true");
|
auto value = Value::make_bool(node->loc(), repr == "true");
|
||||||
|
@ -71,15 +110,15 @@ namespace grino
|
||||||
auto expr = node->child(1).lock();
|
auto expr = node->child(1).lock();
|
||||||
size_t address = get_local_address();
|
size_t address = get_local_address();
|
||||||
|
|
||||||
compile(expr, program);
|
compile(expr, program, sym);
|
||||||
|
|
||||||
m_sym.declare(node->loc(), ident, address);
|
sym.declare(node->loc(), ident, address, m_scope.size());
|
||||||
program.push_instr(OPCODE_STORE_LOCAL, address);
|
program.push_instr(OPCODE_STORE_LOCAL, address);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NODE_IDENT: {
|
case NODE_IDENT: {
|
||||||
std::string ident = node->repr();
|
std::string ident = node->repr();
|
||||||
auto entry = m_sym.find(ident);
|
auto entry = sym.find(ident, m_scope.size());
|
||||||
|
|
||||||
if (entry == std::nullopt)
|
if (entry == std::nullopt)
|
||||||
{
|
{
|
||||||
|
@ -114,8 +153,18 @@ namespace grino
|
||||||
|
|
||||||
size_t Compiler::get_local_address()
|
size_t Compiler::get_local_address()
|
||||||
{
|
{
|
||||||
static size_t addr = 0;
|
m_scope.back() += 1;
|
||||||
addr++;
|
return m_scope.back() - 1;
|
||||||
return addr - 1;
|
}
|
||||||
|
|
||||||
|
void Compiler::enter_scope()
|
||||||
|
{
|
||||||
|
m_scope.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::leave_scope()
|
||||||
|
{
|
||||||
|
assert(m_scope.empty() == false);
|
||||||
|
m_scope.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,22 +17,26 @@ namespace grino
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Compiler(Logger& logger, SymTable& sym);
|
explicit Compiler(Logger& logger);
|
||||||
virtual ~Compiler();
|
virtual ~Compiler();
|
||||||
|
|
||||||
void compile(std::shared_ptr<Node> node,
|
void compile(std::shared_ptr<Node> node,
|
||||||
Program& program);
|
Program& program,
|
||||||
|
SymTable& sym);
|
||||||
|
|
||||||
void add_static_func(std::string const& name,
|
void add_static_func(std::string const& name,
|
||||||
std::shared_ptr<StaticFunction> fun);
|
std::shared_ptr<StaticFunction> fun);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Logger& m_logger;
|
Logger& m_logger;
|
||||||
SymTable& m_sym;
|
|
||||||
std::unordered_map<std::string,
|
std::unordered_map<std::string,
|
||||||
std::shared_ptr<StaticFunction>> m_statics;
|
std::shared_ptr<StaticFunction>> m_statics;
|
||||||
|
|
||||||
|
std::vector<size_t> m_scope;
|
||||||
|
|
||||||
size_t get_local_address();
|
size_t get_local_address();
|
||||||
|
void enter_scope();
|
||||||
|
void leave_scope();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "Function.hpp"
|
#include "Function.hpp"
|
||||||
#include "Value.hpp"
|
#include "Value.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
@ -8,10 +9,25 @@ namespace grino
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*explicit*/ Function::Function(std::shared_ptr<Program> prog)
|
||||||
|
: m_prog { prog }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/*virtual*/ Function::~Function()
|
/*virtual*/ Function::~Function()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Function::is_native() const
|
||||||
|
{
|
||||||
|
return m_native != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Program> Function::program() const
|
||||||
|
{
|
||||||
|
return m_prog;
|
||||||
|
}
|
||||||
|
|
||||||
value_t Function::call(args_t args)
|
value_t Function::call(args_t args)
|
||||||
{
|
{
|
||||||
assert(m_native);
|
assert(m_native);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
class Value;
|
class Value;
|
||||||
|
class Program;
|
||||||
|
|
||||||
using value_t = std::shared_ptr<Value>;
|
using value_t = std::shared_ptr<Value>;
|
||||||
using args_t = std::vector<value_t>;
|
using args_t = std::vector<value_t>;
|
||||||
|
@ -15,12 +16,16 @@ namespace grino
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Function(native_t native);
|
explicit Function(native_t native);
|
||||||
|
explicit Function(std::shared_ptr<Program> prog);
|
||||||
virtual ~Function();
|
virtual ~Function();
|
||||||
|
|
||||||
|
bool is_native() const;
|
||||||
|
std::shared_ptr<Program> program() const;
|
||||||
value_t call(args_t args);
|
value_t call(args_t args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
native_t m_native;
|
native_t m_native;
|
||||||
|
std::shared_ptr<Program> m_prog;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace grino
|
||||||
add_text(NODE_CPAR, ")", false);
|
add_text(NODE_CPAR, ")", false);
|
||||||
add_text(NODE_DECL, "$", false);
|
add_text(NODE_DECL, "$", false);
|
||||||
|
|
||||||
|
add_keyword(NODE_LAMBDA, "->", false);
|
||||||
add_keyword(NODE_BOOL, "true", true);
|
add_keyword(NODE_BOOL, "true", true);
|
||||||
add_keyword(NODE_BOOL, "false", true);
|
add_keyword(NODE_BOOL, "false", true);
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace grino
|
||||||
grino::Loc loc {"???", 0};
|
grino::Loc loc {"???", 0};
|
||||||
|
|
||||||
m_vm.set_heap(addr, grino::Value::make_native_function(loc, native));
|
m_vm.set_heap(addr, grino::Value::make_native_function(loc, native));
|
||||||
m_sym_table.declare_object(loc, name, addr);
|
m_sym_table.declare_object(loc, name, addr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::add_static(std::string const& name, static_fun_t fun)
|
void Loader::add_static(std::string const& name, static_fun_t fun)
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
G(NODE_IDENT), \
|
G(NODE_IDENT), \
|
||||||
G(NODE_OPAR), \
|
G(NODE_OPAR), \
|
||||||
G(NODE_CPAR), \
|
G(NODE_CPAR), \
|
||||||
G(NODE_DECL),
|
G(NODE_DECL), \
|
||||||
|
G(NODE_LAMBDA), \
|
||||||
|
G(NODE_PARAMS), \
|
||||||
|
G(NODE_BODY),
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Parser.hpp"
|
#include "Parser.hpp"
|
||||||
|
#include "src/Node.hpp"
|
||||||
#include "src/mutils.hpp"
|
#include "src/mutils.hpp"
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
|
@ -117,16 +118,21 @@ namespace grino
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
{
|
{
|
||||||
if (type_is({NODE_OPAR, NODE_IDENT}))
|
|
||||||
{
|
|
||||||
return parse_funcall();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type_is({NODE_OPAR, NODE_DECL}))
|
if (type_is({NODE_OPAR, NODE_DECL}))
|
||||||
{
|
{
|
||||||
return parse_vardecl();
|
return parse_vardecl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type_is({NODE_OPAR, NODE_LAMBDA}))
|
||||||
|
{
|
||||||
|
return parse_lambda();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_OPAR))
|
||||||
|
{
|
||||||
|
return parse_funcall();
|
||||||
|
}
|
||||||
|
|
||||||
if (type_is(NODE_IDENT)
|
if (type_is(NODE_IDENT)
|
||||||
|| type_is(NODE_BOOL)
|
|| type_is(NODE_BOOL)
|
||||||
|| type_is(NODE_INT))
|
|| type_is(NODE_INT))
|
||||||
|
@ -163,7 +169,7 @@ namespace grino
|
||||||
consume(NODE_OPAR);
|
consume(NODE_OPAR);
|
||||||
|
|
||||||
auto node = make_node(NODE_FUNCALL);
|
auto node = make_node(NODE_FUNCALL);
|
||||||
node->add_child(consume(NODE_IDENT));
|
node->add_child(parse_expr());
|
||||||
|
|
||||||
while (!type_is(NODE_CPAR))
|
while (!type_is(NODE_CPAR))
|
||||||
{
|
{
|
||||||
|
@ -174,4 +180,44 @@ namespace grino
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_lambda()
|
||||||
|
{
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
auto node = consume(NODE_LAMBDA);
|
||||||
|
|
||||||
|
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_is(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_is(NODE_CPAR))
|
||||||
|
{
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ namespace grino
|
||||||
std::shared_ptr<Node> parse_expr();
|
std::shared_ptr<Node> parse_expr();
|
||||||
std::shared_ptr<Node> parse_vardecl();
|
std::shared_ptr<Node> parse_vardecl();
|
||||||
std::shared_ptr<Node> parse_funcall();
|
std::shared_ptr<Node> parse_funcall();
|
||||||
|
std::shared_ptr<Node> parse_lambda();
|
||||||
|
std::shared_ptr<Node> parse_params();
|
||||||
|
std::shared_ptr<Node> parse_body();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,12 @@ namespace grino
|
||||||
return m_constants[index];
|
return m_constants[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Program::push_value(std::shared_ptr<Value> value)
|
||||||
|
{
|
||||||
|
size_t addr = push_constant(value);
|
||||||
|
push_instr(OPCODE_LOAD_CONST, addr);
|
||||||
|
}
|
||||||
|
|
||||||
std::string Program::string() const
|
std::string Program::string() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
|
@ -31,6 +31,8 @@ namespace grino
|
||||||
size_t push_constant(std::shared_ptr<Value> value);
|
size_t push_constant(std::shared_ptr<Value> value);
|
||||||
std::shared_ptr<Value> constant(size_t index) const;
|
std::shared_ptr<Value> constant(size_t index) const;
|
||||||
|
|
||||||
|
void push_value(std::shared_ptr<Value> value);
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -14,8 +14,9 @@ namespace grino
|
||||||
|
|
||||||
void StaticFunction::call(Compiler& compiler,
|
void StaticFunction::call(Compiler& compiler,
|
||||||
node_t node,
|
node_t node,
|
||||||
prog_t prog)
|
prog_t prog,
|
||||||
|
SymTable& sym)
|
||||||
{
|
{
|
||||||
m_fun(compiler, node, prog);
|
m_fun(compiler, node, prog,sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,15 @@
|
||||||
#include "Function.hpp"
|
#include "Function.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
class Compiler;
|
class Compiler;
|
||||||
using node_t = std::shared_ptr<Node>;
|
using node_t = std::shared_ptr<Node>;
|
||||||
using prog_t = Program&;
|
using prog_t = Program&;
|
||||||
using static_fun_t = std::function<void (Compiler&, node_t, prog_t)>;
|
using sym_t = SymTable&;
|
||||||
|
using static_fun_t = std::function<void (Compiler&, node_t, prog_t, sym_t)>;
|
||||||
|
|
||||||
class StaticFunction
|
class StaticFunction
|
||||||
{
|
{
|
||||||
|
@ -19,7 +21,7 @@ namespace grino
|
||||||
explicit StaticFunction(static_fun_t fun);
|
explicit StaticFunction(static_fun_t fun);
|
||||||
virtual ~StaticFunction();
|
virtual ~StaticFunction();
|
||||||
|
|
||||||
void call(Compiler& compiler, node_t node, prog_t prog);
|
void call(Compiler& compiler, node_t node, prog_t prog, sym_t sym);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static_fun_t m_fun;
|
static_fun_t m_fun;
|
||||||
|
|
|
@ -11,9 +11,11 @@ namespace grino
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymTable::declare(Loc const& loc, std::string const& name, size_t addr)
|
void SymTable::declare(Loc const& loc, std::string const& name, size_t addr,
|
||||||
|
size_t scope)
|
||||||
{
|
{
|
||||||
if (find(name))
|
if (auto e = find(name, scope);
|
||||||
|
e && e->scope == scope)
|
||||||
{
|
{
|
||||||
m_logger.log<symbolic_error>(LOG_ERROR, loc, "'"
|
m_logger.log<symbolic_error>(LOG_ERROR, loc, "'"
|
||||||
+ name
|
+ name
|
||||||
|
@ -24,29 +26,46 @@ namespace grino
|
||||||
entry.addr = addr;
|
entry.addr = addr;
|
||||||
entry.name = name;
|
entry.name = name;
|
||||||
entry.is_object = false;
|
entry.is_object = false;
|
||||||
|
entry.scope = scope;
|
||||||
|
|
||||||
m_entries.push_back(entry);
|
m_entries.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymTable::declare_object(Loc const& loc,
|
void SymTable::declare_object(Loc const& loc,
|
||||||
std::string const& name,
|
std::string const& name,
|
||||||
size_t addr)
|
size_t addr,
|
||||||
|
size_t scope)
|
||||||
{
|
{
|
||||||
declare(loc, name, addr);
|
declare(loc, name, addr, scope);
|
||||||
m_entries.back().is_object = true;
|
m_entries.back().is_object = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SymEntry> SymTable::find(std::string const& name)
|
std::optional<SymEntry> SymTable::find(std::string const& name, size_t scope)
|
||||||
{
|
{
|
||||||
|
std::optional<SymEntry> entry;
|
||||||
|
|
||||||
for (size_t i=0; i<m_entries.size(); i++)
|
for (size_t i=0; i<m_entries.size(); i++)
|
||||||
{
|
{
|
||||||
if (m_entries[i].name == name)
|
if (m_entries[i].name == name
|
||||||
|
&& m_entries[i].scope <= scope
|
||||||
|
&& (!entry || m_entries[i].scope > entry->scope))
|
||||||
{
|
{
|
||||||
return m_entries[i];
|
entry = m_entries[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::purge(size_t scope)
|
||||||
|
{
|
||||||
|
auto itr = std::remove_if(std::begin(m_entries),
|
||||||
|
std::end(m_entries),
|
||||||
|
[scope](auto const& entry){
|
||||||
|
return entry.scope > scope;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_entries.erase(itr, std::end(m_entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SymTable::string() const
|
std::string SymTable::string() const
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace grino
|
||||||
std::string name;
|
std::string name;
|
||||||
bool is_object; /* object are on the heap instead of the stack */
|
bool is_object; /* object are on the heap instead of the stack */
|
||||||
size_t addr; /* address on the heap if object, local address otherwise */
|
size_t addr; /* address on the heap if object, local address otherwise */
|
||||||
|
size_t scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
GRINO_ERROR(symbolic_error);
|
GRINO_ERROR(symbolic_error);
|
||||||
|
@ -20,10 +21,15 @@ namespace grino
|
||||||
explicit SymTable(Logger& logger);
|
explicit SymTable(Logger& logger);
|
||||||
virtual ~SymTable();
|
virtual ~SymTable();
|
||||||
|
|
||||||
void declare(Loc const& loc, std::string const& name, size_t addr);
|
void declare(Loc const& loc, std::string const& name, size_t addr,
|
||||||
void declare_object(Loc const& loc, std::string const& name, size_t addr);
|
size_t scope);
|
||||||
|
|
||||||
std::optional<SymEntry> find(std::string const& name);
|
void declare_object(Loc const& loc, std::string const& name, size_t addr,
|
||||||
|
size_t scope);
|
||||||
|
|
||||||
|
std::optional<SymEntry> find(std::string const& name, size_t scope);
|
||||||
|
|
||||||
|
void purge(size_t scope);
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
|
91
src/VM.cpp
91
src/VM.cpp
|
@ -1,33 +1,52 @@
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
#include "src/opcodes.hpp"
|
#include "src/opcodes.hpp"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
/*explicit*/ VM::VM(Logger& logger)
|
/*explicit*/ VM::VM(Logger& logger, Program& program)
|
||||||
: m_logger { logger }
|
: m_logger { logger }
|
||||||
{
|
{
|
||||||
m_frames.push_back(Frame {});
|
Frame frame;
|
||||||
|
frame.pc = m_pc;
|
||||||
|
frame.sp = m_sp;
|
||||||
|
frame.program = &program;
|
||||||
|
m_frames.push_back(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ VM::~VM()
|
/*virtual*/ VM::~VM()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::run(Program& program)
|
void VM::run()
|
||||||
{
|
{
|
||||||
m_bp = 0;
|
m_bp = 0;
|
||||||
m_sp = 0;
|
m_sp = 0;
|
||||||
m_pc = 0;
|
m_pc = 0;
|
||||||
|
|
||||||
while (m_pc < program.size())
|
while (m_pc < program().size())
|
||||||
{
|
{
|
||||||
Instr instr = program.get(m_pc);
|
Instr instr = program().get(m_pc);
|
||||||
|
|
||||||
switch (instr.opcode)
|
switch (instr.opcode)
|
||||||
{
|
{
|
||||||
|
case OPCODE_MK_FUN: {
|
||||||
|
auto prog_val = program().constant(pop());
|
||||||
|
auto prog = prog_val->as_program();
|
||||||
|
auto fun_val = Value::make_function(prog_val->loc(), prog);
|
||||||
|
size_t addr = m_heap.size();
|
||||||
|
set_heap(addr, fun_val);
|
||||||
|
|
||||||
|
auto ref = Value::make_ref(prog_val->loc(), addr);
|
||||||
|
push(program().push_constant(ref));
|
||||||
|
|
||||||
|
m_pc++;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
case OPCODE_NOT: {
|
case OPCODE_NOT: {
|
||||||
auto val = program.constant(pop());
|
auto val = program().constant(pop());
|
||||||
push(program.push_constant(Value::make_bool(val->loc(),
|
push(program().push_constant(Value::make_bool(val->loc(),
|
||||||
!val->as_bool())));
|
!val->as_bool())));
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
@ -37,7 +56,7 @@ namespace grino
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OPCODE_BRF: {
|
case OPCODE_BRF: {
|
||||||
auto val = program.constant(pop())->as_bool();
|
auto val = program().constant(pop())->as_bool();
|
||||||
size_t addr = *instr.param;
|
size_t addr = *instr.param;
|
||||||
|
|
||||||
if (!val)
|
if (!val)
|
||||||
|
@ -62,21 +81,39 @@ namespace grino
|
||||||
|
|
||||||
case OPCODE_STORE_LOCAL: {
|
case OPCODE_STORE_LOCAL: {
|
||||||
size_t addr = *instr.param;
|
size_t addr = *instr.param;
|
||||||
auto value = program.constant(top());
|
auto value = program().constant(top());
|
||||||
set_local(addr, value);
|
set_local(addr, value);
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OPCODE_LOAD_LOCAL: {
|
case OPCODE_LOAD_LOCAL: {
|
||||||
size_t addr = *instr.param;
|
size_t addr = *instr.param;
|
||||||
push(program.push_constant(local(addr)));
|
push(program().push_constant(local(addr)));
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OPCODE_LOAD_OBJ: {
|
case OPCODE_LOAD_OBJ: {
|
||||||
size_t addr = *instr.param;
|
size_t addr = *instr.param;
|
||||||
auto ref = Value::make_ref(Loc {"???", 0}, addr);
|
auto ref = Value::make_ref(Loc {"???", 0}, addr);
|
||||||
push(program.push_constant(ref));
|
push(program().push_constant(ref));
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OPCODE_RET: {
|
||||||
|
m_pc = m_frames.back().pc;
|
||||||
|
size_t old_sp = m_frames.back().sp;
|
||||||
|
|
||||||
|
auto ret_val = program().constant(m_stack[m_sp - 1]);
|
||||||
|
|
||||||
|
m_frames.pop_back();
|
||||||
|
|
||||||
|
while (m_sp > old_sp)
|
||||||
|
{
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
push(program().push_constant(ret_val));
|
||||||
|
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -87,18 +124,33 @@ namespace grino
|
||||||
|
|
||||||
for (size_t i=0; i<N; i++)
|
for (size_t i=0; i<N; i++)
|
||||||
{
|
{
|
||||||
args.insert(std::begin(args), program.constant(pop()));
|
args.insert(std::begin(args), program().constant(pop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ref = program.constant(pop())->as_ref();
|
size_t ref = program().constant(pop())->as_ref();
|
||||||
auto fun = heap(ref)->as_function();
|
auto fun = heap(ref)->as_function();
|
||||||
|
|
||||||
|
if (fun->is_native())
|
||||||
|
{
|
||||||
auto ret = fun->call(args);
|
auto ret = fun->call(args);
|
||||||
|
push(program().push_constant(ret));
|
||||||
push(program.push_constant(ret));
|
|
||||||
|
|
||||||
m_pc++;
|
m_pc++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Frame frame;
|
||||||
|
frame.pc = m_pc;
|
||||||
|
frame.sp = m_sp;
|
||||||
|
frame.program = fun->program().get();
|
||||||
|
m_frames.push_back(frame);
|
||||||
|
|
||||||
|
for (size_t i=0; i<args.size(); i++)
|
||||||
|
{
|
||||||
|
set_local(i, args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pc = 0;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -110,6 +162,7 @@ namespace grino
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string VM::string() const
|
std::string VM::string() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -173,4 +226,10 @@ namespace grino
|
||||||
size_t addr = m_stack[m_sp - 1];
|
size_t addr = m_stack[m_sp - 1];
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Program& VM::program() const
|
||||||
|
{
|
||||||
|
assert(m_frames.back().program);
|
||||||
|
return *m_frames.back().program;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
10
src/VM.hpp
10
src/VM.hpp
|
@ -12,18 +12,21 @@ namespace grino
|
||||||
GRINO_ERROR(execution_error);
|
GRINO_ERROR(execution_error);
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
|
size_t pc;
|
||||||
|
size_t sp;
|
||||||
|
Program* program;
|
||||||
std::unordered_map<size_t, std::shared_ptr<Value>> locals;
|
std::unordered_map<size_t, std::shared_ptr<Value>> locals;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VM
|
class VM
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VM(Logger& logger);
|
explicit VM(Logger& logger, Program& program);
|
||||||
virtual ~VM();
|
virtual ~VM();
|
||||||
|
|
||||||
size_t heap_size() const { return m_heap.size(); }
|
size_t heap_size() const { return m_heap.size(); }
|
||||||
|
|
||||||
void run(Program& program);
|
void run();
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
@ -36,8 +39,10 @@ namespace grino
|
||||||
std::shared_ptr<Value> value);
|
std::shared_ptr<Value> value);
|
||||||
private:
|
private:
|
||||||
Logger& m_logger;
|
Logger& m_logger;
|
||||||
|
|
||||||
std::array<size_t, STACK_SIZE> m_stack;
|
std::array<size_t, STACK_SIZE> m_stack;
|
||||||
std::unordered_map<size_t, std::shared_ptr<Value>> m_heap;
|
std::unordered_map<size_t, std::shared_ptr<Value>> m_heap;
|
||||||
|
|
||||||
std::vector<Frame> m_frames;
|
std::vector<Frame> m_frames;
|
||||||
|
|
||||||
size_t m_sp; /* stack pointer */
|
size_t m_sp; /* stack pointer */
|
||||||
|
@ -48,6 +53,7 @@ namespace grino
|
||||||
size_t pop();
|
size_t pop();
|
||||||
size_t top();
|
size_t top();
|
||||||
|
|
||||||
|
Program& program() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Value.hpp"
|
#include "Value.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
@ -35,6 +36,16 @@ namespace grino
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static*/ std::shared_ptr<Value>
|
||||||
|
Value::make_function(Loc const& loc,
|
||||||
|
std::shared_ptr<Program> val)
|
||||||
|
{
|
||||||
|
auto value = std::make_shared<Value>(loc);
|
||||||
|
value->m_type = TYPE_FUNCTION;
|
||||||
|
value->m_function_val = std::make_shared<Function>(val);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/*static*/ std::shared_ptr<Value> Value::make_ref(Loc const& loc, size_t val)
|
/*static*/ std::shared_ptr<Value> Value::make_ref(Loc const& loc, size_t val)
|
||||||
{
|
{
|
||||||
auto value = std::make_shared<Value>(loc);
|
auto value = std::make_shared<Value>(loc);
|
||||||
|
@ -43,6 +54,21 @@ namespace grino
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
std::shared_ptr<Value> Value::make_program(Loc const& loc,
|
||||||
|
std::shared_ptr<Program> val)
|
||||||
|
{
|
||||||
|
auto value = std::make_shared<Value>(loc);
|
||||||
|
value->m_type = TYPE_PROGRAM;
|
||||||
|
value->m_program_val = val;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Program> Value::as_program() const
|
||||||
|
{
|
||||||
|
return m_program_val;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Value::string() const
|
std::string Value::string() const
|
||||||
{
|
{
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
|
@ -52,6 +78,7 @@ namespace grino
|
||||||
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
|
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
|
||||||
case TYPE_FUNCTION: return "<function>";
|
case TYPE_FUNCTION: return "<function>";
|
||||||
case TYPE_REF: return "&" + std::to_string(*m_ref_val);
|
case TYPE_REF: return "&" + std::to_string(*m_ref_val);
|
||||||
|
case TYPE_PROGRAM: return "<program>";
|
||||||
default:
|
default:
|
||||||
std::cerr << "cannot stringify value "
|
std::cerr << "cannot stringify value "
|
||||||
<< TypeTypeStr[m_type] << std::endl;
|
<< TypeTypeStr[m_type] << std::endl;
|
||||||
|
@ -69,6 +96,7 @@ namespace grino
|
||||||
case TYPE_BOOL: return *m_bool_val == *other.m_bool_val;
|
case TYPE_BOOL: return *m_bool_val == *other.m_bool_val;
|
||||||
case TYPE_INT: return *m_int_val == *other.m_int_val;
|
case TYPE_INT: return *m_int_val == *other.m_int_val;
|
||||||
case TYPE_REF: return *m_ref_val == *other.m_ref_val;
|
case TYPE_REF: return *m_ref_val == *other.m_ref_val;
|
||||||
|
case TYPE_PROGRAM: return false;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
std::cerr << "cannot compare equality with value "
|
std::cerr << "cannot compare equality with value "
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
class Program;
|
||||||
|
|
||||||
class Value
|
class Value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -16,9 +18,14 @@ namespace grino
|
||||||
static std::shared_ptr<Value> make_int(Loc const& loc, int val);
|
static std::shared_ptr<Value> make_int(Loc const& loc, int val);
|
||||||
static std::shared_ptr<Value> make_native_function(Loc const& loc,
|
static std::shared_ptr<Value> make_native_function(Loc const& loc,
|
||||||
native_t val);
|
native_t val);
|
||||||
|
static std::shared_ptr<Value> make_function(Loc const& loc,
|
||||||
|
std::shared_ptr<Program> val);
|
||||||
static std::shared_ptr<Value> make_ref(Loc const& loc,
|
static std::shared_ptr<Value> make_ref(Loc const& loc,
|
||||||
size_t val);
|
size_t val);
|
||||||
|
|
||||||
|
static std::shared_ptr<Value> make_program(Loc const& loc,
|
||||||
|
std::shared_ptr<Program> val);
|
||||||
|
|
||||||
explicit Value(Loc const& loc);
|
explicit Value(Loc const& loc);
|
||||||
virtual ~Value() = default;
|
virtual ~Value() = default;
|
||||||
|
|
||||||
|
@ -28,6 +35,7 @@ namespace grino
|
||||||
int as_int() const { return *m_int_val; }
|
int as_int() const { return *m_int_val; }
|
||||||
std::shared_ptr<Function> as_function() const { return m_function_val; }
|
std::shared_ptr<Function> as_function() const { return m_function_val; }
|
||||||
size_t as_ref() const { return *m_ref_val; }
|
size_t as_ref() const { return *m_ref_val; }
|
||||||
|
std::shared_ptr<Program> as_program() const;
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
bool equals(Value const& other) const;
|
bool equals(Value const& other) const;
|
||||||
|
@ -38,6 +46,7 @@ namespace grino
|
||||||
std::optional<bool> m_bool_val;
|
std::optional<bool> m_bool_val;
|
||||||
std::optional<int> m_int_val;
|
std::optional<int> m_int_val;
|
||||||
std::shared_ptr<Function> m_function_val;
|
std::shared_ptr<Function> m_function_val;
|
||||||
|
std::shared_ptr<Program> m_program_val;
|
||||||
std::optional<size_t> m_ref_val;
|
std::optional<size_t> m_ref_val;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -36,14 +36,14 @@ void run(char** argv, bool debug_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
grino::SymTable sym_table {logger};
|
grino::SymTable sym_table {logger};
|
||||||
grino::VM vm {logger};
|
grino::Program program;
|
||||||
grino::Compiler compiler {logger, sym_table};
|
grino::VM vm {logger, program};
|
||||||
|
grino::Compiler compiler {logger};
|
||||||
|
|
||||||
grino::Loader loader {vm, compiler, sym_table};
|
grino::Loader loader {vm, compiler, sym_table};
|
||||||
loader.load_libraries();
|
loader.load_libraries();
|
||||||
|
|
||||||
grino::Program program;
|
compiler.compile(ast, program, sym_table);
|
||||||
compiler.compile(ast, program);
|
|
||||||
|
|
||||||
if (debug_mode)
|
if (debug_mode)
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@ void run(char** argv, bool debug_mode)
|
||||||
std::cout << program.string() << std::endl;
|
std::cout << program.string() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.run(program);
|
vm.run();
|
||||||
|
|
||||||
if (debug_mode)
|
if (debug_mode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
G(OPCODE_BRF), \
|
G(OPCODE_BRF), \
|
||||||
G(OPCODE_BR), \
|
G(OPCODE_BR), \
|
||||||
G(OPCODE_NOT), \
|
G(OPCODE_NOT), \
|
||||||
G(OPCODE_RET),
|
G(OPCODE_RET), \
|
||||||
|
G(OPCODE_MK_FUN),
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
G(TYPE_BOOL), \
|
G(TYPE_BOOL), \
|
||||||
G(TYPE_INT), \
|
G(TYPE_INT), \
|
||||||
G(TYPE_FUNCTION), \
|
G(TYPE_FUNCTION), \
|
||||||
G(TYPE_REF)
|
G(TYPE_REF), \
|
||||||
|
G(TYPE_PROGRAM)
|
||||||
|
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
|
|
@ -90,3 +90,12 @@ TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
||||||
test_next(lexer, "INT[-7]");
|
test_next(lexer, "INT[-7]");
|
||||||
test_end(lexer);
|
test_end(lexer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_lambda")
|
||||||
|
{
|
||||||
|
grino::Lexer lexer {m_logger, "tests/lexer"};
|
||||||
|
|
||||||
|
lexer.scan(" -> ");
|
||||||
|
test_next(lexer, "LAMBDA");
|
||||||
|
test_end(lexer);
|
||||||
|
}
|
||||||
|
|
|
@ -54,3 +54,23 @@ TEST_CASE_METHOD(ParserTest, "Parser_integer")
|
||||||
test_parse("MODULE(FUNCALL(IDENT[hello],INT[2],INT[34]))",
|
test_parse("MODULE(FUNCALL(IDENT[hello],INT[2],INT[34]))",
|
||||||
"(hello 2 34)");
|
"(hello 2 34)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(IDENT[y])))",
|
||||||
|
"(-> (x y) y)");
|
||||||
|
|
||||||
|
test_parse("MODULE(FUNCALL(LAMBDA(PARAMS("
|
||||||
|
"IDENT[x]"
|
||||||
|
"),BODY("
|
||||||
|
"IDENT[x]"
|
||||||
|
")),INT[4]))",
|
||||||
|
"( (-> (x) x ) 4 )");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue