ADD: foreign functions.

main
bog 2023-09-10 00:03:28 +02:00
parent 149f866172
commit e8e4906cfc
32 changed files with 1162 additions and 32 deletions

6
core/core.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "lib/Function.hpp"
#include "lib/Loader.hpp"
extern "C" void lib(jk::Loader& loader)
{
}

4
doc/grammar.bnf Normal file
View File

@ -0,0 +1,4 @@
PROG ::= FUNCALL*
EXPR ::= LITERAL | FUNCALL
FUNCALL ::= opar ident EXPR* cpar
LITERAL ::= int | ident

13
lib/Code.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "Code.hpp"
namespace jk
{
/*explicit*/ Code::Code(foreign_t foreign)
: m_foreign { foreign }
{
}
/*virtual*/ Code::~Code()
{
}
}

22
lib/Code.hpp Normal file
View File

@ -0,0 +1,22 @@
#ifndef jk_CODE_HPP
#define jk_CODE_HPP
#include "commons.hpp"
#include "Function.hpp"
namespace jk
{
class Code
{
public:
explicit Code(foreign_t foreign);
virtual ~Code();
foreign_t foreign() const { return m_foreign; }
private:
foreign_t m_foreign;
};
}
#endif

80
lib/Compiler.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "Compiler.hpp"
#include "lib/Opcodes.hpp"
#include "Code.hpp"
namespace jk
{
/*explicit*/ Compiler::Compiler(std::shared_ptr<SymTable> sym,
Logger& logger)
: m_sym { sym }
, m_logger(logger)
{
}
/*virtual*/ Compiler::~Compiler()
{
}
void Compiler::compile(std::shared_ptr<Node> node,
std::shared_ptr<Program> program)
{
switch (node->type())
{
case NODE_PROG: {
for (size_t i=0; i<node->size(); i++)
{
compile(node->child(i).lock(), program);
}
} break;
case NODE_FUNCALL: {
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_IDENT: {
std::string ident = node->repr();
auto sym = m_sym->find(ident);
if (!sym)
{
m_logger.log<compile_error>(LOG_ERROR, node->loc(),
std::string()
+ "'"
+ ident
+ "' is undefined");
}
OpcodeType op_load = OPCODE_LOAD;
if (sym->is_global)
{
op_load = OPCODE_LOAD_GLOBAL;
}
if (sym->type->type() == TYPE_FUNCTION)
{
program->push_instr(op_load, sym->addr);
}
} break;
case NODE_INT: {
auto value = Value::make_int(std::stoi(node->repr()));
size_t addr = program->push_constant(value);
program->push_instr(OPCODE_PUSH_CONST, addr);
} break;
default:
std::cerr << "cannot compile unknown node '"
<< NodeTypeStr[node->type()] << "'" << std::endl;
abort();
}
}
}

31
lib/Compiler.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef jk_COMPILER_HPP
#define jk_COMPILER_HPP
#include "commons.hpp"
#include "SymTable.hpp"
#include "Logger.hpp"
#include "Node.hpp"
#include "Program.hpp"
#include "VM.hpp"
namespace jk
{
JK_ERROR(compile_error);
class Compiler
{
public:
explicit Compiler(std::shared_ptr<SymTable> sym,
Logger& logger);
virtual ~Compiler();
void compile(std::shared_ptr<Node> node,
std::shared_ptr<Program> program);
private:
std::shared_ptr<SymTable> m_sym;
Logger& m_logger;
};
}
#endif

19
lib/Function.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "Function.hpp"
#include "Value.hpp"
namespace jk
{
/*explicit*/ Function::Function(foreign_t foreign)
: m_foreign { foreign }
{
}
/*virtual*/ Function::~Function()
{
}
value_t Function::call(std::vector<value_t> const& args)
{
return m_foreign(args);
}
}

26
lib/Function.hpp Normal file
View File

@ -0,0 +1,26 @@
#ifndef jk_FUNCTION_HPP
#define jk_FUNCTION_HPP
#include "commons.hpp"
namespace jk
{
class Value;
using value_t = std::shared_ptr<Value>;
using foreign_t = std::function<value_t(std::vector<value_t>)>;
class Function
{
public:
explicit Function(foreign_t foreign);
virtual ~Function();
value_t call(std::vector<value_t> const& args);
private:
foreign_t m_foreign;
};
}
#endif

View File

@ -6,6 +6,21 @@ namespace jk
: m_logger { logger }
, m_loc { loc }
{
std::vector<std::tuple<NodeType, std::string, bool>> texts = {
{NODE_OPAR, "(", false},
{NODE_CPAR, ")", false}
};
for (auto text: texts)
{
m_scanners.push_back(std::bind(&Lexer::scan_text,
this,
std::get<0>(text),
std::get<1>(text),
std::get<2>(text)));
}
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
}
@ -24,10 +39,10 @@ namespace jk
skip_spaces();
while (more(m_cursor)
&& current(m_cursor) == '#')
&& at(m_cursor) == '#')
{
while (more(m_cursor)
&& current(m_cursor) != '\n')
&& at(m_cursor) != '\n')
{
m_cursor++;
}
@ -60,9 +75,9 @@ namespace jk
std::string text;
while (more(m_cursor)
&& !std::isspace(current(m_cursor)))
&& !std::isspace(at(m_cursor)))
{
text += current(m_cursor);
text += at(m_cursor);
m_cursor++;
}
@ -79,7 +94,7 @@ namespace jk
return index < m_source.size();
}
char Lexer::current(size_t index) const
char Lexer::at(size_t index) const
{
assert(more(index));
@ -89,9 +104,9 @@ namespace jk
void Lexer::skip_spaces()
{
while (more(m_cursor)
&& std::isspace(current(m_cursor)))
&& std::isspace(at(m_cursor)))
{
if (current(m_cursor) == '\n')
if (at(m_cursor) == '\n')
{
m_loc = Loc {
m_loc.path(),
@ -110,9 +125,9 @@ namespace jk
std::string repr;
while (more(cursor)
&& std::isdigit(current(cursor)))
&& std::isdigit(at(cursor)))
{
repr += current(cursor);
repr += at(cursor);
cursor++;
}
@ -127,4 +142,71 @@ namespace jk
return std::nullopt;
}
std::optional<ScanInfo> Lexer::scan_text(NodeType type,
std::string const& text,
bool has_value) const
{
if (m_cursor + text.size() > m_source.size())
{
return std::nullopt;
}
for (size_t i=0; i<text.size(); i++)
{
if (at(m_cursor + i) != text[i])
{
return std::nullopt;
}
}
return ScanInfo {
m_cursor + text.size(),
type,
has_value ? text : ""
};
}
std::optional<ScanInfo> Lexer::scan_ident() const
{
auto car = [](char c){
return std::isalpha(c)
|| c == '_'
|| c == '-'
|| c == '?'
|| c == '!'
|| c == '/';
};
auto cdr = [car](char c){
return car(c)
|| std::isdigit(c)
;
};
size_t cursor = m_cursor;
std::string repr;
if (!more(cursor)
|| !car(at(cursor)))
{
return std::nullopt;
}
repr += at(cursor);
cursor++;
while (more(cursor)
&& cdr(at(cursor)))
{
repr += at(cursor);
cursor++;
}
return ScanInfo {
cursor,
NODE_IDENT,
repr
};
}
}

View File

@ -23,6 +23,8 @@ namespace jk
explicit Lexer(Logger& logger, Loc const& loc);
virtual ~Lexer();
Loc loc() const { return m_loc; }
void scan(std::string const& source);
std::shared_ptr<Node> next();
@ -34,11 +36,15 @@ namespace jk
std::vector<scanner_t> m_scanners;
bool more(size_t index) const;
char current(size_t index) const;
char at(size_t index) const;
void skip_spaces();
std::optional<ScanInfo> scan_int() const;
std::optional<ScanInfo> scan_text(NodeType type,
std::string const& text,
bool has_value) const;
std::optional<ScanInfo> scan_ident() const;
};
}

