packages feed

rtk-0.12: test-grammars/grammar.pg

grammar 'Grammar';

/*
Section of Haskell imports. Would go to the source code directly
*/

imports """
import Data.List
import qualified Data.Map as M
"""

/*
Grammar rules section. These rules are started with capital letter.

An alternative may carry a constructor name: 'Star: Clause5 '*'' makes the
generated AST constructor for that alternative 'Star' instead of the
positional default 'Ctr__<Rule>__<index>'. Every constructor-producing
alternative below is named, so the generated front end's AST reads like
prose and survives alternative reordering. Lifted (,Rule1) alternatives
pass another rule's value through, produce no constructor, and therefore
carry no name.
*/

Grammar = GrammarDef: 'grammar' StrLit ';' RuleList
        | GrammarImports: 'grammar' StrLit ';' 'imports' bigstr RuleList ;

RuleList = Rule *;

@shortcuts(r)
Rule = RuleWithOptions: OptionList Rule1
     | ,Rule1 ;

Rule: Rule1 = RuleSimple: Name '=' Clause ';'
            | RuleTyped: Name ':' Name '=' Clause ';'
            | RuleTypedFunc: Name '.' Name ':' Name '=' Clause ';'
            | RuleFunc: '.' Name ':' Name '=' Clause ';' ;

OptionList = Option+ ;

Option = Shortcuts: '@shortcuts' '(' IdList ')'
       | Symmacro: '@symmacro' ;

IdList = Name* ~ ',' ;

@shortcuts(cl)
Clause = Alt: Clause '|' Clause1
       | ,Clause1 ;

# The constructor-name label itself: it binds tighter than '|' and scopes
# over the whole juxtaposition sequence (one alternative). The bare ':'
# token cannot collide with the rule-header forms above because clauses are
# fenced off by '=' ... ';'.
Clause: Clause1 = Labeled: Name ':' Clause2
                | ,Clause2 ;

Clause: Clause2 = Seq: Clause2 Clause3
                | ,Clause3 ;

Clause: Clause3 = Lifted: ',' Clause4
                | Ignored: '!' Clause4
                | ,Clause4 ;

Clause: Clause4 = Star: Clause5 '*'
                | StarDelim: Clause5 '*' '~' Clause5
                | Plus: Clause5 '+'
                | PlusDelim: Clause5 '+' '~' Clause5
                | Opt: Clause5 '?'
                | ,Clause5 ;

Clause: Clause5 = '(' ,Clause ')'
                | Ref: Name
                | Lit: StrLit
                | Dot: '.'
                | Regex: regexplit ;

StrLit = Str: str ;
Name = Ident: id ;

/*
Lexical rules section. These rules are started with regular letter
*/

id = [a-zA-Z][A-Za-z0-9_]* ;
# String literals consume escape pairs atomically (backslash + any char),
# so a literal ending in a backslash ('\\') lexes correctly instead of the
# trailing backslash absorbing the closing quote under maximal munch.
# The backslash class lives in the shared 'backslash' macro below (the same
# convention as java.pg): inline, a class ending in a backslash trips the
# regexplit rule's own escaped-']' alternative and overruns; and the negated
# class is spelled [^\\'] so it does not end in a backslash either. This
# grammar's own front end lexes all of this before the fix takes effect (the
# bootstrap stage: accept the regenerated snapshot, rebuild, and only then
# do '\\' string literals lex in other grammars).
str = '\'' ([^\\'] | backslash .)* '\'' ;
@symmacro
dq = '"' ; # production would not be generated for symmacro
@symmacro
ndq = [^"] ;
@symmacro
backslash = [\\] ;
bigstr = dq dq dq (ndq|dq ndq| dq dq ndq |[\n])* dq dq dq;
regexplit = '[' ([^\]] | '\\]')* ']' ;
Ignore: ws = [ \t\n]+ ;
Ignore: comment = '#' .* [\n] ;
Ignore: comment1 = '/*' ([^\*]|[\*][^\/]|[\n])* '*/';