diff --git a/include/compiler/fzl_parser.h b/include/compiler/fzl_parser.h index 08d20ea..83c39da 100644 --- a/include/compiler/fzl_parser.h +++ b/include/compiler/fzl_parser.h @@ -19,6 +19,7 @@ typedef enum FZL_NODETYPE_FIELD, FZL_NODETYPE_CONSTRUCT, FZL_NODETYPE_LOOP, + FZL_NODETYPE_BREAK, FZL_NODETYPE_ADD, FZL_NODETYPE_SUB, diff --git a/include/compiler/fzl_token.h b/include/compiler/fzl_token.h index 37967bd..db85fb0 100644 --- a/include/compiler/fzl_token.h +++ b/include/compiler/fzl_token.h @@ -16,6 +16,7 @@ typedef enum FZL_TOKTYPE_FOR, // for FZL_TOKTYPE_WHILE, // while + FZL_TOKTYPE_BREAK, // break FZL_TOKTYPE_IF, // if FZL_TOKTYPE_ELSE, // else diff --git a/src/compiler/checkers/fzl_loop_check.c b/src/compiler/checkers/fzl_loop_check.c index ab58bc4..8e3966d 100644 --- a/src/compiler/checkers/fzl_loop_check.c +++ b/src/compiler/checkers/fzl_loop_check.c @@ -20,6 +20,7 @@ void ghost_walk_changed(fzl_node_t *node, bool *changed, fzl_sym_t *sym) switch (node->type) { + case FZL_NODETYPE_BREAK: case FZL_NODETYPE_DECLARATION: case FZL_NODETYPE_CONSTRUCT: case FZL_NODETYPE_CLASS: @@ -134,6 +135,7 @@ bool retrieve_cond_sym(fzl_walker_t *walker, fzl_node_t *node, fzl_sym_t *sym) switch (node->type) { + case FZL_NODETYPE_BREAK: case FZL_NODETYPE_BLOCK: case FZL_NODETYPE_CALL: case FZL_NODETYPE_LITERAL: @@ -202,13 +204,6 @@ bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t start_ip, bool sym_exists = retrieve_cond_sym(walker, node->loop.cond, &sym); if (sym_exists) { - // fzl_sym_t sym; - // bool found = false; - // for (ssize_t i = walker->sym_top - 1; i >= 0; i--) - // if ((found = fzl_symt_get(walker->syms[i], - // (char *)declared->as.string.chars, &sym))) - // break; - if (sym.type >= FZL_TYPE_INT8) { bool value_changed = value_is_changed(node, &sym); diff --git a/src/compiler/fzl_parser.c b/src/compiler/fzl_parser.c index afcedac..010bc03 100644 --- a/src/compiler/fzl_parser.c +++ b/src/compiler/fzl_parser.c @@ -1198,6 +1198,13 @@ fzl_node_t *parse_loop(fzl_parser_t *parser) return NULL; } +// `break` keyword parsing +fzl_node_t *parse_break(fzl_parser_t *parser) +{ + fzl_token_t *tok = consume(parser); + return fzl_mknode(FZL_NODETYPE_BREAK, tok); +} + // Statement parsing fzl_node_t *parse_statement(fzl_parser_t *parser) { @@ -1218,6 +1225,8 @@ fzl_node_t *parse_statement(fzl_parser_t *parser) return parse_block(parser); else if (is(parser, FZL_TOKTYPE_FOR) || is(parser, FZL_TOKTYPE_WHILE)) return parse_loop(parser); + else if (is(parser, FZL_TOKTYPE_BREAK)) + res = parse_break(parser); if (!res) res = parse_expression(parser); @@ -1303,6 +1312,7 @@ void fzl_freeast(fzl_node_t *root) { switch (root->type) { + case FZL_NODETYPE_BREAK: case FZL_NODETYPE_IDENTIFIER: case FZL_NODETYPE_LITERAL: break; diff --git a/src/compiler/fzl_reader.c b/src/compiler/fzl_reader.c index dc37a24..6d6db33 100644 --- a/src/compiler/fzl_reader.c +++ b/src/compiler/fzl_reader.c @@ -78,6 +78,9 @@ fzl_token_t *parse_name(fzl_reader_t *reader) else if (strcmp(value, "while") == 0) tok = fzl_mktoken(FZL_TOKTYPE_WHILE, l.line, l.col, value, size, l.filename); + else if (strcmp(value, "break") == 0) + tok = + fzl_mktoken(FZL_TOKTYPE_BREAK, l.line, l.col, value, size, l.filename); else if (strcmp(value, "new") == 0) tok = fzl_mktoken(FZL_TOKTYPE_NEW, l.line, l.col, value, size, l.filename); else if (strcmp(value, "fn") == 0) diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index 021bb53..0f4de9c 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -203,9 +203,45 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { switch (node->type) { + case FZL_NODETYPE_BREAK: + { + fzl_walk_ctx_t *ctx = walker->ctx; + while (ctx) + { + if (ctx->type == FZL_WALK_CTX_TYPE_LOOP) + break; + + ctx = ctx->parent; + } + + if (!ctx || ctx->type != FZL_WALK_CTX_TYPE_LOOP) + { + snprintf(wlast_err, sizeof(wlast_err), + "'%s': usage of 'break' outsude the loop at %ld:%ld", + node->token->filename, node->token->ln, node->token->cn); + return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; + } + + for (size_t i = walker->sym_top - 1; i > ctx->initial_env_lvl + 1; i--) + push_op(walker, FZL_OPCODE_RM_ENVTABLE); + + if (ctx->breaks_remainder <= ctx->breaks_count) + { + ctx->breaks_remainder *= 2; + ctx->breaks = + realloc(ctx->breaks, ctx->breaks_remainder * sizeof(size_t)); + } + + push_op(walker, FZL_OPCODE_JMP); + ctx->breaks[ctx->breaks_count++] = walker->op_count; + push_word(walker, 0); + + current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; + break; + } + // NOTE: add new levels for infinite loop check: - // 1. Dead condition (f.e. `while (i < 5)` w/o `i` increment) - // 2. Step direction (f.e. `for (; i > 5; i = i - 1)`) + // 1. Step direction (f.e. `for (; i > 5; i = i - 1)`) case FZL_NODETYPE_LOOP: { fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_LOOP); @@ -273,6 +309,11 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) push_op(walker, FZL_OPCODE_JMP); push_word(walker, start_ip); + + // backpatch `break`s + for (size_t i = 0; i < newctx->breaks_count; i++) + walker->ops[newctx->breaks[i]] = walker->op_count; + push_op(walker, FZL_OPCODE_RM_ENVTABLE); bool is_infinite = @@ -289,10 +330,6 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) for (size_t i = walker->sym_top - 1; i > newctx->initial_env_lvl; i--) fzl_freesymt(walker->syms[--walker->sym_top]); - // backpatch `break`s - for (size_t i = 0; i < newctx->breaks_count; i++) - walker->ops[newctx->breaks[i]] = walker->op_count - 1; - free(newctx->breaks); if (!fin_ctx(walker)) {