1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
# frozen_string_literal: true
module Regexp::Expression
module Shared
# Test if this expression has the given test_type, which can be either
# a symbol or an array of symbols to check against the expression's type.
#
# # is it a :group expression
# exp.type? :group
#
# # is it a :set, or :meta
# exp.type? [:set, :meta]
#
def type?(test_type)
test_types = Array(test_type).map(&:to_sym)
test_types.include?(:*) || test_types.include?(type)
end
# Test if this expression has the given test_token, and optionally a given
# test_type.
#
# # Any expressions
# exp.is? :* # always returns true
#
# # is it a :capture
# exp.is? :capture
#
# # is it a :character and a :set
# exp.is? :character, :set
#
# # is it a :meta :dot
# exp.is? :dot, :meta
#
# # is it a :meta or :escape :dot
# exp.is? :dot, [:meta, :escape]
#
def is?(test_token, test_type = nil)
return true if test_token === :*
token == test_token and (test_type ? type?(test_type) : true)
end
# Test if this expression matches an entry in the given scope spec.
#
# A scope spec can be one of:
#
# . An array: Interpreted as a set of tokens, tested for inclusion
# of the expression's token.
#
# . A hash: Where the key is interpreted as the expression type
# and the value is either a symbol or an array. In this
# case, when the scope is a hash, one_of? calls itself to
# evaluate the key's value.
#
# . A symbol: matches the expression's token or type, depending on
# the level of the call. If one_of? is called directly with
# a symbol then it will always be checked against the
# type of the expression. If it's being called for a value
# from a hash, it will be checked against the token of the
# expression.
#
# # any expression
# exp.one_of?(:*) # always true
#
# # like exp.type?(:group)
# exp.one_of?(:group)
#
# # any expression of type meta
# exp.one_of?(:meta => :*)
#
# # meta dots and alternations
# exp.one_of?(:meta => [:dot, :alternation])
#
# # meta dots and any set tokens
# exp.one_of?({meta: [:dot], set: :*})
#
def one_of?(scope, top = true)
case scope
when Array
scope.include?(:*) || scope.include?(token)
when Hash
if scope.has_key?(:*)
test_type = scope.has_key?(type) ? type : :*
one_of?(scope[test_type], false)
else
scope.has_key?(type) && one_of?(scope[type], false)
end
when Symbol
scope.equal?(:*) || (top ? type?(scope) : is?(scope))
else
raise ArgumentError,
"Array, Hash, or Symbol expected, #{scope.class.name} given"
end
end
# Deep-compare two expressions for equality.
#
# When changing the conditions, please make sure to update
# #pretty_print_instance_variables so that it includes all relevant values.
def ==(other)
self.class == other.class &&
text == other.text &&
quantifier == other.quantifier &&
options == other.options &&
(terminal? || expressions == other.expressions)
end
alias :=== :==
alias :eql? :==
def optional?
quantified? && quantifier.min == 0
end
def quantified?
!quantifier.nil?
end
end
Shared.class_eval { def terminal?; self.class.terminal? end }
Shared::ClassMethods.class_eval { def terminal?; true end }
Subexpression.instance_eval { def terminal?; false end }
Shared.class_eval { def capturing?; self.class.capturing? end }
Shared::ClassMethods.class_eval { def capturing?; false end }
Group::Capture.instance_eval { def capturing?; true end }
Shared.class_eval { def comment?; self.class.comment? end }
Shared::ClassMethods.class_eval { def comment?; false end }
Comment.instance_eval { def comment?; true end }
Group::Comment.instance_eval { def comment?; true end }
Shared.class_eval { def decorative?; self.class.decorative? end }
Shared::ClassMethods.class_eval { def decorative?; false end }
FreeSpace.instance_eval { def decorative?; true end }
Group::Comment.instance_eval { def decorative?; true end }
Shared.class_eval { def referential?; self.class.referential? end }
Shared::ClassMethods.class_eval { def referential?; false end }
Backreference::Base.instance_eval { def referential?; true end }
Conditional::Condition.instance_eval { def referential?; true end }
Conditional::Expression.instance_eval { def referential?; true end }
end
|