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

94 lines
3.8 KiB
TypeScript
Raw Normal View History

2021-03-09 14:59:40 +00:00
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";
2021-03-09 14:59:40 +00:00
export function validator(env: Environment, p: Pattern): (v: Value<any>) => boolean {
function walk(p: Pattern, v: Value<any>, recordOkAsTuple = false): boolean {
switch (p.label) {
2021-03-10 22:15:53 +00:00
case M.$atom:
2021-03-09 14:59:40 +00:00
switch (p[0]) {
2021-03-10 22:15:53 +00:00
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';
2021-03-09 14:59:40 +00:00
}
2021-03-10 22:15:53 +00:00
case M.$lit:
2021-03-09 14:59:40 +00:00
return is(v, p[0]);
2021-03-10 22:15:53 +00:00
case M.$ref:
return lookup(refPosition(p), p, env,
2021-03-11 09:56:49 +00:00
(p) => walk(p, v),
(p) => walk(p, v),
2021-03-11 16:59:40 +00:00
(_mod, _modPath, p) => walk(p!, v));
2021-03-10 22:15:53 +00:00
case M.$or:
2021-03-09 14:59:40 +00:00
for (const pp of p[0]) {
if (walk(pp, v)) return true;
}
return false;
2021-03-10 22:15:53 +00:00
case M.$and:
2021-03-09 14:59:40 +00:00
for (const pp of p[0]) {
if (!walk(pp, v)) return false;
}
return true;
2021-03-10 22:15:53 +00:00
case M.$pointer:
2021-03-09 14:59:40 +00:00
return isPointer(v);
2021-03-10 22:15:53 +00:00
case M.$rec:
2021-03-09 14:59:40 +00:00
if (!Record.isRecord(v)) return false;
if (!walk(p[0], v.label)) return false;
return walk(p[1], v, true);
2021-03-10 22:15:53 +00:00
case M.$tuple:
2021-03-09 14:59:40 +00:00
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;
2021-03-10 22:15:53 +00:00
case M.$tuple_STAR_:
2021-03-09 14:59:40 +00:00
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;
2021-03-10 22:15:53 +00:00
case M.$setof:
2021-03-09 14:59:40 +00:00
if (!Set.isSet(v)) return false;
for (const vv of v) {
if (!walk(p[0], vv)) return false;
}
return true;
2021-03-10 22:15:53 +00:00
case M.$dictof:
2021-03-09 14:59:40 +00:00
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;
2021-03-10 22:15:53 +00:00
case M.$dict:
2021-03-09 14:59:40 +00:00
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 {
2021-03-10 22:15:53 +00:00
return (p.label === M.$named) ? walk(p[1], v) : walk(p, v);
2021-03-09 14:59:40 +00:00
}
return v => walk(p, v);
}