/*
 * lumbda.h — A Scheme interpreter in C (NaN-boxed values, Boehm-style GC)
 *
 * Complete port of lumbda.py.
 * Part of the permacomputer platform — code outlasts authors.
 */
#ifndef LUMBDA_H
#define LUMBDA_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <setjmp.h>
#include <stdarg.h>

/* ═══════════════════════════════════════════════════════════════════════════
 * Memory allocation — simple arena/malloc wrapper
 * When libgc is available, compile with -DUSE_BOEHM_GC and link -lgc
 * ═══════════════════════════════════════════════════════════════════════════ */

#ifdef USE_BOEHM_GC
#include <gc.h>
#define ul_malloc(sz)    GC_MALLOC(sz)
#define ul_realloc(p,sz) GC_REALLOC(p,sz)
#define ul_free(p)       ((void)0)
#define ul_strdup(s)     GC_STRDUP(s)
/* Precise-kind allocator for buffers that store NaN-boxed Values. Boehm's
 * conservative scan misses NaN-boxed pointers; our custom kind walks the
 * block, decodes each 8-byte word, & marks the underlying pointer. See
 * gc.c. GC_REALLOC preserves the object's kind, so the same call grows
 * Value buffers correctly. */
#define ul_malloc_values(sz)    lumbda_value_malloc(sz)
#define ul_realloc_values(p,sz) GC_REALLOC(p,sz)
#else
/* Fallback: plain malloc (no collection — acceptable for batch scripts) */
#define ul_malloc(sz)    malloc(sz)
#define ul_realloc(p,sz) realloc(p,sz)
#define ul_free(p)       free(p)
#define ul_malloc_values(sz)    malloc(sz)
#define ul_realloc_values(p,sz) realloc(p,sz)
static inline char *ul_strdup(const char *s) {
    size_t n = strlen(s) + 1;
    char *d = (char *)ul_malloc(n);
    if (d) memcpy(d, s, n);
    return d;
}
#endif

/* Precise tracing for NaN-boxed Values under Boehm GC — implementation in gc.c. */
extern int lumbda_value_kind;
void lumbda_gc_init(void);
void lumbda_gc_set_stack_base(void *base);
void *lumbda_value_malloc(size_t sz);

/* ═══════════════════════════════════════════════════════════════════════════
 * NaN-boxed Value type
 *
 * IEEE 754 double: if exponent bits are all 1 and mantissa != 0, it's NaN.
 * We use quiet NaN with tag bits in the upper mantissa.
 *
 * Layout (64 bits):
 *   [sign:1][exponent:11][quiet:1][tag:3][payload:48]
 *
 * Tag 0 = pointer (Pair, String, Vector, etc — distinguished by pointed-to type tag)
 * Tag 1 = integer (48-bit signed)
 * Tag 2 = symbol (pointer to interned string)
 * Tag 3 = special (NIL, VOID, TRUE, FALSE, EOF, char in payload)
 * Tag 4 = builtin function pointer
 * Tag 5 = rational (pointer to Rational struct)
 * Tag 6 = bignum (pointer to Bignum struct) — arbitrary-precision integer.
 *         Used for any integer outside the 48-bit fixnum range. Arithmetic
 *         promotes fixnum to bignum on overflow & demotes bignum back to
 *         fixnum when result fits, so callers never need to distinguish.
 *
 * Plain doubles (no NaN payload) are floating-point numbers.
 * ═══════════════════════════════════════════════════════════════════════════ */

typedef uint64_t Value;

/* NaN box constants */
#define QNAN        ((uint64_t)0x7FF8000000000000ULL)
#define TAG_MASK    ((uint64_t)0x0007000000000000ULL)
#define TAG_SHIFT   48
#define PAYLOAD_MASK ((uint64_t)0x0000FFFFFFFFFFFFULL)
#define SIGN_BIT    ((uint64_t)0x8000000000000000ULL)

#define TAG_PTR     0ULL
#define TAG_INT     1ULL
#define TAG_SYM     2ULL
#define TAG_SPECIAL 3ULL
#define TAG_BUILTIN 4ULL
#define TAG_RATIONAL 5ULL
#define TAG_BIGNUM   6ULL

/* Fixnum range: signed 48-bit (low 48 bits of payload). */
#define FIXNUM_MIN ((int64_t)(-(1LL << 47)))
#define FIXNUM_MAX ((int64_t)((1LL << 47) - 1))
#define FITS_FIXNUM(v) ((v) >= FIXNUM_MIN && (v) <= FIXNUM_MAX)

/* Construct a NaN-boxed value */
#define NANBOX(tag, payload) (QNAN | ((uint64_t)(tag) << TAG_SHIFT) | ((uint64_t)(payload) & PAYLOAD_MASK))

/* Extract tag and payload */
#define IS_DOUBLE(v)  (((v) & QNAN) != QNAN)
#define GET_TAG(v)    (((v) >> TAG_SHIFT) & 7ULL)
#define GET_PAYLOAD(v) ((v) & PAYLOAD_MASK)
#define GET_PTR(v)    ((void *)(uintptr_t)GET_PAYLOAD(v))

/* Value constructors */
static inline Value make_double(double d) {
    Value v;
    memcpy(&v, &d, sizeof(v));
    return v;
}
static inline double as_double(Value v) {
    double d;
    memcpy(&d, &v, sizeof(d));
    return d;
}

/* Integer: 48-bit signed */
#define VAL_INT(n) NANBOX(TAG_INT, (uint64_t)(int64_t)(n) & PAYLOAD_MASK)
static inline int64_t as_int(Value v) {
    int64_t raw = (int64_t)(GET_PAYLOAD(v));
    /* Sign-extend from 48 bits */
    if (raw & (1ULL << 47)) raw |= ~PAYLOAD_MASK;
    return raw;
}

/* Special values */
#define SPECIAL_NIL   0ULL
#define SPECIAL_VOID  1ULL
#define SPECIAL_TRUE  2ULL
#define SPECIAL_FALSE 3ULL
#define SPECIAL_EOF   4ULL
#define SPECIAL_CHAR_BASE 256ULL  /* char = CHAR_BASE + codepoint */

#define VAL_NIL   NANBOX(TAG_SPECIAL, SPECIAL_NIL)
#define VAL_VOID  NANBOX(TAG_SPECIAL, SPECIAL_VOID)
#define VAL_TRUE  NANBOX(TAG_SPECIAL, SPECIAL_TRUE)
#define VAL_FALSE NANBOX(TAG_SPECIAL, SPECIAL_FALSE)
#define VAL_EOF   NANBOX(TAG_SPECIAL, SPECIAL_EOF)

#define VAL_CHAR(c) NANBOX(TAG_SPECIAL, SPECIAL_CHAR_BASE + (uint32_t)(c))

#define VAL_BOOL(b) ((b) ? VAL_TRUE : VAL_FALSE)

/* Type checks */
#define IS_INT(v)     (!IS_DOUBLE(v) && GET_TAG(v) == TAG_INT)
#define IS_SYM(v)     (!IS_DOUBLE(v) && GET_TAG(v) == TAG_SYM)
#define IS_PTR(v)     (!IS_DOUBLE(v) && GET_TAG(v) == TAG_PTR)
#define IS_SPECIAL(v) (!IS_DOUBLE(v) && GET_TAG(v) == TAG_SPECIAL)
#define IS_BUILTIN(v) (!IS_DOUBLE(v) && GET_TAG(v) == TAG_BUILTIN)
#define IS_RATIONAL(v) (!IS_DOUBLE(v) && GET_TAG(v) == TAG_RATIONAL)
#define IS_BIGNUM(v)  (!IS_DOUBLE(v) && GET_TAG(v) == TAG_BIGNUM)
/* Integer-shaped: either fixnum or bignum. */
#define IS_INTEGER(v) (IS_INT(v) || IS_BIGNUM(v))

#define IS_NIL(v)     ((v) == VAL_NIL)
#define IS_VOID(v)    ((v) == VAL_VOID)
#define IS_TRUE(v)    ((v) == VAL_TRUE)
#define IS_FALSE(v)   ((v) == VAL_FALSE)
#define IS_EOF(v)     ((v) == VAL_EOF)
#define IS_CHAR(v)    (IS_SPECIAL(v) && GET_PAYLOAD(v) >= SPECIAL_CHAR_BASE)

#define AS_CHAR(v)    ((int)(GET_PAYLOAD(v) - SPECIAL_CHAR_BASE))

/* Truthiness: everything is truthy except #f */
#define IS_TRUTHY(v)  (!IS_FALSE(v))

/* ═══════════════════════════════════════════════════════════════════════════
 * Heap object types — all heap objects have a type tag as first field
 * ═══════════════════════════════════════════════════════════════════════════ */

typedef enum {
    OBJ_PAIR,
    OBJ_STRING,
    OBJ_MUTABLE_STRING,
    OBJ_VECTOR,
    OBJ_HASHTABLE,
    OBJ_ENV,
    OBJ_PROC,
    OBJ_COMPILED_PROC,
    OBJ_MACRO,
    OBJ_CODE,
    OBJ_CONTINUATION,
    OBJ_PORT,
    OBJ_ERROR,
    OBJ_SYNTAX_TRANSFORMER,
    OBJ_RATIONAL,
    OBJ_BIGNUM,
} ObjType;

typedef struct ObjHeader {
    ObjType type;
} ObjHeader;

/* Get the heap object type */
static inline ObjType obj_type(Value v) {
    return ((ObjHeader *)GET_PTR(v))->type;
}

/* Wrap a pointer as a Value */
#define VAL_PTR(p)  NANBOX(TAG_PTR, (uintptr_t)(p))

/* ── Pair ────────────────────────────────────────────────────────────────── */

typedef struct Pair {
    ObjHeader hdr;
    Value car;
    Value cdr;
    int line;  /* source line for error reporting, 0 = unknown */
} Pair;

#define IS_PAIR(v) (IS_PTR(v) && obj_type(v) == OBJ_PAIR)
#define AS_PAIR(v) ((Pair *)GET_PTR(v))

Pair *make_pair(Value car, Value cdr);
Value cons(Value car, Value cdr);

/* ── Symbol interning ────────────────────────────────────────────────────── */

typedef struct SymbolEntry {
    char *name;
    Value value;  /* the NaN-boxed symbol value */
    struct SymbolEntry *next;
} SymbolEntry;

#define SYMBOL_TABLE_SIZE 4096

typedef struct {
    SymbolEntry *buckets[SYMBOL_TABLE_SIZE];
} SymbolTable;

extern SymbolTable g_symbols;

Value intern(const char *name);
const char *sym_name(Value sym);

#define VAL_SYM_RAW(p) NANBOX(TAG_SYM, (uintptr_t)(p))

/* ── String ──────────────────────────────────────────────────────────────── */

typedef struct ULString {
    ObjHeader hdr;
    char *data;
    size_t len;
    bool mutable;
} ULString;

#define IS_STRING(v)  (IS_PTR(v) && (obj_type(v) == OBJ_STRING || obj_type(v) == OBJ_MUTABLE_STRING))
#define AS_STRING(v)  ((ULString *)GET_PTR(v))

Value make_string(const char *s, size_t len, bool mutable);
Value make_string_from_cstr(const char *s);  /* immutable */

/* ── Vector ──────────────────────────────────────────────────────────────── */

typedef struct ULVector {
    ObjHeader hdr;
    Value *data;
    size_t len;
    size_t cap;
} ULVector;

#define IS_VECTOR(v) (IS_PTR(v) && obj_type(v) == OBJ_VECTOR)
#define AS_VECTOR(v) ((ULVector *)GET_PTR(v))

Value make_vector(size_t len, Value fill);
Value make_vector_from(Value *items, size_t len);

/* ── Hash Table ──────────────────────────────────────────────────────────── */

typedef struct HTEntry {
    Value key;
    Value value;
    struct HTEntry *next;
} HTEntry;

typedef struct ULHashTable {
    ObjHeader hdr;
    HTEntry **buckets;
    size_t nbuckets;
    size_t count;
} ULHashTable;

#define IS_HASHTABLE(v) (IS_PTR(v) && obj_type(v) == OBJ_HASHTABLE)
#define AS_HASHTABLE(v) ((ULHashTable *)GET_PTR(v))

Value make_hashtable(void);
void ht_set(ULHashTable *ht, Value key, Value val);
Value ht_ref(ULHashTable *ht, Value key, bool *found);
bool ht_delete(ULHashTable *ht, Value key);
size_t ht_count(ULHashTable *ht);

/* ── Rational ────────────────────────────────────────────────────────────── */

typedef struct Rational {
    ObjHeader hdr;
    int64_t num;
    int64_t den;
} Rational;

#define AS_RATIONAL(v) ((Rational *)(uintptr_t)GET_PAYLOAD(v))

Value make_rational(int64_t num, int64_t den);
/* Returns integer Value if den==1, otherwise rational */
Value rational_normalize(int64_t num, int64_t den);

/* ── Bignum — arbitrary-precision integer ────────────────────────────────── */

/*
 * Bignum representation: sign-magnitude.
 *   sign: -1 (negative), 0 (zero — must have n_limbs == 0), +1 (positive)
 *   n_limbs: number of u64 magnitude limbs (always trimmed of leading zeros)
 *   limbs[i]: little-endian magnitude (limb 0 = least-significant 64 bits)
 *
 * Boehm GC owns every Bignum struct & its limbs array. Construction goes
 * through bignum_normalize() which trims leading-zero limbs & forces zero
 * to canonical form (sign=0, n_limbs=0).
 *
 * Result-producing helpers ALWAYS return a Value: if the magnitude fits in
 * 48 signed bits, a fixnum Value is returned; otherwise a bignum Value.
 * Callers therefore never branch on which one they get back.
 */
typedef struct Bignum {
    ObjHeader hdr;
    int32_t sign;       /* -1, 0, +1 */
    uint32_t n_limbs;   /* magnitude limb count after trim */
    uint64_t *limbs;    /* little-endian magnitude */
} Bignum;

#define AS_BIGNUM(v) ((Bignum *)(uintptr_t)GET_PAYLOAD(v))

/* Constructors */
Value make_bignum_from_i64(int64_t v);
Value make_bignum_from_str(const char *s, int base);   /* base 10 or 16 */
Value bignum_from_limbs(int32_t sign, uint64_t *limbs, uint32_t n);

/* Demote a bignum to fixnum when magnitude fits 48 signed bits. Caller passes
 * a (sign, limbs, n_limbs) triple; result is fixnum Value or bignum Value. */
Value bignum_normalize(int32_t sign, uint64_t *limbs, uint32_t n);

/* Promote a fixnum Value to a bignum Value (no-op for already-bignum). */
Value to_bignum(Value v);

/* Decimal-string render (caller frees via ul_free). */
char *bignum_to_str(Bignum *b);

/* Arithmetic — accept fixnum or bignum, return fixnum-or-bignum
 * (demoted when possible). Sign/magnitude internal. */
Value big_add(Value a, Value b);
Value big_sub(Value a, Value b);
Value big_mul(Value a, Value b);
Value big_neg(Value a);
Value big_quotient(Value a, Value b);
Value big_remainder(Value a, Value b);   /* sign of dividend */
Value big_modulo(Value a, Value b);      /* sign of divisor */
Value big_expt(Value base, Value exp);   /* exp >= 0, integer */
int   big_cmp(Value a, Value b);         /* -1, 0, +1 */
bool  big_is_zero(Value v);
bool  big_is_odd(Value v);
Value big_abs(Value v);

/* Coerce any number Value to a fresh Bignum struct (caller owns) for ops
 * that want explicit bignum arguments. */
Bignum *as_bignum(Value v);

/* ── Environment ─────────────────────────────────────────────────────────── */

typedef struct EnvBinding {
    Value sym;
    Value val;
    struct EnvBinding *next;
} EnvBinding;

typedef struct Env {
    ObjHeader hdr;
    EnvBinding **buckets;
    size_t nbuckets;
    size_t count;
    struct Env *parent;
    struct Env *global;  /* shortcut to global env */
} Env;

#define IS_ENV(v) (IS_PTR(v) && obj_type(v) == OBJ_ENV)
#define AS_ENV(v) ((Env *)GET_PTR(v))

Env *make_env(Env *parent);
void env_define(Env *e, Value sym, Value val);
Value env_lookup(Env *e, Value sym);
bool env_set(Env *e, Value sym, Value val);
Env *env_child(Env *parent, Value *params, int nparams, Value rest_param, Value *args, int nargs);
Env *deep_copy_env(Env *env);

/* ── Procedure ───────────────────────────────────────────────────────────── */

/* Parsed formals */
typedef struct {
    Value *params;
    int nparams;
    Value rest;  /* VAL_NIL if no rest param */
} Formals;

/* Expression list (body) */
typedef struct {
    Value *exprs;
    int count;
} ExprList;

typedef struct Proc {
    ObjHeader hdr;
    Value *params;
    int nparams;
    Value rest;      /* rest param symbol, or VAL_NIL */
    ExprList body;
    Env *env;
    const char *name;
    bool has_defs;   /* body starts with define? */
    void *jit_block; /* cached JitBlock*, or NULL */
} Proc;

#define IS_PROC(v) (IS_PTR(v) && obj_type(v) == OBJ_PROC)
#define AS_PROC(v) ((Proc *)GET_PTR(v))

Proc *make_proc(Value *params, int nparams, Value rest, ExprList body, Env *env, const char *name);

/* ── Builtin function ────────────────────────────────────────────────────── */

typedef Value (*BuiltinFn)(Value *args, int nargs, Env *env);

/* Wrap a function pointer */
#define VAL_BUILTIN(fn) NANBOX(TAG_BUILTIN, (uintptr_t)(fn))
#define AS_BUILTIN(v)   ((BuiltinFn)(uintptr_t)GET_PAYLOAD(v))

/* ── Macro ───────────────────────────────────────────────────────────────── */

typedef struct ULMacro {
    ObjHeader hdr;
    Value transformer;  /* Proc, CompiledProc, or SyntaxTransformer */
} ULMacro;

#define IS_MACRO(v) (IS_PTR(v) && obj_type(v) == OBJ_MACRO)
#define AS_MACRO(v) ((ULMacro *)GET_PTR(v))

Value make_macro(Value transformer);

/* ── Error object ────────────────────────────────────────────────────────── */

typedef struct ErrorObject {
    ObjHeader hdr;
    char *message;
    Value *irritants;
    int nirritants;
} ErrorObject;

#define IS_ERROR_OBJ(v) (IS_PTR(v) && obj_type(v) == OBJ_ERROR)
#define AS_ERROR(v)     ((ErrorObject *)GET_PTR(v))

Value make_error_object(const char *msg, Value *irritants, int nirr);

/* ── Port ────────────────────────────────────────────────────────────────── */

typedef enum { PORT_INPUT, PORT_OUTPUT } PortDir;
typedef enum { PORT_FILE, PORT_STRING } PortKind;

typedef struct ULPort {
    ObjHeader hdr;
    PortDir dir;
    PortKind kind;
    FILE *fp;           /* for file ports */
    char *str_buf;      /* for string ports */
    size_t str_len;
    size_t str_pos;
    size_t str_cap;
    bool closed;
} ULPort;

#define IS_PORT(v) (IS_PTR(v) && obj_type(v) == OBJ_PORT)
#define AS_PORT(v) ((ULPort *)GET_PTR(v))

Value make_file_port(FILE *fp, PortDir dir);
Value make_string_input_port(const char *s, size_t len);
Value make_string_output_port(void);
void port_write_char(ULPort *p, int ch);
void port_write_str(ULPort *p, const char *s, size_t len);
int port_read_char(ULPort *p);
int port_peek_char(ULPort *p);
char *port_read_line(ULPort *p);
char *port_get_output_string(ULPort *p);

/* ── Bytecode ────────────────────────────────────────────────────────────── */

typedef enum {
    OP_CONST = 0, OP_LOOKUP = 1, OP_SET = 2, OP_DEFINE = 3,
    OP_POP = 4, OP_DUP = 5, OP_VOID = 6,
    OP_JUMP = 10, OP_JUMP_IF_FALSE = 11,
    OP_JUMP_IF_FALSE_KEEP = 12, OP_JUMP_IF_TRUE_KEEP = 13,
    OP_CALL = 20, OP_TAIL_CALL = 21, OP_RETURN = 22,
    OP_MAKE_CLOSURE = 30,
    OP_PUSH_ENV = 40, OP_POP_ENV = 41, OP_BIND = 42,
    OP_EVAL = 50, OP_CALL_CC = 51,
    OP_ADD = 60, OP_SUB = 61, OP_MUL = 62, OP_NEG = 63,
    OP_NUM_EQ = 64, OP_LT = 65, OP_GT = 66, OP_LE = 67, OP_GE = 68,
    OP_ADD1 = 69, OP_SUB1 = 70,
    OP_CAR = 71, OP_CDR = 72, OP_CONS = 73,
    OP_NULL_P = 74, OP_PAIR_P = 75, OP_NOT = 76, OP_ZERO_P = 77,
    OP_VEC_REF = 78, OP_VEC_SET = 79,
    OP_LOOK_LOOK = 80, OP_LOOK_ADD1 = 81, OP_LOOK_SUB1 = 82,
    OP_CONST_EQ_JF = 83, OP_LOOK_CONST_CALL2 = 84,
    OP_SELF_TAIL_CALL = 85,
} Opcode;

typedef struct {
    Opcode op;
    Value arg;      /* operand — meaning depends on op */
    int arg2;       /* secondary operand (for some superinstructions) */
} Instruction;

typedef struct CodeObj {
    ObjHeader hdr;
    Instruction *instrs;
    int count;
    int cap;
    int *source_map;  /* line numbers, parallel to instrs */
    const char *name;
    /* Self-tail-call optimization info */
    const char *self_name;
    Value *self_params;
    int self_nparams;
} CodeObj;

#define IS_CODE(v) (IS_PTR(v) && obj_type(v) == OBJ_CODE)
#define AS_CODE(v) ((CodeObj *)GET_PTR(v))

typedef struct CompiledProc {
    ObjHeader hdr;
    CodeObj *code;
    Value *params;
    int nparams;
    Value rest;
    Env *env;
    const char *name;
} CompiledProc;

#define IS_COMPILED_PROC(v) (IS_PTR(v) && obj_type(v) == OBJ_COMPILED_PROC)
#define AS_COMPILED_PROC(v) ((CompiledProc *)GET_PTR(v))

/* ── Continuation ────────────────────────────────────────────────────────── */

typedef struct VMFrame {
    Instruction *instrs;
    int ip;
    int n_instrs;
    Env *env;
    Value *stack;
    int stack_len;
    int stack_cap;
    CodeObj *cur_code;     /* saved so RET restores it — needed for SELF_TAIL_CALL after a nested call */
} VMFrame;

typedef struct FullCont {
    ObjHeader hdr;
    VMFrame *frames;
    int nframes;
    Value *stack;
    int stack_len;
    int ip;
    Instruction *instrs;
    int n_instrs;
    Env *env;
    void *vm_id;
} FullCont;

#define IS_CONTINUATION(v) (IS_PTR(v) && obj_type(v) == OBJ_CONTINUATION)
#define AS_CONTINUATION(v) ((FullCont *)GET_PTR(v))

/* Continuation invocation signaling — used by full continuations in the VM.
 * When a FullCont is invoked (as a callable), it sets these thread-locals
 * and longjmps to the VM's trampoline. Similar to Python's _ContInvoked. */
typedef struct {
    jmp_buf jmp;
    bool active;
} ContTrampoline;

extern __thread ContTrampoline *g_cont_trampoline;
extern __thread FullCont *g_cont_invoked;
extern __thread Value g_cont_invoked_val;

FullCont *make_full_cont(VMFrame *frames, int nframes, Value *stack, int stack_len,
                         int ip, Instruction *instrs, int n_instrs, Env *env, void *vm_id);

/* Deep-copy a VMFrame array for multi-shot continuations */
VMFrame *deep_copy_frames(VMFrame *frames, int nframes);

/* ── Syntax Transformer ──────────────────────────────────────────────────── */

typedef struct SyntaxRule {
    Value pattern;
    Value tmpl;
} SyntaxRule;

typedef struct SyntaxTransformer {
    ObjHeader hdr;
    char **literals;
    int nliterals;
    SyntaxRule *rules;
    int nrules;
    Env *def_env;
} SyntaxTransformer;

#define IS_SYNTAX_TRANSFORMER(v) (IS_PTR(v) && obj_type(v) == OBJ_SYNTAX_TRANSFORMER)
#define AS_SYNTAX_TRANSFORMER(v) ((SyntaxTransformer *)GET_PTR(v))

/* ═══════════════════════════════════════════════════════════════════════════
 * Error handling — longjmp-based exception system
 * ═══════════════════════════════════════════════════════════════════════════ */

#define MAX_ERROR_MSG 1024
#define MAX_CALL_STACK 256

typedef struct {
    jmp_buf jmp;
    char message[MAX_ERROR_MSG];
    Value error_obj;           /* ErrorObject Value or VAL_NIL */
    const char *call_stack[MAX_CALL_STACK];
    int call_stack_depth;
    int source_line;
} ErrorContext;

extern __thread ErrorContext *g_error_ctx;

