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

#include "mh_reader.h"

void mh_reader(void);

/**
 * mh_reader.mc - Parse modc-mh/1 export talbes for IMPORT ... FROM "*.mh".
 */
#include "stddef.h"
#include "string.h"
#include "stdlib.h"
#include "ctype.h"
#include "stdio.h"
#include "symkind.h"
#include "mh_exportkind.h"
#include "ttype.h"
static const MhCompleteness MH_COMPLETENESS_COMPLETE = 0;
static const MhCompleteness MH_COMPLETENESS_INCOMPLETE = 1;
static const MhCompleteness MH_COMPLETENESS_OPAQUE = 2;
/** One formal in an export signature: [var|ref|const] [const] [^] Name */
/** One field in an export type layout: name: [const] [^] Type */
static bool mh_ends_with(const char* s, size_t length, const char* suffix)
{
    size_t suffix_len = 0;
    if (s == nil || suffix == nil)
    {
        /* return value captured before defers */
        bool __ret_l108 = false;
        return __ret_l108;
    }
    suffix_len = strlen(suffix);
    if (length < suffix_len)
    {
        /* return value captured before defers */
        bool __ret_l113 = false;
        return __ret_l113;
    }
    /* return value captured before defers */
    bool __ret_l116 = memcmp(s + (length - suffix_len), suffix, suffix_len) == 0;
    return __ret_l116;
}

/**
 * Strip optional surrounding quotes from a string-literal token.
 */
bool mh_strip_string_literal(const char* path, size_t path_len, pcchar* inner, size_t* inner_len)
{
    if (path == nil || inner == nil || inner_len == nil || path_len == 0)
    {
        /* return value captured before defers */
        bool __ret_l127 = false;
        return __ret_l127;
    }
    if (path_len >= 2 && path[0] == '"' && path[path_len - 1] == '"')
    {
        (*inner) = path + 1;
        (*inner_len) = path_len - 2;
        /* return value captured before defers */
        bool __ret_l133 = true;
        return __ret_l133;
    }
    (*inner) = path;
    (*inner_len) = path_len;
    /* return value captured before defers */
    bool __ret_l138 = true;
    return __ret_l138;
}

static bool mh_import_source_is_quoted(const char* path, size_t path_len)
{
    /* return value captured before defers */
    bool __ret_l144 = path != nil && path_len >= 2 && path[0] == '"';
    return __ret_l144;
}

/**
 * Qualident (unquoted FROM) -> relative filesystem path with .mh suffix.
 e.g. fixtures.minimod -> fixtures/minimod.mh
 */
static bool mh_qualident_to_mh_relpath(pcchar inner, size_t inner_len, char* out, size_t out_cap)
{
    size_t i = 0;
    size_t j = 0;
    if (inner == nil || out == nil || out_cap == 0 || inner_len == 0)
    {
        /* return value captured before defers */
        bool __ret_l159 = false;
        return __ret_l159;
    }
    while (i < inner_len)
    {
        {
            char c = inner[i];
            if (c == '.')
            {
                c = '/';
            }
            if (j + 4 >= out_cap)
            {
                /* return value captured before defers */
                bool __ret_l168 = false;
                return __ret_l168;
            }
            out[j] = c;
            j = j + 1;
            i = i + 1;
        }
    }
    out[j] = '.';
    out[j + 1] = 'm';
    out[j + 2] = 'h';
    out[j + 3] = '\0';
    /* return value captured before defers */
    bool __ret_l179 = true;
    return __ret_l179;
}

bool mh_path_is_mh(const char* path, size_t path_len)
{
    pcchar inner = nil;
    size_t inner_len = 0;
    if (!mh_strip_string_literal(path, path_len, &inner, &inner_len))
    {
        /* return value captured before defers */
        bool __ret_l189 = false;
        return __ret_l189;
    }
    if (!mh_import_source_is_quoted(path, path_len))
    {
        /* return value captured before defers */
        bool __ret_l193 = true;
        return __ret_l193;
    }
    /* return value captured before defers */
    bool __ret_l196 = mh_ends_with(inner, inner_len, ".mh");
    return __ret_l196;
}

/**
 * Format the C #include path for a Mod-c import source (.mh symtab world).
 * Quoted "*.mh" -> same path with .h; qualident -> sources + .h.
 */
bool mh_format_h_include(const char* from_path, size_t from_len, char* out, size_t out_cap)
{
    pcchar inner = nil;
    size_t inner_len = 0;
    size_t i = 0;
    size_t j = 0;
    if (from_path == nil || out == nil || out_cap == 0)
    {
        /* return value captured before defers */
        bool __ret_l213 = false;
        return __ret_l213;
    }
    if (!mh_strip_string_literal(from_path, from_len, &inner, &inner_len))
    {
        /* return value captured before defers */
        bool __ret_l217 = false;
        return __ret_l217;
    }
    if (mh_import_source_is_quoted(from_path, from_len))
    {
        if (!mh_ends_with(inner, inner_len, ".mh") || inner_len < 4)
        {
            /* return value captured before defers */
            bool __ret_l222 = false;
            return __ret_l222;
        }
        if (inner_len - 1 >= out_cap)
        {
            /* return value captured before defers */
            bool __ret_l225 = false;
            return __ret_l225;
        }
        memcpy(out, inner, inner_len - 3);
        out[inner_len - 3] = '.';
        out[inner_len - 2] = 'h';
        out[inner_len - 1] = '\0';
        /* return value captured before defers */
        bool __ret_l231 = true;
        return __ret_l231;
    }
    while (i < inner_len)
    {
        {
            char c = inner[i];
            if (c == '.')
            {
                c = '/';
            }
            if (j + 3 >= out_cap)
            {
                /* return value captured before defers */
                bool __ret_l240 = false;
                return __ret_l240;
            }
            out[j] = c;
            j++;
            i++;
        }
    }
    if (j + 3 >= out_cap)
    {
        /* return value captured before defers */
        bool __ret_l248 = false;
        return __ret_l248;
    }
    out[j] = '.';
    out[j + 1] = 'h';
    out[j + 2] = '\0';
    /* return value captured before defers */
    bool __ret_l253 = true;
    return __ret_l253;
}

static char* mh_strdup_n(const char* s, size_t length)
{
    char* copy = malloc(length + 1);
    if (copy == nil)
    {
        /* return value captured before defers */
        char* __ret_l261 = nil;
        return __ret_l261;
    }
    memcpy(copy, s, length);
    copy[length] = '\0';
    /* return value captured before defers */
    char* __ret_l265 = copy;
    return __ret_l265;
}

static void mh_fatal(const char* path, size_t line_no, const char* msg)
{
    if (path != nil)
    {
        fprintf(stderr, "ERROR: %s:%zu: %s\n", path, line_no, msg);
    }
    else
    {
        fprintf(stderr, "ERROR: %s\n", msg);
    }
    exit(1);
}

static char* mh_dirname_copy(const char* path)
{
    char* slash = nil;
    size_t length = 0;
    if (path == nil || path[0] == '\0')
    {
        /* return value captured before defers */
        char* __ret_l286 = mh_strdup_n(".", 1);
        return __ret_l286;
    }
    slash = strrchr(path, '/');
    if (slash == nil)
    {
        /* return value captured before defers */
        char* __ret_l291 = mh_strdup_n(".", 1);
        return __ret_l291;
    }
    if (slash == path)
    {
        /* return value captured before defers */
        char* __ret_l295 = mh_strdup_n("/", 1);
        return __ret_l295;
    }
    length = (size_t)(slash - path);
    /* return value captured before defers */
    char* __ret_l299 = mh_strdup_n(path, length);
    return __ret_l299;
}

/**
 * Resolve IMPORT apth relative to the compiling .mc file directory.
 */
bool mh_resolve_import_path(const char* source_path, const char* from_path, size_t from_len, char* out, size_t out_cap)
{
    pcchar inner = nil;
    size_t inner_len = 0;
    char rel[4096] = {0};
    size_t rel_len = 0;
    char* dir = nil;
    if (out == nil || out_cap == 0)
    {
        /* return value captured before defers */
        bool __ret_l316 = false;
        /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
        return __ret_l316;
    }
    if (!mh_strip_string_literal(from_path, from_len, &inner, &inner_len))
    {
        /* return value captured before defers */
        bool __ret_l320 = false;
        /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
        return __ret_l320;
    }
    if (mh_import_source_is_quoted(from_path, from_len))
    {
        rel_len = inner_len;
        if (rel_len + 1 > ((integer)sizeof(rel)))
        {
            /* return value captured before defers */
            bool __ret_l328 = false;
            /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
            return __ret_l328;
        }
        memcpy(&rel[0], inner, rel_len);
        rel[rel_len] = '\0';
    }
    else
    {
        if (!mh_qualident_to_mh_relpath(inner, inner_len, &rel[0], ((integer)sizeof(rel))))
        {
            /* return value captured before defers */
            bool __ret_l334 = false;
            /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
            return __ret_l334;
        }
        rel_len = strlen(&rel[0]);
    }
    if (rel_len > 0 && rel[0] == '/')
    {
        if (rel_len + 1 > out_cap)
        {
            /* return value captured before defers */
            bool __ret_l344 = false;
            /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
            return __ret_l344;
        }
        memcpy(out, &rel[0], rel_len);
        out[rel_len] = '\0';
        /* return value captured before defers */
        bool __ret_l348 = true;
        /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
        return __ret_l348;
    }
    dir = mh_dirname_copy(source_path);
    if (dir == nil)
    {
        /* return value captured before defers */
        bool __ret_l353 = false;
        /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
        return __ret_l353;
    }
    /* DEFER begin */
    if (dir[strlen(dir) - 1] != '/')
    {
        snprintf(out, out_cap, "%s/%s", dir, &rel[0]);
    }
    else
    {
        snprintf(out, out_cap, "%s%s", dir, &rel[0]);
    }
    /* return value captured before defers */
    bool __ret_l365 = true;
    /* DEFER cleanup (unwinding to function) */
    /* DEFER end */
    free(dir);
    return __ret_l365;
}

