diff --git a/doc/grammar.bnf b/doc/grammar.bnf index ff5eff2..3c3a2be 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -8,6 +8,7 @@ EXPR ::= | FUNCALL | LAMBDA | BLOCK +| ARRAY VARDECL ::= opar decl ident EXPR cpar FUNDECL ::= opar decl opar ident* cpar BODY cpar FUNCALL ::= opar EXPR EXPR* cpar @@ -15,3 +16,4 @@ LAMBDA ::= opar lambda opar PARAMS cpar BODY cpar PARAMS ::= ident* BODY ::= EXPR* BLOCK ::= opar colon EXPR* cpar +ARRAY ::= osquare EXPR* csquare diff --git a/examples/array.gri b/examples/array.gri new file mode 100644 index 0000000..c5ae1fe --- /dev/null +++ b/examples/array.gri @@ -0,0 +1,30 @@ +($ a [1 true 2]) +(assert-eq? 1 (ref a 0)) +(assert-eq? true (ref a 1)) +(assert-eq? 2 (ref a 2)) + +($ b [[2 4 6] [8 10 12] [14 16 18]]) +(assert-eq? 12 (ref b 1 2)) + +(assert-eq? true (empty? [])) +(assert-eq? false (empty? [1])) + +(assert-eq? 7 (head [7 3 1])) +(assert-eq? 3 (ref (tail [7 3 1]) 0)) +(assert-eq? 1 (ref (tail [7 3 1]) 1)) + +($ (c arr) + (if (empty? arr) + 0 + (+ (head arr) (c (tail arr))))) + +(assert-eq? 13 (c [2 4 6 1])) + +($ d (cons 2 (cons 4 [6]))) + +(assert-eq? 2 (ref d 0)) +(assert-eq? 4 (ref d 1)) +(assert-eq? 6 (ref d 2)) + +(assert-eq? 3 (len d)) +(assert-eq? 4 (len (cons 18 d))) diff --git a/lib/core.cpp b/lib/core.cpp index e67810e..72ffe3d 100644 --- a/lib/core.cpp +++ b/lib/core.cpp @@ -5,6 +5,99 @@ GRINO_ERROR(assertion_error); +extern "C" void lib_array(grino::Loader& loader) +{ + loader.add_native("ref", [&loader](auto args){ + std::vector idxs; + for (size_t i=1; ias_int()); + } + + std::function + (std::shared_ptr, std::vector)> + f = [&](std::shared_ptr val, std::vector indexes){ + if (indexes.empty()) + { + return val; + } + + size_t index = indexes[0]; + std::vector next; + + for (size_t i=1; ias_ref())->as_array()[index], next); + }; + + return f(args[0], idxs); + }); + + loader.add_native("empty?", [&loader](auto args){ + auto ref_val = args[0]; + size_t ref = ref_val->as_ref(); + auto array_val = loader.vm().heap(ref); + auto& array = array_val->as_array(); + + return grino::Value::make_bool(array_val->loc(), array.empty()); + }); + + loader.add_native("head", [&loader](auto args){ + auto ref_val = args[0]; + size_t ref = ref_val->as_ref(); + auto array_val = loader.vm().heap(ref); + auto& array = array_val->as_array(); + + return array[0]; + }); + + loader.add_native("tail", [&loader](auto args){ + auto ref_val = args[0]; + size_t ref = ref_val->as_ref(); + auto array_val = loader.vm().heap(ref); + auto& array = array_val->as_array(); + + grino::val_array_t data; + for (size_t i=1; iloc(), data); + size_t addr = loader.vm().heap_size(); + loader.vm().set_heap(addr, res); + + return grino::Value::make_ref(array_val->loc(), addr); + }); + + loader.add_native("cons", [&loader](auto args){ + auto ref_val = args[1]; + size_t ref = ref_val->as_ref(); + auto array_val = loader.vm().heap(ref); + auto& array = array_val->as_array(); + + array.insert(std::begin(array), args[0]); + + auto res = grino::Value::make_array(array_val->loc(), array); + size_t addr = loader.vm().heap_size(); + loader.vm().set_heap(addr, res); + + return grino::Value::make_ref(array_val->loc(), addr); + }); + + loader.add_native("len", [&loader](auto args){ + auto ref_val = args[0]; + size_t ref = ref_val->as_ref(); + auto array_val = loader.vm().heap(ref); + auto& array = array_val->as_array(); + + return grino::Value::make_int(array_val->loc(), array.size()); + }); +} + extern "C" void lib_flow_control(grino::Loader& loader) { loader.add_static("if", [](auto& compiler, auto node, auto& prog, auto& sym) { @@ -330,6 +423,7 @@ extern "C" void lib(grino::Loader& loader) lib_cmp(loader); lib_bool(loader); lib_flow_control(loader); + lib_array(loader); loader.add_native("dump", [](auto args){ std::string sep; diff --git a/src/Compiler.cpp b/src/Compiler.cpp index 69e92e1..fd851f0 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -128,6 +128,15 @@ namespace grino program.push_constant(value)); } break; + case NODE_ARRAY: { + for (size_t i=0; isize(); i++) + { + compile(node->child(i).lock(), program, sym); + } + + program.push_instr(OPCODE_MK_ARRAY, node->size()); + } break; + case NODE_INT: { std::string repr = node->repr(); auto value = Value::make_int(node->loc(), std::stoi(repr)); diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 544f17a..59f8e69 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -8,6 +8,8 @@ namespace grino , m_loc {source_path, 1} { add_text(NODE_COLON, ":", false); + add_text(NODE_OSQUARE, "[", false); + add_text(NODE_CSQUARE, "]", false); add_text(NODE_OPAR, "(", false); add_text(NODE_CPAR, ")", false); add_text(NODE_DECL, "$", false); diff --git a/src/Loader.hpp b/src/Loader.hpp index 944d235..7da623f 100644 --- a/src/Loader.hpp +++ b/src/Loader.hpp @@ -15,6 +15,8 @@ namespace grino explicit Loader(VM& vm, Compiler& compiler, SymTable& sym_table); virtual ~Loader(); + VM& vm() const { return m_vm; } + void load_libraries(); void load_library(std::filesystem::path path); diff --git a/src/Node.hpp b/src/Node.hpp index 6f03099..c960a49 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -18,7 +18,10 @@ G(NODE_PARAMS), \ G(NODE_BODY), \ G(NODE_COLON), \ - G(NODE_BLOCK) + G(NODE_OSQUARE), \ + G(NODE_CSQUARE), \ + G(NODE_BLOCK), \ + G(NODE_ARRAY) namespace grino { diff --git a/src/Parser.cpp b/src/Parser.cpp index 1749e7c..e899506 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -118,6 +118,11 @@ namespace grino std::shared_ptr Parser::parse_expr() { + if (type_is(NODE_OSQUARE)) + { + return parse_array(); + } + if (type_is({NODE_OPAR, NODE_COLON})) { return parse_block(); @@ -272,4 +277,19 @@ namespace grino return node; } + std::shared_ptr Parser::parse_array() + { + auto node = make_node(NODE_ARRAY); + + consume(NODE_OSQUARE); + + while (!type_is(NODE_CSQUARE)) + { + node->add_child(parse_expr()); + } + + consume(NODE_CSQUARE); + + return node; + } } diff --git a/src/Parser.hpp b/src/Parser.hpp index e21f8bf..28b3baa 100644 --- a/src/Parser.hpp +++ b/src/Parser.hpp @@ -41,6 +41,7 @@ namespace grino std::shared_ptr parse_params(); std::shared_ptr parse_body(); std::shared_ptr parse_block(); + std::shared_ptr parse_array(); }; } diff --git a/src/VM.cpp b/src/VM.cpp index 395eb91..3744d0e 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -1,4 +1,5 @@ #include "VM.hpp" +#include "src/Value.hpp" #include "src/opcodes.hpp" #include @@ -31,6 +32,34 @@ namespace grino switch (instr.opcode) { + case OPCODE_MK_ARRAY: { + size_t const N = *instr.param; + + val_array_t array; + + for (size_t i=0; i 0) + { + loc = array[0]->loc(); + } + + auto array_val = Value::make_array(loc, array); + size_t addr = heap_size(); + set_heap(addr, array_val); + + auto ref = Value::make_ref(loc, addr); + push(program().push_constant(ref)); + + m_pc++; + + } break; + case OPCODE_MK_FUN: { auto prog_val = program().constant(pop()); auto prog = prog_val->as_program(); diff --git a/src/Value.cpp b/src/Value.cpp index 4f2c471..3deb0d8 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -86,6 +86,15 @@ namespace grino } } + /*static*/ + std::shared_ptr Value::make_array(Loc const& loc, + val_array_t val) + { + auto value = std::make_shared(loc); + value->m_type = TYPE_ARRAY; + value->m_array_val = val; + return value; + } std::shared_ptr Value::as_program() const { @@ -102,6 +111,19 @@ namespace grino case TYPE_FUNCTION: return ""; case TYPE_REF: return "&" + std::to_string(*m_ref_val); case TYPE_PROGRAM: return ""; + case TYPE_ARRAY: { + std::stringstream ss; + ss << "["; + std::string sep; + for (auto child: *m_array_val) + { + ss << sep << child->string(); + sep = " "; + } + ss << "]"; + return ss.str(); + } break; + default: std::cerr << "cannot stringify value " << TypeTypeStr[m_type] << std::endl; diff --git a/src/Value.hpp b/src/Value.hpp index 90318ed..4b5662e 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -10,6 +10,8 @@ namespace grino { class Program; + using val_array_t = std::vector>; + class Value { public: @@ -31,6 +33,9 @@ namespace grino static std::shared_ptr make_copy(Loc const& loc, std::shared_ptr val); + static std::shared_ptr make_array(Loc const& loc, + val_array_t val); + explicit Value(Loc const& loc); virtual ~Value() = default; @@ -41,6 +46,8 @@ namespace grino 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; + val_array_t& as_array() { return *m_array_val; } + val_array_t const& as_array() const { return *m_array_val; } std::string string() const; bool equals(Value const& other) const; @@ -53,6 +60,7 @@ namespace grino std::shared_ptr m_function_val; std::shared_ptr m_program_val; std::optional m_ref_val; + std::optional m_array_val; }; } diff --git a/src/opcodes.hpp b/src/opcodes.hpp index d852cae..353e721 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -16,7 +16,8 @@ G(OPCODE_BR), \ G(OPCODE_NOT), \ G(OPCODE_RET), \ - G(OPCODE_MK_FUN), + G(OPCODE_MK_FUN), \ + G(OPCODE_MK_ARRAY), namespace grino { diff --git a/src/types.hpp b/src/types.hpp index fa6a732..a91dc00 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -9,7 +9,8 @@ G(TYPE_INT), \ G(TYPE_FUNCTION), \ G(TYPE_REF), \ - G(TYPE_PROGRAM) + G(TYPE_PROGRAM), \ + G(TYPE_ARRAY) namespace grino diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index f0d34b5..fe8f788 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -108,3 +108,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_block") test_next(lexer, "COLON"); test_end(lexer); } + +TEST_CASE_METHOD(LexerTest, "Lexer_array") +{ + grino::Lexer lexer {m_logger, "tests/lexer"}; + + lexer.scan(" [] "); + test_next(lexer, "OSQUARE"); + test_next(lexer, "CSQUARE"); + test_end(lexer); +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index 3f9f85d..5136bf4 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -87,3 +87,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_block") test_parse("MODULE(BLOCK(INT[1],BOOL[true],IDENT[salut]))", "(: 1 true salut )"); } + +TEST_CASE_METHOD(ParserTest, "Parser_array") +{ + test_parse("MODULE(BLOCK(ARRAY(INT[1],BOOL[true],IDENT[salut])))", + "(: [1 true salut] )"); +}