lib/citrus/file.rb in citrus-2.2.2 vs lib/citrus/file.rb in citrus-2.3.0

- old
+ new

@@ -26,38 +26,40 @@ ## Hierarchical syntax rule :file do all(:space, zero_or_more(any(:require, :grammar))) { - find(:require).each {|r| require r.value } - find(:grammar).map {|g| g.value } + if captures[:require] + captures[:require].each {|r| require r.value } + end + + (captures[:grammar] || []).map {|g| g.value } } end rule :grammar do - all(:grammar_keyword, :module_name, :grammar_body, :end_keyword) { + mod all(:grammar_keyword, :module_name, :grammar_body, :end_keyword) do include ModuleNameHelpers def value module_namespace.const_set(module_basename, grammar_body.value) end - } + end end rule :grammar_body do zero_or_more(any(:include, :root, :rule)) { grammar = Grammar.new - find(:include).map do |inc| - grammar.include(inc.value) + if captures[:include] + captures[:include].each {|inc| grammar.include(inc.value) } end - root = find(:root).last grammar.root(root.value) if root - find(:rule).each do |r| - grammar.rule(r.rule_name.value, r.value) + if captures[:rule] + captures[:rule].each {|r| grammar.rule(r.rule_name.value, r.value) } end grammar } end @@ -69,101 +71,110 @@ end rule :rule_body do zero_or_one(:choice) { # An empty rule definition matches the empty string. - matches.length > 0 ? choice.value : Rule.new('') + choice ? choice.value : Rule.for('') } end rule :choice do - all(:sequence, zero_or_more([ :bar, :sequence ])) { + mod all(:sequence, zero_or_more([ :bar, :sequence ])) do def rules - @rules ||= begin - [ sequence.value ] + matches[1].matches.map do |m| - m.matches[1].value - end - end + @rules ||= captures[:sequence].map {|s| s.value } end def value rules.length > 1 ? Choice.new(rules) : rules.first end - } + end end rule :sequence do - one_or_more(:expression) { + mod one_or_more(:label_expression) do def rules - @rules ||= matches.map {|m| m.value } + @rules ||= captures[:label_expression].map {|e| e.value } end def value rules.length > 1 ? Sequence.new(rules) : rules.first end + end + end + + rule :label_expression do + all(zero_or_one(:label), :expression) { + rule = expression.value + rule.label = label.value if label + rule } end rule :expression do all(:prefix, zero_or_one(:extension)) { rule = prefix.value - extension = matches[1].first rule.extension = extension.value if extension rule } end rule :prefix do all(zero_or_one(:predicate), :suffix) { rule = suffix.value - predicate = matches[0].first rule = predicate.value(rule) if predicate rule } end rule :suffix do all(:primary, zero_or_one(:repeat)) { rule = primary.value - repeat = matches[1].first rule = repeat.value(rule) if repeat rule } end rule :primary do - any(:grouping, :proxy, :string_terminal, :terminal) + any(:grouping, :proxy, :terminal) end rule :grouping do - all(:lparen, :rule_body, :rparen) { rule_body.value } + all(:lparen, :rule_body, :rparen) { + rule_body.value + } end ## Lexical syntax rule :require do - all(:require_keyword, :quoted_string) { quoted_string.value } + all(:require_keyword, :quoted_string) { + quoted_string.value + } end rule :include do - all(:include_keyword, :module_name) { + mod all(:include_keyword, :module_name) do include ModuleNameHelpers def value module_namespace.const_get(module_basename) end - } + end end rule :root do - all(:root_keyword, :rule_name) { rule_name.value } + all(:root_keyword, :rule_name) { + rule_name.value + } end # Rule names may contain letters, numbers, underscores, and dashes. They # MUST start with a letter. rule :rule_name do - all(/[a-zA-Z][a-zA-Z0-9_-]*/, :space) { first.to_s } + all(/[a-zA-Z][a-zA-Z0-9_-]*/, :space) { + first.to_s + } end rule :proxy do any(:super, :alias) end @@ -178,86 +189,98 @@ all(notp(:end_keyword), :rule_name) { Alias.new(rule_name.value) } end + rule :terminal do + any(:string_terminal, :regular_expression, :character_class, :dot) + end + rule :string_terminal do any(:quoted_string, :case_insensitive_string) { StringTerminal.new(super(), flags) } end rule :quoted_string do - all(/(["'])(?:\\?.)*?\1/, :space) { + mod all(/(["'])(?:\\?.)*?\1/, :space) do def value - eval(first) + eval(first.to_s) end def flags 0 end - } + end end rule :case_insensitive_string do - all(/`(?:\\?.)*?`/, :space) { + mod all(/`(?:\\?.)*?`/, :space) do def value - eval(first.gsub(/^`|`$/, '"')) + eval(first.to_s.gsub(/^`|`$/, '"')) end def flags Regexp::IGNORECASE end - } + end end - rule :terminal do - any(:regular_expression, :character_class, :dot) { - Terminal.new(super()) - } - end - rule :regular_expression do all(/\/(?:\\?.)*?\/[imxouesn]*/, :space) { - eval(first) + Terminal.new(eval(first.to_s)) } end rule :character_class do all(/\[(?:\\?.)*?\]/, :space) { - Regexp.new('\A' + first, nil, 'n') + Terminal.new(Regexp.new(first.to_s, nil, 'n')) } end rule :dot do all('.', :space) { - DOT + Terminal.new(DOT) } end + rule :label do + all(/[a-zA-Z0-9_]+/, :space, ':', :space) { + first.to_sym + } + end + rule :extension do any(:tag, :block) end rule :tag do - all(:lt, :module_name, :gt) { + mod all(:lt, :module_name, :gt) do include ModuleNameHelpers def value module_namespace.const_get(module_basename) end - } + end end rule :block do all(:lcurly, zero_or_more(any(:block, /[^{}]+/)), :rcurly) { - eval('Proc.new ' + to_s, TOPLEVEL_BINDING) + proc = eval("Proc.new #{to_s}", TOPLEVEL_BINDING) + + # Attempt to detect if this is a module block using some + # extremely simple heuristics. + if to_s =~ /\b(def|include) / + Module.new(&proc) + else + proc + end } end rule :predicate do - any(:and, :not, :but, :label) + any(:and, :not, :but) end rule :and do all('&', :space) { |rule| AndPredicate.new(rule) @@ -274,44 +297,30 @@ all('~', :space) { |rule| ButPredicate.new(rule) } end - rule :label do - all(/[a-zA-Z0-9_]+/, :space, ':', :space) { |rule| - Label.new(rule, first.to_s) - } - end - rule :repeat do - any(:question, :plus, :star) { |rule| - Repeat.new(rule, min, max) - } + any(:question, :plus, :star) end rule :question do - all('?', :space) { - def min; 0 end - def max; 1 end + all('?', :space) { |rule| + Repeat.new(rule, 0, 1) } end rule :plus do - all('+', :space) { - def min; 1 end - def max; Infinity end + all('+', :space) { |rule| + Repeat.new(rule, 1, Infinity) } end rule :star do - all(/[0-9]*/, '*', /[0-9]*/, :space) { - def min - matches[0] == '' ? 0 : matches[0].to_i - end - - def max - matches[2] == '' ? Infinity : matches[2].to_i - end + all(/[0-9]*/, '*', /[0-9]*/, :space) { |rule| + min = captures[0] == '' ? 0 : captures[0].to_i + max = captures[2] == '' ? Infinity : captures[2].to_i + Repeat.new(rule, min, max) } end rule :module_name do all(one_or_more([ zero_or_one('::'), :constant ]), :space) {