/* =====================================
 * Generated by Mod-c 0.26.5
 * MODULE codegen_mh
 * ===================================== */

#include "codegen_mh.h"

void codegen_mh(CodegenContext ctx);

/**
 * codegen_mh.mc - Emit modc-mh/1 symbol export files for EXPORT declarations.
 */
#include "dynbuf.h"
#include "Lexer.h"
#include "ttype.h"
#include "stddef.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "version.h"
#include "node.h"
#include "codegen_common.h"
static bool mh_proc_complete(const Node* n)
{
    if ((((*n)).proc_decl).is_forward)
    {
        /* return value captured before defers */
        bool __ret_l26 = false;
        return __ret_l26;
    }
    if ((((*n)).proc_decl).is_extern && (((*n)).proc_decl).params == nil)
    {
        /* return value captured before defers */
        bool __ret_l29 = false;
        return __ret_l29;
    }
    /* return value captured before defers */
    bool __ret_l31 = true;
    return __ret_l31;
}

static bool mh_type_opaque(const Node* n)
{
    const TType* ty = nil;
    if ((((*n)).type_decl).is_extern)
    {
        /* return value captured before defers */
        bool __ret_l40 = true;
        return __ret_l40;
    }
    ty = (((*n)).type_decl).defined_type;
    if (ty != nil)
    {
        if ((((*ty)).name == nil || ((*ty)).name_len == 0) && ((*ty)).resolved_type != nil)
        {
            ty = ((*ty)).resolved_type;
        }
        if (((*ty)).is_opaque && !((*ty)).is_pointer)
        {
            /* return value captured before defers */
            bool __ret_l49 = true;
            return __ret_l49;
        }
    }
    /* return value captured before defers */
    bool __ret_l52 = false;
    return __ret_l52;
}

typedef const char* pcchar;
static bool mh_first_formal_type_name(const Node* n, pcchar* out_name, size_t* out_len)
{
    const Node* pl = nil;
    const Node* self_param = nil;
    const TType* pt = nil;
    (*out_name) = nil;
    (*out_len) = 0;
    if (n == nil)
    {
        /* return value captured before defers */
        bool __ret_l69 = false;
        return __ret_l69;
    }
    pl = (((*n)).proc_decl).params;
    if (pl == nil || ((*pl)).kind != NODE_PARAM_LIST || (((*pl)).param_list).count == 0)
    {
        /* return value captured before defers */
        bool __ret_l73 = false;
        return __ret_l73;
    }
    self_param = (((*pl)).param_list).params[0];
    if (self_param == nil || ((*self_param)).kind != NODE_PARAM)
    {
        /* return value captured before defers */
        bool __ret_l77 = false;
        return __ret_l77;
    }
    pt = (((*self_param)).param).param_type;
    if (pt == nil)
    {
        /* return value captured before defers */
        bool __ret_l81 = false;
        return __ret_l81;
    }
    if (((*pt)).resolved_type != nil)
    {
        pt = ((*pt)).resolved_type;
    }
    if (((*pt)).is_pointer || ((*pt)).is_array)
    {
        /* return value captured before defers */
        bool __ret_l87 = false;
        return __ret_l87;
    }
    if (((*pt)).name == nil || ((*pt)).name_len == 0)
    {
        /* return value captured before defers */
        bool __ret_l90 = false;
        return __ret_l90;
    }
    (*out_name) = ((*pt)).name;
    (*out_len) = ((*pt)).name_len;
    /* return value captured before defers */
    bool __ret_l94 = true;
    return __ret_l94;
}

static bool mh_method_is_instance(const Node* n)
{
    const char* ft_name = nil;
    size_t ft_len = 0;
    if (n == nil || (((*n)).proc_decl).method_owner == nil || (((*n)).proc_decl).method_owner_len == 0)
    {
        /* return value captured before defers */
        bool __ret_l105 = false;
        return __ret_l105;
    }
    if (!mh_first_formal_type_name(n, &ft_name, &ft_len))
    {
        /* return value captured before defers */
        bool __ret_l108 = false;
        return __ret_l108;
    }
    if (ft_len != (((*n)).proc_decl).method_owner_len)
    {
        /* return value captured before defers */
        bool __ret_l111 = false;
        return __ret_l111;
    }
    /* return value captured before defers */
    bool __ret_l113 = memcmp(ft_name, (((*n)).proc_decl).method_owner, ft_len) == 0;
    return __ret_l113;
}

