Simples fixes to the parser, add is_valid_start_symbol and updated parse_json.

This commit is contained in:
jvelilla
2009-08-14 11:19:54 +00:00
parent aed8d1a516
commit 7a31e80f90

View File

@@ -1,434 +1,446 @@
indexing indexing
description: "Parse serialized JSON data" description: "Parse serialized JSON data"
author: "jvelilla" author: "jvelilla"
date: "2008/08/24" date: "2008/08/24"
revision: "Revision 0.1" revision: "Revision 0.1"
class class
JSON_PARSER JSON_PARSER
inherit inherit
JSON_READER JSON_READER
JSON_TOKENS JSON_TOKENS
create create
make_parser make_parser
feature {NONE} -- Initialize feature {NONE} -- Initialize
make_parser (a_json: STRING) is make_parser (a_json: STRING) is
-- Initialize. -- Initialize.
require require
json_not_empty: a_json /= Void and then not a_json.is_empty json_not_empty: a_json /= Void and then not a_json.is_empty
do do
make (a_json) make (a_json)
is_parsed := True is_parsed := True
create errors.make create errors.make
end end
feature -- Status report feature -- Status report
is_parsed: BOOLEAN is_parsed: BOOLEAN
-- Is parsed? -- Is parsed?
errors: LINKED_LIST [STRING] errors: LINKED_LIST [STRING]
-- Current errors -- Current errors
current_errors: STRING current_errors: STRING
-- Current errors as string -- Current errors as string
do do
create Result.make_empty create Result.make_empty
from from
errors.start errors.start
until until
errors.after errors.after
loop loop
Result.append_string (errors.item + "%N") Result.append_string (errors.item + "%N")
errors.forth errors.forth
end end
end end
feature -- Element change feature -- Element change
report_error (e: STRING) is report_error (e: STRING) is
-- Report error `e' -- Report error `e'
require require
e_not_void: e /= Void e_not_void: e /= Void
do do
errors.force (e) errors.force (e)
end end
feature -- Commands feature -- Commands
parse_json: ?JSON_VALUE is parse_json: ?JSON_VALUE is
-- Parse JSON data `representation' -- Parse JSON data `representation'
do -- start ::= object | array
Result := parse do
if extra_elements then if is_valid_start_symbol then
is_parsed := False Result := parse
end if extra_elements then
end is_parsed := False
end
parse: ?JSON_VALUE is else
-- Parse JSON data `representation' is_parsed := False
local report_error ("Syntax error unexpected token, expecting `{' or `['")
c: CHARACTER end
do end
if is_parsed then
skip_white_spaces parse: ?JSON_VALUE is
c := actual -- Parse JSON data `representation'
inspect c local
when j_OBJECT_OPEN then c: CHARACTER
Result := parse_object do
when j_STRING then if is_parsed then
Result := parse_string skip_white_spaces
when j_ARRAY_OPEN then c := actual
Result := parse_array inspect c
else when j_OBJECT_OPEN then
if c.is_digit or c = j_MINUS then Result := parse_object
Result := parse_number when j_STRING then
elseif is_null then Result := parse_string
Result := create {JSON_NULL} when j_ARRAY_OPEN then
next Result := parse_array
next else
next if c.is_digit or c = j_MINUS then
elseif is_true then Result := parse_number
Result := create {JSON_BOOLEAN}.make_boolean (True) elseif is_null then
next Result := create {JSON_NULL}
next next
next next
elseif is_false then next
Result := create {JSON_BOOLEAN}.make_boolean (False) elseif is_true then
next Result := create {JSON_BOOLEAN}.make_boolean (True)
next next
next next
next next
else elseif is_false then
is_parsed := False Result := create {JSON_BOOLEAN}.make_boolean (False)
report_error ("JSON is not well formed in parse") next
Result := Void next
end next
end next
end else
ensure is_parsed := False
is_parsed_implies_result_not_void: is_parsed implies Result /= Void report_error ("JSON is not well formed in parse")
end Result := Void
end
parse_object: JSON_OBJECT is end
-- object end
-- {} ensure
-- {"key" : "value" [,]} is_parsed_implies_result_not_void: is_parsed implies Result /= Void
local end
has_more: BOOLEAN
l_json_string: ?JSON_STRING parse_object: JSON_OBJECT is
l_value: ?JSON_VALUE -- object
do -- {}
create Result.make -- {"key" : "value" [,]}
-- check if is an empty object {} local
next has_more: BOOLEAN
skip_white_spaces l_json_string: ?JSON_STRING
if actual = j_OBJECT_CLOSE then l_value: ?JSON_VALUE
--is an empty object do
else create Result.make
-- a complex object {"key" : "value"} -- check if is an empty object {}
previous next
from has_more := True until not has_more loop skip_white_spaces
next if actual = j_OBJECT_CLOSE then
skip_white_spaces --is an empty object
l_json_string := parse_string else
next -- a complex object {"key" : "value"}
skip_white_spaces previous
if actual = ':' then from has_more := True until not has_more loop
next next
skip_white_spaces skip_white_spaces
else l_json_string := parse_string
is_parsed := False next
report_error ("%N Input string is a not well formed JSON, expected: : found: " + actual.out) skip_white_spaces
has_more := False if actual = ':' then
end next
skip_white_spaces
l_value := parse else
if is_parsed and then (l_value /= Void and l_json_string /= Void) then is_parsed := False
Result.put (l_value, l_json_string) report_error ("%N Input string is a not well formed JSON, expected: : found: " + actual.out)
next has_more := False
skip_white_spaces end
if actual = j_OBJECT_CLOSE then
has_more := False l_value := parse
elseif actual /= ',' then if is_parsed and then (l_value /= Void and l_json_string /= Void) then
has_more := False Result.put (l_value, l_json_string)
is_parsed := False next
report_error ("JSON Object syntactically malformed expected , found: [" + actual.out + "]") skip_white_spaces
end if actual = j_OBJECT_CLOSE then
else has_more := False
has_more := False elseif actual /= ',' then
-- explain the error has_more := False
end is_parsed := False
end report_error ("JSON Object syntactically malformed expected , found: [" + actual.out + "]")
end end
end else
has_more := False
parse_string: ?JSON_STRING is -- explain the error
-- Parsed string end
local end
has_more: BOOLEAN end
l_json_string: STRING end
l_unicode: STRING
c: like actual parse_string: ?JSON_STRING is
do -- Parsed string
create l_json_string.make_empty local
if actual = j_STRING then has_more: BOOLEAN
from l_json_string: STRING
has_more := True l_unicode: STRING
until c: like actual
not has_more do
loop create l_json_string.make_empty
next if actual = j_STRING then
c := actual from
if c = j_STRING then has_more := True
has_more := False until
elseif c = '%H' then not has_more
next loop
c := actual next
if c = 'u' then c := actual
create l_unicode.make_from_string ("\u") if c = j_STRING then
l_unicode.append (read_unicode) has_more := False
c := actual elseif c = '%H' then
if is_a_valid_unicode (l_unicode) then next
l_json_string.append (l_unicode) c := actual
else if c = 'u' then
has_more := False create l_unicode.make_from_string ("\u")
is_parsed := False l_unicode.append (read_unicode)
report_error ("Input String is not well formed JSON, expected a Unicode value, found [" + c.out + " ]") c := actual
end if is_a_valid_unicode (l_unicode) then
elseif (not is_special_character (c) and not is_special_control (c)) or c = '%N' then l_json_string.append (l_unicode)
has_more := False else
is_parsed := False has_more := False
report_error ("Input String is not well formed JSON, found [" + c.out + " ]") is_parsed := False
else report_error ("Input String is not well formed JSON, expected a Unicode value, found [" + c.out + " ]")
l_json_string.append ("\") end
l_json_string.append (c.out) elseif (not is_special_character (c) and not is_special_control (c)) or c = '%N' then
end has_more := False
else is_parsed := False
if is_special_character (c) and c /= '/' then report_error ("Input String is not well formed JSON, found [" + c.out + " ]")
has_more := False else
is_parsed := False l_json_string.append ("\")
report_error ("Input String is not well formed JSON, found [" + c.out + " ]") l_json_string.append (c.out)
else end
l_json_string.append_character (c) else
end if is_special_character (c) and c /= '/' then
end has_more := False
end is_parsed := False
create Result.make_json (l_json_string) report_error ("Input String is not well formed JSON, found [" + c.out + " ]")
else else
Result := Void l_json_string.append_character (c)
end end
end end
end
parse_array: JSON_ARRAY is create Result.make_json (l_json_string)
-- array else
-- [] Result := Void
-- [elements [,]] end
local end
flag: BOOLEAN
l_value: ?JSON_VALUE parse_array: JSON_ARRAY is
c: like actual -- array
do -- []
create Result.make_array -- [elements [,]]
--check if is an empty array [] local
next flag: BOOLEAN
skip_white_spaces l_value: ?JSON_VALUE
if actual = j_array_close then c: like actual
--is an empty array do
else create Result.make_array
previous --check if is an empty array []
from next
flag := True skip_white_spaces
until if actual = j_array_close then
not flag --is an empty array
loop else
next previous
skip_white_spaces from
l_value := parse flag := True
if is_parsed and then l_value /= Void then until
Result.add (l_value) not flag
next loop
skip_white_spaces next
c := actual skip_white_spaces
if c = j_ARRAY_CLOSE then l_value := parse
flag := False if is_parsed and then l_value /= Void then
elseif c /= ',' then Result.add (l_value)
flag := False next
is_parsed := False skip_white_spaces
report_error ("Array is not well formed JSON, found [" + c.out + " ]") c := actual
end if c = j_ARRAY_CLOSE then
else flag := False
flag := False elseif c /= ',' then
report_error ("Array is not well formed JSON, found [" + actual.out + " ]") flag := False
end is_parsed := False
end report_error ("Array is not well formed JSON, found [" + c.out + " ]")
end end
end else
flag := False
parse_number: ?JSON_NUMBER is report_error ("Array is not well formed JSON, found [" + actual.out + " ]")
-- Parsed number end
local end
sb: STRING end
flag: BOOLEAN end
is_integer: BOOLEAN
c: like actual parse_number: ?JSON_NUMBER is
do -- Parsed number
create sb.make_empty local
sb.append_character (actual) sb: STRING
flag: BOOLEAN
from is_integer: BOOLEAN
flag := True c: like actual
until do
not flag create sb.make_empty
loop sb.append_character (actual)
next
c := actual from
if not has_next or is_close_token (c) flag := True
or c = ',' or c = '%N' or c = '%R' until
then not flag
flag := False loop
previous next
else c := actual
sb.append_character (c) if not has_next or is_close_token (c)
end or c = ',' or c = '%N' or c = '%R'
end then
flag := False
if is_a_valid_number (sb) then previous
if sb.is_integer then else
create Result.make_integer (sb.to_integer) sb.append_character (c)
is_integer := True end
elseif sb.is_double and not is_integer then end
create Result.make_real (sb.to_double)
end if is_a_valid_number (sb) then
else if sb.is_integer then
is_parsed := False create Result.make_integer (sb.to_integer)
report_error ("Expected a number, found: [ " + sb + " ]") is_integer := True
end elseif sb.is_double and not is_integer then
end create Result.make_real (sb.to_double)
end
is_null: BOOLEAN is else
-- Word at index represents null? is_parsed := False
local report_error ("Expected a number, found: [ " + sb + " ]")
l_null: STRING end
l_string: STRING end
do
l_null := null_id is_null: BOOLEAN is
l_string := json_substring (index,index + l_null.count - 1) -- Word at index represents null?
if l_string.is_equal (l_null) then local
Result := True l_null: STRING
end l_string: STRING
end do
l_null := null_id
is_false: BOOLEAN is l_string := json_substring (index,index + l_null.count - 1)
-- Word at index represents false? if l_string.is_equal (l_null) then
local Result := True
l_false: STRING end
l_string: STRING end
do
l_false := false_id is_false: BOOLEAN is
l_string := json_substring (index, index + l_false.count - 1) -- Word at index represents false?
if l_string.is_equal (l_false) then local
Result := True l_false: STRING
end l_string: STRING
end do
l_false := false_id
is_true: BOOLEAN is l_string := json_substring (index, index + l_false.count - 1)
-- Word at index represents true? if l_string.is_equal (l_false) then
local Result := True
l_true: STRING end
l_string: STRING end
do
l_true := true_id is_true: BOOLEAN is
l_string := json_substring (index,index + l_true.count - 1) -- Word at index represents true?
if l_string.is_equal (l_true) then local
Result := True l_true: STRING
end l_string: STRING
end do
l_true := true_id
read_unicode: STRING is l_string := json_substring (index,index + l_true.count - 1)
-- Read unicode and return value if l_string.is_equal (l_true) then
local Result := True
i: INTEGER end
do end
create Result.make_empty
from read_unicode: STRING is
i := 1 -- Read unicode and return value
until local
i > 4 or not has_next i: INTEGER
loop do
next create Result.make_empty
Result.append_character (actual) from
i := i + 1 i := 1
end until
end i > 4 or not has_next
loop
feature {NONE} -- Implementation next
Result.append_character (actual)
is_a_valid_number (a_number: STRING): BOOLEAN is i := i + 1
-- is 'a_number' a valid number based on this regular expression end
-- "-?(?: 0|[1-9]\d+)(?: \.\d+)?(?: [eE][+-]?\d+)?\b"? end
do
Result := a_number.is_real_sequence feature {NONE} -- Implementation
end
is_a_valid_number (a_number: STRING): BOOLEAN is
is_a_valid_unicode (a_unicode: STRING): BOOLEAN is -- is 'a_number' a valid number based on this regular expression
-- is 'a_unicode' a valid unicode based on this regular expression -- "-?(?: 0|[1-9]\d+)(?: \.\d+)?(?: [eE][+-]?\d+)?\b"?
-- "\\u[0-9a-fA-F]{4}" do
local Result := a_number.is_real_sequence
i: INTEGER end
do
if is_a_valid_unicode (a_unicode: STRING): BOOLEAN is
a_unicode.count = 6 and then -- is 'a_unicode' a valid unicode based on this regular expression
a_unicode.item (1) = '\' and then -- "\\u[0-9a-fA-F]{4}"
a_unicode.item (2) = 'u' local
then i: INTEGER
from do
Result := True if
i := 3 a_unicode.count = 6 and then
until a_unicode.item (1) = '\' and then
i > 6 a_unicode.item (2) = 'u'
loop then
inspect a_unicode.item (i) from
when '0'..'9', 'a'..'f', 'A'..'F' then Result := True
else i := 3
Result := False until
i := 6 i > 6
end loop
i := i + 1 inspect a_unicode.item (i)
end when '0'..'9', 'a'..'f', 'A'..'F' then
end else
end Result := False
i := 6
extra_elements: BOOLEAN is end
-- has more elements? i := i + 1
local end
c: like actual end
do end
if has_next then
next extra_elements: BOOLEAN is
end -- has more elements?
from local
c := actual c: like actual
until do
c /= ' ' or c /= '%R' or c /= '%U' or c /= '%T' or c /= '%N' or not has_next if has_next then
loop next
next end
end from
Result := has_next c := actual
end until
c /= ' ' or c /= '%R' or c /= '%U' or c /= '%T' or c /= '%N' or not has_next
feature {NONE} -- Constants loop
next
false_id: STRING is "false" end
Result := has_next
true_id: STRING is "true" end
null_id: STRING is "null" is_valid_start_symbol : BOOLEAN
-- expecting `{' or `[' as start symbol
do
end Result := representation.starts_with ("{") or representation.starts_with ("[")
end
feature {NONE} -- Constants
false_id: STRING is "false"
true_id: STRING is "true"
null_id: STRING is "null"
end