Compare commits

...

6 Commits

Author SHA1 Message Date
bog 59af9d809e ADD: bool arithmetic and comparison operators. 2023-09-11 17:54:59 +02:00
bog bae09ec2f3 ADD: integers. 2023-09-11 15:59:37 +02:00
bog 690122ea4a ADD: test bash script. 2023-09-11 14:27:44 +02:00
bog 0795eb5d9f ADD: eq? and assert-eq? core functions. 2023-09-11 14:05:10 +02:00
bog 20b5e60557 ADD: assert core function. 2023-09-11 13:46:41 +02:00
bog 314fbc0bd6 ADD: native functions. 2023-09-11 12:14:01 +02:00
35 changed files with 1030 additions and 72 deletions

View File

@ -1,6 +1,9 @@
MODULE ::= EXPR*
EXPR ::=
bool
| int
| ident
| VARDECL
| FUNCALL
VARDECL ::= opar decl ident EXPR cpar
FUNCALL ::= opar ident EXPR* cpar

19
examples/bool.gri Normal file
View File

@ -0,0 +1,19 @@
(assert-eq? false (not true))
(assert-eq? true (not false))
(assert-eq? false (not (not false)))
(assert-eq? true (and true true))
(assert-eq? false (and true false))
(assert-eq? false (and false true))
(assert-eq? false (and false false))
(assert-eq? true (and true true true true true))
(assert-eq? false (and true true true true false))
(assert-eq? true (or true true))
(assert-eq? true (or true true))
(assert-eq? true (or true true))
(assert-eq? false (or false false))
(assert-eq? false (or false false false false false))
(assert-eq? true (or false false true false false))

47
examples/int.gri Normal file
View File

@ -0,0 +1,47 @@
(assert-eq? 7 7)
(assert-eq? 0 (+))
(assert-eq? 3 (+ 3))
(assert-eq? 5 (+ 2 3))
(assert-eq? 0 (-))
(assert-eq? -3 (- 3))
(assert-eq? -1 (- 2 3))
(assert-eq? 5 (- 8 3))
(assert-eq? 1 (*))
(assert-eq? 6 (* 3 2))
(assert-eq? 120 (* 1 2 3 4 5))
(assert-eq? 1 (/))
(assert-eq? 6 (/ 6))
(assert-eq? 3 (/ 6 2))
(assert-eq? 2 (/ 12 2 3))
(assert-eq? 1 (%))
(assert-eq? 3 (% 7 4))
(assert-eq? 2 (% 13 7 4))
(assert-eq? 1 (^))
(assert-eq? 2 (^ 2))
(assert-eq? 8 (^ 2 3))
(assert-eq? 64 (^ 2 3 2))
(assert-eq? true (< 5 10))
(assert-eq? false (< 5 5))
(assert-eq? false (< 5 3))
(assert-eq? true (<= 5 10))
(assert-eq? true (<= 5 5))
(assert-eq? false (<= 5 3))
(assert-eq? true (> 15 10))
(assert-eq? false (> 15 15))
(assert-eq? false (> 15 37))
(assert-eq? true (>= 15 10))
(assert-eq? true (>= 15 15))
(assert-eq? false (>= 15 37))
(assert-eq? true (ne? 5 8))
(assert-eq? false (ne? 5 5))

36
examples/run.sh Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
OK=0
TOTAL=0
for file in `find . -name "*.gri" -exec basename {} \;`
do
echo -en "\e[34m$file ... \e[0m"
MSG="$(grino $file 2>&1 > /dev/null)"
RES=$?
if [ "$RES" == "0" ]
then
echo -e "ok"
OK=$(($OK + 1))
else
echo "ko"
echo -e "\t\e[31m$MSG\e[0m"
fi
TOTAL=$(($TOTAL + 1))
done
echo
if [ $OK -eq $TOTAL ]
then
echo -e "\e[32m=== $OK/$TOTAL tests passed ! ===\e[0m"
exit 0
fi
FAILURE=$(($TOTAL - $OK))
echo -e "\e[31m=== $FAILURE/$TOTAL tests failed ===\e[0m"
exit -1

337
lib/core.cpp Normal file
View File

