class SuperExpressiveRuby

Constants

NamedGroupRegex
QuantifierTable

Attributes

state[RW]

Public Class Methods

as_type(type, opts={}) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 131
def as_type(type, opts={})
  proc { |value| { type: type, value: value }.merge(opts) }
end
assert(condition, message) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 140
def assert(condition, message)
  raise StandardError, message unless condition
end
camelize(snake_case_str) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 174
def camelize(snake_case_str)
  snake_case_str.split('_').each_with_object([]).with_index do |(s, acc), idx|
    acc << if idx.zero?
             s
           else
             s.capitalize
           end
  end.join
end
deferred_type(type, opts={}) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 135
def deferred_type(type, opts={})
  type_fn = as_type(type, opts)
  type_fn.call(type_fn)
end
evaluate(el) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 23
def evaluate(el)
  case el[:type]
  when 'noop'
    ''
  when 'anyChar'
    '.'
  when 'whitespaceChar'
    '\\s'
  when 'nonWhitespaceChar'
    '\\S'
  when 'digit'
    '\\d'
  when 'nonDigit'
    '\\D'
  when 'word'
    '\\w'
  when 'nonWord'
    '\\W'
  when 'wordBoundary'
    '\\b'
  when 'nonWordBoundary'
    '\\B'
  when 'startOfInput'
    '^'
  when 'endOfInput'
    '$'
  when 'newline'
    '\\n'
  when 'carriageReturn'
    '\\r'
  when 'tab'
    '\\t'
  when 'nullByte'
    '\\0'
  when 'string'
    el[:value]
  when 'char'
    el[:value]
  when 'range'
    "[#{el[:value][0]}-#{el[:value][1]}]"
  when 'anythingButRange'
    "[^#{el[:value][0]}-#{el[:value][1]}]"
  when 'anyOfChars'
    "[#{el[:value]}]"
  when 'anythingButChars'
    "[^#{el[:value]}]"
  when 'namedBackreference'
    "\\k<#{el[:name]}>"
  when 'backreference'
    "\\#{el[:index]}"
  when 'subexpression'
    el[:value].map { |value| evaluate(value) }.join('')
  when 'optional',
       'zeroOrMore',
       'zeroOrMoreLazy',
       'oneOrMore',
       'oneOrMoreLazy'
    inner = evaluate(el[:value])
    with_group =
      if el[:value][:quantifierRequiresGroup]
        "(?:#{inner})"
      else
        inner
      end
    symbol = QuantifierTable[el[:type].to_sym]
    "#{with_group}#{symbol}"
  when 'betweenLazy',
  'between',
  'atLeast',
  'exactly'
    inner = evaluate(el[:value])
    withGroup =
      if el[:value][:quantifierRequiresGroup]
        "(?:#{inner})"
      else
        inner
      end
    "#{withGroup}#{QuantifierTable[el[:type].to_sym].call(el[:times])}"
  when 'anythingButString'
    chars = el[:value].split('').map { |c| "[^#{c}]" }.join('')
    "(?:#{chars})"
  when 'assertAhead'
    evaluated = el[:value].map { |v| evaluate(v) }.join('')
    "(?=#{evaluated})"
  when 'assertNotAhead'
    evaluated = el[:value].map { |v| evaluate(v) }.join('')
    "(?!#{evaluated})"
  when 'anyOf'
    fused, rest = fuse_elements(el[:value])
    return "[#{fused}]" unless rest.length

    evaluatedRest = rest.map { |v| evaluate(v) }
    separator = evaluatedRest.length > 0 && fused.length > 0 ? '|' : ''
    "(?:#{evaluatedRest.join('|')}#{separator}#{fused ? "[#{fused}]" : ''})"
  when 'capture'
    evaluated = el[:value].map { |v| evaluate(v) }
    "(#{evaluated.join('')})"
  when 'namedCapture'
    evaluated = el[:value].map { |v| evaluate(v) }
    "(?<#{el[:name]}>#{evaluated.join('')})"
  when 'group'
    evaluated = el[:value].map { |v| evaluate(v) }
    "(?:#{evaluated.join('')})"
  else
    raise "Can't process unsupported element type: #{el[:type]}"
  end
end
fuse_elements(elements) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 162
def fuse_elements(elements)
  fusables, rest = partition(elements)
  fused = fusables.map do |el|
    if %w[char anyOfChars].include?(el[:type])
      el[:value]
    else
      "#{el[:value][0]}-#{el[:value][1]}"
    end
  end.join('')
  [fused, rest]
end
is_fusable(element) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 156
def is_fusable(element)
  element[:type] == 'range' ||
    element[:type] == 'char' ||
    element[:type] == 'anyOfChars'
end
new() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 230
def initialize
  self.state = {
    hasDefinedStart: false,
    hasDefinedEnd: false,
    flags: {
      g: false,
      y: false,
      m: false,
      i: false,
      u: false,
      s: false
    },
    stack: [create_stack_frame(t[:root])],
    namedGroups: [],
    totalCaptureGroups: 0
  }
end
partition(a) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 144
def partition(a)
  r = a.each_with_object([[], []]) do |cur, acc|
    if is_fusable(cur)
      acc[0].push(cur)
    else
      acc[1].push(cur)
    end
    acc
  end
  [r[0], r[1]]
end

Public Instance Methods

allow_multiple_matches() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 260
def allow_multiple_matches
  # warn("Warning: Ruby does not have a allow multiple matches option. use String#gsub or String#scan")
  n = clone
  n.state[:flags][:g] = true
  n
end
any_char() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 305
def any_char
  match_element(t[:anyChar])
end
any_of() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 376
def any_of
  frame_creating_element(t[:anyOf])
end
any_of_chars(s) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 524
def any_of_chars(s)
  n = clone
  element_value = t[:anyOfChars].call(escape_special(s))
  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(element_value))
  n
