Files
EWF/json/scanner/json_parser.e
jvelilla 1359a1ceae Add descriptions.
Change in skip_withe_spaces to handle newline %N and carriage Return %R
2008-08-29 10:49:24 +00:00

399 lines
8.4 KiB
Plaintext

indexing
description: "Parse serialized JSON data"
author: "jvelilla"
date: "2008/08/24"
revision: "Revision 0.1"
class
JSON_PARSER
inherit
JSON_READER
JSON_TOKENS
create
make_parser
feature {NONE} -- Initialize
make_parser (a_json: STRING) is
-- Initialize.
require
json_not_empty: a_json /= Void and then not a_json.is_empty
do
make (a_json)
is_parsed := True
create current_errors.make_empty
end
feature -- Access
is_parsed: BOOLEAN
current_errors: STRING
feature -- Commands
parse_json: JSON_VALUE is
--
do
Result := parse
if extra_elements then
is_parsed := false
end
end
parse: JSON_VALUE is
--
local
c: CHARACTER
do
if is_parsed then
skip_withe_spaces
c := actual
if c.is_equal (j_object_open) then
Result := parse_object
elseif c.is_equal (j_string) then
Result := parse_string
elseif c.is_equal (j_array_open) then
Result := parse_array
elseif c.is_digit or c.is_equal (j_minus) then
Result := parse_number
elseif is_null then
--
Result := create {JSON_NULL}
next;next;next
elseif is_true then
Result := create {JSON_BOOLEAN}.make_boolean (true)
next;next;next
elseif is_false then
Result := create {JSON_BOOLEAN}.make_boolean (false)
next;next;next;next
else
is_parsed := false
current_errors.append ("JSON is not well formed in parse")
Result := void
end
end
end
parse_object: JSON_OBJECT is
-- object
-- {}
-- {"key" : "value" [,]}
local
has_more: BOOLEAN
l_json_string: JSON_STRING
l_value: JSON_VALUE
do
create Result.make
-- check if is an empty object {}
next
skip_withe_spaces
if actual.is_equal (j_object_close) then
--is an empty object
else
-- a complex object {"key" : "value"}
previous
from has_more := true until not has_more
loop
next
skip_withe_spaces
l_json_string := parse_string
next
skip_withe_spaces
if actual.is_equal (':') then
next
skip_withe_spaces
else
is_parsed := false
current_errors.append ("%N Input string is a not well formed JSON, expected: : found: " + actual.out +"%N")
has_more := false
end
l_value := parse
if is_parsed then
Result.put (l_value,l_json_string)
next
skip_withe_spaces
if actual.is_equal (j_object_close) then
has_more := false
elseif not actual.is_equal (',') then
has_more := false
is_parsed := false
current_errors.append ("JSON Object sintactically malformed expected , found: [" + actual.out + "] %N")
end
else
has_more := false
-- explain the error
end
end
end
end
parse_string: JSON_STRING is
--
local
has_more: BOOLEAN
l_json_string: STRING
l_unicode: STRING
do
create l_json_string.make_empty
if actual.is_equal (j_string) then
from
has_more := true
until not has_more
loop
next
if actual.is_equal (j_string) then
has_more := false
elseif actual.is_equal ('%H') then
next
if actual.is_equal ('u') then
create l_unicode.make_from_string ("\u")
l_unicode.append (read_unicode)
if is_a_valid_unicode (l_unicode) then
l_json_string.append (l_unicode)
else
has_more := false
is_parsed := false
current_errors.append ("Input String is not well formed JSON, expected a Unicode value, found [" + actual.out + " ] %N")
end
elseif (not special_characters.has (actual) and not special_controls.has (actual)) or actual.is_equal ('%N') then
has_more := false
is_parsed := false
current_errors.append ("Input String is not well formed JSON, found [" + actual.out + " ] %N")
else
l_json_string.append ("\")
l_json_string.append (actual.out)
end
else
if special_characters.has (actual) and not actual.is_equal ('/') then
has_more := false
is_parsed := false
current_errors.append ("Input String is not well formed JSON, found [" + actual.out + " ] %N")
else
l_json_string.append (actual.out)
end
end
end
create Result.make_json (l_json_string)
else
Result := void
end
end
parse_array: JSON_ARRAY is
-- array
-- []
-- [elements [,]]
local
flag: BOOLEAN
l_value: JSON_VALUE
do
create Result.make_array
--check if is an empty array []
next
skip_withe_spaces
if actual.is_equal (j_array_close) then
--is an empty array
else
previous
from
flag := true
until
not flag
loop
next
skip_withe_spaces
l_value := parse
if is_parsed then
Result.add (l_value)
next
skip_withe_spaces
if not actual.is_equal (j_array_close) and not actual.is_equal (',')then
flag := false
is_parsed := false
current_errors.append ("Array is not well formed JSON, found [" + actual.out + " ] %N")
elseif actual.is_equal (j_array_close) then
flag := false
end
else
flag := false
current_errors.append ("Array is not well formed JSON, found [" + actual.out + " ] %N")
end
end
end
end
parse_number: JSON_NUMBER is
--
local
sb: STRING
flag: BOOLEAN
is_integer: BOOLEAN
do
create sb.make_empty
sb.append (actual.out)
from
flag := true
until not flag
loop
next
if not has_next or close_tokens.has (actual) or actual.is_equal (',')
or actual.is_equal ('%N') or actual.is_equal ('%R') then
flag := false
previous
else
sb.append (actual.out)
end
end
if is_a_valid_number (sb) then
if sb.is_integer then
create Result.make_integer (sb.to_integer)
is_integer := true
elseif sb.is_double and not is_integer then
create Result.make_real (sb.to_double)
end
else
is_parsed := false
current_errors.append ("Expected a number, found: [ " + sb + " ]")
end
end
is_null: BOOLEAN is
--
local
l_null: STRING
l_string: STRING
do
l_null := "null"
l_string := json_substring (index,index + l_null.count - 1)
if l_string.is_equal (l_null) then
Result := true
end
end
is_false: BOOLEAN is
--
local
l_false: STRING
l_string: STRING
do
l_false := "false"
l_string := json_substring (index,index + l_false.count - 1)
if l_string.is_equal (l_false) then
Result := true
end
end
is_true: BOOLEAN is
--
local
l_true: STRING
l_string: STRING
do
l_true := "true"
l_string := json_substring (index,index + l_true.count - 1)
if l_string.is_equal (l_true) then
Result := true
end
end
read_unicode: STRING is
--
local
i: INTEGER
do
create Result.make_empty
from
i := 1
until i > 4 or not has_next
loop
next
Result.append (actual.out)
i := i + 1
end
end
feature {NONE} -- Implementation
is_a_valid_number (a_number: STRING): BOOLEAN is
-- is 'a_number' a valid number based on this regular expression
-- "-?(?: 0|[1-9]\d+)(?: \.\d+)?(?: [eE][+-]?\d+)?\b"?
local
word_set: RX_CHARACTER_SET
regexp: RX_PCRE_REGULAR_EXPRESSION
number_regex: STRING
do
create regexp.make
create word_set.make_empty
a_number.right_adjust
a_number.left_adjust
word_set.add_string ("0123456789.eE+-")
regexp.set_word_set (word_set)
number_regex := "-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?\b"
regexp.compile (number_regex)
if regexp.matches (a_number) then
if a_number.is_equal (regexp.captured_substring (0)) then
Result := true
end
end
end
is_a_valid_unicode (a_unicode: STRING): BOOLEAN is
-- is 'a_unicode' a valid unicode based on this regular expression
-- "\\u[0-9a-fA-F]{4}"
local
regexp: RX_PCRE_REGULAR_EXPRESSION
unicode_regex: STRING
do
create regexp.make
unicode_regex := "\\u[0-9a-fA-F]{4}"
regexp.compile (unicode_regex)
if regexp.matches (a_unicode) then
Result := true
check
is_valid: a_unicode.is_equal (regexp.captured_substring (0))
end
end
end
extra_elements: BOOLEAN is
--has more elements?
do
if has_next then
next
end
from
until
not actual.is_equal (' ') or not actual.is_equal ('%R') or
not actual.is_equal ('%U') or not actual.is_equal ('%T')
or not actual.is_equal ('%N') or not has_next
loop
next
end
if has_next then
Result := True
end
end
end