From ab34370e390aa628fe74487833d750071f4beef5 Mon Sep 17 00:00:00 2001 From: bog Date: Mon, 18 Mar 2024 18:20:40 +0100 Subject: [PATCH] :sparkles: num arithmetic. --- Makefile | 3 +- ccm/main.c | 49 +++++- doc/grammar.bnf | 16 ++ lib/CMakeLists.txt | 17 ++ lib/bytecode.c | 3 + lib/bytecode.h | 14 ++ lib/ccm.c | 210 +++++++++++++++++++++++++ lib/ccm.h | 42 +++++ lib/commons.h | 24 +++ lib/compiler.c | 116 ++++++++++++++ lib/compiler.h | 22 +++ lib/err.c | 43 +++++ lib/err.h | 23 +++ lib/exec.c | 130 ++++++++++++++++ lib/exec.h | 22 +++ lib/lexer.c | 354 +++++++++++++++++++++++++++++++++++++++++ lib/lexer.h | 56 +++++++ lib/module.c | 115 ++++++++++++++ lib/module.h | 22 +++ lib/node.c | 77 +++++++++ lib/node.h | 30 +++- lib/parser.c | 381 +++++++++++++++++++++++++++++++++++++++++++++ lib/parser.h | 35 +++++ lib/prog.c | 65 ++++++++ lib/prog.h | 31 ++++ lib/str.c | 69 ++++++++ lib/str.h | 20 +++ lib/type.c | 3 + lib/type.h | 12 ++ lib/value.c | 148 ++++++++++++++++++ lib/value.h | 26 ++++ lib/vec.c | 72 +++++++++ lib/vec.h | 22 +++ tests/num.ccm | 23 +++ tests/run.sh | 39 +++++ 35 files changed, 2328 insertions(+), 6 deletions(-) create mode 100644 doc/grammar.bnf create mode 100644 lib/bytecode.c create mode 100644 lib/bytecode.h create mode 100644 lib/ccm.c create mode 100644 lib/ccm.h create mode 100644 lib/commons.h create mode 100644 lib/compiler.c create mode 100644 lib/compiler.h create mode 100644 lib/err.c create mode 100644 lib/err.h create mode 100644 lib/exec.c create mode 100644 lib/exec.h create mode 100644 lib/lexer.c create mode 100644 lib/lexer.h create mode 100644 lib/module.c create mode 100644 lib/module.h create mode 100644 lib/parser.c create mode 100644 lib/parser.h create mode 100644 lib/prog.c create mode 100644 lib/prog.h create mode 100644 lib/str.c create mode 100644 lib/str.h create mode 100644 lib/type.c create mode 100644 lib/type.h create mode 100644 lib/value.c create mode 100644 lib/value.h create mode 100644 lib/vec.c create mode 100644 lib/vec.h create mode 100644 tests/num.ccm create mode 100755 tests/run.sh diff --git a/Makefile b/Makefile index 598d72a..67b177e 100644 --- a/Makefile +++ b/Makefile @@ -9,4 +9,5 @@ install: build check: @cppcheck --enable=all -q ccm lib \ - --suppress=missingIncludeSystem + --suppress=missingIncludeSystem \ + --suppress=unusedFunction diff --git a/ccm/main.c b/ccm/main.c index 059c5a0..adaaf23 100644 --- a/ccm/main.c +++ b/ccm/main.c @@ -1,7 +1,50 @@ #include +#include +#include -int main() +int main(int argc, char** argv) { - printf("Hello World!\n"); - return 0; + int status = 0; + + if (argc > 1) + { + module_t module; + module_init(&module); + + module_load(&module, argv[1]); + + if (!err_is_ok(&module.err)) + { + err_print_stack_trace(&module.err); + status = 1; + goto free_module; + } + + exec_t exec; + exec_init(&exec); + + exec_module(&exec, &module); + + if (!err_is_ok(&module.ccm.err) + || !err_is_ok(&exec.err)) + { + err_print_stack_trace(&module.ccm.err); + err_print_stack_trace(&exec.err); + status = 1; + goto free_exec; + } + + char trace[CCM_STRLEN]; + memset(trace, 0, CCM_STRLEN); + + ccm_str(&module.ccm, trace, CCM_STRLEN); + printf("%s\n", trace); + + free_exec: + exec_free(&exec); + free_module: + module_free(&module); + } + + return status; } diff --git a/doc/grammar.bnf b/doc/grammar.bnf new file mode 100644 index 0000000..50a8ae5 --- /dev/null +++ b/doc/grammar.bnf @@ -0,0 +1,16 @@ +MODULE ::= EXPR* +EXPR ::= +| TERM +| ASSERT +ASSERT ::= (assert_eq|assert_ne) tuple +TERM ::= FACTOR ((add|sub) FACTOR)* +FACTOR ::= USUB ((mul|div|mod) USUB)* +USUB ::= sub* POW +POW ::= LITERAL (pow LITERAL)? +LITERAL ::= +| BUILTIN +| TUPLE +| opar EXPR cpar +TUPLE ::= +| opar EXPR+ cpar +BUILTIN ::= num diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a11bfc4..f48f33b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,7 +3,20 @@ cmake_minimum_required(VERSION 3.28) project(ccm_library) add_library(ccm_lib + vec.c + str.c node.c + lexer.c + parser.c + module.c + err.c + type.c + value.c + ccm.c + bytecode.c + prog.c + compiler.c + exec.c ) set_property(TARGET ccm_lib PROPERTY C_STANDARD 99) @@ -15,4 +28,8 @@ target_compile_options(ccm_lib target_include_directories(ccm_lib PUBLIC "${CMAKE_SOURCE_DIR}/lib" ) + +target_link_libraries(ccm_lib + PUBLIC -lm +) install(TARGETS ccm_lib) diff --git a/lib/bytecode.c b/lib/bytecode.c new file mode 100644 index 0000000..7df0e26 --- /dev/null +++ b/lib/bytecode.c @@ -0,0 +1,3 @@ +#include "bytecode.h" + +CCM_ENUM_C(Opcode, OPCODES); diff --git a/lib/bytecode.h b/lib/bytecode.h new file mode 100644 index 0000000..fa70b12 --- /dev/null +++ b/lib/bytecode.h @@ -0,0 +1,14 @@ +#ifndef CCM_BYTECODE_H +#define CCM_BYTECODE_H + +#include "commons.h" + +#define OPCODES(G) \ +G(OP_PUSH), G(OP_POP), \ +G(OP_ADD), G(OP_SUB), G(OP_USUB), G(OP_MUL), \ +G(OP_DIV), G(OP_POW), G(OP_MOD), G(OP_MK_TUPLE), \ +G(OP_ASSERT_EQ), G(OP_ASSERT_NE) + +CCM_ENUM_H(Opcode, OPCODES); + +#endif diff --git a/lib/ccm.c b/lib/ccm.c new file mode 100644 index 0000000..46804bf --- /dev/null +++ b/lib/ccm.c @@ -0,0 +1,210 @@ +#include "ccm.h" + +void ccm_init(ccm_t* self) +{ + assert(self); + vec_init(&self->values); + vec_init(&self->stack); + err_init(&self->err); +} + +void ccm_free(ccm_t* self) +{ + assert(self); + err_free(&self->err); + vec_free_elements(&self->values, (void*) value_free); + vec_free(&self->values); + vec_free(&self->stack); +} + +size_t ccm_str(ccm_t* self, char* buffer, size_t size) +{ + assert(self); + assert(buffer); + + size_t sz = 0; + + for (size_t i=0; istack.size; i++) + { + value_t* val = self->values.data[(CCM) self->stack.data[i]]; + sz += value_str(val, buffer + sz, size - sz); + sz += snprintf(buffer + sz, size - sz, "\n"); + } + + return sz; +} + +int ccm_is_num(ccm_t* self, CCM value) +{ + value_t const* val = self->values.data[value]; + return val->type == TYPE_NUM; +} + +double ccm_from_num(ccm_t* self, CCM value) +{ + assert(self); + assert(value < self->values.size); + + if (!ccm_is_num(self, value)) + { + err_push(&self->err, + ((value_t*) self->values.data[value])->line, + "not a num"); + return 0.0; + } + + value_t* val = self->values.data[value]; + + return val->data.num; +} + +CCM ccm_to_num(ccm_t* self, double value, int line) +{ + assert(self); + value_t* val = malloc(sizeof(value_t)); + value_init_num(val, value, line); + vec_push(&self->values, val); + + return self->values.size - 1; +} + +int ccm_is_tuple(ccm_t* self, CCM value) +{ + assert(self); + value_t* val = self->values.data[value]; + assert(val); + return val->type == TYPE_TUPLE; +} + +vec_t* ccm_from_tuple(ccm_t* self, CCM value) +{ + assert(self); + value_t* val = self->values.data[value]; + if (!ccm_is_tuple(self, value)) + { + err_push(&self->err, val->line, "not a tuple"); + return NULL; + } + return val->data.tuple; +} + +CCM ccm_to_tuple(ccm_t* self, vec_t* values, int line) +{ + assert(self); + assert(values); + value_t* value = malloc(sizeof(value_t)); + value_init_new_tuple(value, values, line); + + vec_push(&self->values, value); + return self->values.size - 1; +} + +void ccm_push(ccm_t* self, CCM value) +{ + assert(self); + vec_push(&self->stack, (void*) value); +} + +CCM ccm_pop(ccm_t* self) +{ + assert(self); + void* val = vec_pop(&self->stack); + return (CCM) val; +} + +CCM ccm_top(ccm_t* self, int depth) +{ + assert(self); + assert((size_t) depth < self->stack.size); + + return (CCM) self->stack.data[self->stack.size - 1 - depth]; +} + +void ccm_add(ccm_t* self) +{ + assert(self); + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double rhs = ccm_from_num(self, ccm_rhs); + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, lhs + rhs, line)); +} + +void ccm_sub(ccm_t* self) +{ + assert(self); + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double rhs = ccm_from_num(self, ccm_rhs); + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, lhs - rhs, line)); +} + +void ccm_usub(ccm_t* self) +{ + assert(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, -lhs, line)); +} + +void ccm_mul(ccm_t* self) +{ + assert(self); + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double rhs = ccm_from_num(self, ccm_rhs); + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, lhs * rhs, line)); +} + +void ccm_div(ccm_t* self) +{ + assert(self); + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double rhs = ccm_from_num(self, ccm_rhs); + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, lhs / rhs, line)); +} + +void ccm_mod(ccm_t* self) +{ + assert(self); + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double rhs = ccm_from_num(self, ccm_rhs); + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, fmod(lhs, rhs), line)); +} + +void ccm_pow(ccm_t* self) +{ + assert(self); + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + double rhs = ccm_from_num(self, ccm_rhs); + double lhs = ccm_from_num(self, ccm_lhs); + + ccm_push(self, ccm_to_num(self, powf(lhs, rhs), line)); +} diff --git a/lib/ccm.h b/lib/ccm.h new file mode 100644 index 0000000..d762344 --- /dev/null +++ b/lib/ccm.h @@ -0,0 +1,42 @@ +#ifndef CCM_CCM_H +#define CCM_CCM_H + +#include "commons.h" +#include "vec.h" +#include "value.h" +#include "err.h" + +typedef unsigned long long CCM; + +typedef struct { + err_t err; + vec_t values; + vec_t stack; +} ccm_t; + +void ccm_init(ccm_t* self); +void ccm_free(ccm_t* self); + +size_t ccm_str(ccm_t* self, char* buffer, size_t size); + +int ccm_is_num(ccm_t* self, CCM value); +double ccm_from_num(ccm_t* self, CCM value); +CCM ccm_to_num(ccm_t* self, double value, int line); + +int ccm_is_tuple(ccm_t* self, CCM value); +vec_t* ccm_from_tuple(ccm_t* self, CCM value); +CCM ccm_to_tuple(ccm_t* self, vec_t* values, int line); + +void ccm_push(ccm_t* self, CCM value); +CCM ccm_pop(ccm_t* self); +CCM ccm_top(ccm_t* self, int depth); + +void ccm_add(ccm_t* self); +void ccm_sub(ccm_t* self); +void ccm_usub(ccm_t* self); +void ccm_mul(ccm_t* self); +void ccm_div(ccm_t* self); +void ccm_mod(ccm_t* self); +void ccm_pow(ccm_t* self); + +#endif diff --git a/lib/commons.h b/lib/commons.h new file mode 100644 index 0000000..d28c60f --- /dev/null +++ b/lib/commons.h @@ -0,0 +1,24 @@ +#ifndef CCM_COMMONS_H +#define CCM_COMMONS_H + +#include +#include +#include +#include +#include +#include +#include + +#define CCM_STRLEN 256 +#define CCM_ENUM_ENUM(X) X +#define CCM_ENUM_STR(X) #X + +#define CCM_ENUM_H(PREFIX, DEF) \ + typedef enum { DEF(CCM_ENUM_ENUM) } PREFIX ; \ + extern char const* PREFIX ## Str [] + + +#define CCM_ENUM_C(PREFIX, DEF) \ + char const* PREFIX ## Str [] = {DEF(CCM_ENUM_STR)} + +#endif diff --git a/lib/compiler.c b/lib/compiler.c new file mode 100644 index 0000000..d9d19ad --- /dev/null +++ b/lib/compiler.c @@ -0,0 +1,116 @@ +#include "compiler.h" + +void compiler_init(compiler_t* self, module_t* module) +{ + assert(self); + err_init(&self->err); + self->module = module; +} + +void compiler_free(compiler_t* self) +{ + assert(self); + err_free(&self->err); +} + +void compiler_compile(compiler_t* self, + node_t* node, + prog_t* prog) +{ + assert(self); + assert(node); + assert(prog); + + if (!err_is_ok(&self->err)) + { + return; + } + + switch (node->kind) + { + case NODE_ASSERT_EQ: { + compiler_compile(self, node->children.data[0], prog); + prog_add_instr(prog, OP_ASSERT_EQ, CCM_NO_PARAM); + } break; + + case NODE_ASSERT_NE: { + compiler_compile(self, node->children.data[0], prog); + prog_add_instr(prog, OP_ASSERT_NE, CCM_NO_PARAM); + } break; + + case NODE_MODULE: { + for (size_t i=0; ichildren.size; i++) + { + compiler_compile(self, + node->children.data[i], + prog); + } + } break; + + case NODE_NUM: { + size_t id = prog_add_new_constant( + prog, + ccm_to_num(&self->module->ccm, + atof(node->value), + node->line) + ); + + prog_add_instr(prog, OP_PUSH, id); + } break; + + case NODE_TUPLE: { + for (size_t i=0; ichildren.size; i++) + { + size_t k = node->children.size - 1 - i; + + compiler_compile(self, + node->children.data[k], + prog); + } + + prog_add_instr(prog, OP_MK_TUPLE, node->children.size); + } break; + + case NODE_SUB: { + compiler_compile(self, node->children.data[0], prog); + + if (node->children.size == 2) + { + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_SUB, CCM_NO_PARAM); + } + else + { + prog_add_instr(prog, OP_USUB, CCM_NO_PARAM); + } + } break; + + case NODE_ADD: + case NODE_MUL: + case NODE_DIV: + case NODE_MOD: + case NODE_POW: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + Opcode op; + + switch (node->kind) + { + case NODE_ADD: op = OP_ADD; break; + case NODE_MUL: op = OP_MUL; break; + case NODE_DIV: op = OP_DIV; break; + case NODE_MOD: op = OP_MOD; break; + case NODE_POW: op = OP_POW; break; + default: abort(); + } + + prog_add_instr(prog, op, CCM_NO_PARAM); + } break; + + default: { + fprintf(stderr, "cannot compile node %s\n", + NodeKindStr[node->kind]); + abort(); + } + } +} diff --git a/lib/compiler.h b/lib/compiler.h new file mode 100644 index 0000000..27b3126 --- /dev/null +++ b/lib/compiler.h @@ -0,0 +1,22 @@ +#ifndef CCM_COMPILER_H +#define CCM_COMPILER_H + +#include "commons.h" +#include "bytecode.h" +#include "prog.h" +#include "node.h" +#include "err.h" +#include "module.h" + +typedef struct { + module_t* module; + err_t err; +} compiler_t; + +void compiler_init(compiler_t* self, module_t* module); +void compiler_free(compiler_t* self); + +void compiler_compile(compiler_t* self, + node_t* node, + prog_t* prog); +#endif diff --git a/lib/err.c b/lib/err.c new file mode 100644 index 0000000..cc00629 --- /dev/null +++ b/lib/err.c @@ -0,0 +1,43 @@ +#include "err.h" + +void err_init(err_t* self) +{ + assert(self); + vec_init(&self->logs); +} + +void err_free(err_t* self) +{ + assert(self); + vec_free_elements(&self->logs, NULL); + vec_free(&self->logs); +} + +int err_is_ok(err_t* self) +{ + return self->logs.size == 0; +} + +void err_push(err_t* self, int line, char const* format, ...) +{ + va_list lst; + va_start(lst, format); + err_log_t* log = malloc(sizeof(err_log_t)); + log->line = line; + vsnprintf(log->msg, CCM_STRLEN, format, lst); + va_end(lst); + + vec_push(&self->logs, log); +} + +void err_print_stack_trace(err_t* self) +{ + assert(self); + + for (size_t i=0; ilogs.size; i++) + { + err_log_t const* log = self->logs.data[i]; + + fprintf(stderr, "[ERR:%d] %s\n", log->line, log->msg); + } +} diff --git a/lib/err.h b/lib/err.h new file mode 100644 index 0000000..be312c7 --- /dev/null +++ b/lib/err.h @@ -0,0 +1,23 @@ +#ifndef CCM_ERR_H +#define CCM_ERR_H + +#include "commons.h" +#include "vec.h" + +typedef struct { + int line; + char msg[CCM_STRLEN]; +} err_log_t; + +typedef struct { + vec_t logs; +} err_t; + +void err_init(err_t* self); +void err_free(err_t* self); + +int err_is_ok(err_t* self); +void err_push(err_t* self, int line, char const* format, ...); +void err_print_stack_trace(err_t* self); + +#endif diff --git a/lib/exec.c b/lib/exec.c new file mode 100644 index 0000000..f5d8318 --- /dev/null +++ b/lib/exec.c @@ -0,0 +1,130 @@ +#include "exec.h" +#include "bytecode.h" + +void exec_init(exec_t* self) +{ + assert(self); + self->pc = 0; + err_init(&self->err); +} + +void exec_free(exec_t* self) +{ + assert(self); + err_free(&self->err); +} + +void exec_module(exec_t* self, module_t* module) +{ + assert(self); + assert(module); + + self->pc = 0; + + while (self->pc < module->prog.instrs.size) + { + if (!err_is_ok(&self->err)) + { + return; + } + + instr_t const* instr = module->prog.instrs.data[self->pc]; + exec_instr(self, module, instr->opcode, instr->param); + } +} + +void exec_instr(exec_t* self, + module_t* module, + Opcode op, + int param) +{ + assert(self); + + ccm_t* ccm = &module->ccm; + + switch (op) + { + case OP_ASSERT_NE: + case OP_ASSERT_EQ: { + CCM val = ccm_pop(ccm); + vec_t* values = ccm_from_tuple(ccm, val); + assert(values->size == 2); + int oracle = 1; + + if (op == OP_ASSERT_NE) + { + oracle = 0; + } + + if (value_equals(values->data[0], + values->data[1]) == !oracle) + { + char lhs[CCM_STRLEN]; + value_str(values->data[0], lhs, CCM_STRLEN); + + char rhs[CCM_STRLEN]; + value_str(values->data[1], rhs, CCM_STRLEN); + char const* operator = oracle ? "==" : "!="; + + err_push( + &self->err, + ((value_t*) values->data[0])->line, + "assertion failed: <%s> %s <%s>", + lhs, operator, rhs + ); + } + + self->pc++; + + } break; + + case OP_PUSH: { + CCM value = (CCM) module->prog.constants.data[param]; + ccm_push(ccm, value); + self->pc++; + } break; + + case OP_POP: { + ccm_pop(ccm); + self->pc++; + } break; + + case OP_ADD: { ccm_add(ccm); self->pc++; } break; + case OP_SUB: { ccm_sub(ccm); self->pc++; } break; + case OP_USUB: { ccm_usub(ccm); self->pc++; } break; + case OP_MUL: { ccm_mul(ccm); self->pc++; } break; + case OP_DIV: { ccm_div(ccm); self->pc++; } break; + case OP_MOD: { ccm_mod(ccm); self->pc++; } break; + case OP_POW: { ccm_pow(ccm); self->pc++; } break; + + case OP_MK_TUPLE: { + vec_t* values = malloc(sizeof(vec_t)); + vec_init(values); + int line = -1; + + for (int i=0; ivalues.data[val]); + vec_push(values, v); + + if (line == -1) + { + line = v->line; + } + } + + CCM value = ccm_to_tuple(ccm, values, line); + ccm_push(ccm, value); + + self->pc++; + } break; + + default: { + fprintf(stderr, + "cannot exec opcode '%s'", + OpcodeStr[op]); + abort(); + } + } +} diff --git a/lib/exec.h b/lib/exec.h new file mode 100644 index 0000000..031bdd7 --- /dev/null +++ b/lib/exec.h @@ -0,0 +1,22 @@ +#ifndef CCM_EXEC_H +#define CCM_EXEC_H + +#include "commons.h" +#include "module.h" +#include "err.h" + +typedef struct { + err_t err; + size_t pc; +} exec_t; + +void exec_init(exec_t* self); +void exec_free(exec_t* self); + +void exec_module(exec_t* self, module_t* module); +void exec_instr(exec_t* self, + module_t* module, + Opcode op, + int param); + +#endif diff --git a/lib/lexer.c b/lib/lexer.c new file mode 100644 index 0000000..39ea8d1 --- /dev/null +++ b/lib/lexer.c @@ -0,0 +1,354 @@ +#include "lexer.h" +#include "str.h" + +#define CCM_KEYWORD(KW, KIND, HAS_VAL) \ +if ( (node = lexer_try_new_keyword(self, KW, KIND, HAS_VAL)) ) \ +{\ + return node; \ +} + +void lexer_init(lexer_t* self) +{ + assert(self); + self->source = NULL; + err_init(&self->err); + str_init(&self->separators); + + vec_init(&self->texts); + lexer_add_text(self, ",", NODE_COMMA); + lexer_add_text(self, "(", NODE_OPAR); + lexer_add_text(self, ")", NODE_CPAR); + lexer_add_text(self, "+", NODE_ADD); + lexer_add_text(self, "-", NODE_SUB); + lexer_add_text(self, "*", NODE_MUL); + lexer_add_text(self, "/", NODE_DIV); + lexer_add_text(self, "^", NODE_POW); + lexer_add_text(self, "%", NODE_MOD); +} + +void lexer_free(lexer_t* self) +{ + assert(self); + if (self->source) + { + free(self->source); + self->source = NULL; + } + + err_free(&self->err); + str_free(&self->separators); + vec_free_elements(&self->texts, NULL); + vec_free(&self->texts); +} + +void lexer_scan(lexer_t* self, char const* source) +{ + assert(self); + assert(source); + + self->line = 1; + self->cursor = 0; + self->source = strdup(source); +} + +void lexer_add_text(lexer_t* self, char const* repr, NodeKind kind) +{ + assert(self); + lexer_entry_t* entry = malloc(sizeof(lexer_entry_t)); + entry->repr = repr; + entry->kind = kind; + + str_push(&self->separators, repr[0]); + vec_push(&self->texts, entry); +} + +node_t* lexer_peek(lexer_t* self, int lookahead) +{ + assert(self); + lexer_state_t state = lexer_state(self); + + node_t* node = NULL; + + for (int i=0; i<=lookahead; i++) + { + node = lexer_try_new_next(self); + if (node && i < lookahead) + { + node_free(node); + free(node); + } + } + + lexer_restore(self, state); + + return node; +} + +int lexer_peek_kind(lexer_t* self, NodeKind kind, int lookahead) +{ + assert(self); + node_t* peek = lexer_peek(self, lookahead); + int res = (peek != NULL && peek->kind == kind); + + if (peek) + { + node_free(peek); + free(peek); + } + + return res; +} + +lexer_state_t lexer_state(lexer_t* self) +{ + assert(self); + lexer_state_t state = { + self->cursor, + self->line + }; + + return state; +} + +void lexer_restore(lexer_t* self, lexer_state_t state) +{ + assert(self); + self->cursor = state.cursor; + self->line = state.line; +} + +node_t* lexer_try_new_next(lexer_t* self) +{ + assert(self); + + if (!err_is_ok(&self->err)) + { + return NULL; + } + + lexer_skip_spaces(self); + + while (self->cursor < (ssize_t) strlen(self->source) + && self->source[self->cursor] == '#') + { + while (self->cursor < (ssize_t) strlen(self->source) + && self->source[self->cursor] != '\n') + { + self->cursor++; + } + + lexer_skip_spaces(self); + } + + node_t* node = NULL; + + if ( (node = lexer_try_new_num(self)) ) + { + return node; + } + + for (size_t i=0; itexts.size; i++) + { + if ( (node = lexer_try_new_text( + self, + ((lexer_entry_t*) self->texts.data[i])->repr, + ((lexer_entry_t*) self->texts.data[i])->kind, 0)) ) { + return node; + } + } + + CCM_KEYWORD("assert_eq", NODE_ASSERT_EQ, 0); + CCM_KEYWORD("assert_ne", NODE_ASSERT_NE, 0); + + if (self->cursor < (ssize_t) strlen(self->source)) + { + str_t s; + str_init(&s); + size_t i = self->cursor; + + while (i < strlen(self->source) + && !lexer_is_sep(self, i)) + { + str_push(&s, self->source[i]); + i++; + } + + err_push(&self->err, self->line, "unknown symbol '%s'", s.value); + + str_free(&s); + } + + return NULL; +} + +int lexer_consume_next(lexer_t* self, NodeKind kind) +{ + assert(self); + node_t* node = lexer_try_new_next(self); + + if (node == NULL) + { + err_push(&self->err, self->line, + "expected token '%s' but got nothing", + NodeKindStr[kind] + strlen("NODE_")); + + return 0; + } + else if (node->kind != kind) + { + err_push(&self->err, self->line, + "expected token '%s' but got '%s'", + NodeKindStr[kind] + strlen("NODE_"), + NodeKindStr[node->kind] + strlen("NODE_")); + + node_free(node); + free(node); + + return 0; + } + + node_free(node); + free(node); + + return 1; +} + +void lexer_skip_spaces(lexer_t* self) +{ + assert(self); + + while (self->cursor < (ssize_t) strlen(self->source) + && isspace(self->source[self->cursor])) + { + if (self->source[self->cursor] == '\n') + { + self->line++; + } + + self->cursor++; + } +} + +int lexer_is_sep(lexer_t* self, ssize_t pos) +{ + assert(self); + if (pos < 0 || pos >= (ssize_t) strlen(self->source)) { return 1; } + + char c = self->source[pos]; + + if (str_find(&self->separators, c) >= 0) + { + return 1; + } + + return isspace(c); +} + +node_t* lexer_try_new_keyword(lexer_t* self, + char const* keyword, + NodeKind kind, + int has_value) +{ + assert(self); + assert(keyword); + + for (size_t i=0; icursor + i >= strlen(self->source) + || keyword[i] != self->source[self->cursor + i]) + { + return NULL; + } + } + + if (!lexer_is_sep(self, self->cursor - 1) + || !lexer_is_sep(self, self->cursor + strlen(keyword))) + { + return NULL; + } + + node_t* res = malloc(sizeof(node_t)); + node_init(res, kind, (has_value ? keyword : ""), self->line); + self->cursor += strlen(keyword); + + return res; +} + +node_t* lexer_try_new_text(lexer_t* self, + char const* text, + NodeKind kind, + int has_value) +{ + assert(self); + assert(text); + + for (size_t i=0; icursor + i >= strlen(self->source) + || text[i] != self->source[self->cursor + i]) + { + return NULL; + } + } + + node_t* res = malloc(sizeof(node_t)); + node_init(res, kind, (has_value ? text : ""), self->line); + self->cursor += strlen(text); + + return res; +} + +node_t* lexer_try_new_num(lexer_t* self) +{ + assert(self); + size_t cursor = self->cursor; + str_t value; + str_init(&value); + + if (cursor < strlen(self->source) + && self->source[cursor] == '-') + { + str_push(&value, self->source[cursor]); + cursor++; + } + + while (cursor < strlen(self->source) + && isdigit(self->source[cursor])) + { + str_push(&value, self->source[cursor]); + cursor++; + } + + if (cursor < strlen(self->source) + && self->source[cursor] == '.') + { + str_push(&value, self->source[cursor]); + cursor++; + + while (cursor < strlen(self->source) + && isdigit(self->source[cursor])) + { + str_push(&value, self->source[cursor]); + cursor++; + } + } + + if (value.size == 0 + || (value.size == 1 && !isdigit(value.value[0])) + || !lexer_is_sep(self, self->cursor - 1) + || !lexer_is_sep(self, cursor) + ) + { + str_free(&value); + return NULL; + } + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_NUM, value.value, self->line); + + str_free(&value); + + self->cursor = cursor; + + return node; +} diff --git a/lib/lexer.h b/lib/lexer.h new file mode 100644 index 0000000..0f43bd9 --- /dev/null +++ b/lib/lexer.h @@ -0,0 +1,56 @@ +#ifndef CCM_LEXER_H +#define CCM_LEXER_H + +#include "commons.h" +#include "node.h" +#include "str.h" +#include "err.h" + +typedef struct { + ssize_t cursor; + int line; +} lexer_state_t; + +typedef struct { + char const* repr; + NodeKind kind; +} lexer_entry_t; + +typedef struct { + err_t err; + str_t separators; + char* source; + ssize_t cursor; + int line; + vec_t texts; +} lexer_t; + +void lexer_init(lexer_t* self); +void lexer_free(lexer_t* self); + +void lexer_scan(lexer_t* self, char const* source); +void lexer_skip_spaces(lexer_t* self); +int lexer_is_sep(lexer_t* self, ssize_t pos); + +void lexer_add_text(lexer_t* self, char const* repr, NodeKind kind); + +node_t* lexer_peek(lexer_t* self, int lookahead); +int lexer_peek_kind(lexer_t* self, NodeKind kind, int lookahead); +lexer_state_t lexer_state(lexer_t* self); +void lexer_restore(lexer_t* self, lexer_state_t state); + +node_t* lexer_try_new_next(lexer_t* self); +int lexer_consume_next(lexer_t* self, NodeKind kind); + +node_t* lexer_try_new_keyword(lexer_t* self, + char const* keyword, + NodeKind kind, + int has_value); + +node_t* lexer_try_new_text(lexer_t* self, + char const* text, + NodeKind kind, + int has_value); + +node_t* lexer_try_new_num(lexer_t* self); +#endif diff --git a/lib/module.c b/lib/module.c new file mode 100644 index 0000000..ea465ad --- /dev/null +++ b/lib/module.c @@ -0,0 +1,115 @@ +#include "module.h" +#include "lexer.h" +#include "parser.h" +#include "compiler.h" + +void module_init(module_t* self) +{ + assert(self); + self->source = NULL; + prog_init(&self->prog); + err_init(&self->err); + ccm_init(&self->ccm); +} + +void module_free(module_t* self) +{ + assert(self); + + if (self->source) + { + free(self->source); + } + + ccm_free(&self->ccm); + prog_free(&self->prog); + err_free(&self->err); +} + +int module_load(module_t* self, char const* path) +{ + assert(self); + assert(path); + + if (module_load_source(self, path) != 0) + { + return 1; + } + + lexer_t lexer; + lexer_init(&lexer); + + lexer_scan(&lexer, self->source); + + parser_t parser; + parser_init(&parser, &lexer); + + node_t* ast = parser_try_new_parse(&parser); + + if (!err_is_ok(&lexer.err) || !err_is_ok(&parser.err)) + { + err_print_stack_trace(&lexer.err); + err_print_stack_trace(&parser.err); + err_push(&self->err, lexer.line, "invalid module"); + goto free_parser; + } + + compiler_t compiler; + compiler_init(&compiler, self); + + compiler_compile(&compiler, ast, &self->prog); + + compiler_free(&compiler); + node_free(ast); + free(ast); +free_parser: + parser_free(&parser); + lexer_free(&lexer); + + return 0; +} + +int module_load_source(module_t* self, char const* path) +{ + assert(self); + FILE* file = fopen(path, "r+"); + + if (!file) + { + err_push(&self->err, 0, "cannot load file '%s'", path); + return 1; + } + + size_t sz = 0; + size_t cap = 2; + char* data = malloc(sizeof(char) * cap); + + char buf; + size_t size; + + while ( (size = fread(&buf, 1, sizeof(char), file)) ) + { + if (sz + 1 >= cap) + { + cap *= 2; + char* next = realloc(data, sizeof(char) * cap); + + if (next) + { + data = next; + } + } + + data[sz] = buf; + sz++; + } + + data[sz] = '\0'; + + self->source = strdup(data); + + free(data); + fclose(file); + + return 0; +} diff --git a/lib/module.h b/lib/module.h new file mode 100644 index 0000000..d5ba22e --- /dev/null +++ b/lib/module.h @@ -0,0 +1,22 @@ +#ifndef CCM_MODULE_H +#define CCM_MODULE_H + +#include "commons.h" +#include "prog.h" +#include "err.h" +#include "ccm.h" + +typedef struct { + err_t err; + char* source; + prog_t prog; + ccm_t ccm; +} module_t; + +void module_init(module_t* self); +void module_free(module_t* self); + +int module_load(module_t* self, char const* path); +int module_load_source(module_t* self, char const* path); + +#endif diff --git a/lib/node.c b/lib/node.c index b6347a4..2d7e0c0 100644 --- a/lib/node.c +++ b/lib/node.c @@ -1 +1,78 @@ #include "node.h" + +CCM_ENUM_C(NodeKind, NODE_KIND); + +void node_init(node_t* self, + NodeKind kind, + char const* value, + int line) +{ + assert(self); + self->line = line; + self->kind = kind; + vec_init(&self->children); + + if (value) + { + self->value = strdup(value); + } +} + +void node_free(node_t* self) +{ + assert(self); + vec_free_elements(&self->children, (void*) node_free); + vec_free(&self->children); + + if (self->value) + { + free(self->value); + self->value = NULL; + } +} + +void node_push_new_child(node_t* self, node_t* child) +{ + assert(self); + assert(child); + + vec_push(&self->children, child); +} + +size_t node_str(node_t* self, char* buffer, size_t size) +{ + assert(self); + size_t sz = 0; + + sz += snprintf( + buffer + sz, size - sz, "%s", + NodeKindStr[self->kind] + strlen("NODE_") + ); + + if (strlen(self->value) > 0) + { + sz += snprintf(buffer + sz, size - sz, "[%s]", self->value); + } + + if (self->children.size > 0) + { + sz += snprintf(buffer + sz, size - sz, "("); + + for (size_t i=0; ichildren.size; i++) + { + if (i > 0) + { + sz += snprintf(buffer + sz, size - sz, ","); + } + sz += node_str( + self->children.data[i], + buffer + sz, size - sz + ); + } + + sz += snprintf(buffer + sz, size - sz, ")"); + } + + return sz; +} + diff --git a/lib/node.h b/lib/node.h index 4579a1f..2738d31 100644 --- a/lib/node.h +++ b/lib/node.h @@ -1,4 +1,30 @@ -#ifndef NODE_H -#define NODE_H +#ifndef CCM_NODE_H +#define CCM_NODE_H + +#include "commons.h" +#include "vec.h" + +#define NODE_KIND(G) \ +G(NODE_MODULE), \ +G(NODE_NUM), G(NODE_OPAR), G(NODE_CPAR), \ +G(NODE_POW), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), \ +G(NODE_DIV), G(NODE_MOD), G(NODE_COMMA), G(NODE_TUPLE), \ +G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE) + +CCM_ENUM_H(NodeKind, NODE_KIND); + +typedef struct { + NodeKind kind; + char* value; + vec_t children; + int line; +} node_t; + +void node_init(node_t* self, NodeKind kind, + char const* value, int line); +void node_free(node_t* self); + +void node_push_new_child(node_t* self, node_t* child); +size_t node_str(node_t* self, char* buffer, size_t size); #endif diff --git a/lib/parser.c b/lib/parser.c new file mode 100644 index 0000000..aa57635 --- /dev/null +++ b/lib/parser.c @@ -0,0 +1,381 @@ +#include "parser.h" + +#define CCM_TRY(rule) parser_try_new_rule(self, rule) + +void parser_init(parser_t* self, lexer_t* lexer) +{ + assert(self); + assert(lexer); + + self->lexer = lexer; + err_init(&self->err); + self->current = NULL; +} + +void parser_free(parser_t* self) +{ + err_free(&self->err); +} + +node_t* parser_try_new_parse(parser_t* self) +{ + assert(self); + return CCM_TRY(parser_try_new_module); +} + +node_t* parser_try_new_rule(parser_t* self, rule_t rule) +{ + if (!err_is_ok(&self->err)) + { + return NULL; + } + + lexer_state_t state = lexer_state(self->lexer); + + node_t* result = rule(self); + if (result) { return result; } + + lexer_restore(self->lexer, state); + + return (void*) NULL; +} + +int parser_ensure(parser_t* self, node_t* node, NodeKind kind) +{ + assert(self); + + if (!node) + { + err_push(&self->err, self->lexer->line, + "expected token '%s', got nothing", + NodeKindStr[kind] + strlen("NODE_")); + return 0; + } + + if (node->kind != kind) + { + err_push(&self->err, self->lexer->line, + "expected token '%s', got '%s'", + NodeKindStr[kind] + strlen("NODE_"), + NodeKindStr[node->kind] + strlen("NODE_")); + return 0; + } + + return 1; +} + +node_t* parser_try_new_module(parser_t* self) +{ + assert(self); + node_t* module = malloc(sizeof(node_t)); + node_init(module, NODE_MODULE, "", self->lexer->line); + + node_t* node = NULL; + + do { + node = CCM_TRY(parser_try_new_expr); + + if (!node) + { + node_free(module); + free(module); + return NULL; + } + + node_push_new_child(module, node); + lexer_skip_spaces(self->lexer); + } while(self->lexer->cursor < (ssize_t) strlen(self->lexer->source)); + return module; + +} + +node_t* parser_try_new_expr(parser_t* self) +{ + assert(self); + + if (lexer_peek_kind(self->lexer, NODE_ASSERT_EQ, 0) + || lexer_peek_kind(self->lexer, NODE_ASSERT_NE, 0)) + { + return CCM_TRY(parser_try_new_assert); + } + + return CCM_TRY(parser_try_new_term); +} + +node_t* parser_try_new_assert(parser_t* self) +{ + assert(self); + node_t* node = malloc(sizeof(node_t)); + + if (lexer_peek_kind(self->lexer, NODE_ASSERT_EQ, 0)) + { + lexer_consume_next(self->lexer, NODE_ASSERT_EQ); + node_init(node, NODE_ASSERT_EQ, "", self->lexer->line); + } + else if (lexer_peek_kind(self->lexer, NODE_ASSERT_NE, 0)) + { + lexer_consume_next(self->lexer, NODE_ASSERT_NE); + node_init(node, NODE_ASSERT_NE, "", self->lexer->line); + } + else + { + free(node); + return NULL; + } + + + node_t* tuple = CCM_TRY(parser_try_new_tuple); + + if (!tuple) + { + node_free(node); + free(node); + return NULL; + } + + node_push_new_child(node, tuple); + + return node; +} + +node_t* parser_try_new_term(parser_t* self) +{ + assert(self); + node_t* lhs = CCM_TRY(parser_try_new_factor); + if (!lhs) { return NULL; } + + while (lexer_peek_kind(self->lexer, NODE_ADD, 0) + || lexer_peek_kind(self->lexer, NODE_SUB, 0)) + { + node_t* node = lexer_try_new_next(self->lexer); + node_t* rhs = CCM_TRY(parser_try_new_factor); + + if (!rhs) + { + node_free(lhs); + free(lhs); + node_free(node); + free(node); + return NULL; + } + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + +node_t* parser_try_new_factor(parser_t* self) +{ + assert(self); + node_t* lhs = CCM_TRY(parser_try_new_usub); + if (!lhs) { return NULL; } + + while (lexer_peek_kind(self->lexer, NODE_MUL, 0) + || lexer_peek_kind(self->lexer, NODE_DIV, 0) + || lexer_peek_kind(self->lexer, NODE_MOD, 0)) + { + node_t* node = lexer_try_new_next(self->lexer); + + node_t* rhs = CCM_TRY(parser_try_new_usub); + + if (!rhs) + { + node_free(lhs); + free(lhs); + node_free(node); + free(node); + return NULL; + } + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + +node_t* parser_try_new_usub(parser_t* self) +{ + assert(self); + + if (lexer_peek_kind(self->lexer, NODE_SUB, 0)) + { + lexer_consume_next(self->lexer, NODE_SUB); + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_SUB, "", self->lexer->line); + + node_t* rhs = CCM_TRY(parser_try_new_usub); + + if (!rhs) + { + node_free(node); + free(node); + + return NULL; + } + + node_push_new_child(node, rhs); + + return node; + } + else + { + return CCM_TRY(parser_try_new_pow); + } + + return NULL; +} + +node_t* parser_try_new_pow(parser_t* self) +{ + assert(self); + node_t* lhs = CCM_TRY(parser_try_new_literal); + if (!lhs) { return NULL; } + + if (lexer_peek_kind(self->lexer, NODE_POW, 0)) + { + if (!lexer_consume_next(self->lexer, NODE_POW)) + { + node_free(lhs); + free(lhs); + return NULL; + } + + node_t* rhs = CCM_TRY(parser_try_new_literal); + + if (!rhs) + { + node_free(lhs); + free(lhs); + + return NULL; + } + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_POW, "", self->lexer->line); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + +node_t* parser_try_new_literal(parser_t* self) +{ + assert(self); + + if (lexer_peek_kind(self->lexer, NODE_OPAR, 0)) + { + node_t* tuple = CCM_TRY(parser_try_new_tuple); + + if (tuple) + { + return tuple; + } + + if (!lexer_consume_next(self->lexer, NODE_OPAR)) + { + return NULL; + } + + node_t* expr = CCM_TRY(parser_try_new_expr); + + if (!lexer_consume_next(self->lexer, NODE_CPAR)) + { + if (expr) + { + node_free(expr); + free(expr); + } + + return NULL; + } + + return expr; + } + + return CCM_TRY(parser_try_new_builtin); +} + +node_t* parser_try_new_tuple(parser_t* self) +{ + assert(self); + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_TUPLE, "", self->lexer->line); + + if (!lexer_consume_next(self->lexer, NODE_OPAR)) + { + node_free(node); + free(node); + return NULL; + } + + node_t* lhs = CCM_TRY(parser_try_new_expr); + + if (!lhs) + { + node_free(node); + free(node); + return NULL; + } + + node_push_new_child(node, lhs); + + int contains_more_than_one_expr = 0; + + while (lexer_peek_kind(self->lexer, NODE_COMMA, 0)) + { + lexer_consume_next(self->lexer, NODE_COMMA); + + node_t* child = CCM_TRY(parser_try_new_expr); + + if (!child) + { + node_free(node); + free(node); + return NULL; + } + + node_push_new_child(node, child); + contains_more_than_one_expr = 1; + } + + lexer_consume_next(self->lexer, NODE_CPAR); + + if (!contains_more_than_one_expr) + { + node_free(node); + free(node); + return NULL; + } + + return node; +} + +node_t* parser_try_new_builtin(parser_t* self) +{ + assert(self); + + node_t* node = lexer_try_new_next(self->lexer); + parser_ensure(self, node, NODE_NUM); + + if (node && node->kind == NODE_NUM) + { + return node; + } + + if (node) + { + node_free(node); + free(node); + } + + return NULL; +} + diff --git a/lib/parser.h b/lib/parser.h new file mode 100644 index 0000000..86d469a --- /dev/null +++ b/lib/parser.h @@ -0,0 +1,35 @@ +#ifndef CCM_PARSER_H +#define CCM_PARSER_H + +#include "commons.h" +#include "lexer.h" +#include "str.h" +#include "err.h" + +typedef struct { + err_t err; + lexer_t* lexer; + node_t* current; +} parser_t; + +typedef node_t* (*rule_t)(parser_t*); + +void parser_init(parser_t* self, lexer_t* lexer); +void parser_free(parser_t* self); + +node_t* parser_try_new_parse(parser_t* self); +node_t* parser_try_new_rule(parser_t* self, rule_t rule); +int parser_ensure(parser_t* self, node_t* node, NodeKind kind); + +node_t* parser_try_new_module(parser_t* self); +node_t* parser_try_new_expr(parser_t* self); +node_t* parser_try_new_assert(parser_t* self); +node_t* parser_try_new_term(parser_t* self); +node_t* parser_try_new_factor(parser_t* self); +node_t* parser_try_new_usub(parser_t* self); +node_t* parser_try_new_pow(parser_t* self); +node_t* parser_try_new_literal(parser_t* self); +node_t* parser_try_new_tuple(parser_t* self); +node_t* parser_try_new_builtin(parser_t* self); + +#endif diff --git a/lib/prog.c b/lib/prog.c new file mode 100644 index 0000000..b22feb0 --- /dev/null +++ b/lib/prog.c @@ -0,0 +1,65 @@ +#include "prog.h" + +void prog_init(prog_t* self) +{ + assert(self); + vec_init(&self->instrs); + vec_init(&self->constants); +} + +void prog_free(prog_t* self) +{ + assert(self); + vec_free_elements(&self->instrs, NULL); + vec_free(&self->instrs); + vec_free(&self->constants); +} + +size_t prog_add_instr(prog_t* self, Opcode opcode, int param) +{ + assert(self); + instr_t* instr = malloc(sizeof(instr_t)); + instr->opcode = opcode; + instr->param = param; + vec_push(&self->instrs, instr); + + return self->instrs.size - 1; +} + +size_t prog_add_new_constant(prog_t* self, CCM value) +{ + assert(self); + + vec_push(&self->constants, (void*) value); + return self->constants.size - 1; +} + +size_t prog_str(prog_t* self, char* buffer, size_t size) +{ + assert(self); + assert(buffer); + + size_t sz = 0; + + for (size_t i=0; iinstrs.size; i++) + { + instr_t const* instr = self->instrs.data[i]; + + if (instr->param == CCM_NO_PARAM) + { + sz += snprintf(buffer + sz, size - sz, "%zu\t%s\n", + i, + OpcodeStr[instr->opcode] + + strlen("OP_")); + } + else + { + sz += snprintf(buffer + sz, size - sz, "%zu\t%s %d\n", + i, + OpcodeStr[instr->opcode] + strlen("OP_"), + instr->param); + } + } + + return sz; +} diff --git a/lib/prog.h b/lib/prog.h new file mode 100644 index 0000000..32dc789 --- /dev/null +++ b/lib/prog.h @@ -0,0 +1,31 @@ +#ifndef CCM_PROG_H +#define CCM_PROG_H + +#include "commons.h" +#include "vec.h" +#include "bytecode.h" +#include "value.h" +#include "ccm.h" + +#define CCM_NO_PARAM (-1) + +typedef struct { + Opcode opcode; + int param; +} instr_t; + +typedef struct { + vec_t instrs; + vec_t constants; +} prog_t; + +void prog_init(prog_t* self); +void prog_free(prog_t* self); + +size_t prog_add_instr(prog_t* self, Opcode opcode, int param); +size_t prog_add_new_constant(prog_t* self, CCM value); + +size_t prog_str(prog_t* self, char* buffer, size_t size); + +#endif + diff --git a/lib/str.c b/lib/str.c new file mode 100644 index 0000000..b1b8876 --- /dev/null +++ b/lib/str.c @@ -0,0 +1,69 @@ +#include "str.h" + +void str_init(str_t* self) +{ + assert(self); + self->size = 0; + self->capacity = 0; + self->value = NULL; +} + +void str_free(str_t* self) +{ + assert(self); + + if (self->value) + { + free(self->value); + } +} + +ssize_t str_find(str_t* self, char c) +{ + assert(self); + + for (size_t i=0; isize; i++) + { + if (self->value[i] == c) + { + return i; + } + } + + return -1; +} + +void str_push(str_t* self, char c) +{ + assert(self); + + if (self->value == NULL) + { + self->capacity = 1; + self->value = malloc(sizeof(char) * self->capacity); + } + + if (self->size + 2 >= self->capacity) + { + self->capacity *= 2; + self->value = realloc( + self->value, + sizeof(char) * self->capacity + ); + } + + self->value[self->size] = c; + self->size++; + self->value[self->size] = '\0'; +} + +void str_push_cstr(str_t* self, char const* rhs) +{ + assert(self); + assert(rhs); + + for (size_t i=0; idata.num = num; + self->type = TYPE_NUM; + self->line = line; +} + +void value_init_new_tuple(value_t* self, vec_t* values, int line) +{ + assert(self); + assert(values); + self->data.tuple = values; + self->type = TYPE_TUPLE; + self->line = line; +} + +value_t* value_new_clone(value_t* self) +{ + assert(self); + + value_t* value = malloc(sizeof(value_t)); + + switch (self->type) + { + case TYPE_NUM: { + value_init_num(value, self->data.num, self->line); + } break; + case TYPE_TUPLE: { + vec_t* vec = malloc(sizeof(vec_t)); + vec_init(vec); + + for (size_t i=0; idata.tuple->size; i++) + { + vec_push( + vec, + value_new_clone(self->data.tuple->data[i]) + ); + } + + value_init_new_tuple(value, vec, self->line); + } break; + default: { + free(value); + fprintf(stderr, "cannot clone value of type '%s'\n", + TypeStr[self->type]); + abort(); + } break; + } + + return value; +} + +void value_free(value_t* self) +{ + if (self->type == TYPE_TUPLE) + { + vec_free_elements(self->data.tuple, (void*) value_free); + vec_free(self->data.tuple); + free(self->data.tuple); + } +} + +size_t value_str(value_t* self, char* buffer, size_t size) +{ + assert(self); + assert(buffer); + size_t sz = 0; + + switch (self->type) + { + case TYPE_NUM: { + sz += snprintf(buffer + sz, size - sz, "%lf", + self->data.num); + } break; + + case TYPE_TUPLE: { + sz += snprintf(buffer + sz, size - sz, "("); + + for (size_t i=0; i < self->data.tuple->size; i++) + { + if (i > 0) + { + sz += snprintf(buffer + sz, size - sz, ", "); + } + + sz += value_str(self->data.tuple->data[i], + buffer + sz, + size - sz); + } + + sz += snprintf(buffer + sz, size - sz, ")"); + } break; + + default: assert(0); + + } + + return sz; +} + +int value_equals(value_t* self, value_t* rhs) +{ + assert(self); + assert(rhs); + + if (self->type != rhs->type) + { + return 0; + } + + switch (self->type) + { + case TYPE_NUM: { + return self->data.num == rhs->data.num; + } break; + + case TYPE_TUPLE: { + if (self->data.tuple->size != rhs->data.tuple->size) + { + return 0; + } + + for (size_t i=0; idata.tuple->size; i++) + { + if (!value_equals( + self->data.tuple->data[i], + rhs->data.tuple->data[i] + )) + { + return 0; + } + } + + return 1; + } break; + + default: { + fprintf( + stderr, + "cannot test equality on value of type '%s'\n", + TypeStr[self->type]); + abort(); + } break; + } +} diff --git a/lib/value.h b/lib/value.h new file mode 100644 index 0000000..1e4dd03 --- /dev/null +++ b/lib/value.h @@ -0,0 +1,26 @@ +#ifndef CCM_VALUE_H +#define CCM_VALUE_H + +#include "commons.h" +#include "type.h" +#include "vec.h" + +typedef struct { + union { + double num; + vec_t* tuple; + } data; + + Type type; + int line; +} value_t; + +void value_init_num(value_t* self, double num, int line); +void value_init_new_tuple(value_t* self, vec_t* values, int line); +value_t* value_new_clone(value_t* self); +void value_free(value_t* self); + +size_t value_str(value_t* self, char* buffer, size_t size); +int value_equals(value_t* self, value_t* rhs); + +#endif diff --git a/lib/vec.c b/lib/vec.c new file mode 100644 index 0000000..99e07b1 --- /dev/null +++ b/lib/vec.c @@ -0,0 +1,72 @@ +#include "vec.h" + +void vec_init(vec_t* self) +{ + assert(self); + self->size = 0; + self->capacity = 0; + self->data = NULL; +} + +void vec_free_elements(vec_t* self, void (*free_fun)(void*)) +{ + assert(self); + + for (size_t i=0; isize; i++) + { + if (free_fun) + { + free_fun(self->data[i]); + } + + free(self->data[i]); + } +} + +void vec_free(vec_t* self) +{ + assert(self); + + free(self->data); + self->data = NULL; + self->size = 0; + self->capacity = 0; +} + +void vec_push(vec_t* self, void* value) +{ + assert(self); + if (self->capacity == 0) + { + self->capacity = 2; + self->data = calloc(self->capacity, sizeof(void*)); + } + + if (self->size + 1 >= self->capacity) + { + self->capacity *= 2; + + void** v = realloc( + self->data, + self->capacity * sizeof(void*) + ); + + assert(v); + + self->data = v; + } + + self->data[self->size] = value; + self->size++; +} + +void* vec_pop(vec_t* self) +{ + assert(self); + assert(self->size > 0); + + void* value = self->data[self->size - 1]; + self->size--; + return value; +} + diff --git a/lib/vec.h b/lib/vec.h new file mode 100644 index 0000000..2005227 --- /dev/null +++ b/lib/vec.h @@ -0,0 +1,22 @@ +#ifndef CCM_VEC_H +#define CCM_VEC_H + +#include "commons.h" + +/** +* @file saze +**/ +typedef struct { + size_t capacity; + size_t size; + void** data; +} vec_t; + +void vec_init(vec_t* self); +void vec_free_elements(vec_t* self, void (*free_fun)(void*)); +void vec_free(vec_t* self); + +void vec_push(vec_t* self, void* value); +void* vec_pop(vec_t* self); + +#endif diff --git a/tests/num.ccm b/tests/num.ccm new file mode 100644 index 0000000..1bc7066 --- /dev/null +++ b/tests/num.ccm @@ -0,0 +1,23 @@ + +assert_eq (0, 0) + +# PRECEDENCE +# ========== + +assert_eq (1 + 2 * 3, 7) +assert_eq ((1 + 2) * 3, 9) + +# MOD +# === +assert_eq (2, 100 % 3.5) + +# POW +# === +assert_eq (-8, -2^3) +assert_eq (5, 25^0.5) + +# UNARY SUB +# ========= +assert_eq (16, --16) +assert_eq (-16, ---16) + diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..d689449 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +TOTAL=0 +FAILED=0 + +for file in $(find . -name "*.ccm" | sort) +do + LOG=$(ccm $file 2>&1) + RET=$? + TOTAL=$(($TOTAL + 1)) + + + if [ $RET -eq 0 ] + then + echo -e "\e[36m$file\t\e[32mok\e[0m" + else + echo -e "\e[36m$file\t\e[31mfailed\e[0m" + + echo "$LOG" + + FAILED=$(($FAILED + 1)) + fi +done + +echo + +if [ $FAILED -eq 0 ] +then + echo -e "\e[32m--- All tests passed ($TOTAL) ---\e[0m" +else + TEST_WORD="tests" + + if [ $FAILED -eq 1 ] + then + TEST_WORD="test" + fi + + echo -e "\e[31m--- $FAILED $TEST_WORD failed ---\e[0m" +fi