static void mh_append_type_spec(DynBuf* out, const TType* t)
{
    const TType* ty = t;
    if (out == nil || ty == nil)
    {
                return;
    }
    if ((((*ty)).name == nil || ((*ty)).name_len == 0) && ((*ty)).resolved_type != nil)
    {
        ty = ((*ty)).resolved_type;
    }
    if (((*ty)).is_const)
    {
        dynbuf_append(out, "const ");
    }
    if (((*ty)).is_pointer)
    {
        dynbuf_append_char(out, '^');
    }
    if (((*ty)).name != nil && ((*ty)).name_len > 0)
    {
        dynbuf_appendn(out, ((*ty)).name, ((*ty)).name_len);
        if (((*ty)).width > 0)
        {
            char wbuf[16] = {0};
            snprintf(&wbuf[0], 16, " %d", ((*ty)).width);
            dynbuf_append(out, &wbuf[0]);
        }
    }
    else if (((*ty)).is_opaque)
    {
        dynbuf_append(out, "opaque");
    }
    else
    {
        dynbuf_append(out, "?");
    }
}

static void mh_append_formal(DynBuf* out, const Node* param)
{
    if (out == nil || param == nil || ((*param)).kind != NODE_PARAM)
    {
                return;
    }
    if ((((*param)).param).is_var)
    {
        dynbuf_append(out, "var ");
    }
    else if ((((*param)).param).is_ref)
    {
        dynbuf_append(out, "ref ");
    }
    else if ((((*param)).param).is_const)
    {
        dynbuf_append(out, "const ");
    }
    mh_append_type_spec(out, (((*param)).param).param_type);
}

static void mh_append_signature(DynBuf* out, const Node* params_list, const TType* return_type, bool emit_result)
{
    size_t i = 0;
    const Node* p = nil;
    size_t count = 0;
    if (out == nil)
    {
                return;
    }
    if (params_list != nil && ((*params_list)).kind == NODE_PARAM_LIST)
    {
        count = (((*params_list)).param_list).count;
    }
    if (count == 0 && !(emit_result && return_type != nil))
    {
                return;
    }
    dynbuf_append_char(out, ' ');
    dynbuf_append_char(out, '(');
    {
        const long long __for_end_l203 = (long long)(count);
        for (i = 1; (long long)(i) <= __for_end_l203; i++)
        {
            {
                p = (((*params_list)).param_list).params[i - 1];
                if (i > 1)
                {
                    dynbuf_append(out, ", ");
                }
                mh_append_formal(out, p);
            }
        }
    }
    dynbuf_append_char(out, ')');
    if (emit_result && return_type != nil)
    {
        dynbuf_append(out, " : ");
        mh_append_type_spec(out, return_type);
    }
}

static void mh_append_type_signature(DynBuf* out, const Node* mt)
{
    size_t i = 0;
    const Node* p = nil;
    size_t count = 0;
    const Node* pl = nil;
    if (out == nil || mt == nil || ((*mt)).kind != NODE_METHOD_TYPE)
    {
                return;
    }
    if ((((*mt)).method_type).is_function)
    {
        dynbuf_append(out, " function");
    }
    else
    {
        dynbuf_append(out, " procedure");
    }
    pl = (((*mt)).method_type).params;
    if (pl != nil && ((*pl)).kind == NODE_PARAM_LIST)
    {
        count = (((*pl)).param_list).count;
    }
    dynbuf_append_char(out, ' ');
    dynbuf_append_char(out, '(');
    {
        const long long __for_end_l243 = (long long)(count);
        for (i = 1; (long long)(i) <= __for_end_l243; i++)
        {
            {
                p = (((*pl)).param_list).params[i - 1];
                if (i > 1)
                {
                    dynbuf_append(out, ", ");
                }
                mh_append_formal(out, p);
            }
        }
    }
    dynbuf_append_char(out, ')');
    if ((((*mt)).method_type).is_function && (((*mt)).method_type).return_type != nil)
    {
        dynbuf_append(out, " : ");
        mh_append_type_spec(out, (((*mt)).method_type).return_type);
    }
}

