diff --git a/meson.build b/meson.build index 8c693e5..c499225 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,10 @@ bison_target = custom_target( bison, '-d', '-o', '@OUTPUT@', '@INPUT@' ] ) + +cc = meson.get_compiler('c') +m_dep = cc.find_library('m') + executable( 'wuz', sources: [ @@ -39,8 +43,12 @@ executable( 'src/value.c', 'src/type.c', 'src/compiler.c', + 'src/cstatic.c', 'src/vm.c', ], + dependencies: [ + m_dep + ], install: true ) diff --git a/src/commons.h b/src/commons.h index 32d7a26..887001c 100644 --- a/src/commons.h +++ b/src/commons.h @@ -1,6 +1,7 @@ #ifndef COMMONS_H #define COMMON_H +#include #include #include #include diff --git a/src/compiler.c b/src/compiler.c index d8e0eb8..80b70d4 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -26,11 +26,61 @@ void compile_node(compiler* self, node* root, program* prog) program_add_instr(prog, OP_PUSH, idx); value_free(&val); } + else if (root->type == NODE_INTEGER) + { + value val; + value_init_integer(&val, + atoi(root->value), + 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_UADD) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IUADD, NO_PARAM); + } + else if (root->type == NODE_USUB) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IUSUB, NO_PARAM); + } + else if (root->type == NODE_ADD) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IADD, NO_PARAM); + } + else if (root->type == NODE_SUB) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_ISUB, NO_PARAM); + } + else if (root->type == NODE_MUL) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IMUL, NO_PARAM); + } + else if (root->type == NODE_DIV) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IDIV, NO_PARAM); + } + else if (root->type == NODE_MOD) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IMOD, NO_PARAM); + } + else if (root->type == NODE_POW) + { + compile_children(self, root, prog); + program_add_instr(prog, OP_IPOW, NO_PARAM); + } else if (root->type == NODE_EQ) { compile_children(self, root, prog); diff --git a/src/cstatic.c b/src/cstatic.c new file mode 100644 index 0000000..6627c4e --- /dev/null +++ b/src/cstatic.c @@ -0,0 +1,210 @@ +#include "cstatic.h" +#include "type.h" + +void cstatic_init(cstatic* self) +{ + assert(self); +} + +void cstatic_free(cstatic* self) +{ + assert(self); +} + +int cstatic_resolve(cstatic* self, node* ast) +{ + assert(self); + assert(ast); + + if (ast->type == NODE_INTEGER) + { + return TY_INTEGER; + } + else if (ast->type == NODE_BOOLEAN + || ast->type == NODE_EQ + || ast->type == NODE_NE) + { + return TY_BOOLEAN; + } + else + { + for (size_t i=0; ichildren.size; i++) + { + int ty = cstatic_resolve(self, ast->children.data[i]); + + if (ty != TY_NIL) + { + return ty; + } + } + } + + return TY_NIL; +} + +int cstatic_check(cstatic* self, node* ast, char* msg, size_t size) +{ + assert(self); + assert(ast); + + // Children + for (size_t i=0; ichildren.size; i++) + { + int status = cstatic_check(self, ast->children.data[i], + msg, size); + + if (!status) + { + return 0; + } + + } + + // Integer Binops + if (ast->type == NODE_EQ + || ast->type == NODE_NE) + { + int status = cstatic_check_same_type(self, + ast->children.data[0], + ast->children.data[1], + msg, + size); + + if (!status) + { + return status; + } + } + + // Boolean Unops + else if (ast->type == NODE_ASSERT + || ast->type == NODE_NOT) + { + int status = cstatic_check_type(self, + ast->children.data[0], + TY_BOOLEAN, + msg, + size); + + if (!status) + { + return status; + } + } + + // Boolean Binops + else if (ast->type == NODE_AND + || ast->type == NODE_OR) + { + int status = cstatic_check_same_type(self, + ast->children.data[0], + ast->children.data[1], + msg, + size); + + if (!status) + { + return status; + } + + status = cstatic_check_type(self, + ast->children.data[0], + TY_BOOLEAN, + msg, + size); + if (!status) + { + return status; + } + } + + // Integer Unops + else if (ast->type == NODE_UADD + || ast->type == NODE_USUB) + { + int status = cstatic_check_type(self, + ast->children.data[0], + TY_INTEGER, + msg, + size); + if (!status) + { + return status; + } + } + + // Integer Binops + else if (ast->type == NODE_ADD + || ast->type == NODE_SUB + || ast->type == NODE_MUL + || ast->type == NODE_DIV + || ast->type == NODE_MOD + || ast->type == NODE_POW + ) + { + int status = cstatic_check_same_type(self, + ast->children.data[0], + ast->children.data[1], + msg, + size); + + if (!status) + { + return status; + } + + status = cstatic_check_type(self, + ast->children.data[0], + TY_INTEGER, + msg, + size); + if (!status) + { + return status; + } + } + + return 1; +} + +int cstatic_check_type(cstatic* self, node* lhs, int rhs, + char* msg, size_t size) +{ + assert(self); + assert(lhs); + + int left = cstatic_resolve(self, lhs); + + if (left != rhs) + { + snprintf(msg, size, "E(%d): expected '%s', got '%s'.", + lhs->lineno, + TypesStr[left], + TypesStr[rhs]); + return 0; + } + + return 1; +} + +int cstatic_check_same_type(cstatic* self, node* lhs, node* rhs, + char* msg, size_t size) +{ + assert(self); + assert(lhs); + assert(rhs); + + int left = cstatic_resolve(self, lhs); + int right = cstatic_resolve(self, rhs); + + if (left != right) + { + snprintf(msg, size, "E(%d): expected '%s', got '%s'.", + lhs->lineno, + TypesStr[left], + TypesStr[right]); + return 0; + } + + return 1; +} diff --git a/src/cstatic.h b/src/cstatic.h new file mode 100644 index 0000000..f0974af --- /dev/null +++ b/src/cstatic.h @@ -0,0 +1,23 @@ +#ifndef CSTATIC_H +#define CSTATIC_H + +#include "commons.h" +#include "node.h" + +typedef struct { + int _unused; +} cstatic; + +void cstatic_init(cstatic* self); +void cstatic_free(cstatic* self); + +int cstatic_resolve(cstatic* self, node* ast); +int cstatic_check(cstatic* self, node* ast, char* msg, size_t size); + +int cstatic_check_type(cstatic* self, node* lhs, int rhs, + char* msg, size_t size); + +int cstatic_check_same_type(cstatic* self, node* lhs, node* rhs, + char* msg, size_t size); + +#endif diff --git a/src/lex.l b/src/lex.l index d466580..ee8f3e6 100644 --- a/src/lex.l +++ b/src/lex.l @@ -10,6 +10,7 @@ COMMENT ::[^\n]* WHITESPACES [ \t]+ BOOLEAN true|false +INTEGER -?[0-9]+ %% "\n" { line++; } @@ -17,6 +18,13 @@ BOOLEAN true|false {WHITESPACES} {} "assert" { return ASSERT; } +"+" { return ADD; } +"-" { return SUB; } +"*" { return MUL; } +"/" { return DIV; } +"%" { return MOD; } +"^" { return POW; } + "==" { return EQ; } "!=" { return NE; } "&&" { return AND; } @@ -26,9 +34,16 @@ BOOLEAN true|false ")" { return CPAR; } {BOOLEAN} { - yylval.boolean = yytext; + yylval.str = yytext; return BOOLEAN; } + +{INTEGER} { + yylval.str = yytext; + return INTEGER; +} + + . { fprintf(stderr, "E(%d): Lexical error near '%s'.\n", line, yytext); exit(-1); diff --git a/src/main.c b/src/main.c index 7ea39f2..75bef70 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,7 @@ #include "parser.h" #include "compiler.h" #include "vm.h" +#include "cstatic.h" extern FILE* yyin; extern node* ast; @@ -15,56 +16,77 @@ 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; + } + else + { + yyin = stdin; } - fprintf(stderr, "Usage: wuz \n"); - return -1; + node* n = malloc(sizeof(node)); + node_init(n, NODE_PROG, "", 1); + stack_push(n); + + yyparse(); + + ast = stack_pop(); + stack_free(); + + // Static checking + cstatic cs; + cstatic_init(&cs); + size_t const SZ = 512; + char msg[SZ]; + + int status = cstatic_check(&cs, ast, msg, SZ); + + if (!status) + { + fprintf(stderr, "%s\n", msg); + exit(-1); + } + + // Compilation + compiler comp; + compiler_init(&comp); + + program prog; + program_init(&prog); + + compile_node(&comp, ast, &prog); + + // Execution + 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); + cstatic_free(&cs); + node_free(ast); + + if (argc > 1) + { + fclose(yyin); + } + + return 0; } diff --git a/src/node.h b/src/node.h index 830e203..aa21b4f 100644 --- a/src/node.h +++ b/src/node.h @@ -3,10 +3,13 @@ #define NODE_TYPE(G) \ G(NODE_PROG), \ - G(NODE_BOOLEAN), \ + G(NODE_BOOLEAN), G(NODE_INTEGER), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), \ G(NODE_EQ), G(NODE_NE), \ - G(NODE_ASSERT) + G(NODE_ASSERT), \ + G(NODE_UADD), G(NODE_USUB), G(NODE_ADD), G(NODE_SUB), \ + G(NODE_MUL), G(NODE_DIV), G(NODE_MOD), G(NODE_POW) + #include "mutils.h" #include "commons.h" diff --git a/src/opcodes.h b/src/opcodes.h index a38f911..1f7aac1 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -5,8 +5,19 @@ #define OPCODES(G) \ G(OP_PUSH), \ - G(OP_AND), G(OP_OR), G(OP_NOT), \ - G(OP_EQ), G(OP_ASSERT) + G(OP_AND), \ + G(OP_OR), \ + G(OP_NOT), \ + G(OP_EQ), \ + G(OP_ASSERT), \ + G(OP_IADD), \ + G(OP_ISUB), \ + G(OP_IMUL), \ + G(OP_IDIV), \ + G(OP_IMOD), \ + G(OP_IPOW), \ + G(OP_IUADD), \ + G(OP_IUSUB), enum Opcodes { OPCODES(GEN_ENUM) diff --git a/src/parser.y b/src/parser.y index fc89b53..8d924dd 100644 --- a/src/parser.y +++ b/src/parser.y @@ -17,24 +17,28 @@ %} %union { - char* boolean; + char* str; void* node_val; }; %left ASSERT -%token BOOLEAN +%token BOOLEAN +%token INTEGER %left EQ NE %left AND %left OR +%left ADD SUB +%left MUL DIV MOD +%left POW %left NOT %type expr; %token OPAR CPAR %% -prog: exprs { - -} +prog: + | exprs { + } ; exprs: @@ -58,6 +62,67 @@ expr: $$ = n; } + | ADD expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_UADD, "", line); + node_add_child(n, $2); + $$ = n; + } + + | SUB expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_USUB, "", line); + node_add_child(n, $2); + $$ = n; + } + + | expr ADD expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_ADD, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr SUB expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_SUB, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr MUL expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_MUL, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr DIV expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_DIV, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr MOD expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_MOD, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } + + | expr POW expr { + node *n = malloc(sizeof(node)); + node_init(n, NODE_POW, "", line); + node_add_child(n, $1); + node_add_child(n, $3); + $$ = n; + } | expr EQ expr { node *n = malloc(sizeof(node)); @@ -108,6 +173,12 @@ expr: node_init(n, NODE_BOOLEAN, $1, line); $$ = n; } + + | INTEGER { + node* n = malloc(sizeof(node)); + node_init(n, NODE_INTEGER, $1, line); + $$ = n; + } ; %% diff --git a/src/type.h b/src/type.h index 1e0d595..8bb66da 100644 --- a/src/type.h +++ b/src/type.h @@ -4,7 +4,9 @@ #include "commons.h" #define TYPES(G) \ - G(TY_BOOLEAN) + G(TY_NIL), \ + G(TY_BOOLEAN), \ + G(TY_INTEGER), enum Types { TYPES(GEN_ENUM) diff --git a/src/value.c b/src/value.c index 69fe260..efe2020 100644 --- a/src/value.c +++ b/src/value.c @@ -10,6 +10,16 @@ void value_init_boolean(value* self, int boolean, int lineno) type_init(self->type, TY_BOOLEAN); } +void value_init_integer(value* self, int integer, int lineno) +{ + assert(self); + self->val.integer = integer; + self->lineno = lineno; + + self->type = malloc(sizeof(type)); + type_init(self->type, TY_INTEGER); +} + void value_free(value* self) { assert(self); @@ -45,6 +55,11 @@ int value_equals(value* self, value* rhs) return self->val.boolean == rhs->val.boolean; } + if (self->type->base_type == TY_INTEGER) + { + return self->val.integer == rhs->val.integer; + } + size_t const SZ = 512; char ty_str[SZ]; @@ -65,6 +80,9 @@ size_t value_str(value* self, char* buffer, size_t size) return snprintf(buffer, size, "%s", self->val.boolean == 0 ? "false" : "true"); + case TY_INTEGER: + return snprintf(buffer, size, "%d", + self->val.integer); default: { fprintf(stderr, "E: unknown value"); exit(-1); diff --git a/src/value.h b/src/value.h index 2a92da1..bc6245a 100644 --- a/src/value.h +++ b/src/value.h @@ -9,10 +9,12 @@ typedef struct { int lineno; union { int boolean; + int integer; } val; } value; void value_init_boolean(value* self, int boolean, int lineno); +void value_init_integer(value* self, int integer, int lineno); void value_free(value* self); value* value_new_clone(value* self); diff --git a/src/vm.c b/src/vm.c index b52312b..08e29bc 100644 --- a/src/vm.c +++ b/src/vm.c @@ -46,6 +46,14 @@ void vm_exec(vm* self, program* prog) case OP_OR: vm_or(self); break; case OP_NOT: vm_not(self); break; case OP_EQ: vm_eq(self); break; + case OP_IADD: vm_iadd(self); break; + case OP_IUADD: vm_iuadd(self); break; + case OP_ISUB: vm_isub(self); break; + case OP_IUSUB: vm_iusub(self); break; + case OP_IMUL: vm_imul(self); break; + case OP_IDIV: vm_idiv(self); break; + case OP_IMOD: vm_imod(self); break; + case OP_IPOW: vm_ipow(self); break; case OP_ASSERT: vm_assert(self); break; default: { fprintf(stderr, "unknown opcode %s\n", @@ -165,6 +173,136 @@ void vm_not(vm* self) self->pc++; } +void vm_iadd(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, lhs->val.integer + rhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_iuadd(vm* self) +{ + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, lhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_isub(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, lhs->val.integer - rhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_iusub(vm* self) +{ + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, -lhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_imul(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, lhs->val.integer * rhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_idiv(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, lhs->val.integer / rhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_imod(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, lhs->val.integer % rhs->val.integer, lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + +void vm_ipow(vm* self) +{ + value* rhs = vm_pop_value(self); + value* lhs = vm_pop_value(self); + + value* val = malloc(sizeof(value)); + value_init_integer(val, pow(lhs->val.integer, rhs->val.integer), lhs->lineno); + vm_push_value(self, val); + + value_free(rhs); + free(rhs); + value_free(lhs); + free(lhs); + + self->pc++; +} + void vm_eq(vm* self) { assert(self); diff --git a/src/vm.h b/src/vm.h index 45caa1a..c9f4932 100644 --- a/src/vm.h +++ b/src/vm.h @@ -29,6 +29,16 @@ void vm_and(vm* self); void vm_or(vm* self); void vm_not(vm* self); +void vm_iadd(vm* self); +void vm_iuadd(vm* self); +void vm_isub(vm* self); +void vm_iusub(vm* self); +void vm_imul(vm* self); +void vm_idiv(vm* self); +void vm_imod(vm* self); +void vm_ipow(vm* self); + + void vm_eq(vm* self); void vm_assert(vm* self); diff --git a/tests/err_ty_int_bool.wuz b/tests/err_ty_int_bool.wuz new file mode 100644 index 0000000..2ff019f --- /dev/null +++ b/tests/err_ty_int_bool.wuz @@ -0,0 +1,30 @@ ++ true +- true +1 + true +1 - true +1 * true +1 / true +1 % true +1 ^ true + +true + 1 +true - 1 +true * 1 +true / 1 +true % 1 +true ^ 1 + +true + false +true - false +true * false +true / false +true % false +true ^ false + +1 && true +2 || false +!5 + +true && 1 +false || -2 +!!5 diff --git a/tests/errors.sh b/tests/errors.sh new file mode 100755 index 0000000..c403629 --- /dev/null +++ b/tests/errors.sh @@ -0,0 +1,37 @@ +#!/usr/bin/sh +OK=0 +KO=0 +COUNTER=0 +LINE=1 + +echo -e "\e[34m=== Error Testing ===\e[0m" + +for file in $(find . -name "err_*.wuz") +do + LINE=1 + + while read line; + do + echo $line | wuz &> /dev/null + RET="$?" + if [ $RET -ne 0 ] || [ "$line" == "" ] + then + OK=$(($OK+1)) + else + KO=$(($KO+1)) + echo -e "\e[33m\tE($file:$LINE) assertion failed\e[0m" + fi + + COUNTER=$(($COUNTER + 1)) + LINE=$(($LINE + 1)) + done < $file + + if [ "$KO" == "0" ] + then + echo -e "$file ... \e[32mok\e[0m" + else + echo -e "$file ... \e[31mko\e[0m" + fi + +done + diff --git a/tests/run.sh b/tests/run.sh index f3d7a3a..6ab6895 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -3,7 +3,9 @@ OK=0 KO=0 -for file in $(find . -name "*.wuz") +echo -e "\e[34m=== Unit Testing ===\e[0m" + +for file in $(find . -name "test_*.wuz") do wuz $file RES="$?" @@ -29,3 +31,6 @@ else echo -e "\e[31m$KO tests failed [$TOTAL]\e[0m" fi +echo + +./errors.sh diff --git a/tests/booleans.wuz b/tests/test_booleans.wuz similarity index 100% rename from tests/booleans.wuz rename to tests/test_booleans.wuz diff --git a/tests/test_integers.wuz b/tests/test_integers.wuz new file mode 100644 index 0000000..25af4c3 --- /dev/null +++ b/tests/test_integers.wuz @@ -0,0 +1,22 @@ +assert 1 == 1 +assert 2 != 3 + +assert -1 == 2 + -3 +assert -8 == -(3 + 1) * 2 + +assert 7 == 1 + 2 * 3 +assert 9 == (1 + 2) * 3 + +assert 8 == 2^3 + +assert -8 == -2^3 + +assert 1 == 5 % 2 +assert 2 == 5 / 2 +assert 3 == 6 / 2 + +assert 64 == 2^(3+3) + +assert 1 == -2 - -3 + +assert 5 + 7 == 12