crazy-parser
Version:
A light-weight parser combinator
108 lines (90 loc) • 2.39 kB
text/typescript
/*
A naïve implementation of JSON parser in pure JavaScript.
It's about a thousand times slower than the native one, so don't use it.
This is to prove the correctness of this library.
*/
import {
Parser,
asum,
char,
cr,
digit,
eof,
hex,
lazy,
lf,
Nothing,
one,
sequence,
space,
tab
} from "./index"
import {many, some} from "./prefix"
import * as V from "./void"
type MyJSON
= boolean
| number
| null
| string
| Array<MyJSON>
| { [key: string]: MyJSON }
const pWhitespace = many(asum(space, lf, cr, tab))
const pBoolean =
V.str("true").cmap(true).or(V.str("false").cmap(false))
const pNull =
V.str("null").cmap(null)
const pNumber =
sequence(
char("-").orPure(""),
some(digit).where(digits => digits[0] != "0" || (digits[0] == "0" && digits.length == 1)),
V.char(".").right(some(digit)).optional(),
sequence(
char("e").or(char("E")),
char("+").or(char("-")).orPure(""),
some(digit)).optional(),
).map(args =>
{
const [a0, a1_, a2_, a3_] = args
const a1 = a1_.join("")
const a2 = a2_ == Nothing ? "" : `.${a2_.join("")}`
if (a3_ == Nothing)
return parseFloat(`${a0}${a1}${a2}`)
const [a30, a31, a32] = a3_
return parseFloat(`${a0}${a1}${a2}${a30}${a31}${a32}`)
})
const pString =
V.char("\"").$_(many(
V.char("\\").right(asum(
V.char("\"").cmap("\""),
V.char("\\").cmap("\\"),
V.char("/").cmap("/"),
V.char("b").cmap("\b"),
V.char("f").cmap("\f"),
V.char("n").cmap("\n"),
V.char("r").cmap("\r"),
V.char("t").cmap("\t"),
V.char("u").right(hex.x(4)).map(ds => String.fromCodePoint(Number(`0x${ds.join("")}`))),
)).or(one.where(c => c != "\""))
))._$(V.char("\"")).map(cs => cs.join(""))
const pValue =
pWhitespace.$_(asum<MyJSON>(
pString,
pNumber,
pBoolean,
pNull,
lazy(() => pObject),
lazy(() => pArray),
))._$(pWhitespace)
const pArray: Parser<Array<MyJSON>> =
V.char("[").$_(
pValue.and(many(V.char(",").$_(pValue))).map(a => [a[0], ...a[1]])
.orPure([]) // Note: The whitespace should've been consumed
)._$(V.char("]"))
const pObjectEntry =
pWhitespace.$_(pString.and(pWhitespace.$_(V.char(":")).$_(pValue)))
const pObject: Parser<{ [key: string]: MyJSON }> =
V.char("{").$_(
pObjectEntry.and(many(V.char(",").$_(pObjectEntry))).map(a => Object.fromEntries([a[0], ...a[1]]))
.orPure({})
)._$(V.char("}"))
export const json: Parser<any> = pValue._$(eof)