Lime Parser Generator 0.1.0
Runtime-extensible LALR(1) parser with SIMD tokenization and LLVM JIT
Loading...
Searching...
No Matches
parse_context.h
1/*
2** Parse context - manages parser state for a single parse session
3**
4** The parse context pins a snapshot for the duration of parsing,
5** ensuring grammar stability even if extensions are modified.
6*/
7#ifndef PARSE_CONTEXT_H
8#define PARSE_CONTEXT_H
9
10#include "snapshot.h"
11#include <stdbool.h>
12
13/* Forward declaration (full definition in parser.h) */
14typedef struct ParseContext ParseContext;
15
35 void *engine;
47 struct GrammarContextStack *context_stack;
48
61
69 LimeHostReduceFn host_reduce;
70 void *host_reduce_user;
71
78};
79
80/*
81** Create a parse context with the given snapshot.
82** Acquires a reference to the snapshot.
83*/
84ParseContext *parse_context_create(ParserSnapshot *snap);
85
86/*
87** Borrowed-snapshot variant: same as parse_context_create except
88** snap->refcount is NOT touched. Caller guarantees snap outlives
89** the returned ParseContext. See parse_begin_borrowed() in the
90** parser.h-style API section below.
91*/
92ParseContext *parse_context_create_borrowed(ParserSnapshot *snap);
93
94/*
95** Destroy a parse context, releasing the snapshot.
96*/
97void parse_context_destroy(ParseContext *ctx);
98
99/*
100** Drop the calling thread's pooled ParseContext (if any).
101**
102** Lime maintains a thread-local single-slot pool of recycled
103** ParseContexts so that parse_begin / parse_end can avoid
104** malloc/free + stack alloc/destroy on every parse. The pool
105** memory is reclaimed at process exit by default; this function
106** is for callers who want to drop the cached context explicitly
107** before joining a parser thread (test harnesses, leak hunters).
108**
109** Safe to call from a thread that has never called parse_begin
110** (no-op). Not thread-safe across threads -- each thread drains
111** its own slot.
112*/
113void parse_context_pool_drain(void);
114
115/* ------------------------------------------------------------------ */
116/* parser.h wrappers */
117/* ------------------------------------------------------------------ */
118
119ParseContext *parse_begin(ParserSnapshot *snap);
120void parse_end(ParseContext *ctx);
121ParserSnapshot *parse_get_snapshot(ParseContext *ctx);
122
153ParseContext *parse_begin_borrowed(ParserSnapshot *snap);
154
155/*
156** Feed a token to the push parser.
157**
158** ctx Active parse context.
159** token_code Token code (0 for end-of-input).
160** token_value Semantic value (may be NULL).
161** location Byte offset of the token in the source, or
162** LIME_LOC_UNKNOWN if the grammar does not declare
163** %locations or the caller does not track positions.
164**
165** Returns 0 on success, non-zero on parse error.
166*/
167int parse_token(ParseContext *ctx, int token_code, void *token_value, int location);
168
169/* ------------------------------------------------------------------ */
170/* Host-reduce binding (Letter 30) */
171/* ------------------------------------------------------------------ */
172
173/*
174** Bind a host-reduce hook on this parse session, overriding any
175** hook carried by the snapshot. When set, the push parser calls
176** `fn` on every base-grammar reduce to run the action and produce
177** the LHS value (see LimeHostReduceFn in snapshot.h). Pass fn==NULL
178** to clear the session override and fall back to the snapshot's
179** host_reduce (which may itself be NULL = recognition-only).
180**
181** This keeps the snapshot immutable/shareable: a composed snapshot
182** can be driven by different reduce dispatches per session. Has no
183** effect on the shift hot path; the hook is consulted only inside a
184** reduce.
185*/
186void parse_set_host_reduce(ParseContext *ctx, LimeHostReduceFn fn, void *user);
187
188/*
189** Return the semantic value the start-rule reduce produced once the
190** parse has accepted, or NULL if the parse has not accepted, ran no
191** host-reduce, or the start action produced NULL. The pointer's
192** lifetime is the host's responsibility (Lime never frees it).
193*/
194void *parse_result(const ParseContext *ctx);
195
196/*
197** Sentinel value for `location` callers who do not track positions
198** (or who cannot attribute a position to a given token, e.g. an
199** injected end-of-input marker). Guaranteed to be -1 so that
200** existing code that passed an integer offset happens to be
201** forward-compatible when offsets are always >= 0.
202*/
203#define LIME_LOC_UNKNOWN (-1)
204
205/* ------------------------------------------------------------------ */
206/* Grammar-context binding (optional) */
207/* ------------------------------------------------------------------ */
208
209/* Forward declaration; full type lives in include/grammar_context.h. */
211
227void parse_attach_context_stack(ParseContext *ctx, GrammarContextStack *stack);
228
241int parse_token_lex(ParseContext *ctx, int token_code, void *token_value, const char *lexeme,
242 int location);
243
244/* ------------------------------------------------------------------ */
245/* Snapshot action table lookup helpers */
246/* ------------------------------------------------------------------ */
247
248uint16_t snap_find_shift_action(const ParserSnapshot *snap, uint16_t stateno, uint16_t iLookAhead);
249uint16_t snap_find_reduce_action(const ParserSnapshot *snap, uint16_t stateno, uint16_t iLookAhead);
250
251/* ------------------------------------------------------------------ */
252/* Context-sensitive token admissibility (multi-grammar composition) */
253/* ------------------------------------------------------------------ */
254
255/* Sentinel returned by parse_context_current_state() when no parse is
256** in progress (no state to constrain a token). Equal to UINT16_MAX. */
257#define LIME_NO_STATE ((uint16_t)0xFFFFu)
258
259/* Classification of a token's action in a given LR state. Derived
260** from the snapshot's self-describing action-code ranges (see
261** snapshot.h: yy_max_shift / yy_min_shiftreduce / yy_min_reduce /
262** yy_error_action / yy_accept_action). Result type of
263** lime_token_admissible_in_state().
264**
265** "Admissible" = anything other than LIME_TOK_NONE: the parser would
266** make progress (shift, shift-reduce, reduce, or accept) on the token
267** in that state. Scanner glue resolving a lexeme that collides
268** between a base grammar and a loaded extension uses this to decide
269** which token code to emit: prefer the code admissible in the current
270** parser state. When BOTH a base and an extension code are
271** admissible, the collision is genuinely ambiguous and must be
272** resolved by the disambiguation strategy (fork-resolve), not here. */
273typedef enum LimeTokenAdmissibility {
274 LIME_TOK_NONE = 0,
275 LIME_TOK_SHIFT,
276 LIME_TOK_SHIFTREDUCE,
277 LIME_TOK_REDUCE,
278 LIME_TOK_ACCEPT
279} LimeTokenAdmissibility;
280
281/* Current LR state at the top of ctx's parse stack, or LIME_NO_STATE
282** when no parse is in progress. Raw introspection value: between
283** tokens it may be a pending shift-reduce encoding, not a settled
284** state. For an admissibility decision use
285** parse_context_token_admissible() instead (lookahead-correct). */
286uint16_t parse_context_current_state(const ParseContext *ctx);
287
288/* Lookahead-correct admissibility oracle: would the parser bound to
289** `ctx`, in its current state, make progress on `external_token_code`
290** (shift / shift-reduce / reduce / accept) rather than syntax-error?
291** This is the primary entry point for context-sensitive keyword
292** disambiguation -- it replays the engine's shift/reduce/goto loop
293** read-only (resolving pending reduces and lookahead-gated default
294** reduces) without mutating the live parse or running user actions.
295** Returns LIME_TOK_SHIFT (treat-as-admissible) before the first
296** token and on any internal limit, so it never wrongly vetoes.
297** See src/parse_engine.c and docs/MULTI_GRAMMAR.md. */
298LimeTokenAdmissibility parse_context_token_admissible(
299 const ParseContext *ctx, int external_token_code);
300
301/* Lower-level variant: classify `external_token_code` against an
302** explicit, already-settled LR state of `snap` (no pending-reduce
303** resolution -- the caller must pass a real state). Most callers
304** want parse_context_token_admissible() instead, which handles the
305** pending-reduce / default-reduce resolution for them.
306**
307** `external_token_code` is the EXTERNAL token code (the value an
308** extension or the grammar's .h #define uses). The %first_token
309** offset is applied internally; EOF (0) is preserved. Out-of-range
310** codes classify as LIME_TOK_NONE. When `stateno` is LIME_NO_STATE
311** the result is LIME_TOK_SHIFT (treat-as-admissible).
312**
313** Pure function over read-only snapshot tables; O(1) -- one action-
314** table probe. Single-grammar parsers never call this. */
315LimeTokenAdmissibility lime_token_admissible_in_state(
316 const ParserSnapshot *snap, uint16_t stateno, int external_token_code);
317
318/* ------------------------------------------------------------------ */
319/* Tier 1: full-statement fork simulation */
320/* ------------------------------------------------------------------ */
321
322/* Outcome of simulating a fresh parse of a snapshot over a fixed token
323** buffer (lime_simulate_parse). Used to rank candidate grammars when
324** a collision is admissible in more than one: feed each candidate the
325** real upcoming token stream and see how far it gets. */
326typedef struct LimeForkTrial {
327 uint32_t tokens_consumed; /* external tokens shifted before stopping */
328 bool reached_accept; /* true if the parse reached an accept state */
329 uint32_t error_count; /* unrecoverable errors encountered (0 or 1) */
331
332/* Read-only, action-free simulation of parsing `tokens[0..ntokens)`
333** (plus an implicit trailing EOF) with `snap`'s automaton, starting
334** from the LR start state. Mutates nothing and runs no user/host
335** reduce actions; it replays the engine's shift/reduce/goto loop on a
336** private state stack purely to measure how far the grammar gets.
337**
338** This is the Tier 1 primitive behind multi-grammar fork-resolve: on a
339** collision where two loaded dialects both admit the colliding token,
340** simulate each over the real lookahead and prefer the one that
341** reaches accept (or gets furthest with fewest errors). Resolves
342** structural overlap that DIVERGES within the statement; two truly-
343** identical productions trial identically (rank by Tier 2 / Tier 3).
344**
345** Cost: invoked only on a registered-extension collision; never on the
346** single-grammar / no-collision hot path. No allocation for parses
347** that stay within the inline scratch depth. */
348LimeForkTrial lime_simulate_parse(const ParserSnapshot *snap,
349 const int *tokens, uint32_t ntokens);
350
351#endif /* PARSE_CONTEXT_H */
struct GrammarContextStack GrammarContextStack
Opaque grammar context stack handle.
Per-parse-session state.
ParserSnapshot * snapshot
Snapshot pinned for this parse session.
LimeHostReduceFn host_reduce
Letter 30: optional per-session host-reduce override.
bool borrowed_snapshot
When true, this ParseContext was created via parse_begin_borrowed() and the caller guarantees the sna...
void * result_value
Letter 30: the LHS value the top-level (start-rule) reduce produced, captured when the parse accepts.
void * engine
Owned by parse_engine.c (opaque)
struct GrammarContextStack * context_stack
Optional grammar-mode boundary detector.
Opaque snapshot handle.
Definition snapshot.h:177