module Treetop module Compiler grammar Metagrammar rule treetop_file requires:(space? require_statement)* spacing:space? module_or_grammar suffix:space? { def compile requires.text_value + spacing.text_value + module_or_grammar.compile + suffix.text_value end } end rule require_statement spacing:space? "require" [ \t]+ [^\n\r]+ [\n\r] end rule module_or_grammar module_declaration / grammar end rule module_declaration module_prefix:(('module'/'class') space name:([A-Z] alphanumeric_char* ('::' [A-Z] alphanumeric_char*)*) space) module_contents:(module_declaration / grammar) suffix:(space 'end') { def compile module_prefix.text_value + module_contents.compile + suffix.text_value end def parser_name module_prefix.name.text_value+'::'+module_contents.parser_name end } end rule grammar 'grammar' space grammar_name space ('do' space)? declaration_sequence space? 'end' end rule grammar_name ([A-Z] alphanumeric_char*) end rule declaration_sequence head:declaration tail:(space declaration)* { def declarations [head] + tail end def tail super.elements.map { |elt| elt.declaration } end } / '' { def compile(builder) end } end rule declaration parsing_rule / include_declaration end rule include_declaration 'include' space [A-Z] (alphanumeric_char / '::')* { def compile(builder) builder << text_value end } end rule parsing_rule 'rule' space nonterminal space ('do' space)? parsing_expression space 'end' end rule parsing_expression choice / sequence / primary end rule choice head:alternative tail:(space? '/' space? alternative)+ { def alternatives [head] + tail end def tail super.elements.map {|elt| elt.alternative} end def parent_modules [] end def inline_modules (alternatives.map {|alt| alt.inline_modules }).flatten end def inline_module nil end } end rule sequence sequence_body node_class_declarations { def sequence_elements [sequence_body.head] + tail end def tail sequence_body.tail end def parent_modules node_class_declarations.inline_modules end def inline_modules (sequence_elements.map {|elt| elt.inline_modules}).flatten + [sequence_element_accessor_module] + parent_modules end def inline_module node_class_declarations.inline_module end def inline_module_name node_class_declarations.inline_module_name end } end rule sequence_body variable_length_sequence_body / labeled_expression_sequence_body end rule variable_length_sequence_body head:optionally_labeled_sequence_primary tail:(space optionally_labeled_sequence_primary)+ { def tail super.elements.map {|elt| elt.optionally_labeled_sequence_primary } end } end rule labeled_expression_sequence_body labeled_sequence_primary { def head self end def tail [] end } end rule alternative sequence / primary end rule primary prefix atomic { def compile(address, builder, parent_expression=nil) prefix.compile(address, builder, self) end def prefixed_expression atomic end def parent_modules [] end def inline_modules atomic.inline_modules end def inline_module atomic.inline_module end def inline_module_name nil end } / prefix space? atomic:predicate_block { def compile(address, builder, parent_expression=nil) prefix.compile(address, builder, self) end def prefixed_expression atomic end def parent_modules [] end def inline_modules [] end def inline_module nil end } / atomic suffix node_class_declarations { def compile(address, builder, parent_expression=nil) if node_class_declarations.inline_module && atomic.inline_module STDERR.puts "Extraneous module ignored after suffix: #{input[interval].inspect}" end suffix.compile(address, builder, self) end def optional_expression atomic end def node_class_name node_class_declarations.node_class_name end def parent_modules node_class_declarations.inline_modules end def inline_modules atomic.inline_modules + parent_modules end def inline_module node_class_declarations.inline_module end def inline_module_name node_class_declarations.inline_module_name end } / atomic node_class_declarations { def compile(address, builder, parent_expression=nil) if node_class_declarations.inline_module && atomic.inline_module STDERR.puts "Extraneous module ignored with nested atomic: #{input[interval].inspect}" end atomic.compile(address, builder, self) end def node_class_name node_class_declarations.node_class_name end def parent_modules node_class_declarations.inline_modules end def inline_modules atomic.inline_modules + parent_modules end def inline_module node_class_declarations.inline_module end def inline_module_name node_class_declarations.inline_module_name end } end rule optionally_labeled_sequence_primary labeled_sequence_primary / unlabeled_sequence_primary end rule labeled_sequence_primary named_label sequence_primary { def compile(lexical_address, builder) sequence_primary.compile(lexical_address, builder) end def parent_modules [] end def inline_modules sequence_primary.inline_modules end def label_name named_label.name end } end rule unlabeled_sequence_primary null_label sequence_primary { def compile(lexical_address, builder) sequence_primary.compile(lexical_address, builder) end def parent_modules [] end def inline_modules sequence_primary.inline_modules end def label_name if sequence_primary.instance_of?(Nonterminal) sequence_primary.text_value else nil end end } end rule label named_label / null_label end rule named_label (alpha_char alphanumeric_char*) ':' { def name elements[0].text_value end } end rule null_label '' { def name nil end } end rule sequence_primary prefix atomic { def compile(lexical_address, builder) prefix.compile(lexical_address, builder, self) end def prefixed_expression elements[1] end def parent_modules [] end def inline_modules atomic.inline_modules end def inline_module_name nil end } / prefix space? atomic:predicate_block { def compile(address, builder, parent_expression=nil) prefix.compile(address, builder, self) end def prefixed_expression atomic end def parent_modules [] end def inline_modules [] end } / atomic suffix { def compile(lexical_address, builder) suffix.compile(lexical_address, builder, self) end def node_class_name nil end def parent_modules [] end def inline_modules atomic.inline_modules end def inline_module_name nil end } / atomic end rule suffix repetition_suffix / optional_suffix end rule optional_suffix '?' end rule node_class_declarations node_class_expression trailing_inline_module { def node_class_name node_class_expression.node_class_name end def inline_modules trailing_inline_module.inline_modules end def inline_module trailing_inline_module.inline_module end def inline_module_name inline_module.module_name if inline_module end } end rule repetition_suffix '+' / '*' / occurrence_range end rule occurrence_range space? min:([0-9])* '..' max:([0-9])* end rule prefix '&' / '!' / '~' end rule atomic terminal / nonterminal / parenthesized_expression end rule parenthesized_expression '(' space? parsing_expression space? ')' { def parent_modules [] end def inline_modules parsing_expression.inline_modules end def inline_module parsing_expression.inline_module end } end rule nonterminal !keyword_inside_grammar (alpha_char alphanumeric_char*) end rule terminal quoted_string / character_class / anything_symbol end rule quoted_string qs:(single_quoted_string / double_quoted_string) modifiers:([ir]*) { def string qs.text_value end } end rule double_quoted_string '"' string:(!'"' ("\\\\" / '\"' / .))* '"' end rule single_quoted_string "'" string:(!"'" ("\\\\" / "\\'" / .))* "'" end rule character_class '[' characters:(!']' ('\\' . / bracket_expression / !'\\' .))+ ']' { def characters super.text_value end } end rule bracket_expression '[:' '^'? ( 'alnum' / 'alpha' / 'blank' / 'cntrl' / 'digit' / 'graph' / 'lower' / 'print' / 'punct' / 'space' / 'upper' / 'xdigit' / 'word' ) ':]' end rule anything_symbol '.' end rule node_class_expression space '<' (!'>' .)+ '>' { def node_class_name elements[2].text_value end } / '' { def node_class_name nil end } end rule trailing_inline_module space inline_module { def parent_modules [] end def inline_modules [inline_module] end def inline_module_name inline_module.module_name end } / '' { def parent_modules [] end def inline_modules [] end def inline_module nil end def inline_module_name nil end } end rule predicate_block '' inline_module end rule inline_module '{' (inline_module / ![{}] .)* '}' end rule keyword_inside_grammar ('rule' / 'end') !non_space_char end rule non_space_char !space . end rule alpha_char [A-Za-z_] end rule alphanumeric_char alpha_char / [0-9] end rule space (white / comment_to_eol)+ end rule comment_to_eol '#' (!"\n" .)* end rule white [ \t\n\r] end end end end