export interface PResultOk { readonly ok: true, readonly value: T, readonly rest: string, } export interface PResultErr { readonly ok: false, readonly expected: Set, readonly rest: string, } export type PResult = PResultOk | PResultErr export abstract class P { abstract parse(rest: string): PResult map(f: (x: T) => U): P { let parser = this return new class Map extends P { parse(rest: string): PResult { let result = parser.parse(rest) if (!result.ok) { return result } return { ok: true, value: f(result.value), rest: result.rest } } }() } and(p2: P): P<[T, U]> { let p1 = this return new class And extends P<[T, U]> { parse(rest: string): PResult<[T, U]> { let result1 = p1.parse(rest) if (!result1.ok) { return result1 } let item1 = result1.value rest = result1.rest let result2 = p2.parse(rest) if (!result2.ok) { return result2 } let item2 = result2.value rest = result2.rest return { ok: true, value: [item1, item2], rest } } }() } andMap(f: (x1: T, x2: U) => V, p2: P): P { let p1 = this return new class AndMap extends P { parse(rest: string): PResult { return p1 .and(p2) .map((x: [T, U]) => f(x[0], x[1])) .parse(rest) } }() } or(...ps: P[]): P { let p0: P = this return new class Or extends P { parse(rest: string): PResult { let expected = new Set() for (let p of [p0].concat(ps)) { let result = p.parse(rest) if (result.ok) { return result } expected = new Set([...expected, ...result.expected]) } return { ok: false, expected, rest } } }() } many(): P { let p = this return new class Many extends P { parse(rest: string): PResult { let items = [] while (rest) { let parseResult = p.parse(rest) if (!parseResult.ok) { break } items.push(parseResult.value) rest = parseResult.rest } return { ok: true, value: items, rest } } }() } some(): P { let p = this return new class Some extends P { parse(rest: string): PResult { return p .and(p.many()) .map((r: [T, T[]]) => [r[0]].concat(r[1])) .parse(rest) } }() } space(): P { let p = this return new class Space extends P { parse(rest: string): PResult { let result = p.parse(rest) if (!result.ok) { return result } rest = result.rest let trim = 0 while (rest.charAt(trim) === " ") { trim++ } return Object.assign(result, { rest: rest.slice(trim) }) } }() } optional(defaultValue: U): P { let p = this return new class Optional extends P { parse(rest: string): PResult { let result = p.parse(rest) if (!result.ok) { return { ok: true, rest, value: defaultValue, } } return result } }() } eof(): P { let p = this return new class EOF extends P { parse(rest: string): PResult { let result = p.parse(rest) if (!result.ok) { return result } rest = result.rest if (rest) { return { ok: false, expected: new Set([""]), rest } } return result } }() } parens(left: P, right: P): P { let p = this return new class Parens extends P { parse(rest: string): PResult { return left .and(p) .and(right) .map((x: [[L, T], R]) => x[0][1]) .parse(rest) } }() } tag(tagNames: string[]): P { let p = this return new class Tag extends P { parse(rest: string): PResult { let result = p.parse(rest) if (!result.ok) { return { ok: false, expected: new Set(tagNames), rest: result.rest, } } return result } }() } } export class Symbol extends P { constructor(private literal: string) { super() } parse(rest: string): PResult { let peek = rest.slice(0, this.literal.length) if (this.literal === peek) { rest = rest.slice(this.literal.length) return { ok: true, value: peek, rest } } return { ok: false, expected: new Set([`'${this.literal}'`]), rest } } } export class Regex extends P { constructor(private pattern: RegExp) { super() } parse(rest: string): PResult { let m = rest.match(this.pattern) if (m == null) { return { ok: false, expected: new Set([`/${this.pattern.source}/`]), rest, } } let value = m[0] rest = rest.slice(m[0].length) return { ok: true, value, rest } } } export let int = new class Int extends P { parse(rest: string): PResult { let m = rest.match(/\d+/) if (m == null) { return { ok: false, expected: new Set(["integer"]), rest } } return { ok: true, value: parseInt(m[0]), rest: rest.slice(m[0].length) } } }() export let float = new class Float extends P { parse(rest: string): PResult { let m = rest.match(/[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)/) if (m == null) { return { ok: false, expected: new Set(["float"]), rest } } return { ok: true, value: parseFloat(m[0]), rest: rest.slice(m[0].length) } } }()