| ... | @@ -1259,7 +1259,7 @@ pub const Parser = struct { | ... | @@ -1259,7 +1259,7 @@ pub const Parser = struct { |
| 1259 | // TODO: We don't strictly have to copy values which do not contain any escape | 1259 | // TODO: We don't strictly have to copy values which do not contain any escape |
| 1260 | // characters if flagged with the option. | 1260 | // characters if flagged with the option. |
| 1261 | const slice = token.slice(input, i); | 1261 | const slice = token.slice(input, i); |
| 1262 | return Value{ .String = try mem.dupe(allocator, u8, slice) }; | 1262 | return Value{ .String = try unescapeStringAlloc(allocator, slice) }; |
| 1263 | } | 1263 | } |
| 1264 | | 1264 | |
| 1265 | fn parseNumber(p: *Parser, token: Token, input: []const u8, i: usize) !Value { | 1265 | fn parseNumber(p: *Parser, token: Token, input: []const u8, i: usize) !Value { |
| ... | @@ -1270,6 +1270,45 @@ pub const Parser = struct { | ... | @@ -1270,6 +1270,45 @@ pub const Parser = struct { |
| 1270 | } | 1270 | } |
| 1271 | }; | 1271 | }; |
| 1272 | | 1272 | |
| | 1273 | pub fn unescapeStringAlloc(alloc: *Allocator, input: []const u8) ![]u8 { |
| | 1274 | var output = try alloc.alloc(u8, input.len); |
| | 1275 | errdefer alloc.free(output); |
| | 1276 | |
| | 1277 | var inIndex: usize = 0; |
| | 1278 | var outIndex: usize = 0; |
| | 1279 | while(inIndex < input.len) { |
| | 1280 | if(input[inIndex] == '\\'){ |
| | 1281 | if(input[inIndex + 1] == 'u'){ |
| | 1282 | const codepoint = std.fmt.parseInt(u32, input[inIndex+2 .. inIndex+6], 16) catch return error.InvalidUnicodeHexSymbol; |
| | 1283 | outIndex += std.unicode.utf8Encode(codepoint, output[outIndex..]) catch return error.InvalidUnicodeHexSymbol; |
| | 1284 | inIndex += 6; |
| | 1285 | } else { |
| | 1286 | output[outIndex] = @as(u8, |
| | 1287 | switch(input[inIndex + 1]){ |
| | 1288 | '\\' => '\\', |
| | 1289 | '/' => '/', |
| | 1290 | 'n' => '\n', |
| | 1291 | 'r' => '\r', |
| | 1292 | 't' => '\t', |
| | 1293 | 'f' => 12, |
| | 1294 | 'b' => 8, |
| | 1295 | '"' => '"', |
| | 1296 | else => return error.InvalidEscapeCharacter; |
| | 1297 | } |
| | 1298 | ); |
| | 1299 | inIndex += 2; |
| | 1300 | outIndex += 1; |
| | 1301 | } |
| | 1302 | } else { |
| | 1303 | output[outIndex] = input[inIndex]; |
| | 1304 | inIndex += 1; |
| | 1305 | outIndex += 1; |
| | 1306 | } |
| | 1307 | } |
| | 1308 | |
| | 1309 | return try alloc.realloc(unescaped, outIndex); |
| | 1310 | } |
| | 1311 | |
| 1273 | test "json.parser.dynamic" { | 1312 | test "json.parser.dynamic" { |
| 1274 | var p = Parser.init(debug.global_allocator, false); | 1313 | var p = Parser.init(debug.global_allocator, false); |
| 1275 | defer p.deinit(); | 1314 | defer p.deinit(); |
| ... | @@ -1379,3 +1418,34 @@ test "parsing empty string gives appropriate error" { | ... | @@ -1379,3 +1418,34 @@ test "parsing empty string gives appropriate error" { |
| 1379 | defer p.deinit(); | 1418 | defer p.deinit(); |
| 1380 | testing.expectError(error.UnexpectedEndOfJson, p.parse("")); | 1419 | testing.expectError(error.UnexpectedEndOfJson, p.parse("")); |
| 1381 | } | 1420 | } |
| | 1421 | |
| | 1422 | test "escaped characters" { |
| | 1423 | const input = |
| | 1424 | \\{ |
| | 1425 | \\ "backslash": "\\", |
| | 1426 | \\ "forwardslash": "\/", |
| | 1427 | \\ "newline": "\n", |
| | 1428 | \\ "carriagereturn": "\r", |
| | 1429 | \\ "tab": "\t", |
| | 1430 | \\ "formfeed": "\f", |
| | 1431 | \\ "backspace": "\b", |
| | 1432 | \\ "doublequote": "\"", |
| | 1433 | \\ "unicode": "\u0105" |
| | 1434 | \\} |
| | 1435 | ; |
| | 1436 | |
| | 1437 | var p = Parser.init(debug.global_allocator, false); |
| | 1438 | const tree = try p.parse(input); |
| | 1439 | |
| | 1440 | const obj = tree.root.Object; |
| | 1441 | |
| | 1442 | testing.expect(mem.eql(u8, obj.get("backslash").?.value.String, "\\")); |
| | 1443 | testing.expect(mem.eql(u8, obj.get("forwardslash").?.value.String, "/")); |
| | 1444 | testing.expect(mem.eql(u8, obj.get("newline").?.value.String, "\n")); |
| | 1445 | testing.expect(mem.eql(u8, obj.get("carriagereturn").?.value.String, "\r")); |
| | 1446 | testing.expect(mem.eql(u8, obj.get("tab").?.value.String, "\t")); |
| | 1447 | testing.expect(mem.eql(u8, obj.get("formfeed").?.value.String, "\x0C")); |
| | 1448 | testing.expect(mem.eql(u8, obj.get("backspace").?.value.String, "\x08")); |
| | 1449 | testing.expect(mem.eql(u8, obj.get("doublequote").?.value.String, "\"")); |
| | 1450 | testing.expect(mem.eql(u8, obj.get("unicode").?.value.String, "ą")); |
| | 1451 | } |