50
lib/Loader.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "Loader.hpp"
#include "lib/Function.hpp"
#include "lib/config.in.hpp"
#include <dlfcn.h>
namespace jk
{
/*explicit*/ Loader::Loader(std::shared_ptr<VM> vm,
std::shared_ptr<SymTable> sym)
: m_vm { vm }
, m_sym { sym }
{
}
/*virtual*/ Loader::~Loader()
{
}
void Loader::load()
{
for (auto entry: std::filesystem::directory_iterator(JOKO_LIBDIR))
{
if (entry.path().extension() == ".so")
{
void* handle = dlopen(entry.path().string().c_str(), RTLD_LAZY);
assert(handle);
typedef void(*func)(Loader&);
func f = (func) dlsym(handle, "lib");
assert(f);
f(*this);
}
}
}
void Loader::declare(std::string const& name, foreign_t body)
{
auto fun = std::make_shared<Function>(body);
auto fun_val = Value::make_function(fun);
size_t addr = m_vm->push_heap(fun_val);
auto ref = Value::make_ref(addr);
size_t global_addr = m_sym->declare_global(name,
fun_val->type().lock(),
Loc {"global", 1, 1});
m_vm->set_global(global_addr, ref);
}
}

27
lib/Loader.hpp Normal file
View File

@ -0,0 +1,27 @@
#ifndef jk_LOADER_HPP
#define jk_LOADER_HPP
#include "commons.hpp"
#include "VM.hpp"
#include "SymTable.hpp"
#include "Function.hpp"
namespace jk
{
class Loader
{
public:
explicit Loader(std::shared_ptr<VM> vm,
std::shared_ptr<SymTable> sym);
virtual ~Loader();
void load();
void declare(std::string const& name, foreign_t body);
private:
std::shared_ptr<VM> m_vm;
std::shared_ptr<SymTable> m_sym;
};
}
#endif

View File

@ -6,7 +6,12 @@
#define NODE_TYPE(G) \
G(NODE_PROG), \
G(NODE_INT)
G(NODE_INT), \
G(NODE_OPAR), \
G(NODE_CPAR), \
G(NODE_IDENT), \
G(NODE_FUNCALL),
namespace jk
{

19
lib/Opcodes.hpp Normal file
View File

@ -0,0 +1,19 @@
#ifndef jk_OPCODES_HPP
#define jk_OPCODES_HPP
#include "commons.hpp"
#define OPCODES(G) \
G(OPCODE_PUSH_CONST), \
G(OPCODE_CALL), \
G(OPCODE_LOAD), \
G(OPCODE_STORE), \
G(OPCODE_LOAD_GLOBAL), \
G(OPCODE_MK_FUNCTION)
namespace jk
{
JK_ENUM(Opcode, OPCODES);
}
#endif

View File

@ -12,8 +12,131 @@ namespace jk
{
}
std::shared_ptr<Node> Parser::parse(std::string const&)
std::shared_ptr<Node> Parser::parse(std::string const& source)
{
return nullptr;
std::shared_ptr<Node> tok;
m_cursor = 0;
m_lexer->scan(source);
while ( (tok=m_lexer->next()) )
{
m_tokens.push_back(tok);
}
if (m_tokens.empty())
{
return std::make_shared<Node>(NODE_PROG, "",
Loc {m_lexer->loc().path(), 1, 1});
}
return parse_prog();
}
std::shared_ptr<Node> Parser::current() const
{
assert(m_cursor < m_tokens.size());
return m_tokens[m_cursor];
}
Loc Parser::loc() const
{
return current()->loc();
}
std::shared_ptr<Node> Parser::consume()
{
assert(m_cursor < m_tokens.size());
auto tok = m_tokens[m_cursor];
m_cursor++;
return tok;
}
std::shared_ptr<Node> Parser::consume(NodeType type)
{
assert(m_cursor < m_tokens.size());
if (m_tokens[m_cursor]->type() != type)
{
std::stringstream ss;
ss << "expected '"
<< std::string(NodeTypeStr[type])
.substr(std::string("NODE_").size())
<< "', got '"
<< std::string(NodeTypeStr[m_tokens[m_cursor]->type()])
.substr(std::string("NODE_").size())
<< "'";
m_logger.log<syntax_error>(LOG_ERROR,
m_tokens[m_cursor]->loc(),
ss.str());
}
return consume();
}
bool Parser::type_is(NodeType type, int lookahead) const
{
if (m_cursor + lookahead >= m_tokens.size())
{
return false;
}
return m_tokens[m_cursor + lookahead]->type() == type;
}
std::shared_ptr<Node> Parser::parse_prog()
{
auto root = std::make_shared<Node>(NODE_PROG, "", loc());
while (m_cursor < m_tokens.size())
{
root->add_child(parse_funcall());
}
return root;
}
std::shared_ptr<Node> Parser::parse_expr()
{
if (type_is(NODE_OPAR))
{
return parse_funcall();
}
return parse_literal();
}
std::shared_ptr<Node> Parser::parse_funcall()
{
auto root = std::make_shared<Node>(NODE_FUNCALL, "", loc());
consume(NODE_OPAR);
while (!type_is(NODE_CPAR))
{
root->add_child(parse_expr());
}
consume(NODE_CPAR);
return root;
}
std::shared_ptr<Node> Parser::parse_literal()
{
if (type_is(NODE_INT)
|| type_is(NODE_IDENT))
{
return consume();
}
m_logger.log<syntax_error>(LOG_ERROR,
loc(),
std::string()
+ "unexpected token '"
+ current()->repr()
+ "'");
abort();
}
}