@ -0,0 +1,337 @@
#include "../src/Loader.hpp"
#include "src/Logger.hpp"
#include "src/Value.hpp"
GRINO_ERROR(assertion_error);
extern "C" void lib_int(grino::Loader& loader)
{
loader.add_native("+", [](auto args){
int result = 0;
for (auto value: args)
{
result += value->as_int();
}
grino::Loc loc {"???", 0};
if (args.empty() == false)
{
loc = args.front()->loc();
}
return grino::Value::make_int(loc, result);
});
loader.add_native("*", [](auto args){
int result = 1;
for (auto value: args)
{
result *= value->as_int();
}
grino::Loc loc {"???", 0};
if (args.empty() == false)
{
loc = args.front()->loc();
}
return grino::Value::make_int(loc, result);
});
loader.add_native("-", [](auto args){
grino::Loc loc {"???", 0};
if (args.empty() == false)
{
loc = args.front()->loc();
}
else
{
return grino::Value::make_int(loc, 0);
}
int result = args[0]->as_int();
if (args.size() == 1)
{
return grino::Value::make_int(loc, -result);
}
for (size_t i=1; i<args.size(); i++)
{
result -= args[i]->as_int();
}
return grino::Value::make_int(loc, result);
});
loader.add_native("/", [](auto args){
grino::Loc loc {"???", 0};
if (args.empty() == false)
{
loc = args.front()->loc();
}
else
{
return grino::Value::make_int(loc, 1);
}
int result = args[0]->as_int();
if (args.size() == 1)
{
return grino::Value::make_int(loc, result);
}
for (size_t i=1; i<args.size(); i++)
{
result /= args[i]->as_int();
}
return grino::Value::make_int(loc, result);
});
loader.add_native("%", [](auto args){
grino::Loc loc {"???", 0};
if (args.empty() == false)
{
loc = args.front()->loc();
}
else
{
return grino::Value::make_int(loc, 1);
}
int result = args[0]->as_int();
if (args.size() == 1)
{
return grino::Value::make_int(loc, result);
}
for (size_t i=1; i<args.size(); i++)
{
result %= args[i]->as_int();
}
return grino::Value::make_int(loc, result);
});
loader.add_native("^", [](auto args){
grino::Loc loc {"???", 0};
if (args.empty() == false)
{
loc = args.front()->loc();
}
else
{
return grino::Value::make_int(loc, 1);
}
int result = args[0]->as_int();
if (args.size() == 1)
{
return grino::Value::make_int(loc, result);
}
for (size_t i=1; i<args.size(); i++)
{
result = std::pow(result, args[i]->as_int());
}
return grino::Value::make_int(loc, result);
});
}
extern "C" void lib_cmp(grino::Loader& loader)
{
loader.add_native("<", [](auto args){
int lhs = args[0]->as_int();
int rhs = args[1]->as_int();
return grino::Value::make_bool(args[0]->loc(), lhs < rhs);
});
loader.add_native("<=", [](auto args){
int lhs = args[0]->as_int();
int rhs = args[1]->as_int();
return grino::Value::make_bool(args[0]->loc(), lhs <= rhs);
});
loader.add_native(">", [](auto args){
int lhs = args[0]->as_int();
int rhs = args[1]->as_int();
return grino::Value::make_bool(args[0]->loc(), lhs > rhs);
});
loader.add_native(">=", [](auto args){
int lhs = args[0]->as_int();
int rhs = args[1]->as_int();
return grino::Value::make_bool(args[0]->loc(), lhs >= rhs);
});
loader.add_native("ne?", [](auto args){
int lhs = args[0]->as_int();
int rhs = args[1]->as_int();
return grino::Value::make_bool(args[0]->loc(), lhs != rhs);
});
}
extern "C" void lib_bool(grino::Loader& loader)
{
loader.add_native("not", [](auto args){
return grino::Value::make_bool(args[0]->loc(), !args[0]->as_bool());
});
loader.add_static("and", [](auto& compiler, auto node, auto& program){
std::vector<size_t> to_false;
for (size_t i=1; i<node->size(); i++)
{
compiler.compile(node->child(i).lock(), program);
to_false.push_back(program.size());
program.push_instr(grino::OPCODE_BRF, 0 /* to false */);
}
size_t addr = program
.push_constant(grino::Value::make_bool(node->loc(), true));
program.push_instr(grino::OPCODE_LOAD_CONST, addr);
size_t to_end = program.size();
program.push_instr(grino::OPCODE_BR, 0 /* to end */);
for (auto a: to_false)
{
program.set_param(a, program.size());
}
addr = program
.push_constant(grino::Value::make_bool(node->loc(), false));
program.push_instr(grino::OPCODE_LOAD_CONST, addr);
program.set_param(to_end, program.size());
});
loader.add_static("or", [](auto& compiler, auto node, auto& program){
std::vector<size_t> to_true;
for (size_t i=1; i<node->size(); i++)
{
compiler.compile(node->child(i).lock(), program);
program.push_instr(grino::OPCODE_NOT);
to_true.push_back(program.size());
program.push_instr(grino::OPCODE_BRF, 0 /* to true */);
}
size_t addr = program
.push_constant(grino::Value::make_bool(node->loc(), false));
program.push_instr(grino::OPCODE_LOAD_CONST, addr);
size_t to_end = program.size();
program.push_instr(grino::OPCODE_BR, 0 /* to end */);
for (auto a: to_true)
{
program.set_param(a, program.size());
}
addr = program
.push_constant(grino::Value::make_bool(node->loc(), true));
program.push_instr(grino::OPCODE_LOAD_CONST, addr);
program.set_param(to_end, program.size());
});
}
extern "C" void lib_assert(grino::Loader& loader)
{
loader.add_native("assert", [](auto args){
for (auto value: args)
{
if (!value->as_bool())
{
grino::Logger logger;
logger.log<assertion_error>(grino::LOG_ASSERT,
value->loc(),
"assertion failed");
}
}
return grino::Value::make_bool(args.front()->loc(), true);
});
loader.add_native("assert-eq?", [](auto args){
auto lhs = args.front();
for (size_t i=1; i<args.size(); i++)
{
auto rhs = args[i];
if (!lhs->equals(*rhs))
{
grino::Logger logger;
std::stringstream ss;
ss << "'" << lhs->string() << "' is not equals to '"
<< rhs->string() << "'";
logger.log<assertion_error>(grino::LOG_ASSERT,
lhs->loc(),
ss.str());
}
}
return grino::Value::make_bool(args.front()->loc(), true);
});
}
extern "C" void lib(grino::Loader& loader)
{
lib_assert(loader);
lib_int(loader);
lib_cmp(loader);
lib_bool(loader);
loader.add_native("dump", [](auto args){
std::string sep;
std::stringstream ss;
for (auto arg: args)
{
ss << sep << arg->string();
sep = " ";
}
std::cout << ss.str() << std::endl;
return grino::Value::make_nil(args.back()->loc());
});
loader.add_native("eq?", [](auto args){
auto lhs = args.front();
for (size_t i=1; i<args.size(); i++)
{
auto rhs = args[i];
if (!lhs->equals(*rhs))
{
return grino::Value::make_bool(rhs->loc(), false);
}
}
return grino::Value::make_bool(args.front()->loc(), true);
});
}

