// tracing: off import type { Schema, SchemaAny } from "./_schema.js" import * as S from "./_schema.js" import { hasContinuation, SchemaContinuationSymbol } from "./_schema.js" import type * as T from "./These.js" import * as Th from "./These.js" export interface ParserEnv { cache?: { getOrSet: (i: I, parser: Parser) => T.These getOrSetParser: (parser: Parser) => (i: I) => Th.These getOrSetParsers: < Parsers extends Record> >( parsers: Parsers ) => { [k in keyof Parsers]: (u: unknown) => Th.These } } lax?: boolean } /** * @tsplus type ets/Schema/Parser */ export type Parser = { (u: I, env?: ParserEnv): T.These } export const interpreters: (( schema: SchemaAny ) => Opt<() => Parser>)[] = [ Opt.partial( miss => (schema: S.SchemaAny): (() => Parser) => { if (schema instanceof S.SchemaNamed) { return () => { const self = parserFor(schema.self) return (u, env) => Th.mapError_(self(u, env), e => S.namedE(schema.name, e)) } } if (schema instanceof S.SchemaMapParserError) { return () => { const self = parserFor(schema.self) return (u, env) => Th.mapError_(self(u, env), schema.mapError) } } if (schema instanceof S.SchemaIdentity) { return () => u => Th.succeed(u) } if (schema instanceof S.SchemaPipe) { return () => { const self = parserFor(schema.self) const that = parserFor(schema.that) return (u, env) => Th.chain_( pipe( self(u, env), Th.mapError(e => S.compositionE(Chunk(S.prevE(e)))) ), (a, w) => pipe( that(a, env), Th.foldM( a => (w._tag === "Some" ? Th.warn(a, w.value) : Th.succeed(a)), (a, e) => w._tag === "Some" ? Th.warn( a, S.compositionE(w.value.errors.append(S.nextE(e))) ) : Th.warn(a, e), e => w._tag === "None" ? Th.fail(S.compositionE(Chunk(S.nextE(e)))) : Th.fail(S.compositionE(w.value.errors.append(S.nextE(e)))) ) ) ) } } if (schema instanceof S.SchemaParser) { return () => schema.parser } if (schema instanceof S.SchemaRefinement) { return () => { const self = parserFor(schema.self) return (u, env) => env?.lax ? // refinements can really pile up self(u, env) : Th.chain_( pipe( self(u, env), Th.mapError(e => S.compositionE(Chunk(S.prevE(e)))) ), ( a, w ): Th.These< S.CompositionE< S.PrevE | S.NextE> >, unknown > => schema.refinement(a) ? w._tag === "None" ? Th.succeed(a) : Th.warn(a, w.value) : Th.fail( S.compositionE( w._tag === "None" ? Chunk(S.nextE(S.refinementE(schema.error(a)))) : w.value.errors.append( S.nextE(S.refinementE(schema.error(a))) ) ) ) ) } } return miss() } ) ] const cache = new WeakMap() function parserFor( schema: Schema ): Parser { if (cache.has(schema)) { return cache.get(schema) } if (schema instanceof S.SchemaLazy) { let x: Parser const parser: Parser = (__, env) => { if (!x) { x = parserFor(schema.self()) } return x(__, env) } cache.set(schema, parser) return parser as Parser } for (const interpreter of interpreters) { const _ = interpreter(schema) if (_._tag === "Some") { let x: Parser const parser: Parser = (__, env) => { if (!x) { x = _.value() } return x(__, env) } return parser as Parser } } if (hasContinuation(schema)) { let x: Parser const parser: Parser = (__, env) => { if (!x) { x = parserFor(schema[SchemaContinuationSymbol]) } return x(__, env) } cache.set(schema, parser) return parser as Parser } throw new Error(`Missing parser integration for: ${schema.constructor}`) } export { parserFor as for }