Compare commits

...

3 Commits

Author SHA1 Message Date
bog b1085992c8 ADD: bool arith core functions. 2023-09-10 16:16:20 +02:00
bog 05ba4cb99c ADD: bool literals. 2023-09-10 14:39:23 +02:00
bog 664ae63adf ADD: int arith core functions. 2023-09-10 12:46:42 +02:00
25 changed files with 437 additions and 15 deletions

View File

@ -1,8 +1,152 @@
#include <cmath>
#include "lib/Function.hpp"
#include "lib/Loader.hpp"
#include "lib/Node.hpp"
#include "lib/Opcodes.hpp"
#include "lib/SymTable.hpp"
#include "lib/Value.hpp"
extern "C" void num_arith(jk::Loader& loader)
{
loader.declare("+", [](auto args){
int res = 0;
for (auto arg: args)
{
res += arg->as_int();
}
return jk::Value::make_int(res);
});
loader.declare("-", [](auto args){
int res = args[0]->as_int();
for (size_t i=1; i<args.size(); i++)
{
res -= args[i]->as_int();
}
return jk::Value::make_int(res);
});
loader.declare("*", [](auto args){
int res = 1;
for (auto arg: args)
{
res *= arg->as_int();
}
return jk::Value::make_int(res);
});
loader.declare("/", [](auto args){
int res = args[0]->as_int();
for (size_t i=1; i<args.size(); i++)
{
res /= args[i]->as_int();
}
return jk::Value::make_int(res);
});
loader.declare("^", [](auto args){
int res = args[0]->as_int();
for (size_t i=1; i<args.size(); i++)
{
res = std::pow(res, args[i]->as_int());
}
return jk::Value::make_int(res);
});
loader.declare("%", [](auto args){
int res = args[0]->as_int();
for (size_t i=1; i<args.size(); i++)
{
res %= args[i]->as_int();
}
return jk::Value::make_int(res);
});
}
extern "C" void bool_arith(jk::Loader& loader)
{
loader.declare_static("and", [loader](std::shared_ptr<jk::Node> node,
std::shared_ptr<jk::Program> program,
std::shared_ptr<jk::SymTable> sym) {
std::vector<size_t> to_false;
for (size_t i=1; i<node->size(); i++)
{
loader.compiler()->compile(node->child(i).lock(), program, sym);
to_false.push_back(program->size());
program->push_instr(jk::OPCODE_BRF, 0);
}
program->push_instr(jk::OPCODE_PUSH_CONST,
program->push_constant(jk::Value::make_bool(true)));
size_t end = program->size();
program->push_instr(jk::OPCODE_BR, 0);
for (auto addr: to_false)
{
program->set_param(addr, program->size());
}
program->push_instr(jk::OPCODE_PUSH_CONST,
program->push_constant(jk::Value::make_bool(false)));
program->set_param(end, program->size());
});
loader.declare_static("or", [loader](std::shared_ptr<jk::Node> node,
std::shared_ptr<jk::Program> program,
std::shared_ptr<jk::SymTable> sym) {
std::vector<size_t> to_false;
for (size_t i=1; i<node->size(); i++)
{
loader.compiler()->compile(node->child(i).lock(), program, sym);
program->push_instr(jk::OPCODE_NOT);
to_false.push_back(program->size());
program->push_instr(jk::OPCODE_BRF, 0);
}
program->push_instr(jk::OPCODE_PUSH_CONST,
program->push_constant(jk::Value::make_bool(false)));
size_t end = program->size();
program->push_instr(jk::OPCODE_BR, 0);
for (auto addr: to_false)
{
program->set_param(addr, program->size());
}
program->push_instr(jk::OPCODE_PUSH_CONST,
program->push_constant(jk::Value::make_bool(true)));
program->set_param(end, program->size());
});
loader.declare("not", [](auto args){
auto value = args[0]->as_bool();
return jk::Value::make_bool(!value);
});
}
extern "C" void lib(jk::Loader& loader)
{
num_arith(loader);
bool_arith(loader);
loader.declare("dump", [](auto args){
std::string sep;

View File

@ -11,4 +11,4 @@ BODY ::= expr*
VARDECL ::= opar decl ident EXPR cpar
FUNDECL ::= opar decl opar ident PARAMS cpar EXPR cpar
FUNCALL ::= opar ident EXPR* cpar
LITERAL ::= int | ident
LITERAL ::= bool | int | ident

View File

@ -1,6 +1,7 @@
#include "Compiler.hpp"
#include "lib/Opcodes.hpp"
#include "Code.hpp"
#include "StaticFunction.hpp"
namespace jk
{
@ -28,12 +29,21 @@ namespace jk
} break;
case NODE_FUNCALL: {
for (size_t i=0; i<node->size(); i++)
if (auto itr=m_statics.find(node->child(0).lock()->repr());
itr != std::end(m_statics))
{
compile(node->child(i).lock(), program, sym);
itr->second->call(node, program, sym);
}
else
{
for (size_t i=0; i<node->size(); i++)
{
compile(node->child(i).lock(), program, sym);
}
program->push_instr(OPCODE_CALL, node->size() - 1);
}
program->push_instr(OPCODE_CALL, node->size() - 1);
} break;
case NODE_VARDECL: {
@ -66,7 +76,6 @@ namespace jk
size_t addr = program->push_constant(Value::make_code(code));
program->push_instr(OPCODE_PUSH_CONST, addr);
program->push_instr(OPCODE_MK_FUNCTION);
//program->push_instr(OpcodeType opcode)
} break;
case NODE_IDENT: {
@ -91,6 +100,12 @@ namespace jk
program->push_instr(OPCODE_PUSH_CONST, addr);
} break;
case NODE_BOOL: {
auto value = Value::make_bool(node->repr() == "true");
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;
@ -99,4 +114,9 @@ namespace jk
}
}
void Compiler::declare_static(std::string const& name,
std::shared_ptr<StaticFunction> fun)
{
m_statics[name] = fun;
}
}

View File

@ -12,6 +12,8 @@ namespace jk
{
JK_ERROR(compile_error);
class StaticFunction;
class Compiler
{
public:
@ -22,8 +24,13 @@ namespace jk
std::shared_ptr<Program> program,
std::shared_ptr<SymTable> sym);
void declare_static(std::string const& name,
std::shared_ptr<StaticFunction> fun);
private:
Logger& m_logger;
std::unordered_map<std::string,
std::shared_ptr<StaticFunction>> m_statics;
};
}

View File

@ -6,6 +6,11 @@ namespace jk
: m_logger { logger }
, m_loc { loc }
{
std::vector<std::tuple<NodeType, std::string, bool>> keywords = {
{NODE_BOOL, "true", true},
{NODE_BOOL, "false", true},
};
std::vector<std::tuple<NodeType, std::string, bool>> texts = {
{NODE_RARROW, "->", false},
{NODE_DECL, "$", false},
@ -15,6 +20,7 @@ namespace jk
for (auto text: texts)
{
m_texts.push_back(std::get<1>(text));
m_scanners.push_back(std::bind(&Lexer::scan_text,
this,
std::get<0>(text),
@ -22,6 +28,15 @@ namespace jk
std::get<2>(text)));
}
for (auto kw: keywords)
{
m_scanners.push_back(std::bind(&Lexer::scan_keyword,
this,
std::get<0>(kw),
std::get<1>(kw),
std::get<2>(kw)));
}
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
}
@ -103,6 +118,24 @@ namespace jk
return m_source[index];
}
bool Lexer::is_sep(size_t index) const
{
if (std::isspace(at(index)))
{
return true;
}
for (auto txt: m_texts)
{
if (txt == std::string(1, at(index)))
{
return true;
}
}
return false;
}
void Lexer::skip_spaces()
{
while (more(m_cursor)
@ -169,12 +202,46 @@ namespace jk
};
}
std::optional<ScanInfo> Lexer::scan_keyword(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;
}
}
if (m_cursor + text.size() < m_source.size()
&& !is_sep(m_cursor + text.size()))
{
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 == '^'
|| c == '%'
|| c == '?'
|| c == '!'
|| c == '/';

View File

@ -34,9 +34,11 @@ namespace jk
size_t m_cursor;
std::string m_source;
std::vector<scanner_t> m_scanners;
std::vector<std::string> m_texts;
bool more(size_t index) const;
char at(size_t index) const;
bool is_sep(size_t index) const;
void skip_spaces();
@ -44,6 +46,11 @@ namespace jk
std::optional<ScanInfo> scan_text(NodeType type,
std::string const& text,
bool has_value) const;
std::optional<ScanInfo> scan_keyword(NodeType type,
std::string const& text,
bool has_value) const;
std::optional<ScanInfo> scan_ident() const;
};
}