static void mh_module_grow(MhModule* m)
{
    size_t new_cap = 0;
    MhExport* new_exports = nil;
    if (((*m)).count < ((*m)).capacity)
    {
                return;
    }
    new_cap = (((*m)).capacity == 0 ? 8u : ((*m)).capacity * 2u);
    new_exports = (MhExport*)realloc(((*m)).exports, new_cap * ((integer)sizeof(MhExport)));
    if (new_exports == nil)
    {
        mh_fatal(nil, 0, "mh_reader_load: out of memory");
    }
    ((*m)).exports = new_exports;
    ((*m)).capacity = new_cap;
}

static void mh_module_add(MhModule* m, MhExport exp)
{
    mh_module_grow(m);
    ((*m)).exports[((*m)).count] = exp;
    ((*m)).count = ((*m)).count + 1;
}

SymbolKind mh_export_to_symkind(MhExportKind kind)
{
    switch (kind)
    {
        case MH_KIND_PROCEDURE:
        {
            /* return value captured before defers */
            SymbolKind __ret_l400 = SYM_KIND_PROC;
            return __ret_l400;
        }
        break;
        case MH_KIND_FUNCTION:
        {
            /* return value captured before defers */
            SymbolKind __ret_l401 = SYM_KIND_FUNC;
            return __ret_l401;
        }
        break;
        case MH_KIND_TYPE:
        {
            /* return value captured before defers */
            SymbolKind __ret_l402 = SYM_KIND_TYPE;
            return __ret_l402;
        }
        break;
        case MH_KIND_ENUM:
        {
            /* return value captured before defers */
            SymbolKind __ret_l403 = SYM_KIND_TYPE;
            return __ret_l403;
        }
        break;
        case MH_KIND_VAR:
        {
            /* return value captured before defers */
            SymbolKind __ret_l404 = SYM_KIND_VAR;
            return __ret_l404;
        }
        break;
        case MH_KIND_CONST:
        {
            /* return value captured before defers */
            SymbolKind __ret_l405 = SYM_KIND_CONST;
            return __ret_l405;
        }
        break;
        default:
        {
            /* return value captured before defers */
            SymbolKind __ret_l406 = SYM_KIND_UNKNOWN;
            return __ret_l406;
        }
        break;
    }
}

const MhExport* mh_module_find(const MhModule* m, const char* name, size_t name_len)
{
    size_t i = 0;
    if (m == nil || name == nil || name_len == 0)
    {
        /* return value captured before defers */
        const MhExport* __ret_l417 = nil;
        return __ret_l417;
    }
    {
        const long long __for_end_l420 = (long long)(((*m)).count);
        for (i = 1; (long long)(i) <= __for_end_l420; i++)
        {
            {
                MhExport* exp = &((*m)).exports[i - 1];
                if (((*exp)).name_len == name_len && memcmp(((*exp)).name, name, name_len) == 0)
                {
                    /* return value captured before defers */
                    const MhExport* __ret_l423 = exp;
                    return __ret_l423;
                }
            }
        }
    }
    /* return value captured before defers */
    const MhExport* __ret_l427 = nil;
    return __ret_l427;
}

void mh_module_free(MhModule* m)
{
    size_t i = 0;
    if (m == nil)
    {
                return;
    }
    ((*m)).module_name = nil;
    ((*m)).module_name_len = 0;
    {
        const long long __for_end_l442 = (long long)(((*m)).count);
        for (i = 1; (long long)(i) <= __for_end_l442; i++)
        {
            {
                if ((((*m)).exports[i - 1]).formals != nil)
                {
                    free((((*m)).exports[i - 1]).formals);
                    (((*m)).exports[i - 1]).formals = nil;
                    (((*m)).exports[i - 1]).formal_count = 0;
                }
                if ((((*m)).exports[i - 1]).fields != nil)
                {
                    free((((*m)).exports[i - 1]).fields);
                    (((*m)).exports[i - 1]).fields = nil;
                    (((*m)).exports[i - 1]).field_count = 0;
                }
            }
        }
    }
    if (((*m)).exports != nil)
    {
        free(((*m)).exports);
        ((*m)).exports = nil;
    }
    if (((*m)).source != nil)
    {
        free(((*m)).source);
        ((*m)).source = nil;
    }
    ((*m)).source_len = 0;
    ((*m)).count = 0;
    ((*m)).capacity = 0;
    ((*m)).format_version = 0;
}

/** --- line parser helpers (kept local) --- **/
static void mh_skip_ws(pchar* p, pchar end_)
{
    while ((*p) < end_ && ((*(*p)) == ' ' || (*(*p)) == '\t' || (*(*p)) == '\r'))
    {
        {
            (*p) = (*p) + 1;
        }
    }
}