View File

@ -7,6 +7,8 @@
namespace jk
{
JK_ERROR(syntax_error);
class Parser
{
public:
@ -18,6 +20,19 @@ namespace jk
private:
Logger& m_logger;
std::shared_ptr<Lexer> m_lexer;
std::vector<std::shared_ptr<Node>> m_tokens;
size_t m_cursor;
std::shared_ptr<Node> current() const;
Loc loc() const;
std::shared_ptr<Node> consume();
std::shared_ptr<Node> consume(NodeType type);
bool type_is(NodeType type, int lookahead=0) const;
std::shared_ptr<Node> parse_prog();
std::shared_ptr<Node> parse_expr();
std::shared_ptr<Node> parse_funcall();
std::shared_ptr<Node> parse_literal();
};
}

61
lib/Program.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "Program.hpp"
namespace jk
{
/*explicit*/ Program::Program()
{
}
/*virtual*/ Program::~Program()
{
}
Instr Program::get(size_t index) const
{
assert(index < size());
return m_instrs[index];
}
void Program::push_instr(OpcodeType opcode,
std::optional<param_t> param /*= std::nullopt*/)
{
m_instrs.push_back(Instr { opcode, param });
}
std::string Program::string() const
{
std::stringstream ss;
size_t addr = 0;
for (auto const& instr: m_instrs)
{
ss << addr << "\t"
<< std::string(OpcodeTypeStr[instr.opcode])
.substr(std::string("OPCODE_").size());
if (instr.param)
{
ss << "\t" << *instr.param;
}
ss << std::endl;
addr++;
}
return ss.str();
}
size_t Program::push_constant(std::shared_ptr<Value> value)
{
m_constants.push_back(value);
return m_constants.size() - 1;
}
std::shared_ptr<Value> Program::constant(size_t index) const
{
assert(index < m_constants.size());
return m_constants[index];
}
}

41
lib/Program.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef jk_PROGRAM_HPP
#define jk_PROGRAM_HPP
#include "commons.hpp"
#include "Opcodes.hpp"
#include "Value.hpp"
namespace jk
{
using param_t = size_t;
struct Instr {
OpcodeType opcode;
std::optional<param_t> param;
};
class Program
{
public:
explicit Program();
virtual ~Program();
size_t size() const { return m_instrs.size(); }
Instr get(size_t index) const;
void push_instr(OpcodeType opcode,
std::optional<param_t> param = std::nullopt);
size_t push_constant(std::shared_ptr<Value> value);
std::shared_ptr<Value> constant(size_t index) const;
std::string string() const;
private:
std::vector<Instr> m_instrs;
std::vector<std::shared_ptr<Value>> m_constants;
};
}
#endif

37
lib/StaticPass.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "StaticPass.hpp"
namespace jk
{
/*explicit*/ StaticPass::StaticPass(std::shared_ptr<SymTable> sym,
Logger& logger)
: m_sym { sym }
, m_logger { logger }
{
}
/*virtual*/ StaticPass::~StaticPass()
{
}
void StaticPass::pass(std::shared_ptr<Node> node)
{
switch (node->type())
{
case NODE_PROG: {
for (size_t i=0; i<node->size(); i++)
{
pass(node->child(i).lock());
}
} break;
case NODE_FUNCALL: {
} break;
default:
std::cerr << "cannot static_pass unknown node '"
<< NodeTypeStr[node->type()] << "'" << std::endl;
abort();
}
}
}

24
lib/StaticPass.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef jk_STATICPASS_HPP
#define jk_STATICPASS_HPP
#include "commons.hpp"
#include "SymTable.hpp"
#include "Node.hpp"
namespace jk
{
class StaticPass
{
public:
explicit StaticPass(std::shared_ptr<SymTable> sym, Logger& logger);
virtual ~StaticPass();
void pass(std::shared_ptr<Node> node);
private:
std::shared_ptr<SymTable> m_sym;
Logger& m_logger;
};
}
#endif

64
lib/SymTable.cpp Normal file
View File

@ -0,0 +1,64 @@
#include "SymTable.hpp"
namespace jk
{
/*explicit*/ SymTable::SymTable(Logger& logger)
: m_logger { logger }
{
}
/*virtual*/ SymTable::~SymTable()
{
}
size_t
SymTable::declare(std::string const& name,
std::shared_ptr<Type> type,
Loc const& loc)
{
if (find(name))
{
m_logger.log<symbolic_error>(LOG_ERROR,
loc,
std::string()
+ "'"
+ name
+ "' is already defined");
}
size_t id = m_syms.size();
m_syms.push_back(Sym {
name,
type,
loc,
false,
id
});
return id;
}
size_t
SymTable::declare_global(std::string const& name,
std::shared_ptr<Type> type,
Loc const& loc)
{
size_t addr = declare(name, type, loc);
m_syms[addr].is_global = true;
return addr;
}
std::optional<Sym> SymTable::find(std::string const& name) const
{
for (auto const& sym: m_syms)
{
if (sym.name == name)
{
return sym;
}
}
return std::nullopt;
}
}

43
lib/SymTable.hpp Normal file
View File

@ -0,0 +1,43 @@
#ifndef jk_SYMTABLE_HPP
#define jk_SYMTABLE_HPP
#include "commons.hpp"
#include "Type.hpp"
#include "Logger.hpp"
namespace jk
{
struct Sym {
std::string name;
std::shared_ptr<Type> type;
Loc loc;
bool is_global = false;
size_t addr;
};
JK_ERROR(symbolic_error);
class SymTable
{
public:
explicit SymTable(Logger& logger);
virtual ~SymTable();
size_t declare(std::string const& name,
std::shared_ptr<Type> type,
Loc const& loc);
size_t declare_global(std::string const& name,
std::shared_ptr<Type> type,
Loc const& loc);
std::optional<Sym> find(std::string const& name) const;
private:
Logger& m_logger;
std::vector<Sym> m_syms;
size_t m_addr = 0;
};
}
#endif

View File

@ -3,9 +3,13 @@
#include "commons.hpp"
#define TYPE_TYPE(G) \
G(TYPE_NIL), \
G(TYPE_INT)
#define TYPE_TYPE(G) \
G(TYPE_NIL), \
G(TYPE_INT), \
G(TYPE_FUNCTION), \
G(TYPE_CODE), \
G(TYPE_REF)
namespace jk
{

141
lib/VM.cpp Normal file
View File

@ -0,0 +1,141 @@
#include "VM.hpp"
#include "Function.hpp"
#include "Code.hpp"
#include "lib/Opcodes.hpp"
namespace jk
{
/*explicit*/ VM::VM()
{
}
/*virtual*/ VM::~VM()
{
}
void VM::execute(std::shared_ptr<Program> program)
{
Frame frame;
frame.program = program;
m_frames.push_back(frame);
while (m_pc < m_frames.back().program->size())
{
Instr instr = m_frames.back().program->get(m_pc);
switch (instr.opcode)
{
case OPCODE_MK_FUNCTION: {
auto code = program->constant(pop());
auto function = std::make_shared<Function>
(code->as_code()->foreign());
auto value = Value::make_function(function);
size_t addr = push_heap(value);
auto ref = Value::make_ref(addr);
push(program->push_constant(ref));
m_pc++;
} break;
case OPCODE_PUSH_CONST: {
push(*instr.param);
m_pc++;
} break;
case OPCODE_LOAD: {
auto value = m_frames.back().locals[*instr.param];
push(program->push_constant(value));
m_pc++;
} break;
case OPCODE_STORE: {
auto idx = pop();
m_frames.back().locals[*instr.param] =
program->constant(idx);
m_pc++;
} break;
case OPCODE_LOAD_GLOBAL: {
auto value = m_globals[*instr.param];
push(program->push_constant(value));
m_pc++;
} break;
case OPCODE_CALL: {
std::vector<std::shared_ptr<Value>> args;
for (size_t i=0; i<*instr.param; i++)
{
args.insert(std::begin(args), program->constant(pop()));
}
auto ref_val = program->constant(pop());
auto ref = ref_val->as_ref();
auto fun_val = heap(ref);
auto fun = fun_val->as_function();
auto result = fun->call(args);
push(program->push_constant(result));
m_pc++;
} break;
default:
std::cerr << "cannot execute unknown opcode "
<< OpcodeTypeStr[instr.opcode]
<< std::endl;
abort();
}
}
}
std::string VM::string() const
{
std::stringstream ss;
for (size_t i=0; i<m_stack.size(); i++)
{
ss << i << "\t" << m_stack[i] << std::endl;
}
return ss.str();
}
size_t VM::push_heap(std::shared_ptr<Value> value)
{
m_heap.push_back(value);
return m_heap.size() - 1;
}
std::shared_ptr<Value> VM::heap(size_t addr)
{
assert(addr < m_heap.size());
return m_heap[addr];
}
void VM::set_global(size_t addr, std::shared_ptr<Value> value)
{
m_globals[addr] = value;
}
void VM::push(param_t param)
{
m_stack.push_back(param);
}
param_t VM::pop()
{
auto param = m_stack.back();
m_stack.pop_back();
return param;
}
}

41
lib/VM.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef jk_VM_HPP
#define jk_VM_HPP
#include "commons.hpp"
#include "Program.hpp"
namespace jk
{
struct Frame {
std::shared_ptr<Program> program;
std::unordered_map<int, std::shared_ptr<Value>> locals;
};
class VM
{
public:
explicit VM();
virtual ~VM();
void execute(std::shared_ptr<Program> program);
std::string string() const;
size_t push_heap(std::shared_ptr<Value> value);
std::shared_ptr<Value> heap(size_t addr);
void set_global(size_t addr, std::shared_ptr<Value> value);
private:
size_t m_pc = 0;
std::vector<Frame> m_frames;
std::vector<param_t> m_stack;
std::vector<std::shared_ptr<Value>> m_heap;
std::unordered_map<size_t, std::shared_ptr<Value>> m_globals;
void push(param_t param);
param_t pop();
};
}
#endif

View File

@ -1,5 +1,7 @@
#include "Value.hpp"
#include "Type.hpp"
#include "Function.hpp"
#include "Code.hpp"
namespace jk
{
@ -18,8 +20,61 @@ namespace jk
return value;
}
/*static*/ std::shared_ptr<Value>
Value::make_function(std::shared_ptr<Function> val)
{
auto value = std::make_shared<Value>();
value->m_type = std::make_shared<Type>(TYPE_FUNCTION);
value->m_function_val = val;
return value;
}
/*static*/ std::shared_ptr<Value>
Value::make_code(std::shared_ptr<Code> val)
{
auto value = std::make_shared<Value>();
value->m_type = std::make_shared<Type>(TYPE_CODE);
value->m_code_val = val;
return value;
}
/*static*/ std::shared_ptr<Value>
Value::make_ref(size_t val)
{
auto value = std::make_shared<Value>();
value->m_type = std::make_shared<Type>(TYPE_REF);
value->m_ref_val = val;
return value;
}
std::shared_ptr<Function> Value::as_function() const
{
return m_function_val;
}
std::shared_ptr<Code> Value::as_code() const
{
return m_code_val;
}
std::weak_ptr<Type> Value::type() const
{
return m_type;
}
std::string Value::string() const
{
switch (m_type->type())
{
case TYPE_NIL: return "<nil>";
case TYPE_INT: return std::to_string(*m_int_val);
default:
std::cerr << "cannot stringify value '"
<< TypeTypeStr[m_type->type()]
<< "'"
<< std::endl;
abort();
}
}
}

View File

@ -6,23 +6,38 @@
namespace jk
{
class Type;
class Function;
class Code;
class Value
{
public:
static std::shared_ptr<Value> make_nil();
static std::shared_ptr<Value> make_int(int val);
static std::shared_ptr<Value> make_function(std::shared_ptr<Function>
val);
static std::shared_ptr<Value> make_code(std::shared_ptr<Code> val);
static std::shared_ptr<Value> make_ref(size_t val);
explicit Value() = default;
virtual ~Value() = default;
int as_int() const { return *m_int_val; }
size_t as_ref() const { return *m_ref_val; }
std::shared_ptr<Function> as_function() const;
std::shared_ptr<Code> as_code() const;
std::weak_ptr<Type> type() const;
std::string string() const;
private:
std::shared_ptr<Type> m_type;
std::optional<int> m_int_val;
std::shared_ptr<Function> m_function_val;
std::shared_ptr<Code> m_code_val;
std::optional<size_t> m_ref_val;
};
}

View File

@ -14,5 +14,6 @@
#include <filesystem>
#include "mutils.hpp"
#include "config.hpp"
#endif

View File

@ -32,13 +32,30 @@ joko_lib = static_library(
'lib/Type.cpp',
'lib/Value.cpp',
'lib/Function.cpp',
'lib/Code.cpp',
'lib/SymTable.cpp',
'lib/StaticPass.cpp',
'lib/Compiler.cpp',
'lib/Program.cpp',
'lib/VM.cpp',
'lib/Loader.cpp',
],
dependencies: [
])
joko_dep = declare_dependency(link_with: joko_lib)
joko_core_lib = shared_library('joko-core',
sources: [
'core/core.cpp'
],
dependencies: [
joko_dep
],
install: true,
install_dir: joko_libdir)
executable('joko',
sources: [
'src/main.cpp',

View File

@ -2,6 +2,13 @@
#include <getopt.h>
#include "config.hpp"
#include "../lib/Lexer.hpp"
#include "../lib/Parser.hpp"
#include "../lib/SymTable.hpp"
#include "../lib/StaticPass.hpp"
#include "../lib/Compiler.hpp"
#include "../lib/VM.hpp"
#include "lib/Value.hpp"
#include "../lib/Loader.hpp"
int main(int argc, char** argv)
{
@ -64,29 +71,46 @@ int main(int argc, char** argv)
source += line + (file.eof() ? "" : "\n");
}
jk::Lexer lexer {logger, loc};
lexer.scan(source);
auto lexer = std::make_shared<jk::Lexer>(logger, loc);
auto parser = std::make_shared<jk::Parser>(logger, lexer);
auto ast = parser->parse(source);
if (debug_mode)
{
std::cout << "--- tokens ---" << std::endl;
std::shared_ptr<jk::Node> tok;
std::cout << "--- ast ---" << std::endl;
std::cout << ast->string() << std::endl;
}
std::string sep;
while ( (tok = lexer.next()) )
{
std::cout << sep << tok->string();
sep = " ";
}
auto sym = std::make_shared<jk::SymTable>(logger);
std::cout << std::endl;
auto static_pass = std::make_shared<jk::StaticPass>(sym, logger);
static_pass->pass(ast);
auto compiler = std::make_shared<jk::Compiler>(sym, logger);
auto program = std::make_shared<jk::Program>();
auto vm = std::make_shared<jk::VM>();
auto loader = std::make_shared<jk::Loader>(vm, sym);
loader->load();
compiler->compile(ast, program);
if (debug_mode)
{
std::cout << "--- program ---" << std::endl;
std::cout << program->string() << std::endl;
}
vm->execute(program);
if (debug_mode)
{
std::cout << "--- stack ---" << std::endl;
std::cout << vm->string() << std::endl;
}
return 0;
}
return 0;
}