static void mh_append_unit_signature(DynBuf* out, const Node* n)
{
    size_t i = 0;
    const Node* p = nil;
    size_t count = 0;
    const TType* ret = nil;
    bool emit_result = false;
    if (out == nil || n == nil || ((*n)).kind != NODE_PROGRAM)
    {
                return;
    }
    count = (((*n)).program_decl).param_count;
    ret = (((*n)).program_decl).return_type;
    emit_result = (ret != nil);
    if (count == 0 && !emit_result)
    {
                return;
    }
    dynbuf_append_char(out, ' ');
    dynbuf_append_char(out, '(');
    {
        const long long __for_end_l284 = (long long)(count);
        for (i = 1; (long long)(i) <= __for_end_l284; i++)
        {
            {
                p = (((*n)).program_decl).params[i - 1];
                if (i > 1)
                {
                    dynbuf_append(out, ", ");
                }
                mh_append_formal(out, p);
            }
        }
    }
    dynbuf_append_char(out, ')');
    if (emit_result)
    {
        dynbuf_append(out, " : ");
        mh_append_type_spec(out, ret);
    }
}

static void mh_emit_line(DynBuf* out, const char* line)
{
    dynbuf_append(out, line);
    dynbuf_append_char(out, '\n');
}

static void mh_append_ident(DynBuf* out, const char* s, size_t length)
{
    if (out == nil || s == nil || length == 0)
    {
                return;
    }
    dynbuf_appendn(out, s, length);
}

static void mh_append_field_list(DynBuf* out, const Node* st)
{
    size_t i = 0;
    const Node* f = nil;
    const TType* ext = nil;
    size_t emitted = 0;
    if (out == nil || st == nil || ((*st)).kind != NODE_STRUCT_DECL)
    {
                return;
    }
    dynbuf_append_char(out, ' ');
    if ((((*st)).struct_decl).is_union)
    {
        dynbuf_append(out, "union");
    }
    else
    {
        dynbuf_append(out, "struct");
    }
    ext = (((*st)).struct_decl).extends_type;
    if (ext != nil)
    {
        if ((((*ext)).name == nil || ((*ext)).name_len == 0) && ((*ext)).resolved_type != nil)
        {
            ext = ((*ext)).resolved_type;
        }
        if (((*ext)).name != nil && ((*ext)).name_len > 0)
        {
            dynbuf_append(out, " extends ");
            mh_append_ident(out, ((*ext)).name, ((*ext)).name_len);
        }
    }
    dynbuf_append(out, " (");
    {
        const long long __for_end_l350 = (long long)((((*st)).struct_decl).field_count);
        for (i = 1; (long long)(i) <= __for_end_l350; i++)
        {
            {
                f = (((*st)).struct_decl).fields[i - 1];
                if (f == nil || ((*f)).kind != NODE_FIELD_DECL)
                {
                                        /* INVARIANT (on loop exit) */
                    continue;
                }
                if (emitted > 0)
                {
                    dynbuf_append(out, ", ");
                }
                mh_append_ident(out, (((*f)).field_decl).name, (((*f)).field_decl).name_len);
                dynbuf_append(out, ": ");
                if ((((*f)).field_decl).field_type != nil)
                {
                    mh_append_type_spec(out, (((*f)).field_decl).field_type);
                }
                else
                {
                    dynbuf_append(out, "?");
                }
                emitted = emitted + 1;
            }
        }
    }
    dynbuf_append_char(out, ')');
}

static void mh_emit_export_proc(DynBuf* out, const Node* n)
{
    const char* name = (((*n)).proc_decl).name;
    size_t name_len = (((*n)).proc_decl).name_len;
    const char* kind_word = "procedure";
    const char* complete_word = "complete";
    const char* binding = nil;
    const char* owner = nil;
    size_t owner_len = 0;
    if (((*n)).kind == NODE_FUNC_DECL)
    {
        kind_word = "function";
    }
    if (!mh_proc_complete(n))
    {
        complete_word = "incomplete";
    }
    if ((((*n)).proc_decl).method_owner != nil && (((*n)).proc_decl).method_owner_len > 0)
    {
        owner = (((*n)).proc_decl).method_owner;
        owner_len = (((*n)).proc_decl).method_owner_len;
        if (mh_method_is_instance(n))
        {
            binding = "instance";
        }
        else
        {
            binding = "static";
        }
    }
    dynbuf_append(out, "export ");
    if ((((*n)).proc_decl).is_extern)
    {
        dynbuf_append(out, "extern ");
    }
    dynbuf_append(out, kind_word);
    dynbuf_append_char(out, ' ');
    if (owner != nil && owner_len > 0)
    {
        mh_append_ident(out, owner, owner_len);
        dynbuf_append(out, "__");
        mh_append_ident(out, (((*n)).proc_decl).name, (((*n)).proc_decl).name_len);
    }
    else
    {
        mh_append_ident(out, name, name_len);
    }
    dynbuf_append_char(out, ' ');
    dynbuf_append(out, complete_word);
    if (binding != nil)
    {
        dynbuf_append_char(out, ' ');
        dynbuf_append(out, binding);
        dynbuf_append_char(out, ' ');
        mh_append_ident(out, owner, owner_len);
    }
    mh_append_signature(out, (((*n)).proc_decl).params, (((*n)).proc_decl).return_type, ((*n)).kind == NODE_FUNC_DECL);
    dynbuf_append_char(out, '\n');
}

static void mh_emit_export_enum_members(DynBuf* out, const Node* en)
{
    size_t i = 0;
    Node* item = nil;
    if (en == nil || ((*en)).kind != NODE_ENUM_TYPE)
    {
                return;
    }
    {
        const long long __for_end_l435 = (long long)((((*en)).enum_type).count);
        for (i = 1; (long long)(i) <= __for_end_l435; i++)
        {
            {
                item = (((*en)).enum_type).elements[i - 1];
                if (item == nil || ((*item)).kind != NODE_ENUM_ITEM)
                {
                                        /* INVARIANT (on loop exit) */
                    continue;
                }
                dynbuf_append(out, "export const ");
                mh_append_ident(out, (((*item)).enum_item).name, (((*item)).enum_item).name_len);
                dynbuf_append(out, " complete");
                dynbuf_append_char(out, '\n');
            }
        }
    }
}

static void mh_emit_export_type(DynBuf* out, const Node* n)
{
    const char* flag = "complete";
    if ((((*n)).type_decl).is_forward)
    {
                return;
    }
    if ((((*n)).type_decl).enum_type != nil)
    {
        dynbuf_append(out, "export enum ");
        mh_append_ident(out, (((*n)).type_decl).name, (((*n)).type_decl).name_len);
        dynbuf_append(out, " complete");
        dynbuf_append_char(out, '\n');
        mh_emit_export_enum_members(out, (((*n)).type_decl).enum_type);
                return;
    }
    if (mh_type_opaque(n))
    {
        flag = "opaque";
    }
    dynbuf_append(out, "export type ");
    mh_append_ident(out, (((*n)).type_decl).name, (((*n)).type_decl).name_len);
    dynbuf_append_char(out, ' ');
    dynbuf_append(out, flag);
    if ((((*n)).type_decl).struct_body != nil)
    {
        mh_append_field_list(out, (((*n)).type_decl).struct_body);
    }
    if ((((*n)).type_decl).method_type != nil)
    {
        mh_append_type_signature(out, (((*n)).type_decl).method_type);
    }
    dynbuf_append_char(out, '\n');
}

static void mh_emit_export_binding(DynBuf* out, const Node* n, const char* kind_word)
{
    size_t i = 0;
    const char* complete_word = "complete";
    if ((((*n)).var_decl).is_extern)
    {
        complete_word = "incomplete";
    }
    {
        const long long __for_end_l491 = (long long)((((*n)).var_decl).count);
        for (i = 1; (long long)(i) <= __for_end_l491; i++)
        {
            {
                Node* item = (((*n)).var_decl).items[i - 1];
                dynbuf_append(out, "export ");
                dynbuf_append(out, kind_word);
                dynbuf_append_char(out, ' ');
                mh_append_ident(out, (((*item)).var_item).name, (((*item)).var_item).name_len);
                dynbuf_append_char(out, ' ');
                dynbuf_append(out, complete_word);
                dynbuf_append_char(out, '\n');
            }
        }
    }
}

/**
 * Emit MODULE/PROGRAM unit entry (name matches unit; not an EXPORT decl in source).
 * Mirros codegen_common_module_prototype() in generated .h files.
 */
static void mh_emit_unit_entry(DynBuf* out, const Node* n)
{
    const char* kind_word = "procedure";
    if ((((*n)).program_decl).return_type != nil)
    {
        kind_word = "function";
    }
    dynbuf_append(out, "export ");
    dynbuf_append(out, kind_word);
    dynbuf_append_char(out, ' ');
    mh_append_ident(out, (((*n)).program_decl).name, (((*n)).program_decl).name_len);
    dynbuf_append(out, " complete");
    mh_append_unit_signature(out, n);
    dynbuf_append_char(out, '\n');
}

static void mh_emit_exported_decl(DynBuf* out, const Node* d)
{
    if (d == nil)
    {
                return;
    }
    if (((*d)).kind == NODE_PROC_DECL || ((*d)).kind == NODE_FUNC_DECL)
    {
        if ((((*d)).proc_decl).is_exported)
        {
            mh_emit_export_proc(out, d);
        }
    }
    else if (((*d)).kind == NODE_TYPE_DECL)
    {
        if ((((*d)).type_decl).is_exported)
        {
            mh_emit_export_type(out, d);
        }
    }
    else if (((*d)).kind == NODE_VAR_DECL)
    {
        if ((((*d)).var_decl).is_exported)
        {
            mh_emit_export_binding(out, d, "var");
        }
    }
    else if (((*d)).kind == NODE_CONST_DECL)
    {
        if ((((*d)).const_decl).is_exported)
        {
            mh_emit_export_binding(out, d, "const");
        }
    }
    else if (((*d)).kind == NODE_LET_DECL)
    {
        if ((((*d)).let_decl).is_exported)
        {
            mh_emit_export_binding(out, d, "const");
        }
    }
}

bool codegen_mh_has_exports(const Node* root)
{
    size_t i = 0;
    if (root == nil || ((*root)).kind != NODE_PROGRAM)
    {
        /* return value captured before defers */
        bool __ret_l560 = false;
        return __ret_l560;
    }
    {
        const long long __for_end_l563 = (long long)((((*root)).program_decl).count);
        for (i = 1; (long long)(i) <= __for_end_l563; i++)
        {
            {
                Node* d = (((*root)).program_decl).decls[i - 1];
                if (((*d)).kind == NODE_PROC_DECL || ((*d)).kind == NODE_FUNC_DECL)
                {
                    if ((((*d)).proc_decl).is_exported)
                    {
                        /* return value captured before defers */
                        bool __ret_l567 = true;
                        return __ret_l567;
                    }
                }
                else if (((*d)).kind == NODE_TYPE_DECL)
                {
                    if ((((*d)).type_decl).is_exported)
                    {
                        /* return value captured before defers */
                        bool __ret_l571 = true;
                        return __ret_l571;
                    }
                }
                else if (((*d)).kind == NODE_VAR_DECL)
                {
                    if ((((*d)).var_decl).is_exported)
                    {
                        /* return value captured before defers */
                        bool __ret_l575 = true;
                        return __ret_l575;
                    }
                }
                else if (((*d)).kind == NODE_CONST_DECL)
                {
                    if ((((*d)).const_decl).is_exported)
                    {
                        /* return value captured before defers */
                        bool __ret_l579 = true;
                        return __ret_l579;
                    }
                }
                else if (((*d)).kind == NODE_LET_DECL)
                {
                    if ((((*d)).let_decl).is_exported)
                    {
                        /* return value captured before defers */
                        bool __ret_l583 = true;
                        return __ret_l583;
                    }
                }
            }
        }
    }
    /* return value captured before defers */
    bool __ret_l588 = false;
    return __ret_l588;
}

/**
 * Main entry point for this module, codegen_mh()
 */

void codegen_mh(CodegenContext ctx)
{
    const Node* n = (ctx).ast;
    DynBuf* out = (ctx).out;
    char line_buf[256] = {0};
    size_t i = 0;
    if (n == nil || out == nil || ((*n)).kind != NODE_PROGRAM)
    {
        fprintf(stderr, "FATAL ERROR: codegen_mh: expected PROGRAM node\n");
        exit(1);
    }
    mh_emit_line(out, "# modc-mh/1");
    snprintf(&line_buf[0], 256, "# Generated by Mod-c %s", VERSION_BASE);
    mh_emit_line(out, &line_buf[0]);
    dynbuf_append(out, "@module ");
    mh_append_ident(out, (((*n)).program_decl).name, (((*n)).program_decl).name_len);
    dynbuf_append_char(out, '\n');
    mh_emit_line(out, "@format 1");
    {
        const long long __for_end_l614 = (long long)((((*n)).program_decl).count);
        for (i = 1; (long long)(i) <= __for_end_l614; i++)
        {
            {
                mh_emit_exported_decl(out, (((*n)).program_decl).decls[i - 1]);
            }
        }
    }
    mh_emit_unit_entry(out, n);
}
