mirror of
https://github.com/fluencelabs/assemblyscript-json
synced 2025-05-05 11:42:18 +00:00
Implement escaped character support
This commit is contained in:
parent
1567bb19c7
commit
f53d0bae16
@ -72,6 +72,10 @@ export class ThrowingJSONHandler extends JSONHandler {
|
|||||||
const TRUE_STR = "true";
|
const TRUE_STR = "true";
|
||||||
const FALSE_STR = "false";
|
const FALSE_STR = "false";
|
||||||
const NULL_STR = "null";
|
const NULL_STR = "null";
|
||||||
|
let CHAR_0 = "0".charCodeAt(0);
|
||||||
|
let CHAR_9 = "9".charCodeAt(0);
|
||||||
|
let CHAR_A = "A".charCodeAt(0);
|
||||||
|
let CHAR_A_LOWER = "a".charCodeAt(0);
|
||||||
|
|
||||||
export class JSONDecoder<JSONHandlerT extends JSONHandler> {
|
export class JSONDecoder<JSONHandlerT extends JSONHandler> {
|
||||||
|
|
||||||
@ -178,28 +182,79 @@ export class JSONDecoder<JSONHandlerT extends JSONHandler> {
|
|||||||
private readString(): string {
|
private readString(): string {
|
||||||
assert(this.readChar() == '"'.charCodeAt(0), "Expected double-quoted string");
|
assert(this.readChar() == '"'.charCodeAt(0), "Expected double-quoted string");
|
||||||
let savedIndex = this.readIndex;
|
let savedIndex = this.readIndex;
|
||||||
|
let stringParts: Array<string> = new Array<string>();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
let byte = this.readChar();
|
let byte = this.readChar();
|
||||||
assert(byte >= 0x20, "Unexpected control character");
|
assert(byte >= 0x20, "Unexpected control character");
|
||||||
// TODO: Make sure unicode handled properly
|
// TODO: Make sure unicode handled properly
|
||||||
if (byte == '"'.charCodeAt(0)) {
|
if (byte == '"'.charCodeAt(0)) {
|
||||||
return String.fromUTF8(this.buffer.buffer.data + savedIndex, this.readIndex - savedIndex - 1);
|
stringParts.push(
|
||||||
|
String.fromUTF8(this.buffer.buffer.data + savedIndex, this.readIndex - savedIndex - 1));
|
||||||
|
return stringParts.join("");
|
||||||
}
|
}
|
||||||
if (byte == "\\".charCodeAt(0)) {
|
if (byte == "\\".charCodeAt(0)) {
|
||||||
// TODO: Decode string properly
|
if (this.readIndex > savedIndex + 1) {
|
||||||
let skipCount = 1;
|
stringParts.push(
|
||||||
if (this.peekChar() == "u".charCodeAt(0)) {
|
String.fromUTF8(this.buffer.buffer.data + savedIndex, this.readIndex - savedIndex - 1));
|
||||||
skipCount += 4;
|
|
||||||
}
|
|
||||||
for (; skipCount > 0; skipCount--) {
|
|
||||||
this.readChar();
|
|
||||||
}
|
}
|
||||||
|
stringParts.push(this.readEscapedChar());
|
||||||
|
savedIndex = this.readIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Should never happen
|
// Should never happen
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readEscapedChar(): string {
|
||||||
|
let byte = this.readChar();
|
||||||
|
// TODO: Use lookup table for anything except \u
|
||||||
|
if (byte == '"'.charCodeAt(0)) {
|
||||||
|
return '"';
|
||||||
|
}
|
||||||
|
if (byte == "\\".charCodeAt(0)) {
|
||||||
|
return "\\";
|
||||||
|
}
|
||||||
|
if (byte == "/".charCodeAt(0)) {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
if (byte == "b".charCodeAt(0)) {
|
||||||
|
return "\b";
|
||||||
|
}
|
||||||
|
if (byte == "n".charCodeAt(0)) {
|
||||||
|
return "\n";
|
||||||
|
}
|
||||||
|
if (byte == "r".charCodeAt(0)) {
|
||||||
|
return "\r";
|
||||||
|
}
|
||||||
|
if (byte == "t".charCodeAt(0)) {
|
||||||
|
return "\t";
|
||||||
|
}
|
||||||
|
if (byte == "u".charCodeAt(0)) {
|
||||||
|
let d1 = this.readHexDigit();
|
||||||
|
let d2 = this.readHexDigit();
|
||||||
|
let d3 = this.readHexDigit();
|
||||||
|
let d4 = this.readHexDigit();
|
||||||
|
let charCode = d1 * 0x1000 + d2 * 0x100 + d3 * 0x10 + d4;
|
||||||
|
return String.fromCodePoint(charCode);
|
||||||
|
}
|
||||||
|
assert(false, "Unexpected escaped character: " + String.fromCharCode(byte));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private readHexDigit(): i32 {
|
||||||
|
let byte = this.readChar();
|
||||||
|
let digit = byte - CHAR_0;
|
||||||
|
if (digit > 9) {
|
||||||
|
digit = byte - CHAR_A + 10;
|
||||||
|
if (digit < 10 || digit > 15) {
|
||||||
|
digit = byte - CHAR_A_LOWER + 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let arr: Array<i32> = [byte, digit];
|
||||||
|
assert(digit >= 0 && digit < 16, "Unexpected \\u digit");
|
||||||
|
return digit;
|
||||||
|
}
|
||||||
|
|
||||||
private parseNumber(): bool {
|
private parseNumber(): bool {
|
||||||
// TODO: Parse floats
|
// TODO: Parse floats
|
||||||
let number: i32 = 0;
|
let number: i32 = 0;
|
||||||
@ -209,10 +264,10 @@ export class JSONDecoder<JSONHandlerT extends JSONHandler> {
|
|||||||
this.readChar();
|
this.readChar();
|
||||||
}
|
}
|
||||||
let digits = 0;
|
let digits = 0;
|
||||||
while ("0".charCodeAt(0) <= this.peekChar() && this.peekChar() <= "9".charCodeAt(0) ) {
|
while (CHAR_0 <= this.peekChar() && this.peekChar() <= CHAR_9 ) {
|
||||||
let byte = this.readChar();
|
let byte = this.readChar();
|
||||||
number *= 10;
|
number *= 10;
|
||||||
number += byte - "0".charCodeAt(0);
|
number += byte - CHAR_0;
|
||||||
digits++;
|
digits++;
|
||||||
}
|
}
|
||||||
if (digits > 0) {
|
if (digits > 0) {
|
||||||
|
@ -67,9 +67,33 @@ class JSONTestHandler extends JSONHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private writeString(str: string): void {
|
private writeString(str: string): void {
|
||||||
// TODO: Implement encoding
|
|
||||||
this.write('"');
|
this.write('"');
|
||||||
this.write(str);
|
let savedIndex = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
let char = str.charCodeAt(i);
|
||||||
|
let needsEscaping = char < 0x20 || char == '"'.charCodeAt(0) || char == '\\'.charCodeAt(0);
|
||||||
|
if (needsEscaping) {
|
||||||
|
this.write(str.substring(savedIndex, i));
|
||||||
|
savedIndex = i + 1;
|
||||||
|
if (char == '"'.charCodeAt(0)) {
|
||||||
|
this.write('\\"');
|
||||||
|
} else if (char == "\\".charCodeAt(0)) {
|
||||||
|
this.write("\\\\");
|
||||||
|
} else if (char == "\b".charCodeAt(0)) {
|
||||||
|
this.write("\\b");
|
||||||
|
} else if (char == "\n".charCodeAt(0)) {
|
||||||
|
this.write("\\n");
|
||||||
|
} else if (char == "\r".charCodeAt(0)) {
|
||||||
|
this.write("\\r");
|
||||||
|
} else if (char == "\t".charCodeAt(0)) {
|
||||||
|
this.write("\\t");
|
||||||
|
} else {
|
||||||
|
// TODO: Implement encoding for other contol characters
|
||||||
|
assert(false, "Unsupported control chracter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.write(str.substring(savedIndex, str.length));
|
||||||
this.write('"');
|
this.write('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +156,18 @@ export class StringConversionTests {
|
|||||||
return this.roundripTest('{"str":"foo"}');
|
return this.roundripTest('{"str":"foo"}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static shouldHandleStringEscaped(): bool {
|
||||||
|
return this.roundripTest('"\\"\\\\\\/\\n\\t\\b\\r\\t"', '"\\"\\\\/\\n\\t\\b\\r\\t"');
|
||||||
|
}
|
||||||
|
|
||||||
|
static shouldHandleStringUnicodeEscaped1(): bool {
|
||||||
|
return this.roundripTest('"\\u0022"', '"\\""');
|
||||||
|
}
|
||||||
|
|
||||||
|
static shouldHandleStringUnicodeEscaped2(): bool {
|
||||||
|
return this.roundripTest('"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430"', '"Полтора Землекопа"');
|
||||||
|
}
|
||||||
|
|
||||||
static shouldMultipleKeys(): bool {
|
static shouldMultipleKeys(): bool {
|
||||||
return this.roundripTest('{"str":"foo","bar":"baz"}');
|
return this.roundripTest('{"str":"foo","bar":"baz"}');
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user