class Parse
Attributes
@return [Error, nil]
See {Parse::rule}.
@return [Position]
Protected Class Methods
defines method name
with body body
. Inside body
{#rule_start_pos} is the value of {#pos} right before the defined method's call.
@param [Symbol] name @return [void]
# File lib/parse.rb, line 188 def self.rule(name, &body) define_method(name) do old_rule_start_pos = @rule_start_pos @rule_start_pos = pos begin if $DEBUG then STDERR.puts("#{pos.file}:#{pos.line+1}:#{pos.column+1}: entering rule :#{name}"); end r = instance_eval(&body) if $DEBUG then t = if r.nil? then "(with nil)" else "(with #{r.class.to_s})" end; STDERR.puts("#{pos.file}:#{pos.line+1}:#{pos.column+1}: exiting rule :#{name} #{t}"); end r ensure @rule_start_pos = old_rule_start_pos end end end
@overload token(name, [description], string | regexp | &body)
A shorthand for:
rule name do expect(description) { no_errors { r = (scan(string) | scan(regexp) | body) and whitespace_and_comments and r } } end
Method whitespace_and_comments
must be defined!
description
defaults to "`#{string}'"
, %(regexp "#{regexp}")
or "#{name}"
(in that order, depending on what is defined).
@return [void]
# File lib/parse.rb, line 237 def self.token(name, *args, &body) pattern, body = if body.nil? then pattern = args.pop [pattern, proc { scan(pattern) }] else [nil, body] end description = args.pop || case pattern when nil then "#{name}" when String then "`#{pattern}'" when Regexp then %(regexp "#{pattern.source}") end raise ArgumentError, "wrong number of arguments" unless args.empty? rule name do expect(description) { no_errors { r = instance_eval(&body) and whitespace_and_comments and r } } end end
Public Instance Methods
parses text
.
@param [String] text @param [String] file file the text
is taken from. @raise [Parse::Error, IOError] @return [Object] what {#start} returns (non-nil).
# File lib/parse.rb, line 29 def call(text, file = "-") @text = StringScanner.new(text) @file = file @most_probable_error = nil @allow_errors = true @rule_start_pos = nil r = start() if r.nil? or not @text.eos? then if @most_probable_error then raise @most_probable_error else raise Error.new(StringScannerPosition.new(@text.pos, @text, @file), "syntax error") end end return r end
Protected Instance Methods
alias for {#_1} and {#_2}
# File lib/parse.rb, line 171 def _(arg = nil, &block) if arg then _1(arg) else _2(&block) end end
@param [ASTNode] node @return [ASTNode] node
which is
{ASTNode#initialize_pos}({#rule_start_pos})-ed.
# File lib/parse.rb, line 212 def _1(node) node.initialize_pos(rule_start_pos) end
calls f
. If f
results in nil then it restores {#pos} to the value before the call.
@return what f
returns.
# File lib/parse.rb, line 309 def _2(&f) old_text_pos = @text.pos f.() or begin @text.pos = old_text_pos nil end end
calls f
and returns true.
@return [true]
# File lib/parse.rb, line 298 def act(&f) f.() true end
is {#pos} at the beginning of the text?
# File lib/parse.rb, line 283 def begin? @text.pos == 0 end
is {#pos} at the end of the text?
# File lib/parse.rb, line 276 def end? @text.eos? end
sets {#most_probable_error} to error
if it is more probable than {#most_probable_error} or if {#most_probable_error} is nil.
@param [Error] error @return [nil]
# File lib/parse.rb, line 400 def error(error) if @allow_errors then if @most_probable_error.nil? or @most_probable_error.pos < error.pos then @most_probable_error = error elsif @most_probable_error and @most_probable_error.pos == error.pos then @most_probable_error = @most_probable_error.or error else # do nothing end end return nil end
macro
# File lib/parse.rb, line 419 def expect(what_1, *what_2_n, &body) p = pos and body.() or expected(p, what_1, *what_2_n) end
macro
# File lib/parse.rb, line 414 def expected(pos, what_1, *what_2_n) error(Expected.new(pos, what_1, *what_2_n)) end
calls f
using {#_2} many times until it returns nil.
@return [Array] an {Array} of non-nil results of f
.
# File lib/parse.rb, line 321 def many(&f) r = [] while true f0 = _(&f) if f0 then r.push(f0) else break end end r end
calls block
. Inside the block
{#error} has no effect.
@return what block
returns.
# File lib/parse.rb, line 427 def no_errors(&block) old_allow_errors = @allow_errors begin @allow_errors = false block.() ensure @allow_errors = old_allow_errors end end
@overload not_follows
(*method_ids)
calls methods specified by +method_ids+. If any of them returns non-nil then this method returns nil, otherwise it returns true. The methods are called inside {#no_errors}. {#pos} is restored after each method's call. @param [Array<Symbol>] method_ids @return [true, nil]
@overload not_follows
(&f)
calls +f+. If +f+ returns non-nil then this method returns nil, otherwise it returns true. +f+ is called inside {#no_errors}. {#pos} is restored after +f+'s call. @return [true, nil]
# File lib/parse.rb, line 371 def not_follows(*method_ids, &f) if f then if no_errors { _{ f.() } } then nil else true end else # if not method_ids.empty? then if no_errors { method_ids.any? { |method_id| _{ __send__(method_id) } } } then nil else true end end end
The same as f.() and many(&f)
.
@return [Array, nil] an {Array} of results of f
or nil if the first call
to +f+ returned nil.
# File lib/parse.rb, line 347 def one_or_more(&f) f1 = f.() and f2_n = many(&f) and [f1, *f2_n] end
calls f
using {#_2}.
@return [Array] an empty {Array} if f
results in nil and an {Array}
containing the single result of +f+ otherwise.
# File lib/parse.rb, line 338 def opt(&f) [_(&f)].compact end
@return [Position] current {Position} in text
passed to {#call}.
# File lib/parse.rb, line 271 def pos StringScannerPosition.new(@text.pos, @text, @file) end
scans arg
in text
passed to {#call} starting from {#pos} and, if scanned successfully, advances {#pos} and returns the scanned sub-{String}. Otherwise it calls {#expected} and returns nil.
@param [String, Regexp] arg @return [String, nil]
# File lib/parse.rb, line 163 def scan(arg) case arg when Regexp then @text.scan(arg) or expected(pos, %(regexp "#{arg.source}")) when String then @text.scan(Regexp.new(Regexp.escape(arg))) or expected(pos, %("#{arg}")) end end