%% name = KPeg::FormatParser
%% custom_initialize = true
%% pre-class { require ‘kpeg/grammar’ }
%% {
## # Creates a new kpeg format parser for +str+. def initialize(str, debug=false) setup_parser(str, debug) @g = KPeg::Grammar.new end ## # The parsed grammar attr_reader :g alias_method :grammar, :g
}
eol = "\n" eof_comment = "#" (!eof .)* comment = "#" (!eol .)* eol space = " " | "\t" | eol - = (space | comment)* kleene = "*" # Allow - by itself, but not at the beginning var = < "-" | /[a-z][\w-]*/i > { text } method = < /[a-z_]\w*/i > { text } dbl_escapes = "n" { "\n" } | "s" { " " } | "r" { "\r" } | "t" { "\t" } | "v" { "\v" } | "f" { "\f" } | "b" { "\b" } | "a" { "\a" } | "e" { "\e" } | "\\" { "\\" } | "\"" { "\"" } | num_escapes | < . > { text } num_escapes = < /[0-7]{1,3}/ > { [text.to_i(8)].pack("U") } | "x" < /[a-f\d]{2}/i > { [text.to_i(16)].pack("U") } # TODO use /\h{2}/ after 1.8 support is dropped dbl_seq = < /[^\\"]+/ > { text } dbl_not_quote = ("\\" dbl_escapes | dbl_seq)*:ary { Array(ary) } dbl_string = "\"" dbl_not_quote:s "\"" { @g.str(s.join) }
sgl_escape_quote = “\‘” { “’” }
sgl_seq = < /[^']/ > { text } sgl_not_quote = (sgl_escape_quote | sgl_seq)*:segs { Array(segs) } sgl_string = "'" sgl_not_quote:s "'" { @g.str(s.join) } string = dbl_string | sgl_string not_slash = < ("\\/" | /[^\/]/)+ > { text } regexp_opts = < [a-z]* > { text } regexp = "/" not_slash:body "/" regexp_opts:opts { @g.reg body, opts } char = < /[a-z\d]/i > { text } char_range = "[" char:l "-" char:r "]" { @g.range(l,r) } range_num = < /[1-9]\d*/ > { text } range_elem = < range_num|kleene > { text } mult_range = "[" - range_elem:l - "," - range_elem:r - "]" { [l == "*" ? nil : l.to_i, r == "*" ? nil : r.to_i] } | "[" - range_num:e - "]" { [e.to_i, e.to_i] } curly_block = curly curly = "{" < (spaces | /[^{}"']+/ | string | curly)* > "}" { @g.action(text) } nested_paren = "(" (/[^()"']+/ | string | nested_paren)* ")" value = value:v ":" var:n { @g.t(v,n) } | value:v "?" { @g.maybe(v) } | value:v "+" { @g.many(v) } | value:v "*" { @g.kleene(v) } | value:v mult_range:r { @g.multiple(v, *r) } | "&" value:v { @g.andp(v) } | "!" value:v { @g.notp(v) } | "(" - expression:o - ")" { o } | "@<" - expression:o - ">" { @g.bounds(o) } | "<" - expression:o - ">" { @g.collect(o) } | curly_block | "~" method:m < nested_paren? > { @g.action("#{m}#{text}") } | "." { @g.dot } | "@" var:name < nested_paren? > !(- "=") { @g.invoke(name, text.empty? ? nil : text) } | "^" var:name < nested_paren? > { @g.foreign_invoke("parent", name, text) } | "%" var:gram "." var:name < nested_paren? > { @g.foreign_invoke(gram, name, text) } | var:name < nested_paren? > !(- "=") { @g.ref(name, nil, text.empty? ? nil : text) } | char_range | regexp | string spaces = (space | comment)+ values = values:s spaces value:v { @g.seq(s, v) } | value:l spaces value:r { @g.seq(l, r) } | value choose_cont = - "|" - values:v { v } expression = values:v choose_cont+:alts { @g.any(v, *alts) } | values args = args:a "," - var:n - { a + [n] } | - var:n - { [n] } statement = - var:v "(" args:a ")" - "=" - expression:o { @g.set(v, o, a) } | - var:v - "=" - expression:o { @g.set(v, o) } | - "%" var:name - "=" - < /[:\w]+/ > { @g.add_foreign_grammar(name, text) } | - "%%" - curly:act { @g.add_setup act } | - "%%" - var:name - curly:act { @g.add_directive name, act } | - "%%" - var:name - "=" - < (!"\n" .)+ > { @g.set_variable(name, text) } statements = statement (- statements)? eof = !. root = statements - eof_comment? eof
# These are a seperate set of rules used to parse an ast declaration
ast_constant = < /[A-Z]\w*/ > { text } ast_word = < /[a-z_]\w*/i > { text } ast_sp = (" " | "\t")* ast_words = ast_words:r ast_sp "," ast_sp ast_word:w { r + [w] } | ast_word:w { [w] } ast_root = ast_constant:c "(" ast_words:w ")" { [c, w] } | ast_constant:c "()"? { [c, []] }