ADD: arrays.
parent
8b5b243cab
commit
d8025d967f
|
@ -8,6 +8,7 @@ EXPR ::=
|
||||||
| FUNCALL
|
| FUNCALL
|
||||||
| LAMBDA
|
| LAMBDA
|
||||||
| BLOCK
|
| BLOCK
|
||||||
|
| ARRAY
|
||||||
VARDECL ::= opar decl ident EXPR cpar
|
VARDECL ::= opar decl ident EXPR cpar
|
||||||
FUNDECL ::= opar decl opar ident* cpar BODY cpar
|
FUNDECL ::= opar decl opar ident* cpar BODY cpar
|
||||||
FUNCALL ::= opar EXPR EXPR* cpar
|
FUNCALL ::= opar EXPR EXPR* cpar
|
||||||
|
@ -15,3 +16,4 @@ LAMBDA ::= opar lambda opar PARAMS cpar BODY cpar
|
||||||
PARAMS ::= ident*
|
PARAMS ::= ident*
|
||||||
BODY ::= EXPR*
|
BODY ::= EXPR*
|
||||||
BLOCK ::= opar colon EXPR* cpar
|
BLOCK ::= opar colon EXPR* cpar
|
||||||
|
ARRAY ::= osquare EXPR* csquare
|
||||||
|
|
|
@ -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)))
|
94
lib/core.cpp
94
lib/core.cpp
|
@ -5,6 +5,99 @@
|
||||||
|
|
||||||
GRINO_ERROR(assertion_error);
|
GRINO_ERROR(assertion_error);
|
||||||
|
|
||||||
|
extern "C" void lib_array(grino::Loader& loader)
|
||||||
|
{
|
||||||
|
loader.add_native("ref", [&loader](auto args){
|
||||||
|
std::vector<size_t> idxs;
|
||||||
|
for (size_t i=1; i<args.size(); i++)
|
||||||
|
{
|
||||||
|
idxs.push_back(args[i]->as_int());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<std::shared_ptr<grino::Value>
|
||||||
|
(std::shared_ptr<grino::Value>, std::vector<size_t>)>
|
||||||
|
f = [&](std::shared_ptr<grino::Value> val, std::vector<size_t> indexes){
|
||||||
|
if (indexes.empty())
|
||||||
|
{
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = indexes[0];
|
||||||
|
std::vector<size_t> next;
|
||||||
|
|
||||||
|
for (size_t i=1; i<indexes.size(); i++)
|
||||||
|
{
|
||||||
|
next.push_back(indexes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return f(loader.vm().heap(val->as_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; i<array.size(); i++)
|
||||||
|
{
|
||||||
|
data.push_back(array[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = grino::Value::make_array(array_val->loc(), 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)
|
extern "C" void lib_flow_control(grino::Loader& loader)
|
||||||
{
|
{
|
||||||
loader.add_static("if", [](auto& compiler, auto node, auto& prog, auto& sym) {
|
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_cmp(loader);
|
||||||
lib_bool(loader);
|
lib_bool(loader);
|
||||||
lib_flow_control(loader);
|
lib_flow_control(loader);
|
||||||
|
lib_array(loader);
|
||||||
|
|
||||||
loader.add_native("dump", [](auto args){
|
loader.add_native("dump", [](auto args){
|
||||||
std::string sep;
|
std::string sep;
|
||||||
|
|
|
@ -128,6 +128,15 @@ namespace grino
|
||||||
program.push_constant(value));
|
program.push_constant(value));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_ARRAY: {
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile(node->child(i).lock(), program, sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
program.push_instr(OPCODE_MK_ARRAY, node->size());
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_INT: {
|
case NODE_INT: {
|
||||||
std::string repr = node->repr();
|
std::string repr = node->repr();
|
||||||
auto value = Value::make_int(node->loc(), std::stoi(repr));
|
auto value = Value::make_int(node->loc(), std::stoi(repr));
|
||||||
|
|
|
@ -8,6 +8,8 @@ namespace grino
|
||||||
, m_loc {source_path, 1}
|
, m_loc {source_path, 1}
|
||||||
{
|
{
|
||||||
add_text(NODE_COLON, ":", false);
|
add_text(NODE_COLON, ":", false);
|
||||||
|
add_text(NODE_OSQUARE, "[", false);
|
||||||
|
add_text(NODE_CSQUARE, "]", false);
|
||||||
add_text(NODE_OPAR, "(", false);
|
add_text(NODE_OPAR, "(", false);
|
||||||
add_text(NODE_CPAR, ")", false);
|
add_text(NODE_CPAR, ")", false);
|
||||||
add_text(NODE_DECL, "$", false);
|
add_text(NODE_DECL, "$", false);
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace grino
|
||||||
explicit Loader(VM& vm, Compiler& compiler, SymTable& sym_table);
|
explicit Loader(VM& vm, Compiler& compiler, SymTable& sym_table);
|
||||||
virtual ~Loader();
|
virtual ~Loader();
|
||||||
|
|
||||||
|
VM& vm() const { return m_vm; }
|
||||||
|
|
||||||
void load_libraries();
|
void load_libraries();
|
||||||
void load_library(std::filesystem::path path);
|
void load_library(std::filesystem::path path);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
G(NODE_PARAMS), \
|
G(NODE_PARAMS), \
|
||||||
G(NODE_BODY), \
|
G(NODE_BODY), \
|
||||||
G(NODE_COLON), \
|
G(NODE_COLON), \
|
||||||
G(NODE_BLOCK)
|
G(NODE_OSQUARE), \
|
||||||
|
G(NODE_CSQUARE), \
|
||||||
|
G(NODE_BLOCK), \
|
||||||
|
G(NODE_ARRAY)
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
|
|
@ -118,6 +118,11 @@ namespace grino
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
{
|
{
|
||||||
|
if (type_is(NODE_OSQUARE))
|
||||||
|
{
|
||||||
|
return parse_array();
|
||||||
|
}
|
||||||
|
|
||||||
if (type_is({NODE_OPAR, NODE_COLON}))
|
if (type_is({NODE_OPAR, NODE_COLON}))
|
||||||
{
|
{
|
||||||
return parse_block();
|
return parse_block();
|
||||||
|
@ -272,4 +277,19 @@ namespace grino
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ namespace grino
|
||||||
std::shared_ptr<Node> parse_params();
|
std::shared_ptr<Node> parse_params();
|
||||||
std::shared_ptr<Node> parse_body();
|
std::shared_ptr<Node> parse_body();
|
||||||
std::shared_ptr<Node> parse_block();
|
std::shared_ptr<Node> parse_block();
|
||||||
|
std::shared_ptr<Node> parse_array();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
src/VM.cpp
29
src/VM.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
|
#include "src/Value.hpp"
|
||||||
#include "src/opcodes.hpp"
|
#include "src/opcodes.hpp"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -31,6 +32,34 @@ namespace grino
|
||||||
|
|
||||||
switch (instr.opcode)
|
switch (instr.opcode)
|
||||||
{
|
{
|
||||||
|
case OPCODE_MK_ARRAY: {
|
||||||
|
size_t const N = *instr.param;
|
||||||
|
|
||||||
|
val_array_t array;
|
||||||
|
|
||||||
|
for (size_t i=0; i<N; i++)
|
||||||
|
{
|
||||||
|
array.insert(std::begin(array), program().constant(pop()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc loc {"???", 0};
|
||||||
|
|
||||||
|
if (N > 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: {
|
case OPCODE_MK_FUN: {
|
||||||
auto prog_val = program().constant(pop());
|
auto prog_val = program().constant(pop());
|
||||||
auto prog = prog_val->as_program();
|
auto prog = prog_val->as_program();
|
||||||
|
|
|
@ -86,6 +86,15 @@ namespace grino
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
std::shared_ptr<Value> Value::make_array(Loc const& loc,
|
||||||
|
val_array_t val)
|
||||||
|
{
|
||||||
|
auto value = std::make_shared<Value>(loc);
|
||||||
|
value->m_type = TYPE_ARRAY;
|
||||||
|
value->m_array_val = val;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Program> Value::as_program() const
|
std::shared_ptr<Program> Value::as_program() const
|
||||||
{
|
{
|
||||||
|
@ -102,6 +111,19 @@ namespace grino
|
||||||
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>";
|
case TYPE_PROGRAM: return "<program>";
|
||||||
|
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:
|
default:
|
||||||
std::cerr << "cannot stringify value "
|
std::cerr << "cannot stringify value "
|
||||||
<< TypeTypeStr[m_type] << std::endl;
|
<< TypeTypeStr[m_type] << std::endl;
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace grino
|
||||||
{
|
{
|
||||||
class Program;
|
class Program;
|
||||||
|
|
||||||
|
using val_array_t = std::vector<std::shared_ptr<Value>>;
|
||||||
|
|
||||||
class Value
|
class Value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -31,6 +33,9 @@ namespace grino
|
||||||
static std::shared_ptr<Value> make_copy(Loc const& loc,
|
static std::shared_ptr<Value> make_copy(Loc const& loc,
|
||||||
std::shared_ptr<Value> val);
|
std::shared_ptr<Value> val);
|
||||||
|
|
||||||
|
static std::shared_ptr<Value> make_array(Loc const& loc,
|
||||||
|
val_array_t val);
|
||||||
|
|
||||||
explicit Value(Loc const& loc);
|
explicit Value(Loc const& loc);
|
||||||
virtual ~Value() = default;
|
virtual ~Value() = default;
|
||||||
|
|
||||||
|
@ -41,6 +46,8 @@ namespace grino
|
||||||
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::shared_ptr<Program> 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;
|
std::string string() const;
|
||||||
bool equals(Value const& other) const;
|
bool equals(Value const& other) const;
|
||||||
|
@ -53,6 +60,7 @@ namespace grino
|
||||||
std::shared_ptr<Function> m_function_val;
|
std::shared_ptr<Function> m_function_val;
|
||||||
std::shared_ptr<Program> m_program_val;
|
std::shared_ptr<Program> m_program_val;
|
||||||
std::optional<size_t> m_ref_val;
|
std::optional<size_t> m_ref_val;
|
||||||
|
std::optional<val_array_t> m_array_val;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
G(OPCODE_BR), \
|
G(OPCODE_BR), \
|
||||||
G(OPCODE_NOT), \
|
G(OPCODE_NOT), \
|
||||||
G(OPCODE_RET), \
|
G(OPCODE_RET), \
|
||||||
G(OPCODE_MK_FUN),
|
G(OPCODE_MK_FUN), \
|
||||||
|
G(OPCODE_MK_ARRAY),
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
G(TYPE_INT), \
|
G(TYPE_INT), \
|
||||||
G(TYPE_FUNCTION), \
|
G(TYPE_FUNCTION), \
|
||||||
G(TYPE_REF), \
|
G(TYPE_REF), \
|
||||||
G(TYPE_PROGRAM)
|
G(TYPE_PROGRAM), \
|
||||||
|
G(TYPE_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
|
|
|
@ -108,3 +108,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_block")
|
||||||
test_next(lexer, "COLON");
|
test_next(lexer, "COLON");
|
||||||
test_end(lexer);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -87,3 +87,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_block")
|
||||||
test_parse("MODULE(BLOCK(INT[1],BOOL[true],IDENT[salut]))",
|
test_parse("MODULE(BLOCK(INT[1],BOOL[true],IDENT[salut]))",
|
||||||
"(: 1 true salut )");
|
"(: 1 true salut )");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_array")
|
||||||
|
{
|
||||||
|
test_parse("MODULE(BLOCK(ARRAY(INT[1],BOOL[true],IDENT[salut])))",
|
||||||
|
"(: [1 true salut] )");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue