module JsRegex::SecondPass
After conversion of a full Regexp::Expression tree, this checks for Node
instances that need further processing.
Public Class Methods
call(tree)
click to toggle source
# File lib/js_regex/second_pass.rb, line 8 def call(tree) alternate_conditional_permutations(tree) tree end
Private Class Methods
adapt_backref_to_permutation(backref_node, caps_per_branch, i)
click to toggle source
# File lib/js_regex/second_pass.rb, line 93 def adapt_backref_to_permutation(backref_node, caps_per_branch, i) new_num = backref_node.children[0].to_i + caps_per_branch * i backref_node.update(children: [new_num.to_s]) end
adapt_conditional_to_permutation(conditional_node, truthy)
click to toggle source
# File lib/js_regex/second_pass.rb, line 83 def adapt_conditional_to_permutation(conditional_node, truthy) branches = conditional_node.children[1...-1] if branches.count == 1 truthy || null_quantify(branches.first) else null_quantify(truthy ? branches.last : branches.first) end conditional_node.update(type: :plain) end
adapt_referenced_group_to_permutation(group_node, truthy)
click to toggle source
# File lib/js_regex/second_pass.rb, line 79 def adapt_referenced_group_to_permutation(group_node, truthy) truthy ? min_quantify(group_node) : null_quantify(group_node) end
alternate_conditional_permutations(tree)
click to toggle source
# File lib/js_regex/second_pass.rb, line 15 def alternate_conditional_permutations(tree) permutations = conditional_tree_permutations(tree) return if permutations.empty? alternatives = permutations.map.with_index do |variant, i| Node.new((i.zero? ? '(?:' : '|(?:'), variant, ')') end tree.update(children: alternatives) end
build_permutation(node, all_conds, truthy_conds, caps_per_branch, i)
click to toggle source
# File lib/js_regex/second_pass.rb, line 66 def build_permutation(node, all_conds, truthy_conds, caps_per_branch, i) truthy = truthy_conds.include?(node.reference) if node.type.equal?(:captured_group) && all_conds.include?(node.reference) adapt_referenced_group_to_permutation(node, truthy) elsif node.type.equal?(:conditional) adapt_conditional_to_permutation(node, truthy) elsif node.type.equal?(:backref_num) adapt_backref_to_permutation(node, caps_per_branch, i) end end
captured_group_count(tree)
click to toggle source
# File lib/js_regex/second_pass.rb, line 54 def captured_group_count(tree) count = 0 crawl(tree) { |node| count += 1 if node.type.equal?(:captured_group) } count end
condition_permutations(conditions)
click to toggle source
# File lib/js_regex/second_pass.rb, line 60 def condition_permutations(conditions) (0..(conditions.length)).inject([]) do |arr, n| arr + conditions.combination(n).to_a end end
conditional_tree_permutations(tree)
click to toggle source
# File lib/js_regex/second_pass.rb, line 25 def conditional_tree_permutations(tree) all_conds = conditions(tree) return [] if all_conds.empty? caps_per_branch = captured_group_count(tree) condition_permutations(all_conds).map.with_index do |truthy_conds, i| tree_permutation = tree.clone # find referenced groups and conditionals and make one-sided crawl(tree_permutation) do |node| build_permutation(node, all_conds, truthy_conds, caps_per_branch, i) end end end
conditions(tree)
click to toggle source
# File lib/js_regex/second_pass.rb, line 46 def conditions(tree) conditions = [] crawl(tree) do |node| conditions << node.reference if node.type.equal?(:conditional) end conditions end
crawl(node) { |node| ... }
click to toggle source
# File lib/js_regex/second_pass.rb, line 40 def crawl(node, &block) return if node.instance_of?(String) yield(node) node.children.each { |child| crawl(child, &block) } end
guarantees_at_least_one_match?(quantifier)
click to toggle source
# File lib/js_regex/second_pass.rb, line 108 def guarantees_at_least_one_match?(quantifier) quantifier.nil? || quantifier.min > 0 end
min_quantify(node)
click to toggle source
# File lib/js_regex/second_pass.rb, line 98 def min_quantify(node) return if guarantees_at_least_one_match?(qtf = node.quantifier) if qtf.max.equal?(1) # any zero_or_one quantifier (?, ??, ?+) node.update(quantifier: nil) else node.update(quantifier: "{1,#{qtf.max}}#{'?' if qtf.reluctant?}") end end
null_quantify(node)
click to toggle source
# File lib/js_regex/second_pass.rb, line 112 def null_quantify(node) node.update(quantifier: '{0}') end