✨ num arithmetic.
parent
3493fda418
commit
ab34370e39
3
Makefile
3
Makefile
|
@ -9,4 +9,5 @@ install: build
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@cppcheck --enable=all -q ccm lib \
|
@cppcheck --enable=all -q ccm lib \
|
||||||
--suppress=missingIncludeSystem
|
--suppress=missingIncludeSystem \
|
||||||
|
--suppress=unusedFunction
|
||||||
|
|
49
ccm/main.c
49
ccm/main.c
|
@ -1,7 +1,50 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <exec.h>
|
||||||
|
|
||||||
int main()
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
printf("Hello World!\n");
|
int status = 0;
|
||||||
return 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -3,7 +3,20 @@ cmake_minimum_required(VERSION 3.28)
|
||||||
project(ccm_library)
|
project(ccm_library)
|
||||||
|
|
||||||
add_library(ccm_lib
|
add_library(ccm_lib
|
||||||
|
vec.c
|
||||||
|
str.c
|
||||||
node.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)
|
set_property(TARGET ccm_lib PROPERTY C_STANDARD 99)
|
||||||
|
@ -15,4 +28,8 @@ target_compile_options(ccm_lib
|
||||||
target_include_directories(ccm_lib
|
target_include_directories(ccm_lib
|
||||||
PUBLIC "${CMAKE_SOURCE_DIR}/lib"
|
PUBLIC "${CMAKE_SOURCE_DIR}/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(ccm_lib
|
||||||
|
PUBLIC -lm
|
||||||
|
)
|
||||||
install(TARGETS ccm_lib)
|
install(TARGETS ccm_lib)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "bytecode.h"
|
||||||
|
|
||||||
|
CCM_ENUM_C(Opcode, OPCODES);
|
|
@ -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
|
|
@ -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; i<self->stack.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));
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CCM_COMMONS_H
|
||||||
|
#define CCM_COMMONS_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -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; i<node->children.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; i<node->children.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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; i<self->logs.size; i++)
|
||||||
|
{
|
||||||
|
err_log_t const* log = self->logs.data[i];
|
||||||
|
|
||||||
|
fprintf(stderr, "[ERR:%d] %s\n", log->line, log->msg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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; i<param; i++)
|
||||||
|
{
|
||||||
|
CCM val = ccm_pop(ccm);
|
||||||
|
value_t* v = value_new_clone(ccm->values.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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; i<self->texts.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; i<strlen(keyword); i++)
|
||||||
|
{
|
||||||
|
if (self->cursor + 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; i<strlen(text); i++)
|
||||||
|
{
|
||||||
|
if (self->cursor + 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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
77
lib/node.c
77
lib/node.c
|
@ -1 +1,78 @@
|
||||||
#include "node.h"
|
#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; i<self->children.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
30
lib/node.h
30
lib/node.h
|
@ -1,4 +1,30 @@
|
||||||
#ifndef NODE_H
|
#ifndef CCM_NODE_H
|
||||||
#define 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
|
#endif
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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; i<self->instrs.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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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; i<self->size; 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; i<strlen(rhs); i++)
|
||||||
|
{
|
||||||
|
str_push(self, rhs[i]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef CCM_STR_H
|
||||||
|
#define CCM_STR_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
size_t capacity;
|
||||||
|
char* value;
|
||||||
|
} str_t;
|
||||||
|
|
||||||
|
void str_init(str_t* self);
|
||||||
|
void str_free(str_t* self);
|
||||||
|
|
||||||
|
ssize_t str_find(str_t* self, char c);
|
||||||
|
|
||||||
|
void str_push(str_t* self, char c);
|
||||||
|
void str_push_cstr(str_t* self, char const* rhs);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
CCM_ENUM_C(Type, TYPES);
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef CCM_TYPE_H
|
||||||
|
#define CCM_TYPE_H
|
||||||
|
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
|
#define TYPES(G) \
|
||||||
|
G(TYPE_NUM), \
|
||||||
|
G(TYPE_TUPLE)
|
||||||
|
|
||||||
|
CCM_ENUM_H(Type, TYPES);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,148 @@
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
void value_init_num(value_t* self, double num, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->data.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; i<self->data.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; i<self->data.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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; i<self->size; 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue