Bootstrap schema schema
This commit is contained in:
parent
7c587f03d3
commit
932818145b
|
@ -10,6 +10,7 @@
|
|||
"types": "lib/index.d.ts",
|
||||
"author": "Tony Garnock-Jones <tonyg@leastfixedpoint.com>",
|
||||
"scripts": {
|
||||
"regenerate": "./bin/preserves-schema-ts.js ../../../../schema/schema.txt > src/schemaschema.ts",
|
||||
"clean": "rm -rf lib dist",
|
||||
"prepare": "tsc && rollup -c",
|
||||
"rollupwatch": "rollup -c -w",
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
import { Record, KeyedDictionary } from '@preserves/core';
|
||||
import { AtomKind, Pattern, Schema } from './meta';
|
||||
import { Record, Dictionary, Value } from '@preserves/core';
|
||||
import { Pattern, Schema } from './meta';
|
||||
import * as M from './meta';
|
||||
|
||||
export const BASE: Schema = Record(M.___schema, [1, new KeyedDictionary<symbol, Pattern, never>([
|
||||
[Symbol.for('any'), Record(M.___and, [[]])],
|
||||
[Symbol.for('bool'), Record(M.___atom, [M.___Boolean as AtomKind])],
|
||||
[Symbol.for('float'), Record(M.___atom, [M.___Float as AtomKind])],
|
||||
[Symbol.for('double'), Record(M.___atom, [M.___Double as AtomKind])],
|
||||
[Symbol.for('int'), Record(M.___atom, [M.___SignedInteger as AtomKind])],
|
||||
[Symbol.for('string'), Record(M.___atom, [M.___String as AtomKind])],
|
||||
[Symbol.for('bytes'), Record(M.___atom, [M.___ByteString as AtomKind])],
|
||||
[Symbol.for('symbol'), Record(M.___atom, [M.___Symbol as AtomKind])],
|
||||
[Symbol.for('ref'), Record(M.___pointer, [])],
|
||||
export const BASE: Schema = Record(M.$schema, [new Dictionary<Value, never>([
|
||||
[M.$version, 1],
|
||||
[M.$definitions, new Dictionary<Pattern, never>([
|
||||
[Symbol.for('any'), Record(M.$and, [[]])],
|
||||
[Symbol.for('bool'), Record(M.$atom, [M.$Boolean])],
|
||||
[Symbol.for('float'), Record(M.$atom, [M.$Float])],
|
||||
[Symbol.for('double'), Record(M.$atom, [M.$Double])],
|
||||
[Symbol.for('int'), Record(M.$atom, [M.$SignedInteger])],
|
||||
[Symbol.for('string'), Record(M.$atom, [M.$String])],
|
||||
[Symbol.for('bytes'), Record(M.$atom, [M.$ByteString])],
|
||||
[Symbol.for('symbol'), Record(M.$atom, [M.$Symbol])],
|
||||
[Symbol.for('ref'), Record(M.$pointer, [])],
|
||||
])],
|
||||
])]);
|
||||
|
|
|
@ -2,8 +2,8 @@ import { BASE, compile, readSchema } from '../index';
|
|||
import fs from 'fs';
|
||||
|
||||
export function main(argv: Array<string>) {
|
||||
console.log('// ' + JSON.stringify(argv));
|
||||
const src = fs.readFileSync(__dirname + '/../../../../../../schema/schema.txt', 'utf-8');
|
||||
const [filename] = argv;
|
||||
const src = fs.readFileSync(filename, 'utf-8');
|
||||
const sch = readSchema(src);
|
||||
console.log(compile(
|
||||
[{ moduleName: 'BASE', modulePath: 'BASE', schema: BASE, inline: true }],
|
||||
|
|
|
@ -23,11 +23,11 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
function environmentWith<R>(name: symbol,
|
||||
kLocal: () => R,
|
||||
kOther: (e: CompileEnvEntry, p: Pattern) => R): R {
|
||||
if (Schema._.definitions(schema).has(name)) {
|
||||
if (Schema._.details(schema).get(M.$definitions).has(name)) {
|
||||
return kLocal();
|
||||
}
|
||||
for (const e of env) {
|
||||
const p = Schema._.definitions(e.schema).get(name);
|
||||
const p = Schema._.details(e.schema).get(M.$definitions).get(name);
|
||||
if (p !== void 0) {
|
||||
return kOther(e, p);
|
||||
}
|
||||
|
@ -56,12 +56,10 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
function literal(v: Input): Item {
|
||||
let varname = literals.get(v);
|
||||
if (varname === void 0) {
|
||||
const s = '___' + (
|
||||
v.asPreservesText()
|
||||
.replace('_', '__')
|
||||
.replace('*', '_STAR_')
|
||||
);
|
||||
varname = M.isValidToken(s, true) ? s : '__lit' + literals.size;
|
||||
const s = v.asPreservesText()
|
||||
.replace('_', '__')
|
||||
.replace('*', '_STAR_');
|
||||
varname = M.isValidToken('_' + s, true) ? '$' + s : '__lit' + literals.size;
|
||||
literals.set(v, varname);
|
||||
}
|
||||
return varname;
|
||||
|
@ -69,19 +67,19 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
|
||||
function typeFor(p: Pattern): Item {
|
||||
switch (p.label) {
|
||||
case M.___atom:
|
||||
case M.$atom:
|
||||
switch (p[0]) {
|
||||
case M.___Boolean: return `boolean`;
|
||||
case M.___Float: return `_.SingleFloat`;
|
||||
case M.___Double: return `_.DoubleFloat`;
|
||||
case M.___SignedInteger: return `number`;
|
||||
case M.___String: return `string`;
|
||||
case M.___ByteString: return `_.Bytes`;
|
||||
case M.___Symbol: return `symbol`;
|
||||
case M.$Boolean: return `boolean`;
|
||||
case M.$Float: return `_.SingleFloat`;
|
||||
case M.$Double: return `_.DoubleFloat`;
|
||||
case M.$SignedInteger: return `number`;
|
||||
case M.$String: return `string`;
|
||||
case M.$ByteString: return `_.Bytes`;
|
||||
case M.$Symbol: return `symbol`;
|
||||
}
|
||||
case M.___lit:
|
||||
case M.$lit:
|
||||
return `(typeof ${literal(p[0])})`;
|
||||
case M.___ref:
|
||||
case M.$ref:
|
||||
return environmentWith(
|
||||
p[0],
|
||||
() => p[0].description!,
|
||||
|
@ -92,28 +90,28 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
return `${e.moduleName}.${p[0].description!}`;
|
||||
}
|
||||
});
|
||||
case M.___or:
|
||||
case M.$or:
|
||||
return opseq('never', ' | ', ... p[0].map(pp => typeFor(pp)));
|
||||
case M.___and:
|
||||
case M.$and:
|
||||
return opseq('_.Value', ' & ', ... p[0].map(pp => typeFor(pp)));
|
||||
case M.___pointer:
|
||||
case M.$pointer:
|
||||
return `any`; // TODO: what to do here?
|
||||
case M.___rec:
|
||||
case M.$rec:
|
||||
return seq('_.Record', anglebrackets(typeFor(p[0]), typeFor(p[1])));
|
||||
case M.___tuple:
|
||||
case M.$tuple:
|
||||
return brackets(... p[0].map(pp => typeFor(unname(pp))));
|
||||
case M.___tuple_STAR_:
|
||||
case M.$tuple_STAR_:
|
||||
if (p[0].length === 0) {
|
||||
return seq('Array<', typeFor(unname(p[1])), '>');
|
||||
} else {
|
||||
return brackets(... p[0].map(pp => typeFor(unname(pp))),
|
||||
seq('... Array<', typeFor(unname(p[1])), '>'));
|
||||
}
|
||||
case M.___setof:
|
||||
case M.$setof:
|
||||
return seq('_.KeyedSet<', typeFor(p[0]), '>');
|
||||
case M.___dictof:
|
||||
case M.$dictof:
|
||||
return seq('_.KeyedDictionary', anglebrackets(typeFor(p[0]), typeFor(p[1])));
|
||||
case M.___dict:
|
||||
case M.$dict:
|
||||
return parens(seq(
|
||||
block(
|
||||
... Array.from(p[0]).map(([k, vp]) =>
|
||||
|
@ -129,17 +127,17 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
|
||||
function walk(v: string, p: Pattern, recordOkAsTuple = false): Item {
|
||||
switch (p.label) {
|
||||
case M.___atom:
|
||||
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.$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:
|
||||
case M.$lit:
|
||||
switch (typeof p[0]) {
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
|
@ -149,26 +147,26 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
default:
|
||||
return `_.is(${v}, ${literal(p[0])})`;
|
||||
}
|
||||
case M.___ref:
|
||||
case M.$ref:
|
||||
return applyPredicate(p[0], v);
|
||||
case M.___or:
|
||||
case M.$or:
|
||||
return opseq('false', ' || ', ... p[0].map(pp => walk(v, pp)));
|
||||
case M.___and:
|
||||
case M.$and:
|
||||
return opseq('true', ' && ', ... p[0].map(pp => walk(v, pp)));
|
||||
case M.___pointer:
|
||||
case M.$pointer:
|
||||
return `_.isPointer(${v})`;
|
||||
case M.___rec:
|
||||
case M.$rec:
|
||||
return opseq('true', ' && ',
|
||||
`_.Record.isRecord(${v})`,
|
||||
walk(`${v}.label`, p[0]),
|
||||
walk(v, p[1], true));
|
||||
case M.___tuple:
|
||||
case M.$tuple:
|
||||
return opseq('true', ' && ',
|
||||
... (recordOkAsTuple ? []
|
||||
: [`_.Array.isArray(${v})`, `!_.Record.isRecord(${v})`]),
|
||||
`(${v}.length === ${p[0].length})`,
|
||||
... p[0].map((pp, i) => walk(`${v}[${i}]`, unname(pp))));
|
||||
case M.___tuple_STAR_:
|
||||
case M.$tuple_STAR_:
|
||||
return opseq('true', ' && ',
|
||||
... (recordOkAsTuple ? []
|
||||
: [`_.Array.isArray(${v})`, `!_.Record.isRecord(${v})`]),
|
||||
|
@ -178,14 +176,14 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
parens(walk('v', unname(p[1]))),
|
||||
`)`),
|
||||
... p[0].map((pp, i) => walk(`${v}[${i}]`, unname(pp))));
|
||||
case M.___setof:
|
||||
case M.$setof:
|
||||
return opseq('true', ' && ',
|
||||
`_.Set.isSet(${v})`,
|
||||
fnblock(
|
||||
seq(`for (const vv of ${v}) `, block(
|
||||
seq('if (!(', walk('vv', p[0]), ')) return false'))),
|
||||
seq('return true')));
|
||||
case M.___dictof:
|
||||
case M.$dictof:
|
||||
return opseq('true', ' && ',
|
||||
`_.Dictionary.isDictionary(${v})`,
|
||||
fnblock(
|
||||
|
@ -193,7 +191,7 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
seq('if (!(', walk('e[0]', p[0]), ')) return false'),
|
||||
seq('if (!(', walk('e[1]', p[1]), ')) return false'))),
|
||||
seq('return true')));
|
||||
case M.___dict:
|
||||
case M.$dict:
|
||||
return opseq('true', ' && ',
|
||||
`_.Dictionary.isDictionary(${v})`,
|
||||
... Array.from(p[0]).map(([k, vp]) => {
|
||||
|
@ -209,24 +207,24 @@ export function compile(env: Array<CompileEnvEntry>, schema: Schema, preservesMo
|
|||
}
|
||||
|
||||
function unname(p: NamedPattern): Pattern {
|
||||
return (p.label === M.___named) ? p[1] : p;
|
||||
return (p.label === M.$named) ? p[1] : p;
|
||||
}
|
||||
|
||||
function fieldName(np: NamedPattern, index: number): string {
|
||||
return (np.label === M.___named) ? np[0].description! : `_field${index}`;
|
||||
return (np.label === M.$named) ? np[0].description! : `_field${index}`;
|
||||
}
|
||||
|
||||
function fieldEntry(np: NamedPattern, index: number): Item {
|
||||
return seq(JSON.stringify(fieldName(np, index)), ': ', typeFor(unname(np)));
|
||||
}
|
||||
|
||||
for (const [name0, pattern] of Schema._.definitions(schema)) {
|
||||
for (const [name0, pattern] of Schema._.details(schema).get(M.$definitions)) {
|
||||
const name = name0 as symbol;
|
||||
temps = [];
|
||||
const recognizer = walk('v', pattern);
|
||||
if (pattern.label === M.___rec &&
|
||||
pattern[0].label === M.___lit &&
|
||||
pattern[1].label === M.___tuple)
|
||||
if (pattern.label === M.$rec &&
|
||||
pattern[0].label === M.$lit &&
|
||||
pattern[1].label === M.$tuple)
|
||||
{
|
||||
types.push(
|
||||
seq(`export const ${name.description!} = _.Record.makeConstructor<`,
|
||||
|
|
|
@ -5,37 +5,37 @@ import { Value, Float, Bytes, is, isPointer, Record, Dictionary, Set } from "@pr
|
|||
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:
|
||||
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.$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:
|
||||
case M.$lit:
|
||||
return is(v, p[0]);
|
||||
case M.___ref:
|
||||
case M.$ref:
|
||||
return walk(lookup(env, p[0]), v);
|
||||
case M.___or:
|
||||
case M.$or:
|
||||
for (const pp of p[0]) {
|
||||
if (walk(pp, v)) return true;
|
||||
}
|
||||
return false;
|
||||
case M.___and:
|
||||
case M.$and:
|
||||
for (const pp of p[0]) {
|
||||
if (!walk(pp, v)) return false;
|
||||
}
|
||||
return true;
|
||||
case M.___pointer:
|
||||
case M.$pointer:
|
||||
return isPointer(v);
|
||||
case M.___rec:
|
||||
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:
|
||||
case M.$tuple:
|
||||
if (!Array.isArray(v)) return false;
|
||||
if (!recordOkAsTuple && Record.isRecord(v)) return false;
|
||||
if (p[0].length !== v.length) return false;
|
||||
|
@ -43,7 +43,7 @@ export function validator(env: Environment, p: Pattern): (v: Value<any>) => bool
|
|||
if (!walknamed(p[0][i], v[i])) return false;
|
||||
}
|
||||
return true;
|
||||
case M.___tuple_STAR_:
|
||||
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;
|
||||
|
@ -54,20 +54,20 @@ export function validator(env: Environment, p: Pattern): (v: Value<any>) => bool
|
|||
if (!walknamed(p[1], v[i])) return false;
|
||||
}
|
||||
return true;
|
||||
case M.___setof:
|
||||
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:
|
||||
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:
|
||||
case M.$dict:
|
||||
if (!Dictionary.isDictionary<Value<any>>(v)) return false;
|
||||
for (const e of p[0]) {
|
||||
const vv = v.get(e[0]);
|
||||
|
@ -82,7 +82,7 @@ export function validator(env: Environment, p: Pattern): (v: Value<any>) => bool
|
|||
}
|
||||
|
||||
function walknamed(p: NamedPattern, v: Value<any>): boolean {
|
||||
return (p.label === M.___named) ? walk(p[1], v) : walk(p, v);
|
||||
return (p.label === M.$named) ? walk(p[1], v) : walk(p, v);
|
||||
}
|
||||
|
||||
return v => walk(p, v);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Dictionary, KeyedDictionary, Record, Value, preserves } from '@preserves/core';
|
||||
import { Value, preserves } from '@preserves/core';
|
||||
|
||||
export type Input = Value<never>;
|
||||
|
||||
|
@ -15,65 +15,15 @@ export const DOT = Symbol.for('.');
|
|||
export const DOTDOTDOT = Symbol.for('...');
|
||||
export const EQUALS = Symbol.for('=');
|
||||
export const ORSYM = Symbol.for('/');
|
||||
export const ___Boolean = Symbol.for('Boolean');
|
||||
export const ___ByteString = Symbol.for('ByteString');
|
||||
export const ___Double = Symbol.for('Double');
|
||||
export const ___Float = Symbol.for('Float');
|
||||
export const ___SignedInteger = Symbol.for('SignedInteger');
|
||||
export const ___String = Symbol.for('String');
|
||||
export const ___Symbol = Symbol.for('Symbol');
|
||||
export const ___and = Symbol.for('and');
|
||||
export const ___atom = Symbol.for('atom');
|
||||
export const ___dict = Symbol.for('dict');
|
||||
export const ___dictof = Symbol.for('dictof');
|
||||
export const ___lit = Symbol.for('lit');
|
||||
export const ___named = Symbol.for('named');
|
||||
export const ___or = Symbol.for('or');
|
||||
export const ___pointer = Symbol.for('pointer');
|
||||
export const ___rec = Symbol.for('rec');
|
||||
export const ___ref = Symbol.for('ref');
|
||||
export const ___schema = Symbol.for('schema');
|
||||
export const ___setof = Symbol.for('setof');
|
||||
export const ___tuple = Symbol.for('tuple');
|
||||
export const ___tuple_STAR_ = Symbol.for('tuple*');
|
||||
export const ___version = Symbol.for('version');
|
||||
|
||||
export * from './schemaschema';
|
||||
import { Schema, Pattern, $definitions } from './schemaschema';
|
||||
|
||||
export type Environment = Array<Schema>;
|
||||
|
||||
export const Schema = Record.makeConstructor<{
|
||||
version: number,
|
||||
definitions: KeyedDictionary<symbol, Pattern, never>,
|
||||
}>()(Symbol.for('schema'), ['version', 'definitions']);
|
||||
export type Schema = ReturnType<typeof Schema>;
|
||||
|
||||
export type AtomKind =
|
||||
| typeof ___Boolean
|
||||
| typeof ___Float
|
||||
| typeof ___Double
|
||||
| typeof ___SignedInteger
|
||||
| typeof ___String
|
||||
| typeof ___ByteString
|
||||
| typeof ___Symbol;
|
||||
|
||||
export type Pattern =
|
||||
| Record<typeof ___atom, [AtomKind], never>
|
||||
| Record<typeof ___lit, [Input], never>
|
||||
| Record<typeof ___ref, [symbol], never>
|
||||
| Record<typeof ___or, [Array<Pattern>], never>
|
||||
| Record<typeof ___and, [Array<Pattern>], never>
|
||||
| Record<typeof ___pointer, [], never>
|
||||
| Record<typeof ___rec, [Pattern, Pattern], never>
|
||||
| Record<typeof ___tuple, [Array<NamedPattern>], never>
|
||||
| Record<typeof ___tuple_STAR_, [Array<NamedPattern>, NamedPattern], never>
|
||||
| Record<typeof ___setof, [Pattern], never>
|
||||
| Record<typeof ___dictof, [Pattern, Pattern], never>
|
||||
| Record<typeof ___dict, [Dictionary<Pattern, never>], never>;
|
||||
|
||||
export type NamedPattern = Record<typeof ___named, [symbol, Pattern], never> | Pattern;
|
||||
|
||||
export function lookup(env: Environment, name: symbol): Pattern {
|
||||
for (const s of env) {
|
||||
const p = Schema._.definitions(s).get(name);
|
||||
const p = Schema._.details(s).get($definitions).get(name);
|
||||
if (p !== void 0) return p;
|
||||
}
|
||||
throw new Error(preserves`Schema: unbound name ${name}`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Reader, Annotated, Dictionary, is, KeyedDictionary, peel, preserves, Record, strip, Tuple } from '@preserves/core';
|
||||
import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Value } from '@preserves/core';
|
||||
import { Input, NamedPattern, Pattern, Schema } from './meta';
|
||||
import * as M from './meta';
|
||||
|
||||
|
@ -37,8 +37,8 @@ export function readSchema(source: string): Schema {
|
|||
|
||||
export function parseSchema(toplevelTokens: Array<Input>): Schema {
|
||||
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT);
|
||||
let version: number | undefined = void 0;
|
||||
let definitions = new KeyedDictionary<symbol, Pattern, never>();
|
||||
let version: M.Version | undefined = void 0;
|
||||
let definitions = new Dictionary<Pattern, never>();
|
||||
for (const clause0 of toplevelClauses) {
|
||||
const clause = clause0.map(peel);
|
||||
if (!Array.isArray(clause)) {
|
||||
|
@ -53,8 +53,9 @@ export function parseSchema(toplevelTokens: Array<Input>): Schema {
|
|||
throw new Error(preserves`Duplicate definition: ${clause}`);
|
||||
}
|
||||
definitions.set(name, parseDefinition(name, clause.slice(2).map(peel)));
|
||||
} else if (clause.length === 2 && is(clause[0], M.___version)) {
|
||||
if (typeof clause[1] !== 'number') invalidClause(clause);
|
||||
} else if (clause.length === 2 && is(clause[0], M.$version)) {
|
||||
// TODO: use asVersion
|
||||
if (!M.isVersion(clause[1])) invalidClause(clause);
|
||||
version = clause[1];
|
||||
} else {
|
||||
invalidClause(clause);
|
||||
|
@ -63,10 +64,10 @@ export function parseSchema(toplevelTokens: Array<Input>): Schema {
|
|||
if (version === void 0) {
|
||||
throw new Error("Schema: missing version declaration.");
|
||||
}
|
||||
if (version !== 1) {
|
||||
throw new Error("Schema: unsupported version " + version);
|
||||
}
|
||||
return Record(M.___schema, [version, definitions]);
|
||||
return Record(M.$schema, [new Dictionary<Value>([
|
||||
[M.$version, version],
|
||||
[M.$definitions, definitions],
|
||||
])]);
|
||||
}
|
||||
|
||||
function parseDefinition(name: symbol, body: Array<Input>): Pattern {
|
||||
|
@ -77,8 +78,8 @@ function parseOp(body: Array<Input>, op: Input, k: (p: Array<Input>) => Pattern)
|
|||
const pieces = splitBy(body, op);
|
||||
if (pieces.length === 1) return k(pieces[0]);
|
||||
switch (op) {
|
||||
case M.ORSYM: return Record(M.___or, [pieces.map(k)]);
|
||||
case M.ANDSYM: return Record(M.___and, [pieces.map(k)]);
|
||||
case M.ORSYM: return Record(M.$or, [pieces.map(k)]);
|
||||
case M.ANDSYM: return Record(M.$and, [pieces.map(k)]);
|
||||
default: throw new Error("Internal error: unexpected operator");
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +101,7 @@ function parseBase(name: symbol, body: Array<Input>): Pattern {
|
|||
const namedwalk = (b: Input): NamedPattern => {
|
||||
const name = findName(b);
|
||||
if (name === false) return walk(b);
|
||||
return Record(M.___named, [name, walk(b)]);
|
||||
return Record(M.$named, [name, walk(b)]);
|
||||
};
|
||||
const walkitems = (b: Input): Pattern[] => {
|
||||
b = peel(b);
|
||||
|
@ -115,56 +116,56 @@ function parseBase(name: symbol, body: Array<Input>): Pattern {
|
|||
if (typeof item === 'symbol') {
|
||||
const s = item.description;
|
||||
if (s === void 0) complain();
|
||||
if (s[0] === '=') return Record(M.___lit, [Symbol.for(s.slice(1))]);
|
||||
return Record(M.___ref, [item]);
|
||||
if (s[0] === '=') return Record(M.$lit, [Symbol.for(s.slice(1))]);
|
||||
return Record(M.$ref, [item]);
|
||||
} else if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
|
||||
const label = item.label;
|
||||
if (Record.isRecord<Input, [], never>(label)) {
|
||||
if (label.length !== 0) complain();
|
||||
switch (label.label) {
|
||||
case M.___lit:
|
||||
case M.$lit:
|
||||
if (item.length !== 1) complain();
|
||||
return Record(M.___lit, [item[0]]);
|
||||
case M.___or:
|
||||
return Record(M.$lit, [item[0]]);
|
||||
case M.$or:
|
||||
if (item.length !== 1) complain();
|
||||
return Record(M.___or, [walkitems(item[0])]);
|
||||
case M.___and:
|
||||
return Record(M.$or, [walkitems(item[0])]);
|
||||
case M.$and:
|
||||
if (item.length !== 1) complain();
|
||||
return Record(M.___and, [walkitems(item[0])]);
|
||||
case M.___rec:
|
||||
return Record(M.$and, [walkitems(item[0])]);
|
||||
case M.$rec:
|
||||
if (item.length !== 2) complain();
|
||||
return Record(M.___rec, [walk(item[0]), walk(item[1])]);
|
||||
return Record(M.$rec, [walk(item[0]), walk(item[1])]);
|
||||
default:
|
||||
complain();
|
||||
}
|
||||
} else {
|
||||
return Record(M.___rec, [Record(M.___lit, [label]), Record(M.___tuple, [item.map(namedwalk)])]);
|
||||
return Record(M.$rec, [Record(M.$lit, [label]), Record(M.$tuple, [item.map(namedwalk)])]);
|
||||
}
|
||||
} else if (Array.isArray(item)) {
|
||||
if (is(item[item.length - 1], M.DOTDOTDOT)) {
|
||||
if (item.length < 2) complain();
|
||||
return Record(M.___tuple_STAR_, [
|
||||
return Record(M.$tuple_STAR_, [
|
||||
item.slice(0, item.length - 2).map(namedwalk),
|
||||
namedwalk(item[item.length - 2]),
|
||||
]);
|
||||
} else {
|
||||
return Record(M.___tuple, [item.map(namedwalk)]);
|
||||
return Record(M.$tuple, [item.map(namedwalk)]);
|
||||
}
|
||||
} else if (Dictionary.isDictionary<Input, never>(item)) {
|
||||
if (item.size === 2 && item.has(M.DOTDOTDOT)) {
|
||||
const v = item.clone();
|
||||
v.delete(M.DOTDOTDOT);
|
||||
const [[kp, vp]] = v.entries();
|
||||
return Record(M.___dictof, [walk(kp), walk(vp)]);
|
||||
return Record(M.$dictof, [walk(kp), walk(vp)]);
|
||||
} else {
|
||||
return Record(M.___dict, [item.mapEntries<Pattern, Input, never>(([k, vp]) =>
|
||||
return Record(M.$dict, [item.mapEntries<Pattern, Input, never>(([k, vp]) =>
|
||||
[strip(k), walk(vp)])]);
|
||||
}
|
||||
} else if (Set.isSet<never>(item)) {
|
||||
if (item.size !== 1) complain();
|
||||
const [vp] = item.entries();
|
||||
return Record(M.___setof, [walk(vp)]);
|
||||
return Record(M.$setof, [walk(vp)]);
|
||||
} else {
|
||||
return Record(M.___lit, [strip(item)]);
|
||||
return Record(M.$lit, [strip(item)]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
import * as _ from "@preserves/core";
|
||||
|
||||
export const $1 = 1;
|
||||
export const $Boolean = Symbol.for("Boolean");
|
||||
export const $ByteString = Symbol.for("ByteString");
|
||||
export const $Double = Symbol.for("Double");
|
||||
export const $Float = Symbol.for("Float");
|
||||
export const $SignedInteger = Symbol.for("SignedInteger");
|
||||
export const $String = Symbol.for("String");
|
||||
export const $Symbol = Symbol.for("Symbol");
|
||||
export const $and = Symbol.for("and");
|
||||
export const $atom = Symbol.for("atom");
|
||||
export const $definitions = Symbol.for("definitions");
|
||||
export const $dict = Symbol.for("dict");
|
||||
export const $dictof = Symbol.for("dictof");
|
||||
export const $lit = Symbol.for("lit");
|
||||
export const $named = Symbol.for("named");
|
||||
export const $or = Symbol.for("or");
|
||||
export const $pointer = Symbol.for("pointer");
|
||||
export const $rec = Symbol.for("rec");
|
||||
export const $ref = Symbol.for("ref");
|
||||
export const $schema = Symbol.for("schema");
|
||||
export const $setof = Symbol.for("setof");
|
||||
export const $tuple = Symbol.for("tuple");
|
||||
export const $tuple_STAR_ = Symbol.for("tuple*");
|
||||
export const $version = Symbol.for("version");
|
||||
|
||||
export const Schema = _.Record.makeConstructor<{
|
||||
"details": (
|
||||
{
|
||||
get(k: typeof $version): Version;
|
||||
get(k: typeof $definitions): _.KeyedDictionary<symbol, Pattern>;
|
||||
has(k: typeof $version): true;
|
||||
has(k: typeof $definitions): true;
|
||||
} & _.Dictionary<_.Value>
|
||||
)
|
||||
}>()($schema, ["details"]);
|
||||
|
||||
export type Schema = _.Record<
|
||||
(typeof $schema),
|
||||
[
|
||||
(
|
||||
{
|
||||
get(k: typeof $version): Version;
|
||||
get(k: typeof $definitions): _.KeyedDictionary<symbol, Pattern>;
|
||||
has(k: typeof $version): true;
|
||||
has(k: typeof $definitions): true;
|
||||
} & _.Dictionary<_.Value>
|
||||
)
|
||||
]
|
||||
>;
|
||||
|
||||
export type Version = (typeof $1);
|
||||
|
||||
export type Pattern = (
|
||||
_.Record<
|
||||
(typeof $atom),
|
||||
[
|
||||
(
|
||||
(typeof $Boolean) |
|
||||
(typeof $Float) |
|
||||
(typeof $Double) |
|
||||
(typeof $SignedInteger) |
|
||||
(typeof $String) |
|
||||
(typeof $ByteString) |
|
||||
(typeof $Symbol)
|
||||
)
|
||||
]
|
||||
> |
|
||||
_.Record<(typeof $pointer), []> |
|
||||
_.Record<(typeof $lit), [_.Value]> |
|
||||
_.Record<(typeof $ref), [symbol]> |
|
||||
_.Record<(typeof $or), [Array<Pattern>]> |
|
||||
_.Record<(typeof $and), [Array<Pattern>]> |
|
||||
_.Record<(typeof $rec), [Pattern, Pattern]> |
|
||||
_.Record<(typeof $tuple), [Array<NamedPattern>]> |
|
||||
_.Record<(typeof $tuple_STAR_), [Array<NamedPattern>, NamedPattern]> |
|
||||
_.Record<(typeof $setof), [Pattern]> |
|
||||
_.Record<(typeof $dictof), [Pattern, Pattern]> |
|
||||
_.Record<(typeof $dict), [_.KeyedDictionary<_.Value, Pattern>]>
|
||||
);
|
||||
|
||||
export type NamedPattern = (_.Record<(typeof $named), [symbol, Pattern]> | Pattern);
|
||||
|
||||
|
||||
export function isSchema(v: any): v is Schema {
|
||||
let _tmp0, _tmp1: any;
|
||||
return (
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $schema &&
|
||||
(
|
||||
(v.length === 1) &&
|
||||
(
|
||||
_.Dictionary.isDictionary(v[0]) &&
|
||||
((_tmp0 = v[0].get($version)) !== void 0 && isVersion(_tmp0)) &&
|
||||
(
|
||||
(_tmp1 = v[0].get($definitions)) !== void 0 && (
|
||||
_.Dictionary.isDictionary(_tmp1) &&
|
||||
((() => {
|
||||
for (const e of _tmp1) {
|
||||
if (!(typeof e[0] === 'symbol')) return false;
|
||||
if (!(isPattern(e[1]))) return false;
|
||||
};
|
||||
return true;
|
||||
})())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function isVersion(v: any): v is Version {return v === $1;}
|
||||
|
||||
export function isPattern(v: any): v is Pattern {
|
||||
return (
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $atom &&
|
||||
(
|
||||
(v.length === 1) &&
|
||||
(
|
||||
v[0] === $Boolean ||
|
||||
v[0] === $Float ||
|
||||
v[0] === $Double ||
|
||||
v[0] === $SignedInteger ||
|
||||
v[0] === $String ||
|
||||
v[0] === $ByteString ||
|
||||
v[0] === $Symbol
|
||||
)
|
||||
)
|
||||
) ||
|
||||
(_.Record.isRecord(v) && v.label === $pointer && ((v.length === 0))) ||
|
||||
(_.Record.isRecord(v) && v.label === $lit && ((v.length === 1) && true)) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $ref &&
|
||||
((v.length === 1) && typeof v[0] === 'symbol')
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $or &&
|
||||
(
|
||||
(v.length === 1) &&
|
||||
(
|
||||
_.Array.isArray(v[0]) &&
|
||||
!_.Record.isRecord(v[0]) &&
|
||||
(v[0].length >= 0) &&
|
||||
v[0].slice(0).every(v => (isPattern(v)))
|
||||
)
|
||||
)
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $and &&
|
||||
(
|
||||
(v.length === 1) &&
|
||||
(
|
||||
_.Array.isArray(v[0]) &&
|
||||
!_.Record.isRecord(v[0]) &&
|
||||
(v[0].length >= 0) &&
|
||||
v[0].slice(0).every(v => (isPattern(v)))
|
||||
)
|
||||
)
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $rec &&
|
||||
((v.length === 2) && isPattern(v[0]) && isPattern(v[1]))
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $tuple &&
|
||||
(
|
||||
(v.length === 1) &&
|
||||
(
|
||||
_.Array.isArray(v[0]) &&
|
||||
!_.Record.isRecord(v[0]) &&
|
||||
(v[0].length >= 0) &&
|
||||
v[0].slice(0).every(v => (isNamedPattern(v)))
|
||||
)
|
||||
)
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $tuple_STAR_ &&
|
||||
(
|
||||
(v.length === 2) &&
|
||||
(
|
||||
_.Array.isArray(v[0]) &&
|
||||
!_.Record.isRecord(v[0]) &&
|
||||
(v[0].length >= 0) &&
|
||||
v[0].slice(0).every(v => (isNamedPattern(v)))
|
||||
) &&
|
||||
isNamedPattern(v[1])
|
||||
)
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $setof &&
|
||||
((v.length === 1) && isPattern(v[0]))
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $dictof &&
|
||||
((v.length === 2) && isPattern(v[0]) && isPattern(v[1]))
|
||||
) ||
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $dict &&
|
||||
(
|
||||
(v.length === 1) &&
|
||||
(
|
||||
_.Dictionary.isDictionary(v[0]) &&
|
||||
((() => {
|
||||
for (const e of v[0]) {if (!(true)) return false; if (!(isPattern(e[1]))) return false;};
|
||||
return true;
|
||||
})())
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function isNamedPattern(v: any): v is NamedPattern {
|
||||
return (
|
||||
(
|
||||
_.Record.isRecord(v) &&
|
||||
v.label === $named &&
|
||||
((v.length === 2) && typeof v[0] === 'symbol' && isPattern(v[1]))
|
||||
) ||
|
||||
isPattern(v)
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue