lumbda/c/lumbda.h
russell@unturf.com 2f342c3be2
c-tier bignum — arbitrary-precision integers unblock secp256k1 widths
Adds tagged bignum support alongside the existing 48-bit fixnum on the C
tier. Tag 6 = bignum, heap struct sign-magnitude with u64 little-endian
limbs. Reader emits bignums for any literal past the fixnum range; +, -,
*, quotient, remainder, modulo, expt, =, <, >, abs, odd?, even?,
integer?, exact?, number->string, string->number all promote fixnum →
bignum on overflow & demote back when results fit. Boehm GC owns every
allocation. Schoolbook O(n²) mul + shift-subtract divmod is sufficient
at our 4-limb / 256-bit scale.

Before: (expt 2 48) = 0, (expt 2 256) = 0, secp256k1-p = -4294968273.
After: all three return their exact arbitrary-precision values, matching
Python tier byte-for-byte.

Validated:
- c/test.c — 85/85 pass (+2 new bignum unit tests).
- tests/functional.lsp — 205/205 pass on both C & Python tiers.
- tests/bignum-cross-tier.lsp — 33/33 pass byte-identical on both tiers
  (diff produces no output).
- ecdsa/runs/lumbda-sweep-003/c-tier-bignum-probe.lsp — all four
  assertions now match the Python oracle.
- ecdsa Phase B byte-identity sweep inside QEMU guest:
  n+1=9  p=251           sha256 c668bbe3... — matches Python oracle.
  n+1=18 p=131071        sha256 8a031f96... — matches Python oracle.
  n+1=33 p=2³²-5         sha256 0bc56905... — matches Python oracle.
  Previously the n+1=33 C tier emitted sha256 b024d6d9... (26,078 fewer
  Toffolis due to silent fixnum wrap). Bignums close that gate.

secp256k1 production-width emit (n+1=257) is now structurally unblocked
on C tier; downstream agent (#55) drives that next-step on the ecdsa
side. Asm tier inherits in a follow-up port.
2026-06-06 20:23:37 -04:00

840 lines
32 KiB
C

/*
* 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)
#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)
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
/* ═══════════════════════════════════════════════════════════════════════════
* 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 */