static bool mh_match_word(pchar* p, char* end_, const char* word)
{
    size_t wlen = strlen(word);
    mh_skip_ws(p, end_);
    if ((size_t)(end_ - (*p)) < wlen)
    {
        /* return value captured before defers */
        bool __ret_l486 = false;
        return __ret_l486;
    }
    if (memcmp((*p), word, wlen) != 0)
    {
        /* return value captured before defers */
        bool __ret_l489 = false;
        return __ret_l489;
    }
    if ((size_t)(end_ - (*p)) > wlen)
    {
        char next = (*((*p) + wlen));
        if (isalnum((int)next) || next == '_')
        {
            /* return value captured before defers */
            bool __ret_l494 = false;
            return __ret_l494;
        }
    }
    (*p) = (*p) + wlen;
    /* return value captured before defers */
    bool __ret_l498 = true;
    return __ret_l498;
}

static bool mh_read_ident(pchar* p, char* end_, pchar* name, size_t* name_len)
{
    char* start = nil;
    mh_skip_ws(p, end_);
    if ((*p) >= end_)
    {
        /* return value captured before defers */
        bool __ret_l509 = false;
        return __ret_l509;
    }
    if (!isalpha((int)(*(*p))) && (*(*p)) != '_')
    {
        /* return value captured before defers */
        bool __ret_l512 = false;
        return __ret_l512;
    }
    start = (*p);
    (*p) = (*p) + 1;
    while ((*p) < end_ && (isalnum((int)(*(*p))) || (*(*p)) == '_'))
    {
        {
            (*p) = (*p) + 1;
        }
    }
    (*name) = start;
    (*name_len) = (size_t)((*p) - start);
    if ((*name_len) > 255)
    {
        mh_fatal(nil, 0, "mh_read_ident: identifier longer than 255 characters");
    }
    /* return value captured before defers */
    bool __ret_l524 = (*name_len) > 0;
    return __ret_l524;
}

static bool mh_match_char(pchar* p, char* end_, char ch)
{
    mh_skip_ws(p, end_);
    if ((*p) >= end_ || (*(*p)) != ch)
    {
        /* return value captured before defers */
        bool __ret_l532 = false;
        return __ret_l532;
    }
    (*p) = (*p) + 1;
    /* return value captured before defers */
    bool __ret_l535 = true;
    return __ret_l535;
}

static bool mh_peek_char(pchar peek, pchar end_, char ch)
{
    pchar p = peek;
    while (p < end_ && ((*p) == ' ' || (*p) == '\t' || (*p) == '\r'))
    {
        {
            p = p + 1;
        }
    }
    /* return value captured before defers */
    bool __ret_l547 = p < end_ && (*p) == ch;
    return __ret_l547;
}

static int mh_parse_width_number(const char* path, size_t line_no, pchar* p, pchar end_)
{
    int n = 0;
    int digits = 0;
    if ((*p) < end_ && (*(*p)) == '0')
    {
        (*p) = (*p) + 1;
        if ((*p) < end_ && isdigit((int)(*(*p))))
        {
            mh_fatal(path, line_no, "mh_parse_width_number: width must be a decimal integer");
        }
        /* return value captured before defers */
        int __ret_l566 = 0;
        return __ret_l566;
    }
    while ((*p) < end_ && isdigit((int)(*(*p))))
    {
        {
            if (digits >= 2)
            {
                mh_fatal(path, line_no, "mh_parse_width_number: unsupported width for this type");
            }
            n = n * 10 + ((*(*p)) - '0');
            digits = digits + 1;
            (*p) = (*p) + 1;
        }
    }
    if (digits == 0)
    {
        mh_fatal(path, line_no, "mh_parse_width_number: expected width");
    }
    /* return value captured before defers */
    int __ret_l580 = n;
    return __ret_l580;
}

static bool mh_parse_type_spec(const char* path, size_t line_no, pchar* p, pchar end_, bool* out_is_const, bool* out_is_pointer, pchar* out_name, size_t* out_name_len, int* out_width)
{
    char* name = nil;
    size_t name_len = 0;
    char* start = nil;
    char* more = nil;
    size_t more_len = 0;
    char* save = nil;
    (*out_is_const) = false;
    (*out_is_pointer) = false;
    (*out_name) = nil;
    (*out_name_len) = 0;
    (*out_width) = 0;
    if (mh_match_word(p, end_, "const"))
    {
        (*out_is_const) = true;
    }
    if (mh_match_char(p, end_, '^'))
    {
        (*out_is_pointer) = true;
    }
    if (!mh_read_ident(p, end_, &name, &name_len))
    {
        mh_fatal(path, line_no, "mh_parse_type_spec: expected type name");
    }
    start = name;
    save = (*p);
    mh_skip_ws(p, end_);
    if ((*p) < end_ && isdigit((int)(*(*p))))
    {
        int w = 0;
        w = mh_parse_width_number(path, line_no, p, end_);
        if (!type_width_allowed(name, name_len, w))
        {
            mh_fatal(path, line_no, "mh_parse_type_spec: unsupported width for this type");
        }
        (*out_width) = w;
    }
    else
    {
        (*p) = save;
        while (true)
        {
            {
                save = (*p);
                mh_skip_ws(p, end_);
                if ((*p) >= end_)
                {
                    (*p) = save;
                                        /* INVARIANT (on loop exit) */
                    break;
                }
                if ((*(*p)) == ',' || (*(*p)) == ')' || (*(*p)) == ':')
                {
                    (*p) = save;
                                        /* INVARIANT (on loop exit) */
                    break;
                }
                if (!isalpha((int)(*(*p))) && (*(*p)) != '_')
                {
                    (*p) = save;
                                        /* INVARIANT (on loop exit) */
                    break;
                }
                if (!mh_read_ident(p, end_, &more, &more_len))
                {
                    (*p) = save;
                                        /* INVARIANT (on loop exit) */
                    break;
                }
                name_len = (size_t)(more + more_len - start);
            }
        }
    }
    (*out_name) = start;
    (*out_name_len) = name_len;
    /* return value captured before defers */
    bool __ret_l659 = true;
    return __ret_l659;
}

