diff --git a/spec/json_types.d b/spec/json_types.d new file mode 100644 index 0000000..a8d455b --- /dev/null +++ b/spec/json_types.d @@ -0,0 +1,63 @@ +class JSONValue +{ +} + +class JSONObject : JSONValue +{ + JSONValue[string] value; + + this() + { + } + + this(JSONValue[string] value) + { + this.value = value; + } +} + +class JSONArray : JSONValue +{ + JSONValue[] value; + + this() + { + } + + this(JSONValue[] value) + { + this.value = value; + } +} + +class JSONNumber : JSONValue +{ + double value; + + this(double value) + { + this.value = value; + } +} + +class JSONString : JSONValue +{ + string value; + + this(string value) + { + this.value = value; + } +} + +class JSONTrue : JSONValue +{ +} + +class JSONFalse : JSONValue +{ +} + +class JSONNull : JSONValue +{ +} diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index 22eec6e..c336946 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -334,4 +334,190 @@ EOF expect(results.stderr).to eq "" expect(results.status).to eq 0 end + + it "allows creating a JSON parser" do + write_grammar <> + +ptype JSONValue; +ptype array = JSONValue[]; +ptype dict = JSONValue[string]; +ptype string = string; + +drop /\\s+/; +token lbrace /\\{/; +token rbrace /\\}/; +token lbracket /\\[/; +token rbracket /\\]/; +token comma /,/; +token colon /:/; +token number /-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][-+]?[0-9]+)?/ << + double n; + bool negative; + size_t i = 0u; + if (match[i] == '-') + { + negative = true; + i++; + } + while ('0' <= match[i] && match[i] <= '9') + { + n *= 10.0; + n += (match[i] - '0'); + i++; + } + if (match[i] == '.') + { + i++; + double mult = 0.1; + while ('0' <= match[i] && match[i] <= '9') + { + n += mult * (match[i] - '0'); + mult /= 10.0; + i++; + } + } + if (match[i] == 'e' || match[i] == 'E') + { + bool exp_negative; + i++; + if (match[i] == '-') + { + exp_negative = true; + i++; + } + else if (match[i] == '+') + { + i++; + } + long exp; + while ('0' <= match[i] && match[i] <= '9') + { + exp *= 10; + exp += (match[i] - '0'); + i++; + } + if (exp_negative) + { + exp = -exp; + } + n = pow(n, exp); + } + if (negative) + { + n = -n; + } + $$ = new JSONNumber(n); +>> +token true << + $$ = new JSONTrue(); +>> +token false << + $$ = new JSONFalse(); +>> +token null << + $$ = new JSONNull(); +>> +/"/ << + $mode(string); + string_value = ""; +>> +string: token string (string) /"/ << + $$ = string_value; + $mode(default); +>> +string: /\\\\"/ << + string_value ~= "\\""; +>> +string: /\\\\\\\\/ << + string_value ~= "\\\\"; +>> +string: /\\\\\\// << + string_value ~= "/"; +>> +string: /\\\\b/ << + string_value ~= "\\b"; +>> +string: /\\\\f/ << + string_value ~= "\\f"; +>> +string: /\\\\n/ << + string_value ~= "\\n"; +>> +string: /\\\\r/ << + string_value ~= "\\r"; +>> +string: /\\\\t/ << + string_value ~= "\\t"; +>> +string: /\\\\u[0-9a-fA-F]{4}/ << + /* Not actually going to encode the code point for this example... */ + string_value ~= "{" ~ match[2..6] ~ "}"; +>> +string: /[^\\\\]/ << + string_value ~= match; +>> +Start -> Value << + $$ = $1; +>> +Value -> string << + $$ = new JSONString($1); +>> +Value -> number << + $$ = $1; +>> +Value -> Object << + $$ = $1; +>> +Value -> Array << + $$ = $1; +>> +Value -> true << + $$ = $1; +>> +Value -> false << + $$ = $1; +>> +Value -> null << + $$ = $1; +>> +Object -> lbrace rbrace << + $$ = new JSONObject(); +>> +Object -> lbrace KeyValues rbrace << + $$ = new JSONObject($2); +>> +KeyValues (dict) -> KeyValue << + $$ = $1; +>> +KeyValues -> KeyValues comma KeyValue << + foreach (key, value; $3) + { + $1[key] = value; + } + $$ = $1; +>> +KeyValue (dict) -> string colon Value << + $$ = [$1: $3]; +>> +Array -> lbracket rbracket << + $$ = new JSONArray(); +>> +Array -> lbracket Values rbracket << + $$ = new JSONArray($2); +>> +Values (array) -> Value << + $$ = [$1]; +>> +Values -> Values comma Value << + $$ = $1 ~ [$3]; +>> +EOF + build_parser + compile("spec/test_parsing_json.d", "spec/json_types.d") + end end diff --git a/spec/test_parsing_json.d b/spec/test_parsing_json.d new file mode 100644 index 0000000..3ad4abc --- /dev/null +++ b/spec/test_parsing_json.d @@ -0,0 +1,55 @@ +import testparser; +import std.stdio; +import json_types; + +int main() +{ + return 0; +} + +unittest +{ + string input = ``; + auto parser = new Testparser.Parser(input); + assert(parser.parse()); + + input = `{}`; + parser = new Testparser.Parser(input); + assert(parser.parse()); + assert(cast(JSONObject)parser.result); + + input = `[]`; + parser = new Testparser.Parser(input); + assert(parser.parse()); + assert(cast(JSONArray)parser.result); + + input = `-45.6`; + parser = new Testparser.Parser(input); + assert(parser.parse()); + assert(cast(JSONNumber)parser.result); + assert((cast(JSONNumber)parser.result).value == -45.6); + + input = `2E-2`; + parser = new Testparser.Parser(input); + assert(parser.parse()); + assert(cast(JSONNumber)parser.result); + assert((cast(JSONNumber)parser.result).value == 0.02); + + input = `{"hi":true}`; + parser = new Testparser.Parser(input); + assert(parser.parse()); + assert(cast(JSONObject)parser.result); + JSONObject o = cast(JSONObject)parser.result; + assert(o.value["hi"]); + assert(cast(JSONTrue)o.value["hi"]); + + input = `{"ff": false, "nn": null}`; + parser = new Testparser.Parser(input); + assert(parser.parse()); + assert(cast(JSONObject)parser.result); + o = cast(JSONObject)parser.result; + assert(o.value["ff"]); + assert(cast(JSONFalse)o.value["ff"]); + assert(o.value["nn"]); + assert(cast(JSONNull)o.value["nn"]); +}