View File

@ -1,13 +1,18 @@
#include "Loader.hpp"
#include "lib/Function.hpp"
#include "lib/StaticFunction.hpp"
#include "lib/config.in.hpp"
#include <dlfcn.h>
namespace jk
{
/*explicit*/ Loader::Loader(std::shared_ptr<VM> vm,
std::shared_ptr<StaticPass> static_pass,
std::shared_ptr<Compiler> compiler,
std::shared_ptr<SymTable> sym)
: m_vm { vm }
, m_static_pass { static_pass }
, m_compiler { compiler }
, m_sym { sym }
{
}
@ -42,9 +47,15 @@ namespace jk
auto ref = Value::make_ref(addr);
size_t global_addr = m_sym->declare_global(name,
fun_val->type().lock(),
Loc {"global", 1, 1});
fun_val->type().lock(),
Loc {"global", 1, 1});
m_vm->set_global(global_addr, ref);
}
void Loader::declare_static(std::string const& name, static_fun_t body)
{
m_static_pass->add_static(name);
auto fun = std::make_shared<StaticFunction>(body);
m_compiler->declare_static(name, fun);
}
}

View File

@ -5,6 +5,9 @@
#include "VM.hpp"
#include "SymTable.hpp"
#include "Function.hpp"
#include "StaticFunction.hpp"
#include "Compiler.hpp"
#include "StaticPass.hpp"
namespace jk
{
@ -12,14 +15,21 @@ namespace jk
{
public:
explicit Loader(std::shared_ptr<VM> vm,
std::shared_ptr<StaticPass> static_pass,
std::shared_ptr<Compiler> compiler,
std::shared_ptr<SymTable> sym);
virtual ~Loader();
std::shared_ptr<Compiler> compiler() const { return m_compiler; }
void load();
void declare(std::string const& name, foreign_t body);
void declare_static(std::string const& name, static_fun_t body);
private:
std::shared_ptr<VM> m_vm;
std::shared_ptr<StaticPass> m_static_pass;
std::shared_ptr<Compiler> m_compiler;
std::shared_ptr<SymTable> m_sym;
};
}

View File

@ -7,6 +7,7 @@
#define NODE_TYPE(G) \
G(NODE_PROG), \
G(NODE_INT), \
G(NODE_BOOL), \
G(NODE_OPAR), \
G(NODE_CPAR), \
G(NODE_IDENT), \

View File

@ -10,6 +10,9 @@
G(OPCODE_STORE), \
G(OPCODE_LOAD_GLOBAL), \
G(OPCODE_RET), \
G(OPCODE_BRF), \
G(OPCODE_BR), \
G(OPCODE_NOT), \
G(OPCODE_MK_FUNCTION)
namespace jk

View File

