Add JSON parser test case

This commit is contained in:
Josh Holtrop 2022-10-16 20:17:48 -04:00
parent 62451f3a92
commit dbc5560aec
3 changed files with 304 additions and 0 deletions

63
spec/json_types.d Normal file
View File

@ -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
{
}

View File

@ -334,4 +334,190 @@ EOF
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "allows creating a JSON parser" do
write_grammar <<EOF
<<
import std.math;
import json_types;
string string_value;
>>
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 end

55
spec/test_parsing_json.d Normal file
View File

@ -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"]);
}