preserves/implementations/javascript/packages/schema/src/interpreter.ts

90 lines
3.6 KiB
TypeScript

import { Environment, Pattern, NamedPattern, lookup } from "./meta";
import * as M from './meta';
import { Value, Float, Bytes, is, isPointer, Record, Dictionary, Set } from "@preserves/core";
export function validator(env: Environment, p: Pattern): (v: Value<any>) => boolean {
function walk(p: Pattern, v: Value<any>, 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 walk(lookup(env, p[0]), 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<Value<any>>(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<Value<any>>(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<any>): boolean {
return (p.label === M.___named) ? walk(p[1], v) : walk(p, v);
}
return v => walk(p, v);
}