@ -220,6 +220,7 @@ namespace jk
std::shared_ptr<Node> Parser::parse_literal()
{
if (type_is(NODE_INT)
|| type_is(NODE_BOOL)
|| type_is(NODE_IDENT))
{
return consume();

View File

@ -22,6 +22,12 @@ namespace jk
m_instrs.push_back(Instr { opcode, param });
}
void Program::set_param(size_t index, param_t param)
{
assert(index < size());
m_instrs[index].param = param;
}
std::string Program::string() const
{
std::stringstream ss;

View File

@ -27,6 +27,8 @@ namespace jk
void push_instr(OpcodeType opcode,
std::optional<param_t> param = std::nullopt);
void set_param(size_t index, param_t param);
size_t push_constant(std::shared_ptr<Value> value);
std::shared_ptr<Value> constant(size_t index) const;

20
lib/StaticFunction.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "StaticFunction.hpp"
namespace jk
{
/*explicit*/ StaticFunction::StaticFunction(static_fun_t fun)
: m_fun { fun }
{
}
/*virtual*/ StaticFunction::~StaticFunction()
{
}
void StaticFunction::call(std::shared_ptr<Node> node,
std::shared_ptr<Program> program,
std::shared_ptr<SymTable> sym)
{
m_fun(node, program, sym);
}
}

32
lib/StaticFunction.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef jk_STATICFUNCTION_HPP
#define jk_STATICFUNCTION_HPP
#include "commons.hpp"
#include "Program.hpp"
#include "SymTable.hpp"
#include "Node.hpp"
namespace jk
{
using static_fun_t =
std::function<void
(std::shared_ptr<Node>,
std::shared_ptr<Program>,
std::shared_ptr<SymTable>)>;
class StaticFunction
{
public:
explicit StaticFunction(static_fun_t fun);
virtual ~StaticFunction();
void call(std::shared_ptr<Node> node,
std::shared_ptr<Program> program,
std::shared_ptr<SymTable> sym);
private:
static_fun_t m_fun;
};
}
#endif

View File

@ -19,6 +19,7 @@ namespace jk
{
case NODE_PROG:
case NODE_BODY: {
for (size_t i=0; i<node->size(); i++)
{
pass(node->child(i).lock());
@ -48,11 +49,17 @@ namespace jk
push(std::make_shared<Type>(TYPE_INT));
} break;
case NODE_BOOL: {
push(std::make_shared<Type>(TYPE_BOOL));
} break;
case NODE_IDENT: {
std::string ident = node->repr();
auto entry = m_sym->find(ident);
if (!entry)
if (!entry
&& std::find(std::begin(m_statics), std::end(m_statics),
ident) == std::end(m_statics))
{
m_logger.log<symbolic_error>(LOG_ERROR, node->loc(),
std::string()
@ -61,7 +68,10 @@ namespace jk
+ "' is undefined");
}
push(entry->type);
if (entry)
{
push(entry->type);
}
} break;
case NODE_VARDECL: {
@ -73,13 +83,22 @@ namespace jk
} break;
case NODE_FUNCALL: {
for (size_t i=0; i<node->size(); i++)
auto is_static = std::find(std::begin(m_statics),
std::end(m_statics),
node->child(0).lock()->repr())
!= std::end(m_statics);
if (!is_static)
{
pass(node->child(i).lock());
pop();
for (size_t i=0; i<node->size(); i++)
{
pass(node->child(i).lock());
pop();
}
}
// TODO find actual returned type
push(std::make_shared<Type>(TYPE_NIL));
} break;
default:
@ -89,6 +108,11 @@ namespace jk
}
}
void StaticPass::add_static(std::string const& name)
{
m_statics.push_back(name);
}
void StaticPass::push(std::shared_ptr<Type> type)
{
m_types.push_back(type);

View File

@ -15,10 +15,13 @@ namespace jk
void pass(std::shared_ptr<Node> node);
void add_static(std::string const& name);
private:
std::shared_ptr<SymTable> m_sym;
Logger& m_logger;
std::vector<std::shared_ptr<Type>> m_types;
std::vector<std::string> m_statics;
void push(std::shared_ptr<Type> type);
std::shared_ptr<Type> pop();

View File

@ -6,6 +6,7 @@
#define TYPE_TYPE(G) \
G(TYPE_NIL), \
G(TYPE_INT), \
G(TYPE_BOOL), \
G(TYPE_FUNCTION), \
G(TYPE_CODE), \
G(TYPE_REF)

View File

@ -29,6 +29,31 @@ namespace jk
switch (instr.opcode)
{
case OPCODE_BRF: {
auto value = program()->constant(pop());
if (!value->as_bool())
{
m_pc = *instr.param;
}
else
{
m_pc++;
}
} break;
case OPCODE_BR: {
m_pc = *instr.param;
} break;
case OPCODE_NOT: {
bool value = program()->constant(pop())->as_bool();
push(program()->push_constant(Value::make_bool(!value)));
m_pc++;
} break;
case OPCODE_MK_FUNCTION: {
auto code = program()->constant(pop());

View File

@ -20,6 +20,14 @@ namespace jk
return value;
}
/*static*/ std::shared_ptr<Value> Value::make_bool(bool val)
{
auto value = std::make_shared<Value>();
value->m_type = std::make_shared<Type>(TYPE_BOOL);
value->m_bool_val = val;
return value;
}
/*static*/ std::shared_ptr<Value>
Value::make_function(std::shared_ptr<Function> val)
{
@ -68,6 +76,7 @@ namespace jk
{
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_REF: return "<ref:" + std::to_string(*m_ref_val) + ">";
default:

View File

@ -14,6 +14,7 @@ namespace jk
public:
static std::shared_ptr<Value> make_nil();
static std::shared_ptr<Value> make_int(int val);
static std::shared_ptr<Value> make_bool(bool 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);
@ -23,6 +24,8 @@ namespace jk
virtual ~Value() = default;
int as_int() const { return *m_int_val; }
int as_bool() const { return *m_bool_val; }
size_t as_ref() const { return *m_ref_val; }
std::shared_ptr<Function> as_function() const;
@ -35,6 +38,7 @@ namespace jk
private:
std::shared_ptr<Type> m_type;
std::optional<int> m_int_val;
std::optional<bool> m_bool_val;
std::shared_ptr<Function> m_function_val;
std::shared_ptr<Code> m_code_val;
std::optional<size_t> m_ref_val;

View File

@ -33,6 +33,7 @@ joko_lib = static_library(
'lib/Type.cpp',
'lib/Value.cpp',
'lib/Function.cpp',
'lib/StaticFunction.cpp',
'lib/Code.cpp',
'lib/SymTable.cpp',
'lib/StaticPass.cpp',

View File

@ -85,12 +85,15 @@ int main(int argc, char** argv)
auto compiler = std::make_shared<jk::Compiler>(logger);
auto static_pass = std::make_shared<jk::StaticPass>(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);
auto loader = std::make_shared<jk::Loader>(vm, static_pass,
compiler, sym);
loader->load();
auto static_pass = std::make_shared<jk::StaticPass>(sym, logger);
static_pass->pass(ast);
compiler->compile(ast, program, sym);

View File

@ -81,3 +81,18 @@ TEST_CASE_METHOD(LexerTest, "Lexer_lambda")
test_next(*lexer, "RARROW");
test_end(*lexer);
}
TEST_CASE_METHOD(LexerTest, "Lexer_bools")
{
auto lexer = jk::Factory(m_logger, "tests/lexer").make_lexer();
lexer->scan(" true false truea afalse (true) false!");
test_next(*lexer, "BOOL[true]");
test_next(*lexer, "BOOL[false]");
test_next(*lexer, "IDENT[truea]");
test_next(*lexer, "IDENT[afalse]");
test_next(*lexer, "OPAR");
test_next(*lexer, "BOOL[true]");
test_next(*lexer, "CPAR");
test_next(*lexer, "IDENT[false!]");
test_end(*lexer);
}

View File

@ -71,3 +71,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_fundecl")
")))",
" ($ (f x y) (add y x)) ");
}
TEST_CASE_METHOD(ParserTest, "Parser_bool")
{
test_parser("PROG(VARDECL(IDENT[hello],BOOL[true]))",
"($ hello true)");
}