Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions spec/std/json/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ require "spec"
require "json"

private def it_parses(string, expected_value, file = __FILE__, line = __LINE__)
it "parses #{string} from String", file, line do
it "parses #{string}", file, line do
JSON.parse(string).raw.should eq(expected_value)
end

it "parses #{string} from IO", file, line do
JSON.parse(IO::Memory.new(string)).raw.should eq(expected_value)
end
end

private def it_raises_on_parse(string, file = __FILE__, line = __LINE__)
Expand All @@ -35,7 +31,6 @@ describe JSON::Parser do
it_parses "[true]", [true]
it_parses "[false]", [false]
it_parses %(["hello"]), ["hello"]
it_parses %(["hello", 1]), ["hello", 1]
it_parses "[0]", [0]
it_parses " [ 0 ] ", [0]

Expand Down
4 changes: 2 additions & 2 deletions spec/std/json/pull_parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ class JSON::PullParser
end
end

private def assert_pull_parse(string, file = __FILE__, line = __LINE__)
it "parses #{string}", file, line do
private def assert_pull_parse(string)
it "parses #{string}" do
parser = JSON::PullParser.new string
parser.assert JSON.parse(string).raw
parser.kind.should eq(JSON::PullParser::Kind::EOF)
Expand Down
22 changes: 1 addition & 21 deletions src/json/lexer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,8 @@ abstract class JSON::Lexer
private def consume_number
number_start

# Integer values of up to 18 digits can be computed by doing math:
# no need to store a string value and later parse it.
# For larger numbers, or floats, we store the entire string and later parse it.
@token.int_value = nil
integer = 0_i64
negative = false

if current_char == '-'
append_number_char
negative = true
next_char
end

Expand All @@ -246,19 +238,13 @@ abstract class JSON::Lexer
unexpected_char
else
@token.kind = :int
@token.int_value = 0
number_end
end
when '1'..'9'
append_number_char
digits = 1
integer = (current_char - '0').to_i64
char = next_char
while '0' <= char <= '9'
append_number_char
digits += 1
integer &*= 10
integer &+= char - '0'
char = next_char
end

Expand All @@ -269,13 +255,7 @@ abstract class JSON::Lexer
consume_exponent
else
@token.kind = :int
# Int64::MAX is 9223372036854775807 which has 19 digits.
# With 18 digits we know the number we computed is the one we read.
if digits > 18
number_end
else
@token.int_value = negative ? -integer : integer
end
number_end
end
else
unexpected_char
Expand Down
53 changes: 3 additions & 50 deletions src/json/lexer/io_based.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,17 @@
class JSON::Lexer::IOBased < JSON::Lexer
def initialize(@io : IO)
super()
@current_char = @io.read_byte.try(&.chr) || '\0'
@current_char = @io.read_char || '\0'
end

private getter current_char

private def next_char_no_column_increment
@current_char = @io.read_byte.try(&.chr) || '\0'
@current_char = @io.read_char || '\0'
end

private def consume_string
peek = @io.peek
if !peek || peek.empty?
return consume_string_with_buffer
end

pos = 0

while true
if pos >= peek.size
# We don't have enough data in the peek buffer to create a string:
# default to the slow method
return consume_string_with_buffer
end

char = peek[pos]
case char
when '\\'
# If we find an escape character, go to the slow method
@column_number += pos
return consume_string_at_escape_char(peek, pos)
when '"'
break
else
if 0 <= current_char.ord < 32
unexpected_char
else
pos += 1
end
end
end

@column_number += pos
@token.string_value =
if @expects_object_key
@string_pool.get(peek.to_unsafe, pos)
else
String.new(peek.to_unsafe, pos)
end

@io.skip(pos + 1)
next_char
end

private def consume_string_at_escape_char(peek, pos)
consume_string_with_buffer do
@buffer.write peek[0, pos]
@io.skip(pos)
end
consume_string_with_buffer
end

private def number_start
Expand Down
22 changes: 9 additions & 13 deletions src/json/lexer/string_based.cr
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# :nodoc:
class JSON::Lexer::StringBased < JSON::Lexer
def initialize(string : String)
def initialize(string)
super()
@string = string
@pos = 0
@reader = Char::Reader.new(string)
@number_start = 0
end

Expand Down Expand Up @@ -34,7 +33,7 @@ class JSON::Lexer::StringBased < JSON::Lexer
if @expects_object_key
start_pos += 1
end_pos = current_pos - 1
@token.string_value = @string_pool.get(@string.to_unsafe + start_pos, end_pos - start_pos)
@token.string_value = @string_pool.get(@reader.string.to_unsafe + start_pos, end_pos - start_pos)
else
@token.string_value = string_range(start_pos + 1, current_pos - 1)
end
Expand All @@ -48,30 +47,27 @@ class JSON::Lexer::StringBased < JSON::Lexer
end

private def current_pos
@pos
@reader.pos
end

def string_range(start_pos, end_pos) : String
@string.byte_slice(start_pos, end_pos - start_pos)
@reader.string.byte_slice(start_pos, end_pos - start_pos)
end

def slice_range(start_pos, end_pos) : Bytes
@string.to_slice[start_pos, end_pos - start_pos]
@reader.string.to_slice[start_pos, end_pos - start_pos]
end

private def next_char_no_column_increment
@pos += 1

char = current_char
if char == '\0' && @pos != @string.bytesize
char = @reader.next_char
if char == '\0' && @reader.pos != @reader.string.bytesize
unexpected_char
end

char
end

private def current_char
@string.to_unsafe[@pos].chr
@reader.current_char
end

private def number_start
Expand Down
21 changes: 3 additions & 18 deletions src/json/token.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class JSON::Token
property string_value : String

def int_value : Int64
@int_value || raw_value.to_i64
raw_value.to_i64
rescue exc : ArgumentError
raise ParseException.new(exc.message, line_number, column_number)
end
Expand All @@ -32,25 +32,14 @@ class JSON::Token

property line_number : Int32
property column_number : Int32
setter raw_value : String
setter int_value : Int64?
property raw_value : String

def initialize
@kind = :EOF
@line_number = 0
@column_number = 0
@string_value = ""
@raw_value = ""
@int_value = nil
end

def raw_value
case @kind
when .int?
@int_value.try(&.to_s) || @raw_value
else
@raw_value
end
end

def to_s(io : IO) : Nil
Expand All @@ -62,11 +51,7 @@ class JSON::Token
when .true?
io << "true"
when .int?
if int_value = @int_value
int_value.to_s(io)
else
raw_value.to_s(io)
end
raw_value.to_s(io)
when .float?
raw_value.to_s(io)
when .string?
Expand Down