Compare commits
6 Commits
e5c388a38e
...
59af9d809e
Author | SHA1 | Date |
---|---|---|
bog | 59af9d809e | |
bog | bae09ec2f3 | |
bog | 690122ea4a | |
bog | 0795eb5d9f | |
bog | 20b5e60557 | |
bog | 314fbc0bd6 |
|
@ -1,6 +1,9 @@
|
|||
MODULE ::= EXPR*
|
||||
EXPR ::=
|
||||
bool
|
||||
| int
|
||||
| ident
|
||||
| VARDECL
|
||||
| FUNCALL
|
||||
VARDECL ::= opar decl ident EXPR cpar
|
||||
FUNCALL ::= opar ident EXPR* cpar
|
||||
|
|
|
@ -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))
|
|
@ -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))
|
|
@ -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
|
|
@ -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);
|
||||
});
|
||||
}
|
16
meson.build
16
meson.build
|
@ -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'
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace grino
|
|||
bool has_value);
|
||||
|
||||
std::optional<ScanInfo> scan_ident();
|
||||
std::optional<ScanInfo> scan_int();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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), \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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++)
|
||||
|
|
|
@ -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;
|
||||
|
|
88
src/VM.cpp
88
src/VM.cpp
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
16
src/VM.hpp
16
src/VM.hpp
|
@ -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);
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define grino_COMMONS_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
|
|
@ -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
|
||||
|
|
98
src/main.cpp
98
src/main.cpp
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue