class PatternBase
Provides a base class to simplify the writing of complex regular expressions rules This class completely handles capture numbers and provides convenience methods for many common Regexp operations
@note Users should not normally directly instantiate this class
Attributes
@return [Hash] The processed arguments
@return [String,PatternBase] The pattern to match
@return [PatternBase] The next pattern in the linked list of patterns
@return [Hash] The original arguments passed into initialize
Public Class Methods
Construct a new pattern
@overload initialize(pattern)
matches an exact pattern @param pattern [PatternBase, Regexp, String] the pattern to match
@overload initialize(opts)
@param opts [Hash] options @option opts [PatternBase, Regexp, String] :match the pattern to match @option opts [String] :tag_as what to tag this pattern as @option opts [Array<PatternBase, Symbol>] :includes pattern includes @option opts [String] :reference a name for this pattern can be referred to in earlier or later parts of the pattern list, or in tag_as @option opts [Array<String>] :should_fully_match string that this pattern should fully match @option opts [Array<String>] :should_partial_match string that this pattern should partially match @option opts [Array<String>] :should_not_fully_match string that this pattern should not fully match @option opts [Array<String>] :should_not_partial_match string that this pattern should not partially match @option opts [Enumerator, Integer] :at_most match up to N times, nil to match any number of times @option opts [Enumerator, Integer] :at_least match no fewer than N times, nil to match any number of times @option opts [Enumerator, Integer] :how_many_times match exactly N times @option opts [Array<String>] :word_cannot_be_any_of list of wordlike string that the pattern should not match (this is a qualifier not a unit test) @option opts [Boolean] :dont_back_track? can this pattern backtrack @note Plugins may provide additional options @note all options except :match are optional
@overload initialize(opts, deep_clone, original)
makes a copy of PatternBase @param opts [Hash] the original patterns @arguments with match @param deep_clone [:deep_clone] identifies as a deep_clone construction @param original [Hash] the original patterns @original_arguments @api private @note this should only be called by __deep_clone__, however subclasses must be able to accept this form
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 247 def initialize(*arguments) if arguments.length > 1 && arguments[1] == :deep_clone @arguments = arguments[0] @match = @arguments[:match] @arguments.delete(:match) @original_arguments = arguments[2] @next_pattern = nil return end if arguments.length > 1 # PatternBase was likely constructed like `PatternBase.new(/foo/, option: bar)` puts "PatternBase#new() expects a single Regexp, String, or Hash" puts "PatternBase#new() was provided with multiple arguments" puts "arguments:" puts arguments raise "See error above" end @next_pattern = nil arg1 = arguments[0] arg1 = {match: arg1} unless arg1.is_a? Hash @original_arguments = arg1.clone if arg1[:match].is_a? String arg1[:match] = Regexp.escape(arg1[:match]).gsub("/", "\\/") @match = arg1[:match] elsif arg1[:match].is_a? Regexp raise_if_regex_has_capture_group arg1[:match] @match = arg1[:match].inspect[1..-2] # convert to string and remove the slashes elsif arg1[:match].is_a? PatternBase @match = arg1[:match] else puts <<-HEREDOC.remove_indent Pattern.new() must be constructed with a String, Regexp, or Pattern Provided arguments: #{@original_arguments} HEREDOC raise "See error above" end # ensure that includes is either nil or a flat array if arg1[:includes] arg1[:includes] = [arg1[:includes]] unless arg1[:includes].is_a? Array arg1[:includes] = arg1[:includes].flatten end arg1.delete(:match) @arguments = arg1 end
Public Instance Methods
(see eql?
)
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 529 def ==(other) eql? other end
Deeply clone self
@return [PatternBase] a copy of self
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 833 def __deep_clone__ __deep_clone_self__.insert! @next_pattern.__deep_clone__ end
Deeply clones self, without its next_pattern
@return [PatternBase] a copy of self
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 842 def __deep_clone_self__ options = @arguments.__deep_clone__ options[:match] = @match.__deep_clone__ self.class.new(options, :deep_clone, @original_arguments) end
Adds a capture group if needed
@param [String] regex_as_string the pattern as a string
@return [String] the pattern, potentially with a capture group
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 83 def add_capture_group_if_needed(regex_as_string) regex_as_string = "(#{regex_as_string})" if needs_to_capture? regex_as_string end
Collects information about the capture groups
@api private
@param [Integer] next_group the next group number to use
@return [Array<Hash>] group attributes
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 663 def collect_group_attributes(next_group = optimize_outer_group? ? 0 : 1) groups = do_collect_self_groups(next_group) next_group += groups.length if @match.is_a? PatternBase new_groups = @match.collect_group_attributes(next_group) groups.concat(new_groups) next_group += new_groups.length end if @next_pattern.is_a? PatternBase new_groups = @next_pattern.collect_group_attributes(next_group) groups.concat(new_groups) end groups end
Converts group attributes into a captures hash
@api private
@param [Hash] groups group attributes
@return [Hash] capture hash
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 778 def convert_group_attributes_to_captures(groups) captures = {} groups.each do |group| output = {} output[:name] = group[:tag_as] unless group[:tag_as].nil? if group[:includes].is_a? Array output[:patterns] = convert_includes_to_patterns(group[:includes]) elsif !group[:includes].nil? output[:patterns] = convert_includes_to_patterns([group[:includes]]) end captures[group[:group].to_s] = output end # replace $match and $reference() with the appropriate capture number captures.each do |key, value| next if value[:name].nil? value[:name] = value[:name].gsub(/\$(?:match|reference\((.+)\))/) do |match| next ("$" + key) if match == "$match" reference_group = groups.detect do |group| group[:reference] == Regexp.last_match(1) end "$" + reference_group[:group].to_s end end end
converts an includes array into a patterns array
@api private
@param [Array<PatternBase, Symbol>] includes an includes array
@return [Array<Hash>] a patterns array
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 815 def convert_includes_to_patterns(includes) includes = [includes] unless includes.is_a? Array patterns = includes.flatten.map do |rule| next {include: rule} if rule.is_a?(String) && rule.start_with?("source.", "text.") next {include: rule.to_s} if [:$self, :$base].include? rule next {include: "##{rule}"} if rule.is_a? Symbol rule = PatternBase.new(rule) unless rule.is_a? PatternBase rule.to_tag end patterns end
return a string of any additional attributes that need to be added to the to_s
output indent is a string with the amount of space the parent block is indented, attributes are indented 2 more spaces called by to_s
@param [String] indent the spaces to indent with
@return [String] the attributes to add
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 583 def do_add_attributes(indent) # rubocop:disable Lint/UnusedMethodArgument "" end
Collect group information about self
@param [Integer] next_group The next group number to use
@return [Array<Hash>] group attributes
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 685 def do_collect_self_groups(next_group) groups = [] groups << {group: next_group}.merge(@arguments) if needs_to_capture? groups end
evaluates @match @note optionally override when inheriting @note by default this optionally adds a capture group
@param [Hash] groups group attributes
@return [String] the result of evaluating @match
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 558 def do_evaluate_self(groups) match = @match match = match.evaluate(groups) if match.is_a? PatternBase add_capture_group_if_needed(match) end
What is the name of the method that the user would call top_level is if a freestanding or chaining function is called called by to_s
@param [Boolean] top_level is this top_level or chained
@return [String] the name of the method
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 596 def do_get_to_s_name(top_level) top_level ? "Pattern.new(" : ".then(" end
Call the block for each pattern in the list
@param [Boolean] each_includes should include patterns be called? @yield [self] invokes the block with self
@return [void]
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 133 def each(each_includes = false, &block) yield self @match.each(each_includes, &block) if @match.is_a? PatternBase @next_pattern.each(each_includes, &block) if @next_pattern.is_a? PatternBase return unless each_includes return unless @arguments[:includes].is_a? Array @arguments[:includes].each do |s| next unless s.is_a? Pattern s.each(true, &block) end end
Checks for equality A pattern is considered equal to another pattern if the result of tag_as is equivalent
@param [PatternBase] other the pattern to compare
@return [Boolean] true if other is a PatternBase
and to_tag
is equivalent, false otherwise
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 522 def eql?(other) return false unless other.is_a? PatternBase to_tag == other.to_tag end
evaluates the pattern into a string suitable for inserting into a grammar or constructing a Regexp.
@param [Hash] groups if groups is nil consider this PatternBase
to be the top_level
when a pattern is top_level, group numbers and back references are relative to that pattern
@return [String] the complete pattern
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 334 def evaluate(groups = nil, fixup_refereces: false) top_level = groups.nil? groups = collect_group_attributes if top_level evaluate_array = [''] pat = self while pat.is_a? PatternBase evaluate_array << pat.evaluate_operator evaluate_array << pat.do_evaluate_self(groups) pat = pat.next_pattern end self_evaluate = RegexOperator.evaluate(evaluate_array) self_evaluate = fixup_regex_references(groups, self_evaluate) if top_level || fixup_refereces self_evaluate end
Returns the operator to use when evaluating
@return [RegexOperator] the operator to use
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 569 def evaluate_operator ConcatOperator.new end
Convert group references into backreferences
@api private
@param [Hash] groups group information for the pattern @param [String] self_regex the pattern as string
@return [String] the fixed up regex_string
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 710 def fixup_regex_references(groups, self_regex) # rubocop:disable Metrics/LineLength references = {} # convert all references to group numbers groups.each do |group| references[group[:reference]] = group[:group] unless group[:reference].nil? end # convert back references self_regex = self_regex.gsub(/\(\?\#\[:backreference:([^\\]+?):\]\)/) do match_reference = Regexp.last_match(1) if references[match_reference].nil? raise "\nWhen processing the matchResultOf:#{match_reference}, I couldn't find the group it was referencing" end # if the reference does exist, then replace it with it's number "\\#{references[match_reference]}" end # check for a subroutine to the Nth group, replace it with `\N` self_regex = self_regex.gsub(/\(\?\#\[:subroutine:([^\\]+?):\]\)/) do match_reference = Regexp.last_match(1) if references[match_reference].nil? raise "\nWhen processing the recursivelyMatch:#{match_reference}, I couldn't find the group it was referencing" end # if the reference does exist, then replace it with it's number "\\g<#{references[match_reference]}>" end # rubocop:enable Metrics/LineLength self_regex end
create a copy of this pattern that contains no groups @return [PatternBase]
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 612 def groupless __deep_clone__.map! do |s| s.arguments.delete(:tag_as) s.arguments.delete(:reference) s.arguments.delete(:includes) raise "unable to remove capture" if s.needs_to_capture? end.freeze end
does this pattern contain no capturing groups
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 606 def groupless? collect_group_attributes == [] end
Gets the patterns Hashcode
@return [Integer] the Hashcode
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 507 def hash # TODO: find a better hash code # PatternBase.new("abc") == PatternBase.new(PatternBase.new("abc")) # but PatternBase.new("abc").hash != PatternBase.new(PatternBase.new("abc")).hash @match.hash end
Append pattern to a copy of the linked list of patterns
@param [PatternBase] pattern the pattern to append
@return [PatternBase] a copy of self with pattern appended
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 71 def insert(pattern) new_pattern = __deep_clone__ new_pattern.insert!(pattern).freeze end
Appends pattern to the linked list of patterns
@param [PatternBase] pattern the pattern to append
@return [self]
@see insert
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 57 def insert!(pattern) last = self last = last.next_pattern while last.next_pattern last.next_pattern = pattern self end
Displays the Pattern
for inspection
@return [String] A representation of the pattern
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 696 def inspect super.split(" ")[0] + " match:" + @match.inspect + ">" end
Equivalent to lookAround with type set to :lookAheadFor
@param (see lookAround)
@return (see lookAround)
# File lib/ruby_grammar_builder/pattern_extensions/look_ahead_for.rb, line 14 def lookAheadFor(pattern) if pattern.is_a? Hash pattern[:type] = :lookAheadFor else pattern = {match: pattern, type: :lookAheadFor} end lookAround(pattern) end
Equivalent to lookAround with type set to :lookAheadToAvoid
@param (see lookAround)
@return (see lookAround)
# File lib/ruby_grammar_builder/pattern_extensions/look_ahead_to_avoid.rb, line 13 def lookAheadToAvoid(pattern) if pattern.is_a? Hash pattern[:type] = :lookAheadToAvoid else pattern = {match: pattern, type: :lookAheadToAvoid} end lookAround(pattern) end
Looks around for the pattern
@param [Hash] pattern pattern constructor option [Symbol] :type the look-around type
can be one of :lookAheadFor, :lookAheadToAvoid, :lookBehindFor, :lookBehindToAvoid
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/lookaround_pattern.rb, line 55 def lookAround(pattern) insert(LookAroundPattern.new(pattern)) end
Equivalent to lookAround with type set to :lookBehindFor
@param (see lookAround)
@return (see lookAround)
# File lib/ruby_grammar_builder/pattern_extensions/look_behind_for.rb, line 13 def lookBehindFor(pattern) if pattern.is_a? Hash pattern[:type] = :lookBehindFor else pattern = {match: pattern, type: :lookBehindFor} end lookAround(pattern) end
Equivalent to lookAround with type set to :lookBehindToAvoid
@param (see lookAround)
@return (see lookAround)
# File lib/ruby_grammar_builder/pattern_extensions/look_behind_to_avoid.rb, line 13 def lookBehindToAvoid(pattern) if pattern.is_a? Hash pattern[:type] = :lookBehindToAvoid else pattern = {match: pattern, type: :lookBehindToAvoid} end lookAround(pattern) end
(see map!
)
@return [PatternBase] a transformed copy of self
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 121 def map(map_includes = false, &block) __deep_clone__.map!(map_includes, &block).freeze end
Uses a block to transform all Patterns in the list
@param [Boolean] map_includes should include patterns be mapped? @yield [self] invokes the block with self for modification
@return [self]
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 96 def map!(map_includes = false, &block) yield self if @match.is_a? PatternBase if @match.frozen? puts "frozen @match" puts @match.inspect end @match = @match.map!(map_includes, &block) end if @next_pattern.is_a? PatternBase if @next_pattern.frozen? puts "frozen @next_pattern" puts @next_pattern.inspect end @next_pattern = @next_pattern.map!(map_includes, &block) end map_includes!(&block) if map_includes self end
Uses a block to transform all Patterns in all includes @api private @note only for use by map!
@yield [self] invokes the block with the includes for modification
@return [void]
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 157 def map_includes!(&block) return unless @arguments[:includes].is_a? Array @arguments[:includes].map! do |s| if s.is_a? PatternBase if s.frozen? puts "frozen s" puts s.inspect end end next s.map!(true, &block) if s.is_a? PatternBase next s end end
Match the result of some other pattern
@param [String] reference a reference to match the result of
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/match_result_of.rb, line 59 def matchResultOf(reference) insert(MatchResultOfPattern.new(reference)) end
Optionally match pattern
@param [PatternBase,Regexp,String,Hash] pattern a pattern to optionally match
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/maybe.rb, line 42 def maybe(pattern) insert(MaybePattern.new(pattern)) end
attempts to provide a memorable name for a pattern @return [String]
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 295 def name return @arguments[:reference] unless @arguments[:reference].nil? return @arguments[:tag_as] unless @arguments[:tag_as].nil? to_s end
does @arguments contain any attributes that require this pattern be captured?
@return [Boolean] if this PatternBase
needs to capture
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 25 def needs_to_capture? capturing_attributes = [ :tag_as, :reference, :includes, ] puts @match.class unless @arguments.is_a? Hash !(@arguments.keys & capturing_attributes).empty? end
Match one of the supplied patterns
@param [Array<PatternBase,Regexp,String>] patterns a list of patterns to match
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/one_of.rb, line 97 def oneOf(patterns) insert(OneOfPattern.new(patterns)) end
Match pattern one or more times
@param [PatternBase,Regexp,String,Hash] pattern a pattern to match
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/one_or_more_of.rb, line 34 def oneOrMoreOf(pattern) insert(OneOrMoreOfPattern.new(pattern)) end
Can the capture be optimized out
When the pattern has nothing after it then its capture can instead become capture group 0
@return [Boolean] can this capture become capture group 0
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 44 def optimize_outer_group? needs_to_capture? and @next_pattern.nil? end
Match either the preceding pattern chain or pattern
@param [PatternBase,Regexp,String,Hash] pattern a pattern to match
instead of the previous chain
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/or_pattern.rb, line 50 def or(pattern) insert(OrPattern.new(pattern)) end
Match a pattern that does not exist yet
@param [Symbol] placeholder the name of the pattern to match
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/placeholder.rb, line 83 def placeholder(placeholder) insert(PlaceholderPattern.new(placeholder)) end
Raise an error if regex contains a capturing group
@param [Regexp] regex the regexp to test @param [Integer] check the group to check for
@return [void]
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 856 def raise_if_regex_has_capture_group(regex, check = 1) # this will throw a RegexpError if there are no capturing groups _ignore = with_no_warnings { /#{regex}#{"\\" + check.to_s}/ } # at this point @match contains a capture group, complain raise <<-HEREDOC.remove_indent There is a pattern that is being constructed from a regular expression with a capturing group. This is not allowed, as the group cannot be tracked The bad pattern is #{self} HEREDOC rescue RegexpError # rubocop: disable Lint/HandleExceptions # no capture groups present, purposely do nothing end
Retags all tags_as
@param [Hash] args retag options @option [Boolean] :all (true) should all tags be kept @option [Boolean] :keep (true) should all tags be kept @option [String] :append a string to append to all tags (implies :keep) @option [String] tag_as maps from an old tag_as to a new tag_as @option [String] reference maps from reference to a new tag_as
@return [PatternBase] a copy of self retagged
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 633 def reTag(args) __deep_clone__.map! do |s| # tags are keep unless `all: false` or `keep: false`, and append is not a string discard_tag = (args[:all] == false || args[:keep] == false) discard_tag = false if args[:append].is_a? String args.each do |key, tag| if [s.arguments[:tag_as], s.arguments[:reference]].include? key s.arguments[:tag_as] = tag discard_tag = false end end if args[:append].is_a?(String) && s.arguments[:tag_as] s.arguments[:tag_as] = s.arguments[:tag_as] + "." + args[:append] end s.arguments.delete(:tag_as) if discard_tag end.freeze end
Recursively match some outer pattern
@param [String] reference a reference to an outer pattern
@return [PatternBase] a PatternBase
to append to
# File lib/ruby_grammar_builder/pattern_extensions/recursively_match.rb, line 68 def recursivelyMatch(reference) insert(RecursivelyMatchPattern.new(reference)) end
Resolves any placeholder patterns
@param [Hash] repository the repository to resolve patterns with
@return [PatternBase] a copy of self with placeholders resolved
# File lib/ruby_grammar_builder/pattern_extensions/placeholder.rb, line 94 def resolve(repository) __deep_clone__.map!(true) { |s| s.resolve!(repository) if s.respond_to? :resolve! }.freeze end
Runs the unit tests for self
@return [Boolean] If all test passed return true, otherwise false
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 438 def run_self_tests pass = [true] # some patterns are not able to be evaluated # do not attempt to unless required return true unless [ :should_fully_match, :should_not_fully_match, :should_partially_match, :should_not_partially_match, ].any? { |k| @arguments.include? k } copy = __deep_clone_self__ test_regex = copy.to_r test_fully_regex = wrap_with_anchors(copy).to_r warn = lambda do |symbol| puts [ "", "When testing the pattern #{test_regex.inspect}. The unit test for #{symbol} failed.", "The unit test has the following patterns:", "#{@arguments[symbol].to_yaml}", "The Failing pattern is below:", "#{self}", ].join("\n") end if @arguments[:should_fully_match].is_a? Array unless @arguments[:should_fully_match].all? { |test| test =~ test_fully_regex } warn.call :should_fully_match pass << false end end if @arguments[:should_not_fully_match].is_a? Array unless @arguments[:should_not_fully_match].none? { |test| test =~ test_fully_regex } warn.call :should_not_fully_match pass << false end end if @arguments[:should_partially_match].is_a? Array unless @arguments[:should_partially_match].all? { |test| test =~ test_regex } warn.call :should_partially_match pass << false end end if @arguments[:should_not_partially_match].is_a? Array unless @arguments[:should_not_partially_match].none? { |test| test =~ test_regex } warn.call :should_not_partially_match pass << false end end pass.none?(&:!) end
Runs the unit tests, recursively
@return [Boolean] If all test passed return true, otherwise false
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 417 def run_tests pass = [ run_self_tests, ] # run related unit tests pass << @match.run_tests if @match.is_a? PatternBase pass << @next_pattern.run_tests if @next_pattern.is_a? PatternBase if @arguments[:includes].is_a? Array @arguments[:includes]&.each { |inc| pass << inc.run_tests if inc.is_a? PatternBase } elsif @arguments[:includes].is_a? PatternBase pass << @arguments[:includes].run_tests end pass.none?(&:!) end
Scrambles references of self This method provides a way to rename all references both actual references and references to references will be scrambled in some one to one mapping, all references that were unique before remain unique
This must be idempotent, calling this repeatedly must have references be as if it was called only once, even if the pattern is cloned between calls
this is because it may be called a different number of times depending on the nest level of the patterns
@return [void] nothing
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 756 def self_scramble_references scramble = lambda do |name| return name if name.start_with?("__scrambled__") "__scrambled__" + name end tag_as = @arguments[:tag_as] reference = @arguments[:reference] @arguments[:tag_as] = scramble.call(tag_as) if tag_as.is_a? String @arguments[:reference] = scramble.call(reference) if reference.is_a? String end
(see string_single_entity)
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 601 def single_entity? string_single_entity? evaluate end
To aid in Linters all Patterns support start_pattern
which return the pattern for initial match, for a single match pattern that is itself
@return [self] This pattern
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 498 def start_pattern self end
Construct a new pattern and append to the end
@param [PatternBase] pattern options (see initialize for options) @see initialize
@return [PatternBase] a copy of self with a pattern inserted
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 541 def then(pattern) unless pattern.is_a?(PatternBase) && pattern.next_pattern.nil? pattern = Pattern.new(pattern) end insert(pattern) end
converts a pattern to a Regexp
@param [Hash] groups if groups is nil consider this PatternBase
to be the top_level
when a pattern is top_level, group numbers and back references are relative to that pattern
@return [Regexp] the pattern as a Regexp
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 360 def to_r(groups = nil) with_no_warnings { Regexp.new(evaluate(groups)) } end
Displays the PatternBase
as you would write it in code
@param [Integer] depth the current nesting depth @param [Boolean] top_level is this a top level pattern or is it being chained
@return [String] The pattern as a string
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 372 def to_s(depth = 0, top_level = true) # TODO: make this method easier to understand # rubocop:disable Metrics/LineLength begin plugins = Grammar.plugins plugins.reject! { |p| (@original_arguments.keys & p.class.options).empty? } regex_as_string = case @original_arguments[:match] when PatternBase then @original_arguments[:match].to_s(depth + 2, true) when Regexp then @original_arguments[:match].inspect when String then "/" + Regexp.escape(@original_arguments[:match]) + "/" end indent = " " * depth output = indent + do_get_to_s_name(top_level) # basic pattern information output += "\n#{indent} match: " + regex_as_string.lstrip output += ",\n#{indent} tag_as: \"" + @arguments[:tag_as] + '"' if @arguments[:tag_as] output += ",\n#{indent} reference: \"" + @arguments[:reference] + '"' if @arguments[:reference] # unit tests output += ",\n#{indent} should_fully_match: " + @arguments[:should_fully_match].to_s if @arguments[:should_fully_match] output += ",\n#{indent} should_not_fully_match: " + @arguments[:should_not_fully_match].to_s if @arguments[:should_not_fully_match] output += ",\n#{indent} should_partially_match: " + @arguments[:should_partially_match].to_s if @arguments[:should_partially_match] output += ",\n#{indent} should_not_partially_match: " + @arguments[:should_not_partially_match].to_s if @arguments[:should_not_partially_match] output += ",\n#{indent} includes: " + @arguments[:includes].to_s if @arguments[:includes] # add any linter/transform configurations plugins.each { |p| output += p.display_options(indent + " ", @original_arguments) } # subclass, ending and recursive output += do_add_attributes(indent) output += ",\n#{indent})" output += @next_pattern.to_s(depth, false).lstrip if @next_pattern output rescue return @original_arguments.to_s end # rubocop:enable Metrics/LineLength end
converts a PatternBase
to a Hash representing a textmate rule
@return [Hash] The pattern as a textmate grammar rule
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 307 def to_tag output = { match: evaluate, } output[:captures] = convert_group_attributes_to_captures(collect_group_attributes) if optimize_outer_group? # optimize captures by removing outermost output[:match] = output[:match][1..-2] output[:name] = output[:captures]["0"][:name] output[:captures]["0"].delete(:name) output[:captures].reject! { |_, v| !v || v.empty? } end output.reject! { |_, v| !v || v.empty? } output end
Uses block to recursively transform includes
@yield [PatternBase,Symbol,Regexp,String] invokes the block with each include to transform
@return [PatternBase] a copy of self with transformed includes
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 181 def transform_includes(&block) map(true) do |s| s.arguments[:includes].map!(&block) if s.arguments[:includes].is_a? Array end end
Uses block to recursively transform tag_as
@yield [String] Invokes the block to with each tag_as to transform
@return [PatternBase] a copy of self with transformed tag_as
# File lib/ruby_grammar_builder/pattern_variations/base_pattern.rb, line 194 def transform_tag_as(&block) __deep_clone__.map! do |s| s.arguments[:tag_as] = block.call(s.arguments[:tag_as]) if s.arguments[:tag_as] next unless s.arguments[:includes].is_a?(Array) s.arguments[:includes].map! do |i| next i unless i.is_a? PatternBase i.transform_tag_as(&block) end end.freeze end
Match pattern zero or more times
@param [PatternBase,Regexp,String,Hash] pattern a pattern to match
@return [PatternBase] a pattern to append to
# File lib/ruby_grammar_builder/pattern_extensions/zero_or_more_of.rb, line 42 def zeroOrMoreOf(pattern) insert(ZeroOrMoreOfPattern.new(pattern)) end