// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// parser.h -- DVX BASIC parser (recursive descent)
//
// Single-pass compiler: reads tokens from the lexer and emits
// p-code directly via the code generator. No AST. Forward
// references to SUBs/FUNCTIONs are resolved via backpatching.
//
// Embeddable: no DVX dependencies, pure C.

#ifndef DVXBASIC_PARSER_H
#define DVXBASIC_PARSER_H

#include "lexer.h"
#include "codegen.h"
#include "symtab.h"
#include "../runtime/vm.h"

#include <stdint.h>
#include <stdbool.h>

// ============================================================
// Parser state
// ============================================================

// Parse-error string sizes.  PARSE_ERROR_LEN is the final message stored
// on the parser; PARSE_ERR_SCRATCH is used for temporary formatting before
// copying into it (with a "Line N: " prefix).
#define BAS_PARSE_ERROR_LEN    1024
#define BAS_PARSE_ERR_SCRATCH  512

// Number of letters covered by DEFINT/DEFLNG/DEFSNG/DEFDBL/DEFSTR (A-Z).
#define BAS_DEFTYPE_LETTERS    ('Z' - 'A' + 1)
#define BAS_DEFTYPE_NONE       0xFF   // defType[] entry with no DEFxxx in effect (BAS_TYPE_INTEGER is 0, so 0 cannot mean unset)

// Recursion guards for the recursive-descent parser.  Each nested
// expression or block costs a dozen or more C stack frames, so an
// unbounded nesting depth would overflow the (small) DOS stack long
// before the source became unreadable.  These limits keep a
// pathological input a compile error instead of a crash.
#define BAS_MAX_EXPR_DEPTH     64
#define BAS_MAX_BLOCK_DEPTH    32

// Optional compile-time validator for CtrlName.Member references.
// The IDE populates this from the project's .frm files + widget DXE
// metadata so typos die at compile time instead of at event-click
// time.  bascomp leaves it NULL (runs on the host, no widget DXEs)
// and falls back to the runtime error net.  Dynamically-created
// controls aren't in the map, so lookupCtrlType returns NULL and
// validation is skipped for those -- no false positives.
typedef struct {
    // Return the widget type name for a control declared in a .frm,
    // or NULL if the control isn't statically known.
    const char *(*lookupCtrlType)(void *ctx, const char *ctrlName);
    // Return true if `methodName` is valid for widget type `wgtType`
    // (or a common method like Refresh/SetFocus).  Called with the
    // type string returned by lookupCtrlType.
    bool        (*isMethodValid)(void *ctx, const char *wgtType, const char *methodName);
    // Return true if `propName` is a valid property on wgtType.
    bool        (*isPropValid)(void *ctx, const char *wgtType, const char *propName);
    void         *ctx;
} BasCtrlValidatorT;


// Forward jump list (for EXIT FOR / EXIT DO backpatching)
typedef struct {
    int32_t *patchAddr;  // stb_ds dynamic array of code positions to backpatch
} ExitListT;


typedef struct {
    BasLexerT    lex;
    BasCodeGenT  cg;
    BasSymTabT   sym;
    char         error[BAS_PARSE_ERROR_LEN];
    bool         hasError;
    int32_t      errorLine;
    int32_t      prevLine;      // line of the previous token (for error reporting)
    int32_t      lastUdtTypeId;  // index of last resolved UDT type from resolveTypeName
    int32_t      optionBase;         // default array lower bound (0 or 1)
    bool         optionExplicit;     // true = variables must be declared with DIM
    bool         currentProcIsFunction; // true while parsing a FUNCTION body (EXIT FUNCTION / return-value assignment)
    uint8_t      defType[BAS_DEFTYPE_LETTERS]; // default type per letter (A-Z), set by DEFINT etc.
    char         currentProc[BAS_MAX_TOKEN_LEN]; // name of current SUB/FUNCTION
    // Per-form init block tracking
    int32_t      formInitJmpAddr;    // code position of JMP to patch (-1 = none)
    int32_t      formInitCodeStart;  // code position where init block starts (-1 = none)
    // EXIT FOR/DO/SUB/FUNCTION forward-jump backpatch lists
    ExitListT    exitForList;
    ExitListT    exitDoList;
    ExitListT    exitSubList;
    ExitListT    exitFuncList;
    // SELECT CASE keeps its test value on the eval stack for the whole
    // construct.  selectDepth counts how many are currently live; the
    // nearest FOR/DO loop records the depth at its start so EXIT and
    // GOTO can emit one OP_POP per SELECT they jump out of.
    int32_t      selectDepth;
    int32_t      forSelectBase;
    int32_t      doSelectBase;
    // Number of FOR / DO-WHILE loops currently open in the procedure (or
    // module body) being parsed, so EXIT FOR / EXIT DO outside a loop is a
    // compile error rather than a jump patched against an outer scope.
    int32_t      forDepth;
    int32_t      doDepth;
    // SELECT depth at the most recently defined label.  A module-level
    // RETURN discards the test values of every SELECT opened since that
    // label so OP_GOSUB_RET pops the return address, not a test value.
    int32_t      lastLabelSelectDepth;
    // Recursion guards (see BAS_MAX_EXPR_DEPTH / BAS_MAX_BLOCK_DEPTH).
    int32_t      exprDepth;
    int32_t      blockDepth;
    // Optional compile-time CtrlName.Member validator (IDE-only).
    const BasCtrlValidatorT *validator;
} BasParserT;

// ============================================================
// API
// ============================================================

// Initialize parser with source text.
void basParserInit(BasParserT *p, const char *source, int32_t sourceLen);

// Attach an optional compile-time validator for CtrlName.Member
// references.  The parser borrows the pointer -- caller owns the
// underlying struct and must keep it alive until basParserFree.
void basParserSetValidator(BasParserT *p, const BasCtrlValidatorT *v);

// Parse the entire source and generate p-code.
// Returns true on success, false on error (check p->error).
bool basParse(BasParserT *p);

// Build a module from the parsed code. Returns NULL on error.
// Caller owns the module and must free with basModuleFree().
BasModuleT *basParserBuildModule(BasParserT *p);

// Free parser resources.
void basParserFree(BasParserT *p);

#endif // DVXBASIC_PARSER_H
