diff --git a/doc/grammar.bnf b/doc/grammar.bnf index cb7e123..90a533d 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -5,5 +5,9 @@ EXPR ::= | ident | VARDECL | FUNCALL +| LAMBDA 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* diff --git a/examples/fun.gri b/examples/fun.gri new file mode 100644 index 0000000..13ee527 --- /dev/null +++ b/examples/fun.gri @@ -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 )) \ No newline at end of file diff --git a/lib/core.cpp b/lib/core.cpp index 2c74d51..3e756d1 100644 --- a/lib/core.cpp +++ b/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()); }); - loader.add_static("and", [](auto& compiler, auto node, auto& program){ + loader.add_static("and", [](auto& compiler, auto node, + auto& program, + auto& sym){ std::vector to_false; for (size_t i=1; isize(); i++) { - compiler.compile(node->child(i).lock(), program); + compiler.compile(node->child(i).lock(), program, sym); to_false.push_back(program.size()); 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()); }); - loader.add_static("or", [](auto& compiler, auto node, auto& program){ + loader.add_static("or", [](auto& compiler, auto node, + auto& program, auto& sym){ std::vector to_true; for (size_t i=1; isize(); i++) { - compiler.compile(node->child(i).lock(), program); + compiler.compile(node->child(i).lock(), program, sym); program.push_instr(grino::OPCODE_NOT); to_true.push_back(program.size()); program.push_instr(grino::OPCODE_BRF, 0 /* to true */); diff --git a/src/Compiler.cpp b/src/Compiler.cpp index 3e1bd4a..a2cd7c4 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -6,10 +6,10 @@ namespace grino { - /*explicit*/ Compiler::Compiler(Logger& logger, SymTable& sym) + /*explicit*/ Compiler::Compiler(Logger& logger) : m_logger { logger } - , m_sym { sym } { + enter_scope(); } /*virtual*/ Compiler::~Compiler() @@ -17,18 +17,31 @@ namespace grino } void Compiler::compile(std::shared_ptr node, - Program& program) + Program& program, + SymTable& sym) { switch (node->type()) { case NODE_MODULE: { for (size_t i=0; isize(); i++) { - compile(node->child(i).lock(), program); + compile(node->child(i).lock(), program, sym); program.push_instr(OPCODE_POP); } } break; + case NODE_BODY: { + for (size_t i=0; isize(); i++) + { + compile(node->child(i).lock(), program, sym); + + if (i < node->size() - 1) + { + program.push_instr(OPCODE_POP); + } + } + } break; + case NODE_FUNCALL: { std::string ident = node->child(0).lock()->repr(); @@ -36,13 +49,13 @@ namespace grino itr != std::end(m_statics)) { auto fun = itr->second; - fun->call(*this, node, program); + fun->call(*this, node, program, sym); } else { for (size_t i=0; isize(); i++) { - compile(node->child(i).lock(), program); + compile(node->child(i).lock(), program, sym); } program.push_instr(OPCODE_CALL, node->size() - 1); @@ -50,6 +63,32 @@ namespace grino } break; + case NODE_LAMBDA: { + auto params = node->child(0).lock(); + + enter_scope(); + + for (size_t i=0; isize(); 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(); + 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: { std::string repr = node->repr(); auto value = Value::make_bool(node->loc(), repr == "true"); @@ -71,15 +110,15 @@ namespace grino auto expr = node->child(1).lock(); 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); } break; case NODE_IDENT: { std::string ident = node->repr(); - auto entry = m_sym.find(ident); + auto entry = sym.find(ident, m_scope.size()); if (entry == std::nullopt) { @@ -114,8 +153,18 @@ namespace grino size_t Compiler::get_local_address() { - static size_t addr = 0; - addr++; - return addr - 1; + m_scope.back() += 1; + return m_scope.back() - 1; + } + + void Compiler::enter_scope() + { + m_scope.push_back(0); + } + + void Compiler::leave_scope() + { + assert(m_scope.empty() == false); + m_scope.pop_back(); } } diff --git a/src/Compiler.hpp b/src/Compiler.hpp index 7c11dbf..523a486 100644 --- a/src/Compiler.hpp +++ b/src/Compiler.hpp @@ -17,22 +17,26 @@ namespace grino class Compiler { public: - explicit Compiler(Logger& logger, SymTable& sym); + explicit Compiler(Logger& logger); virtual ~Compiler(); void compile(std::shared_ptr node, - Program& program); + Program& program, + SymTable& sym); void add_static_func(std::string const& name, std::shared_ptr fun); private: Logger& m_logger; - SymTable& m_sym; std::unordered_map> m_statics; + std::vector m_scope; + size_t get_local_address(); + void enter_scope(); + void leave_scope(); }; } diff --git a/src/Function.cpp b/src/Function.cpp index 02af6dc..37d40f7 100644 --- a/src/Function.cpp +++ b/src/Function.cpp @@ -1,5 +1,6 @@ #include "Function.hpp" #include "Value.hpp" +#include "Program.hpp" namespace grino { @@ -8,10 +9,25 @@ namespace grino { } + /*explicit*/ Function::Function(std::shared_ptr prog) + : m_prog { prog } + { + } + /*virtual*/ Function::~Function() { } + bool Function::is_native() const + { + return m_native != nullptr; + } + + std::shared_ptr Function::program() const + { + return m_prog; + } + value_t Function::call(args_t args) { assert(m_native); diff --git a/src/Function.hpp b/src/Function.hpp index 1ac67ae..8b71d55 100644 --- a/src/Function.hpp +++ b/src/Function.hpp @@ -6,6 +6,7 @@ namespace grino { class Value; + class Program; using value_t = std::shared_ptr; using args_t = std::vector; @@ -15,12 +16,16 @@ namespace grino { public: explicit Function(native_t native); + explicit Function(std::shared_ptr prog); virtual ~Function(); + bool is_native() const; + std::shared_ptr program() const; value_t call(args_t args); private: native_t m_native; + std::shared_ptr m_prog; }; } diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 3db6e15..5ad60c0 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -11,6 +11,7 @@ namespace grino add_text(NODE_CPAR, ")", false); add_text(NODE_DECL, "$", false); + add_keyword(NODE_LAMBDA, "->", false); add_keyword(NODE_BOOL, "true", true); add_keyword(NODE_BOOL, "false", true); diff --git a/src/Loader.cpp b/src/Loader.cpp index b3be7ea..ac2c0bd 100644 --- a/src/Loader.cpp +++ b/src/Loader.cpp @@ -43,7 +43,7 @@ namespace grino grino::Loc loc {"???", 0}; 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) diff --git a/src/Node.hpp b/src/Node.hpp index 513bfc3..85186dd 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -13,7 +13,10 @@ G(NODE_IDENT), \ G(NODE_OPAR), \ G(NODE_CPAR), \ - G(NODE_DECL), + G(NODE_DECL), \ + G(NODE_LAMBDA), \ + G(NODE_PARAMS), \ + G(NODE_BODY), namespace grino { diff --git a/src/Parser.cpp b/src/Parser.cpp index 7728655..00f9d0e 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,4 +1,5 @@ #include "Parser.hpp" +#include "src/Node.hpp" #include "src/mutils.hpp" namespace grino @@ -117,16 +118,21 @@ namespace grino std::shared_ptr Parser::parse_expr() { - if (type_is({NODE_OPAR, NODE_IDENT})) - { - return parse_funcall(); - } - if (type_is({NODE_OPAR, NODE_DECL})) { 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) || type_is(NODE_BOOL) || type_is(NODE_INT)) @@ -163,7 +169,7 @@ namespace grino consume(NODE_OPAR); auto node = make_node(NODE_FUNCALL); - node->add_child(consume(NODE_IDENT)); + node->add_child(parse_expr()); while (!type_is(NODE_CPAR)) { @@ -174,4 +180,44 @@ namespace grino return node; } + + std::shared_ptr 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 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 Parser::parse_body() + { + auto node = make_node(NODE_BODY); + + while (!type_is(NODE_CPAR)) + { + node->add_child(parse_expr()); + } + + return node; + } + } diff --git a/src/Parser.hpp b/src/Parser.hpp index 54891a5..29041c6 100644 --- a/src/Parser.hpp +++ b/src/Parser.hpp @@ -36,6 +36,9 @@ namespace grino std::shared_ptr parse_expr(); std::shared_ptr parse_vardecl(); std::shared_ptr parse_funcall(); + std::shared_ptr parse_lambda(); + std::shared_ptr parse_params(); + std::shared_ptr parse_body(); }; } diff --git a/src/Program.cpp b/src/Program.cpp index c76053c..c3fb236 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -54,6 +54,12 @@ namespace grino return m_constants[index]; } + void Program::push_value(std::shared_ptr value) + { + size_t addr = push_constant(value); + push_instr(OPCODE_LOAD_CONST, addr); + } + std::string Program::string() const { std::stringstream ss; diff --git a/src/Program.hpp b/src/Program.hpp index 7caf4ac..2c8bb22 100644 --- a/src/Program.hpp +++ b/src/Program.hpp @@ -31,6 +31,8 @@ namespace grino size_t push_constant(std::shared_ptr value); std::shared_ptr constant(size_t index) const; + void push_value(std::shared_ptr value); + std::string string() const; private: diff --git a/src/StaticFunction.cpp b/src/StaticFunction.cpp index cb73e6d..d7157a8 100644 --- a/src/StaticFunction.cpp +++ b/src/StaticFunction.cpp @@ -14,8 +14,9 @@ namespace grino void StaticFunction::call(Compiler& compiler, node_t node, - prog_t prog) + prog_t prog, + SymTable& sym) { - m_fun(compiler, node, prog); + m_fun(compiler, node, prog,sym); } } diff --git a/src/StaticFunction.hpp b/src/StaticFunction.hpp index 3d76886..9905f5d 100644 --- a/src/StaticFunction.hpp +++ b/src/StaticFunction.hpp @@ -5,13 +5,15 @@ #include "Function.hpp" #include "Node.hpp" #include "Program.hpp" +#include "SymTable.hpp" namespace grino { class Compiler; using node_t = std::shared_ptr; using prog_t = Program&; - using static_fun_t = std::function; + using sym_t = SymTable&; + using static_fun_t = std::function; class StaticFunction { @@ -19,7 +21,7 @@ namespace grino explicit StaticFunction(static_fun_t fun); 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: static_fun_t m_fun; diff --git a/src/SymTable.cpp b/src/SymTable.cpp index 9d0df11..d34a65e 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -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(LOG_ERROR, loc, "'" + name @@ -24,29 +26,46 @@ namespace grino entry.addr = addr; entry.name = name; entry.is_object = false; + entry.scope = scope; m_entries.push_back(entry); } void SymTable::declare_object(Loc const& loc, 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; } - std::optional SymTable::find(std::string const& name) + std::optional SymTable::find(std::string const& name, size_t scope) { + std::optional entry; + for (size_t i=0; i 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 diff --git a/src/SymTable.hpp b/src/SymTable.hpp index afbeb29..b5847cc 100644 --- a/src/SymTable.hpp +++ b/src/SymTable.hpp @@ -10,6 +10,7 @@ namespace grino std::string name; 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 scope; }; GRINO_ERROR(symbolic_error); @@ -20,10 +21,15 @@ namespace grino explicit SymTable(Logger& logger); virtual ~SymTable(); - 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); + void declare(Loc const& loc, std::string const& name, size_t addr, + size_t scope); - std::optional find(std::string const& name); + void declare_object(Loc const& loc, std::string const& name, size_t addr, + size_t scope); + + std::optional find(std::string const& name, size_t scope); + + void purge(size_t scope); std::string string() const; diff --git a/src/VM.cpp b/src/VM.cpp index 743d508..68fcaf8 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -1,33 +1,52 @@ #include "VM.hpp" #include "src/opcodes.hpp" +#include namespace grino { - /*explicit*/ VM::VM(Logger& logger) + /*explicit*/ VM::VM(Logger& logger, Program& program) : 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() { } - void VM::run(Program& program) + void VM::run() { m_bp = 0; m_sp = 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) { + 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: { - auto val = program.constant(pop()); - push(program.push_constant(Value::make_bool(val->loc(), + auto val = program().constant(pop()); + push(program().push_constant(Value::make_bool(val->loc(), !val->as_bool()))); m_pc++; } break; @@ -37,7 +56,7 @@ namespace grino } break; case OPCODE_BRF: { - auto val = program.constant(pop())->as_bool(); + auto val = program().constant(pop())->as_bool(); size_t addr = *instr.param; if (!val) @@ -62,21 +81,39 @@ namespace grino case OPCODE_STORE_LOCAL: { size_t addr = *instr.param; - auto value = program.constant(top()); + auto value = program().constant(top()); set_local(addr, value); m_pc++; } break; case OPCODE_LOAD_LOCAL: { size_t addr = *instr.param; - push(program.push_constant(local(addr))); + push(program().push_constant(local(addr))); m_pc++; } break; case OPCODE_LOAD_OBJ: { size_t addr = *instr.param; 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++; } break; @@ -87,18 +124,33 @@ namespace grino for (size_t i=0; ias_ref(); + size_t ref = program().constant(pop())->as_ref(); auto fun = heap(ref)->as_function(); - auto ret = fun->call(args); + if (fun->is_native()) + { + auto ret = fun->call(args); + push(program().push_constant(ret)); + m_pc++; + } + else + { + Frame frame; + frame.pc = m_pc; + frame.sp = m_sp; + frame.program = fun->program().get(); + m_frames.push_back(frame); - push(program.push_constant(ret)); - - m_pc++; + for (size_t i=0; i> locals; }; class VM { public: - explicit VM(Logger& logger); + explicit VM(Logger& logger, Program& program); virtual ~VM(); size_t heap_size() const { return m_heap.size(); } - void run(Program& program); + void run(); std::string string() const; @@ -36,8 +39,10 @@ namespace grino std::shared_ptr value); private: Logger& m_logger; + std::array m_stack; std::unordered_map> m_heap; + std::vector m_frames; size_t m_sp; /* stack pointer */ @@ -48,6 +53,7 @@ namespace grino size_t pop(); size_t top(); + Program& program() const; }; } diff --git a/src/Value.cpp b/src/Value.cpp index 55e0c6e..17307e2 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -1,4 +1,5 @@ #include "Value.hpp" +#include "Program.hpp" namespace grino { @@ -35,6 +36,16 @@ namespace grino return value; } + /*static*/ std::shared_ptr + Value::make_function(Loc const& loc, + std::shared_ptr val) + { + auto value = std::make_shared(loc); + value->m_type = TYPE_FUNCTION; + value->m_function_val = std::make_shared(val); + return value; + } + /*static*/ std::shared_ptr Value::make_ref(Loc const& loc, size_t val) { auto value = std::make_shared(loc); @@ -43,6 +54,21 @@ namespace grino return value; } + /*static*/ + std::shared_ptr Value::make_program(Loc const& loc, + std::shared_ptr val) + { + auto value = std::make_shared(loc); + value->m_type = TYPE_PROGRAM; + value->m_program_val = val; + return value; + } + + std::shared_ptr Value::as_program() const + { + return m_program_val; + } + std::string Value::string() const { switch (m_type) @@ -52,6 +78,7 @@ namespace grino case TYPE_BOOL: return *m_bool_val ? "true" : "false"; case TYPE_FUNCTION: return ""; case TYPE_REF: return "&" + std::to_string(*m_ref_val); + case TYPE_PROGRAM: return ""; default: std::cerr << "cannot stringify value " << TypeTypeStr[m_type] << std::endl; @@ -69,6 +96,7 @@ namespace grino case TYPE_BOOL: return *m_bool_val == *other.m_bool_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_PROGRAM: return false; default: std::cerr << "cannot compare equality with value " diff --git a/src/Value.hpp b/src/Value.hpp index 6d5f8bd..95ef1c4 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -8,6 +8,8 @@ namespace grino { + class Program; + class Value { public: @@ -16,9 +18,14 @@ namespace grino static std::shared_ptr make_int(Loc const& loc, int val); static std::shared_ptr make_native_function(Loc const& loc, native_t val); + static std::shared_ptr make_function(Loc const& loc, + std::shared_ptr val); static std::shared_ptr make_ref(Loc const& loc, size_t val); + static std::shared_ptr make_program(Loc const& loc, + std::shared_ptr val); + explicit Value(Loc const& loc); virtual ~Value() = default; @@ -28,6 +35,7 @@ namespace grino int as_int() const { return *m_int_val; } std::shared_ptr as_function() const { return m_function_val; } size_t as_ref() const { return *m_ref_val; } + std::shared_ptr as_program() const; std::string string() const; bool equals(Value const& other) const; @@ -38,6 +46,7 @@ namespace grino std::optional m_bool_val; std::optional m_int_val; std::shared_ptr m_function_val; + std::shared_ptr m_program_val; std::optional m_ref_val; }; } diff --git a/src/main.cpp b/src/main.cpp index 09731c3..342c882 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,14 +36,14 @@ void run(char** argv, bool debug_mode) } grino::SymTable sym_table {logger}; - grino::VM vm {logger}; - grino::Compiler compiler {logger, sym_table}; + grino::Program program; + grino::VM vm {logger, program}; + grino::Compiler compiler {logger}; grino::Loader loader {vm, compiler, sym_table}; loader.load_libraries(); - grino::Program program; - compiler.compile(ast, program); + compiler.compile(ast, program, sym_table); if (debug_mode) { @@ -51,7 +51,7 @@ void run(char** argv, bool debug_mode) std::cout << program.string() << std::endl; } - vm.run(program); + vm.run(); if (debug_mode) { diff --git a/src/opcodes.hpp b/src/opcodes.hpp index 1211d82..d852cae 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -15,7 +15,8 @@ G(OPCODE_BRF), \ G(OPCODE_BR), \ G(OPCODE_NOT), \ - G(OPCODE_RET), + G(OPCODE_RET), \ + G(OPCODE_MK_FUN), namespace grino { diff --git a/src/types.hpp b/src/types.hpp index 8a0cf5f..fa6a732 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -8,7 +8,9 @@ G(TYPE_BOOL), \ G(TYPE_INT), \ G(TYPE_FUNCTION), \ - G(TYPE_REF) + G(TYPE_REF), \ + G(TYPE_PROGRAM) + namespace grino { diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index ccab9c0..3bc306d 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -90,3 +90,12 @@ TEST_CASE_METHOD(LexerTest, "Lexer_int") test_next(lexer, "INT[-7]"); 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); +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index f01921c..e69fcd4 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -54,3 +54,23 @@ TEST_CASE_METHOD(ParserTest, "Parser_integer") test_parse("MODULE(FUNCALL(IDENT[hello],INT[2],INT[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 )"); + +}