static bool mh_parse_formal(const char* path, size_t line_no, pchar* p, pchar end_, MhFormal* out)
{
    bool t_const = false;
    bool t_ptr = false;
    char* t_name = nil;
    size_t t_len = 0;
    int t_width = 0;
    bool mode_const = false;
    ((*out)).is_var = false;
    ((*out)).is_ref = false;
    ((*out)).is_const = false;
    ((*out)).type_is_const = false;
    ((*out)).type_is_pointer = false;
    ((*out)).type_name = nil;
    ((*out)).type_name_len = 0;
    ((*out)).type_width = 0;
    if (mh_match_word(p, end_, "var"))
    {
        ((*out)).is_var = true;
    }
    else if (mh_match_word(p, end_, "ref"))
    {
        ((*out)).is_ref = true;
    }
    else if (mh_match_word(p, end_, "const"))
    {
        if (mh_peek_char((*p), end_, '^'))
        {
            t_const = true;
            if (mh_match_char(p, end_, '^'))
            {
                t_ptr = true;
            }
            if (!mh_read_ident(p, end_, &t_name, &t_len))
            {
                mh_fatal(path, line_no, "mh_parse_formal: expected type name after const ^");
            }
            ((*out)).type_is_const = true;
            ((*out)).type_is_pointer = t_ptr;
            ((*out)).type_name = t_name;
            ((*out)).type_name_len = t_len;
            ((*out)).type_width = t_width;
            /* return value captured before defers */
            bool __ret_l713 = true;
            return __ret_l713;
        }
        else
        {
            mode_const = true;
            ((*out)).is_const = true;
        }
    }
    if (!mh_parse_type_spec(path, line_no, p, end_, &t_const, &t_ptr, &t_name, &t_len, &t_width))
    {
        /* return value captured before defers */
        bool __ret_l723 = false;
        return __ret_l723;
    }
    ((*out)).type_is_const = t_const;
    ((*out)).type_is_pointer = t_ptr;
    ((*out)).type_name = mh_strdup_n(t_name, t_len);
    ((*out)).type_name_len = t_len;
    ((*out)).type_width = t_width;
    if (((*out)).type_name == nil)
    {
        mh_fatal(path, line_no, "mh_parse_formal: out of memory");
    }
    if (mode_const)
    {
    }
    /* return value captured before defers */
    bool __ret_l736 = true;
    return __ret_l736;
}

static void mh_parse_signature(const char* path, size_t line_no, pchar* p, pchar end_, MhExport* exp)
{
    size_t cap = 0;
    MhFormal* formals = nil;
    size_t count = 0;
    MhFormal f = {0};
    bool t_const = false;
    bool t_ptr = false;
    char* t_name = nil;
    size_t t_len = 0;
    int t_width = 0;
    ((*exp)).has_signature = false;
    ((*exp)).formals = nil;
    ((*exp)).formal_count = 0;
    ((*exp)).has_result = false;
    ((*exp)).result_is_const = false;
    ((*exp)).result_name = nil;
    ((*exp)).result_name_len = 0;
    ((*exp)).result_width = 0;
    if (!mh_match_char(p, end_, '('))
    {
                return;
    }
    ((*exp)).has_signature = true;
    mh_skip_ws(p, end_);
    if (!mh_peek_char((*p), end_, ')'))
    {
        while (true)
        {
            {
                if (count >= cap)
                {
                    size_t new_cap = (cap == 0 ? 4u : cap * 2u);
                    MhFormal* neu = (MhFormal*)realloc(formals, new_cap * ((integer)sizeof(MhFormal)));
                    if (neu == nil)
                    {
                        mh_fatal(path, line_no, "mh_parse_signature: out of memory");
                    }
                    formals = neu;
                    cap = new_cap;
                }
                if (!mh_parse_formal(path, line_no, p, end_, &f))
                {
                    mh_fatal(path, line_no, "mh_parse_signature: expected formal");
                }
                formals[count] = f;
                count = count + 1;
                if (mh_match_char(p, end_, ','))
                {
                                        /* INVARIANT (on loop exit) */
                    continue;
                }
                                /* INVARIANT (on loop exit) */
                break;
            }
        }
    }
    if (!mh_match_char(p, end_, ')'))
    {
        mh_fatal(path, line_no, "mh_parse_signature: expected ')'");
    }
    ((*exp)).formals = formals;
    ((*exp)).formal_count = count;
    if (mh_match_char(p, end_, ':'))
    {
        if (!mh_parse_type_spec(path, line_no, p, end_, &t_const, &t_ptr, &t_name, &t_len, &t_width))
        {
            mh_fatal(path, line_no, "mh_parse_signature: expected result type");
        }
        ((*exp)).has_result = true;
        ((*exp)).result_is_const = t_const;
        ((*exp)).result_is_pointer = t_ptr;
        ((*exp)).result_name = t_name;
        ((*exp)).result_name_len = t_len;
        ((*exp)).result_width = t_width;
    }
}

