lib/regexp_parser/expression/quantifier.rb
module Regexp::Expression
# TODO: in v3.0.0, maybe put Shared back into Base, and inherit from Base and
# call super in #initialize, but raise in #quantifier= and #quantify,
# or introduce an Expression::Quantifiable intermediate class.
# Or actually allow chaining as a more concise but tricky solution than PR#69.
class Quantifier
include Regexp::Expression::Shared
MODES = %i[greedy possessive reluctant]
def initialize(*args)
deprecated_old_init(*args) and return if args.count == 4 || args.count == 5
init_from_token_and_options(*args)
# TODO: remove in v3.0.0, stop removing parts of #token (?)
self.token = token.to_s.sub(/_(greedy|possessive|reluctant)/, '').to_sym
end
def to_h
{
token: token,
text: text,
mode: mode,
min: min,
max: max,
}
end
MODES.each do |mode|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{mode}?
mode.equal?(:#{mode})
end
RUBY
end
alias :lazy? :reluctant?
def min
derived_data[:min]
end
def max
derived_data[:max]
end
def mode
derived_data[:mode]
end
private
def deprecated_old_init(token, text, _min, _max, _mode = :greedy)
warn "Calling `Expression::Base#quantify` or `#{self.class}.new` with 4+ arguments "\
"is deprecated.\nIt will no longer be supported in regexp_parser v3.0.0.\n"\
"Please pass a Regexp::Token instead, e.g. replace `token, text, min, max, mode` "\
"with `::Regexp::Token.new(:quantifier, token, text)`. min, max, and mode "\
"will be derived automatically.\n"\
"Or do `exp.quantifier = #{self.class}.construct(token: token, text: str)`.\n"\
"This is consistent with how Expression::Base instances are created. "
@token = token
@text = text
end
def derived_data
@derived_data ||= begin
min, max =
case text[0]
when '?'; [0, 1]
when '*'; [0, -1]
when '+'; [1, -1]
else
int_min = text[/\{(\d*)/, 1]
int_max = text[/,?(\d*)\}/, 1]
[int_min.to_i, (int_max.empty? ? -1 : int_max.to_i)]
end
mod = text[/.([?+])/, 1]
mode = (mod == '?' && :reluctant) || (mod == '+' && :possessive) || :greedy
{ min: min, max: max, mode: mode }
end
end
end
end