Skip to main content
Home
This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers
This package works with Cloudflare Workers
This package works with Node.js
This package works with Deno
This package works with Bun
This package works with Browsers
JSR Score
58%
Published
a year ago (0.3.5)
Package root>parse.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
const NUM_CHARS = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); export type AstRoot = | AstUndefined | AstNull | AstBool | AstNumber | AstBigInt | AstString | AstSymbol | AstArray | AstObject | AstRegExp | AstDate | AstSet | AstMap; export type AstAny = AstRoot | AstRef; export type AstUndefined = [type: 0]; export type AstNull = [type: 1]; export type AstBool = [type: 2, value: boolean]; export type AstNumber = [type: 3, value: number]; export type AstBigInt = [type: 4, value: bigint]; export type AstString = [type: 5, value: string]; export type AstSymbol = [type: 6, description: string | null]; export type AstArray = [type: 16, items: AstAny[]]; export type AstObject = [ type: 17, name: string | null, entries: [AstString, AstAny][], ]; export type AstRegExp = [type: 32, pattern: string, flags: string | null]; export type AstDate = [type: 33, timestamp: number]; export type AstSet = [type: 34, items: AstAny[]]; export type AstMap = [type: 35, entries: [AstAny, AstAny][]]; export type AstRef = [type: 64, index: number]; let buf = ""; let pos = 0; function consume(s: string) { for (const c of s) { if (buf[pos] !== c) { throw error(); } pos++; } } function white() { while (1) { switch (buf[pos]) { case "\t": case "\v": case "\f": case " ": case "\u00A0": case "\uFEFF": case "\n": case "\r": case "\u2028": case "\u2029": pos++; break; default: return; } } } function error() { return new SyntaxError( buf[pos] ? `Unexpected token '${buf[pos]}' in SuperSerial at position ${pos + 1}` : "Unexpected end of SuperSerial input", ); } function parseAny(): AstAny { white(); if (buf[pos] === "$") { pos++; let result = ""; while (NUM_CHARS.has(buf[pos])) { result += buf[pos++]; } return [64, +result]; } return parseRoot(); } function parseRoot(): AstRoot { white(); switch (buf[pos]) { case "-": case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": { return parseNumber(); } case '"': return parseString(); case "[": return parseArray(); case "/": return parseRegExp(); default: { const name = keyword(); switch (name) { case "null": return [1]; case "true": return [2, true]; case "false": return [2, false]; } if (buf[pos] === "{") { return parseObject(name); } switch (name) { case "undefined": return [0]; case "NaN": return [3, NaN]; case "Infinity": return [3, Infinity]; } if (buf[pos] === "(") { switch (name) { case "Map": return parseMap(); case "Set": return parseSet(); case "Date": return parseDate(); case "Symbol": return parseSymbol(); default: throw error(); } } } } throw error(); } function parseNumber(): AstNumber | AstBigInt { let result = ""; let mult = 1; if (buf[pos] === "-") { pos++; mult = -1; } if (buf[pos] === "I") { pos++; consume("nfinity"); return [3, mult * Infinity]; } if (NUM_CHARS.has(buf[pos])) { result += buf[pos++]; } else { throw error(); } while (NUM_CHARS.has(buf[pos])) { result += buf[pos++]; } if (buf[pos] === "n") { pos++; return [4, BigInt(result) * BigInt(mult)]; } else { if (buf[pos] === ".") { result += buf[pos++]; while (NUM_CHARS.has(buf[pos])) { result += buf[pos++]; } } if (buf[pos] === "e" || buf[pos] === "E") { result += buf[pos++]; if (buf[pos] === "-" || buf[pos] === "+") { result += buf[pos++]; } if (NUM_CHARS.has(buf[pos])) { result += buf[pos++]; } else { throw error(); } while (NUM_CHARS.has(buf[pos])) { result += buf[pos++]; } } } return [3, +result * mult]; } function parseString(): AstString { let result = ""; pos++; while (1) { if (pos >= buf.length) { break; } switch (buf[pos]) { case '"': pos++; return [5, result]; case "\\": pos++; switch (buf[pos]) { case "u": { pos++; let uffff = 0; for (let i = 0; i < 4; i++) { const hex = parseInt(buf[pos], 16); if (!isFinite(hex)) { throw error(); } pos++; uffff = uffff * 16 + hex; } result += String.fromCharCode(uffff); continue; } case '"': pos++; result += '"'; continue; case "\\": pos++; result += "\\"; continue; case "b": pos++; result += "\b"; continue; case "f": pos++; result += "\f"; continue; case "n": pos++; result += "\n"; continue; case "r": pos++; result += "\r"; continue; case "t": pos++; result += "\t"; continue; } break; default: result += buf[pos++]; continue; } break; } throw error(); } function parseArray(): AstArray { pos++; white(); if (buf[pos] === "]") { pos++; return [16, []]; } const result = [] as AstAny[]; result.push(parseAny()); white(); while (buf[pos] === ",") { pos++; result.push(parseAny()); white(); } if (buf[pos] === "]") { pos++; return [16, result]; } throw error(); } function parseObject(name: string | null = null): AstObject { pos++; white(); if (buf[pos] === "}") { pos++; return [17, name, []]; } const result = [] as [AstString, AstAny][]; while (1) { const key = parseString(); // TODO Symbol white(); if (buf[pos] !== ":") { throw error(); } pos++; result.push([key, parseAny()]); white(); if (buf[pos] === ",") { pos++; white(); continue; } if (buf[pos] === "}") { pos++; return [17, name, result]; } break; } throw error(); } function parseRegExp(): AstRegExp { pos++; let pattern = ""; if (buf[pos] === "/") { throw error(); } while (buf[pos]) { if (buf[pos] === "/") { pos++; switch (buf[pos]) { case "i": { pos++; switch (buf[pos]) { case "m": { pos++; if (buf[pos] === "g") { pos++; return [32, pattern, "img"]; } else { return [32, pattern, "im"]; } } case "g": { pos++; if (buf[pos] === "m") { pos++; return [32, pattern, "igm"]; } else { return [32, pattern, "ig"]; } } default: { return [32, pattern, "i"]; } } } case "m": { pos++; switch (buf[pos]) { case "i": { pos++; if (buf[pos] === "g") { pos++; return [32, pattern, "mig"]; } else { return [32, pattern, "mi"]; } } case "g": { pos++; if (buf[pos] === "i") { pos++; return [32, pattern, "mgi"]; } else { return [32, pattern, "mg"]; } } default: { return [32, pattern, "m"]; } } } case "g": { pos++; switch (buf[pos]) { case "m": { pos++; if (buf[pos] === "i") { pos++; return [32, pattern, "gmi"]; } else { return [32, pattern, "gm"]; } } case "i": { pos++; if (buf[pos] === "m") { pos++; return [32, pattern, "gim"]; } else { return [32, pattern, "gi"]; } } default: { return [32, pattern, "g"]; } } } } return [32, pattern, null]; } else if (buf[pos] === "\\") { pattern += buf[pos++]; pattern += buf[pos++]; } else { pattern += buf[pos++]; } } throw error(); } function parseSet(): AstSet { pos++; white(); if (buf[pos] === ")") { pos++; return [34, []]; } const items = [] as AstAny[]; items.push(parseAny()); white(); while (buf[pos] === ",") { pos++; items.push(parseAny()); white(); } if (buf[pos] === ")") { pos++; return [34, items]; } throw error(); } function parseMap(): AstMap { pos++; white(); if (buf[pos] === ")") { pos++; return [35, []]; } const entries = [] as [AstAny, AstAny][]; while (1) { const key = parseAny(); white(); consume("=>"); white(); const value = parseAny(); entries.push([key, value]); white(); if (buf[pos] === ",") { pos++; white(); continue; } if (buf[pos] === ")") { pos++; break; } throw error(); } return [35, entries]; } function parseSymbol(): AstSymbol { pos++; white(); if (buf[pos] === ")") { pos++; return [6, null]; } if (buf[pos] === '"') { const valueString = parseString(); white(); if (buf[pos] === ")") { pos++; return [6, valueString[1]]; } } throw error(); } function parseDate(): AstDate { pos++; white(); let value = ""; let mult = 1; if (buf[pos] === "-") { pos++; mult = -1; } if (NUM_CHARS.has(buf[pos])) { value += buf[pos++]; } else { throw error(); } while (NUM_CHARS.has(buf[pos])) { value += buf[pos++]; } if (buf[pos] === ".") { value += buf[pos++]; while (NUM_CHARS.has(buf[pos])) { value += buf[pos++]; } } if (buf[pos] === "e" || buf[pos] === "E") { value += buf[pos++]; if (buf[pos] === "-" || buf[pos] === "+") { value += buf[pos++]; } if (NUM_CHARS.has(buf[pos])) { value += buf[pos++]; } else { throw error(); } while (NUM_CHARS.has(buf[pos])) { value += buf[pos++]; } } white(); if (buf[pos] === ")") { pos++; return [33, +value * mult]; } throw error(); } function keyword(): string | null { let chartCode = buf.charCodeAt(pos); let result = ""; if ( chartCode >= 65 && chartCode <= 90 || // UPPERCASE chartCode >= 97 && chartCode <= 122 || // lowercase chartCode === 95 // _ ) { result += buf[pos++]; } else { return null; } while ((chartCode = buf.charCodeAt(pos))) { if ( chartCode >= 65 && chartCode <= 90 || // UPPERCASE chartCode >= 97 && chartCode <= 122 || // lowercase chartCode >= 48 && chartCode <= 57 || // number chartCode === 95 // _ ) { result += buf[pos++]; } else { break; } } white(); return result; } export function parse( ctx: string, ): AstRoot[] { buf = ctx; pos = 0; const roots = [] as AstRoot[]; roots.push(parseRoot()); white(); while (buf[pos] === ";") { pos++; roots.push(parseRoot()); white(); } if (buf.length !== pos) { throw error(); } return roots; }