Skip to content

Commit

Permalink
Add prepare package script and
Browse files Browse the repository at this point in the history
  • Loading branch information
juanjoDiaz committed Nov 3, 2020
1 parent a2480cb commit 384f72a
Show file tree
Hide file tree
Showing 9 changed files with 1,001 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ typings/

# Nuxt.js build / generate output
.nuxt
dist
dist/*
!dist/deno

# Gatsby files
.cache/
Expand Down
5 changes: 5 additions & 0 deletions dist/deno/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as Tokenizer } from './tokenizer.ts';
export { default as Parser } from './parser.ts';
export { default as JsonParser } from './jsonparse.ts';
export * as utf8 from './utils/utf-8.ts';
export { TokenType } from './utils/constants.ts';
34 changes: 34 additions & 0 deletions dist/deno/jsonparse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Tokenizer, { TokenizerOptions } from './tokenizer.ts';
import Parser, { StackElement, ParserOptions } from './parser.ts';

interface JSONParserOpts extends TokenizerOptions, ParserOptions {}

export default class JSONParser {
private tokenizer: Tokenizer;
private parser: Parser;

constructor(opts: JSONParserOpts = {}) {
this.tokenizer = new Tokenizer(opts);
this.parser = new Parser(opts);
this.tokenizer.onToken = this.parser.write.bind(this.parser);
}

public write(input: Iterable<number> | string): void {
this.tokenizer.write(input);
}

public set onToken(cb: (token: number, value: any, offset: number) => void) {
this.tokenizer.onToken = cb;
}

public set onValue(
cb: (
value: any,
key: string | number | undefined,
parent: any,
stack: StackElement[],
) => void,
) {
this.parser.onValue = cb;
}
}
232 changes: 232 additions & 0 deletions dist/deno/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { TokenType } from './utils/constants.ts';

const {
LEFT_BRACE,
RIGHT_BRACE,
LEFT_BRACKET,
RIGHT_BRACKET,
COLON,
COMMA,
TRUE,
FALSE,
NULL,
STRING,
NUMBER,
} = TokenType;

// Parser States
enum ParserState {
VALUE,
KEY,
COLON,
COMMA,
STOP,
ERROR,
}
// Parser Modes
export enum ParserMode {
OBJECT,
ARRAY,
}

export interface StackElement {
key: string | number | undefined;
value: any;
mode: ParserMode | undefined;
emit: boolean;
}

export interface ParserOptions {
path?: string;
keepStack?: boolean,
}

const defaultOpts: ParserOptions = {
path: undefined,
keepStack: true,
};

export default class Parser {
private readonly path?: string[];
private readonly keepStack: boolean;
private state: ParserState = ParserState.VALUE;
private mode: ParserMode | undefined = undefined;
private key: string | number | undefined = undefined;
private value: any = undefined;
private stack: StackElement[] = [];

constructor(opts?: ParserOptions) {
opts = { ...defaultOpts, ...opts };

if (opts.path === undefined || opts.path === '$*') {
this.path = undefined;
} else {
if (!opts.path.startsWith('$')) throw new Error(`Invalid selector "${opts.path}". Should start with "$".`);
this.path = opts.path.split('.').slice(1);
if (this.path.includes('')) throw new Error(`Invalid selector "${opts.path}". ".." syntax not supported.`);
}

this.keepStack = opts.keepStack as boolean;
}

private shouldEmit(): boolean {
if (this.path === undefined) return true;
if (this.path.length !== this.stack.length) return false;

for (let i = 0; i < this.path.length - 1; i++) {
const selector = this.path[i];
const key = this.stack[i + 1].key;
if (selector === '*') continue;
if (selector !== key) return false;
}

const selector = this.path[this.path.length - 1];
if (selector === '*') return true;
return selector === this.key?.toString();
}

private push(): void {
this.stack.push({ key: this.key, value: this.value, mode: this.mode, emit: this.shouldEmit() });
}

private pop(): void {
const value = this.value;

let emit;
({ key: this.key, value: this.value, mode: this.mode, emit } = this.stack
.pop() as StackElement);

this.emit(value, emit)

this.state = this.mode !== undefined
? ParserState.COMMA
: ParserState.VALUE;
}

private emit(value: any, emit: boolean) {
if (!this.keepStack && this.stack.every(item => !item.emit)) delete this.value[this.key as string | number];
if (emit) {
this.onValue(value, this.key, this.value, this.stack);
}
}

public write(token: TokenType, value: any): void{
if (this.state === ParserState.VALUE) {
if (
token === STRING || token === NUMBER || token === TRUE ||
token === FALSE || token === NULL
) {
if (this.mode === ParserMode.OBJECT) {
this.value[this.key as string] = value;
this.state = ParserState.COMMA;
} else if (this.mode === ParserMode.ARRAY) {
this.value.push(value);
this.state = ParserState.COMMA;
}

this.emit(value, this.shouldEmit());
return;
}

if (token === LEFT_BRACE) {
this.push();
if (this.mode === ParserMode.OBJECT) {
this.value = this.value[this.key as string] = {};
} else if (this.mode === ParserMode.ARRAY) {
const val = {};
this.value.push(val);
this.value = val;
} else {
this.value = {};
}
this.mode = ParserMode.OBJECT;
this.state = ParserState.KEY;
this.key = undefined;
return;
}

if (token === LEFT_BRACKET) {
this.push();
if (this.mode === ParserMode.OBJECT) {
this.value = this.value[this.key as string] = [];
} else if (this.mode === ParserMode.ARRAY) {
const val: any[] = [];
this.value.push(val);
this.value = val;
} else {
this.value = [];
}
this.mode = ParserMode.ARRAY;
this.state = ParserState.VALUE;
this.key = 0;
return;
}

if (
this.mode === ParserMode.ARRAY && token === RIGHT_BRACKET &&
this.value.length === 0
) {
this.pop();
return;
}
}

if (this.state === ParserState.KEY) {
if (token === STRING) {
this.key = value;
this.state = ParserState.COLON;
return;
}

if (token === RIGHT_BRACE && Object.keys(this.value).length === 0) {
this.pop();
return;
}
}

if (this.state === ParserState.COLON) {
if (token === COLON) {
this.state = ParserState.VALUE;
return;
}
}

if (this.state === ParserState.COMMA) {
if (token === COMMA) {
if (this.mode === ParserMode.ARRAY) {
this.state = ParserState.VALUE;
(this.key as number) += 1;
return;
}

/* istanbul ignore else */
if (this.mode === ParserMode.OBJECT) {
this.state = ParserState.KEY;
return;
}
}

if (
token === RIGHT_BRACE && this.mode === ParserMode.OBJECT ||
token === RIGHT_BRACKET && this.mode === ParserMode.ARRAY
) {
this.pop();
return;
}
}

throw new Error(
"Unexpected " + TokenType[token] + (("(" + JSON.stringify(value) + ")")) +
" in state " + ParserState[this.state],
);
}

public onValue(
value: any,
key: string | number | undefined,
parent: any,
stack: StackElement[],
): void {
// Override me
}
}
Loading

0 comments on commit 384f72a

Please sign in to comment.