# Lime Module Format **Author**: Lime Parser Generator Team **Date**: 2026-04-24 **Status**: Active ## Overview Lime supports modular grammar development through **directive-based metadata** embedded directly in `.lime` files. This format allows you to split large grammars into smaller, reusable modules while maintaining all composition capabilities (dependency tracking, symbol resolution, and topological ordering). ### Why Directive Format? The directive format is designed for developers familiar with Bison/Yacc: - **Familiar Syntax**: Uses `%` directives just like `%token`, `%type`, `%left`, etc. - **Single File**: All metadata in the grammar file itself—no separate YAML or JSON - **No Nesting**: Grammar rules are direct, not wrapped in Markdown or fenced blocks - **Minimal Overhead**: ~4x less metadata overhead compared to literate format - **C-Style Comments**: Standard `/* */` comments throughout - **Professional Format**: Looks like production grammar files ### Comparison to Bison/Yacc If you know Bison/Yacc, you already know most of Lime's module format: | Bison/Yacc | Lime Module | |------------|-------------| | `%token` | Same | | `%type` | Same | | `%left`, `%right` | Same | | *(not available)* | `%module_name` | | *(not available)* | `%module_version` | | *(not available)* | `%require`, `%export`, `%import` | ## Directive Syntax ### Module Identity **Required for modular grammars:** ```lime %module_name identifier %module_version "major.minor.patch" ``` **Optional:** ```lime %module_description "Short description" ``` **Example:** ```lime %module_name expr %module_version "1.0.0" %module_description "Expression grammar with operators and precedence" ``` **Rules:** - `module_name` must be alphanumeric plus underscores (e.g., `expr`, `pg_base`, `json_parser`) - `module_version` must be semantic version format (e.g., `"1.0.0"`, `"2.1.3"`) - `module_description` is optional, use quotes if it contains spaces - If `%module_name` is present, `%module_version` is required ### Dependencies **Syntax:** ```lime %require module_name [version_constraint]. ``` **Examples:** ```lime %require base. %require operators ">= 1.0.0". %require utils ">= 2.1.0 < 3.0.0". ``` **Rules:** - Module name must match a `%module_name` in another file - Version constraint is optional, uses operators: `>=`, `<=`, `>`, `<`, `==` - Constraint must be quoted if it contains spaces - Ends with period (`.`) - Multiple `%require` directives allowed ### Exports **Syntax:** ```lime %export symbol1 symbol2 symbol3. ``` **Example:** ```lime %export a_expr b_expr c_expr func_expr. ``` **Rules:** - Lists non-terminals that this module makes available to other modules - Symbols must be defined in this module's grammar rules - Ends with period (`.`) - Multiple `%export` directives allowed (symbols are accumulated) ### Imports **Syntax:** ```lime %import symbol1 symbol2 ... from module_name. ``` **Example:** ```lime %import IDENT INTEGER STRING from base. %import PLUS MINUS STAR SLASH from operators. ``` **Rules:** - Lists symbols (terminals or non-terminals) imported from another module - `from module_name` clause specifies which module exports them - Symbols must be exported by the specified module - Ends with period (`.`) - Multiple `%import` directives allowed ## Complete Example ```lime /* ======================================================================== */ /* Expression Grammar */ /* ======================================================================== */ %module_name expr %module_version "1.0.0" %module_description "PostgreSQL expression grammar" %require base ">= 1.0.0". %require operators ">= 1.0.0". %export a_expr b_expr c_expr. %import IDENT INTEGER STRING from base. %import PLUS MINUS STAR SLASH from operators. %name ExprParser %token_type {Node*} %extra_argument {ParseState *state} %left PLUS MINUS. %left STAR SLASH. %type a_expr {Node*} %type b_expr {Node*} %type c_expr {Node*} /* ======================================================================== */ /* Grammar Rules */ /* ======================================================================== */ a_expr(A) ::= c_expr(B). { A = B; } a_expr(A) ::= a_expr(B) PLUS a_expr(C). [PLUS] { A = makeSimpleA_Expr(AEXPR_OP, "+", B, C); } a_expr(A) ::= a_expr(B) MINUS a_expr(C). [MINUS] { A = makeSimpleA_Expr(AEXPR_OP, "-", B, C); } b_expr(A) ::= c_expr(B). { A = B; } c_expr(A) ::= INTEGER(B). { A = makeIntConst(B.ival, B.location); } c_expr(A) ::= IDENT(B). { A = makeColumnRef(B.str, NIL, B.location, yyscanner); } ``` ## Module Composition ### Using lime-compose The `lime-compose` tool combines multiple module files into a single grammar: ```bash # Compose modules lime-compose -o output.lime base.lime operators.lime expr.lime # Verbose mode (show resolution order) lime-compose -v -o output.lime base.lime operators.lime expr.lime # List modules without composing lime-compose --list-modules base.lime operators.lime expr.lime # Dry-run (validate without output) lime-compose -n base.lime operators.lime expr.lime ``` ### How Composition Works 1. **Parse**: Extracts `%module_*`, `%require`, `%export`, `%import` directives from each file 2. **Validate**: Checks that all dependencies are satisfied and no circular dependencies exist 3. **Sort**: Performs topological sort to ensure dependencies come before dependents 4. **Strip**: Removes module directives from composed output 5. **Concatenate**: Combines modules in dependency order ### Composed Output The composed grammar includes: - Header comment listing all modules and versions - Module provenance comments (source file, version) - All grammar code with module directives removed - All non-module directives preserved (`%token`, `%type`, `%left`, etc.) **Example output header:** ```lime /* * Composed grammar generated by lime-compose * * Modules (3): * - base 1.0.0 * Base tokens and identifiers * - operators 1.0.0 * Arithmetic and logical operators * - expr 1.0.0 * Expression grammar */ /* ================================================================== */ /* Module: base */ /* Version: 1.0.0 */ /* Source: base.lime */ /* ================================================================== */ /* grammar code here */ ``` ## Migration Guide ### From Markdown + YAML Format If you have existing literate format modules, use the conversion script: ```bash # Convert single file python3 tools/convert_format.py input.md output.lime # Convert all files in directory for f in *.md; do python3 tools/convert_format.py "$f" "${f%.md}.lime" done ``` **What changes:** | Old Format (Markdown + YAML) | New Format (Directive) | |-------------------------------|------------------------| | ````yaml
name: expr
version: 1.0.0
`````` | `%module_name expr`
`%module_version "1.0.0"` | | ````yaml
depends: [base, ops]
`````` | `%require base.`
`%require ops.` | | ````yaml
provides: [a_expr, b_expr]
`````` | `%export a_expr b_expr.` | | ````lime
a_expr ::= ...
`````` | `a_expr ::= ...` | ### From Monolithic Grammar To modularize an existing single-file grammar: 1. **Identify Logical Sections**: Group related rules (e.g., expressions, statements, declarations) 2. **Create Base Module**: Extract common tokens and types 3. **Split Sections**: Move each logical section to its own module 4. **Add Dependencies**: Use `%require` for inter-module dependencies 5. **Define Exports**: Mark which symbols other modules can use 6. **Test Composition**: Verify the composed grammar matches the original **Example:** **Before (monolithic):** ```lime %token IDENT INTEGER PLUS MINUS. %type expr {int} %type stmt {int} expr ::= INTEGER. expr ::= expr PLUS expr. stmt ::= expr. ``` **After (modular):** **base.lime:** ```lime %module_name base %module_version "1.0.0" %export IDENT INTEGER. %token IDENT INTEGER. ``` **expr.lime:** ```lime %module_name expr %module_version "1.0.0" %require base. %import INTEGER from base. %export expr. %token PLUS MINUS. %type expr {int} expr ::= INTEGER. expr ::= expr PLUS expr. ``` **stmt.lime:** ```lime %module_name stmt %module_version "1.0.0" %require expr. %import expr from expr. %export stmt. %type stmt {int} stmt ::= expr. ``` ## Best Practices ### Module Organization 1. **One Concern Per Module**: Each module should handle one grammatical category 2. **Stable Interfaces**: Export symbols are your public API—keep them stable 3. **Version Carefully**: Use semantic versioning (major.minor.patch) 4. **Document Dependencies**: Use `%module_description` to explain what the module does ### Naming Conventions 1. **Module Names**: lowercase with underscores (e.g., `json_parser`, `sql_expr`) 2. **Exported Non-Terminals**: descriptive names (e.g., `a_expr`, `select_stmt`) 3. **Imported Terminals**: UPPERCASE (e.g., `IDENT`, `INTEGER`, `PLUS`) ### Dependency Management 1. **Minimize Dependencies**: Only `%require` what you actually need 2. **Avoid Cycles**: Never create circular dependencies (A→B→A) 3. **Version Constraints**: Use constraints for breaking changes (e.g., `">= 2.0.0"`) 4. **Explicit Imports**: Always list imported symbols with `%import` ### File Layout Recommended order within a `.lime` file: 1. Header comment 2. Module directives (`%module_name`, `%module_version`, `%module_description`) 3. Dependencies (`%require`) 4. Exports/Imports (`%export`, `%import`) 5. Standard Lime directives (`%name`, `%token_type`, `%extra_argument`) 6. Token declarations (`%token`) 7. Precedence declarations (`%left`, `%right`, `%nonassoc`) 8. Type declarations (`%type`) 9. Grammar rules ## Validation ### Module Metadata Validation Lime validates module directives during parsing: - **Error**: `%module_name` without `%module_version` - **Error**: Invalid semantic version format - **Error**: Unknown directive keyword - **Warning**: Undefined symbols in rules ### Composition Validation `lime-compose` validates dependencies: - **Error**: Missing required module - **Error**: Circular dependency - **Error**: Duplicate module names - **Error**: Import from module that doesn't export the symbol - **Warning**: Module exports symbols that no one imports ## Troubleshooting ### "Unknown declaration keyword: %module_name" **Cause**: Using an older version of Lime without module support **Fix**: Rebuild Lime from the latest source: ```bash gcc -o lime lime.c ``` ### "Module 'foo' requires 'bar', which is not available" **Cause**: Missing dependency in composition command **Fix**: Include all required modules: ```bash lime-compose -o output.lime bar.lime foo.lime # bar before foo ``` ### "Cyclic dependency detected among modules" **Cause**: Circular dependency (A→B→A) **Fix**: Refactor to break the cycle: 1. Identify the cycle in error message 2. Extract common dependencies to a base module 3. Have both modules depend on the base instead of each other ### "Symbol 'X' is exported by both 'A' and 'B'" **Cause**: Two modules export the same symbol **Fix**: Rename one of the symbols or remove the duplicate export ## Advanced Topics ### Version Constraints Supported operators: - `>=` : Greater than or equal - `<=` : Less than or equal - `>` : Greater than - `<` : Less than - `==` : Exact match Combine operators for ranges: ```lime %require utils ">= 2.0.0 < 3.0.0". /* 2.x only */ ``` ### Re-exporting Symbols To import and then export a symbol: ```lime %import common_expr from base. %export common_expr. /* Re-export for downstream modules */ ``` ### Optional Dependencies Currently not supported. All `%require` dependencies must be satisfied. ### Conditional Compilation Use Lime's preprocessor directives: ```lime %ifdef ENABLE_JSON %require json ">= 1.0.0". %import json_expr from json. %endif ``` ## References - **Lime Parser Generator**: Main tool documentation - **Bison Manual**: https://www.gnu.org/software/bison/manual/ - **Semantic Versioning**: https://semver.org/ ## Examples See `examples/` directory: - `examples/pg_modular/` - PostgreSQL grammar split into 35+ modules - `examples/datalog/` - Datalog parser with module directives - `examples/xpath/` - XPath query language modules - `examples/xquery/` - XQuery language modules --- **For questions or issues, see**: https://github.com/anthropics/lime/issues