Repository navigation
Templates And Inheritance
Rule templates and rule inheritance in ISPA Parser Generator provide a mechanism for creating reusable, customizable, and modular grammar components.
By parameterizing rules and allowing downstream grammars to detach, extend, or override sub-rules, ISPA enables grammar standard libraries (e.g., standard math expressions or C-like operator hierarchies) to be customized without re-writing entire rule trees.
-
Rule Templates (
rule<P1, P2>): Define generalized grammar structures with parameter placeholders. Templated rules cannot be instantiated directly; they must be specialized/inherited. -
Global Specialization (
expr<>): Implicitly maps template parameters to matching symbols in the global scope. -
Sub-rule Detachment (
detach rule_name): Selectively remove unused sub-rules (and their references) from inherited rules. -
Sub-rule Overriding (
#subrule): Redefine nested rules within inherited templates to adapt matching behavior. -
Rule Aliasing (
expression = expr<...>): Create clean top-level rule definitions bound to parameterized templates.
A templated rule acts as an abstract syntax pattern. It can define nested sub-rules, inline operators, and placeholder tokens.
// Abstract expression template with token placeholders ID and NUMBER
expr<ID, NUMBER>:
(expr '||' expr)
| (expr '&&' expr)
| (expr '==' expr)
| (expr '!=' expr)
| (expr '<' expr)
| (expr '>' expr)
| (expr '<=' expr)
| (expr '>=' expr)
| term
| (expr '?' expr ':' expr)
| (ID '=' expr)
#term:
(term '*' factor)
| (term '/' factor)
| (term '%' factor)
| factor
;
#factor:
addition
| substract
| negative
| group
| function_call
| variable
| NUMBER
;
[inline] #addition:
('+' factor)
;
[inline] #substract:
('-' factor)
;
[inline] #negative:
('!' factor)
;
[inline] #group:
('(' expr ')')
;
[inline] #function_call:
(ID '(' arg_list? ')')
;
[inline] #variable:
ID ('[' expr ']')?
;
[inline] #arg_list:
expr (',' expr)*
;
;
To use a templated rule, it must be inherited and specialized into a non-templated rule.
When specializing a rule, you can use detach to strip unwanted features and re-define #subrules to change behavior:
// Inherit from expr<ID, NUMBER> and customize it
expr : expr<ID, NUMBER>:
// Detach removes specified sub-rules and their references from factor
detach function_call,
variable
detach arg_list
// Override parent rule matching
expr '||' expr
// Override a specific sub-rule to change operator behavior
#addition:
factor '+' factor
;
;
If the template parameter names match globally defined rules or tokens in the scope, you can specialize using empty diamond operators <>:
// Automatically binds template parameters 'ID' and 'NUMBER' to global definitions
expr : expr<>;
You can directly assign a specialized template instance to a new rule identifier:
expression = expr<ID, NUMBER>;
ID: [a-zA-Z0-9]+;
NUMBER: [0-9]+;
Note: Rule inheritance is currently a design concept undergoing evaluation for production feasibility.
-
Rule Detachment Mechanics:
-
Static AST Pruning: When
detachis called, the compiler must rewrite the parent rule's match alternatives to remove references to the detached rule. - Dangling Reference Detection: If a rule is detached, any parent rule strictly requiring it must raise a build-time compiler error.
-
Static AST Pruning: When
-
Parsing Table Overhead:
- Templated inheritance generates new monomorphized rule variants during compilation. Ensure table generators (LL / LR) optimize identical sub-rule expansions to prevent table explosion.
-
Alternative Syntax Options for Production:
-
Composition over Inheritance: Evaluating whether module-level generics (
module expr<ID, NUMBER>) or rule inheritance provides cleaner mental models for grammar authors.
-
Composition over Inheritance: Evaluating whether module-level generics (