void lisp_error(const char *fmt, ...) __attribute__((noreturn));
void lisp_error_with_obj(Value obj, const char *fmt, ...) __attribute__((noreturn));

/* Push/pop error handler */
#define TRY(ctx) \
    do { \
        ErrorContext *_prev = g_error_ctx; \
        ErrorContext ctx; \
        ctx.call_stack_depth = _prev ? _prev->call_stack_depth : 0; \
        if (_prev) memcpy(ctx.call_stack, _prev->call_stack, sizeof(char*) * ctx.call_stack_depth); \
        ctx.error_obj = VAL_NIL; \
        ctx.source_line = 0; \
        g_error_ctx = &ctx; \
        if (setjmp(ctx.jmp) == 0) {

#define CATCH \
        } else {

#define ENDTRY \
        } \
        g_error_ctx = _prev; \
    } while(0)

/* ═══════════════════════════════════════════════════════════════════════════
 * Value stack (dynamic array)
 * ═══════════════════════════════════════════════════════════════════════════ */

typedef struct {
    Value *data;
    int len;
    int cap;
} ValueStack;

void vs_init(ValueStack *s, int cap);
void vs_push(ValueStack *s, Value v);
Value vs_pop(ValueStack *s);
Value vs_peek(ValueStack *s);
void vs_clear(ValueStack *s);

/* ═══════════════════════════════════════════════════════════════════════════
 * Function declarations — reader.c
 * ═══════════════════════════════════════════════════════════════════════════ */

typedef struct {
    char **tokens;
    int *lines;
    int count;
    int cap;
} TokenList;

void tokenize(const char *src, TokenList *out, bool track_lines);
Value parse_one(TokenList *tl, int *pos);
Value parse_atom(const char *tok);
Value *read_all(const char *src, int *count, bool track_lines);

/* ═══════════════════════════════════════════════════════════════════════════
 * Function declarations — printer.c
 * ═══════════════════════════════════════════════════════════════════════════ */

/* Returns a malloc'd string. Caller must free. */
char *show(Value v, bool display);
/* Print to stdout */
void print_value(Value v, bool display, FILE *out);

/* ═══════════════════════════════════════════════════════════════════════════
 * Function declarations — eval.c
 * ═══════════════════════════════════════════════════════════════════════════ */

Value leval(Value expr, Env *env);
Value call_proc(Value proc, Value *args, int nargs, Env *env);
void load_file(const char *path, Env *env);

/* Quasiquote */
Value qq_expand(Value tmpl, Env *env, int depth);

/* Syntax transformer */
Value syntax_transform_value(SyntaxTransformer *st, Value form);

/* Helpers */
Value list_to_value(Value *items, int count);      /* Python list → Lisp list */
int value_to_list(Value v, Value **out);            /* Lisp list → C array, returns count */
Formals parse_formals(Value f);
bool has_internal_defines(ExprList body);
ExprList body_with_env(Value *forms, int count, Env *env);
bool is_proper_list(Value v);
bool values_equal(Value a, Value b);

/* Global state */
extern bool g_auto_compile;

/* ═══════════════════════════════════════════════════════════════════════════
 * Function declarations — builtins.c
 * ═══════════════════════════════════════════════════════════════════════════ */

Env *make_global_env(void);
extern const char *PRELUDE;

/* ═══════════════════════════════════════════════════════════════════════════
 * Function declarations — vm.c
 * ═══════════════════════════════════════════════════════════════════════════ */

CodeObj *make_code(const char *name);
int code_emit(CodeObj *c, Opcode op, Value arg);
void code_emit2(CodeObj *c, Opcode op, Value arg, int arg2);
void code_patch(CodeObj *c, int addr, Value arg);

CompiledProc *compile_proc(Proc *p, Env *env);
Value vm_exec(CodeObj *code, Env *env);
void bc_compile(Value expr, CodeObj *code, Env *env, bool tail);
CodeObj *bc_lambda(Value *body, int nbody, Value *params, int nparams,
                   Value rest, Env *env, const char *name,
                   const char *self_name, Value *self_params, int self_nparams);

/* ═══════════════════════════════════════════════════════════════════════════
 * Function declarations — portal.c
 * ═══════════════════════════════════════════════════════════════════════════ */

void portal_save(Env *env, const char *path, FullCont *continuation);
bool portal_resume(const char *path, Env *base_env, Env **out_env, FullCont **out_cont);
void register_portal_builtins(Env *env);

