Last active
August 21, 2021 01:34
-
-
Save Droogans/88a150817886d6dba827aa74fe26747d to your computer and use it in GitHub Desktop.
yieldable-json in typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs'; | |
import path from 'path'; | |
import {parseAsync} from './yieldable-json'; | |
(async () => { | |
const jsonFileLocation: string = process.argv.slice(-1)[0] as string; | |
const jsonFilePath = path.resolve(process.cwd(), jsonFileLocation); | |
let result; | |
for await (const fragment of parseAsync(await fs.promises.readFile(jsonFilePath))) { | |
if (!Array.isArray(fragment.property)) { | |
continue; | |
} | |
result = fragment.property; | |
break; | |
} | |
// return otherFn(result, ...) | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @ts-nocheck | |
/* ************************************************************************** | |
* | |
* (c) Copyright IBM Corp. 2017 | |
* http://www.opensource.org/licenses/apache2.0.php | |
* | |
* Contributors: | |
* Multiple authors (IBM Corp.) - initial implementation and documentation | |
***************************************************************************/ | |
/* | |
github.com/DefinitelyTyped/DefinitelyTyped/discussions/55269 | |
https://www.npmjs.com/package/yieldable-json doesn't have typescript types | |
**/ | |
const validateIntensity = (intensity: number): number => { | |
intensity = Math.round(intensity); | |
if (intensity > 0 && intensity <= 32) | |
return intensity; | |
else if (intensity <= 0) | |
return 1; | |
else | |
return 32; | |
}; | |
/* | |
https://stackoverflow.com/a/48342359/881224 | |
**/ | |
class ParseError extends Error { | |
constructor(message?: string) { | |
super(message); | |
Object.setPrototypeOf(this, new.target.prototype); | |
} | |
get [Symbol.toStringTag]() { return 'ParseError'; } | |
static get [Symbol.species]() { return ParseError; } | |
} | |
class ParseWrapper { | |
text: string; | |
reviver: any; | |
intensity: number; | |
counter: number; | |
keyN: number; | |
parseStr: string; | |
at: number; | |
ch: string; | |
word: string; | |
constructor(text: Buffer, reviver: any | any[], intensity: number) { | |
this.text = text.toString(); | |
this.reviver = reviver; | |
this.counter = 0; | |
this.intensity = intensity; | |
this.keyN = 0; | |
this.parseStr = this.text; | |
this.at = 0; | |
this.ch = ' '; | |
this.word = ''; | |
} | |
seek() { | |
do { | |
this.ch = this.parseStr.charAt(this.at); | |
this.at++; | |
} while (this.ch <= ' '); | |
return this.ch; | |
}; | |
unseek() { | |
this.ch = this.parseStr.charAt(--this.at); | |
} | |
wordCheck() { | |
this.word = ''; | |
do { | |
this.word += this.ch; | |
this.seek(); | |
} while (this.ch.match(/[a-z]/i)); | |
this.parseStr = this.parseStr.slice(this.at - 1); | |
this.at = 0; | |
return this.word; | |
}; | |
normalizeUnicodedString() { | |
let inQuotes = ' '; | |
let tempIndex = this.at; | |
let index = 0; | |
let slash = 0; | |
let c = '"'; | |
while (c) { | |
index = this.parseStr.indexOf('"', tempIndex + 1); | |
tempIndex = index; | |
this.ch = this.parseStr.charAt(tempIndex - 1); | |
while (this.ch === '\\') { | |
slash++; | |
this.ch = this.parseStr.charAt(tempIndex - (slash + 1)); | |
} | |
if (slash % 2 === 0) { | |
inQuotes = this.parseStr.substring(this.at, index); | |
this.parseStr = this.parseStr.slice(++index); | |
slash = 0; | |
break; | |
} else | |
slash = 0; | |
} | |
// When parsing string values, look for " and \ characters. | |
index = inQuotes.indexOf('\\'); | |
while (index >= 0) { | |
let escapee = { | |
'"': '"', | |
'\'': '\'', | |
'/': '/', | |
'\\': '\\', | |
b: '\b', | |
f: '\f', | |
n: '\n', | |
r: '\r', | |
t: '\t', | |
} as { [key: string]: string }; | |
let hex = 0; | |
let i = 0; | |
let uffff = 0; | |
this.at = index; | |
this.ch = inQuotes.charAt(++this.at); | |
if (this.ch === 'u') { | |
uffff = 0; | |
for (i = 0; i < 4; i += 1) { | |
hex = parseInt(this.ch = inQuotes.charAt(++this.at), 16); | |
if (!isFinite(hex)) { | |
break; | |
} | |
uffff = uffff * 16 + hex; | |
} | |
inQuotes = inQuotes.slice(0, index) + | |
String.fromCharCode(uffff) + inQuotes.slice(index + 6); | |
this.at = index; | |
} else if (typeof escapee[this.ch] === 'string') { | |
inQuotes = inQuotes.slice(0, index) + | |
escapee[this.ch] + inQuotes.slice(index + 2); | |
this.at = index + 1; | |
} else | |
break; | |
index = inQuotes.indexOf('\\', this.at); | |
} | |
this.at = 0; | |
return inQuotes; | |
} | |
async* [Symbol.asyncIterator]() { | |
const yielded = await this.parseYield(); | |
console.log(typeof yielded); | |
yield yielded | |
} | |
async parseYield(): Promise<any> { | |
let key = ''; | |
let returnObj: { [key: string]: any } = {}; | |
let returnArr: string[] = []; | |
let v = ''; | |
let inQuotes = ''; | |
let num = 0; | |
let numHolder = ''; | |
const addup = () => { | |
numHolder += this.ch; | |
this.seek(); | |
}; | |
// Handle premitive types. eg: JSON.parse(21) | |
if (typeof this.parseStr === 'number' || typeof this.parseStr === 'boolean' || | |
this.parseStr === null) { | |
this.parseStr = ''; | |
return this.text; | |
} else if (typeof this.parseStr === 'undefined') { | |
return this.text; | |
} else if (this.parseStr.charAt(0) === '[' && this.parseStr.charAt(1) === ']') { | |
this.parseStr = ''; | |
return []; | |
} else if (this.parseStr.charAt(0) === '{' && this.parseStr.charAt(1) === '}') { | |
this.parseStr = ''; | |
return {}; | |
} else { | |
// Common case: non-premitive types. | |
if (this.keyN !== 1) | |
this.seek(); | |
switch (this.ch) { | |
case '{': | |
// Object case | |
this.seek(); | |
// "this condition will always return false" | |
// due to an unstable, self-modifying class instance state for `this.ch` | |
if (this.ch === '}') { | |
this.parseStr = this.parseStr.slice(this.at); | |
this.at = 0; | |
return returnObj; | |
} | |
do { | |
if (this.ch !== '"') | |
this.seek(); | |
this.keyN = 1; | |
key = await this.parseYield(); | |
this.keyN = 0; | |
this.seek(); | |
returnObj[key] = await this.parseYield(); | |
this.seek(); | |
if (this.ch === '}') { | |
this.parseStr = this.parseStr.slice(this.at); | |
this.at = 0; | |
return returnObj; | |
} | |
} while (this.ch === ','); | |
return new ParseError('Bad object'); | |
case '[': | |
// Array case | |
this.seek(); | |
if (this.ch === ']') { | |
this.parseStr = this.parseStr.slice(this.at); | |
this.at = 0; | |
return returnArr; | |
} | |
this.unseek(); | |
do { | |
v = await this.parseYield(); | |
returnArr.push(v); | |
this.seek(); | |
if (this.ch === ']') { | |
this.parseStr = this.parseStr.slice(this.at); | |
this.at = 0; | |
return returnArr; | |
} | |
} while (this.ch === ','); | |
return new ParseError('Bad array'); | |
case '"': | |
this.parseStr = this.parseStr.slice(this.at - 1); | |
this.at = 0; | |
if (this.parseStr.charAt(0) === '"' && this.parseStr.charAt(1) === '"') { | |
this.parseStr = this.parseStr.slice(2); | |
this.at = 0; | |
return inQuotes; | |
} else { | |
this.seek(); | |
return this.normalizeUnicodedString(); | |
} | |
case '0': | |
case '1': | |
case '2': | |
case '3': | |
case '4': | |
case '5': | |
case '6': | |
case '7': | |
case '8': | |
case '9': | |
case '-': | |
if (this.ch === '-') addup(); | |
do { | |
addup(); | |
if (this.ch === '.' || this.ch === 'e' || this.ch === 'E' || | |
this.ch === '-' || this.ch === '+' || | |
(this.ch >= String.fromCharCode(65) && | |
this.ch <= String.fromCharCode(70))) | |
addup(); | |
} while (this.ch === '-' || this.ch === '+' || (isFinite(this.ch) && this.ch !== '')); | |
num = Number(numHolder); | |
this.parseStr = this.parseStr.slice(this.at - 1); | |
this.at = 0; | |
return num; | |
case 't': | |
this.word = this.wordCheck(); | |
if (this.word === 'true') | |
return true; | |
else return new ParseError('Unexpected character'); | |
case 'f': | |
this.word = this.wordCheck(); | |
if (this.word === 'false') | |
return false; | |
else return new ParseError('Unexpected character'); | |
case 'n': | |
this.word = this.wordCheck(); | |
if (this.word === 'null') | |
return null; | |
else return new ParseError('Unexpected character'); | |
default: | |
return new ParseError('Unexpected character'); | |
} | |
} | |
} | |
revive(yieldedObject: any, key: string) { | |
let k = ''; | |
let v = ''; | |
let val = yieldedObject[key]; | |
if (val && typeof val === 'object') { | |
for (k in val) { | |
if (Object.prototype.hasOwnProperty.call(val, k)) { | |
v = this.revive(val, k); | |
if (v !== undefined) | |
val[k] = v; | |
else | |
delete val[k]; | |
} | |
} | |
} | |
return this.reviver.call(yieldedObject, key, val); | |
}; | |
} | |
export const parseAsync = (data: Buffer | string, reviver?: any | number[], intensity = 1): ParseWrapper => { | |
return new ParseWrapper(data as Buffer, reviver, validateIntensity(intensity)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment