From dd5c65b448da9ea6312d0603ec1d76bf71382039 Mon Sep 17 00:00:00 2001 From: bog Date: Sun, 11 Feb 2024 15:45:39 +0100 Subject: [PATCH] :sparkles: int, float and strings. --- bc/src/compiler.c | 68 ++++++++ bc/src/opcodes.h | 4 +- doc/gux.bnf | 11 +- guxi/src/main.c | 2 +- lang/CMakeLists.txt | 3 +- lang/src/lexer.c | 199 ++++++++++++++++++++++- lang/src/lexer.h | 11 +- lang/src/node.h | 14 +- lang/src/parser.c | 136 +++++++++++++++- lang/src/parser.h | 5 + lang/src/type.c | 21 ++- lang/src/type.h | 5 +- lang/src/type_checker.c | 135 +++++++++++++++- lang/src/type_checker.h | 2 +- lang/src/type_resolver.c | 69 +++++++- lang/src/value.c | 324 ++++++++++++++++++++++++++++++++++++++ lang/src/value.h | 19 +++ lang/tests/lexer.c | 41 ++++- lang/tests/parser.c | 41 +++++ lang/tests/type_checker.c | 110 ++++++++++++- lib/src/commons.h | 1 + lib/src/vec.c | 10 -- lib/src/vec.h | 1 - tests/bool.gux | 14 -- tests/builtins.gux | 68 ++++++++ tests/cmp.gux | 25 +++ vm/src/vm.c | 191 +++++++++++++++++++++- vm/src/vm.h | 3 +- 28 files changed, 1473 insertions(+), 60 deletions(-) delete mode 100644 tests/bool.gux create mode 100644 tests/builtins.gux create mode 100644 tests/cmp.gux diff --git a/bc/src/compiler.c b/bc/src/compiler.c index cff97d2..4b7bd8c 100644 --- a/bc/src/compiler.c +++ b/bc/src/compiler.c @@ -34,6 +34,27 @@ int compiler_compile(struct compiler* self, program_push_instr(program, OP_PUSH, addr); } break; + case NODE_INT: { + struct value* val = malloc(sizeof(struct value)); + value_init_int(val, atoi(node->value), node->line); + size_t addr = program_push_constant(program, val); + program_push_instr(program, OP_PUSH, addr); + } break; + + case NODE_FLOAT: { + struct value* val = malloc(sizeof(struct value)); + value_init_float(val, atof(node->value), node->line); + size_t addr = program_push_constant(program, val); + program_push_instr(program, OP_PUSH, addr); + } break; + + case NODE_STRING: { + struct value* val = malloc(sizeof(struct value)); + value_init_string(val, node->value, 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); @@ -55,6 +76,19 @@ int compiler_compile(struct compiler* self, } break; + case NODE_EQ: { + compiler_compile(self, node->children.data[0], program); + compiler_compile(self, node->children.data[1], program); + program_push_instr(program, OP_EQ, NO_PARAM); + } break; + + case NODE_NE: { + compiler_compile(self, node->children.data[0], program); + compiler_compile(self, node->children.data[1], program); + program_push_instr(program, OP_EQ, NO_PARAM); + program_push_instr(program, OP_NOT, NO_PARAM); + } break; + case NODE_NOT: { compiler_compile(self, node->children.data[0], program); program_push_instr(program, OP_NOT, NO_PARAM); @@ -158,6 +192,40 @@ int compiler_compile(struct compiler* self, vec_free(&to_pivot); } break; + case NODE_ADD: + case NODE_SUB: + case NODE_MUL: + case NODE_DIV: + case NODE_MOD: + case NODE_POW: + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE: + { + + for (size_t i=0; ichildren.size; i++) + { + compiler_compile(self, node->children.data[i], program); + } + + switch (node->type) + { + case NODE_ADD: program_push_instr(program, OP_ADD, NO_PARAM); break; + case NODE_SUB: program_push_instr(program, OP_SUB, NO_PARAM); break; + case NODE_MUL: program_push_instr(program, OP_MUL, NO_PARAM); break; + case NODE_DIV: program_push_instr(program, OP_DIV, NO_PARAM); break; + case NODE_MOD: program_push_instr(program, OP_MOD, NO_PARAM); break; + case NODE_POW: program_push_instr(program, OP_POW, NO_PARAM); break; + case NODE_LT: program_push_instr(program, OP_LT, NO_PARAM); break; + case NODE_LE: program_push_instr(program, OP_LE, NO_PARAM); break; + case NODE_GT: program_push_instr(program, OP_GT, NO_PARAM); break; + case NODE_GE: program_push_instr(program, OP_GE, NO_PARAM); break; + default: break; + } + + } break; + default: { for (size_t i=0; ichildren.size; i++) { diff --git a/bc/src/opcodes.h b/bc/src/opcodes.h index 9061717..fb64900 100644 --- a/bc/src/opcodes.h +++ b/bc/src/opcodes.h @@ -3,7 +3,9 @@ #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) + G(OP_BRT), G(OP_NOP), G(OP_NOT), G(OP_EQ), \ + G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), G(OP_MOD), G(OP_POW), \ + G(OP_LT), G(OP_LE), G(OP_GT),G(OP_GE), #include diff --git a/doc/gux.bnf b/doc/gux.bnf index 228efd1..1ceca80 100644 --- a/doc/gux.bnf +++ b/doc/gux.bnf @@ -8,8 +8,13 @@ EXPR ::= | ASSERT EXPR OR ::= AND (or AND)* -AND ::= NOT (and NOT)* -NOT ::= not* LITERAL +AND ::= EQNE (and EQNE)* +EQNE ::= CMP ((eq|ne) CMP)? +CMP ::= TERM ((lt|le|gt|ge) TERM)? +TERM ::= FACTOR ((add|sub) FACTOR)* +FACTOR ::= POW ((mul|div|mod) POW)* +POW ::= NOT (pow NOT)? +NOT ::= not* LITERAL LITERAL ::= BUILTIN | opar EXPR cpar -BUILTIN ::= bool +BUILTIN ::= bool | int | float | string diff --git a/guxi/src/main.c b/guxi/src/main.c index 5262938..06ccb8c 100644 --- a/guxi/src/main.c +++ b/guxi/src/main.c @@ -275,7 +275,7 @@ int main(int argc, char** argv) if (show_stack) { char buffer[GUX_STR_SIZE]; - vm_str(&vm, buffer, GUX_STR_SIZE); + vm_str(&vm, &program, buffer, GUX_STR_SIZE); printf("%s\n", buffer); } diff --git a/lang/CMakeLists.txt b/lang/CMakeLists.txt index 0e6480c..6c70ae6 100644 --- a/lang/CMakeLists.txt +++ b/lang/CMakeLists.txt @@ -24,9 +24,10 @@ target_include_directories(gux-lang ) target_link_libraries(gux-lang - PUBLIC gux-lib + PUBLIC gux-lib m ) + # ===== # TESTS # ===== diff --git a/lang/src/lexer.c b/lang/src/lexer.c index 6c4b9d9..c902019 100644 --- a/lang/src/lexer.c +++ b/lang/src/lexer.c @@ -1,4 +1,5 @@ #include "lexer.h" +#include "commons.h" void lexer_init(struct lexer* self, char const* source) { @@ -11,7 +12,22 @@ void lexer_init(struct lexer* self, char const* source) vec_init(&self->toks, 1); + lexer_add_tok(self, "<", NODE_LT, 0); + lexer_add_tok(self, "<=", NODE_LE, 0); + lexer_add_tok(self, ">", NODE_GT, 0); + lexer_add_tok(self, ">=", NODE_GE, 0); + + lexer_add_tok(self, "+", NODE_ADD, 0); + lexer_add_tok(self, "-", NODE_SUB, 0); + lexer_add_tok(self, "*", NODE_MUL, 0); + lexer_add_tok(self, "/", NODE_DIV, 0); + lexer_add_tok(self, "%", NODE_MOD, 0); + lexer_add_tok(self, "**", NODE_POW, 0); + + lexer_add_tok(self, "==", NODE_EQ, 0); + lexer_add_tok(self, "!=", NODE_NE, 0); 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); @@ -71,16 +87,33 @@ struct node* lexer_next_new(struct lexer* self) struct token_info info; + if (lexer_scan_float(self, &info)) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, info.type, info.value, self->line); + self->cursor = info.position; + return node; + } + + if (lexer_scan_int(self, &info)) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, info.type, info.value, self->line); + self->cursor = info.position; + return node; + } + struct node* best = NULL; size_t pos = 0; + // tokens for (size_t i=0; itoks.size; i++) { struct tok* tok = self->toks.data[i]; if (lexer_scan_text(self, tok->sym, &info)) { - if (best == NULL || pos > info.position) + if (best == NULL || info.position > pos) { struct node* node = malloc(sizeof(struct node)); node_init(node, tok->type, "", self->line); @@ -88,13 +121,12 @@ struct node* lexer_next_new(struct lexer* self) if (best) { node_free(best); + free(best); } best = node; pos = info.position; } - - self->cursor = info.position; } } @@ -121,6 +153,14 @@ struct node* lexer_next_new(struct lexer* self) return node; } + if (lexer_scan_string(self, &info)) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, info.type, info.value, self->line); + self->cursor = info.position; + return node; + } + return NULL; } @@ -191,7 +231,7 @@ int lexer_scan_keyword(struct lexer* self, } info->position = self->cursor + strlen(keyword); - info->value = keyword; + memcpy(info->value, keyword, GUX_STR_SIZE * sizeof(char)); return 1; } @@ -218,11 +258,160 @@ int lexer_scan_text(struct lexer* self, } info->position = self->cursor + strlen(text); - info->value = text; + memcpy(info->value, text, GUX_STR_SIZE * sizeof(char)); return 1; } +int lexer_scan_int(struct lexer* self, + struct token_info* info) +{ + assert(self); + assert(info); + + size_t cursor = self->cursor; + char value[GUX_STR_SIZE]; + memset(value, 0, GUX_STR_SIZE); + size_t sz = 0; + + if (cursor < strlen(self->source) + && self->source[cursor] == '-') + { + value[sz++] = '-'; + cursor++; + } + + while (cursor < strlen(self->source) + && isdigit(self->source[cursor])) + { + value[sz] = self->source[cursor]; + cursor++; + sz++; + } + + if (sz == 1 && value[0] == '-') + { + return 0; + } + + if (sz > 0) + { + info->position = cursor; + info->type = NODE_INT; + memcpy(info->value, value, GUX_STR_SIZE * sizeof(char)); + return 1; + } + + return 0; +} + +int lexer_scan_float(struct lexer* self, + struct token_info* info) +{ + assert(self); + assert(info); + size_t cursor = self->cursor; + char value[GUX_STR_SIZE]; + memset(value, 0, GUX_STR_SIZE); + size_t sz = 0; + + if (cursor < strlen(self->source) + && self->source[cursor] == '-') + { + value[sz++] = '-'; + cursor++; + } + + while (cursor < strlen(self->source) + && isdigit(self->source[cursor])) + { + value[sz] = self->source[cursor]; + cursor++; + sz++; + } + + int is_float = 0; + + if (cursor < strlen(self->source) + && self->source[cursor] == '.') + { + value[sz++] = '.'; + is_float = 1; + cursor++; + + while (cursor < strlen(self->source) + && isdigit(self->source[cursor])) + { + value[sz] = self->source[cursor]; + cursor++; + sz++; + } + } + + if (sz > 0 && is_float) + { + info->position = cursor; + info->type = NODE_FLOAT; + memcpy(info->value, value, GUX_STR_SIZE * sizeof(char)); + return 1; + } + + return 0; +} + +int lexer_scan_string(struct lexer* self, + struct token_info* info) +{ + assert(self); + assert(info); + + size_t cursor = self->cursor; + char value[GUX_STR_SIZE]; + memset(value, 0, GUX_STR_SIZE); + + size_t sz = 0; + char delim = ' '; + + if (cursor < strlen(self->source) + && (self->source[cursor] == '\'' + || self->source[cursor] == '"')) + { + delim = self->source[cursor]; + cursor++; + } + else + { + return 0; + } + + int found = 0; + + while (cursor < strlen(self->source)) + { + if (self->source[cursor] == delim + && (cursor == 0 || self->source[cursor - 1] != '\\')) + { + found = 1; + cursor++; + break; + } + + value[sz++] = self->source[cursor++]; + } + + if (found) + { + info->position = cursor; + info->type = NODE_STRING; + memcpy(info->value, value, GUX_STR_SIZE * sizeof(char)); + + return 1; + } + + return 0; +} + + void lexer_add_tok(struct lexer* self, char* sym, enum NodeType type, diff --git a/lang/src/lexer.h b/lang/src/lexer.h index aff632c..d8400c2 100644 --- a/lang/src/lexer.h +++ b/lang/src/lexer.h @@ -21,7 +21,7 @@ struct lexer { struct token_info { enum NodeType type; - char* value; + char value[GUX_STR_SIZE]; size_t position; }; @@ -43,6 +43,15 @@ int lexer_scan_text(struct lexer* self, char* text, struct token_info* info); +int lexer_scan_int(struct lexer* self, + struct token_info* info); + +int lexer_scan_float(struct lexer* self, + struct token_info* info); + +int lexer_scan_string(struct lexer* self, + struct token_info* info); + void lexer_add_tok(struct lexer* self, char* sym, enum NodeType type, diff --git a/lang/src/node.h b/lang/src/node.h index d805e2b..4df1a63 100644 --- a/lang/src/node.h +++ b/lang/src/node.h @@ -4,11 +4,15 @@ #include #include -#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) +#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), \ + G(NODE_EQ), G(NODE_NE), G(NODE_INT), G(NODE_FLOAT), \ + G(NODE_STRING), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \ + G(NODE_MOD), G(NODE_POW), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ + G(NODE_GE) GUX_ENUM_H(NodeType, NODE_TYPE); diff --git a/lang/src/parser.c b/lang/src/parser.c index a8f90b8..1ec7fa4 100644 --- a/lang/src/parser.c +++ b/lang/src/parser.c @@ -1,6 +1,7 @@ #include "parser.h" #include "commons.h" #include "lexer.h" +#include "node.h" #include "vec.h" void parser_init(struct parser* self) @@ -230,14 +231,14 @@ struct node* parser_try_new_or(struct parser* self) struct node* parser_try_new_and(struct parser* self) { - struct node* lhs = parser_try_new_not(self); + struct node* lhs = parser_try_new_eqne(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); + struct node* rhs = parser_try_new_eqne(self); node_add_child(node, lhs); node_add_child(node, rhs); @@ -248,6 +249,115 @@ struct node* parser_try_new_and(struct parser* self) return lhs; } +struct node* parser_try_new_eqne(struct parser* self) +{ + struct node* lhs = parser_try_new_cmp(self); + + if (parser_type_is(self, NODE_EQ, 0) + || parser_type_is(self, NODE_NE, 0)) + { + struct node* node = malloc(sizeof(struct node)); + struct node* current = self->tokens.data[self->cursor]; + self->cursor++; + + node_init(node, current->type, "", parser_current_line(self)); + + node_add_child(node, lhs); + node_add_child(node, parser_try_new_cmp(self)); + lhs = node; + } + + return lhs; +} + +struct node* parser_try_new_cmp(struct parser* self) +{ + assert(self); + struct node* lhs = parser_try_new_term(self); + + if (parser_type_is(self, NODE_LT, 0) + || parser_type_is(self, NODE_LE, 0) + || parser_type_is(self, NODE_GT, 0) + || parser_type_is(self, NODE_GE, 0)) + { + struct node* node = malloc(sizeof(struct node)); + struct node* current = self->tokens.data[self->cursor]; + self->cursor++; + + node_init(node, current->type, "", parser_current_line(self)); + + node_add_child(node, lhs); + node_add_child(node, parser_try_new_term(self)); + lhs = node; + } + + return lhs; +} + +struct node* parser_try_new_term(struct parser* self) +{ + assert(self); + struct node* lhs = parser_try_new_factor(self); + + while (parser_type_is(self, NODE_ADD, 0) + || parser_type_is(self, NODE_SUB, 0)) + { + struct node* node = malloc(sizeof(struct node)); + struct node* tok = self->tokens.data[self->cursor]; + node_init(node, tok->type, "", parser_current_line(self)); + self->cursor++; + + node_add_child(node, lhs); + node_add_child(node, parser_try_new_factor(self)); + lhs = node; + } + + return lhs; +} + +struct node* parser_try_new_factor(struct parser* self) +{ + assert(self); + struct node* lhs = parser_try_new_pow(self); + + while (parser_type_is(self, NODE_MUL, 0) + || parser_type_is(self, NODE_DIV, 0) + || parser_type_is(self, NODE_MOD, 0)) + { + struct node* node = malloc(sizeof(struct node)); + struct node* tok = self->tokens.data[self->cursor]; + node_init(node, tok->type, "", parser_current_line(self)); + self->cursor++; + + node_add_child(node, lhs); + node_add_child(node, parser_try_new_pow(self)); + lhs = node; + } + + return lhs; +} + +struct node* parser_try_new_pow(struct parser* self) +{ + assert(self); + struct node* lhs = parser_try_new_not(self); + + if (parser_try_consume(self, NODE_POW) == 0) + { + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_POW, "", parser_current_line(self)); + + node_add_child(node, lhs); + + node_add_child(node, parser_try_new_not(self)); + + lhs = node; + } + + return lhs; +} + + struct node* parser_try_new_not(struct parser* self) { if (parser_try_consume(self, NODE_NOT) == 0) @@ -285,7 +395,25 @@ struct node* parser_try_new_builtin(struct parser* self) { assert(self); - struct node* value = parser_try_consume_new(self, NODE_BOOL); + if (parser_type_is(self, NODE_BOOL, 0)) + { + return parser_try_consume_new(self, NODE_BOOL); + } - return value; + if (parser_type_is(self, NODE_INT, 0)) + { + return parser_try_consume_new(self, NODE_INT); + } + + if (parser_type_is(self, NODE_FLOAT, 0)) + { + return parser_try_consume_new(self, NODE_FLOAT); + } + + if (parser_type_is(self, NODE_STRING, 0)) + { + return parser_try_consume_new(self, NODE_STRING); + } + + return NULL; } diff --git a/lang/src/parser.h b/lang/src/parser.h index 11ff842..0028e11 100644 --- a/lang/src/parser.h +++ b/lang/src/parser.h @@ -29,6 +29,11 @@ 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_eqne(struct parser* self); +struct node* parser_try_new_cmp(struct parser* self); +struct node* parser_try_new_term(struct parser* self); +struct node* parser_try_new_factor(struct parser* self); +struct node* parser_try_new_pow(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); diff --git a/lang/src/type.c b/lang/src/type.c index 457912b..822ab84 100644 --- a/lang/src/type.c +++ b/lang/src/type.c @@ -1,4 +1,5 @@ #include "type.h" +#include "commons.h" #include "vec.h" GUX_ENUM_C(TypeKind, TYPE_KIND); @@ -27,7 +28,13 @@ void type_free(struct type* self) void type_clear(struct type* self) { assert(self); - vec_clear(&self->subtypes); + + while (self->subtypes.size > 0) + { + void* element = vec_pop(&self->subtypes); + type_free(element); + free(element); + } } int type_equals(struct type* self, struct type* rhs) @@ -89,3 +96,15 @@ size_t type_str(struct type* self, char* buffer, size_t size) return sz; } + +int type_is(struct type* self, char const* repr) +{ + assert(self); + assert(repr); + + char buffer[GUX_STR_SIZE]; + type_str(self, buffer, GUX_STR_SIZE); + + int result = strcmp(repr, buffer) == 0; + return result; +} diff --git a/lang/src/type.h b/lang/src/type.h index 4ea2eda..e30863c 100644 --- a/lang/src/type.h +++ b/lang/src/type.h @@ -5,7 +5,8 @@ #include #define TYPE_KIND(G) \ - G(TYPE_VOID), G(TYPE_BOOL) + G(TYPE_VOID), G(TYPE_BOOL), G(TYPE_INT), \ + G(TYPE_FLOAT), G(TYPE_STRING) GUX_ENUM_H(TypeKind, TYPE_KIND); @@ -22,6 +23,6 @@ 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); - +int type_is(struct type* self, char const* repr); #endif diff --git a/lang/src/type_checker.c b/lang/src/type_checker.c index ae0ff12..33a5e4b 100644 --- a/lang/src/type_checker.c +++ b/lang/src/type_checker.c @@ -2,11 +2,13 @@ #include "commons.h" #include "node.h" #include "type.h" +#include "type_resolver.h" void type_checker_init(struct type_checker* self) { assert(self); type_resolver_init(&self->resolver); + memset(self->error_msg, 0, GUX_STR_SIZE); } void type_checker_free(struct type_checker* self) @@ -23,10 +25,91 @@ int type_checker_check(struct type_checker* self, switch (node->type) { + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE: { + struct node* lhs = node->children.data[0]; + struct node* rhs = node->children.data[1]; + + struct type lhs_type; + type_init(&lhs_type, TYPE_VOID); + int solve_lhs = type_resolver_resolve(&self->resolver, lhs, &lhs_type); + + struct type rhs_type; + type_init(&rhs_type, TYPE_VOID); + int solve_rhs = type_resolver_resolve(&self->resolver, rhs, &rhs_type); + + size_t MSG_SZ = GUX_STR_SIZE * 4; + char lhs_msg[GUX_STR_SIZE]; + type_str(&lhs_type, lhs_msg, GUX_STR_SIZE); + char rhs_msg[GUX_STR_SIZE]; + type_str(&rhs_type, rhs_msg, GUX_STR_SIZE); + self->error_line = lhs->line; + + if (lhs_type.kind != TYPE_INT && lhs_type.kind != TYPE_FLOAT) + { + if (rhs_type.kind == TYPE_INT || rhs_type.kind == TYPE_FLOAT) + { + snprintf(self->error_msg, MSG_SZ, + "wrong type: expected <%s>, got <%s>", + rhs_msg, + lhs_msg); + } + else + { + snprintf(self->error_msg, MSG_SZ, + "wrong type: expected or , got <%s>", + lhs_msg); + } + } + else if (rhs_type.kind != TYPE_INT && rhs_type.kind != TYPE_FLOAT) + { + if (lhs_type.kind == TYPE_INT || lhs_type.kind == TYPE_FLOAT) + { + snprintf(self->error_msg, MSG_SZ, + "wrong type: expected <%s>, got <%s>", + lhs_msg, + rhs_msg); + } + else + { + snprintf(self->error_msg, MSG_SZ, + "wrong type: expected or , got <%s>", + lhs_msg); + } + } + else if (!type_equals(&lhs_type, &rhs_type)) + { + snprintf(self->error_msg, MSG_SZ, + "type mismatch (<%s>, <%s>)", + lhs_msg, + rhs_msg); + } + + if (solve_lhs != 0 + || solve_rhs != 0 + || !type_equals(&lhs_type, &rhs_type) + || lhs_type.subtypes.size > 0 + || rhs_type.subtypes.size > 0 + || (lhs_type.kind != TYPE_INT && lhs_type.kind != TYPE_FLOAT) + || (rhs_type.kind != TYPE_INT && rhs_type.kind != TYPE_FLOAT)) + { + type_free(&lhs_type); + type_free(&rhs_type); + + return 1; + } + + type_free(&lhs_type); + type_free(&rhs_type); + + } return 0; + case NODE_ASSERT: case NODE_NOT: case NODE_AND: - case NODE_OR:{ + case NODE_OR: { for (size_t i=0; ichildren.size; i++) { if (type_checker_ensure_kind(self, @@ -38,6 +121,55 @@ int type_checker_check(struct type_checker* self, } } return 0; + case NODE_EQ: + case NODE_NE: { + struct type type; + type_init(&type, TYPE_VOID); + type_resolver_resolve(&self->resolver, node->children.data[0], &type); + + for (size_t i=1; ichildren.size; i++) + { + struct type node_type; + type_init(&node_type, TYPE_VOID); + + type_resolver_resolve(&self->resolver, + node->children.data[i], + &node_type); + + if (!type_equals(&type, &node_type)) + { + self->error_line = node->line; + snprintf(self->error_msg, + GUX_STR_SIZE, "cannot compare different types"); + type_free(&type); + type_free(&node_type); + return 1; + } + + type_free(&node_type); + } + + type_free(&type); + } return 0; + + case NODE_ADD: + case NODE_SUB: + case NODE_MUL: + case NODE_DIV: + case NODE_MOD: + case NODE_POW: { + struct type type; + type_init(&type, TYPE_VOID); + + if (type_resolver_resolve(&self->resolver, node, &type) != 0) + { + type_free(&type); + return 1; + } + + type_free(&type); + } return 0; + default: { for (size_t i=0; ichildren.size; i++) { @@ -66,6 +198,7 @@ int type_checker_ensure_kind(struct type_checker* self, 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_"), diff --git a/lang/src/type_checker.h b/lang/src/type_checker.h index e76d381..8f48a9b 100644 --- a/lang/src/type_checker.h +++ b/lang/src/type_checker.h @@ -8,7 +8,7 @@ struct type_checker { struct type_resolver resolver; - char* error_msg; + char error_msg[GUX_STR_SIZE]; int error_line; }; diff --git a/lang/src/type_resolver.c b/lang/src/type_resolver.c index e17b3a1..ee8ee91 100644 --- a/lang/src/type_resolver.c +++ b/lang/src/type_resolver.c @@ -24,14 +24,81 @@ int type_resolver_resolve(struct type_resolver* self, switch (node->type) { + case NODE_EQ: + case NODE_NE: case NODE_ASSERT: case NODE_BOOL: case NODE_AND: case NODE_OR: - case NODE_NOT:{ + case NODE_NOT: + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE:{ type->kind = TYPE_BOOL; } return 0; + case NODE_INT: { + type->kind = TYPE_INT; + } return 0; + + case NODE_FLOAT: { + type->kind = TYPE_FLOAT; + } return 0; + + case NODE_STRING: { + type->kind = TYPE_STRING; + } return 0; + + case NODE_ADD: + case NODE_SUB: + case NODE_MUL: + case NODE_DIV: + case NODE_MOD: + case NODE_POW:{ + struct type lhs; + type_init(&lhs, TYPE_VOID); + type_resolver_resolve(self, node->children.data[0], &lhs); + + struct type rhs; + type_init(&rhs, TYPE_VOID); + type_resolver_resolve(self, node->children.data[1], &rhs); + + int equals = type_equals(&lhs, &rhs); + + if (type_is(&lhs, "INT") && equals) + { + type->kind = TYPE_INT; + } + else if (type_is(&lhs, "FLOAT") && equals) + { + type->kind = TYPE_FLOAT; + } + else if (type_is(&lhs, "STRING") && equals && node->type == NODE_ADD) + { + type->kind = TYPE_STRING; + } + else if (type_is(&lhs, "STRING") + && type_is(&rhs, "INT") && node->type == NODE_MUL) + { + type->kind = TYPE_STRING; + } + else if (type_is(&lhs, "INT") + && type_is(&rhs, "STRING") && node->type == NODE_MUL) + { + type->kind = TYPE_STRING; + } + else + { + type_free(&lhs); + type_free(&rhs); + return 1; + } + + type_free(&lhs); + type_free(&rhs); + } return 0; + default: { } return 1; } diff --git a/lang/src/value.c b/lang/src/value.c index 258d23f..badea9b 100644 --- a/lang/src/value.c +++ b/lang/src/value.c @@ -9,9 +9,41 @@ void value_init_bool(struct value* self, int value, int line) self->line = line; } +void value_init_int(struct value* self, int value, int line) +{ + assert(self); + + type_init(&self->type, TYPE_INT); + self->data.i = value; + self->line = line; +} + + +void value_init_float(struct value* self, float value, int line) +{ + assert(self); + + type_init(&self->type, TYPE_FLOAT); + self->data.f = value; + self->line = line; +} + +void value_init_string(struct value* self, char* value, int line) +{ + assert(self); + + type_init(&self->type, TYPE_STRING); + self->data.s = strdup(value); + self->line = line; +} + void value_free(struct value* self) { assert(self); + if (self->type.kind == TYPE_STRING) + { + free(self->data.s); + } type_free(&self->type); } @@ -33,6 +65,22 @@ size_t value_str(struct value* self, char* buffer, size_t size) sz += snprintf(buffer + sz, size - sz, "%s", self->data.b ? "true" : "false"); } + else if (self->type.kind == TYPE_INT) + { + sz += snprintf(buffer + sz, size - sz, "%d", self->data.i); + } + else if (self->type.kind == TYPE_FLOAT) + { + sz += snprintf(buffer + sz, size - sz, "%f", self->data.f); + } + else if (self->type.kind == TYPE_STRING) + { + sz += snprintf(buffer + sz, size - sz, "%s", self->data.s); + } + else + { + assert(0); + } } return sz; @@ -79,3 +127,279 @@ struct value* value_try_new_not(struct value* self) return res; } + +struct value* value_try_new_add(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_int(res, self->data.i + rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_float(res, self->data.f + rhs->data.f, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "STRING")) + { + size_t const SZ = strlen(self->data.s) + strlen(rhs->data.s) + 1; + char str[SZ]; + snprintf(str, SZ, "%s%s", self->data.s, rhs->data.s); + value_init_string(res, str, self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_sub(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_int(res, self->data.i - rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_float(res, self->data.f - rhs->data.f, self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_mul(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_int(res, self->data.i * rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_float(res, self->data.f * rhs->data.f, self->line); + return res; + } + + struct value* factor_val = self; + struct value* content_val = rhs; + + if (type_is(&factor_val->type, "STRING") && type_is(&content_val->type, "INT")) + { + struct value* tmp = factor_val; + factor_val = content_val; + content_val = tmp; + } + + if (type_is(&factor_val->type, "INT") && type_is(&content_val->type, "STRING")) + { + int factor = factor_val->data.i; + char* content = content_val->data.s; + + size_t const SZ = factor * strlen(content); + char str[SZ]; + + for (int i=0; iline); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_div(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_int(res, self->data.i / rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_float(res, self->data.f / rhs->data.f, self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_mod(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_int(res, self->data.i % rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_float(res, fmod(self->data.f, rhs->data.f), self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_pow(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_int(res, pow(self->data.i, rhs->data.i), self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_float(res, powf(self->data.f, rhs->data.f), self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_lt(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_bool(res, self->data.i < rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_bool(res, self->data.f < rhs->data.f, self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_le(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_bool(res, self->data.i <= rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_bool(res, self->data.f <= rhs->data.f, self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_gt(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_bool(res, self->data.i > rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_bool(res, self->data.f > rhs->data.f, self->line); + return res; + } + + free(res); + + return NULL; +} + +struct value* value_try_new_ge(struct value* self, struct value* rhs) +{ + assert(self); + assert(rhs); + + struct value* res = malloc(sizeof(struct value)); + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "INT")) + { + value_init_bool(res, self->data.i >= rhs->data.i, self->line); + return res; + } + + if (type_equals(&self->type, &rhs->type) && type_is(&self->type, "FLOAT")) + { + value_init_bool(res, self->data.f >= rhs->data.f, self->line); + return res; + } + + free(res); + + return NULL; +} diff --git a/lang/src/value.h b/lang/src/value.h index f7dbd4e..c3e458e 100644 --- a/lang/src/value.h +++ b/lang/src/value.h @@ -9,6 +9,9 @@ union value_data { int b; + char* s; + float f; + int i; }; struct value { @@ -18,6 +21,10 @@ struct value { }; void value_init_bool(struct value* self, int value, int line); +void value_init_int(struct value* self, int value, int line); +void value_init_float(struct value* self, float value, int line); +void value_init_string(struct value* self, char* value, int line); + void value_free(struct value* self); size_t value_str(struct value* self, char* buffer, size_t size); @@ -26,4 +33,16 @@ 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); +struct value* value_try_new_add(struct value* self, struct value* rhs); +struct value* value_try_new_sub(struct value* self, struct value* rhs); +struct value* value_try_new_mul(struct value* self, struct value* rhs); +struct value* value_try_new_div(struct value* self, struct value* rhs); +struct value* value_try_new_mod(struct value* self, struct value* rhs); +struct value* value_try_new_pow(struct value* self, struct value* rhs); + +struct value* value_try_new_lt(struct value* self, struct value* rhs); +struct value* value_try_new_le(struct value* self, struct value* rhs); +struct value* value_try_new_gt(struct value* self, struct value* rhs); +struct value* value_try_new_ge(struct value* self, struct value* rhs); + #endif diff --git a/lang/tests/lexer.c b/lang/tests/lexer.c index c1ee2bf..423a29b 100644 --- a/lang/tests/lexer.c +++ b/lang/tests/lexer.c @@ -14,7 +14,7 @@ static void test_lexer(char const* source, size_t size, ...) struct lexer lex; lexer_init(&lex, source); int err = lexer_extract(&lex, &tokens); - cr_assert_eq(0, err); + cr_assert_eq(0, err, "%s", lex.error_msg); cr_assert_eq(size, tokens.size, "size (%zu) != tokens.size (%zu)", size, tokens.size); @@ -68,3 +68,42 @@ Test(lexer, comment) { Test(lexer, bool_arith) { test_lexer("&&||!", 3, "AND", "OR", "NOT"); } + +Test(lexer, eqne) { + test_lexer("!= ==", 2, "NE", "EQ"); +} + +Test(lexer, int) { + test_lexer("5 -27", 2, "INT[5]", "INT[-27]"); +} + +Test(lexer, float) { + test_lexer("0.5 -2.7", 2, "FLOAT[0.5]", "FLOAT[-2.7]"); +} + +Test(lexer, string) { + test_lexer("'hello world ' \" bim\" '\\\"hello\\\"'", 3, + "STRING[hello world ]", + "STRING[ bim]", + "STRING[\\\"hello\\\"]"); +} + +Test(lexer, arithemtic) { + test_lexer("+-*/%**", 6, + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW"); + +} + +Test(lexer, comparisons) { + test_lexer(">>=<<=", 4, + "GT", + "GE", + "LT", + "LE"); + +} diff --git a/lang/tests/parser.c b/lang/tests/parser.c index 22b4246..366aebd 100644 --- a/lang/tests/parser.c +++ b/lang/tests/parser.c @@ -67,3 +67,44 @@ Test(parser, bool_arith) { "true && !false || true;"); } + +Test(parser, eqne) { + test_parser("ROOT(EQ(BOOL[true],BOOL[false]))", + "true == false;"); + + test_parser("ROOT(NE(BOOL[true],BOOL[false]))", + "true != false;"); +} + +Test(parser, num) { + test_parser("ROOT(EQ(INT[3],INT[-7]))", + "3 == -7;"); + + test_parser("ROOT(NE(FLOAT[2.9],FLOAT[-3.]))", + "2.9 != -3.;"); + + test_parser("ROOT(NE(STRING[bim],STRING[bam]))", + "'bim' != 'bam';"); +} + +Test(parser, arithmetic) { + test_parser("ROOT(ADD(INT[1],MUL(INT[2],INT[3])))", + "1 + 2 * 3;"); + + test_parser("ROOT(MUL(ADD(INT[1],INT[2]),INT[3]))", + "(1 + 2) * 3;"); + + test_parser("ROOT(SUB(INT[1],DIV(INT[2],INT[3])))", + "1 - 2 / 3;"); + + test_parser("ROOT(DIV(SUB(INT[1],INT[2]),INT[3]))", + "(1 - 2) / 3;"); +} + +Test(parser, cmp) { + test_parser("ROOT(LT(ADD(INT[1],INT[1]),INT[3]))", + "1 + 1 < 3;"); + + test_parser("ROOT(AND(LE(INT[0],INT[2]),GE(FLOAT[5.0],FLOAT[1.0])))", + "0 <= 2 && 5.0 >= 1.0;"); +} diff --git a/lang/tests/type_checker.c b/lang/tests/type_checker.c index cd07058..b91596d 100644 --- a/lang/tests/type_checker.c +++ b/lang/tests/type_checker.c @@ -1,3 +1,4 @@ +#include "commons.h" #include #include #include @@ -14,7 +15,11 @@ static void test_check_ok(char const* source) struct type_checker tc; type_checker_init(&tc); - cr_assert_eq(type_checker_check(&tc, ast), 0); + char buf[GUX_STR_SIZE]; + node_str(ast, buf, GUX_STR_SIZE); + + cr_assert_eq(type_checker_check(&tc, ast), 0, + "type checker failed: %s", buf); type_checker_free(&tc); @@ -29,12 +34,12 @@ static void test_check_ko(char const* source) parser_init(&parser); struct node* ast = parser_try_new_root(&parser, source); - cr_assert_neq(ast, NULL); + cr_assert_neq(ast, NULL, "NULL: %s", source); struct type_checker tc; type_checker_init(&tc); - cr_assert_neq(type_checker_check(&tc, ast), 0); + cr_assert_neq(type_checker_check(&tc, ast), 0, "%s == null", source); type_checker_free(&tc); @@ -48,4 +53,103 @@ Test(type_checker, booleans) { test_check_ok("true && false;"); test_check_ok("true || false;"); test_check_ok("true || !false && false;"); + + test_check_ko("7 && true;"); + test_check_ko("false && -1;"); + test_check_ko("7.0 && true;"); + test_check_ko("false && -1.0;"); + test_check_ko("'true' && true;"); + test_check_ko("false && 'false';"); + + test_check_ko("7 || true;"); + test_check_ko("false || -1;"); + test_check_ko("7.0 || true;"); + test_check_ko("false || -1.0;"); + test_check_ko("'true' || true;"); + test_check_ko("false || 'false';"); + + test_check_ko("!7;"); + test_check_ko("!7.0;"); + test_check_ko("!'true';"); +} + +Test(type_checker, arithmetic) { + test_check_ok("1 + 1;"); + test_check_ok("1.0 + 1.2;"); + test_check_ko("true + false;"); + test_check_ok("'aze' + 'popopo';"); + + test_check_ko("1 - 'salut';"); + test_check_ko("1.2 - 15;"); + test_check_ko("false - 1.2;"); + test_check_ko("\"a\" - true;"); + + test_check_ko("-17 * false;"); + test_check_ko("-2.3 * 'oo';"); + test_check_ko("true * 45;"); + test_check_ko("'1' * 1.02;"); + + test_check_ko("441 / 3.1415;"); + test_check_ko(".9 / false;"); + test_check_ko("true / 'bim';"); + test_check_ko("'aze' / 145;"); + + test_check_ok("33 % 1;"); + test_check_ok("3.3 % 1.2;"); + test_check_ko("false % false;"); + test_check_ko("'' % 'other';"); + + test_check_ok("1 ** -45;"); + test_check_ok("1.2 ** 0.54;"); + test_check_ko("true ** false;"); + test_check_ko("'coucou' ** 'bam';"); +} + +Test(type_checker, cmp) { + test_check_ko("-5 < 3.2;"); + test_check_ko("-5 < true;"); + test_check_ko("-5 < 'salut';"); + test_check_ko("3.2 < 5;"); + test_check_ko("true < 5;"); + test_check_ko("\"5\" < 5;"); + + test_check_ko("-5 <= 3.2;"); + test_check_ko("-5 <= true;"); + test_check_ko("-5 <= 'salut';"); + test_check_ko("3.2 <= 5;"); + test_check_ko("true <= 5;"); + test_check_ko("\"5\" <= 5;"); + + test_check_ko("-5 > 3.2;"); + test_check_ko("-5 > true;"); + test_check_ko("-5 > 'salut';"); + test_check_ko("3.2 > 5;"); + test_check_ko("true > 5;"); + test_check_ko("\"5\" > 5;"); + + test_check_ko("-5 >= 3.2;"); + test_check_ko("-5 >= true;"); + test_check_ko("-5 >= 'salut';"); + test_check_ko("3.2 >= 5;"); + test_check_ko("true >= 5;"); + test_check_ko("\"5\" >= 5;"); + + test_check_ko("true < true;"); + test_check_ko("true <= true;"); + test_check_ko("true > true;"); + test_check_ko("true >= true;"); + + test_check_ko("'hello' < 'hello';"); + test_check_ko("'hello' <= 'hello';"); + test_check_ko("'hello' > 'hello';"); + test_check_ko("'hello' >= 'hello';"); + + test_check_ok("5 > 5;"); + test_check_ok("5.1 > 5.2;"); + test_check_ok("5 >= 5;"); + test_check_ok("5.1 >= 5.2;"); + test_check_ok("5 < 5;"); + test_check_ok("5.1 < 5.2;"); + test_check_ok("5 <= 5;"); + test_check_ok("5.1 <= 5.2;"); } diff --git a/lib/src/commons.h b/lib/src/commons.h index 576c6a7..4287e7c 100644 --- a/lib/src/commons.h +++ b/lib/src/commons.h @@ -1,6 +1,7 @@ #ifndef COMMONS_H #define COMMONS_H +#include #include #include #include diff --git a/lib/src/vec.c b/lib/src/vec.c index 4b31548..1fbc146 100644 --- a/lib/src/vec.c +++ b/lib/src/vec.c @@ -44,16 +44,6 @@ void* vec_pop(struct vec* self) 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); diff --git a/lib/src/vec.h b/lib/src/vec.h index 2737e84..7eb590e 100644 --- a/lib/src/vec.h +++ b/lib/src/vec.h @@ -14,7 +14,6 @@ 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); diff --git a/tests/bool.gux b/tests/bool.gux deleted file mode 100644 index ee93100..0000000 --- a/tests/bool.gux +++ /dev/null @@ -1,14 +0,0 @@ -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); diff --git a/tests/builtins.gux b/tests/builtins.gux new file mode 100644 index 0000000..3346d8e --- /dev/null +++ b/tests/builtins.gux @@ -0,0 +1,68 @@ +############ +# BOOLEANS # +############ +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); + +assert true == true; +assert false == false; +assert true != false; +assert false != true; + +############ +# INTEGERS # +############ +assert 5 == 5; +assert 5 != 2; + +assert 1 + 2 == 3; +assert 1 + -2 == -1; +assert 1 - 2 == -1; +assert 1 - -2 == 3; +assert 3 * 6 == 18; +assert 12 / 2 == 6; +assert 3 / 2 == 1; +assert 12 % 7 == 5; +assert 2 ** 3 == 8; + +assert 1 + 2 * 3 == 7; +assert (1 + 2) * 3 == 9; + +######### +# FLOAT # +######### +assert 2.3 == 2.3; +assert 2.3 != 2.4; + +assert 1.0 + 2.0 == 3.0; +assert 1.0 + -2.0 == -1.0; +assert 1.0 - 2.0 == -1.0; +assert 1.0 - -2.0 == 3.0; +assert 3.0 * 6.0 == 18.0; +assert 12.0 / 2.0 == 6.0; +assert 3.0 / 2.0 == 1.5; +assert 12.0 % 7.0 == 5.0; +assert 2.0 ** 3.0 == 8.0; + +########### +# STRINGS # +########### +assert "hello" == "hello"; +assert "hello" != "world"; +assert 3 * "a" == "aaa"; +assert "a" * 3 == "aaa"; +assert "a" + "b" == "ab"; +assert 2 * "a" + "b" == "aab"; +assert 2 * ("a" + 3 * "b") == "abbbabbb"; \ No newline at end of file diff --git a/tests/cmp.gux b/tests/cmp.gux new file mode 100644 index 0000000..4641bc8 --- /dev/null +++ b/tests/cmp.gux @@ -0,0 +1,25 @@ +assert 5 < 8 == true; +assert 5 < 5 == false; +assert 5 < 4 == false; +assert 5 <= 8 == true; +assert 5 <= 5 == true; +assert 5 <= 4 == false; +assert 14 > 8 == true; +assert 7 > 7 == false; +assert 14 > 32 == false; +assert 14 >= 8 == true; +assert 7 >= 7 == true; +assert 14 >= 32 == false; + +assert 5.0 < 8.0 == true; +assert 5.0 < 5.0 == false; +assert 5.0 < 4.0 == false; +assert 5.0 <= 8.0 == true; +assert 5.0 <= 5.0 == true; +assert 5.0 <= 4.0 == false; +assert 14.0 > 8.0 == true; +assert 7.0 > 7.0 == false; +assert 14.0 > 32.0 == false; +assert 14.0 >= 8.0 == true; +assert 7.0 >= 7.0 == true; +assert 14.0 >= 32.0 == false; diff --git a/vm/src/vm.c b/vm/src/vm.c index 51242e0..f27a33f 100644 --- a/vm/src/vm.c +++ b/vm/src/vm.c @@ -1,5 +1,8 @@ #include "vm.h" #include "commons.h" +#include "program.h" +#include "type.h" +#include "value.h" void vm_init(struct vm* self) { @@ -134,13 +137,190 @@ int vm_exec(struct vm* self, struct program* program) vm_push(self, program_push_constant(program, not)); self->pc++; } break; + + case OP_EQ: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = malloc(sizeof(struct value)); + + if (lhs_val->type.kind == TYPE_BOOL) + { + value_init_bool(res, + lhs_val->data.b == rhs_val->data.b, + lhs_val->line); + } + else if (lhs_val->type.kind == TYPE_INT) + { + value_init_bool(res, + lhs_val->data.i == rhs_val->data.i, + lhs_val->line); + } + else if (lhs_val->type.kind == TYPE_FLOAT) + { + value_init_bool(res, + lhs_val->data.f == rhs_val->data.f, + lhs_val->line); + } + else if (lhs_val->type.kind == TYPE_STRING) + { + value_init_bool(res, + strcmp(lhs_val->data.s, rhs_val->data.s) == 0, + lhs_val->line); + } + else + { + fprintf(stderr, "operation not supported\n"); + return 1; + } + + vm_push(self, program_push_constant(program, res)); + self->pc++; + + } break; + + case OP_ADD: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_add(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_SUB: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_sub(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_MUL: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_mul(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_DIV: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_div(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_MOD: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_mod(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_POW: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_pow(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_LT: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_lt(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_LE: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_le(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_GT: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_gt(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + case OP_GE: { + int rhs = vm_pop(self); + int lhs = vm_pop(self); + + struct value* lhs_val = program->constant_pool.data[lhs]; + struct value* rhs_val = program->constant_pool.data[rhs]; + + struct value* res = value_try_new_ge(lhs_val, rhs_val); + vm_push(self, program_push_constant(program, res)); + + self->pc++; + } break; + + default: assert(0); } } return 0; } -size_t vm_str(struct vm* self, char* buffer, size_t size) +size_t vm_str(struct vm* self, struct program* program, + char* buffer, size_t size) { assert(self); assert(buffer); @@ -154,8 +334,13 @@ size_t vm_str(struct vm* self, char* buffer, size_t size) for (size_t j=0; jsp; j++) { - sz += snprintf(buffer + sz, size - sz, "\t%zu: %d\n", - j, frame->stack[j]); + size_t k = frame->sp - 1 - j; + struct value* v = program->constant_pool.data[frame->stack[k]]; + char val[GUX_STR_SIZE]; + value_str(v, val, GUX_STR_SIZE); + + sz += snprintf(buffer + sz, size - sz, "\t%zu: %d (%s)\n", + k, frame->stack[k], val); } } diff --git a/vm/src/vm.h b/vm/src/vm.h index c70b0eb..5de094b 100644 --- a/vm/src/vm.h +++ b/vm/src/vm.h @@ -30,6 +30,7 @@ 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); +size_t vm_str(struct vm* self, struct program* program, + char* buffer, size_t size); #endif