static void mh_parse_method_type_tail(const char* path, size_t line_no, pchar* p, pchar end_, MhExport* exp)
{
    ((*exp)).is_func_type = false;
    if (mh_match_word(p, end_, "function"))
    {
        ((*exp)).is_func_type = true;
    }
    else if (mh_match_word(p, end_, "procedure"))
    {
        ((*exp)).is_func_type = false;
    }
    else
    {
                return;
    }
    mh_parse_signature(path, line_no, p, end_, exp);
    if (!((*exp)).has_signature)
    {
        mh_fatal(path, line_no, "mh_parse_method_type_tail: expected '(' after procedure/function");
    }
}

static void mh_parse_field_list(const char* path, size_t line_no, pchar* p, pchar end_, MhExport* exp)
{
    size_t cap = 0;
    MhField* fields = nil;
    size_t count = 0;
    char* fname = nil;
    size_t flen = 0;
    bool t_const = false;
    bool t_ptr = false;
    char* t_name = nil;
    size_t t_len = 0;
    int t_width = 0;
    char* ext = nil;
    size_t ext_len = 0;
    ((*exp)).is_struct = false;
    ((*exp)).is_union = false;
    ((*exp)).extends_name = nil;
    ((*exp)).extends_name_len = 0;
    ((*exp)).fields = nil;
    ((*exp)).field_count = 0;
    if (mh_match_word(p, end_, "struct"))
    {
        ((*exp)).is_struct = true;
    }
    else if (mh_match_word(p, end_, "union"))
    {
        ((*exp)).is_union = true;
    }
    else
    {
                return;
    }
    if (mh_match_word(p, end_, "extends"))
    {
        if (!mh_read_ident(p, end_, &ext, &ext_len))
        {
            mh_fatal(path, line_no, "mh_parse_field_list: expected parent type after extends");
        }
        ((*exp)).extends_name = ext;
        ((*exp)).extends_name_len = ext_len;
    }
    if (!mh_match_char(p, end_, '('))
    {
        mh_fatal(path, line_no, "mh_parse_field_list: expected '(' after struct/union");
    }
    mh_skip_ws(p, end_);
    if (!mh_peek_char((*p), end_, ')'))
    {
        while (true)
        {
            {
                if (count >= cap)
                {
                    size_t new_cap = (cap == 0 ? 4u : cap * 2u);
                    MhField* neu = (MhField*)realloc(fields, new_cap * ((integer)sizeof(MhField)));
                    if (neu == nil)
                    {
                        mh_fatal(path, line_no, "mh_parse_field_list: out of memory");
                    }
                    fields = neu;
                    cap = new_cap;
                }
                if (!mh_read_ident(p, end_, &fname, &flen))
                {
                    mh_fatal(path, line_no, "mh_parse_field_list: expected field name");
                }
                if (!mh_match_char(p, end_, ':'))
                {
                    mh_fatal(path, line_no, "mh_parse_field_list: expected ':' after field name");
                }
                if (!mh_parse_type_spec(path, line_no, p, end_, &t_const, &t_ptr, &t_name, &t_len, &t_width))
                {
                    mh_fatal(path, line_no, "mh_parse_field_list: expected field type");
                }
                (fields[count]).name = fname;
                (fields[count]).name_len = flen;
                (fields[count]).type_is_const = t_const;
                (fields[count]).type_is_pointer = t_ptr;
                (fields[count]).type_name = t_name;
                (fields[count]).type_name_len = t_len;
                (fields[count]).type_width = t_width;
                count = count + 1;
                if (mh_match_char(p, end_, ','))
                {
                                        /* INVARIANT (on loop exit) */
                    continue;
                }
                                /* INVARIANT (on loop exit) */
                break;
            }
        }
    }
    if (!mh_match_char(p, end_, ')'))
    {
        mh_fatal(path, line_no, "mh_parse_field_list: expected ')'");
    }
    ((*exp)).fields = fields;
    ((*exp)).field_count = count;
}

static void mh_parse_export_line(const char* path, size_t line_no, char* line, MhModule* m)
{
    char* p = line;
    char* end_ = line + strlen(line);
    char* name = nil;
    size_t name_len = 0;
    bool is_extern = false;
    MhExportKind kind = 0;
    MhCompleteness completeness = 0;
    MhExport exp = {0};
    char* owner = nil;
    size_t owner_len = 0;
    bool is_method = false;
    bool is_instance = false;
    if (!mh_match_word(&p, end_, "export"))
    {
        mh_fatal(path, line_no, "expected export");
    }
    if (mh_match_word(&p, end_, "extern"))
    {
        is_extern = true;
    }
    if (mh_match_word(&p, end_, "procedure"))
    {
        kind = MH_KIND_PROCEDURE;
    }
    else if (mh_match_word(&p, end_, "function"))
    {
        kind = MH_KIND_FUNCTION;
    }
    else if (mh_match_word(&p, end_, "type"))
    {
        kind = MH_KIND_TYPE;
    }
    else if (mh_match_word(&p, end_, "enum"))
    {
        kind = MH_KIND_ENUM;
    }
    else if (mh_match_word(&p, end_, "var"))
    {
        kind = MH_KIND_VAR;
    }
    else if (mh_match_word(&p, end_, "const"))
    {
        kind = MH_KIND_CONST;
    }
    else
    {
        mh_fatal(path, line_no, "mh_parse_export_line: expected export kind");
    }
    if (!mh_read_ident(&p, end_, &name, &name_len))
    {
        mh_fatal(path, line_no, "mh_parse_export_line: expected export name");
    }
    if (mh_match_word(&p, end_, "complete"))
    {
        completeness = MH_COMPLETENESS_COMPLETE;
    }
    else if (mh_match_word(&p, end_, "incomplete"))
    {
        completeness = MH_COMPLETENESS_INCOMPLETE;
    }
    else if (mh_match_word(&p, end_, "opaque"))
    {
        completeness = MH_COMPLETENESS_OPAQUE;
    }
    else
    {
        mh_fatal(path, line_no, "mh_parse_export_line: expected complete, incomplete, or opaque");
    }
    if (mh_match_word(&p, end_, "instance"))
    {
        is_method = true;
        is_instance = true;
        if (!mh_read_ident(&p, end_, &owner, &owner_len))
        {
            mh_fatal(path, line_no, "mh_parse_export_line: expected owner type after instance");
        }
    }
    else if (mh_match_word(&p, end_, "static"))
    {
        is_method = true;
        is_instance = false;
        if (!mh_read_ident(&p, end_, &owner, &owner_len))
        {
            mh_fatal(path, line_no, "mh_parse_export_line: expected owner type after static");
        }
    }
    (exp).kind = kind;
    (exp).is_extern = is_extern;
    (exp).completeness = completeness;
    (exp).name = mh_strdup_n(name, name_len);
    (exp).name_len = name_len;
    (exp).is_method = is_method;
    (exp).is_instance = is_instance;
    (exp).method_owner = nil;
    (exp).method_owner_len = 0;
    if ((exp).name == nil)
    {
        mh_fatal(path, line_no, "mh_parse_export_line: out of memory");
    }
    if (is_method)
    {
        (exp).method_owner = mh_strdup_n(owner, owner_len);
        (exp).method_owner_len = owner_len;
        if ((exp).method_owner == nil)
        {
            mh_fatal(path, line_no, "mh_parse_export_line: out of memory (owner)");
        }
    }
    (exp).has_signature = false;
    (exp).formals = nil;
    (exp).formal_count = 0;
    (exp).has_result = false;
    (exp).result_is_const = false;
    (exp).result_is_pointer = false;
    (exp).result_name = nil;
    (exp).result_name_len = 0;
    (exp).result_width = 0;
    (exp).is_struct = false;
    (exp).is_union = false;
    (exp).extends_name = nil;
    (exp).extends_name_len = 0;
    (exp).fields = nil;
    (exp).field_count = 0;
    (exp).is_func_type = false;
    if (kind == MH_KIND_PROCEDURE || kind == MH_KIND_FUNCTION)
    {
        mh_parse_signature(path, line_no, &p, end_, &exp);
    }
    if (kind == MH_KIND_TYPE)
    {
        mh_parse_field_list(path, line_no, &p, end_, &exp);
        if (!(exp).is_struct && !(exp).is_union)
        {
            mh_parse_method_type_tail(path, line_no, &p, end_, &exp);
        }
    }
    mh_module_add(m, exp);
}

