Skip to content

Instantly share code, notes, and snippets.

@alphaKAI
Last active June 21, 2016 10:09
Show Gist options
  • Save alphaKAI/b8c8853602b2c58a552a3942831c7506 to your computer and use it in GitHub Desktop.
Save alphaKAI/b8c8853602b2c58a552a3942831c7506 to your computer and use it in GitHub Desktop.
Yet Another JSON Parser for D[WIP].
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 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