1 module json;
2 
3 import pry;
4 
5 import std.stdio, std.conv, std.uni, std.variant, std.typecons,
6 	std.json, std.datetime;;
7 import file = std.file;
8 import stdx.data.json;
9 
10 import taggedalgebraic;
11 
12 struct JSValue {
13 	union Base {
14 		typeof(null) null_;
15 		bool boolean;
16 		double num;
17 		string str;
18 		JSValue[] array;
19 		JSValue[string] object;
20 	}
21 	alias Payload = TaggedAlgebraic!Base;
22 	Payload payload;
23 	alias payload this;
24 	this(T)(T value){ payload = Payload(value); }
25 }
26 
27 alias S = SimpleStream!string;
28 
29 enum allowedChars =
30 		unicode.Cc.add('"', '"'+1).add('\\', '\\'+1).inverted;
31 enum hex = CodepointSet('0', '9'+1, 'a', 'f'+1, 'A', 'F'+1);
32 
33 auto jsString(){
34 	with(parsers!S) {
35 		auto unescaped_string = set!allowedChars.rep!0;
36 		auto escaped_string = any(
37 			set!allowedChars,
38 			seq(tk!'\\', any(
39 				tk!'"',
40 				tk!'\\',
41 				tk!'/',
42 				tk!'b'.map!(_ => cast(dchar)'\b'),
43 				tk!'f'.map!(_ => cast(dchar)'\f'),
44 				tk!'n'.map!(_ => cast(dchar)'\n'),
45 				tk!'r'.map!(_ => cast(dchar)'\r'),
46 				tk!'t'.map!(_ => cast(dchar)'\t'),
47 				seq(tk!'u', set!hex.rep!(4,4)).map!(x => cast(dchar)to!int(x[1], 16))
48 			)).map!(x => x[1])
49 		).utfString!(char, 0);
50 		auto full = seq(tk!'"', any(unescaped_string, escaped_string), tk!'"');
51 		return full.skipWs.map!(x => x[1]);
52 	}
53 }
54 
55 unittest {
56 	assert(`""`.parse(jsString) == "");
57 	assert(`"abc"`.parse(jsString) == "abc");
58 }
59 
60 auto jsNumber(){
61 	with(parsers!S) {
62 		auto digit = range!('0','9');
63 		return seq(
64 			tk!'-'.optional,
65 			any(
66 				tk!'0',
67 				seq(range!('1', '9'), digit.rep!0)
68 			), // got to skip whitespace in front of tokens
69 			// optional fraction
70 			seq(
71 				tk!'.',
72 				digit.rep!1
73 			).optional,
74 			// optional exponent
75 			seq(
76 				any(tk!'e', tk!'E'),
77 				any(tk!'+', tk!'-').optional,
78 				digit.rep!1
79 			).optional
80 		).slice.skipWs.map!(x => to!double(x));
81 	}
82 }
83 
84 unittest{
85 	assert("0".parse(jsNumber) == 0);
86 	assert(" -1e2".parse(jsNumber) == -100);
87 	assert("3.1415".parse(jsNumber) == 3.1415);
88 }
89 
90 auto jsonParser(){
91 	with(parsers!S) {
92 		auto jsValue = dynamic!JSValue;
93 		auto pair = seq(jsString, stk!':', jsValue).map!(x => tuple(x[0], x[2]));
94 		auto jsObject = seq(
95 			tk!'{',
96 			seq(
97 				pair, seq(stk!',', pair).map!(x => x[1]).aa!0
98 			).optional.map!((x){
99 				if(x.isNull) return null;
100 				auto head = x[0];
101 				auto aa = x[1];
102 				aa[head[0]] = head[1];
103 				return aa;
104 			}),
105 			stk!'}'
106 		).skipWs.map!(x => x[1]);
107 		auto jsArray = seq(
108 			tk!'[',
109 			delimited(jsValue, stk!','),
110 			stk!']'
111 		).skipWs.map!(x => x[1]);
112 		jsValue = any(
113 			jsString.map!(x => JSValue(x)),
114 			jsNumber.map!(x => JSValue(x)),
115 			jsObject.map!(x => JSValue(x)),
116 			jsArray.map!(x => JSValue(x)),
117 			literal!"true".map!(x => JSValue(true)),
118 			literal!"false".map!(x => JSValue(false)),
119 			literal!"null".map!(x => JSValue(null))
120 		);
121 		return jsValue;
122 	}
123 }
124 
125 unittest {
126 	auto v = `{ "a": 12, "b": [1,2,3 ], "c" : true, "null" : null }`.parse(jsonParser);
127 	assert(v["a"] == 12);
128 	assert(v["b"] == [ 1, 2, 3 ]);
129 	assert(v["c"] == true);
130 	assert(v["null"] == null);
131 }
132 
133 void parseStd(string data){
134 	parseJSON(data);
135 }
136 
137 void parsePry(string data){
138 	data.parse(jsonParser);
139 }
140 
141 void parseDataJson(string data){
142 	toJSONValue(data);
143 }
144 
145 void main(string[] argv){
146 	void usage(){
147 		writeln("Usage:\n./json <file>");
148 	}
149 	if(argv.length != 2) return usage();
150 	const iterations = 1_000;
151 	string data = cast(string)file.read(argv[1]);
152 	version(std_json){
153 		string name = "std.json";
154 		auto dg = () => parseStd(data);
155 	}
156 	else version(pry){
157 		string name = "pry";
158 		auto dg = () => parsePry(data);
159 	}
160 	else version(stdx_data_json){
161 		string name = "stdx.data.json";
162 		auto dg = () => parseDataJson(data);
163 	}
164 	else {
165 		static assert(false, "define one version of std_json, pry, stdx_data_json");
166 	}
167 	auto results = benchmark!(dg)(iterations);
168 	writefln("%s: %s us", name,
169 		results[0].usecs / cast(double)iterations);
170 }