end
anything_but_chars(chars) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 553
def anything_but_chars(chars)
  assert(chars.is_a?(String), "chars must be a string (got #{chars})")
  assert(chars.length > 0, 'chars must have at least one character')

  n = clone
  element_value = t[:anythingButChars].call(escape_special(chars))
  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(element_value))
  n
end
anything_but_range(a, b) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 564
def anything_but_range(a, b)
  str_a = a.to_s
  str_b = b.to_s

  assert(str_a.length === 1, "a must be a single character or number (got #{str_a})")
  assert(str_b.length === 1, "b must be a single character or number (got #{str_b})")
  assert(str_a[0].ord < str_b[0].ord, "a must have a smaller character value than b (a = #{str_a[0].ord}, b = #{str_b[0].ord})")

  n = clone
  element_value = t[:anythingButRange].call([a, b])
  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(element_value))
  n
end
anything_but_string(str) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 542
def anything_but_string(str)
  assert(str.is_a?(String), "str must be a string (got #{str})")
  assert(str.length > 0, 'str must have least one character')

  n = clone
  element_value - t[:anythingButString].call(escape_special(str))
  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(element_value))
  n
end
apply_quantifier(element) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 783
def apply_quantifier(element)
  current_frame = get_current_frame
  if current_frame[:quantifier]
    wrapped = current_frame[:quantifier][:value].call(element)
    current_frame[:quantifier] = nil
    return wrapped
  end
  element
end
apply_subexpression_defaults(expr) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 700
def apply_subexpression_defaults(expr)
  out = {}.merge(expr)

  out[:namespace] = out.has_key?(:namespace) ? out[:namespace] : ''
  out[:ignoreFlags] = out.has_key?(:ignoreFlags) ? out[:ignoreFlags] : true
  out[:ignoreStartAndEnd] = out.has_key?(:ignoreStartAndEnd) ? out[:ignoreStartAndEnd] : true
  assert(out[:namespace].is_a?(String), 'namespace must be a string')
  assert(out[:ignoreFlags].is_a?(TrueClass) || out[:ignoreFlags].is_a?(FalseClass), 'ignoreFlags must be a boolean')
  assert(out[:ignoreStartAndEnd].is_a?(TrueClass) || out[:ignoreStartAndEnd].is_a?(FalseClass), 'ignoreStartAndEnd must be a boolean')

  out
end
assert(condition, message) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 801
def assert(condition, message)
  self.class.assert(condition, message)
  raise StandardError, message unless condition
end
assert_ahead() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 384
def assert_ahead
  frame_creating_element(t[:assertAhead])
end
assert_not_ahead() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 388
def assert_not_ahead
  frame_creating_element(t[:assertNotAhead])
end
at_least(n) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 463
def at_least(n)
  assert(n.is_a?(Integer) && n > 0, "n must be a positive integer (got #{n})")
  nxt = clone
  current_frame = nxt.get_current_frame
  if current_frame[:quantifier]
    raise StandardError, "cannot quantify regular expression with 'atLeast' because it's already being quantified with '#{currentFrame.quantifier.type}'"
  end

  current_frame[:quantifier] = t[:atLeast].call(n)
  nxt
end
backreference(index) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 362
def backreference(index)
  assert(index.is_a?(Integer), 'index must be a number')
  assert(index > 0 && index <= state[:totalCaptureGroups],
         "invalid index #{index}. There are #{state[:totalCaptureGroups]} capture groups on this SuperExpression")
  match_element(t[:backreference].call(index))
end
between(x, y) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 475
def between(x, y)
  assert(x.is_a?(Integer) && x >= 0, "x must be an integer (got #{x})")
  assert(y.is_a?(Integer) && y > 0, "y must be an integer greater than 0 (got #{y})")
  assert(x < y, "x must be less than y (x = #{x}, y = #{y})")

  nxt = clone
  current_frame = nxt.get_current_frame
  if current_frame[:quantifier]
    raise StandardError, "cannot quantify regular expression with 'between' because it's already being quantified with '#{currentFrame.quantifier.type}'"
  end

  current_frame[:quantifier] = t[:between].call(x, y)
  nxt
end
between_lazy(x, y) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 490
def between_lazy(x, y)
  assert(x.is_a?(Integer) && x >= 0, "x must be an integer (got #{x})")
  assert(y.is_a?(Integer) && y > 0, "y must be an integer greater than 0 (got #{y})")
  assert(x < y, "x must be less than y (x = #{x}, y = #{y})")

  n = clone
  current_frame = n.get_current_frame
  if current_frame[:quantifier]
    raise StandardError, "cannot quantify regular expression with 'betweenLazy' because it's already being quantified with '#{current_frame[:quantifier][:type]}'"
  end

  current_frame[:quantifier] = t[:betweenLazy].call(x, y)
  n
end
capture() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 392
def capture
  n = clone
  new_frame = create_stack_frame(t[:capture])
  n.state[:stack].push(new_frame)
  n.state[:totalCaptureGroups] += 1
  n
end
carriage_return() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 345
def carriage_return
  match_element(t[:carriageReturn])
end
case_insensitive() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 274
def case_insensitive
  n = clone
  n.state[:flags][:i] = true
  n
end
char(c) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 596
def char(c)
  assert(c.is_a?(String), "c must be a string (got #{c})")
  assert(c.length == 1, "char() can only be called with a single character (got #{c})")

  n = clone
  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(t[:char].call(escape_special(c))))
  n
end
create_stack_frame(type) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 256
def create_stack_frame(type)
  { type: type, quantifier: nil, elements: [] }
end
digit() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 317
def digit
  match_element(t[:digit])
end
end() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 532
def end
  assert(state[:stack].length > 1, 'Cannot call end while building the root expression.')

  n = clone
  old_frame = n.state[:stack].pop
  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(old_frame[:type][:value].call(old_frame[:elements])))
  n
end
end_of_input() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 515
def end_of_input
  assert(!state[:hasDefinedEnd], 'This regex already has a defined end of input')

  n = clone
  n.state[:hasDefinedEnd] = true
  n.get_current_element_array.push(t[:endOfInput])
  n
end
escape_special(s) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 252
def escape_special(s)
  Regexp.escape(s)
end
exactly(n) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 450
def exactly(n)
  assert(n.is_a?(Integer) && n > 0, "n must be a positive integer (got #{n})")

  nxt = clone
  current_frame = nxt.get_current_frame
  if current_frame[:quantifier]
    raise StandardError, "cannot quantify regular expression with 'exactly' because it's already being quantified with '#{current_frame[:quantifier][:type]}'"
  end

  current_frame[:quantifier] = t[:exactly].call(n)
  nxt
end
frame_creating_element(type_fn) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 369
def frame_creating_element(type_fn)
  n = clone
  new_frame = create_stack_frame(type_fn)
  n.state[:stack].push(new_frame)
  n
end
get_current_element_array() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 797
def get_current_element_array
  get_current_frame[:elements]
end
get_current_frame() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 793
def get_current_frame
  state[:stack][state[:stack].length - 1]
end
get_regex_pattern_and_flags() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 760
def get_regex_pattern_and_flags
  assert state[:stack].length === 1,
         "Cannot compute the value of a not yet fully specified regex object.
           \n(Try adding a .end() call to match the '#{get_current_frame[:type][:type]}')\n"
  pattern = get_current_element_array.map { |el| self.class.evaluate(el) }.join('')
  flag = nil
  state[:flags].map do |name, is_on|
    if is_on
      flag = 0 if !flag
      case name
      when :s
        flag = flag | Regexp::MULTILINE
      when :i
        flag = flag | Regexp::IGNORECASE
      when :x
        flag = flag | Regexp::EXTENDED
      end
    end
  end
  pat = (pattern == '' ? '(?:)' : pattern)
  [pat, flag]
