mirror of
https://github.com/gnosygnu/xowa.git
synced 2024-09-29 06:50:50 +00:00
185 lines
6.7 KiB
Java
185 lines
6.7 KiB
Java
/*
|
|
XOWA: the XOWA Offline Wiki Application
|
|
Copyright (C) 2012-2017 gnosygnu@gmail.com
|
|
|
|
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
|
|
or alternatively under the terms of the Apache License Version 2.0.
|
|
|
|
You may use XOWA according to either of these licenses as is most appropriate
|
|
for your project on a case-by-case basis.
|
|
|
|
The terms of each license can be found in the source code repository:
|
|
|
|
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
|
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
|
*/
|
|
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 Gfo_number_parser num_parser = new Gfo_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 += 5; break; // \uFFFF 1 u + 4 hex-dec; ISSUE#:486; DATE:2019-06-02
|
|
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);
|
|
if (num_parser.Has_frac())
|
|
return factory.Decimal(doc, num_bgn, pos);
|
|
else {
|
|
if (num_parser.Is_int())
|
|
return factory.Int(doc, num_bgn, pos);
|
|
else
|
|
return factory.Long(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");
|
|
}
|