/* XOWA: the XOWA Offline Wiki Application Copyright (C) 2012 gnosygnu@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package gplx.langs.jsons; import gplx.*; import gplx.langs.*; import gplx.core.primitives.*; public class Json_parser { private byte[] src; private int src_len, pos; private final Number_parser num_parser = new Number_parser(); public Json_factory Factory() {return factory;} private final Json_factory factory = new Json_factory(); public Json_doc Parse_by_apos_ary(String... ary) {return Parse_by_apos(String_.Concat_lines_nl(ary));} public Json_doc Parse_by_apos(String s) {return Parse(Bry_.Replace(Bry_.new_u8(s), Byte_ascii.Apos, Byte_ascii.Quote));} public Json_doc Parse(String src) {return Parse(Bry_.new_u8(src));} public Json_doc Parse(byte[] src) { synchronized (factory) { this.src = src; if (src == null) return null; this.src_len = src.length; if (src_len == 0) return null; this.pos = 0; Skip_ws(); boolean root_is_nde = true; switch (src[pos]) { case Byte_ascii.Curly_bgn: root_is_nde = Bool_.Y; break; case Byte_ascii.Brack_bgn: root_is_nde = Bool_.N; break; default: return null; } Skip_ws(); Json_doc doc = new Json_doc(); Json_grp root = null; if (root_is_nde) root = Make_nde(doc); else root = Make_ary(doc); doc.Ctor(src, root); return doc; } } private Json_nde Make_nde(Json_doc doc) { ++pos; // brack_bgn Json_nde nde = new Json_nde(doc, pos); while (pos < src_len) { Skip_ws(); if (src[pos] == Byte_ascii.Curly_end) {++pos; return nde;} else nde.Add(Make_kv(doc)); Skip_ws(); switch (src[pos++]) { case Byte_ascii.Comma: break; case Byte_ascii.Curly_end: return nde; default: throw Err_.new_unhandled(src[pos - 1]); } } throw Err_.new_wo_type("eos inside nde"); } private Json_itm Make_kv(Json_doc doc) { Json_itm key = Make_string(doc); Skip_ws(); Chk(Byte_ascii.Colon); Skip_ws(); Json_itm val = Make_val(doc); return new Json_kv(key, val); } private Json_itm Make_val(Json_doc doc) { while (pos < src_len) { byte b = src[pos]; switch (b) { case Byte_ascii.Ltr_n: return Make_literal(Bry_null_ull , 3, factory.Null()); case Byte_ascii.Ltr_f: return Make_literal(Bry_bool_alse , 4, factory.Bool_n()); case Byte_ascii.Ltr_t: return Make_literal(Bry_bool_rue , 3, factory.Bool_y()); case Byte_ascii.Quote: return Make_string(doc); case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: case Byte_ascii.Dash: return Make_num(doc); case Byte_ascii.Brack_bgn: return Make_ary(doc); case Byte_ascii.Curly_bgn: return Make_nde(doc); } throw Err_.new_unhandled(Char_.To_str(b)); } throw Err_.new_wo_type("eos reached in val"); } private Json_itm Make_literal(byte[] remainder, int remainder_len, Json_itm singleton) { ++pos; // 1st char int literal_end = pos + remainder_len; if (Bry_.Eq(src, pos, literal_end, remainder)) { pos = literal_end; return singleton; } throw Err_.new_("json.parser", "invalid literal", "excerpt", Bry_.Mid_by_len_safe(src, pos - 1, 16)); } private Json_itm Make_string(Json_doc doc) { int bgn = pos++; // ++: quote_bgn boolean exact = true; while (pos < src_len) { switch (src[pos]) { case Byte_ascii.Backslash: ++pos; // backslash switch (src[pos]) { case Byte_ascii.Ltr_u: pos += 4; break; // \uFFFF 4 hex-dec default: ++pos; break; // \? " \ / b f n r t } exact = false; break; case Byte_ascii.Quote: return factory.Str(doc, bgn, ++pos, exact); // ++: quote_end default: ++pos; break; } } throw Err_.new_wo_type("eos reached inside quote"); } private Json_itm Make_num(Json_doc doc) { int num_bgn = pos; boolean loop = true; while (loop) { if (pos == src_len) throw Err_.new_wo_type("eos reached inside num"); switch (src[pos]) { case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: ++pos; break; case Byte_ascii.Dot: case Byte_ascii.Dash: case Byte_ascii.Plus: case Byte_ascii.Ltr_E: case Byte_ascii.Ltr_e: // e e+ e- E E+ E- ++pos; break; default: loop = false; break; } } num_parser.Parse(src, num_bgn, pos); return num_parser.Has_frac() ? factory.Decimal(doc, num_bgn, pos) : factory.Int(doc, num_bgn, pos); } private Json_ary Make_ary(Json_doc doc) { Json_ary rv = factory.Ary(pos++, pos); // brack_bgn while (pos < src_len) { Skip_ws(); if (src[pos] == Byte_ascii.Brack_end) {++pos; return rv;} else rv.Add(Make_val(doc)); Skip_ws(); switch (src[pos]) { case Byte_ascii.Comma: ++pos; break; case Byte_ascii.Brack_end: ++pos; return rv; } } throw Err_.new_wo_type("eos inside ary"); } private void Skip_ws() { while (pos < src_len) { switch (src[pos]) { case Byte_ascii.Space: case Byte_ascii.Nl: case Byte_ascii.Tab: case Byte_ascii.Cr: ++pos; break; default: return; } } } private void Chk(byte expd) { if (src[pos] == expd) ++pos; else throw err_(src, pos, "expected '{0}' but got '{1}'", Char_.To_str(expd), Char_.To_str(src[pos])); } private Err err_(byte[] src, int bgn, String fmt, Object... args) {return err_(src, bgn, src.length, fmt, args);} private Err err_(byte[] src, int bgn, int src_len, String fmt, Object... args) { String msg = String_.Format(fmt, args) + " " + Int_.To_str(bgn) + " " + String_.new_u8__by_len(src, bgn, 20); return Err_.new_wo_type(msg); } private static final byte[] Bry_bool_rue = Bry_.new_a7("rue"), Bry_bool_alse = Bry_.new_a7("alse"), Bry_null_ull = Bry_.new_a7("ull"); }