diff --git a/src/json.zig b/src/json.zig index 02d5ee3..225ecb0 100644 --- a/src/json.zig +++ b/src/json.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); pub const JSONError = error{ IncorrectType, @@ -16,17 +17,43 @@ pub const JSONType = enum { Null, }; +pub const JSONFormat = struct { + indent_level: usize = 4, + indent_char: u8 = ' ', + one_line: bool = false, +}; + +fn printIndent(writer: std.fs.File.Writer, format: JSONFormat, level: usize) !void { + if (!format.one_line) { + try writer.writeByte('\n'); + } + + for (0..level) |_| { + try writer.writeByteNTimes(format.indent_char, format.indent_level); + } +} + +fn indentString(allocator: std.mem.Allocator, format: JSONFormat, level: usize) ![]const u8 { + var str = std.ArrayList(u8).init(allocator); + + if (!format.one_line) { + try str.append('\n'); + } + + for (0..level) |_| { + try str.appendNTimes(format.indent_char, format.indent_level); + } + + return try str.toOwnedSlice(); +} + pub const JSONObject = struct { allocator: std.mem.Allocator, children: MapType, const MapType = std.StringHashMap(*JSONValue); - pub fn get(self: *JSONObject, name: []const u8) ?*JSONValue { - return self.children.get(name); - } - - fn init(allocator: std.mem.Allocator) JSONError!*JSONObject { + pub fn create(allocator: std.mem.Allocator) JSONError!*JSONObject { var self = try allocator.create(JSONObject); self.allocator = allocator; self.children = MapType.init(allocator); @@ -34,6 +61,59 @@ pub const JSONObject = struct { return self; } + pub fn get(self: *JSONObject, name: []const u8) ?*JSONValue { + return self.children.get(name); + } + + pub fn print(self: *JSONObject, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void { + try writer.writeByte('{'); + + var iter = self.children.keyIterator(); + var first = true; + while (iter.next()) |key| { + if (!first) { + try writer.writeAll(", "); + } + + try printIndent(writer, format, indent_level + 1); + try writer.print("\"{s}\": ", .{key.*}); + + try self.children.get(key.*).?.print(writer, format, indent_level + 1); + first = false; + } + + try printIndent(writer, format, indent_level); + try writer.writeByte('}'); + } + + pub fn toString(self: *JSONObject, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 { + var str = std.ArrayList(u8).init(allocator); + try str.append('{'); + + var iter = self.children.keyIterator(); + var first = true; + while (iter.next()) |key| { + if (!first) { + try str.appendSlice(", "); + } + + try str.appendSlice(try indentString(allocator, format, indent_level + 1)); + try str.append('"'); + try str.appendSlice(key.*); + try str.appendSlice("\": "); + + const valstr = try self.children.get(key.*).?.toString(allocator, format, indent_level + 1); + defer allocator.free(valstr); + try str.appendSlice(valstr); + first = false; + } + + try str.appendSlice(try indentString(allocator, format, indent_level)); + try str.append('}'); + + return try str.toOwnedSlice(); + } + pub fn deinit(self: *JSONObject) void { var iter = self.children.valueIterator(); while (iter.next()) |child| { @@ -41,9 +121,10 @@ pub const JSONObject = struct { } var names = self.children.keyIterator(); - while (names.next()) |name| { - self.allocator.free(name.*); + while (names.next()) |key| { + self.allocator.free(key.*); } + self.children.deinit(); self.allocator.destroy(self); } @@ -63,6 +144,9 @@ pub const JSONValue = struct { getBool: *const fn (self: *const JSONValue) JSONError!bool = incorrectValue(bool), deinit: *const fn (self: *JSONValue) void, + + print: *const fn (self: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void, + toString: *const fn (self: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8, }; fn incorrectValue(comptime T: type) *const fn (*const JSONValue) JSONError!T { @@ -97,34 +181,50 @@ pub const JSONValue = struct { return self.vtable.getObject(self); } + pub fn print(self: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void { + return self.vtable.print(self, writer, format, indent_level); + } + + pub fn toString(self: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 { + return self.vtable.toString(self, allocator, format, indent_level); + } + pub fn deinit(self: *JSONValue) void { self.vtable.deinit(self); } - fn createNull(allocator: std.mem.Allocator) JSONError!*JSONValue { + pub fn createNull(allocator: std.mem.Allocator) JSONError!*JSONValue { var self = try allocator.create(JSONValue); self.type = .Null; - self.vtable = .{ .deinit = basicDeinit }; + self.vtable = .{ .deinit = basicDeinit, .print = printNull, .toString = toStringNull }; self.allocator = allocator; return self; } + fn printNull(_: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void { + return writer.writeAll("null"); + } + + fn toStringNull(_: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) ![]const u8 { + return allocator.dupe(u8, "null"); + } + fn basicDeinit(self: *JSONValue) void { self.allocator.destroy(self); } }; -const JSONArrayValue = struct { +pub const JSONArrayValue = struct { value: JSONValue, array: []*JSONValue, - fn init(allocator: std.mem.Allocator, array: []*JSONValue) !*JSONValue { + pub fn create(allocator: std.mem.Allocator, array: []*JSONValue) !*JSONValue { var self = try allocator.create(JSONArrayValue); self.array = array; self.value = .{ .type = .Array, - .vtable = .{ .getArray = getValue, .deinit = deinit }, + .vtable = .{ .getArray = getValue, .print = print, .toString = toString, .deinit = deinit }, .allocator = allocator, }; @@ -136,6 +236,47 @@ const JSONArrayValue = struct { return self.array; } + fn print(value: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void { + const self: *const JSONArrayValue = @fieldParentPtr("value", value); + + try writer.writeAll("[ "); + + var first = true; + for (self.array) |val| { + if (!first) { + try writer.writeAll(", "); + } + + try val.print(writer, format, indent_level); + first = false; + } + + try writer.writeAll(" ]"); + } + + fn toString(value: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 { + const self: *const JSONArrayValue = @fieldParentPtr("value", value); + var str = std.ArrayList(u8).init(allocator); + + try str.appendSlice("[ "); + + var first = true; + for (self.array) |val| { + if (!first) { + try str.appendSlice(", "); + } + + const valstr = try val.toString(allocator, format, indent_level); + defer allocator.free(valstr); + try str.appendSlice(valstr); + first = false; + } + + try str.appendSlice(" ]"); + + return str.toOwnedSlice(); + } + fn deinit(value: *JSONValue) void { var self: *JSONArrayValue = @fieldParentPtr("value", value); for (self.array) |val| { @@ -146,16 +287,16 @@ const JSONArrayValue = struct { } }; -const JSONObjectValue = struct { +pub const JSONObjectValue = struct { value: JSONValue, object: *JSONObject, - fn init(allocator: std.mem.Allocator, object: *JSONObject) !*JSONValue { + pub fn create(allocator: std.mem.Allocator, object: *JSONObject) !*JSONValue { var self = try allocator.create(JSONObjectValue); self.object = object; self.value = .{ .type = .Object, - .vtable = .{ .getObject = getValue, .deinit = deinit }, + .vtable = .{ .getObject = getValue, .print = print, .toString = toString, .deinit = deinit }, .allocator = allocator, }; @@ -167,6 +308,16 @@ const JSONObjectValue = struct { return self.object; } + fn print(value: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void { + const self: *const JSONObjectValue = @fieldParentPtr("value", value); + try self.object.print(writer, format, indent_level); + } + + fn toString(value: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 { + const self: *const JSONObjectValue = @fieldParentPtr("value", value); + return self.object.toString(allocator, format, indent_level); + } + fn deinit(value: *JSONValue) void { var self: *const JSONObjectValue = @fieldParentPtr("value", value); self.object.deinit(); @@ -174,16 +325,16 @@ const JSONObjectValue = struct { } }; -const JSONIntValue = struct { +pub const JSONIntValue = struct { value: JSONValue, int: isize, - fn init(allocator: std.mem.Allocator, value: isize) !*JSONValue { + pub fn create(allocator: std.mem.Allocator, value: isize) !*JSONValue { var self = try allocator.create(JSONIntValue); self.int = value; self.value = .{ .type = .Int, - .vtable = .{ .getInt = getValue, .deinit = deinit }, + .vtable = .{ .getInt = getValue, .print = print, .toString = toString, .deinit = deinit }, .allocator = allocator, }; @@ -195,22 +346,32 @@ const JSONIntValue = struct { return self.int; } + fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void { + const self: *const JSONIntValue = @fieldParentPtr("value", value); + try writer.print("{d}", .{self.int}); + } + + fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 { + const self: *const JSONIntValue = @fieldParentPtr("value", value); + return std.fmt.allocPrint(allocator, "{d}", .{self.int}); + } + fn deinit(value: *JSONValue) void { var self: *JSONIntValue = @fieldParentPtr("value", value); self.value.allocator.destroy(self); } }; -const JSONFloatValue = struct { +pub const JSONFloatValue = struct { value: JSONValue, float: f64, - fn init(allocator: std.mem.Allocator, value: f64) !*JSONValue { + pub fn create(allocator: std.mem.Allocator, value: f64) !*JSONValue { var self = try allocator.create(JSONFloatValue); self.float = value; self.value = .{ .type = .Float, - .vtable = .{ .getFloat = getValue, .deinit = deinit }, + .vtable = .{ .getFloat = getValue, .print = print, .toString = toString, .deinit = deinit }, .allocator = allocator, }; @@ -222,22 +383,32 @@ const JSONFloatValue = struct { return self.float; } + fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void { + const self: *const JSONFloatValue = @fieldParentPtr("value", value); + try writer.print("{d}", .{self.float}); + } + + fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 { + const self: *const JSONFloatValue = @fieldParentPtr("value", value); + return std.fmt.allocPrint(allocator, "{d}", .{self.float}); + } + fn deinit(value: *JSONValue) void { - var self: *JSONIntValue = @fieldParentPtr("value", value); + var self: *const JSONFloatValue = @fieldParentPtr("value", value); self.value.allocator.destroy(self); } }; -const JSONBoolValue = struct { +pub const JSONBoolValue = struct { value: JSONValue, boolean: bool, - fn init(allocator: std.mem.Allocator, value: bool) !*JSONValue { + pub fn create(allocator: std.mem.Allocator, value: bool) !*JSONValue { var self = try allocator.create(JSONBoolValue); self.boolean = value; self.value = .{ .type = .Bool, - .vtable = .{ .getBool = getValue, .deinit = deinit }, + .vtable = .{ .getBool = getValue, .print = print, .toString = toString, .deinit = deinit }, .allocator = allocator, }; @@ -249,6 +420,24 @@ const JSONBoolValue = struct { return self.boolean; } + fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void { + const self: *const JSONBoolValue = @fieldParentPtr("value", value); + if (self.boolean) { + try writer.writeAll("true"); + } else { + try writer.writeAll("false"); + } + } + + fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 { + const self: *const JSONBoolValue = @fieldParentPtr("value", value); + if (self.boolean) { + return try allocator.dupe(u8, "true"); + } else { + return try allocator.dupe(u8, "false"); + } + } + fn deinit(value: *JSONValue) void { var self: *JSONBoolValue = @fieldParentPtr("value", value); self.value.allocator.destroy(self); @@ -259,12 +448,12 @@ pub const JSONStringValue = struct { value: JSONValue, string: []const u8, - pub fn init(allocator: std.mem.Allocator, string: []const u8) !*JSONValue { + pub fn create(allocator: std.mem.Allocator, string: []const u8) !*JSONValue { var self = try allocator.create(JSONStringValue); self.string = string; self.value = .{ .type = .String, - .vtable = .{ .getString = getValue, .deinit = deinit }, + .vtable = .{ .getString = getValue, .print = print, .toString = toString, .deinit = deinit }, .allocator = allocator, }; @@ -276,6 +465,16 @@ pub const JSONStringValue = struct { return self.string; } + fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void { + const self: *const JSONStringValue = @fieldParentPtr("value", value); + try writer.print("\"{s}\"", .{self.string}); + } + + fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 { + const self: *const JSONStringValue = @fieldParentPtr("value", value); + return std.fmt.allocPrint(allocator, "\"{s}\"", .{self.string}); + } + fn deinit(value: *JSONValue) void { var self: *JSONStringValue = @fieldParentPtr("value", value); self.value.allocator.free(self.string); @@ -300,6 +499,14 @@ pub fn parseString(allocator: std.mem.Allocator, string: []const u8) !*JSONObjec return parser.parse(); } +pub fn writeToFile(fileName: []const u8, json: *JSONObject, format: JSONFormat) !void { + var file = try std.fs.cwd().createFile(fileName, .{}); + defer file.close(); + const writer = file.writer(); + + return json.print(writer, format, 0); +} + const JSONParser = struct { allocator: std.mem.Allocator, iter: std.unicode.Utf8Iterator, @@ -354,7 +561,7 @@ const JSONParser = struct { } fn parseObject(self: *JSONParser) JSONError!*JSONObject { - const obj = try JSONObject.init(self.allocator); + const obj = try JSONObject.create(self.allocator); errdefer obj.deinit(); try self.expectNext('{'); @@ -427,7 +634,7 @@ const JSONParser = struct { '"' => { //std.debug.print("Parsing string\n", .{}); const str = try self.parseString(); - value = try JSONStringValue.init(self.allocator, str); + value = try JSONStringValue.create(self.allocator, str); }, '-', '0'...'9' => { //std.debug.print("Parsing number\n", .{}); @@ -436,17 +643,17 @@ const JSONParser = struct { '{' => { //std.debug.print("Parsing object\n", .{}); const obj = try self.parseObject(); - value = try JSONObjectValue.init(self.allocator, obj); + value = try JSONObjectValue.create(self.allocator, obj); }, '[' => { //std.debug.print("Parsing array\n", .{}); const arr = try self.parseArray(); - value = try JSONArrayValue.init(self.allocator, arr); + value = try JSONArrayValue.create(self.allocator, arr); }, 't', 'f' => { //std.debug.print("Parsing bool\n", .{}); const b = try self.parseBool(); - value = try JSONBoolValue.init(self.allocator, b); + value = try JSONBoolValue.create(self.allocator, b); }, 'n' => { //std.debug.print("Parsing null\n", .{}); @@ -640,10 +847,10 @@ const JSONParser = struct { value *= std.math.pow(f64, 10.0, @floatFromInt(exp)); } - return try JSONFloatValue.init(self.allocator, value); + return try JSONFloatValue.create(self.allocator, value); } else { const value: isize = std.fmt.parseInt(isize, buf.items, 10) catch unreachable; - return try JSONIntValue.init(self.allocator, value); + return try JSONIntValue.create(self.allocator, value); } } }; @@ -892,3 +1099,8 @@ test "file parsing" { const root = try parseFile(std.testing.allocator, "test/test.json"); root.deinit(); } + +test "123" { + var root = try JSONObject.create(std.testing.allocator); + defer root.deinit(); +} diff --git a/src/main.zig b/src/main.zig index 1bfecf7..b5eb517 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,12 +8,14 @@ pub fn main() void { } var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); defer { if (gpa.deinit() == .leak) { std.debug.print("Memory was leaked D:\n", .{}); } } + var arena = std.heap.ArenaAllocator.init(gpa.allocator()); + defer arena.deinit(); + const allocator = arena.allocator(); const fileName = std.mem.span(std.os.argv[1]); @@ -21,10 +23,18 @@ pub fn main() void { std.debug.print("Failed to parse json: {any}\n", .{err}); std.process.exit(2); }; - defer root.deinit(); + //defer root.deinit(); printObject(root, 0); std.debug.print("\n", .{}); + + const stdout = std.io.getStdOut(); + const writer = stdout.writer(); + root.print(writer, .{}, 0) catch {}; + std.debug.print("\n", .{}); + + const json_str = root.toString(allocator, .{}, 0) catch return; + std.debug.print("{s}\n", .{json_str}); } fn printIndent(indent: usize) void {