From 5cb141e4cf50be55b41acf3521e203b6a4eeb758 Mon Sep 17 00:00:00 2001 From: bog Date: Mon, 1 Apr 2024 21:08:42 +0200 Subject: [PATCH] :sparkles: float arithmetic. --- doc/grammar.bnf | 1 + features/float.sk | 10 ++ lib/include/lexer.h | 1 + lib/include/node.h | 2 +- lib/include/state.h | 5 + lib/include/token.h | 2 +- lib/include/value.h | 3 +- lib/src/compiler.c | 7 ++ lib/src/exec.c | 8 ++ lib/src/lexer.c | 59 ++++++++++ lib/src/parser.c | 46 +++++++- lib/src/state.c | 266 ++++++++++++++++++++++++++++++++++++-------- lib/src/value.c | 4 + tests/lexer.h | 12 ++ 14 files changed, 372 insertions(+), 54 deletions(-) create mode 100644 features/float.sk diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 12db630..6224dc6 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -16,3 +16,4 @@ LITERAL ::= BUILTIN ::= | int | bool +| float diff --git a/features/float.sk b/features/float.sk new file mode 100644 index 0000000..794b763 --- /dev/null +++ b/features/float.sk @@ -0,0 +1,10 @@ +assert 6.200 eq 6.2 +assert 5.2 + 1.2 eq 6.4 +assert 5.2 - 1.2 eq 4.0 +assert 5.2 * 1.2 eq 6.24 +assert 5.2 / 2.0 eq 2.6 +assert 12.0 % 2.5 eq 2.0 +assert 25.0 ^ 0.5 eq 5.0 + +assert 1 + 1.0 eq 2.0 +assert 3.0 * 2 eq 6.0 diff --git a/lib/include/lexer.h b/lib/include/lexer.h index 815ba80..b478b5e 100644 --- a/lib/include/lexer.h +++ b/lib/include/lexer.h @@ -30,6 +30,7 @@ bool lexer_next_is(struct lexer* self, TokenKind kind); void lexer_consume_next(struct lexer* self); struct token* lexer_try_new_next(struct lexer* self); struct token* lexer_try_scan_int(struct lexer* self); +struct token* lexer_try_scan_float(struct lexer* self); struct token* lexer_try_scan_text(struct lexer* self, char const* text, TokenKind kind); diff --git a/lib/include/node.h b/lib/include/node.h index c01263c..96ccd12 100644 --- a/lib/include/node.h +++ b/lib/include/node.h @@ -10,7 +10,7 @@ G(NODE_ADD), G(NODE_SUB), G(NODE_MUL),\ G(NODE_DIV), G(NODE_POW), G(NODE_MOD),\ G(NODE_USUB), G(NODE_ASSERT_EQ), \ G(NODE_BOOL), G(NODE_AND), G(NODE_OR), \ -G(NODE_NOT) +G(NODE_NOT), G(NODE_FLOAT) SK_ENUM_H(NodeKind, NODE_KIND); diff --git a/lib/include/state.h b/lib/include/state.h index 0dfa780..0bf9949 100644 --- a/lib/include/state.h +++ b/lib/include/state.h @@ -51,6 +51,11 @@ SK state_push(struct state* self, SK state_push_int(struct state* self, int integer, int line); SK state_push_bool(struct state* self, bool boolean, int line); +SK state_push_float(struct state* self, double real, int line); + +TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs); +double state_as_real(struct state* self, SK lhs); +int state_line(struct state* self, SK lhs); SK state_add(struct state* self); SK state_sub(struct state* self); diff --git a/lib/include/token.h b/lib/include/token.h index ca50d2e..9923658 100644 --- a/lib/include/token.h +++ b/lib/include/token.h @@ -11,7 +11,7 @@ G(TOKEN_MUL), G(TOKEN_DIV), G(TOKEN_MOD), \ G(TOKEN_POW), G(TOKEN_OPAR), G(TOKEN_CPAR), \ G(TOKEN_ASSERT), G(TOKEN_ASSERT_EQ), \ G(TOKEN_BOOL), G(TOKEN_AND), G(TOKEN_OR), \ -G(TOKEN_NOT) +G(TOKEN_NOT), G(TOKEN_FLOAT) SK_ENUM_H(TokenKind, TOKEN_KIND); diff --git a/lib/include/value.h b/lib/include/value.h index 150d3ce..322f248 100644 --- a/lib/include/value.h +++ b/lib/include/value.h @@ -5,13 +5,14 @@ #include "node.h" #define TYPE_KIND(G) \ -G(TYPE_INT), G(TYPE_BOOL) +G(TYPE_INT), G(TYPE_BOOL), G(TYPE_FLOAT) SK_ENUM_H(TypeKind, TYPE_KIND); union val { int integer; + double real; bool boolean; }; diff --git a/lib/src/compiler.c b/lib/src/compiler.c index 851e270..9a05d63 100644 --- a/lib/src/compiler.c +++ b/lib/src/compiler.c @@ -62,6 +62,13 @@ void compiler_compile(struct compiler* self, val); } break; + case NODE_FLOAT: { + union val val; + val.real = atof(node->token->value); + compiler_compile_value(self, node, prog, TYPE_FLOAT, + val); + } break; + case NODE_ADD: { compiler_compile_children(self, node, prog); prog_add_instr(prog, OP_ADD, SK_NO_PARAM); diff --git a/lib/src/exec.c b/lib/src/exec.c index fd676b2..d4342e7 100644 --- a/lib/src/exec.c +++ b/lib/src/exec.c @@ -74,6 +74,14 @@ void exec_execute(struct exec* self, ); } break; + case TYPE_FLOAT: { + state_push_float( + state, + constant->val.real, + constant->line + ); + } break; + case TYPE_BOOL: { state_push_bool( state, diff --git a/lib/src/lexer.c b/lib/src/lexer.c index 1931ece..73fe99c 100644 --- a/lib/src/lexer.c +++ b/lib/src/lexer.c @@ -133,6 +133,11 @@ struct token* lexer_try_new_next(struct lexer* self) struct token* tok = NULL; + if ( (tok=lexer_try_scan_float(self)) ) + { + return tok; + } + if ( (tok=lexer_try_scan_int(self)) ) { return tok; @@ -212,6 +217,60 @@ struct token* lexer_try_scan_int(struct lexer* self) return tok; } +struct token* lexer_try_scan_float(struct lexer* self) +{ + assert(self); + size_t cursor = self->context.cursor; + struct str value; + str_init(&value); + + if (cursor < self->len + && self->source[cursor] == '-') + { + str_push(&value, self->source[cursor]); + cursor++; + } + + while (cursor < self->len + && isdigit(self->source[cursor])) + { + str_push(&value, self->source[cursor]); + cursor++; + } + + if (cursor >= self->len + || self->source[cursor] != '.') + { + str_free(&value); + return NULL; + } + + str_push(&value, self->source[cursor]); + cursor++; + + while (cursor < self->len + && isdigit(self->source[cursor])) + { + str_push(&value, self->source[cursor]); + cursor++; + } + + struct token* tok = NULL; + + if (value.size > 0 + && (value.value[0] != '-' || value.size > 1)) + { + tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_FLOAT, + value.value, self->context.line); + + self->context.cursor = cursor; + } + + str_free(&value); + return tok; +} + struct token* lexer_try_scan_text(struct lexer* self, char const* text, TokenKind kind) diff --git a/lib/src/parser.c b/lib/src/parser.c index 230b951..37fd1a0 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -50,18 +50,40 @@ struct node* parser_try_root(struct parser* self) node_init(node, NODE_ROOT, tok); + struct str error_str; + str_init(&error_str); + while (!lexer_is_at_end(&self->lexer)) { struct node* expr = SK_TRY(parser_try_expr); + if (!expr) + { + while (self->lexer.context.cursor < self->lexer.len + && !lexer_is_sep(&self->lexer, + self->lexer.context.cursor)) + { + str_push(&error_str, + self->lexer.source[ + self->lexer.context.cursor + ]); + self->lexer.context.cursor++; + } + + break; + } + node_push_new_child(node, expr); } if (self->lexer.context.cursor < self->lexer.len) { - errors_push(self->lexer.context.line, "unexpected end"); + errors_push(self->lexer.context.line, + "unexpected end near '%s'", + error_str.value); } + str_free(&error_str); return node; } @@ -87,12 +109,19 @@ struct node* parser_try_assert(struct parser* self) struct token* tok = lexer_try_new_next(&self->lexer); struct node* lhs = SK_TRY(parser_try_expr); - if (!lhs) { return NULL; } + if (!lhs) + { + token_free(tok); free(tok); + return NULL; + } if (!lexer_next_is(&self->lexer, TOKEN_ASSERT_EQ)) { token_free(tok); free(tok); + + node_free(lhs); + free(lhs); return NULL; } @@ -102,6 +131,8 @@ struct node* parser_try_assert(struct parser* self) { token_free(tok); free(tok); + node_free(lhs); + free(lhs); return NULL; } @@ -398,5 +429,16 @@ struct node* parser_try_builtin(struct parser* self) return node; } + if (lexer_next_is(&self->lexer, TOKEN_FLOAT)) + { + struct node* node = malloc(sizeof(struct node)); + + node_init(node, + NODE_FLOAT, + lexer_try_new_next(&self->lexer)); + + return node; + } + return NULL; } diff --git a/lib/src/state.c b/lib/src/state.c index e911ab5..662e280 100644 --- a/lib/src/state.c +++ b/lib/src/state.c @@ -146,19 +146,86 @@ SK state_push_bool(struct state* self, bool boolean, int line) return state_push(self, TYPE_BOOL, val, line); } +SK state_push_float(struct state* self, double real, int line) +{ + assert(self); + union val val; + val.real = real; + return state_push(self, TYPE_FLOAT, val ,line); +} + +TypeKind state_common_num_type(struct state* self, SK lhs, SK rhs) +{ + assert(self); + + struct value const* left = state_try_get_value(self, lhs); + struct value const* right = state_try_get_value(self, rhs); + + if (left->type == TYPE_FLOAT + || right->type == TYPE_FLOAT) + { + return TYPE_FLOAT; + } + + return TYPE_INT; +} + +double state_as_real(struct state* self, SK lhs) +{ + assert(self); + + struct value* value = state_try_get_value(self, lhs); + + if (value->type == TYPE_INT) + { + return value->val.integer; + } + else if (value->type == TYPE_FLOAT) + { + return value->val.real; + } + + errors_push(value->line, "expected a number, got '%s'", + TypeKindStr[value->type] + strlen("TYPE_")); + return 0.0; +} + +int state_line(struct state* self, SK lhs) +{ + assert(self); + struct value const* value = state_try_get_value(self, lhs); + return value->line; +} + SK state_add(struct state* self) { SK rhs = state_pop(self); SK lhs = state_pop(self); - struct value* left = state_try_get_value(self, lhs); - struct value* right = state_try_get_value(self, rhs); + TypeKind type = state_common_num_type(self, lhs, rhs); - return state_push_int( - self, - left->val.integer + right->val.integer, - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + state_as_real(self, lhs) + state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + state_as_real(self, lhs) + state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot addition"); + return 0; + } } SK state_sub(struct state* self) @@ -166,27 +233,60 @@ SK state_sub(struct state* self) SK rhs = state_pop(self); SK lhs = state_pop(self); - struct value* left = state_try_get_value(self, lhs); - struct value* right = state_try_get_value(self, rhs); + TypeKind type = state_common_num_type(self, lhs, rhs); - return state_push_int( - self, - left->val.integer - right->val.integer, - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + state_as_real(self, lhs) - state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + state_as_real(self, lhs) - state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot substract"); + return 0; + } } SK state_usub(struct state* self) { SK lhs = state_pop(self); + struct value const* value = state_try_get_value(self, lhs); + TypeKind type = value->type; - struct value* left = state_try_get_value(self, lhs); - - return state_push_int( - self, - -left->val.integer, - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + -state_as_real(self, lhs), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + -state_as_real(self, lhs), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot apply unary substraction"); + return 0; + } } SK state_mul(struct state* self) @@ -194,14 +294,30 @@ SK state_mul(struct state* self) SK rhs = state_pop(self); SK lhs = state_pop(self); - struct value* left = state_try_get_value(self, lhs); - struct value* right = state_try_get_value(self, rhs); + TypeKind type = state_common_num_type(self, lhs, rhs); - return state_push_int( - self, - left->val.integer * right->val.integer, - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + state_as_real(self, lhs) * state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + state_as_real(self, lhs) * state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot multiply"); + return 0; + } } SK state_div(struct state* self) @@ -209,14 +325,30 @@ SK state_div(struct state* self) SK rhs = state_pop(self); SK lhs = state_pop(self); - struct value* left = state_try_get_value(self, lhs); - struct value* right = state_try_get_value(self, rhs); + TypeKind type = state_common_num_type(self, lhs, rhs); - return state_push_int( - self, - left->val.integer / right->val.integer, - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + state_as_real(self, lhs) / state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + state_as_real(self, lhs) / state_as_real(self, rhs), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot divide"); + return 0; + } } SK state_mod(struct state* self) @@ -224,14 +356,32 @@ SK state_mod(struct state* self) SK rhs = state_pop(self); SK lhs = state_pop(self); - struct value* left = state_try_get_value(self, lhs); - struct value* right = state_try_get_value(self, rhs); + TypeKind type = state_common_num_type(self, lhs, rhs); - return state_push_int( - self, - fmod(left->val.integer, right->val.integer), - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + fmod(state_as_real(self, lhs), + state_as_real(self, rhs)), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + fmod(state_as_real(self, lhs), + state_as_real(self, rhs)), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot apply mod"); + return 0; + } } SK state_pow(struct state* self) @@ -239,14 +389,32 @@ SK state_pow(struct state* self) SK rhs = state_pop(self); SK lhs = state_pop(self); - struct value* left = state_try_get_value(self, lhs); - struct value* right = state_try_get_value(self, rhs); + TypeKind type = state_common_num_type(self, lhs, rhs); - return state_push_int( - self, - powf(left->val.integer, right->val.integer), - left->line - ); + if (type == TYPE_INT) + { + return state_push_int( + self, + powf(state_as_real(self, lhs), + state_as_real(self, rhs)), + state_line(self, lhs) + ); + } + else if (type == TYPE_FLOAT) + { + return state_push_float( + self, + powf(state_as_real(self, lhs), + state_as_real(self, rhs)), + state_line(self, lhs) + ); + } + else + { + errors_push(state_line(self, lhs), + "cannot apply pow"); + return 0; + } } SK state_and(struct state* self) diff --git a/lib/src/value.c b/lib/src/value.c index bc4406b..5c9af91 100644 --- a/lib/src/value.c +++ b/lib/src/value.c @@ -47,6 +47,10 @@ void value_str(struct value* self, struct str* dest) str_format(dest, "%d", self->val.integer); } break; + case TYPE_FLOAT: { + str_format(dest, "%f", self->val.real); + } break; + case TYPE_BOOL: { str_format(dest, "%s", self->val.boolean ? "true" : "false"); diff --git a/tests/lexer.h b/tests/lexer.h index a6c42b0..9eb6709 100644 --- a/tests/lexer.h +++ b/tests/lexer.h @@ -70,12 +70,24 @@ static void test_lexer_bool() "NOT" ); } + +static void test_lexer_float() +{ + test_lexer(".4 7. 2.3 -6.12", 4, + "FLOAT[.4]", + "FLOAT[7.]", + "FLOAT[2.3]", + "FLOAT[-6.12]" + ); +} + void register_lexer() { CU_pSuite suite = CU_add_suite("Lexer", 0, 0); CU_add_test(suite, "Integers", test_lexer_int); CU_add_test(suite, "Assertions", test_lexer_assert); CU_add_test(suite, "Booleans", test_lexer_bool); + CU_add_test(suite, "Floats", test_lexer_float); } #endif