✨ booleans.
parent
1f154374a8
commit
5a67acd796
|
@ -0,0 +1,4 @@
|
||||||
|
*~*
|
||||||
|
*\#*
|
||||||
|
build
|
||||||
|
.cache
|
|
@ -0,0 +1,9 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(gux LANGUAGES C)
|
||||||
|
|
||||||
|
add_subdirectory(lib)
|
||||||
|
add_subdirectory(lang)
|
||||||
|
add_subdirectory(bc)
|
||||||
|
add_subdirectory(vm)
|
||||||
|
add_subdirectory(guxi)
|
|
@ -0,0 +1,18 @@
|
||||||
|
.PHONY: build tests install
|
||||||
|
|
||||||
|
build:
|
||||||
|
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
|
tests: build
|
||||||
|
build/lang/gux-lang-tests
|
||||||
|
|
||||||
|
install: check tests
|
||||||
|
sudo cmake --install build
|
||||||
|
|
||||||
|
check:
|
||||||
|
@cppcheck --enable=all lib lang vm -q \
|
||||||
|
--suppress=missingIncludeSystem \
|
||||||
|
--suppress=unusedFunction \
|
||||||
|
--suppress=missingInclude \
|
||||||
|
--suppress=unmatchedSuppression
|
|
@ -0,0 +1,22 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(gux-bc LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(gux-bc OBJECT
|
||||||
|
src/compiler.c
|
||||||
|
src/program.c
|
||||||
|
src/opcodes.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET gux-bc PROPERTY C_STANDARD 99)
|
||||||
|
|
||||||
|
add_dependencies(gux-bc gux-lang)
|
||||||
|
|
||||||
|
target_include_directories(gux-bc
|
||||||
|
PUBLIC gux-lang
|
||||||
|
PUBLIC ${CMAKE_SOURCE_DIR}/bc/src
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(gux-bc
|
||||||
|
PUBLIC gux-lang
|
||||||
|
)
|
|
@ -0,0 +1,171 @@
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "commons.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "value.h"
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
void compiler_init(struct compiler* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compiler_free(struct compiler* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
int compiler_compile(struct compiler* self,
|
||||||
|
struct node* node,
|
||||||
|
struct program* program)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(node);
|
||||||
|
assert(program);
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_BOOL: {
|
||||||
|
struct value* val = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(val,
|
||||||
|
strcmp(node->value, "true") == 0 ? 1 : 0,
|
||||||
|
node->line);
|
||||||
|
size_t addr = program_push_constant(program, val);
|
||||||
|
program_push_instr(program, OP_PUSH, addr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_ASSERT: {
|
||||||
|
compiler_compile(self, node->children.data[0], program);
|
||||||
|
size_t brt = program_push_instr(program, OP_BRT, NO_PARAM);
|
||||||
|
program_push_instr(program, OP_PUSH, GUX_RET_ASSERT);
|
||||||
|
size_t halt = program_push_instr(program, OP_HALT, node->line);
|
||||||
|
|
||||||
|
struct instruction* instr = program->instructions.data[brt];
|
||||||
|
instr->param = halt + 1;
|
||||||
|
|
||||||
|
if (halt + 1 >= program->instructions.size)
|
||||||
|
{
|
||||||
|
program_push_instr(program, OP_NOP, NO_PARAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(value, VAL_TRUE, node->line);
|
||||||
|
program_push_instr(program, OP_PUSH,
|
||||||
|
program_push_constant(program, value));
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_NOT: {
|
||||||
|
compiler_compile(self, node->children.data[0], program);
|
||||||
|
program_push_instr(program, OP_NOT, NO_PARAM);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_AND: {
|
||||||
|
struct vec to_pivot;
|
||||||
|
vec_init(&to_pivot, 1);
|
||||||
|
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_compile(self, node->children.data[i], program);
|
||||||
|
|
||||||
|
size_t* addr = malloc(sizeof(size_t));
|
||||||
|
*addr = program_push_instr(program, OP_BRF, NO_PARAM); // to pivot
|
||||||
|
vec_push(&to_pivot, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// true case
|
||||||
|
struct value* true_val = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(true_val, VAL_TRUE, node->line);
|
||||||
|
program_push_instr(program, OP_PUSH,
|
||||||
|
program_push_constant(program, true_val));
|
||||||
|
|
||||||
|
size_t to_end = program_push_instr(program, OP_BR, NO_PARAM); // to end
|
||||||
|
|
||||||
|
// pivot
|
||||||
|
size_t pivot_point = program->instructions.size;
|
||||||
|
|
||||||
|
// false case
|
||||||
|
struct value* false_val = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(false_val, VAL_FALSE, node->line);
|
||||||
|
program_push_instr(program, OP_PUSH,
|
||||||
|
program_push_constant(program, false_val));
|
||||||
|
|
||||||
|
// end
|
||||||
|
size_t end_point = program->instructions.size;
|
||||||
|
|
||||||
|
for (size_t i=0; i<to_pivot.size; i++)
|
||||||
|
{
|
||||||
|
size_t addr = *(size_t*)to_pivot.data[i];
|
||||||
|
struct instruction* instr = program->instructions.data[addr];
|
||||||
|
instr->param = pivot_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct instruction* instr = program->instructions.data[to_end];
|
||||||
|
instr->param = end_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&to_pivot);
|
||||||
|
vec_free(&to_pivot);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_OR: {
|
||||||
|
struct vec to_pivot;
|
||||||
|
vec_init(&to_pivot, 1);
|
||||||
|
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_compile(self, node->children.data[i], program);
|
||||||
|
|
||||||
|
size_t* addr = malloc(sizeof(size_t));
|
||||||
|
*addr = program_push_instr(program, OP_BRT, NO_PARAM); // to pivot
|
||||||
|
vec_push(&to_pivot, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// false case
|
||||||
|
struct value* false_val = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(false_val, VAL_FALSE, node->line);
|
||||||
|
program_push_instr(program, OP_PUSH,
|
||||||
|
program_push_constant(program, false_val));
|
||||||
|
|
||||||
|
size_t to_end = program_push_instr(program, OP_BR, NO_PARAM); // to end
|
||||||
|
|
||||||
|
// pivot
|
||||||
|
size_t pivot_point = program->instructions.size;
|
||||||
|
|
||||||
|
// false case
|
||||||
|
struct value* true_val = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(true_val, VAL_TRUE, node->line);
|
||||||
|
program_push_instr(program, OP_PUSH,
|
||||||
|
program_push_constant(program, true_val));
|
||||||
|
|
||||||
|
// end
|
||||||
|
size_t end_point = program->instructions.size;
|
||||||
|
|
||||||
|
for (size_t i=0; i<to_pivot.size; i++)
|
||||||
|
{
|
||||||
|
size_t addr = *(size_t*)to_pivot.data[i];
|
||||||
|
struct instruction* instr = program->instructions.data[addr];
|
||||||
|
instr->param = pivot_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct instruction* instr = program->instructions.data[to_end];
|
||||||
|
instr->param = end_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&to_pivot);
|
||||||
|
vec_free(&to_pivot);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_compile(self, node->children.data[i], program);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef COMPILER_H
|
||||||
|
#define COMPILER_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <node.h>
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
struct compiler {
|
||||||
|
char* error_msg;
|
||||||
|
int error_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
void compiler_init(struct compiler* self);
|
||||||
|
void compiler_free(struct compiler* self);
|
||||||
|
|
||||||
|
int compiler_compile(struct compiler* self,
|
||||||
|
struct node* node,
|
||||||
|
struct program* program);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "opcodes.h"
|
||||||
|
|
||||||
|
GUX_ENUM_C(Opcodes, OPCODES);
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef OPCODES_H
|
||||||
|
#define OPCODES_H
|
||||||
|
|
||||||
|
#define OPCODES(G) \
|
||||||
|
G(OP_PUSH), G(OP_POP), G(OP_BRF), G(OP_BR), G(OP_HALT), \
|
||||||
|
G(OP_BRT), G(OP_NOP), G(OP_NOT)
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
|
||||||
|
GUX_ENUM_H(Opcodes, OPCODES);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include "program.h"
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
|
void program_init(struct program* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_init(&self->constant_pool, 1);
|
||||||
|
vec_init(&self->instructions, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void program_free(struct program* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->constant_pool.size; i++)
|
||||||
|
{
|
||||||
|
value_free(self->constant_pool.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&self->constant_pool);
|
||||||
|
vec_free(&self->constant_pool);
|
||||||
|
|
||||||
|
vec_free_elements(&self->instructions);
|
||||||
|
vec_free(&self->instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t program_push_instr(struct program* self, enum Opcodes op, int param)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct instruction* instr = malloc(sizeof(struct instruction));
|
||||||
|
instr->opcode = op;
|
||||||
|
instr->param = param;
|
||||||
|
|
||||||
|
vec_push(&self->instructions, instr);
|
||||||
|
return self->instructions.size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t program_push_constant(struct program* self, struct value* value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(value);
|
||||||
|
|
||||||
|
vec_push(&self->constant_pool, value);
|
||||||
|
return self->constant_pool.size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t program_str(struct program* self, char* buffer, size_t size)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(buffer);
|
||||||
|
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
sz += snprintf(buffer, size, "[CONSTANTS]\n");
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->constant_pool.size; i++)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "\t%ld: ", i);
|
||||||
|
sz += value_str(self->constant_pool.data[i], buffer + sz, size - sz);
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "[PROGRAM]\n");
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->instructions.size; i++)
|
||||||
|
{
|
||||||
|
struct instruction* instr = (struct instruction*)
|
||||||
|
self->instructions.data[i];
|
||||||
|
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "\t%ld: %s %d\n",
|
||||||
|
i,
|
||||||
|
OpcodesStr[instr->opcode] + strlen("OP_"),
|
||||||
|
instr->param);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sz;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef PROGRAM_H
|
||||||
|
#define PROGRAM_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <value.h>
|
||||||
|
#include "opcodes.h"
|
||||||
|
|
||||||
|
#define NO_PARAM -1
|
||||||
|
|
||||||
|
struct instruction {
|
||||||
|
enum Opcodes opcode;
|
||||||
|
int param;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct program {
|
||||||
|
struct vec instructions;
|
||||||
|
struct vec constant_pool;
|
||||||
|
};
|
||||||
|
|
||||||
|
void program_init(struct program* self);
|
||||||
|
void program_free(struct program* self);
|
||||||
|
|
||||||
|
size_t program_push_instr(struct program* self, enum Opcodes op, int param);
|
||||||
|
size_t program_push_constant(struct program* self, struct value* value);
|
||||||
|
|
||||||
|
size_t program_str(struct program* self, char* buffer, size_t size);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
ROOT ::= EXPR*
|
||||||
|
|
||||||
|
INSTR ::=
|
||||||
|
| EXPR semicolon
|
||||||
|
|
||||||
|
EXPR ::=
|
||||||
|
| OR
|
||||||
|
| ASSERT EXPR
|
||||||
|
|
||||||
|
OR ::= AND (or AND)*
|
||||||
|
AND ::= NOT (and NOT)*
|
||||||
|
NOT ::= not* LITERAL
|
||||||
|
|
||||||
|
LITERAL ::= BUILTIN | opar EXPR cpar
|
||||||
|
BUILTIN ::= bool
|
|
@ -0,0 +1,24 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(guxi LANGUAGES C)
|
||||||
|
|
||||||
|
add_executable(guxi
|
||||||
|
src/main.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET guxi PROPERTY C_STANDARD 99)
|
||||||
|
|
||||||
|
add_dependencies(guxi gux-vm)
|
||||||
|
|
||||||
|
target_include_directories(guxi
|
||||||
|
PUBLIC gux-vm gux-bc
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(guxi
|
||||||
|
PUBLIC gux-vm
|
||||||
|
PUBLIC gux-lang
|
||||||
|
PUBLIC gux-bc
|
||||||
|
PUBLIC gux-lib
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS guxi)
|
|
@ -0,0 +1,309 @@
|
||||||
|
#include <commons.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <lexer.h>
|
||||||
|
#include <parser.h>
|
||||||
|
#include <type_checker.h>
|
||||||
|
#include <compiler.h>
|
||||||
|
#include <vm.h>
|
||||||
|
|
||||||
|
char* load_new_source(char const* path)
|
||||||
|
{
|
||||||
|
FILE* file = fopen(path, "r+");
|
||||||
|
assert(file);
|
||||||
|
|
||||||
|
char* source = malloc(sizeof(char));
|
||||||
|
size_t size = 0;
|
||||||
|
size_t cap = 1;
|
||||||
|
|
||||||
|
char buf;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
while ( (sz=fread(&buf, sizeof(char), 1, file)) > 0)
|
||||||
|
{
|
||||||
|
if (size + 1 >= cap)
|
||||||
|
{
|
||||||
|
cap *= 2;
|
||||||
|
source = realloc(source, sizeof(char) * cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
source[size] = buf;
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size + 1 >= cap)
|
||||||
|
{
|
||||||
|
cap *= 2;
|
||||||
|
source = realloc(source, sizeof(char) * cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
source[size] = '\0';
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
static int show_tokens = 0;
|
||||||
|
static int show_ast = 0;
|
||||||
|
static int show_bytecodes = 0;
|
||||||
|
static int show_stack = 0;
|
||||||
|
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
static struct option long_opts[] = {
|
||||||
|
{"tokens", no_argument, &show_tokens, 1},
|
||||||
|
{"ast", no_argument, &show_ast, 1},
|
||||||
|
{"bytecodes", no_argument, &show_bytecodes, 1},
|
||||||
|
{"stack", no_argument, &show_stack, 1},
|
||||||
|
{"help", no_argument, 0, 'h'}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt_index = 0;
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "h", long_opts, &opt_index);
|
||||||
|
|
||||||
|
if (c == -1) { break; }
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'h': {
|
||||||
|
printf("Usage: guxi [OPTION]... [SOURCE]...\n");
|
||||||
|
printf("OPTIONS:\n");
|
||||||
|
printf("\t--ast\tshow abstract syntax tree.\n");
|
||||||
|
printf("\t--tokens\tshow tokens.\n");
|
||||||
|
printf("\t-h, --help\tprint this help message.\n");
|
||||||
|
exit(0);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (optind < argc)
|
||||||
|
{
|
||||||
|
char const* path = argv[optind++];
|
||||||
|
char* src = load_new_source(path);
|
||||||
|
|
||||||
|
struct lexer lex;
|
||||||
|
lexer_init(&lex, src);
|
||||||
|
|
||||||
|
struct vec tokens;
|
||||||
|
vec_init(&tokens, 1);
|
||||||
|
int err = lexer_extract(&lex, &tokens);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[%s:%d] %s\n",
|
||||||
|
path,
|
||||||
|
lex.line,
|
||||||
|
lex.error_msg);
|
||||||
|
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
free(src);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_tokens)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
char buf[GUX_STR_SIZE];
|
||||||
|
node_str(tokens.data[i], buf, GUX_STR_SIZE);
|
||||||
|
printf("%s\n", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct parser parser;
|
||||||
|
parser_init(&parser);
|
||||||
|
|
||||||
|
struct node* ast = parser_try_new_root(&parser, src);
|
||||||
|
|
||||||
|
if (ast == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[%s:%d] %s\n",
|
||||||
|
path,
|
||||||
|
parser.error_line,
|
||||||
|
parser.error_msg);
|
||||||
|
|
||||||
|
parser_free(&parser);
|
||||||
|
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
free(src);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (show_ast)
|
||||||
|
{
|
||||||
|
char msg[GUX_STR_SIZE];
|
||||||
|
node_str(ast, msg, GUX_STR_SIZE);
|
||||||
|
printf("%s\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct type_checker checker;
|
||||||
|
type_checker_init(&checker);
|
||||||
|
|
||||||
|
if (type_checker_check(&checker, ast) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[%s:%d] %s\n",
|
||||||
|
path,
|
||||||
|
checker.error_line,
|
||||||
|
checker.error_msg);
|
||||||
|
|
||||||
|
type_checker_free(&checker);
|
||||||
|
|
||||||
|
if (ast)
|
||||||
|
{
|
||||||
|
node_free(ast);
|
||||||
|
free(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_free(&parser);
|
||||||
|
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
free(src);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct program program;
|
||||||
|
program_init(&program);
|
||||||
|
|
||||||
|
struct compiler compiler;
|
||||||
|
compiler_init(&compiler);
|
||||||
|
|
||||||
|
if (compiler_compile(&compiler, ast, &program) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[%s:%d] %s\n",
|
||||||
|
path,
|
||||||
|
compiler.error_line,
|
||||||
|
compiler.error_msg);
|
||||||
|
|
||||||
|
compiler_free(&compiler);
|
||||||
|
program_free(&program);
|
||||||
|
type_checker_free(&checker);
|
||||||
|
|
||||||
|
if (ast)
|
||||||
|
{
|
||||||
|
node_free(ast);
|
||||||
|
free(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_free(&parser);
|
||||||
|
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
free(src);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_bytecodes)
|
||||||
|
{
|
||||||
|
char buffer[GUX_STR_SIZE];
|
||||||
|
program_str(&program, buffer, GUX_STR_SIZE);
|
||||||
|
printf("%s\n", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vm vm;
|
||||||
|
vm_init(&vm);
|
||||||
|
|
||||||
|
err = vm_exec(&vm, &program);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "[%s:%d] %s\n",
|
||||||
|
path,
|
||||||
|
vm.error_line,
|
||||||
|
vm.error_msg);
|
||||||
|
|
||||||
|
vm_free(&vm);
|
||||||
|
compiler_free(&compiler);
|
||||||
|
program_free(&program);
|
||||||
|
type_checker_free(&checker);
|
||||||
|
|
||||||
|
if (ast)
|
||||||
|
{
|
||||||
|
node_free(ast);
|
||||||
|
free(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_free(&parser);
|
||||||
|
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
free(src);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_stack)
|
||||||
|
{
|
||||||
|
char buffer[GUX_STR_SIZE];
|
||||||
|
vm_str(&vm, buffer, GUX_STR_SIZE);
|
||||||
|
printf("%s\n", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free
|
||||||
|
// ----
|
||||||
|
vm_free(&vm);
|
||||||
|
compiler_free(&compiler);
|
||||||
|
program_free(&program);
|
||||||
|
type_checker_free(&checker);
|
||||||
|
|
||||||
|
if (ast)
|
||||||
|
{
|
||||||
|
node_free(ast);
|
||||||
|
free(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_free(&parser);
|
||||||
|
|
||||||
|
for (size_t i=0; i<tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
free(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
project(gux-lang LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(gux-lang OBJECT
|
||||||
|
src/node.c
|
||||||
|
src/lexer.c
|
||||||
|
src/parser.c
|
||||||
|
src/type.c
|
||||||
|
src/value.c
|
||||||
|
src/type_resolver.c
|
||||||
|
src/type_checker.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET gux-lang PROPERTY C_STANDARD 99)
|
||||||
|
|
||||||
|
add_dependencies(gux-lang gux-lib)
|
||||||
|
|
||||||
|
target_include_directories(gux-lang
|
||||||
|
PUBLIC gux-lib
|
||||||
|
PUBLIC ${CMAKE_SOURCE_DIR}/lang/src
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(gux-lang
|
||||||
|
PUBLIC gux-lib
|
||||||
|
)
|
||||||
|
|
||||||
|
# =====
|
||||||
|
# TESTS
|
||||||
|
# =====
|
||||||
|
project(gux-lang-tests LANGUAGES C)
|
||||||
|
|
||||||
|
add_executable(gux-lang-tests
|
||||||
|
tests/trivial.c
|
||||||
|
tests/node.c
|
||||||
|
tests/lexer.c
|
||||||
|
tests/parser.c
|
||||||
|
tests/type.c
|
||||||
|
tests/value.c
|
||||||
|
tests/type_checker.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET gux-lang-tests PROPERTY C_STANDARD 99)
|
||||||
|
|
||||||
|
add_dependencies(gux-lang-tests gux-lang)
|
||||||
|
|
||||||
|
target_include_directories(gux-lang-tests
|
||||||
|
PUBLIC gux-lang
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg_check_modules(CRITERION criterion REQUIRED IMPORTED_TARGET)
|
||||||
|
|
||||||
|
target_link_libraries(gux-lang-tests
|
||||||
|
PUBLIC gux-lang
|
||||||
|
PUBLIC gux-lib
|
||||||
|
PRIVATE PkgConfig::CRITERION
|
||||||
|
)
|
|
@ -0,0 +1,238 @@
|
||||||
|
#include "lexer.h"
|
||||||
|
|
||||||
|
void lexer_init(struct lexer* self, char const* source)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(source);
|
||||||
|
|
||||||
|
self->source = strdup(source);
|
||||||
|
self->cursor = 0;
|
||||||
|
self->line = 1;
|
||||||
|
|
||||||
|
vec_init(&self->toks, 1);
|
||||||
|
|
||||||
|
lexer_add_tok(self, "&&", NODE_AND, 0);
|
||||||
|
lexer_add_tok(self, "||", NODE_OR, 0);
|
||||||
|
lexer_add_tok(self, "!", NODE_NOT, 0);
|
||||||
|
lexer_add_tok(self, "(", NODE_OPAR, 0);
|
||||||
|
lexer_add_tok(self, ")", NODE_CPAR, 0);
|
||||||
|
lexer_add_tok(self, ";", NODE_SEMICOLON, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_free(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
free(self->source);
|
||||||
|
|
||||||
|
vec_free_elements(&self->toks);
|
||||||
|
vec_free(&self->toks);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lexer_extract(struct lexer* self, struct vec* buffer)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(buffer);
|
||||||
|
|
||||||
|
struct node* node;
|
||||||
|
|
||||||
|
while ( (node=lexer_next_new(self)) )
|
||||||
|
{
|
||||||
|
vec_push(buffer, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->cursor < strlen(self->source))
|
||||||
|
{
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||||
|
"unexpected symbol '%c'", self->source[self->cursor]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* lexer_next_new(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
lexer_skip_spaces(self);
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
while (self->cursor < strlen(self->source)
|
||||||
|
&& self->source[self->cursor] == '#')
|
||||||
|
{
|
||||||
|
while (self->cursor < strlen(self->source)
|
||||||
|
&& self->source[self->cursor] != '\n')
|
||||||
|
{
|
||||||
|
self->cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_skip_spaces(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct token_info info;
|
||||||
|
|
||||||
|
struct node* best = NULL;
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->toks.size; i++)
|
||||||
|
{
|
||||||
|
struct tok* tok = self->toks.data[i];
|
||||||
|
|
||||||
|
if (lexer_scan_text(self, tok->sym, &info))
|
||||||
|
{
|
||||||
|
if (best == NULL || pos > info.position)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, tok->type, "", self->line);
|
||||||
|
|
||||||
|
if (best)
|
||||||
|
{
|
||||||
|
node_free(best);
|
||||||
|
}
|
||||||
|
|
||||||
|
best = node;
|
||||||
|
pos = info.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->cursor = info.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best)
|
||||||
|
{
|
||||||
|
self->cursor = pos;
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexer_scan_keyword(self, "true", &info)
|
||||||
|
|| lexer_scan_keyword(self, "false", &info))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_BOOL, info.value, self->line);
|
||||||
|
self->cursor = info.position;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexer_scan_keyword(self, "assert", &info))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_ASSERT, "", self->line);
|
||||||
|
self->cursor = info.position;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lexer_is_sep(struct lexer* self, size_t index)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(index < strlen(self->source));
|
||||||
|
char c = self->source[index];
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->toks.size; i++)
|
||||||
|
{
|
||||||
|
if (c == ((struct tok*)self->toks.data[i])->sym[0])
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isspace(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_skip_spaces(struct lexer* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
while (self->cursor < strlen(self->source)
|
||||||
|
&& isspace(self->source[self->cursor]))
|
||||||
|
{
|
||||||
|
if (self->source[self->cursor] == '\n')
|
||||||
|
{
|
||||||
|
self->line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lexer_scan_keyword(struct lexer* self,
|
||||||
|
char* keyword,
|
||||||
|
struct token_info* info)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(keyword);
|
||||||
|
assert(info);
|
||||||
|
|
||||||
|
size_t cursor = self->cursor;
|
||||||
|
|
||||||
|
for (size_t i=0; i<strlen(keyword); i++)
|
||||||
|
{
|
||||||
|
if (cursor >= strlen(self->source)
|
||||||
|
|| keyword[i] != self->source[self->cursor + i])
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(self->cursor == 0
|
||||||
|
|| lexer_is_sep(self, self->cursor - 1)))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cursor + 1 >= strlen(self->source)
|
||||||
|
|| lexer_is_sep(self, cursor)))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->position = self->cursor + strlen(keyword);
|
||||||
|
info->value = keyword;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lexer_scan_text(struct lexer* self,
|
||||||
|
char* text,
|
||||||
|
struct token_info* info)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(text);
|
||||||
|
assert(info);
|
||||||
|
|
||||||
|
size_t cursor = self->cursor;
|
||||||
|
|
||||||
|
for (size_t i=0; i<strlen(text); i++)
|
||||||
|
{
|
||||||
|
if (cursor >= strlen(self->source)
|
||||||
|
|| text[i] != self->source[self->cursor + i])
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->position = self->cursor + strlen(text);
|
||||||
|
info->value = text;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_add_tok(struct lexer* self,
|
||||||
|
char* sym,
|
||||||
|
enum NodeType type,
|
||||||
|
int is_keyword)
|
||||||
|
{
|
||||||
|
(void) self;
|
||||||
|
struct tok* tok = malloc(sizeof(struct tok));
|
||||||
|
tok->sym = sym;
|
||||||
|
tok->type = type;
|
||||||
|
tok->is_keyword = is_keyword;
|
||||||
|
|
||||||
|
vec_push(&self->toks, tok);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef LEXER_H
|
||||||
|
#define LEXER_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
struct tok {
|
||||||
|
char* sym;
|
||||||
|
enum NodeType type;
|
||||||
|
int is_keyword;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lexer {
|
||||||
|
char* source;
|
||||||
|
size_t cursor;
|
||||||
|
int line;
|
||||||
|
char error_msg[GUX_STR_SIZE];
|
||||||
|
struct vec toks;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct token_info {
|
||||||
|
enum NodeType type;
|
||||||
|
char* value;
|
||||||
|
size_t position;
|
||||||
|
};
|
||||||
|
|
||||||
|
void lexer_init(struct lexer* self, char const* source);
|
||||||
|
void lexer_free(struct lexer* self);
|
||||||
|
|
||||||
|
int lexer_extract(struct lexer* self, struct vec* buffer);
|
||||||
|
struct node* lexer_next_new(struct lexer* self);
|
||||||
|
|
||||||
|
int lexer_is_sep(struct lexer* self, size_t index);
|
||||||
|
|
||||||
|
void lexer_skip_spaces(struct lexer* self);
|
||||||
|
|
||||||
|
int lexer_scan_keyword(struct lexer* self,
|
||||||
|
char* keyword,
|
||||||
|
struct token_info* info);
|
||||||
|
|
||||||
|
int lexer_scan_text(struct lexer* self,
|
||||||
|
char* text,
|
||||||
|
struct token_info* info);
|
||||||
|
|
||||||
|
void lexer_add_tok(struct lexer* self,
|
||||||
|
char* sym,
|
||||||
|
enum NodeType type,
|
||||||
|
int is_keyword);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include "node.h"
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
GUX_ENUM_C(NodeType, NODE_TYPE);
|
||||||
|
|
||||||
|
void node_init(struct node* self, enum NodeType type,
|
||||||
|
char const* value, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->type = type;
|
||||||
|
self->value = strdup(value);
|
||||||
|
vec_init(&self->children, 1);
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_free(struct node* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
free(self->value);
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->children.size; i++)
|
||||||
|
{
|
||||||
|
node_free(self->children.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&self->children);
|
||||||
|
vec_free(&self->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node_add_new_child(struct node* self,
|
||||||
|
enum NodeType type, char const* value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* child = malloc(sizeof(struct node));
|
||||||
|
node_init(child, type, value, self->line);
|
||||||
|
|
||||||
|
vec_push(&self->children, child);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node_add_child(struct node* self, struct node* child)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_push(&self->children, child);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t node_str(struct node* self, char* buffer, size_t size)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(buffer);
|
||||||
|
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%s",
|
||||||
|
NodeTypeStr[self->type] + strlen("NODE_"));
|
||||||
|
|
||||||
|
if (strcmp(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;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef NODE_H
|
||||||
|
#define NODE_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
#define NODE_TYPE(G) \
|
||||||
|
G(NODE_ROOT), \
|
||||||
|
G(NODE_SEMICOLON), \
|
||||||
|
G(NODE_BOOL), G(NODE_ASSERT), G(NODE_OPAR), \
|
||||||
|
G(NODE_CPAR), G(NODE_AND), G(NODE_OR), G(NODE_NOT)
|
||||||
|
|
||||||
|
GUX_ENUM_H(NodeType, NODE_TYPE);
|
||||||
|
|
||||||
|
struct node {
|
||||||
|
enum NodeType type;
|
||||||
|
char* value;
|
||||||
|
struct vec children;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
void node_init(struct node* self, enum NodeType type,
|
||||||
|
char const* value, int line);
|
||||||
|
void node_free(struct node* self);
|
||||||
|
|
||||||
|
struct node* node_add_new_child(struct node* self,
|
||||||
|
enum NodeType type, char const* value);
|
||||||
|
|
||||||
|
struct node* node_add_child(struct node* self, struct node* child);
|
||||||
|
|
||||||
|
size_t node_str(struct node* self, char* buffer, size_t size);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,291 @@
|
||||||
|
#include "parser.h"
|
||||||
|
#include "commons.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
void parser_init(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_init(&self->tokens, 1);
|
||||||
|
self->cursor = 0;
|
||||||
|
self->error_line = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parser_free(struct parser* self)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<self->tokens.size; i++)
|
||||||
|
{
|
||||||
|
node_free(self->tokens.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&self->tokens);
|
||||||
|
vec_free(&self->tokens);
|
||||||
|
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
int parser_current_line(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->cursor >= self->tokens.size)
|
||||||
|
{
|
||||||
|
struct node* node = (struct node*)
|
||||||
|
self->tokens.data[self->tokens.size - 1];
|
||||||
|
return node->line;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node = (struct node*) self->tokens.data[self->cursor];
|
||||||
|
return node->line;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parser_type_is(struct parser* self,
|
||||||
|
enum NodeType type,
|
||||||
|
size_t lookahead)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->cursor + lookahead >= self->tokens.size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((struct node*)
|
||||||
|
self->tokens.data[self->cursor + lookahead])->type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_consume_new(struct parser* self, enum NodeType type)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (!parser_type_is(self, type, 0))
|
||||||
|
{
|
||||||
|
struct node* token = ((struct node*)self->tokens.data[self->cursor]);
|
||||||
|
|
||||||
|
self->error_line = parser_current_line(self);
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE, "expected <%s>, got <%s>",
|
||||||
|
NodeTypeStr[type] + strlen("NODE_"),
|
||||||
|
NodeTypeStr[token->type]
|
||||||
|
+ strlen("NODE_"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* token = self->tokens.data[self->cursor];
|
||||||
|
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, token->type, token->value, token->line);
|
||||||
|
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parser_ensure(struct parser* self, enum NodeType type)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->cursor >= self->tokens.size)
|
||||||
|
{
|
||||||
|
self->error_line = parser_current_line(self);
|
||||||
|
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||||
|
"expected <%s>, got nothing",
|
||||||
|
NodeTypeStr[type] + strlen("NODE_"));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* current = self->tokens.data[self->cursor];
|
||||||
|
|
||||||
|
if (current->type != type)
|
||||||
|
{
|
||||||
|
self->error_line = current->line;
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||||
|
"expected <%s>, got <%s>",
|
||||||
|
NodeTypeStr[type] + strlen("NODE_"),
|
||||||
|
NodeTypeStr[current->type] + strlen("NODE_"));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parser_try_consume(struct parser* self, enum NodeType type)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (!parser_type_is(self, type, 0))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_root(struct parser* self, char const* source)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct lexer lex;
|
||||||
|
lexer_init(&lex, source);
|
||||||
|
lexer_extract(&lex, &self->tokens);
|
||||||
|
lexer_free(&lex);
|
||||||
|
|
||||||
|
struct node* root = malloc(sizeof(struct node));
|
||||||
|
node_init(root, NODE_ROOT, "", 0);
|
||||||
|
|
||||||
|
while (self->cursor < self->tokens.size)
|
||||||
|
{
|
||||||
|
struct node* node = parser_try_new_instr(self);
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
node_free(root);
|
||||||
|
free(root);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root->line == 0)
|
||||||
|
{
|
||||||
|
root->line = node->line;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_add_child(root, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_instr(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct node* node = parser_try_new_expr(self);
|
||||||
|
|
||||||
|
if (parser_ensure(self, NODE_SEMICOLON) != 0)
|
||||||
|
{
|
||||||
|
node_free(node);
|
||||||
|
free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_expr(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (parser_type_is(self, NODE_ASSERT, 0))
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_ASSERT, "", parser_current_line(self));
|
||||||
|
self->cursor++;
|
||||||
|
|
||||||
|
struct node* expr = parser_try_new_expr(self);
|
||||||
|
|
||||||
|
if (!expr)
|
||||||
|
{
|
||||||
|
node_free(node);
|
||||||
|
free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_add_child(node, expr);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node = parser_try_new_or(self);
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_or(struct parser* self)
|
||||||
|
{
|
||||||
|
struct node* lhs = parser_try_new_and(self);
|
||||||
|
|
||||||
|
while (parser_try_consume(self, NODE_OR) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_OR, "", parser_current_line(self));
|
||||||
|
|
||||||
|
struct node* rhs = parser_try_new_and(self);
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
node_add_child(node, rhs);
|
||||||
|
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_and(struct parser* self)
|
||||||
|
{
|
||||||
|
struct node* lhs = parser_try_new_not(self);
|
||||||
|
|
||||||
|
while (parser_try_consume(self, NODE_AND) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_AND, "", parser_current_line(self));
|
||||||
|
|
||||||
|
struct node* rhs = parser_try_new_not(self);
|
||||||
|
|
||||||
|
node_add_child(node, lhs);
|
||||||
|
node_add_child(node, rhs);
|
||||||
|
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_not(struct parser* self)
|
||||||
|
{
|
||||||
|
if (parser_try_consume(self, NODE_NOT) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_NOT, "", parser_current_line(self));
|
||||||
|
|
||||||
|
node_add_child(node, parser_try_new_not(self));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser_try_new_literal(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_literal(struct parser* self)
|
||||||
|
{
|
||||||
|
if (parser_try_consume(self, NODE_OPAR) == 0)
|
||||||
|
{
|
||||||
|
struct node* node = parser_try_new_expr(self);
|
||||||
|
|
||||||
|
if (parser_try_consume(self, NODE_CPAR) != 0)
|
||||||
|
{
|
||||||
|
node_free(node);
|
||||||
|
free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser_try_new_builtin(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_new_builtin(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
struct node* value = parser_try_consume_new(self, NODE_BOOL);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef PARSER_H
|
||||||
|
#define PARSER_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
struct parser {
|
||||||
|
struct vec tokens;
|
||||||
|
size_t cursor;
|
||||||
|
char error_msg[GUX_STR_SIZE];
|
||||||
|
int error_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
void parser_init(struct parser* self);
|
||||||
|
void parser_free(struct parser* self);
|
||||||
|
|
||||||
|
int parser_current_line(struct parser* self);
|
||||||
|
int parser_type_is(struct parser* self,
|
||||||
|
enum NodeType type,
|
||||||
|
size_t lookahead);
|
||||||
|
|
||||||
|
struct node* parser_try_consume_new(struct parser* self, enum NodeType type);
|
||||||
|
int parser_try_consume(struct parser* self, enum NodeType type);
|
||||||
|
int parser_ensure(struct parser* self, enum NodeType type);
|
||||||
|
|
||||||
|
struct node* parser_try_new_root(struct parser* self, char const* source);
|
||||||
|
struct node* parser_try_new_instr(struct parser* self);
|
||||||
|
struct node* parser_try_new_expr(struct parser* self);
|
||||||
|
struct node* parser_try_new_or(struct parser* self);
|
||||||
|
struct node* parser_try_new_and(struct parser* self);
|
||||||
|
struct node* parser_try_new_not(struct parser* self);
|
||||||
|
struct node* parser_try_new_literal(struct parser* self);
|
||||||
|
struct node* parser_try_new_builtin(struct parser* self);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,91 @@
|
||||||
|
#include "type.h"
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
GUX_ENUM_C(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
void type_init(struct type* self, enum TypeKind kind)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->kind = kind;
|
||||||
|
vec_init(&self->subtypes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_free(struct type* self)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<self->subtypes.size; i++)
|
||||||
|
{
|
||||||
|
struct type* sty = self->subtypes.data[i];
|
||||||
|
type_free(sty);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free_elements(&self->subtypes);
|
||||||
|
vec_free(&self->subtypes);
|
||||||
|
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_clear(struct type* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
vec_clear(&self->subtypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int type_equals(struct type* self, struct type* rhs)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(rhs);
|
||||||
|
|
||||||
|
if (self->kind != rhs->kind
|
||||||
|
|| self->subtypes.size != rhs->subtypes.size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->subtypes.size; i++)
|
||||||
|
{
|
||||||
|
if (!type_equals(self->subtypes.data[i], rhs->subtypes.data[i]))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_add_subtype(struct type* self, enum TypeKind kind)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct type* ty = malloc(sizeof(struct type));
|
||||||
|
type_init(ty, kind);
|
||||||
|
|
||||||
|
vec_push(&self->subtypes, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t type_str(struct type* self, char* buffer, size_t size)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(buffer);
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%s",
|
||||||
|
TypeKindStr[self->kind] + strlen("TYPE_"));
|
||||||
|
|
||||||
|
if (self->subtypes.size > 0)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "(");
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->subtypes.size; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
sz += type_str(self->subtypes.data[i], buffer + sz, size - sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
sz += snprintf(buffer + sz, size - sz, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sz;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef TYPE_H
|
||||||
|
#define TYPE_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
#define TYPE_KIND(G) \
|
||||||
|
G(TYPE_VOID), G(TYPE_BOOL)
|
||||||
|
|
||||||
|
GUX_ENUM_H(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
|
struct type {
|
||||||
|
enum TypeKind kind;
|
||||||
|
struct vec subtypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
void type_init(struct type* self, enum TypeKind kind);
|
||||||
|
void type_free(struct type* self);
|
||||||
|
|
||||||
|
void type_clear(struct type* self);
|
||||||
|
|
||||||
|
int type_equals(struct type* self, struct type* rhs);
|
||||||
|
void type_add_subtype(struct type* self, enum TypeKind kind);
|
||||||
|
size_t type_str(struct type* self, char* buffer, size_t size);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,80 @@
|
||||||
|
#include "type_checker.h"
|
||||||
|
#include "commons.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
void type_checker_init(struct type_checker* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
type_resolver_init(&self->resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_checker_free(struct type_checker* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
type_resolver_free(&self->resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
int type_checker_check(struct type_checker* self,
|
||||||
|
struct node* node)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(node);
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_ASSERT:
|
||||||
|
case NODE_NOT:
|
||||||
|
case NODE_AND:
|
||||||
|
case NODE_OR:{
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
if (type_checker_ensure_kind(self,
|
||||||
|
node->children.data[i],
|
||||||
|
TYPE_BOOL) != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} return 0;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
if (type_checker_check(self, node->children.data[i]) != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type_checker_ensure_kind(struct type_checker* self,
|
||||||
|
struct node* node,
|
||||||
|
enum TypeKind kind)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(node);
|
||||||
|
|
||||||
|
struct type type;
|
||||||
|
type_init(&type, TYPE_VOID);
|
||||||
|
type_resolver_resolve(&self->resolver, node, &type);
|
||||||
|
|
||||||
|
if (type.kind != kind || type.subtypes.size != 0)
|
||||||
|
{
|
||||||
|
self->error_line = node->line;
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE,
|
||||||
|
"type mismatch: expected <%s>, got <%s>",
|
||||||
|
TypeKindStr[kind] + strlen("TYPE_"),
|
||||||
|
TypeKindStr[type.kind] + strlen("TYPE_"));
|
||||||
|
|
||||||
|
type_free(&type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_free(&type);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef TYPE_CHECKER_H
|
||||||
|
#define TYPE_CHECKER_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <type.h>
|
||||||
|
#include <node.h>
|
||||||
|
#include <type_resolver.h>
|
||||||
|
|
||||||
|
struct type_checker {
|
||||||
|
struct type_resolver resolver;
|
||||||
|
char* error_msg;
|
||||||
|
int error_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
void type_checker_init(struct type_checker* self);
|
||||||
|
void type_checker_free(struct type_checker* self);
|
||||||
|
|
||||||
|
int type_checker_check(struct type_checker* self,
|
||||||
|
struct node* node);
|
||||||
|
|
||||||
|
int type_checker_ensure_kind(struct type_checker* self,
|
||||||
|
struct node* node,
|
||||||
|
enum TypeKind kind);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "type_resolver.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
void type_resolver_init(struct type_resolver* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void type_resolver_free(struct type_resolver* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
int type_resolver_resolve(struct type_resolver* self,
|
||||||
|
struct node* node,
|
||||||
|
struct type* type)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(node);
|
||||||
|
assert(type);
|
||||||
|
|
||||||
|
type_clear(type);
|
||||||
|
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_ASSERT:
|
||||||
|
case NODE_BOOL:
|
||||||
|
case NODE_AND:
|
||||||
|
case NODE_OR:
|
||||||
|
case NODE_NOT:{
|
||||||
|
type->kind = TYPE_BOOL;
|
||||||
|
} return 0;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
} return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef TYPE_RESOLVER_H
|
||||||
|
#define TYPE_RESOLVER_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <node.h>
|
||||||
|
#include <type.h>
|
||||||
|
|
||||||
|
struct type_resolver {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void type_resolver_init(struct type_resolver* self);
|
||||||
|
void type_resolver_free(struct type_resolver* self);
|
||||||
|
|
||||||
|
int type_resolver_resolve(struct type_resolver* self,
|
||||||
|
struct node* node,
|
||||||
|
struct type* type);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
void value_init_bool(struct value* self, int value, int line)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
type_init(&self->type, TYPE_BOOL);
|
||||||
|
self->data.b = value;
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void value_free(struct value* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
type_free(&self->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t value_str(struct value* self, char* buffer, size_t size)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(buffer);
|
||||||
|
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
if (self->type.subtypes.size == 0)
|
||||||
|
{
|
||||||
|
if (self->type.kind == TYPE_VOID)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "void");
|
||||||
|
}
|
||||||
|
else if (self->type.kind == TYPE_BOOL)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "%s",
|
||||||
|
self->data.b ? "true" : "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_and(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
if (self->type.kind != TYPE_BOOL
|
||||||
|
|| self->type.subtypes.size != 0
|
||||||
|
|| !type_equals(&self->type, &rhs->type))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(res, self->data.b && rhs->data.b, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_or(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
if (self->type.kind != TYPE_BOOL
|
||||||
|
|| self->type.subtypes.size != 0
|
||||||
|
|| !type_equals(&self->type, &rhs->type))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(res, self->data.b || rhs->data.b, self->line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* value_try_new_not(struct value* self)
|
||||||
|
{
|
||||||
|
if (self->type.kind != TYPE_BOOL
|
||||||
|
|| self->type.subtypes.size != 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value* res = malloc(sizeof(struct value));
|
||||||
|
value_init_bool(res, !self->data.b, self->line);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef VALUE_H
|
||||||
|
#define VALUE_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
#include <type.h>
|
||||||
|
|
||||||
|
#define VAL_TRUE 1
|
||||||
|
#define VAL_FALSE 0
|
||||||
|
|
||||||
|
union value_data {
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct value {
|
||||||
|
struct type type;
|
||||||
|
int line;
|
||||||
|
union value_data data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void value_init_bool(struct value* self, int value, int line);
|
||||||
|
void value_free(struct value* self);
|
||||||
|
|
||||||
|
size_t value_str(struct value* self, char* buffer, size_t size);
|
||||||
|
|
||||||
|
struct value* value_try_new_and(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_or(struct value* self, struct value* rhs);
|
||||||
|
struct value* value_try_new_not(struct value* self);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <vec.h>
|
||||||
|
#include <node.h>
|
||||||
|
#include <lexer.h>
|
||||||
|
|
||||||
|
static void test_lexer(char const* source, size_t size, ...)
|
||||||
|
{
|
||||||
|
va_list lst;
|
||||||
|
va_start(lst, size);
|
||||||
|
|
||||||
|
struct vec tokens;
|
||||||
|
vec_init(&tokens, 1);
|
||||||
|
|
||||||
|
struct lexer lex;
|
||||||
|
lexer_init(&lex, source);
|
||||||
|
int err = lexer_extract(&lex, &tokens);
|
||||||
|
cr_assert_eq(0, err);
|
||||||
|
cr_assert_eq(size, tokens.size,
|
||||||
|
"size (%zu) != tokens.size (%zu)",
|
||||||
|
size, tokens.size);
|
||||||
|
|
||||||
|
for (size_t i=0; i<size; i++)
|
||||||
|
{
|
||||||
|
size_t SZ = 256;
|
||||||
|
char buf[SZ];
|
||||||
|
node_str(tokens.data[i], buf, SZ);
|
||||||
|
cr_assert_str_eq(va_arg(lst, char*), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(lst);
|
||||||
|
|
||||||
|
lexer_free(&lex);
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, builtins) {
|
||||||
|
test_lexer("true false;", 3,
|
||||||
|
"BOOL[true]",
|
||||||
|
"BOOL[false]",
|
||||||
|
"SEMICOLON");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, error) {
|
||||||
|
struct vec tokens;
|
||||||
|
vec_init(&tokens, 1);
|
||||||
|
|
||||||
|
struct lexer lex;
|
||||||
|
lexer_init(&lex, " true § false;");
|
||||||
|
|
||||||
|
int res = lexer_extract(&lex, &tokens);
|
||||||
|
cr_assert_neq(0, res);
|
||||||
|
|
||||||
|
lexer_free(&lex);
|
||||||
|
vec_free_elements(&tokens);
|
||||||
|
vec_free(&tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, assert) {
|
||||||
|
test_lexer("assert", 1, "ASSERT");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Test(lexer, comment) {
|
||||||
|
test_lexer("assert # salut \n assert", 2, "ASSERT", "ASSERT");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, bool_arith) {
|
||||||
|
test_lexer("&&||!", 3, "AND", "OR", "NOT");
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
Test(node, string) {
|
||||||
|
size_t const SZ = 256;
|
||||||
|
char str[SZ];
|
||||||
|
|
||||||
|
struct node node;
|
||||||
|
node_init(&node, NODE_ROOT, "", 1);
|
||||||
|
node_add_new_child(&node, NODE_ROOT, "hello");
|
||||||
|
node_add_new_child(&node, NODE_ROOT, "");
|
||||||
|
|
||||||
|
|
||||||
|
node_str(&node, str, SZ);
|
||||||
|
cr_assert_str_eq(str, "ROOT(ROOT[hello],ROOT)");
|
||||||
|
node_free(&node);
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include "commons.h"
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <parser.h>
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
static void test_parser(char const* oracle, char const* source)
|
||||||
|
{
|
||||||
|
struct parser parser;
|
||||||
|
parser_init(&parser);
|
||||||
|
|
||||||
|
struct node* ast = parser_try_new_root(&parser, source);
|
||||||
|
cr_assert_neq(NULL, ast, "%s", parser.error_msg);
|
||||||
|
|
||||||
|
{
|
||||||
|
char buf[GUX_STR_SIZE];
|
||||||
|
node_str(ast, buf, GUX_STR_SIZE);
|
||||||
|
cr_assert_str_eq(oracle, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_free(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_parser_fail(char const* source)
|
||||||
|
{
|
||||||
|
struct parser parser;
|
||||||
|
parser_init(&parser);
|
||||||
|
|
||||||
|
struct node* ast = parser_try_new_root(&parser, source);
|
||||||
|
|
||||||
|
if (ast)
|
||||||
|
{
|
||||||
|
char msg[GUX_STR_SIZE];
|
||||||
|
node_str(ast, msg, GUX_STR_SIZE);
|
||||||
|
cr_assert_eq(NULL, ast, "ast = %s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
cr_assert_eq(NULL, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, boolean) {
|
||||||
|
test_parser_fail("true");
|
||||||
|
test_parser_fail(";");
|
||||||
|
test_parser("ROOT(BOOL[true])", "true;");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, assert) {
|
||||||
|
test_parser("ROOT(ASSERT(BOOL[false]))",
|
||||||
|
"assert false;");
|
||||||
|
|
||||||
|
test_parser_fail("assert;");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, bool_arith) {
|
||||||
|
test_parser("ROOT(NOT(NOT(BOOL[true])))",
|
||||||
|
"!!true;");
|
||||||
|
|
||||||
|
test_parser("ROOT(OR(OR(BOOL[true],BOOL[false]),BOOL[false]))",
|
||||||
|
"true || false || false;");
|
||||||
|
|
||||||
|
test_parser("ROOT(OR(AND(BOOL[true],BOOL[false]),BOOL[true]))",
|
||||||
|
"true && false || true;");
|
||||||
|
|
||||||
|
test_parser("ROOT(AND(BOOL[true],OR(BOOL[false],BOOL[true])))",
|
||||||
|
"true && (false || true);");
|
||||||
|
|
||||||
|
test_parser("ROOT(OR(AND(BOOL[true],NOT(BOOL[false])),BOOL[true]))",
|
||||||
|
"true && !false || true;");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
|
||||||
|
Test(trivial, test) {
|
||||||
|
cr_assert(1 + 1 == 2);
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include "commons.h"
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <type.h>
|
||||||
|
|
||||||
|
Test(type, atomic_type) {
|
||||||
|
struct type ty;
|
||||||
|
type_init(&ty, TYPE_BOOL);
|
||||||
|
|
||||||
|
char buf[GUX_STR_SIZE];
|
||||||
|
type_str(&ty, buf, GUX_STR_SIZE);
|
||||||
|
|
||||||
|
cr_assert_str_eq(buf, "BOOL");
|
||||||
|
|
||||||
|
type_free(&ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(type, compound_type) {
|
||||||
|
struct type ty;
|
||||||
|
type_init(&ty, TYPE_BOOL);
|
||||||
|
type_add_subtype(&ty, TYPE_VOID);
|
||||||
|
type_add_subtype(&ty, TYPE_BOOL);
|
||||||
|
|
||||||
|
char buf[GUX_STR_SIZE];
|
||||||
|
type_str(&ty, buf, GUX_STR_SIZE);
|
||||||
|
|
||||||
|
cr_assert_str_eq(buf, "BOOL(VOID,BOOL)");
|
||||||
|
|
||||||
|
type_free(&ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(type, equals) {
|
||||||
|
struct type lhs;
|
||||||
|
type_init(&lhs, TYPE_BOOL);
|
||||||
|
type_add_subtype(&lhs, TYPE_VOID);
|
||||||
|
type_add_subtype(&lhs, TYPE_BOOL);
|
||||||
|
|
||||||
|
struct type rhs;
|
||||||
|
type_init(&rhs, TYPE_BOOL);
|
||||||
|
type_add_subtype(&rhs, TYPE_VOID);
|
||||||
|
type_add_subtype(&rhs, TYPE_BOOL);
|
||||||
|
|
||||||
|
cr_assert(type_equals(&lhs, &rhs));
|
||||||
|
|
||||||
|
type_free(&rhs);
|
||||||
|
type_free(&lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(type, no_equals) {
|
||||||
|
struct type lhs;
|
||||||
|
type_init(&lhs, TYPE_BOOL);
|
||||||
|
type_add_subtype(&lhs, TYPE_VOID);
|
||||||
|
type_add_subtype(&lhs, TYPE_BOOL);
|
||||||
|
|
||||||
|
struct type rhs;
|
||||||
|
type_init(&rhs, TYPE_BOOL);
|
||||||
|
type_add_subtype(&rhs, TYPE_BOOL);
|
||||||
|
type_add_subtype(&rhs, TYPE_BOOL);
|
||||||
|
|
||||||
|
cr_assert(!type_equals(&lhs, &rhs));
|
||||||
|
|
||||||
|
type_free(&rhs);
|
||||||
|
type_free(&lhs);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <parser.h>
|
||||||
|
#include <parser.h>
|
||||||
|
#include <type_checker.h>
|
||||||
|
|
||||||
|
static void test_check_ok(char const* source)
|
||||||
|
{
|
||||||
|
struct parser parser;
|
||||||
|
parser_init(&parser);
|
||||||
|
|
||||||
|
struct node* ast = parser_try_new_root(&parser, source);
|
||||||
|
cr_assert_neq(ast, NULL);
|
||||||
|
|
||||||
|
struct type_checker tc;
|
||||||
|
type_checker_init(&tc);
|
||||||
|
|
||||||
|
cr_assert_eq(type_checker_check(&tc, ast), 0);
|
||||||
|
|
||||||
|
type_checker_free(&tc);
|
||||||
|
|
||||||
|
node_free(ast);
|
||||||
|
free(ast);
|
||||||
|
parser_free(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_check_ko(char const* source)
|
||||||
|
{
|
||||||
|
struct parser parser;
|
||||||
|
parser_init(&parser);
|
||||||
|
|
||||||
|
struct node* ast = parser_try_new_root(&parser, source);
|
||||||
|
cr_assert_neq(ast, NULL);
|
||||||
|
|
||||||
|
struct type_checker tc;
|
||||||
|
type_checker_init(&tc);
|
||||||
|
|
||||||
|
cr_assert_neq(type_checker_check(&tc, ast), 0);
|
||||||
|
|
||||||
|
type_checker_free(&tc);
|
||||||
|
|
||||||
|
node_free(ast);
|
||||||
|
free(ast);
|
||||||
|
parser_free(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(type_checker, booleans) {
|
||||||
|
test_check_ok("true;");
|
||||||
|
test_check_ok("true && false;");
|
||||||
|
test_check_ok("true || false;");
|
||||||
|
test_check_ok("true || !false && false;");
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <value.h>
|
||||||
|
|
||||||
|
static void test_bool_ops(int lhs_val, int rhs_val,
|
||||||
|
struct value* (*op)(struct value*, struct value*),
|
||||||
|
int oracle)
|
||||||
|
{
|
||||||
|
struct value lhs;
|
||||||
|
value_init_bool(&lhs, lhs_val, 0);
|
||||||
|
|
||||||
|
struct value rhs;
|
||||||
|
value_init_bool(&rhs, rhs_val, 0);
|
||||||
|
|
||||||
|
struct value* res = op(&lhs, &rhs);
|
||||||
|
cr_assert_neq(res, NULL);
|
||||||
|
cr_assert(type_equals(&lhs.type, &res->type));
|
||||||
|
cr_assert_eq(res->data.b, oracle);
|
||||||
|
|
||||||
|
value_free(&rhs);
|
||||||
|
value_free(&lhs);
|
||||||
|
value_free(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(value, booleans_ops) {
|
||||||
|
test_bool_ops(VAL_TRUE, VAL_TRUE, &value_try_new_and, VAL_TRUE);
|
||||||
|
test_bool_ops(VAL_TRUE, VAL_FALSE, &value_try_new_and, VAL_FALSE);
|
||||||
|
test_bool_ops(VAL_FALSE, VAL_TRUE, &value_try_new_and, VAL_FALSE);
|
||||||
|
test_bool_ops(VAL_FALSE, VAL_FALSE, &value_try_new_and, VAL_FALSE);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(gux-lib LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(gux-lib OBJECT
|
||||||
|
src/commons.h
|
||||||
|
src/vec.h
|
||||||
|
src/vec.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(gux-lib
|
||||||
|
PUBLIC src
|
||||||
|
)
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
|
||||||
|
target_compile_options(gux-lib
|
||||||
|
PUBLIC -Wall -Wextra -g
|
||||||
|
)
|
||||||
|
|
||||||
|
endif()
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef COMMONS_H
|
||||||
|
#define COMMONS_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define GUX_STR_SIZE 256
|
||||||
|
#define GUX_ENUM_IDENT(X) X
|
||||||
|
#define GUX_ENUM_STR(X) #X
|
||||||
|
#define GUX_ENUM_H(Prefix, Types) \
|
||||||
|
enum Prefix { Types(GUX_ENUM_IDENT) }; \
|
||||||
|
extern char* Prefix ## Str[];
|
||||||
|
|
||||||
|
#define GUX_ENUM_C(Prefix, Types) \
|
||||||
|
char* Prefix ## Str[] = { Types(GUX_ENUM_STR) };
|
||||||
|
|
||||||
|
#define GUX_RET_ERROR 1
|
||||||
|
#define GUX_RET_ASSERT 2
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
void vec_init(struct vec* self, size_t capacity)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(capacity > 0);
|
||||||
|
|
||||||
|
self->capacity = capacity;
|
||||||
|
self->size = 0;
|
||||||
|
|
||||||
|
self->data = malloc(self->capacity * sizeof(void*));
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_free(struct vec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
free(self->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_push(struct vec* self, void* element)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(element);
|
||||||
|
|
||||||
|
if (self->size >= self->capacity)
|
||||||
|
{
|
||||||
|
self->capacity *= 2;
|
||||||
|
self->data = realloc(self->data, self->capacity * sizeof(void*));
|
||||||
|
}
|
||||||
|
|
||||||
|
self->data[self->size] = element;
|
||||||
|
self->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* vec_pop(struct vec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(self->size > 0);
|
||||||
|
|
||||||
|
void* element = self->data[self->size - 1];
|
||||||
|
self->size--;
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_clear(struct vec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
while (self->size > 0)
|
||||||
|
{
|
||||||
|
vec_pop(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vec_free_elements(struct vec* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->size; i++)
|
||||||
|
{
|
||||||
|
free(self->data[i]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef VEC_H
|
||||||
|
#define VEC_H
|
||||||
|
|
||||||
|
#include <commons.h>
|
||||||
|
|
||||||
|
struct vec {
|
||||||
|
void** data;
|
||||||
|
size_t capacity;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
void vec_init(struct vec* self, size_t capacity);
|
||||||
|
void vec_free(struct vec* self);
|
||||||
|
|
||||||
|
void vec_push(struct vec* self, void* element);
|
||||||
|
void* vec_pop(struct vec* self);
|
||||||
|
void vec_clear(struct vec* self);
|
||||||
|
|
||||||
|
void vec_free_elements(struct vec* self);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
assert true;
|
||||||
|
|
||||||
|
assert !false;
|
||||||
|
assert !!true;
|
||||||
|
|
||||||
|
assert true && true;
|
||||||
|
assert !(true && false);
|
||||||
|
assert !(false && true);
|
||||||
|
assert !(false && false);
|
||||||
|
|
||||||
|
assert true || true;
|
||||||
|
assert true || false;
|
||||||
|
assert false || true;
|
||||||
|
assert !(false || false);
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/env bash
|
||||||
|
|
||||||
|
SUCCESS=0
|
||||||
|
TOTAL=0
|
||||||
|
|
||||||
|
for file in $(find . -name "*.gux" | sort)
|
||||||
|
do
|
||||||
|
MSG=$(guxi $file 2>&1)
|
||||||
|
RET=$?
|
||||||
|
|
||||||
|
echo -en "$file...\t"
|
||||||
|
if [ $RET -eq 0 ]
|
||||||
|
then
|
||||||
|
echo -e "\e[32mpass\e[0m"
|
||||||
|
SUCCESS=$(($SUCCESS + 1))
|
||||||
|
else
|
||||||
|
echo -e "\e[31mfail\e[0m"
|
||||||
|
echo -e "\e[33m$MSG\e[0m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL=$(($TOTAL + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ $SUCCESS -eq $TOTAL ]
|
||||||
|
then
|
||||||
|
echo -e "\e[32m--- All tests passed ---\e[0m"
|
||||||
|
else
|
||||||
|
FAIL=$(($TOTAL - $SUCCESS))
|
||||||
|
echo -e "\e[31m... $FAIL tests failed ...\e[0m"
|
||||||
|
fi
|
|
@ -0,0 +1,21 @@
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(gux-vm LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(gux-vm OBJECT
|
||||||
|
src/vm.h
|
||||||
|
src/vm.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET gux-vm PROPERTY C_STANDARD 99)
|
||||||
|
|
||||||
|
add_dependencies(gux-vm gux-bc)
|
||||||
|
|
||||||
|
target_include_directories(gux-vm
|
||||||
|
PUBLIC gux-bc
|
||||||
|
PUBLIC ${CMAKE_SOURCE_DIR}/vm/src
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(gux-vm
|
||||||
|
PUBLIC gux-bc
|
||||||
|
)
|
|
@ -0,0 +1,163 @@
|
||||||
|
#include "vm.h"
|
||||||
|
#include "commons.h"
|
||||||
|
|
||||||
|
void vm_init(struct vm* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->pc = 0;
|
||||||
|
self->fp = 0;
|
||||||
|
|
||||||
|
vm_add_frame(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_free(struct vm* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
size_t fp = self->fp;
|
||||||
|
|
||||||
|
for (size_t i=0; i<fp; i++)
|
||||||
|
{
|
||||||
|
vm_remove_frame(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->fp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_add_frame(struct vm* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
self->stack[self->fp] = malloc(sizeof(struct frame));
|
||||||
|
self->stack[self->fp]->sp = 0;
|
||||||
|
self->fp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_remove_frame(struct vm* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(self->fp > 0);
|
||||||
|
|
||||||
|
free(self->stack[self->fp - 1]);
|
||||||
|
self->fp--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_push(struct vm* self, int param)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct frame* frame = self->stack[self->fp - 1];
|
||||||
|
frame->stack[frame->sp] = param;
|
||||||
|
frame->sp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vm_pop(struct vm* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
struct frame* frame = self->stack[self->fp - 1];
|
||||||
|
assert(frame->sp > 0);
|
||||||
|
int param = frame->stack[frame->sp - 1];
|
||||||
|
frame->sp--;
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vm_exec(struct vm* self, struct program* program)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(program);
|
||||||
|
|
||||||
|
while (self->pc < program->instructions.size)
|
||||||
|
{
|
||||||
|
struct instruction* instr = program->instructions.data[self->pc];
|
||||||
|
enum Opcodes opcode = instr->opcode;
|
||||||
|
int param = instr->param;
|
||||||
|
struct frame* frame = self->stack[self->fp - 1];
|
||||||
|
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case OP_PUSH: {
|
||||||
|
vm_push(self, param);
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_POP: {
|
||||||
|
frame->sp--;
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_BRF: {
|
||||||
|
int val_id = vm_pop(self);
|
||||||
|
struct value* value = program->constant_pool.data[val_id];
|
||||||
|
|
||||||
|
if (value->data.b == 0)
|
||||||
|
{
|
||||||
|
self->pc = param;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->pc++;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_BRT: {
|
||||||
|
int val_id = vm_pop(self);
|
||||||
|
struct value* value = program->constant_pool.data[val_id];
|
||||||
|
|
||||||
|
if (value->data.b != 0)
|
||||||
|
{
|
||||||
|
self->pc = param;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->pc++;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_BR: {
|
||||||
|
self->pc = param;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_HALT: {
|
||||||
|
int code = vm_pop(self);
|
||||||
|
snprintf(self->error_msg, GUX_STR_SIZE, "program halt");
|
||||||
|
self->error_line = param;
|
||||||
|
|
||||||
|
return code;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_NOP: {
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_NOT: {
|
||||||
|
int addr = vm_pop(self);
|
||||||
|
struct value* val = program->constant_pool.data[addr];
|
||||||
|
struct value* not = value_try_new_not(val);
|
||||||
|
vm_push(self, program_push_constant(program, not));
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t vm_str(struct vm* self, char* buffer, size_t size)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(buffer);
|
||||||
|
|
||||||
|
size_t sz = 0;
|
||||||
|
|
||||||
|
for (size_t i=0; i<self->fp; i++)
|
||||||
|
{
|
||||||
|
struct frame* frame = self->stack[i];
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "[FRAME %zu]\n", i);
|
||||||
|
|
||||||
|
for (size_t j=0; j<frame->sp; j++)
|
||||||
|
{
|
||||||
|
sz += snprintf(buffer + sz, size - sz, "\t%zu: %d\n",
|
||||||
|
j, frame->stack[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sz;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef VM_H
|
||||||
|
#define VM_H
|
||||||
|
#include <commons.h>
|
||||||
|
#include <program.h>
|
||||||
|
#include <vec.h>
|
||||||
|
|
||||||
|
#define FRAME_DEPTH 256
|
||||||
|
#define STACK_DEPTH 256
|
||||||
|
|
||||||
|
struct frame {
|
||||||
|
int stack[STACK_DEPTH];
|
||||||
|
size_t sp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vm {
|
||||||
|
char error_msg[GUX_STR_SIZE];
|
||||||
|
int error_line;
|
||||||
|
size_t pc;
|
||||||
|
size_t fp;
|
||||||
|
struct frame* stack[FRAME_DEPTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
void vm_init(struct vm* self);
|
||||||
|
void vm_free(struct vm* self);
|
||||||
|
void vm_add_frame(struct vm* self);
|
||||||
|
void vm_remove_frame(struct vm* self);
|
||||||
|
|
||||||
|
void vm_push(struct vm* self, int param);
|
||||||
|
int vm_pop(struct vm* self);
|
||||||
|
|
||||||
|
int vm_exec(struct vm* self, struct program* program);
|
||||||
|
|
||||||
|
size_t vm_str(struct vm* self, char* buffer, size_t size);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue