I’m writing a language that interprets JSON as a direct representation of AST, with the AST structured as a simple tree of expressions, as illustrated below:
{
"expr": {
"type": "arith-add",
"deps": [
{
"expr": {
"type": "num",
"repr": 1
}
},
{
"expr": {
"type": "num",
"repr": null // mistake, would be nice to retrieve line:col of the `null`
}
}
]
}
}
As you can see, the user may potentially make a mistake by writing JSON improperly relative to the expected structure (not relative to the JSON syntax). Therefore, my question is how to utilize the std.json
library to parse these dynamic structures and, in the event of an ill-formed structure, identify the location where the error occurs in order to provide a helpful hint to the user.
What I currently know is how to traverse JSON tree “dynamically” (as people often refer to it):
const tree = try std.json.parseFromSlice(std.json.Value, alloc, file, .{});
switch(tree.value) {
// .array, .object, etc.
}
Additionally, I know how to print “diagnostics” in case a JSON does not comply the specified Zig type:
// Provided by @garrisonhh from Discord
const std = @import("std");
const alloc = std.heap.c_allocator;
const Type = []const []const u8;
pub fn main() !void {
var scanner = std.json.Scanner.initCompleteInput(alloc,
\\["a", "b", {"this": "breaks parsing"}]
);
defer scanner.deinit();
var diag = std.json.Diagnostics{};
scanner.enableDiagnostics(&diag);
const parsed = std.json.parseFromTokenSource(Type, alloc, &scanner, .{}) catch {
std.log.debug("parsing failed at {d}:{d}\n", .{ diag.getLine(), diag.getColumn() });
std.process.exit(1);
};
defer parsed.deinit();
std.log.debug("{any}", .{parsed.value});
}
The rest is unknown but it feels like std.json
is quite complex and can cover the desired functionality.