json parsing

This commit is contained in:
Vadim Lopatin 2015-02-06 11:55:41 +03:00
parent 424bf7a83e
commit 589a02debd
1 changed files with 125 additions and 57 deletions

View File

@ -1,9 +1,10 @@
module dlangui.core.settings; module dlangui.core.settings;
import std.range; import std.range;
import std.algorithm; import std.algorithm : equal;
import std.conv; import std.conv : to;
import std.utf; import std.utf : encode;
import std.math : pow;
enum SettingType { enum SettingType {
STRING, STRING,
@ -1015,12 +1016,8 @@ final class Setting {
@property char peek() { @property char peek() {
return pos < json.length ? json[pos] : 0; return pos < json.length ? json[pos] : 0;
} }
/// returns next char, 0 if eof
@property char nextChar() {
return pos < json.length ? json[pos++] : 0;
}
/// skips current char, returns next one (or null if eof) /// skips current char, returns next one (or null if eof)
@property char skipChar() { @property char nextChar() {
if (pos < json.length - 1) { if (pos < json.length - 1) {
return json[++pos]; return json[++pos];
} else { } else {
@ -1102,7 +1099,7 @@ final class Setting {
error("unexpected end of file while parsing unicode character entity inside string"); error("unexpected end of file while parsing unicode character entity inside string");
dchar ch = 0; dchar ch = 0;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
int d = parseHexDigit(skipChar); int d = parseHexDigit(nextChar);
if (d < 0) if (d < 0)
error("error while parsing unicode character entity inside string"); error("error while parsing unicode character entity inside string");
ch = (ch << 4) | d; ch = (ch << 4) | d;
@ -1112,23 +1109,23 @@ final class Setting {
return buf[0..sz].dup; return buf[0..sz].dup;
} }
string parseString() { @property string parseString() {
char ch = peek; char ch = peek;
if (ch != '\"') { if (ch != '\"') {
error("cannot parse string"); error("cannot parse string");
} }
char[] res; char[] res;
for (;;) { for (;;) {
ch = skipChar; ch = nextChar;
if (!ch) if (!ch)
error("unexpected end of file while parsing string"); error("unexpected end of file while parsing string");
if (ch == '\"') { if (ch == '\"') {
skipChar; nextChar;
return cast(string)res; return cast(string)res;
} }
if (ch == '\\') { if (ch == '\\') {
// escape sequence // escape sequence
ch = skipChar; ch = nextChar;
switch (ch) { switch (ch) {
case 'n': case 'n':
res ~= '\n'; res ~= '\n';
@ -1161,16 +1158,16 @@ final class Setting {
} }
return cast(string)res; return cast(string)res;
} }
string parseIdent() { @property string parseIdent() {
char ch = peek; char ch = peek;
if (ch == '\"') { if (ch == '\"') {
return parseString(); return parseString;
} }
char[] res; char[] res;
if (isAlpha(ch)) { if (isAlpha(ch)) {
res ~= ch; res ~= ch;
for (;;) { for (;;) {
ch = skipChar; ch = nextChar;
if (isAlNum(ch)) { if (isAlNum(ch)) {
res ~= ch; res ~= ch;
} else { } else {
@ -1197,71 +1194,142 @@ final class Setting {
pos += ident.length; pos += ident.length;
return true; return true;
} }
Setting parseNumber(Setting res) {
return res;
}
}
private Setting parseValue(ref JsonParser parser) { // parse long, ulong or double
char ch = parser.skipSpaces; void parseNumber(Setting res) {
Setting res = new Setting(); char ch = peek;
if (ch == '\"') { int sign = 1;
res = parser.parseString; if (ch == '-') {
return res; sign = -1;
} else if (ch == '[') { nextChar;
//res.parseArray(parser); }
} else if (ch == '{') { if (!isDigit(ch))
res.parseMap(parser); error("cannot parse number");
} else { ulong n = 0;
if (parser.parseKeyword("null")) { while (isDigit(ch)) {
return res; n = n * 10 + (ch - '0');
} else if (parser.parseKeyword("true")) { ch = nextChar;
res = true; }
return res; if (ch == '.' || ch == 'e' || ch == 'E') {
} else if (parser.parseKeyword("false")) { // floating
res = false; ulong n2 = 0;
return res; ulong n2_div = 1;
} else if (ch == '-' || JsonParser.isDigit(ch)) { if (ch == '.') {
return parser.parseNumber(res); ch = nextChar;
while(isDigit(ch)) {
n2 = n2 * 10 + (ch - '0');
n2_div *= 10;
ch = nextChar;
}
if (isAlpha(ch) && ch != 'e' && ch != 'E')
error("error while parsing number");
}
int shift = 0;
int shiftSign = 1;
if (ch == 'e' || ch == 'E') {
ch = nextChar;
if (ch == '-') {
shiftSign = -1;
ch = nextChar;
}
if (!isDigit(ch))
error("error while parsing number");
while(isDigit(ch)) {
shift = shift * 10 + (ch - '0');
ch = nextChar;
}
if (shiftSign < 0)
shift = -shift;
}
if (isAlpha(ch))
error("error while parsing number");
double v = sign > 0 ? n : -n;
if (n2) // part after period
v += cast(double)n2 / n2_div;
if (shift) // E part - pow10
v *= pow(10.0, shift);
res.floating = v;
} else { } else {
parser.error("cannot parse JSON value"); // integer
if (isAlpha(ch))
error("cannot parse number");
if (sign < 0 || !(n & 0x8000000000000000L))
res.integer = cast(long)(n * sign); // signed
else
res.uinteger = n; // unsigned
} }
} }
return res;
} }
private void parseMap(ref JsonParser parser) { private void parseMap(ref JsonParser parser) {
clear(SettingType.OBJECT); clear(SettingType.OBJECT);
parser.skipChar; // skip initial { parser.nextChar; // skip initial {
for(;;) { for(;;) {
char ch = parser.skipSpaces; char ch = parser.skipSpaces;
if (ch == '}') { if (ch == '}') {
parser.skipChar; parser.nextChar;
break;
} }
string key = parser.parseIdent(); string key = parser.parseIdent;
ch = parser.skipSpaces; ch = parser.skipSpaces;
if (ch != ':') if (ch != ':')
parser.error("no : char after object field name"); parser.error("no : char after object field name");
parser.skipChar(); parser.nextChar;
Setting value = parseValue(parser); this[key] = (new Setting()).parseJSON(parser);
this[key] = value;
ch = parser.skipSpaces; ch = parser.skipSpaces;
if (ch == ',') { if (ch == ',') {
parser.skipChar(); parser.nextChar;
parser.skipSpaces(); parser.skipSpaces;
} else if (ch != '}') { } else if (ch != '}') {
parser.error("unexpected character when waiting for , or } while parsing object"); parser.error("unexpected character when waiting for , or } while parsing object");
} }
} }
} }
private void parseJSON(ref JsonParser parser) {
char ch = parser.skipSpaces; private void parseArray(ref JsonParser parser) {
if (ch == '{') { clear(SettingType.ARRAY);
parseMap(parser); parser.nextChar; // skip initial [
} else if (ch == '[') { for(;;) {
} else if (ch == '\"') { char ch = parser.skipSpaces;
if (ch == ']') {
parser.nextChar;
break;
}
Setting value = new Setting();
value.parseJSON(parser);
this[_store.array.length] = value;
ch = parser.skipSpaces;
if (ch == ',') {
parser.nextChar;
parser.skipSpaces;
} else if (ch != ']') {
parser.error("unexpected character when waiting for , or ] while parsing array");
}
} }
} }
private Setting parseJSON(ref JsonParser parser) {
char ch = parser.skipSpaces;
if (ch == '\"') {
this = parser.parseString;
} else if (ch == '[') {
parseArray(parser);
} else if (ch == '{') {
parseMap(parser);
} else if (parser.parseKeyword("null")) {
// do nothing - we already have NULL value
} else if (parser.parseKeyword("true")) {
this = true;
} else if (parser.parseKeyword("false")) {
this = false;
} else if (ch == '-' || JsonParser.isDigit(ch)) {
parser.parseNumber(this);
} else {
parser.error("cannot parse JSON value");
}
return this;
}
bool parseJSON(string s) { bool parseJSON(string s) {
clear(SettingType.NULL); clear(SettingType.NULL);
JsonParser parser; JsonParser parser;