end
group() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 380
def group
  frame_creating_element(t[:group])
end
line_by_line() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 267
def line_by_line
  # warn("Warning: Ruby does not have a line by line option. use \A or \z as an alternative")
  n = clone
  n.state[:flags][:m] = true
  n
end
match_element(type_fn) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 299
def match_element(type_fn)
  n = clone
  n.get_current_element_array.push(n.apply_quantifier(type_fn))
  n
end
merge_subexpression(el, options, parent, increment_capture_groups) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 622
def merge_subexpression(el, options, parent, increment_capture_groups)
  next_el = el.clone
  next_el[:index] += parent.state[:totalCaptureGroups] if next_el[:type] == 'backreference'

  increment_capture_groups.call if next_el[:type] == 'capture'

  if next_el[:type] === 'namedCapture'
    group_name =
      if options[:namespace]
        "#{options[:namespace]}#{next_el[:name]}"
      else
        next_el[:name]
      end

    parent.track_named_group(group_name)
    next_el[:name] = group_name
  end

  if next_el[:type] == 'namedBackreference'
    next_el[:name] =
      if options[:namespace]
        "#{options[:namespace]}#{next_el[:name]}"
      else
        next_el[:name]
      end
  end

  if next_el[:containsChild]
    next_el[:value] = merge_subexpression(
      next_el[:value],
      options,
      parent,
      increment_capture_groups
    )
  elsif next_el[:containsChildren]
    next_el[:value] = next_el[:value].map do |e|
      merge_subexpression(
        e,
        options,
        parent,
        increment_capture_groups
      )
    end
  end

  if next_el[:type] == 'startOfInput'

    return @@t[:noop] if options[:ignoreStartAndEnd]

    assert(
      !parent.state[:hasDefinedStart],
      'The parent regex already has a defined start of input. ' +
      'You can ignore a subexpressions startOfInput/endOfInput markers with the ignoreStartAndEnd option'
    )

    assert(
      !parent.state[:hasDefinedEnd],
      'The parent regex already has a defined end of input. ' +
        'You can ignore a subexpressions startOfInput/endOfInput markers with the ignoreStartAndEnd option'
    )

    parent.state[:hasDefinedStart] = true
  end

  if next_el[:type] == 'endOfInput'
    return @@t[:noop] if options[:ignoreStartAndEnd]

    assert(
      !parent.state[:hasDefinedEnd],
      'The parent regex already has a defined start of input. ' +
      'You can ignore a subexpressions startOfInput/endOfInput markers with the ignoreStartAndEnd option'
    )

    parent.state[:hasDefinedEnd] = true
  end
  next_el
end
named_backreference(name) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 357
def named_backreference(name)
  assert(state[:namedGroups].include?(name), "no capture group called '#{name}' exists (create one with .namedCapture())")
  match_element(t[:namedBackreference].call(name))
end
named_capture(name) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 409
def named_capture(name)
  n = clone
  new_frame = create_stack_frame(t[:namedCapture].call(name))

  n.track_named_group(name)
  n.state[:stack].push(new_frame)
  n.state[:totalCaptureGroups] += 1
  n
end
newline() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 341
def newline
  match_element(t[:newline])
end
non_digit() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 321
def non_digit
  match_element(t[:nonDigit])
end
non_whitespace_char() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 313
def non_whitespace_char
  match_element(t[:nonWhitespaceChar])
end
non_word() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 329
def non_word
  match_element(t[:nonWord])
end
non_word_boundary() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 337
def non_word_boundary
  match_element(t[:nonWordBoundary])
end
null_byte() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 353
def null_byte
  match_element(t[:nullByte])
end
one_or_more() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 442
def one_or_more
  quantifier_element('oneOrMore')
end
one_or_more_lazy() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 446
def one_or_more_lazy
  quantifier_element('oneOrMoreLazy')
end
optional() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 430
def optional
  quantifier_element('optional')
end
quantifier_element(type_fn_name) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 419
def quantifier_element(type_fn_name)
  n = clone
  current_frame = n.get_current_frame
  if current_frame[:quantifier]
    raise StandardError, "cannot quantify regular expression with '#{type_fn_name}' because it's already being quantified with '#{current_frame[:quantifier][:type]}'"
  end

  current_frame[:quantifier] = t[type_fn_name.to_sym]
  n
