class Xr

Constants

Error
VERSION

Public Class Methods

new(greedy: true) click to toggle source
# File lib/xr.rb, line 9
def initialize(greedy: true)
  @greedy = greedy
end

Public Instance Methods

*(*exprs) click to toggle source
# File lib/xr.rb, line 42
def *(*exprs)
  expr = try_to_s(Xr { exprs })
  Regexp.new(@greedy ? "(#{expr})*" : "(#{expr})*?")
end
+(*exprs) click to toggle source
# File lib/xr.rb, line 47
def +(*exprs)
  expr = try_to_s(Xr { exprs })
  Regexp.new(@greedy ? "(#{expr})+" : "(#{expr})+?")
end
Xr(&block) click to toggle source
# File lib/xr.rb, line 124
def Xr(&block)
  xr = ::Xr.new
  pat = xr.parse(xr.instance_eval(&block))
  pat.respond_to?(:call) ? pat.() : pat
end
_(*exprs) click to toggle source
# File lib/xr.rb, line 15
def _ *exprs
  case exprs
  in [] then Xr.new
  in Integer => num, *exprs
    expr = Xr { exprs }
    Regexp.new("(#{expr}){#{num}}")
  in Range => range, *exprs
    expr = Xr { exprs }
    Regexp.new("(#{expr}){#{range.begin},#{range.end}}")
  end
end
_?(*exprs, &block) click to toggle source
# File lib/xr.rb, line 27
def _? *exprs, &block
  block_given? and return Xr.new(greedy: false).instance_eval(&block)
  case exprs
  in [] then Xr.new(greedy: false)
  else
    expr = try_to_s(Xr { exprs })
    Regexp.new(@greedy ? "(#{expr})?" : "(#{expr})??")
  end
end
any(*sets) click to toggle source
# File lib/xr.rb, line 52
def any(*sets)
  set = sets.map { try_to_s(_1) }.join
  Regexp.new("[#{set}]")
end
backref(name) click to toggle source
# File lib/xr.rb, line 71
def backref(name)
  case name
  in Integer then ->(pre) { Regexp.new("#{pre}\\#{name}") }
  in Symbol | String
    name = name.to_s
    valid_name?(name) or raise Error, "invalid group name"
    ->(pre) { Regexp.new("#{pre}\\k<#{name}>") }
  end
end
except(*sets) click to toggle source
# File lib/xr.rb, line 57
def except(*sets)
  set = sets.map { try_to_s(_1) }.join
  Regexp.new("[^#{set}]")
end
group(name = nil, &block) click to toggle source
# File lib/xr.rb, line 62
def group(name = nil, &block)
  expr = Xr.new.instance_eval(&block)
  if name
    valid_name?(name) or raise Error, "invalid group name"
    Regexp.new("(?<#{name}>#{expr})")
  else Regexp.new("(#{expr})")
  end
end
unmatchable(= UNMATCHABLE) click to toggle source
# File lib/xr.rb, line 13
  def unmatchable = UNMATCHABLE

  def _ *exprs
    case exprs
    in [] then Xr.new
    in Integer => num, *exprs
      expr = Xr { exprs }
      Regexp.new("(#{expr}){#{num}}")
    in Range => range, *exprs
      expr = Xr { exprs }
      Regexp.new("(#{expr}){#{range.begin},#{range.end}}")
    end
  end

  def _? *exprs, &block
    block_given? and return Xr.new(greedy: false).instance_eval(&block)
    case exprs
    in [] then Xr.new(greedy: false)
    else
      expr = try_to_s(Xr { exprs })
      Regexp.new(@greedy ? "(#{expr})?" : "(#{expr})??")
    end
  end

  def | other
    @expr = @expr ? Regexp.union([@expr, other].map { parse(_1) }) : parse(other)
    self
  end

  def *(*exprs)
    expr = try_to_s(Xr { exprs })
    Regexp.new(@greedy ? "(#{expr})*" : "(#{expr})*?")
  end

  def +(*exprs)
    expr = try_to_s(Xr { exprs })
    Regexp.new(@greedy ? "(#{expr})+" : "(#{expr})+?")
  end

  def any(*sets)
    set = sets.map { try_to_s(_1) }.join
    Regexp.new("[#{set}]")
  end

  def except(*sets)
    set = sets.map { try_to_s(_1) }.join
    Regexp.new("[^#{set}]")
  end

  def group(name = nil, &block)
    expr = Xr.new.instance_eval(&block)
    if name
      valid_name?(name) or raise Error, "invalid group name"
      Regexp.new("(?<#{name}>#{expr})")
    else Regexp.new("(#{expr})")
    end
  end

  def backref(name)
    case name
    in Integer then ->(pre) { Regexp.new("#{pre}\\#{name}") }
    in Symbol | String
      name = name.to_s
      valid_name?(name) or raise Error, "invalid group name"
      ->(pre) { Regexp.new("#{pre}\\k<#{name}>") }
    end
  end

  def valid_name?(name) = name.match?(/ \A [a-zA-Z_][a-zA-Z0-9_]* \z /x) # :nodoc:

  def try_to_s(expr) # :nodoc:
    case expr
    in String then Regexp.escape(expr)
    in Regexp then expr.to_s
    in Range  then "#{expr.begin}-#{expr.end}"
    in Proc   then expr
    in Symbol then parse(expr).to_s
    end
  end

  def parse(expr) # :nodoc:
    case expr
    in String         then Regexp.new(Regexp.escape(expr))
    in Regexp | Proc  then expr
    in Xr           then expr.instance_variable_get(:@expr)
    in Array
      pre, *exprs = *expr
      pre = try_to_s(pre).to_s
      exprs.each do |exp|
        if exp.respond_to?(:call)
          pre = exp.(pre).to_s
        else
          pre = "#{pre}#{try_to_s(exp)}"
        end
      end
      Regexp.new(pre)
    in :not_newline   then Regexp.new(".")
    in :anychar       then Regexp.new('[^a-z]')
    in :line_start    then /^/
    in :line_end      then /$/
    in :string_start  then /\A/
    in :string_end    then /\z/
    in :word_boundary then /\b/
    in :alpha | :alnum | :digit | :xdigit | :cntrl | :blank | :space |
       :lower | :upper |
       :graph | :print | :punct | :word | :ascii
      Regexp.new("[[:#{expr}:]]")
    end
  end
end

def Xr(&block)
  xr = ::Xr.new
  pat = xr.parse(xr.instance_eval(&block))
  pat.respond_to?(:call) ? pat.() : pat
|(other) click to toggle source
# File lib/xr.rb, line 37
def | other
  @expr = @expr ? Regexp.union([@expr, other].map { parse(_1) }) : parse(other)
  self
end