require ‘rattler/compiler’

grammar Rattler::Compiler::Metagrammar

include Rattler::Parsers

%whitespace (SPACE+ / comment)*

grammar <- heading rules EOF <Grammar>

heading <- requires module_decl? includes start_directive? { heading *_ }

requires <- require_statement* { { :requires => _ } }

require_statement <- ~‘require` @(!eol .)+ ~eol

/ ~`require_relative` @(!eol .)+ ~eol                       { expand_relative _ }

module_decl <- ~‘parser` constant (~’<‘ constant)? ~eol { parser_decl *_ }

/ ~`grammar` constant ~eol                                  { { :grammar_name => _ } }

includes <- (~‘include` constant ~eol)* { { :includes => _ } }

start_directive <- ~‘%start` identifier { { :start_rule => _ } }

rules <- (directive / rule / block_close)* <RuleSet>

directive <- ws_directive / wc_directive / inline_directive / fragments

ws_directive <- ws_decl ~‘{’ { start_ws _ }

/ ws_decl                                                   { set_ws _ }

ws_decl <- ~‘%whitespace` unattributed

wc_directive <- wc_decl ~‘{’ { start_wc _ }

/ wc_decl                                                   { set_wc _ }

wc_decl <- ~‘%word_character` unattributed

inline_directive <- ~‘%inline` ~’{‘ { start_inline }

/ ~`%inline`                                                { set_inline }

fragments <- ~‘%fragments` ~’{‘ { start_fragments }

/ ~`%fragments`                                             { set_fragments }

block_close <- ~‘}’ { end_block }

rule <- identifier ~‘<-’ expression { rule *_ }

unattributed <- unattributed ~‘/’ terms <Choice>

/ terms

expression <- expression ~‘/’ attributed <Choice>

/ attributed

attributed <- attributed? (semantic_action / node_action) <AttributedSequence>

/ attributed_terms

old_node_action <- @(name (~‘.’ var_name)?)

semantic_action <- ~‘{’ action_code ~‘}’ <SemanticAction>

action_code <- @(( ‘{’ [^}]* ‘}’

/ [^{}]         )*)

node_action <- ~‘<’ (name (~‘.’ var_name)?)? literal? ~‘>’ <NodeAction>

attributed_terms <- attributed term <Sequence>

/ terms

terms <- terms term <Sequence>

/ term

term <- fail_expr / labeled / labelable

fail_expr <- fail_keyword fail_arg <Fail>

labeled <- var_name ~‘:’ labelable <Label>

labelable <- semantic_term / list / list_term

semantic_term <- ~‘^’ semantic_action

/ ~'&' semantic_action                                      { Assert[_] }
/ ~'!' semantic_action                                      { Disallow[_] }
/ ~'~' semantic_action                                      { Skip[_] }

list <- list_term ~(‘*’ ‘,’) list_term { list0 *_ }

/ list_term ~('+' ',') list_term                            { list1 *_ }
/ list_term repeat_count ~',' list_term                     <ListParser>

list_term <- prefixed

/ prefixable
/ expected 'term'

prefixed <- ~‘&’ prefixable <Assert>

/ ~'!' prefixable                                           <Disallow>
/ ~'~' prefixable                                           <Skip>
/ ~'@' prefixable                                           <Token>

prefixable <- prefixed / suffixable

/ expected 'primary'

suffixed <- suffixable ~‘?’ { optional _ }

/ suffixable ~'*' !','                                      { zero_or_more _ }
/ suffixable ~'+' !','                                      { one_or_more _ }
/ suffixable repeat_count !','                              <Repeat>

repeat_count <- @DIGIT+ ~‘..’ @DIGIT+ { _.map {|s| s.to_i } }

/ @DIGIT+ ~'..'                                             { [_.to_i, nil] }
/ @DIGIT+                                                   { [_.to_i] * 2 }

suffixable <- suffixed / primary

/ expected 'primary'

primary <- ~‘(’ expression ~‘)’

/ atom

atom <- ~‘EOF` <Eof>

/ ~`E`                                                      <ESymbol>
/ ~`super`                                                  { Super[:pending] }
/ posix_class                                               { posix_class _ }
/ identifier !'<-'                                          <Apply>
/ literal                                                   { literal _ }
/ word_literal                                              { word_literal _ }
/ class                                                     { char_class _ }
/ back_reference                                            <BackReference>
/ ~'.'                                                      { Match[/./] }

%inline

fail_keyword <- ‘fail` / `fail_rule` / `fail_parse` / `expected`

fail_arg <- ~‘(’ literal ~‘)’

/ literal

posix_class <- ‘ALNUM`

/ `ALPHA`
/ `ASCII`
/ `BLANK`
/ `CNTRL`
/ `DIGIT`
/ `GRAPH`
/ `LOWER`
/ `PRINT`
/ `PUNCT`
/ `SPACE`
/ `UPPER`
/ `XDIGIT`
/ `WORD`

literal <- @(‘“’ (‘\’ . / [^”])* ‘“’)

/ @("'" ('\\' . / [^'])* "'")
/ @('%(' ('\\' . / [^)])* ')')
/ @('%{' ('\\' . / [^}])* '}')
/ @('%[' ('\\' . / [^\]])* ']')
/ @('%<' ('\\' . / [^>])* '>')
/ @('%' q:PUNCT ('\\' . / !$q .)* $q)

word_literal <- @(“‘” (’\‘ . / [^`])* “`”)

class <- @(‘[’ (!‘]’ range)+ ‘]’)

name <- var_name

/ constant

identifier <- !‘EOF` @(ALPHA WORD*)

back_reference <- @(‘$’ LOWER WORD*)

var_name <- @(LOWER WORD*)

constant <- @((UPPER WORD* ‘::’)* UPPER WORD*)

const_name <- @(UPPER WORD*)

%fragments

range <- ‘[:’ posix_name ‘:]’

/ class_char ('-' class_char)?

posix_name <- ‘alnum`

/ `alpha`
/ `ascii`
/ `blank`
/ `cntrl`
/ `digit`
/ `graph`
/ `lower`
/ `print`
/ `punct`
/ `space`
/ `upper`
/ `xdigit`

class_char <- ‘\’ [0-3] [0-7] [0-7]

/ '\\x' XDIGIT XDIGIT
/ '\\' .
/ [^\\\]]

eol <- ~(BLANK* (EOF / ‘;’ / “r”? “n” / comment))

comment <- ~(‘#’ [^n]*)