.\" lime_grammar.5 - manual page for the Lime grammar file format .\" Copyright (c) 2024-2026 Lime Parser Generator Project .\" Public Domain .\" .TH LIME_GRAMMAR 5 "May 2026" "Lime 0.5.1" "File Formats" .SH NAME lime_grammar \- Lime parser generator grammar file format .SH DESCRIPTION A Lime grammar file defines the syntax of a language using production rules and configuration directives. Lime reads this file and generates an LALR(1) parser in C. .PP Grammar files conventionally use the .B .lime file extension. A grammar file consists of: .IP \(bu 2 Directive declarations (lines beginning with .BR % ) .IP \(bu 2 Production rules .IP \(bu 2 C\-language comments .RB ( "/* ... */" ) .SH "PRODUCTION RULES" A production rule defines how a non-terminal symbol is constructed from a sequence of terminal and non-terminal symbols. The general form is: .PP .RS .EX lhs(A) ::= sym1(B) sym2(C) sym3. { A = f(B, C); } .EE .RE .PP .TP .I lhs The non-terminal being defined (left-hand side). Non-terminal names begin with a lowercase letter. .TP .I sym1, sym2, sym3 The right-hand side symbols. Terminal symbols begin with an uppercase letter. Non-terminal symbols begin with a lowercase letter. .TP .I (A), (B), (C) Optional aliases that name the semantic value of each symbol for use in the action code. .TP .BI "{ " "C code" " }" An optional semantic action executed when the rule is reduced. Inside the action, aliases refer to the semantic values of the corresponding symbols. .IP Alias substitution is .B lexically aware: identifiers that match an alias are rewritten to stack-slot references only in code positions. Occurrences inside string literals .RB ( "\"...\"" ), character literals .RB ( "'...'" ), line comments .RB ( // ), and block comments .RB ( /*\ ...\ */ ) are preserved verbatim. C backslash escapes inside string and character literals are honored, so embedded .B \\\" or .B \\' sequences do not prematurely close the literal. .PP Every rule must be terminated by a period .RB ( . ). .PP A rule may specify an explicit precedence by appending a bracket-enclosed terminal symbol before the action: .PP .RS .EX expr(A) ::= MINUS expr(B). [UMINUS] { A = -B; } .EE .RE .PP This assigns the rule the precedence of .B UMINUS instead of the default (rightmost terminal). .SH DIRECTIVES All directives begin with a .B % character. .SS "Parser Configuration" .TP .BI %name " identifier" Set the prefix for generated parser functions. The default is .BR Parse , so the generated functions are named .BR ParseAlloc , .BR ParseFree , .BR Parse , and .BR ParseTrace . With .BR "%name Calc" , they become .BR CalcAlloc , .BR CalcFree , etc. .TP .BI %token_type " {C-type}" Declare the C data type for terminal symbol values (tokens). This is the type of the third argument to the parser function. Example: .BR "%token_type {int}" . .TP .BI %default_type " {C-type}" Declare the default C data type for non-terminal symbol values. Non-terminals without an explicit .B %type declaration use this type. .TP .BI %type " symbol {C-type}" Declare the C data type for a specific non-terminal's semantic value. Example: .BR "%type expr {int}" . .TP .BI %extra_argument " {C-declaration}" Declare an extra argument passed to the parser function on each call. This is commonly a pointer to an application context structure. Example: .BR "%extra_argument {ParseState *state}" . .TP .BI %extra_context " {C-declaration}" Declare an extra context stored in the parser object, set once during allocation. Unlike .BR %extra_argument , this value persists across calls. .TP .BI %start_symbol " symbol" Declare the start symbol of the grammar. By default, the left-hand side of the first production rule is the start symbol. .TP .BI %token_prefix " prefix" Prepend .I prefix to all token .B #define names in the generated header file. Useful to avoid name collisions. For example, .B "%token_prefix TK_" causes the token .B INTEGER to be defined as .BR TK_INTEGER . .TP .BI %stack_size " N" Set the initial parser stack depth to .IR N . The stack grows automatically if needed. The default is typically sufficient for most grammars. .TP .BI %stack_size_limit " N" Set the maximum parser stack depth to .IR N . If the stack reaches this limit, a stack overflow error is triggered. .SS "Include and Code Blocks" .TP .B %include .BI { " C code " } Insert C code at the top of the generated parser source file. Typically used for .B #include directives and type definitions. .TP .B %code .BI { " C code " } Insert C code into the generated parser source, after the include section. .SS "Precedence and Associativity" Precedence is declared from lowest to highest. Each directive defines a new precedence level. Symbols listed on the same directive share the same level and associativity. .TP .BI %left " sym1 sym2 " ... Declare .IR sym1 , " sym2" , " ..." as left-associative at a new precedence level. .TP .BI %right " sym1 sym2 " ... Declare .IR sym1 , " sym2" , " ..." as right-associative at a new precedence level. .TP .BI %nonassoc " sym1 sym2 " ... Declare .IR sym1 , " sym2" , " ..." as non-associative at a new precedence level. Non-associative operators cause a parse error when two appear at the same level without explicit grouping. .SS "Token Declarations" .TP .BI %token " name" . Declare a terminal symbol. Token declarations are optional but influence the order of token code assignment in the generated header. .TP .BI %token_class " name sym1 sym2 " ... Define a token class: a named group of terminal symbols that can be used as a single symbol in production rules. .TP .BI %fallback " first_token token1 token2 " ... Declare that if .IR token1 , " token2" , " ..." cause a syntax error, the parser should retry with .I first_token instead. This is useful for handling context-sensitive keywords. .TP .BI %wildcard " token" . Declare .I token as a wildcard that matches any token not otherwise handled by the parser in the current state. .SS "Error Handling" .TP .B %syntax_error .BI { " C code " } C code to execute when a syntax error is detected. The following identifiers are available inside the block: .RS .TP .B yymajor Token code (int) of the offending lookahead token. .TP .B yyminor The token's semantic value (type set by .BR %token_type ). The alias .B TOKEN also refers to this value. .TP .B TOKEN_LOC Source location of the token (type .BR YYLOCATIONTYPE , typically .BR LimeLocation ). Only defined when .B %locations is in effect. .TP .B yypParser Opaque parser handle (void*) that can be passed to the introspection functions .BR Parse , .BR ParseState , and .BR ParseExpectedTokens to build rich diagnostics. See the "Diagnostics API" section below. .RE .sp Example: .RS .nf %syntax_error { fprintf(stderr, "error near %s\\n", ParseTokenName(yymajor)); int s = ParseState(yypParser); int n = ParseExpectedTokens(s, NULL, 0); int *codes = malloc(n * sizeof(int)); ParseExpectedTokens(s, codes, n); fprintf(stderr, " expected one of:"); for (int i = 0; i < n; i++) { fprintf(stderr, " %s", ParseTokenName(codes[i])); } fprintf(stderr, "\\n"); free(codes); } .fi .RE .TP .B %parse_failure .BI { " C code " } C code to execute when the parser fails completely (cannot recover from a syntax error). .TP .B %parse_accept .BI { " C code " } C code to execute when the input is successfully parsed. .TP .B %stack_overflow .BI { " C code " } C code to execute if the parser stack overflows. .TP .BI %error_sync " N" Configure error recovery behavior. .BI %expect " N" Declare the expected number of parsing conflicts. If exactly .I N conflicts are found, .B lime exits with status 0. This suppresses conflict warnings when conflicts are intentional. .SS Destructors .TP .BI %destructor " symbol { C code }" Declare C code to execute when a value of type .I symbol is popped from the parser stack without being used in a reduction action. This is essential for freeing dynamically allocated semantic values to prevent memory leaks. .TP .B %token_destructor .BI { " C code " } Declare a default destructor for all terminal symbols. .TP .B %default_destructor .BI { " C code " } Declare a default destructor for all non-terminal symbols that do not have a specific .B %destructor declaration. .SS "Memory Management" .TP .BI %realloc " function" Specify the memory reallocation function used by the generated parser for stack growth. The default is .BR realloc (3). .TP .BI %free " function" Specify the memory deallocation function used by the generated parser. The default is .BR free (3). .SS "Location Tracking" .TP .B %locations Enable source location tracking in the parser. When enabled, each token and non-terminal carries location information that can be used for error reporting. A grammar that declares .B %locations must use .BR ParseLoc () (or its renamed equivalent under .BR %name_prefix ) to feed tokens; locations are stored in .B yyStackEntry.yyloc and can be read in actions via the .B @ macros described below. .TP .BI %location_type " {" " type " "}" Declare the C type used to carry locations. The default is .B LimeLocation (a {start, end} byte-offset struct). Grammars typically override this -- for example, to use a single .B int byte offset (PostgreSQL's YYLTYPE convention) or a string for source-text accumulation (PostgreSQL's ecpg preprocessor). The declared type becomes .BR YYLOCATIONTYPE . .TP .B Location aliases in actions Within an action body the following macros expand to stack-slot location reads: .RS .IP \(bu 2 .B @ -- the location of the i-th RHS symbol whose alias was declared in the rule head (e.g. .BR "e(E) ::= a(X) PLUS b(Y)." " -- @X, @Y)." .IP \(bu 2 .B "@$" or .B "@" -- the location of the LHS slot. Inside an action body these bind to a per-reduce local that holds the LHS location. Lime populates the local before the action runs (via .B YYLLOC_DEFAULT when defined, or the built-in default otherwise) and commits it to the LHS slot after the action returns. An action body can read the default and/or write the local via .BI "@$ = " expr ; the written value is what survives. This matches Bison's documented ordering. .RE .TP .BI "#define YYLLOC_DEFAULT(" "Current" ", " "Rhs" ", " "N" )\| ... .PD 0 .TP .B Location override Define the macro in the grammar's .B %include block (or in any header included therein) to override Lime's built-in location-inheritance rule on every reduce. Semantics match Bison's documented contract: .PD .RS .IP \(bu 2 .I Current is an lvalue of type .BR YYLOCATIONTYPE ; assign the LHS location to it. .IP \(bu 2 .I Rhs is a 0-indexed array of .BR YYLOCATIONTYPE . .RI Rhs[ i ] for .IR i " = 1.." N is the i-th RHS symbol's location. .B Rhs[0] is the location of the slot below the rule on the parser stack (used for the empty-rule fallback). .IP \(bu 2 .I N is the number of RHS symbols (0 for empty rules). .IP \(bu 2 .BI YYRHSLOC( Rhs , " K" ) is provided as .BI (( Rhs )[ K ]) so user macros that follow Bison's documented YYRHSLOC indirection also work unmodified. .RE .IP When the macro is .I not defined, Lime applies its built-in rule: for non-empty rules the LHS yyloc is .B Rhs[1] (via slot reuse), for empty rules it is the lookahead's location. .IP The array is stack-allocated per reduce, sized to fit the longest RHS in the grammar ( .BR YYNRHS_MAX , emitted by .BR lime ). No VLA is used. .IP .B Note on timing: Lime evaluates .B YYLLOC_DEFAULT after the action body, then assigns the result to the LHS slot. Bison evaluates it before the action body and stages it in .IR yyloc . This is observable when an action body reads .B "@$" ; see the alias documentation above. .SS "Preprocessor Directives" .TP .BI %ifdef " macro" Begin a conditional section. The enclosed grammar text is included only if .I macro was defined with the .B \-D command-line option. .TP .BI %ifndef " macro" Begin a conditional section that is included only if .I macro is .I not defined. .TP .B %endif End a conditional section started by .B %ifdef or .BR %ifndef . .SS "AST Generation" .TP .BI %ast_prefix " prefix" Set the prefix for automatically generated AST node types. .TP .BI %ast_node " symbol { members }" Define an AST node type for a non-terminal. .TP .BI %ast_list " symbol { member-type }" Define an AST list node type for a non-terminal. .TP .B %ast_auto Enable automatic generation of AST construction code in reduction actions. .SS "Module Directives" These directives support modular grammar development, where a large grammar is split into multiple files that are composed together. .TP .BI %module_name " identifier" Declare the module name for this grammar file. Must be alphanumeric with underscores. If present, .B %module_version is required. .TP .BI %module_version " \(dqmajor.minor.patch\(dq" Declare the module version using semantic versioning. .TP .BI %module_description " \(dqdescription\(dq" Optional human-readable description of the module. .TP .BI %require " module" " " \fR[\fIversion-constraint\fR]\fB. Declare a dependency on another module. The version constraint is optional and supports operators: .BR >= ", " <= ", " > ", " < ", " == . Example: .BR "%require base \(dq>= 1.0.0\(dq." . .TP .BI %export " sym1 sym2 " ...\fB.\fR Declare non-terminal symbols exported by this module for use by other modules. .TP .BI %import " sym1 sym2 " ... " from " module\fB.\fR Import symbols (terminal or non-terminal) from another module. .SS "Dialect-Conditional Blocks" .TP .BI %dialect " NAME .BI { " body " } Generator-time conditional rule block. The body is included verbatim when the macro .BI dialect_ NAME is defined at .B lime invocation time (typically via the .BI \-Ddialect= NAME CLI shorthand); when it is not defined, the entire region is removed. .IP .B %dialect is sugar over .BI "%ifdef dialect_" NAME .B { body } %endif with brace-scoped (rather than .BR %endif -terminated) body syntax. The brace scan is string- and comment-aware: a .B } inside .BR "\"...\"" , .BR '...' , .BR "/* ... */" , or .B "// ..." does not close the block. Newlines are preserved in either branch so downstream error messages carry the correct line number. .IP Locked design rules: .RS .IP \(bu 2 .I NAME must match .BR [A-Za-z_][A-Za-z0-9_]* . .IP \(bu 2 A missing brace, an unterminated body, or a nested .B %dialect inside another .B %dialect is a hard error with file-and-line diagnostics. .IP \(bu 2 .B %dialect blocks share the same token namespace as the rest of the grammar; two blocks declaring the same .B %token dedup, but conflicting .B %type annotations fail LALR construction the same way they would outside any .B %dialect block. .IP \(bu 2 Multiple .BI \-Ddialect= X .BI \-Ddialect= Y compose: both bodies are included. There is no umbrella .B dialect macro. .RE .IP v0.4.0. Worked example, design constraints, and the idempotence guarantee live in .B docs/DIALECT.md . .SS "File-Level Inheritance" .TP .BI %extends " \(dqpath\(dq Load and merge another grammar file at parser-generation time. The base file's rules, tokens, types, .B %include blocks, and other directives are absorbed into the deriving grammar before code generation. .B %extends may appear multiple times (the .I diamond pattern); the diamond convergence rules below resolve which body, type, or override wins. .IP Path resolution: .RS .IP 1. 4 Absolute path if the string starts with .BR / . .IP 2. 4 Relative to the directory of the file containing the .B %extends directive. .IP 3. 4 Entries of the .B LIME_PATH environment variable, colon-separated, in order. .RE .IP The current working directory is intentionally .I not searched (it shifts under build systems). Resolution failure and .B %extends cycles are hard errors. .IP .B "Diamond / convergence rules" (see .B docs/EXTENDS.md for full worked examples): .RS .IP \(bu 2 Rule identity is .BR ( LHS_name , .IR RHS_symbol_sequence ). Alias names, action body, and precedence mark are not part of identity, so .B %override and .B %remove match on rule shape regardless of how aliases are spelled. .IP \(bu 2 If two .B %extends paths overlap (oracle.lime and mysql.lime each .B %extends ansi_sql.lime), re-loads of identical rules silently dedup. .IP \(bu 2 If two paths .B %override the same rule with different bodies and the deriving file does not itself disambiguate, Lime errors with "diamond inheritance conflict" and a hint to add an .B %override in the derived file. .IP \(bu 2 A shallower-depth .B %override or .B %remove in the deriving file unconditionally wins over deeper sibling decisions. .IP \(bu 2 .B %include blocks concatenate in DFS order: base file first, then derived files in the order they appear. .RE .IP v0.4.1. .TP .B %override .I rule Replace a base file's rule body by identity match. The directive precedes a normal rule declaration; only the action body changes -- the rule identity .RB ( "(LHS, RHS)" ) must already exist in the inherited grammar. A missing target is a hard error ("%override: no rule of matching identity to override"). Example: .RS .PP .EX %extends "ansi_sql.lime" %override select_clause(R) ::= SELECT STAR FROM IDENT. { R = oracle_sel(); } .EE .RE .IP v0.4.1. .TP .BI %remove " rule-shape" . Drop a rule from the inherited grammar by identity match. No action body; the directive ends at the rule's terminating .BR . . If the target does not exist, the response depends on the build: .B LIME_STRICT builds (auto-defined for meson .BR buildtype=debug " / " debugoptimized ) error; release builds warn to stderr and continue. Downstream pinned grammars frequently outlive a base file's exact rule shape, which is why the soft default exists; CI runs built with .B LIME_STRICT catch the drift early. v0.4.1. .TP .BI %override_type " symbol\fR { \fIC-type\fR } Widen a base file's .B %type declaration for .IR symbol . When this actually changes the inherited type, Lime prints a noisy stderr warning of the form .RS .PP .EX file.lime:NN: warning: %override_type widens SYM from existing type; user-responsibility ABI compat .EE .PP .RE .IP The warning is intentional. The author is asserting that the new type is ABI-compatible with the prior type wherever the generated parser already uses it; Lime cannot verify the assertion. v0.4.1. See .B docs/EXTENDS.md for the ten locked design rules. .SS "Embedded Grammar Wiring" .TP .BI %embed " NAME " \fBTRIGGER\fP \(aq\fIlex\fP\(aq \fBENTRY_TOKEN\fP " TOK" . Declare a runtime grammar-mode boundary. When the host parser sees the trigger lexeme .I lex in a position that admits the entry-point terminal .IR TOK , the runtime context-switch machinery enters the embedded sub-grammar registered under .IR NAME . The directive is pure sugar over .B context_switch_register_trigger() (see .BR docs/CONTEXT_SWITCH.md ): it emits the .IB Prefix _embed_table[] and the .IB Prefix SetEmbedSnapshot() / .IB Prefix RegisterEmbedTriggers() helpers in the generated .IB basename _snapshot.c (only when .B \-n is active). .IP Fields: .RS .IP \(bu 2 .I NAME -- mode-label identifier; used as the runtime .B mode_name string. .IP \(bu 2 .RI \(aq lex \(aq -- single- or double-quoted trigger lexeme; non-empty free-form bytes. The formatter .RB ( \-F ) normalises to single quotes on emit. .IP \(bu 2 .I TOK -- entry-point lookahead terminal; must already be declared via .BR %token . .RE .IP Multiple .B %embed directives compose; each registers one trigger, in declaration order. Without .BR \-n , .B lime warns that the directive's effect is dropped (the snapshot file, where the wiring lives, is not emitted). v0.4.4. See .B docs/EMBED.md . .SH SYMBOL NAMING .IP \(bu 2 Terminal symbols (tokens) must start with an uppercase letter. .IP \(bu 2 Non-terminal symbols must start with a lowercase letter. .IP \(bu 2 The special symbol .B error is a non-terminal used for error recovery. When the parser encounters a syntax error, it pops states until it finds one where .B error is a valid lookahead, then shifts the .B error symbol and continues parsing. .SH COMMENTS C-style comments .RB ( "/* ... */" ) and C++-style line comments .RB ( "// ..." ) may appear anywhere in the grammar file outside of action code blocks. .SH EXAMPLES A minimal calculator grammar: .PP .RS .EX %name Calc %token_type {int} %type expr {int} %extra_argument {int *result} %include { #include } %syntax_error { fprintf(stderr, "syntax error\\n"); } %token PLUS MINUS TIMES DIVIDE INTEGER. %left PLUS MINUS. %left TIMES DIVIDE. %start_symbol program program ::= expr(A). { *result = A; } expr(A) ::= expr(B) PLUS expr(C). { A = B + C; } expr(A) ::= expr(B) MINUS expr(C). { A = B \- C; } expr(A) ::= expr(B) TIMES expr(C). { A = B * C; } expr(A) ::= expr(B) DIVIDE expr(C). { A = B / C; } expr(A) ::= INTEGER(B). { A = B; } .EE .RE .PP Using fallback tokens for context-sensitive keywords: .PP .RS .EX %fallback ID ABORT ACTION AFTER ANALYZE. .EE .RE .PP This means that if ABORT, ACTION, AFTER, or ANALYZE cause a syntax error in context, the parser retries with ID. .PP Using preprocessor conditionals: .PP .RS .EX %ifdef ENABLE_JSON json_expr(A) ::= expr(B) ARROW expr(C). { A = json_get(B, C); } %endif .EE .RE .PP A modular grammar file: .PP .RS .EX %module_name expr %module_version "1.0.0" %module_description "Expression grammar" %require base ">= 1.0.0". %export a_expr b_expr. %import IDENT INTEGER from base. %type a_expr {Node*} a_expr(A) ::= a_expr(B) PLUS a_expr(C). { A = make_binop('+', B, C); } a_expr(A) ::= INTEGER(B). { A = make_int(B); } .EE .RE .SH "GLR MODE" Lime ships an opt-in Generalized-LR (GLR) parsing engine alongside the default LALR(1) push parser. GLR handles grammars that LALR(1) cannot, by forking the parse stack on shift/reduce or reduce/reduce conflicts and continuing all alternatives in parallel through a Graph-Structured Stack; heads that converge to the same state are merged. When two reductions produce the same nonterminal at the same position, a user-supplied disambiguation callback picks the winner. If none picks, the parse reports unresolvable ambiguity. .PP The GLR entry point is .PP .RS .EX int lime_parse_glr(ParseContext *ctx, GLRDisambiguateFn cb, void *user); .EE .RE .PP declared in .BR . Grammars need no syntactic markup to opt in; the same Lime-generated snapshot supports both modes. Callers that never call .B lime_parse_glr() pay zero cost for GLR support being present in the runtime -- the LALR fast path .RB ( src/parse_engine.c , .BR src/snapshot.c , .BR src/jit_codegen.c ) is byte-identical to the pre-GLR tree. Measured GLR overhead vs LALR on unambiguous input is 5--8\(mu (see .BR docs/GLR.md ). v0.3.4. .SH "DIAGNOSTICS API" .PP Every Lime-generated parser exports three functions that enable rich error reporting (Rust-compiler-style caret underlines and "expected one of ..." hints). All three are renameable via the .B %name directive or the .B \-P command-line flag. .TP .BI "const char *" ParseTokenName( "int tokenCode" ); Return the string name of a terminal token code, or .B NULL if the code is out of range. .TP .BI "int " ParseState( "void *parser" ); Return the current parser state number, or .B \-1 if the handle is invalid. A freshly-initialized parser is in state 0. The return value is meaningful only as an input to .BR ParseExpectedTokens . .TP .BI "int " ParseExpectedTokens( "int stateno" ", int *" out ", int " max ); Fill .I out with the token codes that are valid at .IR stateno and return the count written (up to .IR max ). If .I out is .B NULL or .I max is 0, returns the count that would have been written so the caller can size a buffer and call again. Token codes are returned in ascending numeric order and can be passed to .B ParseTokenName to obtain human-readable names. .PP There is also a string-returning helper for quick use: .TP .BI "char *" ParseExpectedTokensString( "void *parser" ); Return a heap-allocated comma-separated list of expected token names at the parser's current state (e.g., .BR """SELECT,""\ INSERT,\ UPDATE"" ). The caller must .BR free (3) the result. .SH "BISON-COMPATIBILITY API" .PP Functions that exist to ease porting from Bison-generated parsers. All renameable via the .B %name_prefix directive. .TP .BI "int " Parse_get_lookahead( "void *parser" ", " "ParseTOKENTYPE *value" " [, " "YYLOCATIONTYPE *loc" "]" ); .PD 0 .TP .BI "void " Parse_clear_lookahead( "void *parser" ); .PD Action-body access to the parser's pending lookahead. Equivalent to Bison's .BR yychar " / " yyclearin . Returns .B YYEMPTY (-2) when no .B Parse() call is in progress. Used by grammars where an empty production needs to consume the lookahead and pull further tokens from the lexer itself (PostgreSQL plpgsql is the motivating example). P0-NEW-5. .TP .BI "void " Parse_drain( "void *parser" ); Fire any pending default reduces eagerly, matching Bison's pull-mode timing. In Lime's push-mode, after .B Parse() shifts a token the parser may be in a state whose only available next action is a reduce. By default Lime waits for the next .B Parse() call to confirm the reduce; this opt-in primitive forces the reduce to fire now. Useful for drivers whose action bodies have side effects (writing to an output stream, mutating shared state) that must precede the lexer's next side effects. PostgreSQL ecpg is the motivating example. Loops until the top of stack is in a state that requires a real lookahead, the parser accepts, or encounters an error. Safe to call between .B Parse() invocations and idempotent past quiescence. Action bodies fired during drain see no lookahead .RB ( Parse_get_lookahead returns .BR YYEMPTY ). P0-NEW-8. .SH "RUST OUTPUT" The .B \-\-rust flag on .BR lime (1) emits a self-contained Rust mirror of the parser alongside the C output. The Rust output is additive -- the C path is unchanged; both .B .c and .B .rs fall out of one .B lime invocation. Rust-specific grammar directives let action bodies and types diverge from the C side when needed: .TP .B %rust_action { code } Per-rule Rust action body override. Placed AFTER the rule's existing C .B { ... } body, attaches to the most-recent rule. When emitting the Rust output the rule's .B yy_rule_ function uses this body verbatim instead of B$ P-substituting the C body. Lets the same rule have different semantics in C and Rust, or lets the user write Rust-specific syntax (Result, ?, pattern match) that doesn't translate from C. .TP .BI "%rust_extra_argument {" "Type" } Rust analog of .BR %extra_argument . Sets the Parser struct's .B user field type and threads it as .B "&mut Type" into every reduce callback's .BR ReduceCtx . Default .B "()" when the directive is omitted. .TP .BI "%rust_value_type {" "Type" } Override the semantic Value type in Rust output. Default .BR i64 . Use for grammars whose semantic values aren't integers -- .BR String ", " "Box" ", " "struct" , etc. The C side is unaffected; per-symbol typing on the C side goes through .BR %type ", " %token_type which Rust output silently ignores. .TP .B %rust_syntax_error { code } .PD 0 .TP .B %rust_parse_accept { code } .TP .B %rust_parse_failure { code } .TP .B %rust_stack_overflow { code } .PD Optional Rust-side parse-event hooks. Each emits as a method on the .B XParser impl block; the body has access to .B "&mut self" plus .RB ( "_token: u16" ", " "_state: u16" ) on .BR on_syntax_error . Default no-op when the directive is omitted. These are the Rust analogs of .BR %syntax_error ", " %parse_accept ", " %parse_failure ", " %stack_overflow ; the C side keeps its own bodies independently. .SH "RUST API" The generated .B .rs exposes: .TP .B "pub struct XParser" The parser, named .B X + .B Parser where X comes from .BR %name . Has fields .BR stack ", " final_value " (the start-rule's value, populated" on accept), and .BR user " (the " %rust_extra_argument slot). .TP .B "pub fn new() -> Self" .PD 0 .TP .BI "pub fn new_with_user(user: " UserArg ") -> Self" .PD Construction. The .B Default bound on .B new() is implicit when .B UserArg = () or another type with .BR Default ; otherwise use .BR new_with_user . .TP .BI "pub fn push(&mut self, token_code: u16, value: " Value ") -> Result" Feed an external token code (with FIRST_TOKEN offset already applied per the .B pub const constants emitted alongside). Returns .B Ok(true) on accept, .B Ok(false) on accept-pending, .B Err(...) on parse error or out-of-range token. .TP .B "pub fn finalize(&mut self) -> Result" Equivalent to .BR push(0, Value::default()) . .PP See .B docs/RUST_OUTPUT.md for the complete reference, .B docs/RUST_BENCHMARK.md for performance measurements, and .B examples/rust_calc/ for an end-to-end Cargo example. .SH "SEE ALSO" .BR lime (1), .BR lime_lex (5), .BR lime-lsp (1), .BR yacc (1), .BR bison (1) .PP Further documentation in the source tree: .B docs/DIALECT.md , .B docs/EXTENDS.md , .B docs/EMBED.md , .B docs/DIFF_CONFLICTS.md , .B docs/LINT.md , .B docs/GLR.md , .B docs/LSP.md , .B docs/CONTEXT_SWITCH.md , .B docs/MODULE_FORMAT.md . .SH AUTHORS The Lemon parser generator was originally written by D. Richard Hipp for the SQLite project. Lime is an extended version of Lemon with support for modular grammars, runtime extensions, and additional code generation features. .SH LICENSE Public Domain.