mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
replace INTEGER_REGEX/FLOAT_REGEX with byte-level parse_number
This commit is contained in:
+58
-45
@@ -78,61 +78,74 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def parse_number(markup, ss)
|
||||
def parse_number(markup, _ss = nil)
|
||||
len = markup.bytesize
|
||||
return false if len == 0
|
||||
|
||||
# Quick reject: first byte must be digit or dash
|
||||
first = markup.getbyte(0)
|
||||
return false if first != DASH && (first < ZERO || first > NINE)
|
||||
pos = 0
|
||||
first = markup.getbyte(pos)
|
||||
if first == DASH
|
||||
pos += 1
|
||||
return false if pos >= len
|
||||
b = markup.getbyte(pos)
|
||||
return false if b < ZERO || b > NINE
|
||||
pos += 1
|
||||
elsif first >= ZERO && first <= NINE
|
||||
pos += 1
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
# check if the markup is simple integer or float
|
||||
case markup
|
||||
when INTEGER_REGEX
|
||||
# Scan digits
|
||||
while pos < len
|
||||
b = markup.getbyte(pos)
|
||||
break unless b >= ZERO && b <= NINE
|
||||
pos += 1
|
||||
end
|
||||
|
||||
# If we consumed everything, it's a simple integer
|
||||
if pos == len
|
||||
return Integer(markup, 10)
|
||||
when FLOAT_REGEX
|
||||
end
|
||||
|
||||
# Check for dot (float)
|
||||
if markup.getbyte(pos) == DOT
|
||||
dot_pos = pos
|
||||
pos += 1
|
||||
# Must have at least one digit after dot
|
||||
digit_after_dot = pos
|
||||
while pos < len
|
||||
b = markup.getbyte(pos)
|
||||
break unless b >= ZERO && b <= NINE
|
||||
pos += 1
|
||||
end
|
||||
|
||||
if pos > digit_after_dot && pos == len
|
||||
# Simple float like "123.456"
|
||||
return markup.to_f
|
||||
elsif pos > digit_after_dot
|
||||
# Float followed by more dots or other chars: "1.2.3.4"
|
||||
# Return the float portion up to second dot
|
||||
first_dot_pos = dot_pos + 1
|
||||
while pos < len
|
||||
b = markup.getbyte(pos)
|
||||
if b == DOT
|
||||
return markup.byteslice(0, pos).to_f
|
||||
elsif b < ZERO || b > NINE
|
||||
return false
|
||||
end
|
||||
|
||||
ss.string = markup
|
||||
# the first byte must be a digit or a dash
|
||||
byte = ss.scan_byte
|
||||
|
||||
return false if byte != DASH && (byte < ZERO || byte > NINE)
|
||||
|
||||
if byte == DASH
|
||||
peek_byte = ss.peek_byte
|
||||
|
||||
# if it starts with a dash, the next byte must be a digit
|
||||
return false if peek_byte.nil? || !(peek_byte >= ZERO && peek_byte <= NINE)
|
||||
pos += 1
|
||||
end
|
||||
|
||||
# The markup could be a float with multiple dots
|
||||
first_dot_pos = nil
|
||||
num_end_pos = nil
|
||||
|
||||
while (byte = ss.scan_byte)
|
||||
return false if byte != DOT && (byte < ZERO || byte > NINE)
|
||||
|
||||
# we found our number and now we are just scanning the rest of the string
|
||||
next if num_end_pos
|
||||
|
||||
if byte == DOT
|
||||
if first_dot_pos.nil?
|
||||
first_dot_pos = ss.pos
|
||||
return markup.byteslice(0, pos).to_f
|
||||
else
|
||||
# we found another dot, so we know that the number ends here
|
||||
num_end_pos = ss.pos - 1
|
||||
end
|
||||
# dot at end: "123."
|
||||
return markup.byteslice(0, dot_pos).to_f
|
||||
end
|
||||
end
|
||||
|
||||
num_end_pos = markup.length if ss.eos?
|
||||
|
||||
if num_end_pos
|
||||
# number ends with a number "123.123"
|
||||
markup.byteslice(0, num_end_pos).to_f
|
||||
else
|
||||
# number ends with a dot "123."
|
||||
markup.byteslice(0, first_dot_pos).to_f
|
||||
end
|
||||
# Not a number (has non-digit, non-dot characters)
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user