From 83ff0b57747bdb22f2e49485913a849633448212 Mon Sep 17 00:00:00 2001 From: samsonium Date: Wed, 14 Oct 2026 19:56:27 +0300 Subject: [PATCH] feat: implement conditions --- exl/conditions.fzs | 16 +++ include/common/fzl_opcodes.h | 30 +++-- include/compiler/fzl_parser.h | 21 ++++ include/compiler/fzl_token.h | 26 +++- include/compiler/fzl_walker.h | 3 + src/compiler/fzl_debug.c | 25 ++++ src/compiler/fzl_parser.c | 217 +++++++++++++++++++++++++++++++++- src/compiler/fzl_reader.c | 50 +++++++- src/compiler/fzl_walker.c | 157 +++++++++++++++++++++++- src/vm/fzl_vm.c | 153 +++++++++++++++++++++++- src/vm/fzl_vm_debug.c | 58 ++++++++- 11 files changed, 731 insertions(+), 25 deletions(-) create mode 100644 exl/conditions.fzs diff --git a/exl/conditions.fzs b/exl/conditions.fzs new file mode 100644 index 0000000..4e7dbf4 --- /dev/null +++ b/exl/conditions.fzs @@ -0,0 +1,16 @@ +bool flag = true + +if (!flag) + printline("buhhh, flag is not true") +else if (2 + 2 >= 4) +{ + printline ("two plus two is four") +} +else { + printline("gotcha") +} + +{ + printline("block after") +} + diff --git a/include/common/fzl_opcodes.h b/include/common/fzl_opcodes.h index 4bc136b..2ac85ae 100644 --- a/include/common/fzl_opcodes.h +++ b/include/common/fzl_opcodes.h @@ -7,6 +7,7 @@ typedef enum FZL_OPCODE_LOADCONST = 0xF00001, // Load const into T (temp) reg FZL_OPCODE_CALL_BUILTIN = 0xF00002, // Call builtin function FZL_OPCODE_PUSHNUM = 0xF00003, // Push number into stack + FZL_OPCODE_PUSHBOOL = 0xF00004, // Push boolean into stack FZL_OPCODE_MK_ENVTABLE = 0xF10001, // Allocate new environment table FZL_OPCODE_RM_ENVTABLE = 0xF10002, // Free environment table @@ -14,13 +15,28 @@ typedef enum FZL_OPCODE_LOOKUP_ENV = 0xF10004, // Lookup name in environment table FZL_OPCODE_WRITE = 0xF10005, // Wirte into variable - FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in arg strack - FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in arg stack - FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in arg stack - FZL_OPCODE_DIV = 0xA00004, // Divide two numbers in arg stack - FZL_OPCODE_POW = 0xA00005, // Power first number in arg stack by second - FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in arg stack - FZL_OPCODE_NEG = 0xA00007, // Negotiate number in arg stack + FZL_OPCODE_JMP = 0xF20001, // Jump to specified IP address + FZL_OPCODE_JZ = 0xF20002, // Jump if top value in dstack is zero + + FZL_OPCODE_ADD = 0xA00001, // Sum two numbers in dstack + FZL_OPCODE_SUB = 0xA00002, // Subract two numbers in dstack + FZL_OPCODE_MUL = 0xA00003, // Multiply two numbers in dstack + FZL_OPCODE_DIV = 0xA00004, // Divide two numbers in dstack + FZL_OPCODE_POW = 0xA00005, // Power first number in dstack by second + FZL_OPCODE_MOD = 0xA00006, // Get module of two numbers in dstack + FZL_OPCODE_NEG = 0xA00007, // Negotiate number in dstack + + FZL_OPCODE_CMP_EQ = 0xB00001, // Equal + FZL_OPCODE_CMP_NEQ = 0xB00002, // Not equal + FZL_OPCODE_CMP_LT = 0xB00003, // Less than + FZL_OPCODE_CMP_LTEQ = 0xB00004, // Less than or equal + FZL_OPCODE_CMP_GT = 0xB00005, // Greater than + FZL_OPCODE_CMP_GTEQ = 0xB00006, // Greater than or equal + + FZL_OPCODE_AND = 0xC00001, // A && B + FZL_OPCODE_OR = 0xC00002, // A || B + + FZL_OPCODE_NOT = 0xD00001, // !A } fzl_opcode_t; #endif // FZL_OPCODES_H diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index b8bee88..21b361f 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -12,6 +12,7 @@ typedef enum FZL_NODETYPE_CALL, FZL_NODETYPE_LITERAL, FZL_NODETYPE_IDENTIFIER, + FZL_NODETYPE_CONDITIONAL, FZL_NODETYPE_ADD, FZL_NODETYPE_SUB, @@ -23,6 +24,18 @@ typedef enum FZL_NODETYPE_UNARY_PLUS, FZL_NODETYPE_UNARY_MINUS, + FZL_NODETYPE_OR, + FZL_NODETYPE_AND, + + FZL_NODETYPE_EQUAL, + FZL_NODETYPE_NEQUAL, + FZL_NODETYPE_LESS, + FZL_NODETYPE_LESS_EQUAL, + FZL_NODETYPE_GREATER, + FZL_NODETYPE_GREATER_EQUAL, + + FZL_NODETYPE_NOT, + FZL_NODETYPE_DECLARATION, FZL_NODETYPE_ASSIGN } fzl_nodetype_t; @@ -72,6 +85,14 @@ typedef struct fzl_node fzl_token_t *type; struct fzl_node *identifier; } var; + + // Conditional blocks + struct + { + struct fzl_node *condition; + struct fzl_node *true_block; + struct fzl_node *else_block; + } cond; }; } fzl_node_t; diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index a289fe5..911a76d 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -8,17 +8,35 @@ // Token type typedef enum { + FZL_TOKTYPE_IF, // if + FZL_TOKTYPE_ELSE, // else + FZL_TOKTYPE_NAME, // abc FZL_TOKTYPE_TYPE, // i16 FZL_TOKTYPE_LPAREN, // ( FZL_TOKTYPE_RPAREN, // ) + FZL_TOKTYPE_LBRACE, // { + FZL_TOKTYPE_RBRACE, // } - FZL_TOKTYPE_COMMA, // , - FZL_TOKTYPE_DOT, // . + FZL_TOKTYPE_COMMA, // , + FZL_TOKTYPE_DOT, // . + FZL_TOKTYPE_SEMICOLON, // ; - FZL_TOKTYPE_STR_LITERAL, // "" - FZL_TOKTYPE_NUM_LITERAL, // 123 + FZL_TOKTYPE_STR_LITERAL, // "" + FZL_TOKTYPE_NUM_LITERAL, // 123 + FZL_TOKTYPE_BOOL_LITERAL, // true + + FZL_TOKTYPE_DAMPERSAND, // && + FZL_TOKTYPE_DVBAR, // || + + FZL_TOKTYPE_EQEQ, // == + FZL_TOKTYPE_REQ, // >= + FZL_TOKTYPE_LEQ, // <= + FZL_TOKTYPE_NEQ, // != + FZL_TOKTYPE_LANGLE, // < + FZL_TOKTYPE_RANGLE, // > + FZL_TOKTYPE_NOT, // ! FZL_TOKTYPE_STAR, // * FZL_TOKTYPE_SLASH, // / diff --git a/include/compiler/fzl_walker.h b/include/compiler/fzl_walker.h index e51a34c..d44cb4c 100644 --- a/include/compiler/fzl_walker.h +++ b/include/compiler/fzl_walker.h @@ -30,6 +30,9 @@ typedef struct fzl_symtable_t *syms[64]; size_t sym_top; + + size_t bp_table[64]; + size_t bp_top; } fzl_walker_t; // Allocate new walker diff --git a/src/compiler/fzl_debug.c b/src/compiler/fzl_debug.c index 5df5833..14e5f95 100644 --- a/src/compiler/fzl_debug.c +++ b/src/compiler/fzl_debug.c @@ -63,6 +63,19 @@ void print_node(fzl_node_t *node, int level) printf("%s Literal '%s'\n", levelbuf, (char *)node->token->value); break; + case FZL_NODETYPE_CONDITIONAL: + printf("%s Conditional block:\n", levelbuf); + printf("-%s Condition:\n", levelbuf); + print_node(node->cond.condition, level + 2); + printf("-%s True block:\n", levelbuf); + print_node(node->cond.true_block, level + 2); + if (node->cond.else_block) + { + printf("-%s Else block:\n", levelbuf); + print_node(node->cond.else_block, level + 2); + } + break; + case FZL_NODETYPE_BLOCK: printf("%s Block: ", levelbuf); @@ -77,6 +90,18 @@ void print_node(fzl_node_t *node, int level) print_node(node->list.entries[i], level + 1); break; + + case FZL_NODETYPE_NOT: + printf("%s Inverse:\n", levelbuf); + print_node(node->unary_op.operand, level + 1); + break; + + case FZL_NODETYPE_EQUAL: + case FZL_NODETYPE_NEQUAL: + case FZL_NODETYPE_LESS: + case FZL_NODETYPE_LESS_EQUAL: + case FZL_NODETYPE_GREATER: + case FZL_NODETYPE_GREATER_EQUAL: case FZL_NODETYPE_ADD: case FZL_NODETYPE_SUB: case FZL_NODETYPE_MUL: diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index 9c69b77..373d106 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -120,6 +120,8 @@ static fzl_nodetype_t unary_op_to_type(fzl_toktype_t type) return FZL_NODETYPE_UNARY_PLUS; case FZL_TOKTYPE_MINUS: return FZL_NODETYPE_UNARY_MINUS; + case FZL_TOKTYPE_NOT: + return FZL_NODETYPE_NOT; default: return 0; @@ -144,6 +146,11 @@ fzl_node_t *parse_primary(fzl_parser_t *parser) fzl_token_t *tok = consume(parser); return fzl_mknode(FZL_NODETYPE_LITERAL, tok); } + else if (is(parser, FZL_TOKTYPE_BOOL_LITERAL)) + { + fzl_token_t *tok = consume(parser); + return fzl_mknode(FZL_NODETYPE_LITERAL, tok); + } else if (is(parser, FZL_TOKTYPE_LPAREN)) { consume(parser); @@ -241,7 +248,8 @@ fzl_node_t *parse_unary(fzl_parser_t *parser) if (!parser) return NULL; - if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS)) + if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS) && + !is(parser, FZL_TOKTYPE_NOT)) return parse_postfix(parser); fzl_token_t *op = consume(parser); @@ -372,12 +380,131 @@ fzl_node_t *parse_term(fzl_parser_t *parser) return left; } -// Expression parsing -fzl_node_t *parse_expression(fzl_parser_t *parser) +// Parse conditional operations +fzl_node_t *parse_cond(fzl_parser_t *parser) { - return parse_term(parser); + if (!parser) + return NULL; + + fzl_node_t *left = parse_term(parser); + fzl_node_t *right = NULL; + + if (!left) + return NULL; + + while (is(parser, FZL_TOKTYPE_EQEQ) || is(parser, FZL_TOKTYPE_NEQ) || + is(parser, FZL_TOKTYPE_LANGLE) || is(parser, FZL_TOKTYPE_LEQ) || + is(parser, FZL_TOKTYPE_RANGLE) || is(parser, FZL_TOKTYPE_REQ)) + { + fzl_token_t *op = consume(parser); + right = parse_term(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", + op->ln, op->cn); + return NULL; + } + + fzl_node_t *result; + switch (op->type) + { + case FZL_TOKTYPE_EQEQ: + result = fzl_mknode(FZL_NODETYPE_EQUAL, op); + break; + case FZL_TOKTYPE_NEQ: + result = fzl_mknode(FZL_NODETYPE_NEQUAL, op); + break; + case FZL_TOKTYPE_LANGLE: + result = fzl_mknode(FZL_NODETYPE_LESS, op); + break; + case FZL_TOKTYPE_LEQ: + result = fzl_mknode(FZL_NODETYPE_LESS_EQUAL, op); + break; + case FZL_TOKTYPE_RANGLE: + result = fzl_mknode(FZL_NODETYPE_GREATER, op); + break; + case FZL_TOKTYPE_REQ: + result = fzl_mknode(FZL_NODETYPE_GREATER_EQUAL, op); + break; + default: + return NULL; + } + + result->binary_op.left = left; + result->binary_op.right = right; + left = result; + } + + return left; } +// Parse conditional && +fzl_node_t *parse_and(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_cond(parser); + fzl_node_t *right = NULL; + + if (!left) + return NULL; + + while (is(parser, FZL_TOKTYPE_DAMPERSAND)) + { + fzl_token_t * or = consume(parser); + right = parse_cond(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", + or->ln, or->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_AND, or); + node->binary_op.left = left; + node->binary_op.right = right; + left = node; + } + + return left; +} + +// Parse conditional || +fzl_node_t *parse_or(fzl_parser_t *parser) +{ + if (!parser) + return NULL; + + fzl_node_t *left = parse_and(parser); + fzl_node_t *right = NULL; + + if (!left) + return NULL; + + while (is(parser, FZL_TOKTYPE_DVBAR)) + { + fzl_token_t * or = consume(parser); + right = parse_and(parser); + if (!right) + { + snprintf(plast_err, sizeof(plast_err), "unfinished expression at %ld:%ld", + or->ln, or->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_OR, or); + node->binary_op.left = left; + node->binary_op.right = right; + left = node; + } + + return left; +} + +// Expression parsing +fzl_node_t *parse_expression(fzl_parser_t *parser) { return parse_and(parser); } + // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { @@ -447,6 +574,88 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return left; } + if (is(parser, FZL_TOKTYPE_IF)) + { + fzl_token_t *if_tok = consume(parser); + fzl_token_t *lparen = consume(parser); + if (lparen->type != FZL_TOKTYPE_LPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "expected '(' before condition at %ld:%ld", lparen->ln, + lparen->cn); + return NULL; + } + + fzl_node_t *condition = parse_expression(parser); + if (!condition) + return NULL; + + fzl_token_t *rparen = consume(parser); + if (rparen->type != FZL_TOKTYPE_RPAREN) + { + snprintf(plast_err, sizeof(plast_err), + "expected ')' after condition at %ld:%ld", rparen->ln, + rparen->cn); + return NULL; + } + + fzl_node_t *else_block = NULL; + fzl_node_t *true_block = parse_statement(parser); + if (!true_block) + return NULL; + + if (is(parser, FZL_TOKTYPE_ELSE)) + { + consume(parser); // else + else_block = parse_statement(parser); + if (!else_block) + return NULL; + } + + fzl_node_t *stmt_node = fzl_mknode(FZL_NODETYPE_CONDITIONAL, if_tok); + stmt_node->cond.condition = condition; + stmt_node->cond.true_block = true_block; + stmt_node->cond.else_block = else_block; + + return stmt_node; + } + + if (is(parser, FZL_TOKTYPE_LBRACE)) + { + fzl_token_t *block_tok = consume(parser); + + size_t remainder = 2; + size_t count = 0; + fzl_node_t **stmts = malloc(remainder * sizeof(fzl_node_t *)); + + while (peek(parser) && !is(parser, FZL_TOKTYPE_RBRACE)) + { + fzl_node_t *node = parse_statement(parser); + if (!node) + return NULL; + + if (remainder <= count) + { + remainder *= 2; + stmts = realloc(stmts, remainder); + } + + stmts[count++] = node; + } + fzl_token_t *rbrace = consume(parser); + if (!rbrace || rbrace->type != FZL_TOKTYPE_RBRACE) + { + snprintf(plast_err, sizeof(plast_err), + "missing '}' after block at %ld:%ld", rbrace->ln, rbrace->cn); + return NULL; + } + + fzl_node_t *node = fzl_mknode(FZL_NODETYPE_BLOCK, block_tok); + node->list.entries = stmts; + node->list.entry_count = count; + return node; + } + return parse_expression(parser); } diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index c9d3a1b..91305d1 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -74,12 +74,19 @@ fzl_token_t *parse_name(fzl_reader_t *reader) size_t line = reader->line_n; size_t pos = start - reader->line_start; - if (strcmp(value, "str") == 0 || strcmp(value, "char") == 0 || - strcmp(value, "uint") == 0 || strcmp(value, "int") == 0 || - strcmp(value, "i8") == 0 || strcmp(value, "i16") == 0 || - strcmp(value, "i32") == 0 || strcmp(value, "i64") == 0 || - strcmp(value, "u8") == 0 || strcmp(value, "u16") == 0 || - strcmp(value, "u32") == 0 || strcmp(value, "u64") == 0) + if (strcmp(value, "if") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_IF, line, pos, value, size); + else if (strcmp(value, "else") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_ELSE, line, pos, value, size); + else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0) + tok = fzl_mktoken(FZL_TOKTYPE_BOOL_LITERAL, line, pos, value, size); + else if (strcmp(value, "str") == 0 || strcmp(value, "char") == 0 || + strcmp(value, "bool") == 0 || strcmp(value, "uint") == 0 || + strcmp(value, "int") == 0 || strcmp(value, "i8") == 0 || + strcmp(value, "i16") == 0 || strcmp(value, "i32") == 0 || + strcmp(value, "i64") == 0 || strcmp(value, "u8") == 0 || + strcmp(value, "u16") == 0 || strcmp(value, "u32") == 0 || + strcmp(value, "u64") == 0) tok = fzl_mktoken(FZL_TOKTYPE_TYPE, line, pos, value, size); else tok = fzl_mktoken(FZL_TOKTYPE_NAME, line, pos, value, size); @@ -175,6 +182,13 @@ fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type) return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); } +// Add double-char token +fzl_token_t *add_double(fzl_reader_t *reader, fzl_toktype_t type) +{ + size_t p = move(reader, 2); + return fzl_mktoken(type, reader->line_n, p - reader->line_start, NULL, 0); +} + int fzl_read(fzl_reader_t *reader) { if (reader->position >= reader->size) @@ -194,6 +208,30 @@ int fzl_read(fzl_reader_t *reader) if (curr == '\'' || curr == '"') token = parse_string_literal(reader); + else if (curr == '=' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_EQEQ); + else if (curr == '>' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_REQ); + else if (curr == '<' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_LEQ); + else if (curr == '!' && reader->source[p + 1] == '=') + token = add_double(reader, FZL_TOKTYPE_NEQ); + else if (curr == '|' && reader->source[p + 1] == '|') + token = add_double(reader, FZL_TOKTYPE_DVBAR); + else if (curr == '&' && reader->source[p + 1] == '&') + token = add_double(reader, FZL_TOKTYPE_DAMPERSAND); + else if (curr == '>') + token = add_single(reader, FZL_TOKTYPE_RANGLE); + else if (curr == '<') + token = add_single(reader, FZL_TOKTYPE_LANGLE); + else if (curr == '!') + token = add_single(reader, FZL_TOKTYPE_NOT); + else if (curr == ';') + token = add_single(reader, FZL_TOKTYPE_SEMICOLON); + else if (curr == '{') + token = add_single(reader, FZL_TOKTYPE_LBRACE); + else if (curr == '}') + token = add_single(reader, FZL_TOKTYPE_RBRACE); else if (curr == '(') token = add_single(reader, FZL_TOKTYPE_LPAREN); else if (curr == ')') diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index ddcffc6..8e4054e 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -13,6 +13,9 @@ #include #include +// TODO: implement backpatching struct for jumping on if/else blocks +// TODO: implement conditional binops; conditions + char wlast_err[256]; fzl_walker_t *fzl_mkwalker(fzl_node_t *root) @@ -52,6 +55,9 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root) return NULL; } + memset(walker->bp_table, 0, sizeof(walker->bp_table)); + walker->bp_top = 0; + return walker; } @@ -152,8 +158,139 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { switch (node->type) { - case FZL_NODETYPE_UNARY_PLUS: + case FZL_NODETYPE_NOT: + { + fzl_walk_res_t res = walk(walker, node->unary_op.operand); + if (res.error) + return res; + + push_op(walker, FZL_OPCODE_NOT); + current_res = (fzl_walk_res_t){.type = FZL_TYPE_BOOL, .error = false}; break; + } + + case FZL_NODETYPE_EQUAL: + case FZL_NODETYPE_NEQUAL: + case FZL_NODETYPE_LESS: + case FZL_NODETYPE_LESS_EQUAL: + case FZL_NODETYPE_GREATER: + case FZL_NODETYPE_GREATER_EQUAL: + { + fzl_walk_res_t left = walk(walker, node->binary_op.left); + if (left.error) + return left; + + fzl_walk_res_t right = walk(walker, node->binary_op.right); + if (right.error) + return right; + + if (!is_compatible(left.type, right.type)) + { + snprintf(wlast_err, sizeof(wlast_err), + "incompatible types at %ld:%ld", node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + push_op(walker, FZL_OPCODE_CMP_EQ); + break; + case FZL_NODETYPE_NEQUAL: + push_op(walker, FZL_OPCODE_CMP_NEQ); + break; + case FZL_NODETYPE_LESS: + push_op(walker, FZL_OPCODE_CMP_LT); + break; + case FZL_NODETYPE_LESS_EQUAL: + push_op(walker, FZL_OPCODE_CMP_LTEQ); + break; + case FZL_NODETYPE_GREATER: + push_op(walker, FZL_OPCODE_CMP_GT); + break; + case FZL_NODETYPE_GREATER_EQUAL: + push_op(walker, FZL_OPCODE_CMP_GTEQ); + break; + default: + break; + } + + current_res = (fzl_walk_res_t){.type = left.type, .error = false}; + break; + } + + case FZL_NODETYPE_OR: + case FZL_NODETYPE_AND: + { + fzl_walk_res_t left = walk(walker, node->binary_op.left); + if (left.error) + return left; + + fzl_walk_res_t right = walk(walker, node->binary_op.right); + if (right.error) + return right; + + if (!is_compatible(left.type, right.type)) + { + snprintf(wlast_err, sizeof(wlast_err), + "incompatible types at %ld:%ld", node->token->ln, + node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + if (node->type == FZL_NODETYPE_AND) + push_op(walker, FZL_OPCODE_AND); + else + push_op(walker, FZL_OPCODE_OR); + + current_res = (fzl_walk_res_t){.type = left.type, .error = false}; + break; + } + + case FZL_NODETYPE_CONDITIONAL: + { + fzl_walk_res_t cres = walk(walker, node->cond.condition); + if (cres.error) + return cres; + + push_op(walker, FZL_OPCODE_JZ); + + size_t jz_if = walker->op_count; + push_word(walker, (uint64_t)0); + + fzl_walk_res_t tblock_res = walk(walker, node->cond.true_block); + if (tblock_res.error) + return tblock_res; + + push_op(walker, FZL_OPCODE_JMP); + size_t jmp_else = walker->op_count; + push_word(walker, (uint64_t)0); + + walker->ops[jz_if] = walker->op_count; + + if (node->cond.else_block) + { + fzl_walk_res_t fblock_res = walk(walker, node->cond.else_block); + if (fblock_res.error) + return fblock_res; + } + + walker->ops[jmp_else] = walker->op_count; + + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + break; + } + + case FZL_NODETYPE_UNARY_PLUS: + { + fzl_walk_res_t res = walk(walker, node->unary_op.operand); + if (res.error) + return res; + + current_res = res; + break; + } case FZL_NODETYPE_IDENTIFIER: { @@ -248,6 +385,15 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_word(walker, num); current_res = (fzl_walk_res_t){.type = FZL_TYPE_INT, .error = false}; } + else if (node->token->type == FZL_TOKTYPE_BOOL_LITERAL) + { + fzl_token_t *tok = node->token; + uint64_t val = strcmp(tok->value, "true") == 0 ? 1 : 0; + + push_op(walker, FZL_OPCODE_PUSHBOOL); + push_word(walker, val); + current_res = (fzl_walk_res_t){.type = FZL_TYPE_BOOL, .error = false}; + } break; case FZL_NODETYPE_ADD: @@ -423,6 +569,12 @@ int fzl_walk(fzl_walker_t *walker) fzl_walk_res_t result = walk(walker, walker->root); push_op(walker, FZL_OPCODE_RM_ENVTABLE); + if (walker->bp_top > 0) + { + snprintf(wlast_err, sizeof(wlast_err), "unfinished conditional block"); + return -1; + } + if (result.error && !wlast_err[0]) snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); @@ -443,5 +595,8 @@ void fzl_freewalker(fzl_walker_t *walker) if (walker->ops) free(walker->ops); + while (walker->sym_top > 0) + fzl_freesymt(walker->syms[--walker->sym_top]); + free(walker); } diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 480dd14..c762ac4 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -123,6 +123,149 @@ int fzl_vm_run(fzl_vm_t *vm) switch (opcode) { + case FZL_OPCODE_CMP_EQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t leq = vm->dstack[--vm->dtop]; + fzl_value_t req = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = leq.as.b == req.as.b}; + break; + + case FZL_OPCODE_CMP_NEQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lneq = vm->dstack[--vm->dtop]; + fzl_value_t rneq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, + .as.b = lneq.as.u != rneq.as.u}; + break; + + case FZL_OPCODE_CMP_LT: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t llt = vm->dstack[--vm->dtop]; + fzl_value_t rlt = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llt.as.u < rlt.as.u}; + break; + + case FZL_OPCODE_CMP_LTEQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t llteq = vm->dstack[--vm->dtop]; + fzl_value_t rlteq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_BOOL, .as.b = llteq.as.u <= rlteq.as.u}; + break; + + case FZL_OPCODE_CMP_GT: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lgt = vm->dstack[--vm->dtop]; + fzl_value_t rgt = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lgt.as.u > rgt.as.u}; + break; + + case FZL_OPCODE_CMP_GTEQ: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lgteq = vm->dstack[--vm->dtop]; + fzl_value_t rgteq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_BOOL, .as.b = lgteq.as.u >= rgteq.as.u}; + break; + + case FZL_OPCODE_NOT: + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + return -1; + } + + fzl_value_t notval = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = !notval.as.b}; + break; + + case FZL_OPCODE_JZ: + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + return -1; + } + + fzl_value_t jzval = vm->dstack[--vm->dtop]; + uint64_t jzip = vm->ops[++vm->ip] - 1; + if (jzval.as.b == 0) + vm->ip = jzip; + break; + + case FZL_OPCODE_JMP: + vm->ip = vm->ops[++vm->ip] - 1; + break; + + case FZL_OPCODE_AND: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t land = vm->dstack[--vm->dtop]; + fzl_value_t rand = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, + .as.b = land.as.b && rand.as.b}; + break; + + case FZL_OPCODE_OR: + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + return -1; + } + + fzl_value_t lor = vm->dstack[--vm->dtop]; + fzl_value_t ror = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lor.as.b || ror.as.b}; + break; + case FZL_OPCODE_STORE: if (vm->dtop < 1) { @@ -350,8 +493,16 @@ int fzl_vm_run(fzl_vm_t *vm) uint64_t push_n = vm->ops[++vm->ip]; vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_UINT, .as.u = push_n}; + break; + } + + case FZL_OPCODE_PUSHBOOL: + { + uint64_t push_b = vm->ops[++vm->ip]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = push_b}; + break; } - break; default: snprintf(last_err, sizeof(last_err), diff --git a/src/vm/fzl_vm_debug.c b/src/vm/fzl_vm_debug.c index e22ebd1..c53fd19 100644 --- a/src/vm/fzl_vm_debug.c +++ b/src/vm/fzl_vm_debug.c @@ -1,4 +1,5 @@ #include "common/fzl_opcodes.h" +#include #include #include @@ -15,11 +16,17 @@ void fzl_vm_print_ops(fzl_vm_t *vm) for (size_t i = 0; i < vm->ops_count; i++) { + printf("%04ld: ", i); + switch (vm->ops[i]) { case FZL_OPCODE_LOADCONST: - printf("LOADCONST #%lu\n", vm->ops[++i]); + { + size_t const_n = vm->ops[++i]; + fzl_const_t c = vm->consts[const_n]; + printf("LOADCONST #%lu ('%s')\n", const_n, c.as.string.chars); break; + } case FZL_OPCODE_CALL_BUILTIN: printf("CALL_BUILTIN #%lu\n", vm->ops[++i]); @@ -29,6 +36,10 @@ void fzl_vm_print_ops(fzl_vm_t *vm) printf("PUSHNUM %lu\n", vm->ops[++i]); break; + case FZL_OPCODE_PUSHBOOL: + printf("PUSHBOOL %s\n", vm->ops[++i] ? "true" : "false"); + break; + case FZL_OPCODE_MK_ENVTABLE: printf("MK_ENVTABLE\n"); break; @@ -58,12 +69,55 @@ void fzl_vm_print_ops(fzl_vm_t *vm) printf("Math opcode\n"); break; + case FZL_OPCODE_AND: + printf("AND\n"); + break; + + case FZL_OPCODE_OR: + printf("OR\n"); + break; + + case FZL_OPCODE_CMP_EQ: + printf("CMP_EQ\n"); + + case FZL_OPCODE_CMP_NEQ: + printf("CMP_NEQ\n"); + break; + + case FZL_OPCODE_CMP_LT: + printf("CMP_LT\n"); + break; + + case FZL_OPCODE_CMP_LTEQ: + printf("CMP_LTEQ\n"); + break; + + case FZL_OPCODE_CMP_GT: + printf("CMP_GT\n"); + break; + + case FZL_OPCODE_CMP_GTEQ: + printf("CMP_GTEQ\n"); + break; + + case FZL_OPCODE_NOT: + printf("CMP_NOT\n"); + break; + case FZL_OPCODE_NEG: printf("NEG\n"); break; + case FZL_OPCODE_JZ: + printf("JZ IP: %ld\n", vm->ops[++i]); + break; + + case FZL_OPCODE_JMP: + printf("JMP IP %ld\n", vm->ops[++i]); + break; + default: - printf("Unknown"); + printf("\n"); } } }