/* Portal checkpoint signal — set to path to trigger save at next VM safe point */
extern __thread const char *g_portal_checkpoint_path;

/* ═══════════════════════════════════════════════════════════════════════════
 * Commonly used interned symbols — cached for fast comparison
 * ═══════════════════════════════════════════════════════════════════════════ */

extern Value SYM_QUOTE, SYM_IF, SYM_COND, SYM_AND, SYM_OR;
extern Value SYM_WHEN, SYM_UNLESS, SYM_BEGIN, SYM_DEFINE, SYM_SET;
extern Value SYM_LAMBDA, SYM_LAMBDA_UC, SYM_LET, SYM_LET_STAR, SYM_LETREC;
extern Value SYM_LETREC_STAR, SYM_DO, SYM_QUASIQUOTE, SYM_UNQUOTE;
extern Value SYM_UNQUOTE_SPLICING, SYM_DEFINE_MACRO, SYM_DEFMACRO;
extern Value SYM_DEFINE_SYNTAX, SYM_LET_SYNTAX, SYM_LETREC_SYNTAX;
extern Value SYM_SYNTAX_RULES, SYM_VALUES, SYM_CALL_WITH_VALUES;
extern Value SYM_CALL_CC, SYM_CALL_CC2, SYM_APPLY, SYM_EVAL;
extern Value SYM_ERROR, SYM_DEFINE_RECORD_TYPE, SYM_MODULE, SYM_IMPORT;
extern Value SYM_LOAD, SYM_INCLUDE, SYM_PARAMETERIZE, SYM_DYNAMIC_WIND;
extern Value SYM_WITH_EXCEPTION_HANDLER, SYM_GUARD, SYM_DEFINE_VALUES;
extern Value SYM_LET_VALUES, SYM_LET_STAR_VALUES, SYM_CASE;
extern Value SYM_ELSE, SYM_ARROW, SYM_DOT, SYM_ELLIPSIS, SYM_UNDERSCORE;
extern Value SYM_EXPORT;

void init_symbols(void);

/* ═══════════════════════════════════════════════════════════════════════════
 * Utility macros
 * ═══════════════════════════════════════════════════════════════════════════ */

#define CAR(v) (AS_PAIR(v)->car)
#define CDR(v) (AS_PAIR(v)->cdr)
#define CAAR(v) CAR(CAR(v))
#define CADR(v) CAR(CDR(v))
#define CDAR(v) CDR(CAR(v))
#define CDDR(v) CDR(CDR(v))
#define CADDR(v) CAR(CDDR(v))

/* Is v callable? */
static inline bool is_callable(Value v) {
    if (IS_BUILTIN(v)) return true;
    if (IS_PTR(v)) {
        ObjType t = obj_type(v);
        return t == OBJ_PROC || t == OBJ_COMPILED_PROC || t == OBJ_CONTINUATION;
    }
    return false;
}

/* Number extraction — works for int, double, rational, bignum */
static inline bool is_number(Value v) {
    return IS_INT(v) || IS_DOUBLE(v) || IS_RATIONAL(v) || IS_BIGNUM(v);
}

double as_number_double(Value v);  /* coerce any number to double */
int64_t as_number_int(Value v);    /* coerce any number to int (truncate) */

/* Arithmetic that preserves exactness */
Value num_add(Value a, Value b);
Value num_sub(Value a, Value b);
Value num_mul(Value a, Value b);
Value num_div(Value a, Value b);
Value num_neg(Value a);
bool num_eq(Value a, Value b);
bool num_lt(Value a, Value b);
bool num_gt(Value a, Value b);
bool num_le(Value a, Value b);
bool num_ge(Value a, Value b);

/* Record type registry */
typedef struct RecordType {
    char *name;
    char **fields;
    int nfields;
    char *parent;
    struct RecordType *next;
} RecordType;

extern RecordType *g_record_types;
RecordType *find_record_type(const char *name);
void register_record_type(const char *name, char **fields, int nfields, const char *parent);
bool is_subtype(const char *child, const char *ancestor);

/* Module registry */
typedef struct Module {
    char *name;
    Env *env;
    char **exports;
    int nexports;
    struct Module *next;
} Module;

extern Module *g_modules;

#endif /* LUMBDA_H */
