lib/citrus/file.rb in citrus-2.3.2 vs lib/citrus/file.rb in citrus-2.3.3
- old
+ new
@@ -1,5 +1,7 @@
+# encoding: UTF-8
+
require 'citrus'
module Citrus
# Some helper methods for rules that alias +module_name+ and don't want to
# use +Kernel#eval+ to retrieve Module objects.
@@ -7,11 +9,11 @@
def module_segments
@module_segments ||= module_name.value.split('::')
end
def module_namespace
- module_segments[0..-2].inject(Object) do |namespace, constant|
+ module_segments[0...-1].inject(Object) do |namespace, constant|
constant.empty? ? namespace : namespace.const_get(constant)
end
end
def module_basename
@@ -26,92 +28,75 @@
## Hierarchical syntax
rule :file do
all(:space, zero_or_more(any(:require, :grammar))) {
- if captures[:require]
- captures[:require].each {|r| require r.value }
+ captures[:require].each do |req|
+ file = req.value
+ begin
+ require file
+ rescue ::LoadError => e
+ begin
+ Citrus.require(file)
+ rescue LoadError
+ # Re-raise the original LoadError.
+ raise e
+ end
+ end
end
- (captures[:grammar] || []).map {|g| g.value }
+ captures[:grammar].map {|g| g.value }
}
end
rule :grammar do
- mod all(:grammar_keyword, :module_name, :grammar_body, :end_keyword) do
+ mod all(:grammar_keyword, :module_name, zero_or_more(any(:include, :root, :rule)), :end_keyword) do
include ModuleNameHelpers
def value
- module_namespace.const_set(module_basename, grammar_body.value)
- end
- end
- end
+ grammar = module_namespace.const_set(module_basename, Grammar.new)
- rule :grammar_body do
- zero_or_more(any(:include, :root, :rule)) {
- grammar = Grammar.new
-
- if captures[:include]
captures[:include].each {|inc| grammar.include(inc.value) }
- end
+ captures[:rule].each {|r| grammar.rule(r.rule_name.value, r.value) }
- grammar.root(root.value) if root
+ grammar.root(root.value) if root
- if captures[:rule]
- captures[:rule].each {|r| grammar.rule(r.rule_name.value, r.value) }
+ grammar
end
-
- grammar
- }
+ end
end
rule :rule do
- all(:rule_keyword, :rule_name, :rule_body, :end_keyword) {
- rule_body.value
+ all(:rule_keyword, :rule_name, zero_or_one(:expression), :end_keyword) {
+ # An empty rule definition matches the empty string.
+ expression ? expression.value : Rule.for('')
}
end
- rule :rule_body do
- zero_or_one(:choice) {
- # An empty rule definition matches the empty string.
- choice ? choice.value : Rule.for('')
+ rule :expression do
+ all(:sequence, zero_or_more([ :bar, :sequence ])) {
+ rules = captures[:sequence].map {|s| s.value }
+ rules.length > 1 ? Choice.new(rules) : rules.first
}
end
- rule :choice do
- mod all(:sequence, zero_or_more([ :bar, :sequence ])) do
- def rules
- @rules ||= captures[:sequence].map {|s| s.value }
- end
-
- def value
- rules.length > 1 ? Choice.new(rules) : rules.first
- end
- end
- end
-
rule :sequence do
- mod one_or_more(:label_expression) do
- def rules
- @rules ||= captures[:label_expression].map {|e| e.value }
- end
-
- def value
- rules.length > 1 ? Sequence.new(rules) : rules.first
- end
- end
+ one_or_more(:labelled) {
+ rules = captures[:labelled].map {|l| l.value }
+ rules.length > 1 ? Sequence.new(rules) : rules.first
+ }
end
- rule :label_expression do
- all(zero_or_one(:label), :expression) {
- rule = expression.value
+ rule :labelled do
+ all(zero_or_one(:label), :extended) {
+ rule = extended.value
rule.label = label.value if label
rule
}
end
- rule :expression do
+ rule :extended do
all(:prefix, zero_or_one(:extension)) {
rule = prefix.value
rule.extension = extension.value if extension
rule
}
@@ -136,12 +121,12 @@
rule :primary do
any(:grouping, :proxy, :terminal)
end
rule :grouping do
- all(:lparen, :rule_body, :rparen) {
- rule_body.value
+ all(:lparen, :expression, :rparen) {
+ expression.value
}
end
## Lexical syntax
@@ -190,16 +175,18 @@
Alias.new(rule_name.value)
}
end
rule :terminal do
- any(:string_terminal, :regular_expression, :character_class, :dot)
- end
+ any(:quoted_string, :case_insensitive_string, :regular_expression, :character_class, :dot) {
+ primitive = super()
- rule :string_terminal do
- any(:quoted_string, :case_insensitive_string) {
- StringTerminal.new(super(), flags)
+ if String === primitive
+ StringTerminal.new(primitive, flags)
+ else
+ Terminal.new(primitive)
+ end
}
end
rule :quoted_string do
mod all(/(["'])(?:\\?.)*?\1/, :space) do
@@ -225,23 +212,23 @@
end
end
rule :regular_expression do
all(/\/(?:\\?.)*?\/[imxouesn]*/, :space) {
- Terminal.new(eval(first.to_s))
+ eval(first.to_s)
}
end
rule :character_class do
all(/\[(?:\\?.)*?\]/, :space) {
- Terminal.new(Regexp.new(first.to_s, nil, 'n'))
+ eval("/#{first.to_s}/")
}
end
rule :dot do
all('.', :space) {
- Terminal.new(DOT)
+ DOT
}
end
rule :label do
all(/[a-zA-Z0-9_]+/, :space, ':', :space) {
@@ -315,12 +302,12 @@
}
end
rule :star do
all(/[0-9]*/, '*', /[0-9]*/, :space) { |rule|
- min = captures[0] == '' ? 0 : captures[0].to_i
- max = captures[2] == '' ? Infinity : captures[2].to_i
+ min = captures[1] == '' ? 0 : captures[1].to_i
+ max = captures[3] == '' ? Infinity : captures[3].to_i
Repeat.new(rule, min, max)
}
end
rule :module_name do
@@ -345,7 +332,14 @@
rule :constant, /[A-Z][a-zA-Z0-9_]*/
rule :white, /[ \t\n\r]/
rule :comment, /#.*/
rule :space, zero_or_more(any(:white, :comment))
+ end
+
+ def File.parse(*args) # :nodoc:
+ super
+ rescue ParseError => e
+ # Raise SyntaxError when a parse fails.
+ raise SyntaxError, e
end
end