Last active
June 21, 2016 10:09
-
-
Save alphaKAI/b8c8853602b2c58a552a3942831c7506 to your computer and use it in GitHub Desktop.
Yet Another JSON Parser for D[WIP].
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.conv; | |
enum JSONNodeValueType { | |
Numeric, | |
String, | |
Boolean, | |
Array, | |
NULL, | |
JSONObject | |
} | |
mixin template JSONNodeValueBase(TYPE) { | |
private TYPE value; | |
this (TYPE value) { | |
this.value = value; | |
} | |
@property void setValue(TYPE value) { | |
this.value = value; | |
} | |
@property TYPE getValue() { | |
return this.value; | |
} | |
} | |
class JSONNumeric { mixin JSONNodeValueBase!float; } | |
class JSONString { mixin JSONNodeValueBase!string; } | |
class JSONBoolean { mixin JSONNodeValueBase!bool; } | |
class JSONNULL { | |
void setValue(X)(X v) { this.setValue; } | |
void setValue() { throw new Exception("Can not apply any value to null object"); } | |
string getValue() { return "null"; } | |
} | |
class JSONNodeValue { | |
JSONNodeValueType type; | |
private { | |
JSONNumeric jsonNumeric; | |
JSONString jsonString; | |
JSONBoolean jsonBoolean; | |
JSONArray jsonArray; | |
JSONNULL jsonNULL; | |
JSONObject jsonObject; | |
} | |
this(JSONNodeValueType type) { | |
this.type = type; | |
} | |
this(X)(X value) { | |
this.setValue(value); | |
} | |
private static genSetValueString(T, alias R, alias L, R2)() { | |
return "void setValue(" ~ T.stringof ~ " value) {" | |
~ "this.type = JSONNodeValueType." ~ R.stringof ~ ";" | |
~ "this." ~ L.stringof ~ " = new " ~ R2.stringof ~ "(value);" | |
~ "}"; | |
} | |
private static genSetValueString(T, alias R, alias L)() { | |
return "void setValue(" ~ T.stringof ~ " value) {" | |
~ "this.type = JSONNodeValueType." ~ R.stringof ~ ";" | |
~ "this." ~ L.stringof ~ " = value;" | |
~ "}"; | |
} | |
mixin(genSetValueString!(float, JSONNodeValueType.Numeric, jsonNumeric, JSONNumeric)); | |
mixin(genSetValueString!(string, JSONNodeValueType.String, jsonString, JSONString)); | |
mixin(genSetValueString!(bool, JSONNodeValueType.Boolean, jsonBoolean, JSONBoolean)); | |
void setValue(JSONNULL value) { this.type = JSONNodeValueType.NULL; } | |
mixin(genSetValueString!(JSONArray, JSONNodeValueType.Array, jsonArray)); | |
mixin(genSetValueString!(JSONObject, JSONNodeValueType.JSONObject, jsonObject)); | |
private auto getValue(X)(X val) { | |
if (val is null) { throw new Exception("The value haven't set!");} | |
else { return val.getValue; } | |
} | |
string getValue() { | |
string returnString; | |
if (type == JSONNodeValueType.Numeric) { | |
returnString = getValue(this.jsonNumeric).to!string; | |
} else if (type == JSONNodeValueType.String) { | |
returnString = "\"" ~ getValue(this.jsonString).to!string ~ "\""; | |
}else if (type == JSONNodeValueType.Boolean) { | |
returnString = getValue(this.jsonBoolean).to!string; | |
} else if (type == JSONNodeValueType.Array) { | |
import std.algorithm, std.array; | |
returnString = "[" ~ getValue(this.jsonArray).map!(x => x.getValue).join(", ") ~ "]"; | |
} else if (type == JSONNodeValueType.NULL) { | |
returnString = getValue(this.jsonNULL).to!string; | |
} else if (type == JSONNodeValueType.JSONObject) { | |
returnString = this.jsonObject.to!string; | |
} | |
return returnString; | |
} | |
private static genGetValueString(T, alias R, alias L)() { | |
return T.stringof ~ " getValue(JSONNodeValueType type)() if (type == JSONNodeValueType." ~ R.stringof ~ ") {" | |
~ "return getValue(this." ~ L.stringof ~");" | |
~ "}"; | |
} | |
mixin(genGetValueString!(float, JSONNodeValueType.Numeric, jsonNumeric)); | |
mixin(genGetValueString!(string, JSONNodeValueType.String, jsonString)); | |
mixin(genGetValueString!(bool, JSONNodeValueType.Boolean, jsonBoolean)); | |
mixin(genGetValueString!(JSONArray, JSONNodeValueType.Array, jsonArray)); | |
mixin(genGetValueString!(JSONNULL, JSONNodeValueType.NULL, jsonNULL)); | |
JSONObject getValue(JSONNodeValueType type)() if (type == JSONNodeValueType.JSONObject) { return this.jsonObject; } | |
@property JSONNodeValueType getType() { | |
return this.type; | |
} | |
} | |
class JSONArray { | |
mixin JSONNodeValueBase!(JSONNodeValue[]); | |
void addValue(JSONNodeValue value) { this.value ~= value; } | |
} | |
class JSONObject { | |
mixin JSONNodeValueBase!(JSONNode[string]); | |
alias value this; | |
this() {} | |
this(JSONNode[] nodes) { | |
foreach (node; nodes) { | |
this.addNode(node); | |
} | |
} | |
void addNode(JSONNode node) { | |
this.value[node.key] = node; | |
} | |
} | |
class JSONNode { | |
string key; | |
JSONNodeValue value; | |
JSONNodeValueType type; | |
private static genThisString(L, alias R)() { | |
return "this(string key, " ~ L.stringof ~ " value) { this.key = key; this.type = JSONNodeValueType." ~ R.stringof ~ "; this(); setValue(value); }"; | |
} | |
this(string key, JSONNodeValue value) { | |
this.key = key; | |
this.value = value; | |
this.type = value.type; | |
} | |
mixin(genThisString!(float, JSONNodeValueType.Numeric)); | |
mixin(genThisString!(string, JSONNodeValueType.String)); | |
mixin(genThisString!(bool, JSONNodeValueType.Boolean)); | |
mixin(genThisString!(JSONNULL, JSONNodeValueType.NULL)); | |
mixin(genThisString!(JSONArray, JSONNodeValueType.Array)); | |
mixin(genThisString!(JSONObject, JSONNodeValueType.JSONObject)); | |
private this() { value = new JSONNodeValue(type); } | |
@property void setValue(X)(X value) { | |
this.value.setValue(value); | |
} | |
@property JSONNodeValue getValue() { | |
return this.value; | |
} | |
auto gv() { | |
return this.value.getValue; | |
} | |
} | |
/+ | |
{ | |
"string":"string", | |
"integer": 123, | |
"float": 1.23, | |
"bool":true, | |
"array": ["string", 123, 1.23], | |
"object": { | |
"string":"string", | |
"integer": 123 | |
} | |
} | |
Mapping to D's JSON Handling class -> | |
JSONObject { | |
JSONNode("string", JSONNodeValue<string>("string")), | |
JSONNode("integer", JSONNodeValue<numeric>(123)), | |
JSONNode("float", JSONNodeValue<numeric>(1.23)), | |
JSONNode("bool", JSONNodeValue<bool>(true)), | |
JSONNode("array", JSONNodeValue<array>( | |
JSONNodeValue<string>("string"), | |
JSONNodeValue<numeric>(123), | |
JSONNodeValue<numeric>(1.23)), | |
JSONNode("object", JSONNodeValue<JSONObject> { | |
JSONNode("string", JSONNodeValue<string>("string")), | |
JSONNode("integer", JSONNodeValue<numeric>(123)) | |
}) | |
} | |
+/ | |
/+ | |
unittest { | |
JSONObject json = new JSONObject; | |
json.addNode(new JSONNode("string", "string")); | |
json.addNode(new JSONNode("integer", 123)); | |
json.addNode(new JSONNode("float", 1.23)); | |
json.addNode(new JSONNode("bool", true)); | |
json.addNode(new JSONNode("array", | |
new JSONArray([ | |
new JSONNodeValue("string"), | |
new JSONNodeValue(123), | |
new JSONNodeValue(1.23) | |
]))); | |
json.addNode(new JSONNode("object", new JSONObject([ | |
new JSONNode("string", "string"), | |
new JSONNode("integer", 123) | |
]))); | |
// dumpJSONString(json); | |
} | |
+/ | |
import std.range, | |
std.stdio; | |
void dumpJSONString(JSONObject json, int depth = 0) { | |
size_t c; | |
writeln("{"); | |
foreach(string key, JSONNode node; json.getValue) { | |
if (node.type == JSONNodeValueType.JSONObject) { | |
if (c) { | |
writeln(","); | |
} | |
foreach (_; ((depth + 1) * 2).iota) { write(" "); } | |
write("\"" ~ key ~ "\" : "); | |
dumpJSONString(node.getValue.getValue!(JSONNodeValueType.JSONObject), depth + 1); | |
} else { | |
if (c) { | |
writeln(","); | |
} | |
foreach (_; ((depth + 1) * 2 ).iota) { write(" "); } | |
write("\"" ~ key ~ "\" : ", node.getValue.getValue()); | |
} | |
c++; | |
} | |
writeln; | |
foreach (_; (depth * 2).iota) { write(" "); } | |
write("}"); | |
if (depth == 0) writeln; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This code is ```Experimental Implementation```, needs refactoring!!! | |
*/ | |
import std.conv, | |
std.math; | |
import jsonStructure; | |
long at; | |
string ch; | |
string[string] escapee; | |
string text; | |
void error(string errStr) { | |
import std.stdio; | |
writeln("syntaxError"); | |
writeln("message: ", errStr); | |
writeln("at: ", at); | |
writeln("text: ", text); | |
throw new Error(errStr); | |
} | |
void init() { | |
escapee = [ | |
"\"": "\"", | |
"\\": "\\", | |
"/": "/", | |
"b": "b", | |
"f": "\f", | |
"n": "\n", | |
"r": "\r", | |
"t": "\t" | |
]; | |
} | |
string next() { | |
if (at < text.length) { | |
ch = text[at].to!string; | |
at += 1; | |
return ch; | |
} else { | |
ch = null; | |
return null; | |
} | |
} | |
string next(string c) { | |
if (c && c != ch) { | |
error("Expected '" ~ c ~ "' instade of '" ~ ch ~ "'"); | |
} | |
return next; | |
} | |
JSONNodeValue numberProc() { | |
float _number; | |
string str; | |
if (ch == "-") { | |
str = "-"; | |
next("-"); | |
} | |
while ("0" <= ch && ch <= "9") { | |
str ~= ch; | |
next; | |
} | |
if (ch == ".") { | |
str ~= "."; | |
while (next && "0" <= ch && ch <= "9") { | |
str ~= ch; | |
} | |
} | |
if (ch == "e" || ch == "E") { | |
str ~= ch; | |
next; | |
if (ch == "-" || ch == "+") { | |
str ~= ch; | |
next; | |
} | |
while ("0" <= ch && ch <= "9") { | |
str ~= ch; | |
next; | |
} | |
} | |
_number = str.to!float; | |
if (isNaN(_number)) { | |
error("Bad number"); | |
return null; | |
} else { | |
JSONNodeValue jsonNodeValue = new JSONNodeValue(JSONNodeValueType.Numeric); | |
jsonNodeValue.setValue(_number); | |
return jsonNodeValue; | |
} | |
} | |
JSONNodeValue stringProc() { | |
float hex, | |
i, | |
uffff; | |
string str = ""; | |
if (ch == "\"") { | |
while (next()) { | |
if (ch == "\"") { | |
next; | |
JSONNodeValue jsonNodeValue = new JSONNodeValue(JSONNodeValueType.String); | |
jsonNodeValue.setValue(str); | |
return jsonNodeValue; | |
} else if (ch == "\\") { | |
next; | |
if (ch == "u") { | |
uffff = 0; | |
for (i = 0; i < 4; i++) { | |
hex = next().to!float; | |
if (!isFinite(hex)) { | |
break; | |
} | |
uffff = uffff * 16 + hex; | |
} | |
str ~= (cast(char)uffff).to!string; | |
} else if (ch in escapee) { | |
str ~= escapee[ch]; | |
} else { | |
break; | |
} | |
} else { | |
str ~= ch; | |
} | |
} | |
} | |
error("Bad String"); | |
return null; | |
} | |
void white() { | |
while (ch && ch <= " ") { | |
next; | |
} | |
} | |
JSONNodeValue wordProc() { | |
JSONNodeValue jsonNodeValue; | |
switch(ch) { | |
case "t": | |
next("t"); | |
next("r"); | |
next("u"); | |
next("e"); | |
jsonNodeValue = new JSONNodeValue(JSONNodeValueType.Boolean); | |
jsonNodeValue.setValue(true); | |
return jsonNodeValue; | |
case "f": | |
next("f"); | |
next("a"); | |
next("l"); | |
next("s"); | |
next("e"); | |
jsonNodeValue = new JSONNodeValue(JSONNodeValueType.Boolean); | |
jsonNodeValue.setValue(false); | |
return jsonNodeValue; | |
case "n": | |
next("n"); | |
next("u"); | |
next("l"); | |
next("l"); | |
jsonNodeValue = new JSONNodeValue(JSONNodeValueType.Boolean); | |
jsonNodeValue.setValue(new JSONNULL); | |
return jsonNodeValue; | |
default: break; | |
} | |
error("Unexpected '" ~ ch ~ "'"); | |
return null; | |
} | |
JSONNodeValue arrayProc() { | |
JSONArray array = new JSONArray([]); | |
JSONNodeValue jsonNodeValue = new JSONNodeValue(JSONNodeValueType.Array); | |
if (ch == "[") { | |
next("["); | |
white(); | |
if (ch == "]") { | |
next("]"); | |
jsonNodeValue.setValue(array); | |
return jsonNodeValue; | |
} | |
while (ch) { | |
array.addValue(valueProc()); | |
white(); | |
if (ch == "]") { | |
next("]"); | |
jsonNodeValue.setValue(array); | |
return jsonNodeValue; | |
} | |
next(","); | |
white; | |
} | |
} | |
throw new Error("Bad Array"); | |
} | |
JSONNodeValue objectProc() { | |
string key; | |
JSONNodeValue jsonNodeValue = new JSONNodeValue(JSONNodeValueType.JSONObject); | |
JSONObject object = new JSONObject; | |
if (ch == "{") { | |
next("{"); | |
white; | |
if (ch == "}") { | |
next("}"); | |
jsonNodeValue.setValue(object); | |
return jsonNodeValue; | |
} | |
while (ch) { | |
key = stringProc().getValue!(JSONNodeValueType.String); | |
white; | |
next(":"); | |
object.addNode(new JSONNode(key, valueProc())); | |
white; | |
if (ch == "}") { | |
next("}"); | |
jsonNodeValue.setValue(object); | |
return jsonNodeValue; | |
} | |
next(","); | |
white(); | |
} | |
} | |
throw new Error("Bad Object"); | |
} | |
JSONNodeValue valueProc() { | |
white; | |
switch (ch) { | |
case "{": | |
return objectProc; | |
case "[": | |
return arrayProc; | |
case "\"": | |
return stringProc; | |
case "-": | |
return numberProc; | |
default: | |
return ("0" <= ch && ch <= "9") ? numberProc : wordProc; | |
} | |
} | |
//test | |
unittest() { | |
import std.stdio; | |
text = `{ | |
"string":"string", | |
"integer": 123, | |
"float": 1.23, | |
"bool":true, | |
"array": ["string", 123, 1.23], | |
"object": { | |
"string":"string", | |
"integer": 123 | |
} | |
}`; | |
at = 0; | |
ch = " "; | |
JSONNodeValue result = valueProc; | |
white; | |
if (ch) { | |
writeln("syntaxError"); | |
} | |
JSONObject json = result.getValue!(JSONNodeValueType.JSONObject); | |
dumpJSONString(json); | |
/* | |
TODO: Fix awful syntax(too many uses of getValue): | |
writeln(typeid( | |
json.getValue["bool"].getValue.getValue!(JSONNodeValueType.Boolean) | |
)); | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment