From bd8272abc79b863b3d0d3f68c9d957747c06bcf9 Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 5 Nov 2026 00:04:12 +0300 Subject: [PATCH] feat: finish generator --- src/compiler/generator/gen_assign.c | 122 ++++++++++++ src/compiler/generator/gen_block.c | 27 +++ src/compiler/generator/gen_call.c | 231 +++++++++++++++++++++++ src/compiler/generator/gen_compare.c | 83 ++++++++ src/compiler/generator/gen_condition.c | 37 ++++ src/compiler/generator/gen_declaration.c | 60 ++++++ src/compiler/generator/gen_identifier.c | 29 +++ src/compiler/generator/gen_literal.c | 59 ++++++ src/compiler/generator/gen_logical.c | 29 +++ src/compiler/generator/gen_math.c | 47 +++++ src/compiler/generator/gen_unary_math.c | 20 ++ 11 files changed, 744 insertions(+) create mode 100644 src/compiler/generator/gen_assign.c create mode 100644 src/compiler/generator/gen_block.c create mode 100644 src/compiler/generator/gen_call.c create mode 100644 src/compiler/generator/gen_compare.c create mode 100644 src/compiler/generator/gen_condition.c create mode 100644 src/compiler/generator/gen_declaration.c create mode 100644 src/compiler/generator/gen_identifier.c create mode 100644 src/compiler/generator/gen_literal.c create mode 100644 src/compiler/generator/gen_logical.c create mode 100644 src/compiler/generator/gen_math.c create mode 100644 src/compiler/generator/gen_unary_math.c diff --git a/src/compiler/generator/gen_assign.c b/src/compiler/generator/gen_assign.c new file mode 100644 index 0000000..c086488 --- /dev/null +++ b/src/compiler/generator/gen_assign.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +// Assignment to declaration +gen_res_t assign_declaration(fzl_gen_t *gen, fzl_node_t *node) +{ + gen_res_t rres = gen_common(gen, node->binary_op.right); + if (rres.error) + return gen_error; + + gen_res_t lres = gen_common(gen, node->binary_op.left); + if (lres.error) + return gen_error; + + if (!gen_is_types_compatible(rres.type, lres.type, rres.classname, + lres.classname)) + return gen_error; + + gen_op(gen, FZL_OPCODE_WRITE); + + return (gen_res_t){ + .error = false, .type = rres.type, .classname = rres.classname}; +} + +// Assignment to identifier +gen_res_t assign_id(fzl_gen_t *gen, fzl_node_t *node) +{ + gen_res_t rres = gen_common(gen, node->binary_op.right); + if (rres.error) + return gen_error; + + char *name = (char *)node->binary_op.left->token->value; + fzl_sym_t sym; + bool found = false; + ssize_t i = gen->syms_top - 1; + for (; i >= 0; i--) + if ((found = fzl_symt_get(gen->syms[i], name, &sym))) + break; + + if (!found) + return gen_error; + + if (!gen_is_types_compatible(sym.type, rres.type, sym.classname, + rres.classname)) + return gen_error; + + if (!sym.slot_set) + return gen_error; + + size_t depth = gen->syms_top - 1 - i; + gen_op(gen, FZL_OPCODE_STORE_LOCAL); + gen_word(gen, depth); + gen_word(gen, sym.slot); + + return (gen_res_t){ + .error = false, .type = rres.type, .classname = rres.classname}; +} + +// Assignment to field +gen_res_t assign_field(fzl_gen_t *gen, fzl_node_t *node) +{ + gen_res_t rres = gen_common(gen, node->binary_op.right); + if (rres.error) + return gen_error; + + fzl_node_t *field_node = node->binary_op.left; + gen_res_t obj_res = gen_common(gen, field_node->memb.parent); + if (obj_res.error) + return gen_error; + + fzl_ctable_t *ct = NULL; + for (size_t i = 0; i < gen->classes_count; i++) + { + if (strcmp(obj_res.classname, gen->classes[i]->name) == 0) + { + ct = gen->classes[i]; + break; + } + } + + if (!ct) + return gen_error; + + fzl_ctable_field_t *ctf = NULL; + 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) + return gen_error; + + gen_op(gen, FZL_OPCODE_SET_FIELD); + gen_word(gen, ctf->slot_index); + + return (gen_res_t){ + .error = false, .type = rres.type, .classname = rres.classname}; +} + +// Assignment +gen_res_t gen_assign(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + if (node->binary_op.left->type == FZL_NODETYPE_DECLARATION) + return assign_declaration(gen, node); + + if (node->binary_op.left->type == FZL_NODETYPE_IDENTIFIER) + return assign_id(gen, node); + + if (node->binary_op.left->type == FZL_NODETYPE_FIELD) + return assign_field(gen, node); + + return gen_error; +} diff --git a/src/compiler/generator/gen_block.c b/src/compiler/generator/gen_block.c new file mode 100644 index 0000000..8dc7f1c --- /dev/null +++ b/src/compiler/generator/gen_block.c @@ -0,0 +1,27 @@ +#include +#include + +// Block definition +gen_res_t gen_block(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + fzl_gen_ctx_t *ctx = gen_ctx(gen, FZL_GEN_CTX_TYPE_UNECCESSARY, node); + if (!ctx) + return gen_error; + + gen_op(gen, FZL_OPCODE_MK_ENVTABLE); + if (node->list.entry_count > 0) + for (size_t i = 0; i < node->list.entry_count; i++) + if (gen_common(gen, node->list.entries[i]).error) + return gen_error; + + ctx = gen_fin_ctx(gen); + if (!ctx) + return gen_error; + + gen_op(gen, FZL_OPCODE_RM_ENVTABLE); + + return gen_ok_null; +} diff --git a/src/compiler/generator/gen_call.c b/src/compiler/generator/gen_call.c new file mode 100644 index 0000000..155d62d --- /dev/null +++ b/src/compiler/generator/gen_call.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Call definition +// NOTE: split by call type (builtin/declared/extern) +gen_res_t gen_call(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + // `printline(x)` builtin function + if (strcmp(node->token->value, "printline") == 0) + { + if (node->call.args_count > 0) + for (size_t i = 0; i < node->call.args_count; i++) + if (gen_common(gen, node->call.args[i]).error) + return gen_error; + + gen_op(gen, FZL_OPCODE_CALL_BUILTIN); + gen_word(gen, FZL_BUILTIN_PRINTLINE); + if (node->call.args_count < 1) + return gen_error; + + return gen_ok_null; + } + + // `exit(x?)` builtin function + if (strcmp(node->token->value, "exit") == 0) + { + if (node->call.args_count > 0) + { + for (size_t i = 0; i < node->call.args_count; i++) + if (gen_common(gen, node->call.args[i]).error) + return gen_error; + + gen_op(gen, FZL_OPCODE_CALL_BUILTIN); + gen_word(gen, FZL_BUILTIN_EXIT); + if (node->call.args_count < 1) + return gen_error; + } + + return gen_ok_null; + } + + 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++) + if (gen_common(gen, node->call.args[i]).error) + return gen_error; + + char *name = (char *)node->token->value; + fzl_sym_t sym; + bool found = gen_resolve_name(gen, name, &sym); + if (!found) + return gen_error; + + gen_res_t result; + if (sym.node->type == FZL_NODETYPE_EXTERN_FN) + { + size_t fn_n = 0; + fzl_extern_fn_t *fn = NULL; + for (size_t i = 0; i < gen->extern_fns_count; i++) + { + if (strcmp(gen->extern_fns[i].used_name, name) == 0) + { + fn = &gen->extern_fns[i]; + fn_n = i; + break; + } + } + + if (!fn) + return gen_error; + + fzl_type_t rettype; + char *rettypecn = NULL; + + if (!fn->has_return) + { + rettype = FZL_TYPE_NULL; + } + else if (fn->ret_type.is_primitive) + { + rettype = fn->ret_type.primitive; + } + else + { + fzl_extern_type_t *type = &gen->extern_types[fn->ret_type.type_slot]; + switch (type->kind) + { + case FZL_EXTERN_KIND_REF: + rettype = FZL_TYPE_HANDLE; + rettypecn = type->name; + break; + + case FZL_EXTERN_KIND_STRUCT: + rettype = FZL_TYPE_STRUCT; + rettypecn = type->name; + break; + + case FZL_EXTERN_KIND_HANDLE: + rettype = FZL_TYPE_HANDLE; + rettypecn = type->name; + break; + + case FZL_EXTERN_KIND_ALIAS: + if (type->alias_target.is_primitive) + { + rettype = type->alias_target.primitive; + } + else + { + fzl_extern_type_t *aliased = + &gen->extern_types[type->alias_target.type_slot]; + switch (aliased->kind) + { + case FZL_EXTERN_KIND_ALIAS: + return gen_error; + + case FZL_EXTERN_KIND_HANDLE: + rettype = FZL_TYPE_HANDLE; + rettypecn = aliased->name; + break; + + case FZL_EXTERN_KIND_STRUCT: + rettype = FZL_TYPE_STRUCT; + rettypecn = aliased->name; + break; + + case FZL_EXTERN_KIND_REF: + rettype = FZL_TYPE_HANDLE; + rettypecn = aliased->name; + break; + + default: + return gen_error; + } + } + break; + } + } + + gen_op(gen, FZL_OPCODE_CALL_EXTERN); + gen_word(gen, fn_n); + + return (gen_res_t){ + .error = false, .type = rettype, .classname = rettypecn}; + } + else + { + if (!sym.ip) + return gen_error; + + gen_op(gen, FZL_OPCODE_CALL); + gen_word(gen, sym.ip); + + return (gen_res_t){ + .error = false, .type = sym.type, .classname = sym.classname}; + } + + return result; + } + + if (node->call.callee->type == FZL_NODETYPE_FIELD) + { + fzl_node_t *parent_node = node->call.callee->memb.parent; + fzl_node_t *method_node = node->call.callee->memb.field; + + gen_res_t parent_res = gen_common(gen, parent_node); + if (parent_res.error) + return gen_error; + + if (parent_res.type != FZL_TYPE_OBJECT || !parent_res.classname) + return gen_error; + + char *classname = parent_res.classname; + bool class_exists = false; + for (size_t i = 0; i < gen->classes_count; i++) + { + if (strcmp(classname, gen->classes[i]->name) == 0) + { + class_exists = true; + break; + } + } + + if (!class_exists) + return gen_error; + + size_t classlen = strlen(classname); + size_t fnlen = strlen(method_node->token->value); + char *methodname = malloc(classlen + fnlen + 2); + strcpy(methodname, classname); + strcpy((methodname + classlen + 1), node->call.callee->token->value); + methodname[classlen] = '_'; + methodname[classlen + fnlen + 1] = 0; + + fzl_sym_t fn_sym; + bool found_method = false; + for (ssize_t i = gen->syms_top - 1; i >= 0; i--) + if ((found_method = fzl_symt_get(gen->syms[i], methodname, &fn_sym))) + break; + + if (!found_method) + return gen_error; + + free(methodname); + + if (node->call.args_count > 0) + for (size_t i = 0; i < node->call.args_count; i++) + if (gen_common(gen, node->call.args[i]).error) + return gen_error; + + gen_op(gen, FZL_OPCODE_CALL); + gen_word(gen, fn_sym.ip); + + return (gen_res_t){ + .error = false, .type = fn_sym.type, .classname = fn_sym.classname}; + } + + return gen_error; +} diff --git a/src/compiler/generator/gen_compare.c b/src/compiler/generator/gen_compare.c new file mode 100644 index 0000000..0b575f9 --- /dev/null +++ b/src/compiler/generator/gen_compare.c @@ -0,0 +1,83 @@ +#include +#include +#include + +// Comparison operations +gen_res_t gen_compare(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + gen_res_t left = gen_common(gen, node->binary_op.left); + if (left.error) + return left; + + gen_res_t right = gen_common(gen, node->binary_op.right); + if (right.error) + return right; + + if (!gen_is_types_compatible(left.type, right.type, left.classname, + right.classname)) + return gen_error; + + if (left.type == FZL_TYPE_STRING && right.type == FZL_TYPE_STRING) + { + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + gen_op(gen, FZL_OPCODE_CMP_EQ_STR); + break; + + case FZL_NODETYPE_NEQUAL: + gen_op(gen, FZL_OPCODE_CMP_NEQ_STR); + break; + + default: + return gen_error; + } + } + else if (left.type == FZL_TYPE_OBJECT && right.type == FZL_TYPE_OBJECT) + { + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + gen_op(gen, FZL_OPCODE_CMP_EQ_OBJ); + break; + + case FZL_NODETYPE_NEQUAL: + gen_op(gen, FZL_OPCODE_CMP_NEQ_OBJ); + break; + + default: + return gen_error; + } + } + else + { + switch (node->type) + { + case FZL_NODETYPE_EQUAL: + gen_op(gen, FZL_OPCODE_CMP_EQ); + break; + case FZL_NODETYPE_NEQUAL: + gen_op(gen, FZL_OPCODE_CMP_NEQ); + break; + case FZL_NODETYPE_LESS: + gen_op(gen, FZL_OPCODE_CMP_LT); + break; + case FZL_NODETYPE_LESS_EQUAL: + gen_op(gen, FZL_OPCODE_CMP_LTEQ); + break; + case FZL_NODETYPE_GREATER: + gen_op(gen, FZL_OPCODE_CMP_GT); + break; + case FZL_NODETYPE_GREATER_EQUAL: + gen_op(gen, FZL_OPCODE_CMP_GTEQ); + break; + default: + break; + } + } + + return (gen_res_t){.error = false, .type = FZL_TYPE_BOOL, .classname = NULL}; +} diff --git a/src/compiler/generator/gen_condition.c b/src/compiler/generator/gen_condition.c new file mode 100644 index 0000000..376dbe0 --- /dev/null +++ b/src/compiler/generator/gen_condition.c @@ -0,0 +1,37 @@ +#include + +// Conditional operation +gen_res_t gen_condition(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + gen_res_t cres = gen_common(gen, node->cond.condition); + if (cres.error) + return gen_error; + + gen_op(gen, FZL_OPCODE_JZ); + + size_t jz_if = gen->ops_count; + gen_word(gen, (uint64_t)0); + + gen_res_t tblock_res = gen_common(gen, node->cond.true_block); + if (tblock_res.error) + return gen_error; + + gen_op(gen, FZL_OPCODE_JMP); + size_t jmp_else = gen->ops_count; + gen_word(gen, (uint64_t)0); + + gen->ops[jz_if] = gen->ops_count; + + if (node->cond.else_block) + { + gen_res_t fblock_res = gen_common(gen, node->cond.else_block); + if (fblock_res.error) + return gen_error; + } + + gen->ops[jmp_else] = gen->ops_count; + return gen_ok_null; +} diff --git a/src/compiler/generator/gen_declaration.c b/src/compiler/generator/gen_declaration.c new file mode 100644 index 0000000..6307ba4 --- /dev/null +++ b/src/compiler/generator/gen_declaration.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +// Variable (or argument) declaration +gen_res_t gen_declaration(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + char *name = (char *)node->var.identifier->token->value; + fzl_type_t type; + char typecn[256]; + memset(typecn, 0, sizeof(typecn)); + + bool resolved = gen_resolve_type(gen, node->var.type, &type, typecn); + if (!resolved) + return gen_error; + + fzl_ctable_t *ct = NULL; + if (type == FZL_TYPE_OBJECT) + { + for (size_t i = 0; i < gen->classes_count; i++) + { + if (strcmp(node->var.type->value, gen->classes[i]->name) == 0) + { + ct = gen->classes[i]; + break; + } + } + } + + fzl_symtable_res_t res = + fzl_symt_put(gen->syms[gen->syms_top - 1], node, name, type, 0); + + if (ct) + fzl_symt_setobjclass(gen->syms[gen->syms_top - 1], name, ct->name); + + if (res != FZL_SYMTABLE_RESULT_OK) + return gen_error; + + if (fzl_symt_setslot(gen->syms[gen->syms_top - 1], name) < 0) + return gen_error; + + fzl_token_t *id_tok = node->var.identifier->token; + size_t const_n = + gen_add_const(gen, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = id_tok->value, + .as.string.length = id_tok->value_sz}); + + gen_op(gen, FZL_OPCODE_LOADCONST); + gen_word(gen, const_n); + + gen_op(gen, FZL_OPCODE_STORE); + gen_op(gen, FZL_OPCODE_LOADCONST); + gen_word(gen, const_n); + + return (gen_res_t){.error = false, .type = type, .classname = typecn}; +} diff --git a/src/compiler/generator/gen_identifier.c b/src/compiler/generator/gen_identifier.c new file mode 100644 index 0000000..c7559ce --- /dev/null +++ b/src/compiler/generator/gen_identifier.c @@ -0,0 +1,29 @@ +#include + +// Identifier lookup +gen_res_t gen_identifier(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + fzl_sym_t sym; + bool exists = false; + ssize_t i = gen->syms_top - 1; + for (; i >= 0; i--) + if ((exists = fzl_symt_get(gen->syms[i], node->token->value, &sym))) + break; + + if (!exists) + return gen_error; + + if (!sym.slot_set) + return gen_error; + + size_t depth = gen->syms_top - 1 - i; + gen_op(gen, FZL_OPCODE_LOAD_LOCAL); + gen_word(gen, depth); + gen_word(gen, sym.slot); + + return (gen_res_t){ + .error = false, .type = sym.type, .classname = sym.classname}; +} diff --git a/src/compiler/generator/gen_literal.c b/src/compiler/generator/gen_literal.c new file mode 100644 index 0000000..0d3dcca --- /dev/null +++ b/src/compiler/generator/gen_literal.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +gen_res_t gen_literal(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + if (node->token->type == FZL_TOKTYPE_STR_LITERAL) + { + size_t n = gen_add_const( + gen, (fzl_const_t){.type = FZL_CONSTTYPE_STRING, + .as.string.chars = node->token->value, + .as.string.length = node->token->value_sz}); + + gen_op(gen, FZL_OPCODE_LOADCONST); + gen_word(gen, n); + + return (gen_res_t){ + .error = false, .type = FZL_TYPE_STRING, .classname = NULL}; + } + + if (node->token->type == FZL_TOKTYPE_NUM_LITERAL) + { + gen_op(gen, FZL_OPCODE_PUSHNUM); + + uint64_t num = strtol(node->token->value, NULL, 10); + gen_word(gen, num); + + return (gen_res_t){.error = false, .type = FZL_TYPE_INT, .classname = NULL}; + } + + if (node->token->type == FZL_TOKTYPE_BOOL_LITERAL) + { + fzl_token_t *tok = node->token; + uint64_t val = strcmp(tok->value, "true") == 0 ? 1 : 0; + + gen_op(gen, FZL_OPCODE_PUSHBOOL); + gen_word(gen, val); + + return (gen_res_t){ + .error = false, .type = FZL_TYPE_BOOL, .classname = NULL}; + } + + if (node->token->type == FZL_TOKTYPE_NULL_LITERAL) + { + gen_op(gen, FZL_OPCODE_PUSHNUM); + gen_word(gen, 0); + gen_op(gen, FZL_OPCODE_DEREFERENCE); + + return (gen_res_t){ + .error = false, .type = FZL_TYPE_OBJECT, .classname = null_name}; + } + + return gen_error; +} diff --git a/src/compiler/generator/gen_logical.c b/src/compiler/generator/gen_logical.c new file mode 100644 index 0000000..0058e36 --- /dev/null +++ b/src/compiler/generator/gen_logical.c @@ -0,0 +1,29 @@ +#include +#include +#include + +// Binary logical operations (and / or) +gen_res_t gen_logical(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + gen_res_t left = gen_common(gen, node->binary_op.left); + if (left.error) + return left; + + gen_res_t right = gen_common(gen, node->binary_op.right); + if (right.error) + return right; + + if (!gen_is_types_compatible(left.type, right.type, left.classname, + right.classname)) + return gen_error; + + if (node->type == FZL_NODETYPE_AND) + gen_op(gen, FZL_OPCODE_AND); + else + gen_op(gen, FZL_OPCODE_OR); + + return (gen_res_t){.error = false, .type = FZL_TYPE_BOOL, .classname = NULL}; +} diff --git a/src/compiler/generator/gen_math.c b/src/compiler/generator/gen_math.c new file mode 100644 index 0000000..8b85057 --- /dev/null +++ b/src/compiler/generator/gen_math.c @@ -0,0 +1,47 @@ +#include +#include +#include + +// Binary math operations +gen_res_t gen_math(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + gen_res_t lres = gen_common(gen, node->binary_op.left); + if (lres.error) + return lres; + + gen_res_t rres = gen_common(gen, node->binary_op.right); + if (rres.error) + return rres; + + if (lres.type < FZL_TYPE_INT8 || rres.type < FZL_TYPE_INT8) + return gen_error; + + switch (node->type) + { + case FZL_NODETYPE_ADD: + gen_op(gen, FZL_OPCODE_ADD); + break; + case FZL_NODETYPE_SUB: + gen_op(gen, FZL_OPCODE_SUB); + break; + case FZL_NODETYPE_MUL: + gen_op(gen, FZL_OPCODE_MUL); + break; + case FZL_NODETYPE_DIV: + gen_op(gen, FZL_OPCODE_DIV); + break; + case FZL_NODETYPE_POW: + gen_op(gen, FZL_OPCODE_POW); + break; + case FZL_NODETYPE_MOD: + gen_op(gen, FZL_OPCODE_MOD); + break; + default: + return gen_error; + } + + return (gen_res_t){.error = false, .type = lres.type, .classname = NULL}; +} diff --git a/src/compiler/generator/gen_unary_math.c b/src/compiler/generator/gen_unary_math.c new file mode 100644 index 0000000..0b81fce --- /dev/null +++ b/src/compiler/generator/gen_unary_math.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +// Unary math operations +gen_res_t gen_unary_math(fzl_gen_t *gen, fzl_node_t *node) +{ + if (!gen || !node) + return gen_error; + + gen_res_t res = gen_common(gen, node->unary_op.operand); + if (res.error) + return gen_error; + + if (node->type == FZL_NODETYPE_UNARY_MINUS) + gen_op(gen, FZL_OPCODE_NEG); + + return (gen_res_t){.error = false, .type = res.type, .classname = NULL}; +}