View File

@ -50,3 +50,18 @@ TEST_CASE_METHOD(LexerTest, "Lexer_error")
lexer->scan(" § ");
REQUIRE_THROWS_AS(lexer->next(), jk::lexical_error);
}
TEST_CASE_METHOD(LexerTest, "Lexer_funcall")
{
auto lexer = jk::Factory(m_logger, "tests/lexer").make_lexer();
lexer->scan(" )( salut salut! salut? _salut -salut salut/monde");
test_next(*lexer, "CPAR");
test_next(*lexer, "OPAR");
test_next(*lexer, "IDENT[salut]");
test_next(*lexer, "IDENT[salut!]");
test_next(*lexer, "IDENT[salut?]");
test_next(*lexer, "IDENT[_salut]");
test_next(*lexer, "IDENT[-salut]");
test_next(*lexer, "IDENT[salut/monde]");
test_end(*lexer);
}

View File

@ -8,10 +8,29 @@ public:
explicit ParserTest() {}
virtual ~ParserTest() {}
void test_parser(std::string const& oracle, std::string const& source)
{
auto parser = jk::Factory(m_logger, "tests/parser").make_parser();
auto root = parser->parse(source);
REQUIRE(oracle == root->string());
}
protected:
jk::Logger m_logger;
};
TEST_CASE_METHOD(ParserTest, "Parser_")
TEST_CASE_METHOD(ParserTest, "Parser_funcall")
{
test_parser("PROG",
" ");
test_parser("PROG(FUNCALL(IDENT[hello]))",
" (hello) ");
test_parser("PROG(FUNCALL(IDENT[hello],INT[1],INT[2]))",
" (hello 1 2) ");
test_parser("PROG(FUNCALL(IDENT[hello],INT[1],INT[2],IDENT[bim!]))",
" (hello 1 2 bim!) ");
}