| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | // https://tools.ietf.org/html/rfc8259 |
| 4 | 4 | |
| 5 | 5 | const std = @import("index.zig"); |
| 6 | const debug = std.debug; |
| 6 | 7 | const mem = std.mem; |
| 7 | 8 | |
| 8 | 9 | const u1 = @IntType(false, 1); |
| ... | ... | @@ -86,7 +87,9 @@ pub const Token = struct { |
| 86 | 87 | // parsing state requires ~40-50 bytes of stack space. |
| 87 | 88 | // |
| 88 | 89 | // Conforms strictly to RFC8529. |
| 89 | | pub const StreamingJsonParser = struct { |
| 90 | // |
| 91 | // For a non-byte based wrapper, consider using TokenStream instead. |
| 92 | pub const StreamingParser = struct { |
| 90 | 93 | // Current state |
| 91 | 94 | state: State, |
| 92 | 95 | // How many bytes we have counted for the current token |
| ... | ... | @@ -109,13 +112,13 @@ pub const StreamingJsonParser = struct { |
| 109 | 112 | const array_bit = 1; |
| 110 | 113 | const max_stack_size = @maxValue(u8); |
| 111 | 114 | |
| 112 | | pub fn init() StreamingJsonParser { |
| 113 | | var p: StreamingJsonParser = undefined; |
| 115 | pub fn init() StreamingParser { |
| 116 | var p: StreamingParser = undefined; |
| 114 | 117 | p.reset(); |
| 115 | 118 | return p; |
| 116 | 119 | } |
| 117 | 120 | |
| 118 | | pub fn reset(p: *StreamingJsonParser) void { |
| 121 | pub fn reset(p: *StreamingParser) void { |
| 119 | 122 | p.state = State.TopLevelBegin; |
| 120 | 123 | p.count = 0; |
| 121 | 124 | // Set before ever read in main transition function |
| ... | ... | @@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct { |
| 175 | 178 | |
| 176 | 179 | // Only call this function to generate array/object final state. |
| 177 | 180 | pub fn fromInt(x: var) State { |
| 178 | | std.debug.assert(x == 0 or x == 1); |
| 181 | debug.assert(x == 0 or x == 1); |
| 179 | 182 | const T = @TagType(State); |
| 180 | 183 | return State(T(x)); |
| 181 | 184 | } |
| ... | ... | @@ -205,7 +208,7 @@ pub const StreamingJsonParser = struct { |
| 205 | 208 | // tokens. token2 is always null if token1 is null. |
| 206 | 209 | // |
| 207 | 210 | // There is currently no error recovery on a bad stream. |
| 208 | | pub fn feed(p: *StreamingJsonParser, c: u8, token1: *?Token, token2: *?Token) Error!void { |
| 211 | pub fn feed(p: *StreamingParser, c: u8, token1: *?Token, token2: *?Token) Error!void { |
| 209 | 212 | token1.* = null; |
| 210 | 213 | token2.* = null; |
| 211 | 214 | p.count += 1; |
| ... | ... | @@ -217,7 +220,7 @@ pub const StreamingJsonParser = struct { |
| 217 | 220 | } |
| 218 | 221 | |
| 219 | 222 | // Perform a single transition on the state machine and return any possible token. |
| 220 | | fn transition(p: *StreamingJsonParser, c: u8, token: *?Token) Error!bool { |
| 223 | fn transition(p: *StreamingParser, c: u8, token: *?Token) Error!bool { |
| 221 | 224 | switch (p.state) { |
| 222 | 225 | State.TopLevelBegin => switch (c) { |
| 223 | 226 | '{' => { |
| ... | ... | @@ -852,10 +855,116 @@ pub const StreamingJsonParser = struct { |
| 852 | 855 | } |
| 853 | 856 | }; |
| 854 | 857 | |
| 858 | // A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens. |
| 859 | pub const TokenStream = struct { |
| 860 | i: usize, |
| 861 | slice: []const u8, |
| 862 | parser: StreamingParser, |
| 863 | token: ?Token, |
| 864 | |
| 865 | pub fn init(slice: []const u8) TokenStream { |
| 866 | return TokenStream{ |
| 867 | .i = 0, |
| 868 | .slice = slice, |
| 869 | .parser = StreamingParser.init(), |
| 870 | .token = null, |
| 871 | }; |
| 872 | } |
| 873 | |
| 874 | pub fn next(self: *TokenStream) !?Token { |
| 875 | if (self.token) |token| { |
| 876 | self.token = null; |
| 877 | return token; |
| 878 | } |
| 879 | |
| 880 | var t1: ?Token = undefined; |
| 881 | var t2: ?Token = undefined; |
| 882 | |
| 883 | while (self.i < self.slice.len) { |
| 884 | try self.parser.feed(self.slice[self.i], &t1, &t2); |
| 885 | self.i += 1; |
| 886 | |
| 887 | if (t1) |token| { |
| 888 | self.token = t2; |
| 889 | return token; |
| 890 | } |
| 891 | } |
| 892 | |
| 893 | if (self.i > self.slice.len) { |
| 894 | try self.parser.feed(' ', &t1, &t2); |
| 895 | self.i += 1; |
| 896 | |
| 897 | if (t1) |token| { |
| 898 | return token; |
| 899 | } |
| 900 | } |
| 901 | |
| 902 | return null; |
| 903 | } |
| 904 | }; |
| 905 | |
| 906 | fn checkNext(p: *TokenStream, id: Token.Id) void { |
| 907 | const token = ??(p.next() catch unreachable); |
| 908 | debug.assert(token.id == id); |
| 909 | } |
| 910 | |
| 911 | test "token" { |
| 912 | const s = |
| 913 | \\{ |
| 914 | \\ "Image": { |
| 915 | \\ "Width": 800, |
| 916 | \\ "Height": 600, |
| 917 | \\ "Title": "View from 15th Floor", |
| 918 | \\ "Thumbnail": { |
| 919 | \\ "Url": "http://www.example.com/image/481989943", |
| 920 | \\ "Height": 125, |
| 921 | \\ "Width": 100 |
| 922 | \\ }, |
| 923 | \\ "Animated" : false, |
| 924 | \\ "IDs": [116, 943, 234, 38793] |
| 925 | \\ } |
| 926 | \\} |
| 927 | ; |
| 928 | |
| 929 | var p = TokenStream.init(s); |
| 930 | |
| 931 | checkNext(&p, Token.Id.ObjectBegin); |
| 932 | checkNext(&p, Token.Id.String); // Image |
| 933 | checkNext(&p, Token.Id.ObjectBegin); |
| 934 | checkNext(&p, Token.Id.String); // Width |
| 935 | checkNext(&p, Token.Id.Number); |
| 936 | checkNext(&p, Token.Id.String); // Height |
| 937 | checkNext(&p, Token.Id.Number); |
| 938 | checkNext(&p, Token.Id.String); // Title |
| 939 | checkNext(&p, Token.Id.String); |
| 940 | checkNext(&p, Token.Id.String); // Thumbnail |
| 941 | checkNext(&p, Token.Id.ObjectBegin); |
| 942 | checkNext(&p, Token.Id.String); // Url |
| 943 | checkNext(&p, Token.Id.String); |
| 944 | checkNext(&p, Token.Id.String); // Height |
| 945 | checkNext(&p, Token.Id.Number); |
| 946 | checkNext(&p, Token.Id.String); // Width |
| 947 | checkNext(&p, Token.Id.Number); |
| 948 | checkNext(&p, Token.Id.ObjectEnd); |
| 949 | checkNext(&p, Token.Id.String); // Animated |
| 950 | checkNext(&p, Token.Id.False); |
| 951 | checkNext(&p, Token.Id.String); // IDs |
| 952 | checkNext(&p, Token.Id.ArrayBegin); |
| 953 | checkNext(&p, Token.Id.Number); |
| 954 | checkNext(&p, Token.Id.Number); |
| 955 | checkNext(&p, Token.Id.Number); |
| 956 | checkNext(&p, Token.Id.Number); |
| 957 | checkNext(&p, Token.Id.ArrayEnd); |
| 958 | checkNext(&p, Token.Id.ObjectEnd); |
| 959 | checkNext(&p, Token.Id.ObjectEnd); |
| 960 | |
| 961 | debug.assert((try p.next()) == null); |
| 962 | } |
| 963 | |
| 855 | 964 | // Validate a JSON string. This does not limit number precision so a decoder may not necessarily |
| 856 | 965 | // be able to decode the string even if this returns true. |
| 857 | 966 | pub fn validate(s: []const u8) bool { |
| 858 | | var p = StreamingJsonParser.init(); |
| 967 | var p = StreamingParser.init(); |
| 859 | 968 | |
| 860 | 969 | for (s) |c, i| { |
| 861 | 970 | var token1: ?Token = undefined; |
| ... | ... | @@ -897,46 +1006,46 @@ pub const Value = union(enum) { |
| 897 | 1006 | pub fn dump(self: *const Value) void { |
| 898 | 1007 | switch (self.*) { |
| 899 | 1008 | Value.Null => { |
| 900 | | std.debug.warn("null"); |
| 1009 | debug.warn("null"); |
| 901 | 1010 | }, |
| 902 | 1011 | Value.Bool => |inner| { |
| 903 | | std.debug.warn("{}", inner); |
| 1012 | debug.warn("{}", inner); |
| 904 | 1013 | }, |
| 905 | 1014 | Value.Integer => |inner| { |
| 906 | | std.debug.warn("{}", inner); |
| 1015 | debug.warn("{}", inner); |
| 907 | 1016 | }, |
| 908 | 1017 | Value.Float => |inner| { |
| 909 | | std.debug.warn("{.5}", inner); |
| 1018 | debug.warn("{.5}", inner); |
| 910 | 1019 | }, |
| 911 | 1020 | Value.String => |inner| { |
| 912 | | std.debug.warn("\"{}\"", inner); |
| 1021 | debug.warn("\"{}\"", inner); |
| 913 | 1022 | }, |
| 914 | 1023 | Value.Array => |inner| { |
| 915 | 1024 | var not_first = false; |
| 916 | | std.debug.warn("["); |
| 1025 | debug.warn("["); |
| 917 | 1026 | for (inner.toSliceConst()) |value| { |
| 918 | 1027 | if (not_first) { |
| 919 | | std.debug.warn(","); |
| 1028 | debug.warn(","); |
| 920 | 1029 | } |
| 921 | 1030 | not_first = true; |
| 922 | 1031 | value.dump(); |
| 923 | 1032 | } |
| 924 | | std.debug.warn("]"); |
| 1033 | debug.warn("]"); |
| 925 | 1034 | }, |
| 926 | 1035 | Value.Object => |inner| { |
| 927 | 1036 | var not_first = false; |
| 928 | | std.debug.warn("{{"); |
| 1037 | debug.warn("{{"); |
| 929 | 1038 | var it = inner.iterator(); |
| 930 | 1039 | |
| 931 | 1040 | while (it.next()) |entry| { |
| 932 | 1041 | if (not_first) { |
| 933 | | std.debug.warn(","); |
| 1042 | debug.warn(","); |
| 934 | 1043 | } |
| 935 | 1044 | not_first = true; |
| 936 | | std.debug.warn("\"{}\":", entry.key); |
| 1045 | debug.warn("\"{}\":", entry.key); |
| 937 | 1046 | entry.value.dump(); |
| 938 | 1047 | } |
| 939 | | std.debug.warn("}}"); |
| 1048 | debug.warn("}}"); |
| 940 | 1049 | }, |
| 941 | 1050 | } |
| 942 | 1051 | } |
| ... | ... | @@ -952,53 +1061,53 @@ pub const Value = union(enum) { |
| 952 | 1061 | fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void { |
| 953 | 1062 | switch (self.*) { |
| 954 | 1063 | Value.Null => { |
| 955 | | std.debug.warn("null"); |
| 1064 | debug.warn("null"); |
| 956 | 1065 | }, |
| 957 | 1066 | Value.Bool => |inner| { |
| 958 | | std.debug.warn("{}", inner); |
| 1067 | debug.warn("{}", inner); |
| 959 | 1068 | }, |
| 960 | 1069 | Value.Integer => |inner| { |
| 961 | | std.debug.warn("{}", inner); |
| 1070 | debug.warn("{}", inner); |
| 962 | 1071 | }, |
| 963 | 1072 | Value.Float => |inner| { |
| 964 | | std.debug.warn("{.5}", inner); |
| 1073 | debug.warn("{.5}", inner); |
| 965 | 1074 | }, |
| 966 | 1075 | Value.String => |inner| { |
| 967 | | std.debug.warn("\"{}\"", inner); |
| 1076 | debug.warn("\"{}\"", inner); |
| 968 | 1077 | }, |
| 969 | 1078 | Value.Array => |inner| { |
| 970 | 1079 | var not_first = false; |
| 971 | | std.debug.warn("[\n"); |
| 1080 | debug.warn("[\n"); |
| 972 | 1081 | |
| 973 | 1082 | for (inner.toSliceConst()) |value| { |
| 974 | 1083 | if (not_first) { |
| 975 | | std.debug.warn(",\n"); |
| 1084 | debug.warn(",\n"); |
| 976 | 1085 | } |
| 977 | 1086 | not_first = true; |
| 978 | 1087 | padSpace(level + indent); |
| 979 | 1088 | value.dumpIndentLevel(indent, level + indent); |
| 980 | 1089 | } |
| 981 | | std.debug.warn("\n"); |
| 1090 | debug.warn("\n"); |
| 982 | 1091 | padSpace(level); |
| 983 | | std.debug.warn("]"); |
| 1092 | debug.warn("]"); |
| 984 | 1093 | }, |
| 985 | 1094 | Value.Object => |inner| { |
| 986 | 1095 | var not_first = false; |
| 987 | | std.debug.warn("{{\n"); |
| 1096 | debug.warn("{{\n"); |
| 988 | 1097 | var it = inner.iterator(); |
| 989 | 1098 | |
| 990 | 1099 | while (it.next()) |entry| { |
| 991 | 1100 | if (not_first) { |
| 992 | | std.debug.warn(",\n"); |
| 1101 | debug.warn(",\n"); |
| 993 | 1102 | } |
| 994 | 1103 | not_first = true; |
| 995 | 1104 | padSpace(level + indent); |
| 996 | | std.debug.warn("\"{}\": ", entry.key); |
| 1105 | debug.warn("\"{}\": ", entry.key); |
| 997 | 1106 | entry.value.dumpIndentLevel(indent, level + indent); |
| 998 | 1107 | } |
| 999 | | std.debug.warn("\n"); |
| 1108 | debug.warn("\n"); |
| 1000 | 1109 | padSpace(level); |
| 1001 | | std.debug.warn("}}"); |
| 1110 | debug.warn("}}"); |
| 1002 | 1111 | }, |
| 1003 | 1112 | } |
| 1004 | 1113 | } |
| ... | ... | @@ -1006,13 +1115,13 @@ pub const Value = union(enum) { |
| 1006 | 1115 | fn padSpace(indent: usize) void { |
| 1007 | 1116 | var i: usize = 0; |
| 1008 | 1117 | while (i < indent) : (i += 1) { |
| 1009 | | std.debug.warn(" "); |
| 1118 | debug.warn(" "); |
| 1010 | 1119 | } |
| 1011 | 1120 | } |
| 1012 | 1121 | }; |
| 1013 | 1122 | |
| 1014 | 1123 | // A non-stream JSON parser which constructs a tree of Value's. |
| 1015 | | pub const JsonParser = struct { |
| 1124 | pub const Parser = struct { |
| 1016 | 1125 | allocator: *Allocator, |
| 1017 | 1126 | state: State, |
| 1018 | 1127 | copy_strings: bool, |
| ... | ... | @@ -1026,8 +1135,8 @@ pub const JsonParser = struct { |
| 1026 | 1135 | Simple, |
| 1027 | 1136 | }; |
| 1028 | 1137 | |
| 1029 | | pub fn init(allocator: *Allocator, copy_strings: bool) JsonParser { |
| 1030 | | return JsonParser{ |
| 1138 | pub fn init(allocator: *Allocator, copy_strings: bool) Parser { |
| 1139 | return Parser{ |
| 1031 | 1140 | .allocator = allocator, |
| 1032 | 1141 | .state = State.Simple, |
| 1033 | 1142 | .copy_strings = copy_strings, |
| ... | ... | @@ -1035,52 +1144,26 @@ pub const JsonParser = struct { |
| 1035 | 1144 | }; |
| 1036 | 1145 | } |
| 1037 | 1146 | |
| 1038 | | pub fn deinit(p: *JsonParser) void { |
| 1147 | pub fn deinit(p: *Parser) void { |
| 1039 | 1148 | p.stack.deinit(); |
| 1040 | 1149 | } |
| 1041 | 1150 | |
| 1042 | | pub fn reset(p: *JsonParser) void { |
| 1151 | pub fn reset(p: *Parser) void { |
| 1043 | 1152 | p.state = State.Simple; |
| 1044 | 1153 | p.stack.shrink(0); |
| 1045 | 1154 | } |
| 1046 | 1155 | |
| 1047 | | pub fn parse(p: *JsonParser, input: []const u8) !ValueTree { |
| 1048 | | var mp = StreamingJsonParser.init(); |
| 1156 | pub fn parse(p: *Parser, input: []const u8) !ValueTree { |
| 1157 | var s = TokenStream.init(input); |
| 1049 | 1158 | |
| 1050 | 1159 | var arena = ArenaAllocator.init(p.allocator); |
| 1051 | 1160 | errdefer arena.deinit(); |
| 1052 | 1161 | |
| 1053 | | for (input) |c, i| { |
| 1054 | | var mt1: ?Token = undefined; |
| 1055 | | var mt2: ?Token = undefined; |
| 1056 | | |
| 1057 | | try mp.feed(c, &mt1, &mt2); |
| 1058 | | if (mt1) |t1| { |
| 1059 | | try p.transition(&arena.allocator, input, i, t1); |
| 1060 | | |
| 1061 | | if (mt2) |t2| { |
| 1062 | | try p.transition(&arena.allocator, input, i, t2); |
| 1063 | | } |
| 1064 | | } |
| 1162 | while (try s.next()) |token| { |
| 1163 | try p.transition(&arena.allocator, input, s.i - 1, token); |
| 1065 | 1164 | } |
| 1066 | 1165 | |
| 1067 | | // Handle top-level lonely number values. |
| 1068 | | { |
| 1069 | | const i = input.len; |
| 1070 | | var mt1: ?Token = undefined; |
| 1071 | | var mt2: ?Token = undefined; |
| 1072 | | |
| 1073 | | try mp.feed(' ', &mt1, &mt2); |
| 1074 | | if (mt1) |t1| { |
| 1075 | | try p.transition(&arena.allocator, input, i, t1); |
| 1076 | | } |
| 1077 | | } |
| 1078 | | |
| 1079 | | if (!mp.complete) { |
| 1080 | | return error.IncompleteJsonInput; |
| 1081 | | } |
| 1082 | | |
| 1083 | | std.debug.assert(p.stack.len == 1); |
| 1166 | debug.assert(p.stack.len == 1); |
| 1084 | 1167 | |
| 1085 | 1168 | return ValueTree{ |
| 1086 | 1169 | .arena = arena, |
| ... | ... | @@ -1090,7 +1173,7 @@ pub const JsonParser = struct { |
| 1090 | 1173 | |
| 1091 | 1174 | // Even though p.allocator exists, we take an explicit allocator so that allocation state |
| 1092 | 1175 | // can be cleaned up on error correctly during a `parse` on call. |
| 1093 | | fn transition(p: *JsonParser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { |
| 1176 | fn transition(p: *Parser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { |
| 1094 | 1177 | switch (p.state) { |
| 1095 | 1178 | State.ObjectKey => switch (token.id) { |
| 1096 | 1179 | Token.Id.ObjectEnd => { |
| ... | ... | @@ -1223,7 +1306,7 @@ pub const JsonParser = struct { |
| 1223 | 1306 | } |
| 1224 | 1307 | } |
| 1225 | 1308 | |
| 1226 | | fn pushToParent(p: *JsonParser, value: *const Value) !void { |
| 1309 | fn pushToParent(p: *Parser, value: *const Value) !void { |
| 1227 | 1310 | switch (p.stack.at(p.stack.len - 1)) { |
| 1228 | 1311 | // Object Parent -> [ ..., object, <key>, value ] |
| 1229 | 1312 | Value.String => |key| { |
| ... | ... | @@ -1244,14 +1327,14 @@ pub const JsonParser = struct { |
| 1244 | 1327 | } |
| 1245 | 1328 | } |
| 1246 | 1329 | |
| 1247 | | fn parseString(p: *JsonParser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { |
| 1330 | fn parseString(p: *Parser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { |
| 1248 | 1331 | // TODO: We don't strictly have to copy values which do not contain any escape |
| 1249 | 1332 | // characters if flagged with the option. |
| 1250 | 1333 | const slice = token.slice(input, i); |
| 1251 | 1334 | return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; |
| 1252 | 1335 | } |
| 1253 | 1336 | |
| 1254 | | fn parseNumber(p: *JsonParser, token: *const Token, input: []const u8, i: usize) !Value { |
| 1337 | fn parseNumber(p: *Parser, token: *const Token, input: []const u8, i: usize) !Value { |
| 1255 | 1338 | return if (token.number_is_integer) |
| 1256 | 1339 | Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } |
| 1257 | 1340 | else |
| ... | ... | @@ -1259,10 +1342,8 @@ pub const JsonParser = struct { |
| 1259 | 1342 | } |
| 1260 | 1343 | }; |
| 1261 | 1344 | |
| 1262 | | const debug = std.debug; |
| 1263 | | |
| 1264 | 1345 | test "json parser dynamic" { |
| 1265 | | var p = JsonParser.init(std.debug.global_allocator, false); |
| 1346 | var p = Parser.init(debug.global_allocator, false); |
| 1266 | 1347 | defer p.deinit(); |
| 1267 | 1348 | |
| 1268 | 1349 | const s = |