#include #include #include #include #include #include #include #include #include #include #include char *tmp_method_name = NULL; char *tmp_fnname = NULL; char *tmp_classname = NULL; // Try to get symbol name from callee char *resolve_callee(fzl_analyzer_t *analyzer, fzl_node_t *callee, bool *method) { *method = false; if (callee->type == FZL_NODETYPE_IDENTIFIER) return (char *)callee->token->value; if (callee->type != FZL_NODETYPE_FIELD) { an_push_error(analyzer, callee->token, "unknown call format"); return NULL; } *method = true; fzl_an_res_t parent_res = analyze_common(analyzer, callee->memb.parent); if (parent_res.type != FZL_TYPE_OBJECT || !parent_res.classname) return NULL; char *fn_name = callee->memb.field->token->value; tmp_fnname = fn_name; tmp_classname = parent_res.classname; size_t classname_len = strlen(parent_res.classname); size_t methodname_len = strlen(fn_name); tmp_method_name = malloc(classname_len + methodname_len + 2); if (!tmp_method_name) { an_push_error(analyzer, callee->token, "out of memory"); return NULL; } strcpy(tmp_method_name, parent_res.classname); tmp_method_name[classname_len] = '_'; strcpy(tmp_method_name + classname_len + 1, fn_name); tmp_method_name[classname_len + methodname_len + 1] = 0; return tmp_method_name; } // Analyze call node fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node) { if (!call_node || call_node->type != FZL_NODETYPE_CALL) { an_push_error(analyzer, NULL, "node is not call"); return (fzl_an_res_t){.error = true}; } fzl_node_t *callee = call_node->call.callee; bool is_method = false; char *callee_name = resolve_callee(analyzer, callee, &is_method); if (!callee_name) { an_push_error(analyzer, callee->token, "expression is not callable"); if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } // NOTE: move into `compiler/analyzer/builtins.c` if (strcmp(callee_name, "printline") == 0 || strcmp(callee_name, "exit") == 0) { if (call_node->call.args_count != 1) { snprintf(an_problem, sizeof(an_problem), "'%s' builtin should be called with 1 argument instead of %zu", callee_name, call_node->call.args_count); an_push_error(analyzer, call_node->token, an_problem); if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){ .error = false, .type = FZL_TYPE_NULL, .classname = NULL}; } fzl_sym_t sym; bool sym_found = false; for (ssize_t i = analyzer->symbols_top - 1; i >= 0; i--) { if ((sym_found = fzl_symt_get(analyzer->symbols[i], callee_name, &sym))) break; } if (!sym_found) { snprintf(an_problem, sizeof(an_problem), "cannot resolve symbol '%s'", callee_name); an_push_error(analyzer, call_node->token, an_problem); if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } if (sym.node->type != FZL_NODETYPE_FUNCTION) { snprintf(an_problem, sizeof(an_problem), "'%s' is not callable", callee_name); an_push_error(analyzer, call_node->token, an_problem); if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } size_t offset = is_method ? 1 : 0; if ((sym.node->fn.args_count - offset) != call_node->call.args_count) { if (is_method) { snprintf(an_problem, sizeof(an_problem), "method '%s.%s' is expecting %zu argument(s), got %zu", tmp_classname, tmp_fnname, sym.node->fn.args_count, call_node->call.args_count - offset); } else { snprintf(an_problem, sizeof(an_problem), "function '%s' is expecting %zu argument(s), got %zu", callee_name, sym.node->fn.args_count, call_node->call.args_count - offset); } an_push_error(analyzer, call_node->token, an_problem); if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } for (size_t i = offset; i < sym.node->fn.args_count; i++) { fzl_node_t *arg = sym.node->fn.args[i]; fzl_type_t req_type = an_toktotype(arg->var.type); char *req_classname = NULL; if (req_type == FZL_TYPE_OBJECT) req_classname = arg->var.type->value; fzl_an_res_t got = analyze_common(analyzer, call_node->call.args[i - offset]); if (got.error) { if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } if (!an_is_compatible(req_type, got.type, req_classname, got.classname)) { if (is_method) { snprintf(an_problem, sizeof(an_problem), "method '%s.%s' is expecting %zu argument to have type '%s', " "got '%s'", tmp_classname, tmp_fnname, i + 1 - offset, an_typetostr(req_type, req_classname), an_typetostr(got.type, got.classname)); } else { snprintf(an_problem, sizeof(an_problem), "function '%s' is expecting %zu argument to have type '%s', " "got '%s'", callee_name, i + 1 - offset, an_typetostr(req_type, req_classname), an_typetostr(got.type, got.classname)); } an_push_error(analyzer, call_node->token, an_problem); if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = true}; } } fzl_type_t type = an_toktotype(sym.node->fn.type); char *classname = sym.classname; if (tmp_method_name != NULL) { free(tmp_method_name); tmp_method_name = NULL; } return (fzl_an_res_t){.error = false, .type = type, .classname = classname}; }