From 6592f2cfe592058ab1bc1a59e2d9a03b6eb0ecc6 Mon Sep 17 00:00:00 2001 From: bog Date: Wed, 23 Aug 2023 14:40:43 +0200 Subject: [PATCH] ADD: booleans. --- .gitignore | 4 + Makefile | 8 ++ doc/Makefile | 2 + doc/conf.py | 2 + doc/index.rst | 9 ++ doc/installation.rst | 33 +++++++ meson.build | 46 ++++++++++ src/commons.h | 11 +++ src/compiler.c | 76 ++++++++++++++++ src/compiler.h | 18 ++++ src/lex.l | 38 ++++++++ src/main.c | 70 +++++++++++++++ src/mutils.h | 7 ++ src/node.c | 96 +++++++++++++++++++++ src/node.h | 39 +++++++++ src/opcodes.c | 5 ++ src/opcodes.h | 17 ++++ src/parser.y | 163 +++++++++++++++++++++++++++++++++++ src/program.c | 133 ++++++++++++++++++++++++++++ src/program.h | 37 ++++++++ src/type.c | 39 +++++++++ src/type.h | 26 ++++++ src/utils.c | 12 +++ src/utils.h | 7 ++ src/value.c | 73 ++++++++++++++++ src/value.h | 23 +++++ src/vm.c | 200 +++++++++++++++++++++++++++++++++++++++++++ src/vm.h | 37 ++++++++ tests/booleans.wuz | 39 +++++++++ tests/run.sh | 31 +++++++ 30 files changed, 1301 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 doc/Makefile create mode 100644 doc/conf.py create mode 100644 doc/index.rst create mode 100644 doc/installation.rst create mode 100644 meson.build create mode 100644 src/commons.h create mode 100644 src/compiler.c create mode 100644 src/compiler.h create mode 100644 src/lex.l create mode 100644 src/main.c create mode 100644 src/mutils.h create mode 100644 src/node.c create mode 100644 src/node.h create mode 100644 src/opcodes.c create mode 100644 src/opcodes.h create mode 100644 src/parser.y create mode 100644 src/program.c create mode 100644 src/program.h create mode 100644 src/type.c create mode 100644 src/type.h create mode 100644 src/utils.c create mode 100644 src/utils.h create mode 100644 src/value.c create mode 100644 src/value.h create mode 100644 src/vm.c create mode 100644 src/vm.h create mode 100644 tests/booleans.wuz create mode 100755 tests/run.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95bd0b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~* +*\#* +build +doc/build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..afe0586 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.PHONY: build install tests + +build: + meson setup build + meson compile -C build + +install: build + meson install -C build diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..28e3a8f --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,2 @@ +all: + sphinx-build . build diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..e960cff --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,2 @@ +project = 'WuZ' +html_theme = 'press' diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..be8131e --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,9 @@ +WuZ Programming Language +======================== + +Table of Contents +----------------- + +.. toctree:: + installation + diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 0000000..eed160f --- /dev/null +++ b/doc/installation.rst @@ -0,0 +1,33 @@ +How to install WuZ +================== + +Let's install WuZ ! +For now, the only way is to compile it by yourself. + +Compiling WuZ using Makefile (aka the easy way) +----------------------------------------------- + +You can compile the project using the Makefile +at the repository root. +Then you can install WuZ using the ``make install`` command. +You may need to be root, if so, just do ``sudo make install`` instead. + +.. code-block:: bash + + make + make install + +Et voila ! You are ready to use WuZ. + +Compiling WuZ using Meson +--------------------------- +If you prefere not to use the provided Makefile, +you can use meson directly. + +.. code-block:: bash + + meson setup build + meson compile -C build + meson install -C build + + diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..8c693e5 --- /dev/null +++ b/meson.build @@ -0,0 +1,46 @@ +project( + 'WuZ', + 'c', + version: '0.0.0', + default_options: [ + 'warning_level=3' + ] +) + +flex = find_program('flex') +flex_target = custom_target( + 'flex', + input: ['src/lex.l'], + output: ['lex.yy.c'], + command: [ + flex, '@INPUT@' + ] +) + +bison = find_program('bison') +bison_target = custom_target( + 'bison', + input: ['src/parser.y'], + output: ['parser.c'], + command: [ + bison, '-d', '-o', '@OUTPUT@', '@INPUT@' + ] +) +executable( + 'wuz', + sources: [ + flex_target, + bison_target, + 'src/main.c', + 'src/node.c', + 'src/utils.c', + 'src/opcodes.c', + 'src/program.c', + 'src/value.c', + 'src/type.c', + 'src/compiler.c', + 'src/vm.c', + ], + install: true +) + diff --git a/src/commons.h b/src/commons.h new file mode 100644 index 0000000..32d7a26 --- /dev/null +++ b/src/commons.h @@ -0,0 +1,11 @@ +#ifndef COMMONS_H +#define COMMON_H + +#include +#include +#include +#include + +#include "utils.h" +#include "mutils.h" +#endif diff --git a/src/compiler.c b/src/compiler.c new file mode 100644 index 0000000..d8e0eb8 --- /dev/null +++ b/src/compiler.c @@ -0,0 +1,76 @@ +#include "compiler.h" + +void compiler_init(compiler* self) +{ + assert(self); +} + +void compiler_free(compiler* self) +{ + assert(self); +} + +void compile_node(compiler* self, node* root, program* prog) +{ + assert(self); + assert(root); + assert(prog); + + if (root->type == NODE_BOOLEAN) + { + value val; + value_init_boolean(&val, + strcmp(root->value, "true") == 0, + root->lineno); + size_t idx = program_add_pool(prog, &val); + program_add_instr(prog, OP_PUSH, idx); + value_free(&val); + } + else if (root->type == NODE_ASSERT) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_ASSERT, NO_PARAM); + } + else if (root->type == NODE_EQ) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_EQ, NO_PARAM); + } + else if (root->type == NODE_NE) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_EQ, NO_PARAM); + program_add_instr(prog, OP_NOT, NO_PARAM); + } + else if (root->type == NODE_AND) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_AND, NO_PARAM); + } + else if (root->type == NODE_OR) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_OR, NO_PARAM); + } + else if (root->type == NODE_NOT) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_NOT, NO_PARAM); + } + else + { + compile_children(self, root, prog); + } +} + +void compile_children(compiler* self, node* root, program* prog) +{ + assert(self); + assert(root); + assert(prog); + + for (size_t i=0; ichildren.size; i++) + { + compile_node(self, root->children.data[i], prog); + } +} diff --git a/src/compiler.h b/src/compiler.h new file mode 100644 index 0000000..ab92535 --- /dev/null +++ b/src/compiler.h @@ -0,0 +1,18 @@ +#ifndef COMPILER_H +#define COMPILER_H + +#include "commons.h" +#include "program.h" +#include "node.h" + +typedef struct { + int _unused; +} compiler; + +void compiler_init(compiler* self); +void compiler_free(compiler* self); + +void compile_node(compiler* self, node* root, program* prog); +void compile_children(compiler* self, node* root, program* prog); + +#endif diff --git a/src/lex.l b/src/lex.l new file mode 100644 index 0000000..d466580 --- /dev/null +++ b/src/lex.l @@ -0,0 +1,38 @@ +%{ + #include "parser.h" + #include "src/utils.h" + int line = 1; +%} +%option noyywrap +%option nounput +%option noinput + +COMMENT ::[^\n]* +WHITESPACES [ \t]+ +BOOLEAN true|false + +%% +"\n" { line++; } +{COMMENT} {} +{WHITESPACES} {} + +"assert" { return ASSERT; } +"==" { return EQ; } +"!=" { return NE; } +"&&" { return AND; } +"||" { return OR; } +"!" { return NOT; } +"(" { return OPAR; } +")" { return CPAR; } + +{BOOLEAN} { + yylval.boolean = yytext; + return BOOLEAN; +} +. { + fprintf(stderr, "E(%d): Lexical error near '%s'.\n", line, yytext); + exit(-1); +} +%% + + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7ea39f2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,70 @@ +#include +#include "node.h" +#include "parser.h" +#include "compiler.h" +#include "vm.h" + +extern FILE* yyin; +extern node* ast; +extern void stack_push(node*); +extern node* stack_pop(); +extern void stack_free(); + +int main(int argc, char** argv) +{ + if (argc > 1) + { + yyin = fopen(argv[1], "r"); + + node* n = malloc(sizeof(node)); + node_init(n, NODE_PROG, "", 1); + stack_push(n); + + yyparse(); + + ast = stack_pop(); + stack_free(); + + compiler comp; + compiler_init(&comp); + + program prog; + program_init(&prog); + + compile_node(&comp, ast, &prog); + + vm v; + vm_init(&v); + + vm_exec(&v, &prog); + + if (0) // DEBUG + { + size_t const BUF = 1024; + char buffer[BUF]; + + node_str(ast, buffer, BUF); + printf("-- ast ---\n%s\n\n", buffer); + + program_str(&prog, buffer, BUF); + printf("--- program ---\n%s\n", buffer); + + vm_str(&v, buffer, BUF); + printf("--- stack ---\n%s\n", buffer); + } + + vm_free(&v); + + program_free(&prog); + compiler_free(&comp); + node_free(ast); + + fclose(yyin); + + return 0; + } + + fprintf(stderr, "Usage: wuz \n"); + return -1; +} + diff --git a/src/mutils.h b/src/mutils.h new file mode 100644 index 0000000..aa2f2fb --- /dev/null +++ b/src/mutils.h @@ -0,0 +1,7 @@ +#ifndef MUTILS_H +#define MUTILS_H + +#define GEN_ENUM(X) X +#define GEN_STRING(X) #X + +#endif diff --git a/src/node.c b/src/node.c new file mode 100644 index 0000000..e259de3 --- /dev/null +++ b/src/node.c @@ -0,0 +1,96 @@ +#include "node.h" + + +char const* NodeTypeStr[] = { + NODE_TYPE(GEN_STRING) +}; + +void node_init(node* self, int type, char const* value, int lineno) +{ + assert(self); + self->type = type; + self->lineno = lineno; + + self->value = str_new(value); + self->children.data = NULL; + self->children.size = 0; + self->children.capacity = 0; +} + +void node_free(node* self) +{ + assert(self); + assert(self->value); + free(self->value); + self->value = NULL; + + for (size_t i=0; ichildren.size; i++) + { + node_free(self->children.data[i]); + free(self->children.data[i]); + } + + free(self->children.data); + self->children.data = NULL; + self->children.size = 0; + self->children.capacity = 0; +} + + +void node_add_child(node* self, node* child) +{ + assert(self); + assert(child); + + if (self->children.capacity == 0) + { + self->children.capacity = 1; + self->children.data = malloc(sizeof(node*) + * self->children.capacity); + } + else if (self->children.size >= self->children.capacity) + { + self->children.capacity *= 2; + self->children.data = realloc(self->children.data, + sizeof(node*) + * self->children.capacity); + } + + self->children.data[self->children.size] = child; + self->children.size++; +} + +size_t node_str(node* self, char* buffer, size_t size) +{ + assert(self); + size_t sz = 0; + + char const* ty = NodeTypeStr[self->type] + strlen("NODE_"); + + sz += snprintf(buffer + sz, size - sz, "%s", ty); + + 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; ichildren.size; i++) + { + if (i > 0) + { + sz += snprintf(buffer + sz, size - sz, ","); + } + + sz += node_str(self->children.data[i], + buffer + sz, + size - sz); + } + + sz += snprintf(buffer + sz, size - sz, ")"); + } + + return sz; +} diff --git a/src/node.h b/src/node.h new file mode 100644 index 0000000..830e203 --- /dev/null +++ b/src/node.h @@ -0,0 +1,39 @@ +#ifndef NODE_H +#define NODE_H + +#define NODE_TYPE(G) \ + G(NODE_PROG), \ + G(NODE_BOOLEAN), \ + G(NODE_AND), G(NODE_OR), G(NODE_NOT), \ + G(NODE_EQ), G(NODE_NE), \ + G(NODE_ASSERT) + +#include "mutils.h" +#include "commons.h" + +enum NodeType { + NODE_TYPE(GEN_ENUM) +}; + +extern char const* NodeTypeStr[]; + +typedef struct node { + int type; + char* value; + struct { + size_t capacity; + size_t size; + struct node** data; + } children; + + int lineno; +} node; + +void node_init(node* self, int type, char const* value, int lineno); +void node_free(node* self); + +void node_add_child(node* self, node* child); + +size_t node_str(node* self, char* buffer, size_t size); + +#endif diff --git a/src/opcodes.c b/src/opcodes.c new file mode 100644 index 0000000..ff4a76d --- /dev/null +++ b/src/opcodes.c @@ -0,0 +1,5 @@ +#include "opcodes.h" + +char const* OpcodesStr[] = { + OPCODES(GEN_STRING) +}; diff --git a/src/opcodes.h b/src/opcodes.h new file mode 100644 index 0000000..a38f911 --- /dev/null +++ b/src/opcodes.h @@ -0,0 +1,17 @@ +#ifndef OPCODES_H +#define OPCODES_H + +#include "mutils.h" + +#define OPCODES(G) \ + G(OP_PUSH), \ + G(OP_AND), G(OP_OR), G(OP_NOT), \ + G(OP_EQ), G(OP_ASSERT) + +enum Opcodes { + OPCODES(GEN_ENUM) +}; + +extern char const* OpcodesStr[]; + +#endif diff --git a/src/parser.y b/src/parser.y new file mode 100644 index 0000000..fc89b53 --- /dev/null +++ b/src/parser.y @@ -0,0 +1,163 @@ +%{ + #include + #include + #include "src/node.h" + + extern int line; + void yyerror(char const*); + node* ast = NULL; + + node** stack = NULL; + size_t stack_sz = 0; + size_t stack_cap = 0; + void stack_push(node* n); + node* stack_pop(); + node* stack_top(); + void stack_free(); +%} + +%union { + char* boolean; + void* node_val; +}; + +%left ASSERT +%token BOOLEAN +%left EQ NE +%left AND +%left OR +%left NOT +%type expr; +%token OPAR CPAR + +%% + +prog: exprs { + +} +; + +exprs: + exprs expr { + node* parent = stack_top(); + node_add_child(parent, $2); + } + + | expr { + node* parent = stack_top(); + node_add_child(parent, $1); + } +; + + +expr: + ASSERT expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_ASSERT, "", line); + node_add_child(n, $2); + $$ = n; + } + + + | expr EQ expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_EQ, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr NE expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_NE, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr AND expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_AND, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr OR expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_OR, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | NOT expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_NOT, "", line); + node_add_child(n, $2); + $$ = n; + } + + | OPAR expr CPAR { + $$ = $2; + } + + + | BOOLEAN { + node* n = malloc(sizeof(node)); + node_init(n, NODE_BOOLEAN, $1, line); + $$ = n; + } +; + +%% + +void yyerror(char const* msg) +{ + fprintf(stderr, "E(%d): %s\n", line, msg); + exit(-1); +} + +void stack_push(node* n) +{ + if (stack_cap == 0) + { + stack = malloc(sizeof(node*)); + stack_cap = 1; + } + else if (stack_sz >= stack_cap) + { + stack_cap *= 2; + stack = realloc(stack, sizeof(node*) * stack_cap); + } + + stack[stack_sz] = n; + stack_sz++; +} + +node* stack_pop() +{ + assert(stack_sz > 0); + node* n = stack[stack_sz - 1]; + stack_sz--; + return n; +} + +node* stack_top() +{ + assert(stack_sz > 0); + node* n = stack[stack_sz - 1]; + return n; +} + +void stack_free() +{ + for (size_t i=0; iinstrs.size = 0; + self->instrs.capacity = 0; + self->instrs.data = NULL; + self->pool.size = 0; + self->pool.capacity = 0; + self->pool.data = NULL; +} + +void program_free(program* self) +{ + assert(self); + + for (size_t i=0; iinstrs.size; i++) + { + free(self->instrs.data[i]); + } + + free(self->instrs.data); + self->instrs.data = NULL; + self->instrs.size = 0; + self->instrs.capacity = 0; + + for (size_t i=0; ipool.size; i++) + { + value_free(self->pool.data[i]); + free(self->pool.data[i]); + } + + free(self->pool.data); + self->pool.data = NULL; + self->pool.size = 0; + self->pool.capacity = 0; +} + +void program_add_instr(program* self, int opcode, int param) +{ + assert(self); + + if (self->instrs.capacity == 0) + { + self->instrs.capacity = 1; + self->instrs.data = malloc(sizeof(instr*) + * self->instrs.capacity); + } + else if (self->instrs.size >= self->instrs.capacity) + { + self->instrs.capacity *= 2; + self->instrs.data = realloc( + self->instrs.data, + sizeof(instr) * self->instrs.capacity + ); + } + size_t idx = self->instrs.size; + + self->instrs.data[idx] = malloc(sizeof(instr)); + self->instrs.data[idx]->opcode = opcode; + self->instrs.data[idx]->param = param; + + self->instrs.size++; +} + +size_t program_add_pool(program* self, value* val) +{ + assert(self); + assert(val); + + if (self->pool.capacity == 0) + { + self->pool.capacity = 1; + self->pool.data = malloc(sizeof(value*) + * self->pool.capacity); + } + else if (self->pool.size >= self->pool.capacity) + { + self->pool.capacity *= 2; + self->pool.data = realloc( + self->pool.data, + sizeof(value) * self->pool.capacity + ); + } + size_t idx = self->pool.size; + self->pool.data[idx] = value_new_clone(val); + + self->pool.size++; + + return idx; +} + +size_t program_str(program* self, char* buffer, size_t size) +{ + assert(self); + size_t sz = 0; + + for (size_t i=0; iinstrs.size; i++) + { + int opcode = self->instrs.data[i]->opcode; + int param = self->instrs.data[i]->param; + + if (param != NO_PARAM) + { + sz += snprintf(buffer + sz, size - sz, "%ld\t%s\t%d", + i, + OpcodesStr[opcode] + + strlen("OP_"), + param); + } + else + { + + sz += snprintf(buffer + sz, size - sz, "%ld\t%s\t", + i, + OpcodesStr[opcode] + + strlen("OP_")); + } + + if (opcode == OP_PUSH && param != -1) + { + sz += snprintf(buffer + sz, size - sz, "\t ("); + sz += value_str(self->pool.data[param], + buffer + sz, size - sz); + sz += snprintf(buffer + sz, size - sz, ")"); + } + + sz += snprintf(buffer + sz, size - sz, "\n"); + } + + return sz; +} diff --git a/src/program.h b/src/program.h new file mode 100644 index 0000000..784733f --- /dev/null +++ b/src/program.h @@ -0,0 +1,37 @@ +#ifndef PROGRAM_H +#define PROGRAM_H + +#include "commons.h" +#include "opcodes.h" +#include "value.h" + +#define NO_PARAM (-1) + +typedef struct { + int opcode; + int param; +} instr; + +typedef struct { + struct { + size_t capacity; + size_t size; + instr** data; + } instrs; + + struct { + size_t capacity; + size_t size; + value** data; + } pool; +} program; + +void program_init(program* self); +void program_free(program* self); + +void program_add_instr(program* self, int opcode, int param); +size_t program_add_pool(program* self, value* val); + +size_t program_str(program* self, char* buffer, size_t size); + +#endif diff --git a/src/type.c b/src/type.c new file mode 100644 index 0000000..3b417af --- /dev/null +++ b/src/type.c @@ -0,0 +1,39 @@ +#include "type.h" + + +char const* TypesStr[] = { TYPES(GEN_STRING) }; + +void type_init(type* self, int base_type) +{ + assert(self); + self->base_type = base_type; +} + +void type_free(type* self) +{ + assert(self); +} + +type* type_new_clone(type* self) +{ + assert(self); + + type* clone = malloc(sizeof(type)); + type_init(clone, self->base_type); + + return clone; +} + +int type_equals(type* self, type* rhs) +{ + assert(self); + assert(rhs); + + return self->base_type == rhs->base_type; +} + +size_t type_str(type* self, char* buffer, size_t size) +{ + assert(self); + return snprintf(buffer, size, "%s", TypesStr[self->base_type]); +} diff --git a/src/type.h b/src/type.h new file mode 100644 index 0000000..1e0d595 --- /dev/null +++ b/src/type.h @@ -0,0 +1,26 @@ +#ifndef TYPE_H +#define TYPE + +#include "commons.h" + +#define TYPES(G) \ + G(TY_BOOLEAN) + +enum Types { + TYPES(GEN_ENUM) +}; + +extern char const* TypesStr[]; + +typedef struct type { + int base_type; +} type; + +void type_init(type* self, int base_type); +void type_free(type* self); + +type* type_new_clone(type* self); +int type_equals(type* self, type* rhs); +size_t type_str(type* self, char* buffer, size_t size); + +#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..a996269 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,12 @@ +#include "utils.h" + +char const* str_new(char const* str) +{ + size_t len = strlen(str) + 1; + + char* result = malloc(len); + + memcpy(result, str, len); + + return result; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..b14f765 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,7 @@ +#ifndef UTILS_H +#define UTILS_H +#include + +char const* str_new(char const* str); + +#endif diff --git a/src/value.c b/src/value.c new file mode 100644 index 0000000..69fe260 --- /dev/null +++ b/src/value.c @@ -0,0 +1,73 @@ +#include "value.h" + +void value_init_boolean(value* self, int boolean, int lineno) +{ + assert(self); + self->val.boolean = boolean; + self->lineno = lineno; + + self->type = malloc(sizeof(type)); + type_init(self->type, TY_BOOLEAN); +} + +void value_free(value* self) +{ + assert(self); + type_free(self->type); + free(self->type); + self->type = NULL; +} + +value* value_new_clone(value* self) +{ + assert(self); + value* clone = malloc(sizeof(value)); + clone->type = type_new_clone(self->type); + clone->val = self->val; + clone->lineno = self->lineno; + + return clone; +} + + +int value_equals(value* self, value* rhs) +{ + assert(self); + assert(rhs); + + if (!type_equals(self->type, rhs->type)) + { + return 0; + } + + if (self->type->base_type == TY_BOOLEAN) + { + return self->val.boolean == rhs->val.boolean; + } + + size_t const SZ = 512; + char ty_str[SZ]; + + type_str(self->type, ty_str, SZ); + + fprintf(stderr, "E: cannot test value equality: unknown type '%s'\n", + ty_str); + exit(-1); +} + +size_t value_str(value* self, char* buffer, size_t size) +{ + assert(self); + + switch (self->type->base_type) + { + case TY_BOOLEAN: + return snprintf(buffer, size, "%s", + self->val.boolean == 0 + ? "false" : "true"); + default: { + fprintf(stderr, "E: unknown value"); + exit(-1); + } + } +} diff --git a/src/value.h b/src/value.h new file mode 100644 index 0000000..2a92da1 --- /dev/null +++ b/src/value.h @@ -0,0 +1,23 @@ +#ifndef VALUE_H +#define VALUE_H + +#include "commons.h" +#include "type.h" + +typedef struct { + type* type; + int lineno; + union { + int boolean; + } val; +} value; + +void value_init_boolean(value* self, int boolean, int lineno); +void value_free(value* self); + +value* value_new_clone(value* self); +int value_equals(value* self, value* rhs); + +size_t value_str(value* self, char* buffer, size_t size); + +#endif diff --git a/src/vm.c b/src/vm.c new file mode 100644 index 0000000..b52312b --- /dev/null +++ b/src/vm.c @@ -0,0 +1,200 @@ +#include "vm.h" + +void vm_init(vm* self) +{ + assert(self); + + self->stack.size = 0; + self->stack.capacity = 1; + self->stack.data = malloc(sizeof(value*) * self->stack.capacity); + + self->prog = NULL; + self->pc = 0; +} + +void vm_free(vm* self) +{ + assert(self); + for (size_t i=0; istack.size; i++) + { + value_free(self->stack.data[i]); + free(self->stack.data[i]); + } + + free(self->stack.data); + + self->stack.size = 0; + self->stack.capacity = 0; + self->stack.data = NULL; +} + +void vm_exec(vm* self, program* prog) +{ + assert(self); + assert(prog); + self->prog = prog; + + while (self->pc < prog->instrs.size) + { + int opcode = prog->instrs.data[self->pc]->opcode; + int param = prog->instrs.data[self->pc]->param; + + switch (opcode) + { + case OP_PUSH: vm_push(self, param); break; + case OP_AND: vm_and(self); break; + case OP_OR: vm_or(self); break; + case OP_NOT: vm_not(self); break; + case OP_EQ: vm_eq(self); break; + case OP_ASSERT: vm_assert(self); break; + default: { + fprintf(stderr, "unknown opcode %s\n", + OpcodesStr[opcode]); + exit(-1); + } + } + + } +} + +void vm_push_value(vm* self, value* val) +{ + assert(self); + + if (self->stack.size >= self->stack.capacity) + { + self->stack.capacity *= 2; + self->stack.data = realloc( + self->stack.data, + sizeof(value*) * self->stack.capacity + ); + } + + self->stack.data[self->stack.size] = val; + self->stack.size++; +} + +value* vm_pop_value(vm* self) +{ + assert(self); + assert(self->stack.size > 0); + + value* val = self->stack.data[self->stack.size - 1]; + + self->stack.size--; + + return val; +} + +size_t vm_str(vm* self, char* buffer, size_t size) +{ + assert(self); + size_t sz = 0; + + for (size_t i=0; istack.size; i++) + { + sz += snprintf(buffer + sz, size - sz, + "%ld\t", i); + + + sz += value_str(self->stack.data[i], buffer + sz, size - sz); + + sz += snprintf(buffer + sz, size - sz, + "\n"); + } + + return sz; +} + +void vm_push(vm* self, int param) +{ + assert(self); + assert(param >= 0); + + value* val = self->prog->pool.data[param]; + vm_push_value(self, value_new_clone(val)); + + self->pc++; +} + +void vm_and(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_boolean(val, lhs->val.boolean && rhs->val.boolean, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_or(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_boolean(val, lhs->val.boolean || rhs->val.boolean, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_not(vm* self) +{ + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_boolean(val, !lhs->val.boolean, lhs->lineno); + vm_push_value(self, val); + + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_eq(vm* self) +{ + assert(self); + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_boolean(val, value_equals(lhs, rhs), lhs->lineno); + + vm_push_value(self, val); + + value_free(lhs); free(lhs); + value_free(rhs); free(rhs); + + self->pc++; +} + +void vm_assert(vm* self) +{ + assert(self); + value* lhs = vm_pop_value(self); + + if (!lhs->val.boolean) + { + fprintf(stderr, "E(%d): assertion failed\n", lhs->lineno); + exit(-1); + } + + value_free(lhs); free(lhs); + + self->pc++; +} + diff --git a/src/vm.h b/src/vm.h new file mode 100644 index 0000000..45caa1a --- /dev/null +++ b/src/vm.h @@ -0,0 +1,37 @@ +#ifndef VM_H +#define VM_H + +#include "commons.h" +#include "value.h" +#include "program.h" + +typedef struct { + struct { + size_t capacity; + size_t size; + value** data; + } stack; + + size_t pc; + program* prog; +} vm; + +void vm_init(vm* self); +void vm_free(vm* self); + +void vm_exec(vm* self, program* prog); +void vm_push_value(vm* self, value* val); +value* vm_pop_value(vm* self); +size_t vm_str(vm* self, char* buffer, size_t size); + +void vm_push(vm* self, int param); +void vm_and(vm* self); +void vm_or(vm* self); +void vm_not(vm* self); + +void vm_eq(vm* self); +void vm_assert(vm* self); + + + +#endif diff --git a/tests/booleans.wuz b/tests/booleans.wuz new file mode 100644 index 0000000..026e151 --- /dev/null +++ b/tests/booleans.wuz @@ -0,0 +1,39 @@ +:: not operator +assert true +assert !false +assert !!true + +:: equal operator +:: ============== + +:: and operator +assert true == true && true +assert false == true && false +assert false == false && true +assert false == false && false + +:: or operator +assert true == true || true +assert true == true || false +assert true == false || true +assert false == false || false + +:: not equal operator +:: ================== + +:: and operator +assert false != true && true +assert true != true && false +assert true != false && true +assert true != false && false + +:: or operator +assert false != true || true +assert false != true || false +assert false != false || true +assert true != false || false + +:: groups +assert !(true && false) == false || true +assert !(true || false) == false && true + diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..f3d7a3a --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +OK=0 +KO=0 + +for file in $(find . -name "*.wuz") +do + wuz $file + RES="$?" + + echo -n "$file ... " + + if [ "$RES" == "0" ] + then + echo -e "\e[32mok\e[0m" + OK=$(($OK + 1)) + else + echo -e "\e[31mko\e[0m" + KO=$(($KO+1)) + fi +done + +TOTAL=$(($OK+$KO)) + +if [ $KO -eq 0 ] +then + echo -e "\e[32mAll tests passed [$TOTAL]\e[0m" +else + echo -e "\e[31m$KO tests failed [$TOTAL]\e[0m" +fi +