@init-kz/jsonparse-ts
Version:
Library for parsing partial jsons inspired by original jsonparse written on js
1,172 lines (1,162 loc) • 36 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/constants/constants.ts
var constants_exports = {};
__export(constants_exports, {
CHARS: () => CHARS,
PARSER_MODES: () => PARSER_MODES,
PARSER_STATES: () => PARSER_STATES,
STRING_BUFFER_SIZE: () => STRING_BUFFER_SIZE,
TOKENIZER_STATES: () => TOKENIZER_STATES,
TOKENS: () => TOKENS,
UTF8_BOUNDS: () => UTF8_BOUNDS
});
var TOKENS = {
LEFT_BRACE: 1,
RIGHT_BRACE: 2,
LEFT_BRACKET: 3,
RIGHT_BRACKET: 4,
COLON: 5,
COMMA: 6,
TRUE: 7,
FALSE: 8,
NULL: 9,
STRING: 10,
NUMBER: 11
};
var TOKENIZER_STATES = {
START: 17,
STOP: 18,
TRUE1: 33,
TRUE2: 34,
TRUE3: 35,
FALSE1: 49,
FALSE2: 50,
FALSE3: 51,
FALSE4: 52,
NULL1: 65,
NULL2: 66,
NULL3: 67,
NUMBER1: 81,
NUMBER3: 83,
STRING1: 97,
// After open quote
STRING2: 98,
// After backslash
// unicode hex codes
STRING3: 99,
STRING4: 100,
STRING5: 101,
STRING6: 102
};
var PARSER_STATES = {
VALUE: 113,
KEY: 114,
COLON: 58,
// :
COMMA: 44
// ,
};
var PARSER_MODES = {
OBJECT: 129,
ARRAY: 130
};
var CHARS = {
BACKSLASH: 92,
// '\'
FORWARD_SLASH: 47,
// '/'
BACKSPACE: 8,
// \b
FORM_FEED: 12,
// \f
NEWLINE: 10,
// \n
CARRIAGE_RETURN: 13,
// \r
TAB: 9,
// \t
SPACE: 32,
// ' '
LEFT_BRACE: 123,
// {
RIGHT_BRACE: 125,
// }
LEFT_BRACKET: 91,
// [
RIGHT_BRACKET: 93,
// ]
COLON: 58,
// :
COMMA: 44,
// ,
QUOTE: 34,
// "
MINUS: 45,
// -
PLUS: 43,
// +
DOT: 46,
// .
E: 101,
// e
BIG_E: 69,
// E
T: 116,
// t
F: 102,
// f
N: 110,
// n
L: 108,
// l
S: 115,
// s
B: 98,
// b (Backspace)
R: 114,
// r (Carriage Return)
U: 117,
// u (Unicode sequence start)
A: 97,
// a
BIG_A: 65,
// A
BIG_F: 70,
// F
ZERO: 48,
// 0
NINE: 57,
// 9
WHITESPACES: [
32,
// ' '
9,
// \t
10,
// \n
13
// \r
]
};
var UTF8_BOUNDS = {
MIN_MULTI_BYTE: 128,
INVALID_LOWER: 193,
BYTE_2_MIN: 194,
BYTE_2_MAX: 223,
BYTE_3_MIN: 224,
BYTE_3_MAX: 239,
BYTE_4_MIN: 240,
BYTE_4_MAX: 244,
HIGH_SURROGATE_START: 55296,
HIGH_SURROGATE_END: 56319,
LOW_SURROGATE_START: 56320,
LOW_SURROGATE_END: 57343
};
var STRING_BUFFER_SIZE = 64 * 1024;
// src/utils/utils.ts
function alloc(size) {
if (typeof Buffer !== "undefined" && Buffer.alloc) {
return Buffer.alloc(size);
}
return new Uint8Array(size);
}
function isPrimitive(token) {
return token === TOKENS.STRING || token === TOKENS.NUMBER || token === TOKENS.TRUE || token === TOKENS.FALSE || token === TOKENS.NULL;
}
function tokenName(code) {
const _constants = {
...TOKENS,
...TOKENIZER_STATES,
...PARSER_STATES,
...PARSER_MODES
};
for (const key of Object.keys(_constants)) {
if (_constants[key] === code) {
return key;
}
}
return code ? `0x${code.toString(16)}` : "";
}
function omitEmptyArrayOrObject(value) {
if (Array.isArray(value)) {
return value.map(omitEmptyArrayOrObject).filter((item) => !(Array.isArray(item) && item.length === 0) && !(typeof item === "object" && item !== null && Object.keys(item).length === 0));
} else if (typeof value === "object" && value !== null) {
return Object.fromEntries(
Object.entries(value).map(([key, val]) => [key, omitEmptyArrayOrObject(val)]).filter(([, val]) => !(Array.isArray(val) && val.length === 0) && !(typeof val === "object" && val !== null && Object.keys(val).length === 0))
);
}
return value;
}
// src/handlers/error-handler.ts
var ErrorHandler = class {
constructor(eventEmitter, stateHandler) {
this.eventEmitter = eventEmitter;
this.stateHandler = stateHandler;
}
/**
* Handles unexpected token errors.
*/
parseError(token, value) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STOP);
const error = new Error(
`Unexpected ${tokenName(token)}${value ? ` (${JSON.stringify(value)})` : ""} in state ${tokenName(this.stateHandler.getParsingState())}`
);
this.eventEmitter.handleError(error);
return error;
}
/**
* Handles character-related errors.
*/
charError(buffer, i) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STOP);
const error = new Error(
`Unexpected ${JSON.stringify(
String.fromCharCode(buffer[i])
)} at position ${i} in state ${tokenName(this.stateHandler.getTokenizerState())}`
);
this.eventEmitter.handleError(
error
);
return error;
}
};
// src/handlers/event-emitter.ts
var EventEmitter = class {
valueHandlers = [];
tokenHandlers = [];
errorHandlers = [];
/**
* Subscribes a value event callback.
*/
onValue(callback) {
this.valueHandlers.push(callback);
}
/**
* Emits a parsed value event.
*/
emitValue(value) {
this.valueHandlers.forEach((callback) => callback(value));
return this.valueHandlers.length > 0;
}
/**
* Unsubscribes a value event callback.
*/
offValue(callback) {
this.valueHandlers = this.valueHandlers.filter((cb) => cb !== callback);
}
/**
* Returns true if length of value subscribers more than zero
*/
hasValueHandler() {
return this.valueHandlers.length > 0;
}
/**
* Subscribes a callback to token events.
* @param callback - Function to handle tokens.
*/
onToken(callback) {
this.tokenHandlers.push(callback);
}
/**
* Subscribes a callback to token events.
* @param callback - Function to handle tokens.
*/
offToken(callback) {
this.tokenHandlers = this.tokenHandlers.filter((cb) => cb !== callback);
}
/**
* Emits a token event to all subscribers.
* @param token - The token type.
* @param value - The associated value.
*/
emitToken(token, value) {
if (this.tokenHandlers.length > 0) {
this.tokenHandlers.forEach((callback) => callback(token, value));
}
return this.tokenHandlers.length > 0;
}
/**
* Subscribes an error handler callback.
* @param callback - A function to handle errors.
*/
onError(callback) {
this.errorHandlers.push(callback);
}
/**
* Subscribes an error handler callback.
* @param callback - A function to handle errors.
*/
offError(callback) {
this.errorHandlers = this.errorHandlers.filter((cb) => cb !== callback);
}
/**
* Handles errors by either notifying subscribers or throwing the error.
* @param err - The error to handle.
*/
handleError(err) {
if (this.errorHandlers.length > 0) {
this.errorHandlers.forEach((callback) => callback(err));
} else {
throw err;
}
}
};
// src/handlers/state-handler.ts
var StateHandler = class {
eventEmitter;
stringHandler;
/** Tokenizer State - Tracks the current state of the tokenizer */
tState = TOKENIZER_STATES.START;
/** Current Parsed Value */
value = void 0;
lastValue = void 0;
/** Parser Mode (OBJECT, ARRAY) */
mode = void 0;
/** Stack to maintain parsing context */
stack;
/** Current Parsing State (VALUE, KEY) */
state = PARSER_STATES.VALUE;
/** Current byte offset in the stream */
offset = -1;
/** Current key being processed (for objects) */
key = void 0;
constructor(eventEmitter, stringHandler) {
this.eventEmitter = eventEmitter;
this.stringHandler = stringHandler;
this.stack = [];
}
getTokenizerState() {
return this.tState;
}
incTokenizerState() {
this.tState++;
return this;
}
setOffset(n) {
this.offset = n;
return this;
}
getOffset() {
return this.offset;
}
addOffset(n) {
this.offset += n;
return this;
}
setTokenizerState(n) {
this.tState = n;
return this;
}
getParsingState() {
return this.state;
}
getStack() {
return this.stack;
}
/**
* Pushes the current parsing context onto the stack.
*/
push() {
this.stack.push({ value: this.value, key: this.key, mode: this.mode });
}
/**
* Pops the last parsing context from the stack and emits a value.
*/
pop() {
const value = this.value;
const parent = this.stack.pop();
this.value = parent?.value;
this.key = parent?.key;
this.mode = parent?.mode;
if (typeof parent?.value !== void 0) {
this.lastValue = value;
}
if (this.mode) {
this.state = PARSER_STATES.COMMA;
}
this.eventEmitter.emitValue(value);
if (!this.mode) {
this.state = PARSER_STATES.VALUE;
}
}
incOffset() {
this.offset++;
return this;
}
getValue() {
return this.value;
}
getLastIncompleteValue(omitEmpty = false) {
if (typeof this.lastValue === "undefined" || this.stack.length > 0) {
let currentValue = void 0;
let settedCurrentValue = false;
if (this.mode) {
switch (this.tState) {
case TOKENIZER_STATES.NULL1:
case TOKENIZER_STATES.NULL2:
case TOKENIZER_STATES.NULL3:
currentValue = null;
break;
case TOKENIZER_STATES.TRUE1:
case TOKENIZER_STATES.TRUE2:
case TOKENIZER_STATES.TRUE3:
currentValue = true;
break;
case TOKENIZER_STATES.FALSE1:
case TOKENIZER_STATES.FALSE2:
case TOKENIZER_STATES.FALSE3:
case TOKENIZER_STATES.FALSE4:
currentValue = false;
break;
case TOKENIZER_STATES.STRING1:
case TOKENIZER_STATES.STRING2:
case TOKENIZER_STATES.STRING3:
case TOKENIZER_STATES.STRING4:
case TOKENIZER_STATES.STRING5:
case TOKENIZER_STATES.STRING6:
currentValue = this.stringHandler.flushBuffer().getString();
break;
}
if (typeof currentValue !== "undefined" && typeof this.key !== "undefined" && typeof this.value[this.key] === "undefined") {
this.value[this.key] = currentValue;
settedCurrentValue = true;
}
}
const newStack = [...this.stack, { value: this.value, key: this.key, mode: this.mode }];
let parent = null;
for (let i = newStack.length - 1; i >= 0; i--) {
const currentStack = newStack[i];
if (typeof currentStack?.value !== "undefined") {
parent = currentStack?.value;
}
}
if (parent) {
parent = JSON.parse(JSON.stringify(parent));
}
if (typeof currentValue !== "undefined" && typeof this.key !== "undefined" && settedCurrentValue) delete this.value[this.key];
if (parent) return omitEmpty ? omitEmptyArrayOrObject(parent) : parent;
}
return this.lastValue ?? null;
}
getLastValue() {
if (typeof this.lastValue === "undefined" || this.stack.length > 0) {
const newStack = [...this.stack, { value: this.value, key: this.key, mode: this.mode }];
let parent = null;
for (let i = newStack.length - 1; i >= 0; i--) {
const currentStack = newStack[i];
if (typeof currentStack?.value !== "undefined") {
parent = currentStack?.value;
}
}
if (parent) {
return JSON.parse(JSON.stringify(parent));
}
}
return this.lastValue ?? null;
}
getKey() {
return this.key;
}
getTokenizerStateName() {
return tokenName(this.getTokenizerState());
}
getMode() {
return this.mode;
}
setValue(value) {
this.value = value;
return this;
}
setKey(value) {
this.key = value;
return this;
}
setKeyValue(value) {
if (this.value && (this.key || this.key === 0)) {
this.value[this.key] = value;
}
}
setState(n) {
this.state = n;
return this;
}
setMode(n) {
this.mode = n;
return this;
}
};
// src/handlers/string-handler.ts
var StringHandler = class {
/** Current string being parsed */
string = void 0;
/** Buffer to store string data */
stringBuffer;
/** Offset within the string buffer */
stringBufferOffset = 0;
/** Unicode processing variables */
unicode = void 0;
highSurrogate = void 0;
constructor() {
this.stringBuffer = alloc(STRING_BUFFER_SIZE);
}
/**
* Appends a character to the string buffer.
*/
appendStringChar(char) {
if (this.stringBufferOffset >= STRING_BUFFER_SIZE) {
if (this.string === void 0) this.string = "";
this.string += new TextDecoder().decode(
this.stringBuffer.subarray(0, this.stringBufferOffset)
);
this.stringBufferOffset = 0;
}
this.stringBuffer[this.stringBufferOffset++] = char;
}
/**
* Appends a portion of a buffer to the internal string buffer.
* @param buf - The source Uint8Array buffer.
* @param start - The starting index (optional, defaults to 0).
* @param end - The ending index (optional, defaults to `buf.length`).
*/
appendStringBuf(buf, start = 0, end) {
let size = buf.length;
if (typeof start === "number") {
if (typeof end === "number") {
if (end < 0) {
size = buf.length - start + end;
} else {
size = end - start;
}
} else {
size = buf.length - start;
}
}
if (size < 0) size = 0;
if (this.stringBufferOffset + size > STRING_BUFFER_SIZE) {
if (this.string === void 0) this.string = "";
this.string += new TextDecoder().decode(
this.stringBuffer.subarray(0, this.stringBufferOffset)
);
this.stringBufferOffset = 0;
}
this.stringBuffer.set(
buf.subarray(start, start + size),
this.stringBufferOffset
);
this.stringBufferOffset += size;
}
/**
* Flushes the current buffer into the string.
*/
flushBuffer() {
if (this.stringBufferOffset > 0) {
if (this.string === void 0) {
this.string = "";
}
this.string += new TextDecoder().decode(
this.stringBuffer.subarray(0, this.stringBufferOffset)
);
this.stringBufferOffset = 0;
}
return this;
}
/**
* Gets the final string value.
* Ensures that if no string has been accumulated, it returns an empty string.
* @returns {string} The current accumulated string.
*/
getString() {
return this.string || "";
}
/**
* Resets the string buffer and optionally sets a new initial string value.
* This is useful when starting a new string parsing session.
* @param {string} [stringValue] - Optional initial string value after reset.
*/
reset(stringValue) {
this.stringBufferOffset = 0;
this.string = stringValue;
}
/**
* Resets only the stored string without affecting the buffer offset.
* This can be useful when clearing the current string but keeping buffer state.
* @param {string} [stringValue] - Optional new string value.
*/
resetString(stringValue) {
this.string = stringValue;
}
/**
* Sets the string value from a single character's char code.
* Useful when initializing a string with a character (e.g., starting a number or string).
* @param {number} n - The character code to set as the string.
*/
setFromCharcode(n) {
this.string = String.fromCharCode(n);
}
/**
* Appends a single character (from its char code) to the existing string.
* Useful for dynamically building strings one character at a time.
* @param {number} n - The character code to append to the string.
*/
addFromCharCode(n) {
if (this.string === void 0) {
this.string = "";
}
this.string += String.fromCharCode(n);
return this;
}
setUnicode(unicode) {
this.unicode = unicode;
return this;
}
getUnicode() {
return this.unicode || "";
}
setHighSurrogate(n) {
this.highSurrogate = n;
return this;
}
getHighSurrogate() {
return this.highSurrogate;
}
addUnicodeFromCharCode(n) {
if (this.unicode === void 0) {
this.unicode = "";
}
this.unicode += String.fromCharCode(n);
return this;
}
};
// src/handlers/token-handler.ts
var TokenHandler = class {
constructor(eventEmitter, stateHandler, errorHandler) {
this.eventEmitter = eventEmitter;
this.stateHandler = stateHandler;
this.errorHandler = errorHandler;
}
/**
* Handles incoming tokens and builds the JSON structure.
*/
handleToken(token, value) {
switch (this.stateHandler.getParsingState()) {
case PARSER_STATES.VALUE:
this.handleValueToken(token, value);
break;
case PARSER_STATES.KEY:
this.handleKeyToken(token, value);
break;
case PARSER_STATES.COLON:
this.handleColonToken(token);
break;
case PARSER_STATES.COMMA:
this.handleCommaToken(token);
break;
default:
this.errorHandler.parseError(token, value);
}
this.eventEmitter.emitToken(token, value);
}
/**
* Handles tokens when in VALUE state.
*/
handleValueToken(token, value) {
if (isPrimitive(token)) {
this.stateHandler.setKeyValue(value);
if (this.stateHandler.getMode()) {
this.stateHandler.setState(PARSER_STATES.COMMA);
}
this.eventEmitter.emitValue(value);
} else if (token === TOKENS.LEFT_BRACE) {
this.startObject();
} else if (token === TOKENS.LEFT_BRACKET) {
this.startArray();
} else if (token === TOKENS.RIGHT_BRACE && this.stateHandler.getMode() === PARSER_MODES.OBJECT || token === TOKENS.RIGHT_BRACKET && this.stateHandler.getMode() === PARSER_MODES.ARRAY) {
this.stateHandler.pop();
} else {
this.errorHandler.parseError(token, value);
}
}
/**
* Handles tokens when in KEY state.
*/
handleKeyToken(token, value) {
if (token === TOKENS.STRING) {
this.stateHandler.setKey(value);
this.stateHandler.setState(PARSER_STATES.COLON);
} else if (token === TOKENS.RIGHT_BRACE) {
this.stateHandler.pop();
} else {
this.errorHandler.parseError(token, value);
}
}
/**
* Handles tokens when in COLON state.
*/
handleColonToken(token) {
if (token === TOKENS.COLON) {
this.stateHandler.setState(PARSER_STATES.VALUE);
} else {
this.errorHandler.parseError(token);
}
}
/**
* Handles tokens when in COMMA state.
*/
handleCommaToken(token) {
if (token === TOKENS.COMMA) {
const mode = this.stateHandler.getMode();
if (mode === PARSER_MODES.ARRAY) {
const key = this.stateHandler.getKey() || 0;
this.stateHandler.setKey(key + 1);
this.stateHandler.setState(PARSER_STATES.VALUE);
} else if (mode === PARSER_MODES.OBJECT) {
this.stateHandler.setState(PARSER_STATES.KEY);
}
} else if (token === TOKENS.RIGHT_BRACKET && this.stateHandler.getMode() === PARSER_MODES.ARRAY || token === TOKENS.RIGHT_BRACE && this.stateHandler.getMode() === PARSER_MODES.OBJECT) {
this.stateHandler.pop();
} else {
this.errorHandler.parseError(token);
}
}
/**
* Starts a new object `{}`.
*/
startObject() {
this.stateHandler.push();
let value = this.stateHandler.getValue();
if (value) {
value = value[this.stateHandler.getKey()] = {};
} else {
value = {};
}
this.stateHandler.setValue(value).setKey(void 0).setState(PARSER_STATES.KEY).setMode(PARSER_MODES.OBJECT);
}
/**
* Starts a new array `[]`.
*/
startArray() {
this.stateHandler.push();
let value = this.stateHandler.getValue();
if (value) {
value = value[this.stateHandler.getKey()] = [];
} else {
value = [];
}
this.stateHandler.setValue(value).setKey(0).setMode(PARSER_MODES.ARRAY).setState(PARSER_STATES.VALUE);
}
};
// src/handlers/utf8-handler.ts
var UTF8Handler = class {
/** Remaining bytes for a multi-byte UTF-8 character, number of bytes remaining in multi byte utf8 char to read after split boundary */
bytes_remaining = 0;
/** Total bytes in the current UTF-8 character sequence, bytes in multi byte utf8 char to read */
bytes_in_sequence = 0;
/** Temporary buffers for rebuilding chars split before boundary is reached */
temp_buffs;
constructor() {
this.temp_buffs = {
2: alloc(2),
3: alloc(3),
4: alloc(4)
};
}
/** Get the remaining bytes for a multi-byte character */
getBytesRemaining() {
return this.bytes_remaining;
}
getRemainingBytesInBuff(buffer) {
for (let i = 0; i < this.bytes_remaining; i++) {
this.temp_buffs[this.bytes_in_sequence][this.bytes_in_sequence - this.bytes_remaining + i] = buffer[i];
}
const remainingBytes = this.temp_buffs[this.bytes_in_sequence] || alloc(0);
this.bytes_in_sequence = this.bytes_remaining = 0;
return remainingBytes;
}
handleBoundarySplit(i, buffer) {
for (let j = 0; j <= buffer.length - 1 - i; j++) {
this.temp_buffs[this.bytes_in_sequence][j] = buffer[i + j];
}
this.bytes_remaining = i + this.bytes_in_sequence - buffer.length;
}
/** Set the remaining bytes, ensuring it's not negative */
setBytesRemaining(value) {
if (value < 0) throw new Error("bytes_remaining cannot be negative");
this.bytes_remaining = value;
}
/** Get the total bytes in the current UTF-8 character sequence */
getBytesInSequence() {
return this.bytes_in_sequence;
}
/** Set the total bytes, ensuring it's not negative */
setBytesInSequence(value) {
if (value < 0) throw new Error("bytes_in_sequence cannot be negative");
this.bytes_in_sequence = value;
}
/** Get the temporary buffers for multi-byte characters */
getTempBuffs() {
return this.temp_buffs;
}
/** Set the temporary buffers */
setTempBuffs(buffers) {
this.temp_buffs = buffers;
}
hasBytesRemaining() {
return this.bytes_remaining > 0;
}
};
// src/parsers/base-parser.ts
var BaseParser = class {
/** Event Emitter */
eventEmitter;
/** Handler for current state */
stateHandler;
/** Handler for strings, including multybytes */
stringHandler;
/** Handler for utf8 symbols */
utf8Handler;
errorHandler;
tokenHandler;
encoder;
constructor() {
this.eventEmitter = new EventEmitter();
this.stringHandler = new StringHandler();
this.stateHandler = new StateHandler(this.eventEmitter, this.stringHandler);
this.errorHandler = new ErrorHandler(this.eventEmitter, this.stateHandler);
this.utf8Handler = new UTF8Handler();
this.tokenHandler = new TokenHandler(this.eventEmitter, this.stateHandler, this.errorHandler);
this.encoder = new TextEncoder();
}
/**
* Parses and processes numeric values.
* Can be overridden for custom number handling.
* @param text - The numeric text to parse.
*/
numberReviver(text) {
const result = Number(text);
if (isNaN(result)) {
const error = new Error(`Invalid number: ${text}`);
this.eventEmitter.handleError(error);
return error;
}
if (/^[0-9]+$/.test(text) && result.toString() !== text) {
this.tokenHandler.handleToken(TOKENS.STRING, text);
} else {
this.tokenHandler.handleToken(TOKENS.NUMBER, result);
}
}
};
// src/parsers/parser.ts
var Parser = class extends BaseParser {
getEmitter() {
return this.eventEmitter;
}
onToken(callback) {
this.eventEmitter.onToken(callback);
}
onValue(callback) {
this.eventEmitter.onValue(callback);
}
onError(callback) {
this.eventEmitter.onError(callback);
}
getLastValue() {
return this.stateHandler.getLastValue();
}
getLastIncompleteValue(omitEmpty = false) {
return this.stateHandler.getLastIncompleteValue(omitEmpty);
}
getCurrentKey() {
return this.stateHandler.getKey();
}
getStack() {
return this.stateHandler.getStack();
}
getOffset() {
return this.stateHandler.getOffset();
}
/**
* Processes an incoming buffer of JSON data.
* @param buffer - The input JSON chunk.
*/
write(buffer) {
if (typeof buffer === "string") {
buffer = this.encoder.encode(buffer);
}
let n;
let res = 0;
let hasError;
for (let i = 0; i < buffer.length; i++) {
n = buffer[i];
switch (this.stateHandler.getTokenizerState()) {
case TOKENIZER_STATES.START:
hasError = this.processStartState(n, buffer, i);
if (typeof hasError !== "undefined") return hasError;
break;
case TOKENIZER_STATES.STRING1:
res = this.processStringStartingState(n, buffer, i);
if (typeof res !== "number") return res;
i = res;
break;
case TOKENIZER_STATES.STRING2:
res = this.processStringBackslashState(n, buffer, i);
if (typeof res !== "number") return res;
i = res;
break;
case TOKENIZER_STATES.STRING3:
case TOKENIZER_STATES.STRING4:
case TOKENIZER_STATES.STRING5:
case TOKENIZER_STATES.STRING6:
res = this.processStringUnicodeState(n, buffer, i);
if (typeof res !== "number") return res;
i = res;
break;
case TOKENIZER_STATES.NUMBER1:
case TOKENIZER_STATES.NUMBER3:
res = this.processNumberState(n, i);
if (typeof res !== "number") return res;
i = res;
break;
case TOKENIZER_STATES.TRUE1:
case TOKENIZER_STATES.TRUE2:
case TOKENIZER_STATES.TRUE3:
hasError = this.processTrueState(n, buffer, i);
if (typeof hasError !== "undefined") return hasError;
break;
case TOKENIZER_STATES.FALSE1:
case TOKENIZER_STATES.FALSE2:
case TOKENIZER_STATES.FALSE3:
case TOKENIZER_STATES.FALSE4:
hasError = this.processFalseState(n, buffer, i);
if (typeof hasError !== "undefined") return hasError;
break;
case TOKENIZER_STATES.NULL1:
case TOKENIZER_STATES.NULL2:
case TOKENIZER_STATES.NULL3:
this.processNullState(n, buffer, i);
break;
default:
this.errorHandler.charError(buffer, i);
}
}
}
/**
* Processes tokens at the root level (e.g. `{`, `[`, `:`, `,`).
*/
processStartState(n, buffer, i) {
this.stateHandler.incOffset();
if (n === CHARS.LEFT_BRACE) this.tokenHandler.handleToken(TOKENS.LEFT_BRACE, "{");
else if (n === CHARS.RIGHT_BRACE) this.tokenHandler.handleToken(TOKENS.RIGHT_BRACE, "}");
else if (n === CHARS.LEFT_BRACKET) this.tokenHandler.handleToken(TOKENS.LEFT_BRACKET, "[");
else if (n === CHARS.RIGHT_BRACKET) this.tokenHandler.handleToken(TOKENS.RIGHT_BRACKET, "]");
else if (n === CHARS.COLON) this.tokenHandler.handleToken(TOKENS.COLON, ":");
else if (n === CHARS.COMMA) this.tokenHandler.handleToken(TOKENS.COMMA, ",");
else if (n === CHARS.T) this.stateHandler.setTokenizerState(TOKENIZER_STATES.TRUE1);
else if (n === CHARS.F) this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE1);
else if (n === CHARS.N) this.stateHandler.setTokenizerState(TOKENIZER_STATES.NULL1);
else if (n === CHARS.QUOTE) {
this.stringHandler.reset("");
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.MINUS) {
this.stringHandler.resetString("-");
this.stateHandler.setTokenizerState(TOKENIZER_STATES.NUMBER1);
} else if (n >= CHARS.ZERO && n <= CHARS.NINE) {
this.stringHandler.setFromCharcode(n);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.NUMBER3);
} else if (!CHARS.WHITESPACES.includes(n)) {
return this.errorHandler.charError(buffer, i);
}
}
processStringUnicodeState(n, buffer, i) {
const isNumber = n >= CHARS.ZERO && n <= CHARS.NINE;
const isBigHexLetter = n >= CHARS.BIG_A && n <= CHARS.BIG_F;
const isHexLetter = n >= CHARS.A && n <= CHARS.F;
if (isNumber || isBigHexLetter || isHexLetter) {
this.stringHandler.addUnicodeFromCharCode(n);
const currentState = this.stateHandler.getTokenizerState();
this.stateHandler.incTokenizerState();
if (currentState === TOKENIZER_STATES.STRING6) {
const intVal = parseInt(this.stringHandler.getUnicode(), 16);
this.stringHandler.setUnicode(void 0);
if (this.stringHandler.getHighSurrogate() !== void 0 && intVal >= UTF8_BOUNDS.LOW_SURROGATE_START && intVal <= UTF8_BOUNDS.LOW_SURROGATE_END) {
this.stringHandler.appendStringBuf(this.encoder.encode(String.fromCharCode(this.stringHandler.getHighSurrogate() || 0, intVal)));
this.stringHandler.setHighSurrogate(void 0);
} else if (this.stringHandler.getHighSurrogate() === void 0 && intVal >= UTF8_BOUNDS.HIGH_SURROGATE_START && intVal <= UTF8_BOUNDS.HIGH_SURROGATE_END) {
this.stringHandler.setHighSurrogate(intVal);
} else {
if (this.stringHandler.getHighSurrogate() !== void 0) {
this.stringHandler.appendStringBuf(this.encoder.encode(String.fromCharCode(this.stringHandler.getHighSurrogate() || 0)));
this.stringHandler.setHighSurrogate(void 0);
}
this.stringHandler.appendStringBuf(this.encoder.encode(String.fromCharCode(intVal)));
}
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
}
} else {
return this.errorHandler.charError(buffer, i);
}
return i;
}
processStringBackslashState(n, buffer, i) {
n = buffer[i];
if (n === CHARS.QUOTE) {
this.stringHandler.appendStringChar(n);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.BACKSLASH) {
this.stringHandler.appendStringChar(CHARS.BACKSLASH);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.FORWARD_SLASH) {
this.stringHandler.appendStringChar(CHARS.FORWARD_SLASH);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.B) {
this.stringHandler.appendStringChar(CHARS.BACKSPACE);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.F) {
this.stringHandler.appendStringChar(CHARS.FORM_FEED);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.N) {
this.stringHandler.appendStringChar(CHARS.NEWLINE);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.R) {
this.stringHandler.appendStringChar(CHARS.CARRIAGE_RETURN);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.T) {
this.stringHandler.appendStringChar(CHARS.TAB);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1);
} else if (n === CHARS.U) {
this.stringHandler.setUnicode("");
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING3);
} else {
return this.errorHandler.charError(buffer, i);
}
return i;
}
processStringStartingState(n, buffer, i) {
if (this.utf8Handler.hasBytesRemaining()) {
const remainingBytes = this.utf8Handler.getBytesRemaining();
this.stringHandler.appendStringBuf(
this.utf8Handler.getRemainingBytesInBuff(buffer)
);
i = i + remainingBytes - 1;
} else if (!this.utf8Handler.hasBytesRemaining() && n >= UTF8_BOUNDS.MIN_MULTI_BYTE) {
if (n <= UTF8_BOUNDS.INVALID_LOWER || n > UTF8_BOUNDS.BYTE_4_MAX) {
const error = new Error(
"Invalid UTF-8 character at position " + i + " in state " + this.stateHandler.getTokenizerStateName() + " with value: " + n
);
this.eventEmitter.handleError(error);
return error;
}
if (n >= UTF8_BOUNDS.BYTE_2_MIN && n <= UTF8_BOUNDS.BYTE_2_MAX) this.utf8Handler.setBytesInSequence(2);
if (n >= UTF8_BOUNDS.BYTE_3_MIN && n <= UTF8_BOUNDS.BYTE_3_MAX) this.utf8Handler.setBytesInSequence(3);
if (n >= UTF8_BOUNDS.BYTE_4_MIN && n <= UTF8_BOUNDS.BYTE_4_MAX) this.utf8Handler.setBytesInSequence(4);
if (this.utf8Handler.getBytesInSequence() + i > buffer.length) {
this.utf8Handler.handleBoundarySplit(i, buffer);
i = buffer.length - 1;
} else {
this.stringHandler.appendStringBuf(buffer, i, i + this.utf8Handler.getBytesInSequence());
i = i + this.utf8Handler.getBytesInSequence() - 1;
}
} else if (n === CHARS.QUOTE) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.START);
this.stringHandler.flushBuffer();
this.tokenHandler.handleToken(TOKENS.STRING, this.stringHandler.getString());
this.stateHandler.addOffset(this.encoder.encode(this.stringHandler.getString()).length + 1);
this.stringHandler.resetString();
} else if (n === CHARS.BACKSLASH) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING2);
} else if (n >= CHARS.SPACE) {
this.stringHandler.appendStringChar(n);
} else {
return this.errorHandler.charError(buffer, i);
}
return i;
}
/**
* Parses numeric values.
*/
processNumberState(n, i) {
if (n >= CHARS.ZERO && n <= CHARS.NINE || n === CHARS.DOT || n === CHARS.E || n === CHARS.BIG_E || n === CHARS.PLUS || n === CHARS.MINUS) {
this.stringHandler.addFromCharCode(n);
this.stateHandler.setTokenizerState(TOKENIZER_STATES.NUMBER3);
} else {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.START);
const error = this.numberReviver(this.stringHandler.getString());
if (error) {
return error;
}
this.stateHandler.addOffset(this.stringHandler.getString().length - 1);
this.stringHandler.resetString();
i--;
}
return i;
}
processFalseState(n, buffer, i) {
const tokenizerState = this.stateHandler.getTokenizerState();
if (tokenizerState === TOKENIZER_STATES.FALSE1 && n === CHARS.A) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE2);
return;
}
if (tokenizerState === TOKENIZER_STATES.FALSE2 && n === CHARS.L) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE3);
return;
}
if (tokenizerState === TOKENIZER_STATES.FALSE3 && n === CHARS.S) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE4);
return;
}
if (tokenizerState === TOKENIZER_STATES.FALSE4 && n === CHARS.E) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.START);
this.tokenHandler.handleToken(TOKENS.FALSE, false);
this.stateHandler.addOffset(4);
return;
}
return this.errorHandler.charError(buffer, i);
}
processTrueState(n, buffer, i) {
const tokenizerState = this.stateHandler.getTokenizerState();
if (tokenizerState === TOKENIZER_STATES.TRUE1 && n === CHARS.R) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.TRUE2);
return;
}
if (tokenizerState === TOKENIZER_STATES.TRUE2 && n === CHARS.U) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.TRUE3);
return;
}
if (tokenizerState === TOKENIZER_STATES.TRUE3 && n === CHARS.E) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.START);
this.tokenHandler.handleToken(TOKENS.TRUE, true);
this.stateHandler.addOffset(3);
return;
}
return this.errorHandler.charError(buffer, i);
}
/**
* Processes `null` value.
*/
processNullState(n, buffer, i) {
const tokenizerState = this.stateHandler.getTokenizerState();
if (tokenizerState === TOKENIZER_STATES.NULL1 && n === CHARS.U) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.NULL2);
return;
}
if (tokenizerState === TOKENIZER_STATES.NULL2 && n === CHARS.L) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.NULL3);
return;
}
if (tokenizerState === TOKENIZER_STATES.NULL3 && n === CHARS.L) {
this.stateHandler.setTokenizerState(TOKENIZER_STATES.START);
this.tokenHandler.handleToken(TOKENS.NULL, null);
this.stateHandler.addOffset(3);
return;
}
return this.errorHandler.charError(buffer, i);
}
};
export {
constants_exports as CONSTANTS,
Parser
};
//# sourceMappingURL=index.js.map