booleans.

main
bog 2024-02-10 16:16:00 +01:00
parent 1f154374a8
commit 5a67acd796
44 changed files with 2595 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*~*
*\#*
build
.cache

9
CMakeLists.txt Normal file
View File

@ -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)

18
Makefile Normal file
View File

@ -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

22
bc/CMakeLists.txt Normal file
View File

@ -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
)

171
bc/src/compiler.c Normal file
View File

@ -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;
}

20
bc/src/compiler.h Normal file
View File

@ -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

3
bc/src/opcodes.c Normal file
View File

@ -0,0 +1,3 @@
#include "opcodes.h"
GUX_ENUM_C(Opcodes, OPCODES);

12
bc/src/opcodes.h Normal file
View File

@ -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

77
bc/src/program.c Normal file
View File

@ -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;
}

29
bc/src/program.h Normal file
View File

@ -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

15
doc/gux.bnf Normal file
View File

@ -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

24
guxi/CMakeLists.txt Normal file
View File

@ -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)

309
guxi/src/main.c Normal file
View File

@ -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;
}

59
lang/CMakeLists.txt Normal file
View File

@ -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
)

238
lang/src/lexer.c Normal file
View File

@ -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);
}

51
lang/src/lexer.h Normal file
View File

@ -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

83
lang/src/node.c Normal file
View File

@ -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;
}

33
lang/src/node.h Normal file
View File

@ -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

291
lang/src/parser.c Normal file
View File

@ -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;
}

36
lang/src/parser.h Normal file
View File

@ -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

91
lang/src/type.c Normal file
View File

@ -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;
}

27
lang/src/type.h Normal file
View File

@ -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

80
lang/src/type_checker.c Normal file
View File

@ -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;
}

25
lang/src/type_checker.h Normal file
View File

@ -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

40
lang/src/type_resolver.c Normal file
View File

@ -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;
}

19
lang/src/type_resolver.h Normal file
View File

@ -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

81
lang/src/value.c Normal file
View File

@ -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;
}

29
lang/src/value.h Normal file
View File

@ -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

70
lang/tests/lexer.c Normal file
View File

@ -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");
}

17
lang/tests/node.c Normal file
View File

@ -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);
}

69
lang/tests/parser.c Normal file
View File

@ -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;");
}

5
lang/tests/trivial.c Normal file
View File

@ -0,0 +1,5 @@
#include <criterion/criterion.h>
Test(trivial, test) {
cr_assert(1 + 1 == 2);
}

63
lang/tests/type.c Normal file
View File

@ -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);
}

51
lang/tests/type_checker.c Normal file
View File

@ -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;");
}

29
lang/tests/value.c Normal file
View File

@ -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);
}

21
lib/CMakeLists.txt Normal file
View File

@ -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()

23
lib/src/commons.h Normal file
View File

@ -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

65
lib/src/vec.c Normal file
View File

@ -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]);
}
}

21
lib/src/vec.h Normal file
View File

@ -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

14
tests/bool.gux Normal file
View File

@ -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);

32
tests/run.sh Executable file
View File

@ -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

21
vm/CMakeLists.txt Normal file
View File

@ -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
)

163
vm/src/vm.c Normal file
View File

@ -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;
}

35
vm/src/vm.h Normal file
View File

@ -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