static void mh_parse_line(const char* path, size_t line_no, char* line, MhModule* m)
{
    char* hash = strchr(line, '#');
    size_t i = 0;
    if (hash != nil)
    {
        (*hash) = '\0';
    }
    i = 0;
    while (line[i] != '\0' && (line[i] == ' ' || line[i] == '\t' || line[i] == '\r'))
    {
        {
            i = i + 1;
        }
    }
    if (line[i] == '\0')
    {
                return;
    }
    if (memcmp(line + i, "@module", 7) == 0)
    {
        char* p = line + i;
        char* end_ = line + strlen(line);
        char* name = nil;
        size_t name_len = 0;
        if (!mh_match_word(&p, end_, "@module"))
        {
            mh_fatal(path, line_no, "mh_parse_line: expected @module");
        }
        if (!mh_read_ident(&p, end_, &name, &name_len))
        {
            mh_fatal(path, line_no, "mh_parse_line: expected module name after @module");
        }
        if (((*m)).module_name != nil)
        {
            mh_fatal(path, line_no, "mh_parse_line: duplicate @module line");
        }
        ((*m)).module_name = name;
        ((*m)).module_name_len = name_len;
                return;
    }
    if (memcmp(line + i, "@format", 7) == 0)
    {
        char* p = line + i;
        char* end_ = line + strlen(line);
        if (!mh_match_word(&p, end_, "@format"))
        {
            mh_fatal(path, line_no, "mh_parse_line: expected @format");
        }
        while ((*p) != '\0' && ((*p) == ' ' || (*p) == '\t'))
        {
            {
                p = p + 1;
            }
        }
        if ((*p) == '\0' || !isdigit((int)(*p)))
        {
            mh_fatal(path, line_no, "mh_parse_line: expected format version number");
        }
        if (atoi(p) != 1)
        {
            mh_fatal(path, line_no, "mh_parse_line: unsupported modc-mh format version");
        }
        ((*m)).format_version = 1;
                return;
    }
    if (memcmp(line + i, "export", 6) == 0)
    {
        mh_parse_export_line(path, line_no, line + i, m);
                return;
    }
    mh_fatal(path, line_no, "mh_parse_line: unrecognized .mh line");
}

bool mh_reader_load(const char* resolved_path, MhModule* out)
{
    FILE* fp = nil;
    size_t fsize = 0;
    size_t nread = 0;
    size_t i = 0;
    char* line_start = nil;
    size_t line_no = 0;
    long file_pos = 0;
    long file_end = 0;
    if (resolved_path == nil || out == nil)
    {
        /* return value captured before defers */
        bool __ret_l1134 = false;
        return __ret_l1134;
    }
    ((*out)).module_name = nil;
    ((*out)).module_name_len = 0;
    ((*out)).format_version = 0;
    ((*out)).exports = nil;
    ((*out)).count = 0;
    ((*out)).capacity = 0;
    ((*out)).source = nil;
    ((*out)).source_len = 0;
    fp = fopen(resolved_path, "r");
    if (fp == nil)
    {
        fprintf(stderr, "ERROR: mh_reader_load: cannot open .mh file: %s\n", resolved_path);
        /* return value captured before defers */
        bool __ret_l1149 = false;
        return __ret_l1149;
    }
    file_pos = ftell(fp);
    if (file_pos < 0 || fseek(fp, 0, SEEK_END) != 0)
    {
        fclose(fp);
        fprintf(stderr, "ERROR: mh_reader_load: cannot size .mh file: %s\n", resolved_path);
        /* return value captured before defers */
        bool __ret_l1156 = false;
        return __ret_l1156;
    }
    file_end = ftell(fp);
    fseek(fp, file_pos, SEEK_SET);
    if (file_end < 0)
    {
        fclose(fp);
        fprintf(stderr, "ERROR: mh_reader_load: cannot size .mh file: %s\n", resolved_path);
        /* return value captured before defers */
        bool __ret_l1163 = false;
        return __ret_l1163;
    }
    fsize = (size_t)file_end;
    ((*out)).source = (char*)malloc(fsize + 1);
    if (((*out)).source == nil)
    {
        fclose(fp);
        fprintf(stderr, "ERROR: mh_reader_load: out of memory reading %s\n", resolved_path);
        /* return value captured before defers */
        bool __ret_l1171 = false;
        return __ret_l1171;
    }
    nread = fread(((*out)).source, 1, fsize, fp);
    fclose(fp);
    ((*out)).source[nread] = '\0';
    ((*out)).source_len = nread;
    line_start = ((*out)).source;
    i = 0;
    while (i <= nread)
    {
        {
            if (((*out)).source[i] == '\n' || ((*out)).source[i] == '\0')
            {
                bool at_end = (((*out)).source[i] == '\0');
                if (i > 0 && ((*out)).source[i - 1] == '\r')
                {
                    ((*out)).source[i - 1] = '\0';
                }
                ((*out)).source[i] = '\0';
                line_no = line_no + 1;
                mh_parse_line(resolved_path, line_no, line_start, out);
                if (at_end)
                {
                                        /* INVARIANT (on loop exit) */
                    break;
                }
                line_start = ((*out)).source + i + 1;
            }
            i++;
        }
    }
    if (((*out)).module_name == nil)
    {
        fprintf(stderr, "ERROR: mh_reader_load: %s: missing @module line\n", resolved_path);
        mh_module_free(out);
        /* return value captured before defers */
        bool __ret_l1200 = false;
        return __ret_l1200;
    }
    if (((*out)).format_version != 1)
    {
        fprintf(stderr, "ERROR: mh_reader_load: %s: missing @format 1 line\n", resolved_path);
        mh_module_free(out);
    }
    /* return value captured before defers */
    bool __ret_l1208 = true;
    return __ret_l1208;
}


void mh_reader(void)
{
}
