#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char wlast_err[256]; fzl_walker_t *fzl_mkwalker(fzl_node_t *root) { if (!root) { snprintf(wlast_err, sizeof(wlast_err), "root node is null"); return NULL; } fzl_walker_t *walker = malloc(sizeof(fzl_walker_t)); if (!walker) { snprintf(wlast_err, sizeof(wlast_err), "failed to allocated walker"); return NULL; } fzl_const_hash_pool_t *pool = fzl_mkconstpool(); if (!pool) { snprintf(wlast_err, sizeof(wlast_err), "failed to create constants pool"); return NULL; } walker->root = root; walker->consts = pool; walker->op_count = 0; walker->op_remainder = 2; walker->ops = malloc(walker->op_remainder * sizeof(uint64_t)); walker->sym_top = 0; walker->syms[walker->sym_top++] = fzl_mksymt(); if (!walker->syms[walker->sym_top - 1]) { snprintf(wlast_err, sizeof(wlast_err), "failed to create global symtable"); return NULL; } walker->ctx = NULL; walker->classes_count = 0; memset(walker->classes, 0, sizeof(walker->classes)); return walker; } // --- Walking zone --- // Convert type token to `fzl_type_t` const fzl_type_t tok_to_type(fzl_token_t *tok) { if (strcmp((char *)tok->value, "i8") == 0) return FZL_TYPE_INT8; if (strcmp((char *)tok->value, "i16") == 0) return FZL_TYPE_INT16; if (strcmp((char *)tok->value, "i32") == 0) return FZL_TYPE_INT32; if (strcmp((char *)tok->value, "i64") == 0) return FZL_TYPE_INT64; if (strcmp((char *)tok->value, "u8") == 0) return FZL_TYPE_UINT8; if (strcmp((char *)tok->value, "u16") == 0) return FZL_TYPE_UINT16; if (strcmp((char *)tok->value, "u32") == 0) return FZL_TYPE_UINT32; if (strcmp((char *)tok->value, "u64") == 0) return FZL_TYPE_UINT64; if (strcmp((char *)tok->value, "int") == 0) return FZL_TYPE_INT; if (strcmp((char *)tok->value, "uint") == 0) return FZL_TYPE_UINT; if (strcmp((char *)tok->value, "bool") == 0) return FZL_TYPE_BOOL; if (strcmp((char *)tok->value, "str") == 0) return FZL_TYPE_STRING; if (strcmp((char *)tok->value, "char") == 0) return FZL_TYPE_CHAR; return FZL_TYPE_NULL; } static bool is_compatible(fzl_type_t t1, fzl_type_t t2) { if (t1 == t2) return true; return t1 >= FZL_TYPE_INT8 && t2 >= FZL_TYPE_INT8; } // Add one layer of ctx fzl_walk_ctx_t *nest_ctx(fzl_walker_t *walker, fzl_walk_ctx_type_t type) { if (!walker) return NULL; fzl_walk_ctx_t *newctx = malloc(sizeof(fzl_walk_ctx_t)); if (!newctx) return NULL; newctx->type = type; newctx->parent = walker->ctx; walker->ctx = newctx; return walker->ctx; } // Go outside current ctx and then clean it fzl_walk_ctx_t *fin_ctx(fzl_walker_t *walker) { if (!walker) return NULL; fzl_walk_ctx_t *curr = walker->ctx; walker->ctx = walker->ctx->parent; free(curr); return walker->ctx; } // Add const to consts table size_t add_const(fzl_walker_t *walker, fzl_const_t c) { if (!walker) return false; return fzl_consts_pool_add(walker->consts, c); } // Resize ops if size is equals to capacity void ops_resize_if_needed(fzl_walker_t *walker) { if (!walker) return; if (walker->op_count >= walker->op_remainder) { walker->op_remainder *= 2; walker->ops = realloc(walker->ops, walker->op_remainder * sizeof(uint64_t)); } } // Push opcode to operations array void push_op(fzl_walker_t *walker, fzl_opcode_t opcode) { if (!walker) return; ops_resize_if_needed(walker); walker->ops[walker->op_count++] = (uint64_t)opcode; } // Push 64-bit integer to operations array void push_word(fzl_walker_t *walker, uint64_t word) { if (!walker) return; ops_resize_if_needed(walker); walker->ops[walker->op_count++] = word; } fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node) { if (!walker || !node) return (fzl_walk_res_t){.error = true, .error_msg = "walker is null"}; fzl_walk_res_t current_res; while (node != NULL) { switch (node->type) { case FZL_NODETYPE_CLASS: { fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_CLASS); if (!newctx) { snprintf(wlast_err, sizeof(wlast_err), "failed to nest context"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } newctx->fn = node; newctx->initial_env_lvl = walker->sym_top; walker->classes[walker->classes_count] = fzl_mkctable(node->class.name->value); fzl_ctable_t *class = walker->classes[walker->classes_count++]; class->name = node->class.name->value; for (size_t i = 0; i < node->class.fields_count; i++) { if (class->fields_remainder <= class->fields_count) { class->fields_remainder *= 2; class->fields = realloc(class->fields, class->fields_remainder * sizeof(fzl_ctable_field_t)); } class->fields[class->fields_count] = (fzl_ctable_field_t){ .name = node->class.fields[i]->var.identifier->token->value, .type = tok_to_type(node->class.fields[i]->var.type), .slot_index = class->fields_count++}; } for (size_t i = 0; i < node->class.methods_count; i++) { fzl_node_t *fn = node->class.methods[i]; size_t classname_len = strlen(class->name); size_t fnname_len = strlen(fn->fn.name->value); char *method_name = malloc(classname_len + fnname_len + 2); strcpy(method_name, class->name); method_name[classname_len] = '_'; strcpy((method_name + classname_len + 1), fn->fn.name->value); method_name[classname_len + fnname_len + 1] = 0; fn->fn.name->value = method_name; fzl_walk_res_t fnres = walk(walker, fn); if (fnres.error) return fnres; } if (!fin_ctx(walker)) { snprintf(wlast_err, sizeof(wlast_err), "failed to clean context"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; break; } case FZL_NODETYPE_CONSTRUCT: { fzl_node_t *classname = node->construct.name; fzl_ctable_t *ct = NULL; for (size_t i = 0; i < walker->classes_count; i++) { if (strcmp((char *)classname->token->value, walker->classes[i]->name) == 0) { ct = walker->classes[i]; break; } } if (!ct) { snprintf(wlast_err, sizeof(wlast_err), "'%s': class '%s' not found (at %ld:%ld)", classname->token->filename, (char *)classname->token->value, classname->token->ln, classname->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } push_op(walker, FZL_OPCODE_ALLOC_OBJECT); push_word(walker, ct->fields_count); current_res = (fzl_walk_res_t){ .type = FZL_TYPE_OBJECT, .error = false, .classname = ct->name}; break; } case FZL_NODETYPE_FIELD: { fzl_sym_t sym; bool exists = false; for (ssize_t i = walker->sym_top - 1; i >= 0; i--) if ((exists = fzl_symt_get(walker->syms[i], node->memb.parent->token->value, &sym))) break; if (!exists) { snprintf(wlast_err, sizeof(wlast_err), "'%s': cannot find symbol '%s' at %ld:%ld", node->token->filename, (char *)node->token->value, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (!sym.classname) { snprintf(wlast_err, sizeof(wlast_err), "'%s': '%s' is not an object (at %ld:%ld)", node->token->filename, sym.name, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } fzl_ctable_t *ct = NULL; for (size_t i = 0; i < walker->classes_count; i++) { if (strcmp(sym.classname, walker->classes[i]->name) == 0) { ct = walker->classes[i]; break; } } fzl_token_t *tok = node->memb.parent->token; if (!ct) { snprintf(wlast_err, sizeof(wlast_err), "'%s': access to unknown class '%s' at %ld:%ld", tok->filename, (char *)tok->value, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } fzl_token_t *ftok = node->memb.field->token; fzl_ctable_field_t ctf; bool field_exists = false; for (size_t i = 0; i < ct->fields_count; i++) { if (strcmp(ftok->value, ct->fields[i].name) == 0) { field_exists = true; ctf = ct->fields[i]; break; } } if (!field_exists) { snprintf(wlast_err, sizeof(wlast_err), "'%s': cannot find field '%s' in class '%s' at %ld:%ld", ftok->filename, (char *)ftok->value, (char *)tok->value, ftok->ln, ftok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } fzl_walk_res_t parent_res = walk(walker, node->memb.parent); if (parent_res.error) return parent_res; push_op(walker, FZL_OPCODE_GET_FIELD); push_word(walker, ctf.slot_index); current_res = (fzl_walk_res_t){ .type = ctf.type, .error = false, .classname = sym.classname}; break; } case FZL_NODETYPE_FUNCTION: { push_op(walker, FZL_OPCODE_JMP); size_t skip_ip = walker->op_count; push_word(walker, (uint64_t)0); fzl_type_t fntype = tok_to_type(node->fn.type); fzl_symtable_res_t putres = fzl_symt_put(walker->syms[walker->sym_top - 1], node->fn.name->value, fntype, walker->op_count); if (putres != FZL_SYMTABLE_RESULT_OK) { if (putres == FZL_SYMTABLE_RESULT_EXISTS) snprintf( wlast_err, sizeof(wlast_err), "'%s': cannot define already declared name '%s' at %ld:%ld", node->token->filename, (char *)node->fn.name->value, node->token->ln, node->token->cn); else snprintf(wlast_err, sizeof(wlast_err), "'%s': failed to store name '%s'", node->fn.name->filename, (char *)node->fn.name->value); return (fzl_walk_res_t){.error = true}; } // NOTE: add sym_top limit check size_t lvl = walker->sym_top; walker->syms[walker->sym_top++] = fzl_mksymt(); fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_FN); if (!newctx) { snprintf(wlast_err, sizeof(wlast_err), "failed to nest context"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } newctx->type = FZL_WALK_CTX_TYPE_FN; newctx->fn = node; newctx->initial_env_lvl = lvl; push_op(walker, FZL_OPCODE_MK_ENVTABLE); for (ssize_t i = node->fn.args_count - 1; i >= 0; i--) { fzl_walk_res_t res = walk(walker, node->fn.args[i]); if (res.error) return res; push_op(walker, FZL_OPCODE_WRITE); } fzl_walk_res_t bodyres = walk(walker, node->fn.body); if (bodyres.error) return bodyres; // NOTE: ghost walk (w/o opcodes write) for arg check should be there in // near future push_op(walker, FZL_OPCODE_RM_ENVTABLE); push_op(walker, FZL_OPCODE_RET); walker->ops[skip_ip] = walker->op_count; if (!fin_ctx(walker)) { snprintf(wlast_err, sizeof(wlast_err), "failed to clean context"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } for (ssize_t i = walker->sym_top - 1; i >= lvl; i--) fzl_freesymt(walker->syms[--walker->sym_top]); current_res = (fzl_walk_res_t){.type = fntype, .error = false}; break; } case FZL_NODETYPE_RETURN: { if (!walker->ctx || walker->ctx->type != FZL_WALK_CTX_TYPE_FN) { snprintf( wlast_err, sizeof(wlast_err), "'%s': return cannot be used outside of functions (at %ld:%ld)", node->token->filename, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (node->ret.value) { fzl_walk_res_t retres = walk(walker, node->ret.value); if (retres.error) return retres; if (!is_compatible(retres.type, tok_to_type(walker->ctx->fn->fn.type))) { snprintf( wlast_err, sizeof(wlast_err), "'%s': incompatible types between return and function type at " "%ld:%ld", node->token->filename, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } current_res = (fzl_walk_res_t){.type = retres.type, .error = false}; } else current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; for (size_t i = 0; i < (walker->sym_top - walker->ctx->initial_env_lvl); i++) push_op(walker, FZL_OPCODE_RM_ENVTABLE); push_op(walker, FZL_OPCODE_RET); break; } 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), "'%s': incompatible types at %ld:%ld", node->token->filename, 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), "'%s': incompatible types at %ld:%ld", node->token->filename, 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: { size_t n = add_const( walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, .as.string.chars = node->token->value, .as.string.length = node->token->value_sz}); fzl_sym_t sym; bool exists = false; for (ssize_t i = walker->sym_top - 1; i >= 0; i--) if ((exists = fzl_symt_get(walker->syms[i], node->token->value, &sym))) break; if (!exists) { snprintf(wlast_err, sizeof(wlast_err), "'%s': cannot find symbol '%s' at %ld:%ld", node->token->filename, (char *)node->token->value, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, n); push_op(walker, FZL_OPCODE_LOOKUP_ENV); current_res = (fzl_walk_res_t){ .type = sym.type, .error = false, .classname = sym.classname}; break; } case FZL_NODETYPE_BLOCK: walker->syms[walker->sym_top++] = fzl_mksymt(); push_op(walker, FZL_OPCODE_MK_ENVTABLE); if (node->list.entry_count > 0) for (size_t i = 0; i < node->list.entry_count; i++) { fzl_walk_res_t res = walk(walker, node->list.entries[i]); if (res.error) return res; } fzl_freesymt(walker->syms[--walker->sym_top]); push_op(walker, FZL_OPCODE_RM_ENVTABLE); break; case FZL_NODETYPE_CALL: if (strcmp(node->token->value, "printline") == 0) { if (node->call.args_count > 0) { for (size_t i = 0; i < node->call.args_count; i++) { fzl_walk_res_t res = walk(walker, node->call.args[i]); if (res.error) return res; } } push_op(walker, FZL_OPCODE_CALL_BUILTIN); push_word(walker, FZL_BUILTIN_PRINTLINE); if (node->call.args_count < 1) { snprintf(wlast_err, sizeof(wlast_err), "'%s': 'printline' call with zero arguments at %ld:%ld", node->token->filename, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; } else { if (node->call.callee->type == FZL_NODETYPE_IDENTIFIER) { if (node->call.args_count > 0) { for (size_t i = 0; i < node->call.args_count; i++) { fzl_walk_res_t res = walk(walker, node->call.args[i]); if (res.error) return res; } } fzl_sym_t res; bool found = false; for (ssize_t i = walker->sym_top - 1; i >= 0; i--) if ((found = fzl_symt_get(walker->syms[i], node->token->value, &res))) break; if (!found) { snprintf(wlast_err, sizeof(wlast_err), "call to unknown function '%s'", (char *)node->token->value); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (!res.ip) { snprintf(wlast_err, sizeof(wlast_err), "cannot jump to address 0x0"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } push_op(walker, FZL_OPCODE_CALL); push_word(walker, res.ip); current_res = (fzl_walk_res_t){.type = res.type, .error = false}; } else if (node->call.callee->type == FZL_NODETYPE_FIELD) { fzl_node_t *parent_node = node->call.callee->memb.parent; fzl_sym_t res; bool found = false; for (ssize_t i = walker->sym_top - 1; i >= 0; i--) if ((found = fzl_symt_get(walker->syms[i], parent_node->token->value, &res))) break; if (!found) { snprintf(wlast_err, sizeof(wlast_err), "'%s': access to unknown object '%s' at %ld:%ld", node->token->filename, (char *)parent_node->token->value, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (res.type != FZL_TYPE_OBJECT || !res.classname) { snprintf(wlast_err, sizeof(wlast_err), "'%s': name '%s' is not an object (at %ld:%ld)", parent_node->token->filename, (char *)parent_node->token->value, parent_node->token->ln, parent_node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } found = false; for (size_t i = 0; i < walker->classes_count; i++) { if (strcmp(walker->classes[i]->name, res.classname) == 0) { found = true; break; } } if (!found) { snprintf(wlast_err, sizeof(wlast_err), "'%s': class '%s' not found at %ld:%ld", node->token->filename, res.classname, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } char *classname = res.classname; size_t classlen = strlen(res.classname); size_t fnlen = strlen(node->call.callee->token->value); char *methodname = malloc(classlen + fnlen + 2); strcpy(methodname, res.classname); strcpy((methodname + classlen + 1), node->call.callee->token->value); methodname[classlen] = '_'; methodname[classlen + fnlen + 1] = 0; found = false; for (ssize_t i = walker->sym_top - 1; i >= 0; i--) if ((found = fzl_symt_get(walker->syms[i], methodname, &res))) break; if (!found) { snprintf(wlast_err, sizeof(wlast_err), "'%s': class '%s' has no method '%s' at %ld:%ld", node->token->filename, classname, (char *)node->call.callee->token->value, node->token->ln, node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } fzl_walk_res_t parent = walk(walker, parent_node); if (parent.error) return parent; if (node->call.args_count > 0) { for (size_t i = 0; i < node->call.args_count; i++) { fzl_walk_res_t res = walk(walker, node->call.args[i]); if (res.error) return res; } } push_op(walker, FZL_OPCODE_CALL); push_word(walker, res.ip); current_res = (fzl_walk_res_t){.type = res.type, .error = false}; } } break; case FZL_NODETYPE_LITERAL: if (node->token->type == FZL_TOKTYPE_STR_LITERAL) { size_t n = add_const( walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, .as.string.chars = node->token->value, .as.string.length = node->token->value_sz}); push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, n); current_res = (fzl_walk_res_t){.type = FZL_TYPE_STRING, .error = false}; } else if (node->token->type == FZL_TOKTYPE_NUM_LITERAL) { push_op(walker, FZL_OPCODE_PUSHNUM); uint64_t num = strtol(node->token->value, NULL, 10); 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: case FZL_NODETYPE_SUB: case FZL_NODETYPE_MUL: case FZL_NODETYPE_DIV: case FZL_NODETYPE_POW: case FZL_NODETYPE_MOD: { fzl_walk_res_t lres = walk(walker, node->binary_op.left); if (lres.error) return lres; fzl_walk_res_t rres = walk(walker, node->binary_op.right); if (rres.error) return rres; if (lres.type < FZL_TYPE_INT8) { fzl_token_t *ltok = node->binary_op.left->token; snprintf(wlast_err, sizeof(wlast_err), "'%s': left operand at %ld:%ld is not compatible to integer " "types", ltok->filename, ltok->ln, ltok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (rres.type < FZL_TYPE_INT8) { fzl_token_t *rtok = node->binary_op.right->token; snprintf(wlast_err, sizeof(wlast_err), "'%s': right operand at %ld:%ld is not compatible to " "integer types", rtok->filename, rtok->ln, rtok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } switch (node->type) { case FZL_NODETYPE_ADD: push_op(walker, FZL_OPCODE_ADD); break; case FZL_NODETYPE_SUB: push_op(walker, FZL_OPCODE_SUB); break; case FZL_NODETYPE_MUL: push_op(walker, FZL_OPCODE_MUL); break; case FZL_NODETYPE_DIV: push_op(walker, FZL_OPCODE_DIV); break; case FZL_NODETYPE_POW: push_op(walker, FZL_OPCODE_POW); break; case FZL_NODETYPE_MOD: push_op(walker, FZL_OPCODE_MOD); break; default: break; } current_res = (fzl_walk_res_t){.type = lres.type, .error = false}; break; } case FZL_NODETYPE_UNARY_MINUS: { fzl_walk_res_t res = walk(walker, node->unary_op.operand); if (res.error) return res; if (res.type < FZL_TYPE_INT8) { fzl_token_t *tok = node->unary_op.operand->token; snprintf(wlast_err, sizeof(wlast_err), "'%s': incompatible with unary operator type at %ld:%ld", tok->filename, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } push_op(walker, FZL_OPCODE_NEG); current_res = (fzl_walk_res_t){.type = res.type, .error = false}; break; } case FZL_NODETYPE_DECLARATION: { fzl_ctable_t *ct = NULL; for (size_t i = 0; i < walker->classes_count; i++) { if (strcmp(node->var.type->value, walker->classes[i]->name) == 0) { ct = walker->classes[i]; break; } } fzl_symtable_res_t res = fzl_symt_put( walker->syms[walker->sym_top - 1], node->var.identifier->token->value, ct != NULL ? FZL_TYPE_OBJECT : tok_to_type(node->var.type), 0); if (ct) fzl_symt_setobjclass(walker->syms[walker->sym_top - 1], node->var.identifier->token->value, ct->name); if (res != FZL_SYMTABLE_RESULT_OK) { fzl_token_t *tok = node->var.identifier->token; snprintf(wlast_err, sizeof(wlast_err), "'%s': cannot store '%s' at %ld:%ld", tok->filename, (char *)tok->value, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } fzl_token_t *id_tok = node->var.identifier->token; size_t const_n = add_const( walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, .as.string.chars = id_tok->value, .as.string.length = id_tok->value_sz}); push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, const_n); push_op(walker, FZL_OPCODE_STORE); push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, const_n); current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false}; break; } case FZL_NODETYPE_ASSIGN: { fzl_walk_res_t rres = walk(walker, node->binary_op.right); if (rres.error) return rres; fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_ASSIGN); if (!newctx) { snprintf(wlast_err, sizeof(wlast_err), "failed to nest context"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (node->binary_op.left->type == FZL_NODETYPE_DECLARATION) { fzl_walk_res_t lres = walk(walker, node->binary_op.left); if (lres.error) return lres; push_op(walker, FZL_OPCODE_WRITE); } else if (node->binary_op.left->type == FZL_NODETYPE_IDENTIFIER) { 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], node->binary_op.left->token->value, &sym))) break; if (!found) { fzl_token_t *tok = node->binary_op.left->token->value; snprintf(wlast_err, sizeof(wlast_err), "'%s': name '%s' is not exists at %ld:%ld", tok->filename, (char *)tok->value, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } if (!is_compatible(sym.type, rres.type)) { fzl_token_t *tok = node->binary_op.right->token; snprintf(wlast_err, sizeof(wlast_err), "'%s': R-Value is not compatible with L-Value at %ld:%ld", tok->filename, tok->ln, tok->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } size_t const_n = add_const( walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, .as.string.chars = sym.name, .as.string.length = node->binary_op.left->token->value_sz}); push_op(walker, FZL_OPCODE_LOADCONST); push_word(walker, const_n); push_op(walker, FZL_OPCODE_WRITE); } else if (node->binary_op.left->type == FZL_NODETYPE_FIELD) { fzl_node_t *field_node = node->binary_op.left; fzl_walk_res_t obj_res = walk(walker, field_node->memb.parent); if (obj_res.error) return obj_res; fzl_ctable_t *ct; for (size_t i = 0; i < walker->classes_count; i++) { if (strcmp(obj_res.classname, walker->classes[i]->name) == 0) { ct = walker->classes[i]; break; } } if (!ct) { snprintf(wlast_err, sizeof(wlast_err), "'%s': cannot find class '%s' at %ld:%ld", field_node->token->filename, obj_res.classname, field_node->token->ln, field_node->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } fzl_ctable_field_t *ctf; for (size_t i = 0; i < ct->fields_count; i++) { if (strcmp(field_node->memb.field->token->value, ct->fields[i].name) == 0) { ctf = &ct->fields[i]; break; } } if (!ctf) { fzl_node_t *fn = field_node->memb.field; snprintf(wlast_err, sizeof(wlast_err), "'%s': class '%s' has no field '%s' at %ld:%ld", fn->token->filename, obj_res.classname, (char *)fn->token->value, fn->token->ln, fn->token->cn); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } push_op(walker, FZL_OPCODE_SET_FIELD); push_word(walker, ctf->slot_index); } if (!fin_ctx(walker)) { snprintf(wlast_err, sizeof(wlast_err), "failed to clean context"); return (fzl_walk_res_t){.error = true, .error_msg = wlast_err}; } current_res = (fzl_walk_res_t){.type = rres.type, .error = false}; break; } } node = node->next; } return current_res; } // Walk entire AST recursevily int fzl_walk(fzl_walker_t *walker) { if (!walker) { snprintf(wlast_err, sizeof(wlast_err), "walker is null"); return -1; } nest_ctx(walker, FZL_WALK_CTX_TYPE_GLOBAL); push_op(walker, FZL_OPCODE_MK_ENVTABLE); fzl_walk_res_t result = walk(walker, walker->root); push_op(walker, FZL_OPCODE_RM_ENVTABLE); fin_ctx(walker); if (result.error && !wlast_err[0]) snprintf(wlast_err, sizeof(wlast_err), "%s", result.error_msg); return result.error ? -1 : 0; } // --- Walking zone end --- char *fzl_walker_geterror() { return wlast_err; } void fzl_freewalker(fzl_walker_t *walker) { if (!walker) return; fzl_consts_pool_free(walker->consts); if (walker->ops) free(walker->ops); while (walker->sym_top > 0) fzl_freesymt(walker->syms[--walker->sym_top]); for (size_t i = 0; i < walker->classes_count; i++) fzl_freectable(walker->classes[i]); free(walker); }