mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 03:26:00 -07:00
157 lines
3.6 KiB
Ruby
157 lines
3.6 KiB
Ruby
module Rubinius
|
|
class Splitter
|
|
def self.split_characters(string, pattern, limit, tail_empty)
|
|
if limit
|
|
string.chars.take(limit - 1) << string[(limit - 1)..-1]
|
|
else
|
|
ret = string.chars.to_a
|
|
# Use #byteslice because it returns the right class and taints
|
|
# automatically. This is just appending a "", which is this
|
|
# strange protocol if a negative limit is passed in
|
|
ret << string.byteslice(0,0) if tail_empty
|
|
ret
|
|
end
|
|
end
|
|
|
|
def self.valid_encoding?(string)
|
|
raise ArgumentError, "invalid byte sequence in #{string.encoding.name}" unless string.valid_encoding?
|
|
end
|
|
|
|
def self.split(string, pattern, limit)
|
|
# Odd edge case
|
|
return [] if string.empty?
|
|
|
|
tail_empty = false
|
|
|
|
if undefined.equal?(limit)
|
|
limited = false
|
|
else
|
|
limit = Rubinius::Type.coerce_to limit, Fixnum, :to_int
|
|
|
|
if limit > 0
|
|
return [string.dup] if limit == 1
|
|
limited = true
|
|
else
|
|
if limit < 0
|
|
tail_empty = true
|
|
end
|
|
limited = false
|
|
end
|
|
end
|
|
|
|
pattern ||= ($; || " ")
|
|
|
|
if pattern == ' '
|
|
if limited
|
|
lim = limit
|
|
elsif tail_empty
|
|
lim = -1
|
|
else
|
|
lim = 0
|
|
end
|
|
|
|
return Rubinius.invoke_primitive :string_awk_split, string, lim
|
|
elsif pattern.kind_of?(Regexp)
|
|
else
|
|
pattern = StringValue(pattern) unless pattern.kind_of?(String)
|
|
|
|
valid_encoding?(string)
|
|
valid_encoding?(pattern)
|
|
|
|
trim_end = !tail_empty || limit == 0
|
|
|
|
unless limited
|
|
if pattern.empty?
|
|
if trim_end
|
|
return string.chars.to_a
|
|
end
|
|
else
|
|
return split_on_string(string, pattern, trim_end)
|
|
end
|
|
end
|
|
|
|
pattern = Regexp.new(Regexp.quote(pattern))
|
|
end
|
|
|
|
# Handle // as a special case.
|
|
if pattern.source.empty?
|
|
return split_characters(string, pattern, limited && limit, tail_empty)
|
|
end
|
|
|
|
start = 0
|
|
ret = []
|
|
|
|
last_match = nil
|
|
last_match_end = 0
|
|
|
|
while match = pattern.match_from(string, start)
|
|
break if limited && limit - ret.size <= 1
|
|
|
|
collapsed = match.collapsing?
|
|
|
|
unless collapsed && (match.full.at(0) == last_match_end)
|
|
ret << match.pre_match_from(last_match_end)
|
|
|
|
# length > 1 means there are captures
|
|
if match.length > 1
|
|
ret.concat(match.captures.compact)
|
|
end
|
|
end
|
|
|
|
start = match.full.at(1)
|
|
if collapsed
|
|
start += 1
|
|
end
|
|
|
|
last_match = match
|
|
last_match_end = last_match.full.at(1)
|
|
end
|
|
|
|
if last_match
|
|
ret << last_match.post_match
|
|
elsif ret.empty?
|
|
ret << string.dup
|
|
end
|
|
|
|
# Trim from end
|
|
if undefined.equal?(limit) || limit == 0
|
|
while s = ret.at(-1) and s.empty?
|
|
ret.pop
|
|
end
|
|
end
|
|
|
|
ret
|
|
end
|
|
|
|
def self.split_on_string(string, pattern, trim_end)
|
|
pos = 0
|
|
|
|
ret = []
|
|
|
|
pat_size = pattern.bytesize
|
|
str_size = string.bytesize
|
|
m = Rubinius::Mirror.reflect string
|
|
|
|
while pos < str_size
|
|
nxt = m.find_string(pattern, pos)
|
|
break unless nxt
|
|
|
|
match_size = nxt - pos
|
|
ret << string.byteslice(pos, match_size)
|
|
|
|
pos = nxt + pat_size
|
|
end
|
|
|
|
# No more separators, but we need to grab the last part still.
|
|
ret << string.byteslice(pos, str_size - pos)
|
|
|
|
if trim_end
|
|
while s = ret.at(-1) and s.empty?
|
|
ret.pop
|
|
end
|
|
end
|
|
|
|
ret
|
|
end
|
|
end
|
|
end
|