View File

@ -6,8 +6,11 @@ project('grino',
'cpp_std=c++17'
])
grino_libdir = get_option('prefix') / get_option('libdir') / 'grino'
conf = configuration_data()
conf.set('version', meson.project_version())
conf.set('libdir', grino_libdir)
configure_file(input: 'src/config.in.hpp',
output: 'config.hpp',
@ -24,11 +27,24 @@ grino_src = static_library('grino',
'src/Program.cpp',
'src/VM.cpp',
'src/Value.cpp',
'src/Function.cpp',
'src/StaticFunction.cpp',
'src/SymTable.cpp',
'src/Loader.cpp',
])
grino_dep = declare_dependency(link_with: grino_src)
shared_library('grino_core',
sources: [
'lib/core.cpp'
],
dependencies: [
grino_dep
],
install: true,
install_dir: grino_libdir)
executable('grino',
sources: [
'src/main.cpp'

View File

@ -2,6 +2,7 @@
#include "Program.hpp"
#include "SymTable.hpp"
#include "src/opcodes.hpp"
#include "StaticFunction.hpp"
namespace grino
{
@ -28,9 +29,38 @@ namespace grino
}
} break;
case NODE_FUNCALL: {
std::string ident = node->child(0).lock()->repr();
if (auto itr = m_statics.find(ident);
itr != std::end(m_statics))
{
auto fun = itr->second;
fun->call(*this, node, program);
}
else
{
for (size_t i=0; i<node->size(); i++)
{
compile(node->child(i).lock(), program);
}
program.push_instr(OPCODE_CALL, node->size() - 1);
}
} break;
case NODE_BOOL: {
std::string repr = node->repr();
auto value = Value::make_bool(repr == "true");
auto value = Value::make_bool(node->loc(), repr == "true");
program.push_instr(OPCODE_LOAD_CONST,
program.push_constant(value));
} break;
case NODE_INT: {
std::string repr = node->repr();
auto value = Value::make_int(node->loc(), std::stoi(repr));
program.push_instr(OPCODE_LOAD_CONST,
program.push_constant(value));
@ -58,7 +88,14 @@ namespace grino
m_logger.log<compile_error>(LOG_ERROR, node->loc(), ss.str());
}
program.push_instr(OPCODE_LOAD_LOCAL, entry->addr);
if (entry->is_object)
{
program.push_instr(OPCODE_LOAD_OBJ, entry->addr);
}
else
{
program.push_instr(OPCODE_LOAD_LOCAL, entry->addr);
}
} break;
default:
@ -69,6 +106,12 @@ namespace grino
}
}
void Compiler::add_static_func(std::string const& name,
std::shared_ptr<StaticFunction> fun)
{
m_statics[name] = fun;
}
size_t Compiler::get_local_address()
{
static size_t addr = 0;

View File

@ -10,6 +10,7 @@ namespace grino
{
class Program;
class SymTable;
class StaticFunction;
GRINO_ERROR(compile_error);
@ -22,9 +23,14 @@ namespace grino
void compile(std::shared_ptr<Node> node,
Program& program);
void add_static_func(std::string const& name,
std::shared_ptr<StaticFunction> fun);
private:
Logger& m_logger;
SymTable& m_sym;
std::unordered_map<std::string,
std::shared_ptr<StaticFunction>> m_statics;
size_t get_local_address();
};

20
src/Function.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "Function.hpp"
#include "Value.hpp"
namespace grino
{
/*explicit*/ Function::Function(native_t native)
: m_native { native }
{
}
/*virtual*/ Function::~Function()
{
}
value_t Function::call(args_t args)
{
assert(m_native);
return m_native(args);
}
}

27
src/Function.hpp Normal file
View File

@ -0,0 +1,27 @@
#ifndef grino_FUNCTION_HPP
#define grino_FUNCTION_HPP
#include "commons.hpp"
namespace grino
{
class Value;
using value_t = std::shared_ptr<Value>;
using args_t = std::vector<value_t>;
using native_t = std::function<value_t(args_t)>;
class Function
{
public:
explicit Function(native_t native);
virtual ~Function();
value_t call(args_t args);
private:
native_t m_native;
};
}
#endif

View File

@ -14,6 +14,7 @@ namespace grino
add_keyword(NODE_BOOL, "true", true);
add_keyword(NODE_BOOL, "false", true);
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
}
@ -222,4 +223,35 @@ namespace grino
return std::nullopt;
}
std::optional<ScanInfo> Lexer::scan_int()
{
size_t cursor = m_cursor;
std::string repr;
if (has_more(cursor) && at(cursor) == '-')
{
repr += "-";
cursor++;
}
while (has_more(cursor)
&& std::isdigit(at(cursor)))
{
repr += at(cursor);
cursor++;
}
if (repr.empty()
|| repr.back() == '-')
{
return std::nullopt;
}
return ScanInfo {
cursor,
NODE_INT,
repr
};
}
}

