class Opal::Nodes::RegexpNode

def compile

def compile
  flags.select! do |flag|
    if SUPPORTED_FLAGS =~ flag
      true
    else
      compiler.warning "Skipping the '#{flag}' Regexp flag as it's not widely supported by JavaScript vendors.", @sexp.line
      false
    end
  end
  if value.type == :str
    compile_static_regexp
  else
    compile_dynamic_regexp
  end
end

def compile_dynamic_regexp

def compile_dynamic_regexp
  helper :regexp
  push '$regexp(['
  value.children.each_with_index do |v, index|
    push ', ' unless index.zero?
    push expr(v)
  end
  push ']'
  push ", '#{flags.join}'" if flags.any?
  push ")"
end

def compile_static_regexp

def compile_static_regexp
  value = self.value.children[0]
  case value
  when ''
    push('/(?:)/')
  when /\(\?[(<>#]|[*+?]\+/
    # Safari/WebKit will not execute javascript code if it contains a lookbehind literal RegExp
    # and they fail with "Syntax Error". This tricks their parser by disguising the literal RegExp
    # as string for the dynamic $regexp helper. Safari/Webkit will still fail to execute the RegExp,
    # but at least they will parse and run everything else.
    #
    # Also, let's compile a couple of more patterns into $regexp calls, as there are many syntax
    # errors in RubySpec when ran in reverse, while there shouldn't be (they should be catchable
    # errors) - at least since Node 17.
    static_as_dynamic(value)
  else
    push "#{Regexp.new(value).inspect}#{flags.join}"
  end
end

def extract_flags_and_value

def extract_flags_and_value
  *values, flags_sexp = *children
  self.flags = flags_sexp.children.map(&:to_s)
  self.value = if values.empty?
                 # empty regexp, we can process it inline
                 s(:str, '')
               elsif single_line?(values)
                 # simple plain regexp, we can put it inline
                 values[0]
               else
                 s(:dstr, *values)
               end
  # trimming when //x provided
  # required by parser gem, but JS doesn't support 'x' flag
  if flags.include?('x')
    parts = value.children.map do |part|
      if part.is_a?(::Opal::AST::Node) && part.type == :str
        trimmed_value = part.children[0].gsub(/\A\s*\#.*/, '').gsub(/\s/, '')
        s(:str, trimmed_value)
      else
        part
      end
    end
    self.value = value.updated(nil, parts)
    flags.delete('x')
  end
  if value.type == :str
    # Replacing \A -> ^, \z -> $, required for the parser gem
    self.value = s(:str, value.children[0].gsub('\A', '^').gsub('\z', '$'))
  end
end

def initialize(*)

def initialize(*)
  super
  extract_flags_and_value
end

def raw_value

def raw_value
  self.value = @sexp.loc.expression.source
end

def single_line?(values)

def single_line?(values)
  return false if values.length > 1
  value = values[0]
  # JavaScript doesn't support multiline regexp
  value.type != :str || !value.children[0].include?("\n")
end

def static_as_dynamic(value)

def static_as_dynamic(value)
  helper :regexp
  push '$regexp(["'
  push value.gsub('\\', '\\\\\\\\')
  push '"]'
  push ", '#{flags.join}'" if flags.any?
  push ")"
end