end
range(a, b) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 606
def range(a, b)
  str_a = a.to_s
  str_b = b.to_s

  assert(str_a.length == 1, "a must be a single character or number (got #{str_a})")
  assert(str_b.length == 1, "b must be a single character or number (got #{str_b})")
  assert(str_a[0].ord < str_b[0].ord, "a must have a smaller character value than b (a = #{str_a[0].ord}, b = #{str_b[0].ord})")

  n = clone
  element_value = t[:range].call([str_a, str_b])
  current_frame = n.get_current_frame

  current_frame[:elements].push(n.apply_quantifier(element_value))
  n
end
single_line() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 293
def single_line
  n = clone
  n.state[:flags][:s] = true
  n
end
start_of_input() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 505
def start_of_input
  assert(!state[:hasDefinedStart], 'This regex already has a defined start of input')
  assert(!state[:hasDefinedEnd], 'Cannot define the start of input after the end of input')

  n = clone
  n.state[:hasDefinedStart] = true
  n.get_current_element_array.push(t[:startOfInput])
  n
end
sticky() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 280
def sticky
  # warn("Warning: Ruby does not have a sticky option")
  n = clone
  n.state[:flags][:y] = true
  n
end
string(str) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 579
def string(str)
  assert('' != str, 'str cannot be an empty string')
  n = clone

  element_value =
    if str.length > 1
      t[:string].call(escape_special(str))
    else
      t[:char].call(str)
    end

  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(element_value))

  n
end
subexpression(expr, opts = {}) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 713
def subexpression(expr, opts = {})
  assert(expr.is_a?(SuperExpressiveRuby), 'expr must be a SuperExpressive instance')
  assert(
    expr.state[:stack].length === 1,
    'Cannot call subexpression with a not yet fully specified regex object.' +
    "\n(Try adding a .end() call to match the '#{expr.get_current_frame[:type][:type]}' on the subexpression)\n"
  )

  options = apply_subexpression_defaults(opts)

  expr_n = expr.clone
  expr_n.state = expr.state.deep_dup
  n = clone
  additional_capture_groups = 0

  expr_frame = expr_n.get_current_frame
  closure = proc { additional_capture_groups += 1 }

  expr_frame[:elements] = expr_frame[:elements].map do |e|
    merge_subexpression(e, options, n, closure)
  end

  n.state[:totalCaptureGroups] += additional_capture_groups

  unless options[:ignoreFlags]
    expr_n.state[:flags].to_a.each do |e|
      flag_name = e[0]
      enabled = e[1]
      n.state[:flags][flag_name] = enabled || n.state[:flags][flag_name]
    end
  end

  current_frame = n.get_current_frame
  current_frame[:elements].push(n.apply_quantifier(t[:subexpression].call(expr_frame[:elements])))
  n
end
t() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 248
def t
  @@t
end
tab() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 349
def tab
  match_element(t[:tab])
end
to_regex() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 755
def to_regex
  pattern, flags = get_regex_pattern_and_flags
  Regexp.new(pattern, flags)
end
to_regex_string() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 750
def to_regex_string
  pattern, flags = get_regex_pattern_and_flags
  Regexp.new(pattern, flags).to_s
end
track_named_group(name) click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 400
def track_named_group(name)
  assert(name.is_a?(String), "name must be a string (got #{name})")
  assert(name.length > 0, 'name must be at least one character')
  assert(!state[:namedGroups].include?(name), "cannot use #{name} again for a capture group")
  assert(name.scan(NamedGroupRegex).any?, "name '#{name}' is not valid (only letters, numbers, and underscores)")

  state[:namedGroups].push name
end
unicode() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 287
def unicode
  n = clone
  n.state[:flags][:u] = true
  n
end
whitespace_char() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 309
def whitespace_char
  match_element(t[:whitespaceChar])
end
word() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 325
def word
  match_element(t[:word])
end
word_boundary() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 333
def word_boundary
  match_element(t[:wordBoundary])
end
zero_or_more() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 434
def zero_or_more
  quantifier_element('zeroOrMore')
end
zero_or_more_lazy() click to toggle source
# File lib/super-expressive-ruby/super-expressive-ruby.rb, line 438
def zero_or_more_lazy
  quantifier_element('zeroOrMoreLazy')
end