View File

@ -58,6 +58,7 @@ namespace grino
bool has_value);
std::optional<ScanInfo> scan_ident();
std::optional<ScanInfo> scan_int();
};
}

53
src/Loader.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "Loader.hpp"
#include "Function.hpp"
#include "src/config.in.hpp"
#include <dlfcn.h>
namespace grino
{
/*explicit*/ Loader::Loader(VM& vm, Compiler& compiler, SymTable& sym_table)
: m_vm { vm }
, m_compiler { compiler }
, m_sym_table { sym_table }
{
}
/*virtual*/ Loader::~Loader()
{
}
void Loader::load_libraries()
{
for (auto entry: std::filesystem::directory_iterator(GRINO_LIBDIR))
{
if (entry.path().has_extension()
&& entry.path().extension() == ".so")
{
load_library(entry.path());
}
}
}
void Loader::load_library(std::filesystem::path path)
{
void* handle = dlopen(path.string().c_str(), RTLD_NOW);
typedef void(*libfun)(Loader&);
libfun f = (libfun) dlsym(handle, "lib");
f(*this);
}
void Loader::add_native(std::string const& name, native_t native)
{
size_t addr = m_vm.heap_size();
grino::Loc loc {"???", 0};
m_vm.set_heap(addr, grino::Value::make_native_function(loc, native));
m_sym_table.declare_object(loc, name, addr);
}
void Loader::add_static(std::string const& name, static_fun_t fun)
{
m_compiler.add_static_func(name, std::make_shared<StaticFunction>(fun));
}
}

