import { Environment, Pattern, NamedPattern, lookup } from "./meta"; import * as M from './meta'; import { Value, Float, Bytes, is, isPointer, Record, Dictionary, Set } from "@preserves/core"; import { refPosition } from "./reader"; export function validator(env: Environment, p: Pattern): (v: Value) => boolean { function walk(p: Pattern, v: Value, recordOkAsTuple = false): boolean { switch (p.label) { case M.$atom: switch (p[0]) { case M.$Boolean: return typeof v === 'boolean'; case M.$Float: return Float.isSingle(v); case M.$Double: return Float.isDouble(v); case M.$SignedInteger: return typeof v === 'number'; case M.$String: return typeof v === 'string'; case M.$ByteString: return Bytes.isBytes(v); case M.$Symbol: return typeof v === 'symbol'; } case M.$lit: return is(v, p[0]); case M.$ref: return lookup(refPosition(p), p, env, (p) => walk(p, v), (p) => walk(p, v), (_mod, _modPath, p) => walk(p!, v)); case M.$or: for (const pp of p[0]) { if (walk(pp, v)) return true; } return false; case M.$and: for (const pp of p[0]) { if (!walk(pp, v)) return false; } return true; case M.$pointer: return isPointer(v); case M.$rec: if (!Record.isRecord(v)) return false; if (!walk(p[0], v.label)) return false; return walk(p[1], v, true); case M.$tuple: if (!Array.isArray(v)) return false; if (!recordOkAsTuple && Record.isRecord(v)) return false; if (p[0].length !== v.length) return false; for (let i = 0; i < v.length; i++) { if (!walknamed(p[0][i], v[i])) return false; } return true; case M.$tuple_STAR_: if (!Array.isArray(v)) return false; if (!recordOkAsTuple && Record.isRecord(v)) return false; if (p[0].length > v.length) return false; for (let i = 0; i < p[0].length; i++) { if (!walknamed(p[0][i], v[i])) return false; } for (let i = p[0].length; i < v.length; i++) { if (!walknamed(p[1], v[i])) return false; } return true; case M.$setof: if (!Set.isSet(v)) return false; for (const vv of v) { if (!walk(p[0], vv)) return false; } return true; case M.$dictof: if (!Dictionary.isDictionary>(v)) return false; for (const e of v) { if (!walk(p[0], e[0])) return false; if (!walk(p[1], e[1])) return false; } return true; case M.$dict: if (!Dictionary.isDictionary>(v)) return false; for (const e of p[0]) { const vv = v.get(e[0]); if (vv === void 0) return false; if (!walk(e[1], vv)) return false; } return true; default: ((_p: never) => {})(p); return false; } } function walknamed(p: NamedPattern, v: Value): boolean { return (p.label === M.$named) ? walk(p[1], v) : walk(p, v); } return v => walk(p, v); }