31
src/Loader.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef grino_LOADER_HPP
#define grino_LOADER_HPP
#include "commons.hpp"
#include "VM.hpp"
#include "SymTable.hpp"
#include "Compiler.hpp"
#include "StaticFunction.hpp"
namespace grino
{
class Loader
{
public:
explicit Loader(VM& vm, Compiler& compiler, SymTable& sym_table);
virtual ~Loader();
void load_libraries();
void load_library(std::filesystem::path path);
void add_native(std::string const& name, native_t native);
void add_static(std::string const& name, static_fun_t fun);
private:
VM& m_vm;
Compiler& m_compiler;
SymTable& m_sym_table;
};
}
#endif

View File

@ -5,8 +5,10 @@
#include "src/mutils.hpp"
#include "Loc.hpp"
#define LOG_TYPE(G) \
G(LOG_ERROR)
#define LOG_TYPE(G) \
G(LOG_ERROR), \
G(LOG_ASSERT),
namespace grino
{

View File

@ -7,7 +7,9 @@
#define NODE_TYPE(G) \
G(NODE_MODULE), \
G(NODE_BOOL), \
G(NODE_INT), \
G(NODE_VARDECL), \
G(NODE_FUNCALL), \
G(NODE_IDENT), \
G(NODE_OPAR), \
G(NODE_CPAR), \

View File

@ -93,7 +93,7 @@ namespace grino
}
else
{
return Loc {"???", 1};
return Loc {"???", 0};
}
}
@ -117,13 +117,19 @@ namespace grino
std::shared_ptr<Node> Parser::parse_expr()
{
if (type_is(NODE_OPAR))
if (type_is({NODE_OPAR, NODE_IDENT}))
{
return parse_funcall();
}
if (type_is({NODE_OPAR, NODE_DECL}))
{
return parse_vardecl();
}
if (type_is(NODE_IDENT)
|| type_is(NODE_BOOL))
|| type_is(NODE_BOOL)
|| type_is(NODE_INT))
{
return consume();
}
@ -151,4 +157,21 @@ namespace grino
return node;
}
std::shared_ptr<Node> Parser::parse_funcall()
{
consume(NODE_OPAR);
auto node = make_node(NODE_FUNCALL);
node->add_child(consume(NODE_IDENT));
while (!type_is(NODE_CPAR))
{
node->add_child(parse_expr());
}
consume(NODE_CPAR);
return node;
}
}

View File

@ -35,6 +35,7 @@ namespace grino
std::shared_ptr<Node> parse_module();
std::shared_ptr<Node> parse_expr();
std::shared_ptr<Node> parse_vardecl();
std::shared_ptr<Node> parse_funcall();
};
}

View File

@ -17,6 +17,12 @@ namespace grino
return m_instrs[index];
}
void Program::set_param(size_t addr, size_t param)
{
assert(addr < size());
m_instrs[addr].param = param;
}
size_t Program::push_instr(OpcodeType opcode,
std::optional<size_t> param /*=std::nullopt*/)
{
@ -63,6 +69,7 @@ namespace grino
}
ss << "\n";
addr++;
}
addr = 0;

View File

@ -23,6 +23,8 @@ namespace grino
Instr get(size_t index) const;
void set_param(size_t addr, size_t param);
size_t push_instr(OpcodeType opcode,
std::optional<size_t> param=std::nullopt);

21
src/StaticFunction.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "StaticFunction.hpp"
#include "Compiler.hpp"
namespace grino
{
/*explicit*/ StaticFunction::StaticFunction(static_fun_t fun)
: m_fun { fun }
{
}
/*virtual*/ StaticFunction::~StaticFunction()
{
}
void StaticFunction::call(Compiler& compiler,
node_t node,
prog_t prog)
{
m_fun(compiler, node, prog);
}
}

29
src/StaticFunction.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef grino_STATICFUNCTION_HPP
#define grino_STATICFUNCTION_HPP
#include "commons.hpp"
#include "Function.hpp"
#include "Node.hpp"
#include "Program.hpp"
namespace grino
{
class Compiler;
using node_t = std::shared_ptr<Node>;
using prog_t = Program&;
using static_fun_t = std::function<void (Compiler&, node_t, prog_t)>;
class StaticFunction
{
public:
explicit StaticFunction(static_fun_t fun);
virtual ~StaticFunction();
void call(Compiler& compiler, node_t node, prog_t prog);
private:
static_fun_t m_fun;
};
}
#endif

View File

@ -23,11 +23,19 @@ namespace grino
SymEntry entry;
entry.addr = addr;
entry.name = name;
entry.is_global = false;
entry.is_object = false;
m_entries.push_back(entry);
}
void SymTable::declare_object(Loc const& loc,
std::string const& name,
size_t addr)
{
declare(loc, name, addr);
m_entries.back().is_object = true;
}
std::optional<SymEntry> SymTable::find(std::string const& name)
{
for (size_t i=0; i<m_entries.size(); i++)

View File

@ -8,8 +8,8 @@ namespace grino
{
struct SymEntry {
std::string name;
bool is_global;
size_t addr;
bool is_object; /* object are on the heap instead of the stack */
size_t addr; /* address on the heap if object, local address otherwise */
};
GRINO_ERROR(symbolic_error);
@ -21,6 +21,8 @@ namespace grino
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);
std::optional<SymEntry> find(std::string const& name);
std::string string() const;

View File

@ -25,6 +25,31 @@ namespace grino
switch (instr.opcode)
{
case OPCODE_NOT: {
auto val = program.constant(pop());
push(program.push_constant(Value::make_bool(val->loc(),
!val->as_bool())));
m_pc++;
} break;
case OPCODE_BR: {
m_pc = *instr.param;
} break;
case OPCODE_BRF: {
auto val = program.constant(pop())->as_bool();
size_t addr = *instr.param;
if (!val)
{
m_pc = addr;
}
else
{
m_pc++;
}
} break;
case OPCODE_LOAD_CONST: {
push(*instr.param);
m_pc++;
@ -48,6 +73,34 @@ namespace grino
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));
m_pc++;
} break;
case OPCODE_CALL: {
size_t const N = *instr.param;
std::vector<std::shared_ptr<Value>> args;
for (size_t i=0; i<N; i++)
{
args.insert(std::begin(args), program.constant(pop()));
}
size_t ref = program.constant(pop())->as_ref();
auto fun = heap(ref)->as_function();
auto ret = fun->call(args);
push(program.push_constant(ret));
m_pc++;
} break;
default:
std::cerr << "cannot execute unknown opcode "
<< OpcodeTypeStr[instr.opcode]
@ -69,6 +122,29 @@ namespace grino
return ss.str();
}
std::shared_ptr<Value> VM::local(size_t addr) const
{
return m_frames.back().locals.at(addr);
}
void VM::set_local(size_t addr,
std::shared_ptr<Value> value)
{
m_frames.back().locals[addr] = value;
}
std::shared_ptr<Value> VM::heap(size_t addr) const
{
return m_heap.at(addr);
}
void VM::set_heap(size_t addr,
std::shared_ptr<Value> value)
{
m_heap[addr] = value;
}
void VM::push(size_t addr)
{
if (m_sp >= STACK_SIZE)
@ -97,16 +173,4 @@ namespace grino
size_t addr = m_stack[m_sp - 1];
return addr;
}
std::shared_ptr<Value> VM::local(size_t addr) const
{
return m_frames.back().locals.at(addr);
}
void VM::set_local(size_t addr,
std::shared_ptr<Value> value)
{
m_frames.back().locals[addr] = value;
}
}

View File

@ -21,12 +21,23 @@ namespace grino
explicit VM(Logger& logger);
virtual ~VM();
size_t heap_size() const { return m_heap.size(); }
void run(Program& program);
std::string string() const;
std::shared_ptr<Value> local(size_t addr) const;
void set_local(size_t addr,
std::shared_ptr<Value> value);
std::shared_ptr<Value> heap(size_t addr) const;
void set_heap(size_t addr,
std::shared_ptr<Value> value);
private:
Logger& m_logger;
std::array<size_t, STACK_SIZE> m_stack;
std::unordered_map<size_t, std::shared_ptr<Value>> m_heap;
std::vector<Frame> m_frames;
size_t m_sp; /* stack pointer */
@ -36,9 +47,8 @@ namespace grino
void push(size_t addr);
size_t pop();
size_t top();
std::shared_ptr<Value> local(size_t addr) const;
void set_local(size_t addr,
std::shared_ptr<Value> value);
};
}

View File

@ -2,28 +2,56 @@
namespace grino
{
/*static*/ std::shared_ptr<Value> Value::make_nil()
/*static*/ std::shared_ptr<Value> Value::make_nil(Loc const& loc)
{
auto value = std::make_shared<Value>();
auto value = std::make_shared<Value>(loc);
value->m_type = TYPE_NIL;
return value;
}
/*static*/ std::shared_ptr<Value> Value::make_bool(bool val)
/*static*/ std::shared_ptr<Value> Value::make_bool(Loc const& loc, bool val)
{
auto value = std::make_shared<Value>();
auto value = std::make_shared<Value>(loc);
value->m_type = TYPE_BOOL;
value->m_bool_val = val;
return value;
}
/*static*/ std::shared_ptr<Value> Value::make_int(Loc const& loc, int val)
{
auto value = std::make_shared<Value>(loc);
value->m_type = TYPE_INT;
value->m_int_val = val;
return value;
}
/*static*/
std::shared_ptr<Value> Value::make_native_function(Loc const& loc,
native_t 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)
{
auto value = std::make_shared<Value>(loc);
value->m_type = TYPE_REF;
value->m_ref_val = val;
return value;
}
std::string Value::string() const
{
switch (m_type)
{
case TYPE_NIL: return "<nil>";
case TYPE_INT: return std::to_string(*m_int_val);
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
case TYPE_FUNCTION: return "<function>";
case TYPE_REF: return "&" + std::to_string(*m_ref_val);
default:
std::cerr << "cannot stringify value "
<< TypeTypeStr[m_type] << std::endl;
@ -39,6 +67,8 @@ namespace grino
{
case TYPE_NIL: return true;
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;
default:
std::cerr << "cannot compare equality with value "
@ -46,4 +76,9 @@ namespace grino
abort();
}
}
/*explicit*/ Value::Value(Loc const& loc)
: m_loc { loc }
{
}
}

View File

@ -3,27 +3,42 @@
#include "commons.hpp"
#include "types.hpp"
#include "Function.hpp"
#include "Loc.hpp"
namespace grino
{
class Value
{
public:
static std::shared_ptr<Value> make_nil();
static std::shared_ptr<Value> make_bool(bool val);
static std::shared_ptr<Value> make_nil(Loc const& loc);
static std::shared_ptr<Value> make_bool(Loc const& loc, bool val);
static std::shared_ptr<Value> make_int(Loc const& loc, int val);
static std::shared_ptr<Value> make_native_function(Loc const& loc,
native_t val);
static std::shared_ptr<Value> make_ref(Loc const& loc,
size_t val);
explicit Value() = default;
explicit Value(Loc const& loc);
virtual ~Value() = default;
Loc loc() const { return m_loc; }
TypeType type() const { return m_type; }
bool as_bool() const { return *m_bool_val; }
int as_int() const { return *m_int_val; }
std::shared_ptr<Function> as_function() const { return m_function_val; }
size_t as_ref() const { return *m_ref_val; }
std::string string() const;
bool equals(Value const& other) const;
private:
Loc m_loc;
TypeType m_type = TYPE_NIL;
std::optional<bool> m_bool_val;
std::optional<int> m_int_val;
std::shared_ptr<Function> m_function_val;
std::optional<size_t> m_ref_val;
};
}

View File

@ -2,6 +2,7 @@
#define grino_COMMONS_HPP
#include <cassert>
#include <cmath>
#include <fstream>
#include <iostream>
#include <filesystem>

View File

@ -1,6 +1,9 @@
#ifndef grino_CONFIG_IN_HPP
#define grino_CONFIG_IN_HPP
#include <filesystem>
#define GRINO_VERSION "@version@"
#define GRINO_LIBDIR std::filesystem::path {"@libdir@"}
#endif

View File

@ -9,6 +9,56 @@
#include "VM.hpp"
#include "Logger.hpp"
#include "src/SymTable.hpp"
#include "Loader.hpp"
void run(char** argv, bool debug_mode)
{
std::string source;
{
std::ifstream file { argv[optind] };
assert(file);
std::string line;
while (std::getline(file, line))
{ source += line + (file.eof() ? "" : "\n"); }
}
grino::Logger logger;
grino::Lexer lexer {logger, argv[optind]};
grino::Parser parser {logger, lexer};
auto ast = parser.parse(source);
if (debug_mode)
{
std::cout << "--- ast ---" << std::endl;
std::cout << ast->string() << std::endl;
}
grino::SymTable sym_table {logger};
grino::VM vm {logger};
grino::Compiler compiler {logger, sym_table};
grino::Loader loader {vm, compiler, sym_table};
loader.load_libraries();
grino::Program program;
compiler.compile(ast, program);
if (debug_mode)
{
std::cout << "--- program ---" << std::endl;
std::cout << program.string() << std::endl;
}
vm.run(program);
if (debug_mode)
{
std::cout << "--- stack ---" << std::endl;
std::cout << vm.string() << std::endl;
}
}
int main(int argc, char** argv)
{
@ -53,47 +103,21 @@ int main(int argc, char** argv)
if (optind < argc)
{
std::string source;
{
std::ifstream file { argv[optind] };
assert(file);
std::string line;
while (std::getline(file, line))
{ source += line + (file.eof() ? "" : "\n"); }
}
grino::Logger logger;
grino::Lexer lexer {logger, argv[optind]};
grino::Parser parser {logger, lexer};
auto ast = parser.parse(source);
if (debug_mode)
{
std::cout << "--- ast ---" << std::endl;
std::cout << ast->string() << std::endl;
run(argv, debug_mode);
}
grino::SymTable sym_table {logger};
grino::Compiler compiler {logger, sym_table};
grino::Program program;
compiler.compile(ast, program);
if (debug_mode)
else
{
std::cout << "--- program ---" << std::endl;
std::cout << program.string() << std::endl;
}
grino::VM vm {logger};
vm.run(program);
if (debug_mode)
{
std::cout << "--- stack ---" << std::endl;
std::cout << vm.string() << std::endl;
try
{
run(argv, debug_mode);
}
catch(std::exception const& err)
{
std::cerr << err.what() << std::endl;
exit(-1);
}
}
}

View File

@ -8,7 +8,14 @@
G(OPCODE_LOAD_CONST), \
G(OPCODE_POP), \
G(OPCODE_LOAD_LOCAL), \
G(OPCODE_STORE_LOCAL)
G(OPCODE_STORE_LOCAL), \
G(OPCODE_LOAD_OBJ), \
G(OPCODE_STORE_OBJ), \
G(OPCODE_CALL), \
G(OPCODE_BRF), \
G(OPCODE_BR), \
G(OPCODE_NOT), \
G(OPCODE_RET),
namespace grino
{

View File

@ -5,7 +5,10 @@
#define TYPES(G) \
G(TYPE_NIL), \
G(TYPE_BOOL)
G(TYPE_BOOL), \
G(TYPE_INT), \
G(TYPE_FUNCTION), \
G(TYPE_REF)
namespace grino
{

View File

@ -77,3 +77,16 @@ TEST_CASE_METHOD(LexerTest, "Lexer_no_end_space")
test_next(lexer, "CPAR");
test_end(lexer);
}
TEST_CASE_METHOD(LexerTest, "Lexer_int")
{
grino::Lexer lexer {m_logger, "tests/lexer"};
lexer.scan("4 39 256 2 -7");
test_next(lexer, "INT[4]");
test_next(lexer, "INT[39]");
test_next(lexer, "INT[256]");
test_next(lexer, "INT[2]");
test_next(lexer, "INT[-7]");
test_end(lexer);
}

View File

@ -39,3 +39,18 @@ TEST_CASE_METHOD(ParserTest, "Parser_vardecl")
test_parse("MODULE(VARDECL(IDENT[hello],IDENT[world]))",
"($ hello world)");
}
TEST_CASE_METHOD(ParserTest, "Parser_funcall")
{
test_parse("MODULE(FUNCALL(IDENT[hello]))",
"(hello)");
test_parse("MODULE(FUNCALL(IDENT[f],BOOL[false],BOOL[true]))",
"(f false true)");
}
TEST_CASE_METHOD(ParserTest, "Parser_integer")
{
test_parse("MODULE(FUNCALL(IDENT[hello],INT[2],INT[34]))",
"(hello 2 34)");
}