Cut over to new representation

This commit is contained in:
Tony Garnock-Jones 2021-03-22 12:13:34 +01:00
parent 0304c2631b
commit 4814790d8e
7 changed files with 859 additions and 882 deletions

View File

@ -11,17 +11,17 @@ import { genConstructor } from "./compiler/genctor";
export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
const mod = new ModuleContext(env, schema, options);
const pointerName = M.Schema._._field0(schema).get(M.$pointer);
const pointerName = schema.pointer;
mod.defineType(seq(`export type _ptr = `,
renderType(pointerName === false
renderType(pointerName._variant === 'false'
? EMPTY_TYPE
: typeForDefinition(mod, pointerName)),
: typeForDefinition(mod, M.Definition.Alternative(M.Alternative.Pattern(M.Pattern.SimplePattern(M.SimplePattern.Ref(pointerName.value)))))),
`;`));
mod.defineType(`export type _val = _.Value<_ptr>;`);
mod.defineFunction(ctx =>
seq(`export const _decodePtr = `,
(pointerName === false
(pointerName._variant === 'false'
? '() => { throw new _.DecodeError("Pointers forbidden"); }'
: seq(`(d: _.TypedDecoder<_ptr>) => `, ctx.block(() => [
seq(`let result`),
@ -30,7 +30,7 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
seq(`return result`)]))),
`;`));
for (const [name, def] of M.Schema._._field0(schema).get(M.$definitions)) {
for (const [name, def] of schema.definitions) {
const t = typeForDefinition(mod, def);
const nameStr = stringify(name);
@ -48,7 +48,7 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
}
}
for (const [name0, def] of M.Schema._._field0(schema).get(M.$definitions)) {
for (const [name0, def] of schema.definitions) {
const name = name0 as symbol;
mod.defineFunction(ctx =>

View File

@ -41,12 +41,16 @@ export class ModuleContext {
}
derefPattern([_name, p]: [string, M.Alternative]): M.Definition {
if (p.label === M.$ref) {
return M.lookup(refPosition(p), p, this.env,
if (p._variant === 'Pattern' &&
p.value._variant === 'SimplePattern' &&
p.value.value._variant === 'Ref')
{
return M.lookup(refPosition(p.value.value.value), p.value.value.value, this.env,
(p) => p,
(_modId, _modPath, pp) => pp ?? p);
(_modId, _modPath, pp) => pp ?? M.Definition.Alternative(
M.Alternative.Pattern(p.value)));
} else {
return p;
return M.Definition.Alternative(p);
}
}

View File

@ -11,11 +11,11 @@ export function converterForDefinition(
src: string,
dest: string): Item[]
{
if (p.label === M.$or) {
const alts = p[0];
if (p._variant === 'or') {
const alts = p.patterns;
function loop(i: number): Item[] {
ctx.variantName = alts[i][0];
return [... converterForAlternative(ctx, alts[i][1], src, dest),
ctx.variantName = alts[i].variantLabel;
return [... converterForAlternative(ctx, alts[i].alternative, src, dest),
... ((i < alts.length - 1)
? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))]
: [])];
@ -23,7 +23,7 @@ export function converterForDefinition(
return alts.length === 0 ? [] : loop(0);
} else {
ctx.variantName = void 0;
return converterForAlternative(ctx, p, src, dest);
return converterForAlternative(ctx, p.value, src, dest);
}
}
@ -33,8 +33,8 @@ function converterForAlternative(
src: string,
dest: string): Item[]
{
if (p.label === M.$and) {
const alts = p[0];
if (p._variant === 'and') {
const alts = p.patterns;
function loop(i: number): Item[] {
return (i < alts.length)
? converterFor(ctx, alts[i], src, () => loop(i + 1))
@ -42,11 +42,11 @@ function converterForAlternative(
}
return alts.length === 0 ? [seq(`${dest} = ${src}`)] : loop(0);
} else {
return converterFor(ctx, p, src, simpleValue => {
return converterFor(ctx, M.NamedPattern.anonymous(p.value), src, simpleValue => {
if (simpleValue === void 0) {
return [ctx.buildCapturedCompound(dest)];
} else if (ctx.variantName !== void 0) {
if (typeFor(ctx.mod, p).kind === 'unit') {
if (typeFor(ctx.mod, p.value).kind === 'unit') {
return [ctx.buildCapturedCompound(dest)];
} else {
return [ctx.withCapture('value',
@ -97,12 +97,13 @@ function converterForArray(ctx: FunctionContext,
k: (dest: string) => Item[]): Item
{
const postCheck = () => {
const r = ctx.gentemp(Type.array(simpleType(ctx.mod, M.unname(arrayType))));
const r = ctx.gentemp(Type.array(simpleType(ctx.mod, M.unnameSimplePattern(arrayType))));
const v = ctx.gentempname();
return [
seq(`${r} = []`),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, arrayType, v, vv => [`${r}.push(${vv})`, `continue`]),
... converterFor(ctx, M.promoteNamedSimplePattern(arrayType), v, vv =>
[`${r}.push(${vv})`, `continue`]),
seq(`${r} = void 0`),
seq(`break`)])),
ctx.convertCapture(M.nameFor(arrayType), r, k)];
@ -119,32 +120,32 @@ function converterFor(
ks: (dest: string | undefined) => Item[],
recordFields = false): Item[]
{
let p = M.unname(np);
let p = M.unnamePattern(np);
let maybeName = M.nameFor(np);
if (M.isSimplePattern(p)) {
const dest = ctx.gentemp(simpleType(ctx.mod, p));
return [... converterForSimple(ctx, p, src, dest),
if (p._variant === 'SimplePattern') {
const dest = ctx.gentemp(simpleType(ctx.mod, p.value));
return [... converterForSimple(ctx, p.value, src, dest),
ctx.convertCapture(maybeName, dest, ks)];
} else {
switch (p.label) {
case M.$setof: {
const setPattern = p[0];
switch (p.value._variant) {
case 'setof': {
const setPattern = p.value.pattern;
const r = ctx.gentemp(setType(ctx.mod, setPattern));
const v = ctx.gentempname();
return [
seq(`if (_.Set.isSet<_ptr>(${src})) `, ctx.block(() => [
seq(`${r} = new _.KeyedSet()`),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, setPattern, v, vv =>
... converterFor(ctx, M.anonymousSimplePattern(setPattern), v, vv =>
[`${r}.add(${vv})`, `continue`]),
seq(`${r} = void 0`),
seq(`break`)])),
ctx.convertCapture(maybeName, r, ks)]))];
}
case M.$dictof: {
const keyPattern = p[0];
const valPattern = p[1];
case 'dictof': {
const keyPattern = p.value.key;
const valPattern = p.value.value;
const r = ctx.gentemp(dictionaryType(ctx.mod, keyPattern, valPattern));
const v = ctx.gentempname();
const k = ctx.gentempname();
@ -152,19 +153,20 @@ function converterFor(
seq(`if (_.Dictionary.isDictionary<_ptr>(${src})) `, ctx.block(() => [
seq(`${r} = new _.KeyedDictionary()`),
seq(`for (const [${k}, ${v}] of ${src}) `, ctx.block(() => [
... converterFor(ctx, keyPattern, k, kk =>
converterFor(ctx, valPattern, v, vv =>
... converterFor(ctx, M.anonymousSimplePattern(keyPattern), k, kk =>
converterFor(ctx, M.anonymousSimplePattern(valPattern), v, vv =>
[`${r}.set(${kk}, ${vv})`, `continue`])),
seq(`${r} = void 0`),
seq(`break`)])),
ctx.convertCapture(maybeName, r, ks)]))];
}
default: {
const arrayType = M.simpleArray(p);
const arrayType = M.simpleArray(p.value);
if (arrayType === void 0) {
return converterForCompound(ctx, p, src, recordFields, () => ks(void 0));
return converterForCompound(ctx, p.value, src, recordFields, () => ks(void 0));
} else {
return [converterForArray(ctx, arrayType, src, !recordFields, ks)];
return [converterForArray(
ctx, M.NamedSimplePattern.anonymous(arrayType), src, !recordFields, ks)];
}
}
}
@ -177,32 +179,32 @@ function converterForSimple(
src: string,
dest: string): Item[]
{
switch (p.label) {
case M.$any:
switch (p._variant) {
case 'any':
return [`${dest} = ${src}`];
case M.$atom: {
case 'atom': {
let test: Item;
switch (p[0]) {
case M.$Boolean: test = `typeof ${src} === 'boolean'`; break;
case M.$Float: test = `_.Float.isSingle(${src})`; break;
case M.$Double: test =`_.Float.isDouble(${src})`; break;
case M.$SignedInteger: test = `typeof ${src} === 'number'`; break;
case M.$String: test = `typeof ${src} === 'string'`; break;
case M.$ByteString: test = `_.Bytes.isBytes(${src})`; break;
case M.$Symbol: test = `typeof ${src} === 'symbol'`; break;
switch (p.atomKind._variant) {
case 'Boolean': test = `typeof ${src} === 'boolean'`; break;
case 'Float': test = `_.Float.isSingle(${src})`; break;
case 'Double': test =`_.Float.isDouble(${src})`; break;
case 'SignedInteger': test = `typeof ${src} === 'number'`; break;
case 'String': test = `typeof ${src} === 'string'`; break;
case 'ByteString': test = `_.Bytes.isBytes(${src})`; break;
case 'Symbol': test = `typeof ${src} === 'symbol'`; break;
}
return [seq(`${dest} = `, test, ` ? ${src} : void 0`)];
}
case M.$lit:
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p[0])}) ? null : void 0`];
case M.$ref:
return M.lookup(refPosition(p), p, ctx.mod.env,
(_p) => [`${dest} = to${p[1].description!}(${src})`],
case 'lit':
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p.value)}) ? null : void 0`];
case 'Ref':
return M.lookup(refPosition(p.value), p.value, ctx.mod.env,
(_p) => [`${dest} = to${p.value.name.description!}(${src})`],
(modId, modPath,_p) => {
ctx.mod.imports.add([modId, modPath]);
return [`${dest} = ${modId}.decode${p[1].description!}(${src})`];
return [`${dest} = ${modId}.decode${p.value.name.description!}(${src})`];
});
case M.$pointer:
case 'pointer':
return [`${dest} = _toPtr(${src})`];
default:
((_p: never) => {})(p);
@ -217,28 +219,31 @@ function converterForCompound(
recordFields: boolean,
ks: () => Item[]): Item[]
{
switch (p.label) {
case M.$rec:
switch (p._variant) {
case 'rec':
return [seq(`if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${src})) `, ctx.block(() =>
converterFor(ctx, p[0], `${src}.label`, () =>
converterFor(ctx, p[1], src, ks, true))))];
case M.$tuple:
return converterForTuple(ctx, p[0], src, recordFields, void 0, ks);
case M.$tuple_STAR_:
return converterForTuple(ctx, p[0], src, recordFields, p[1], ks);
case M.$setof:
case M.$dictof:
converterFor(ctx, p.label, `${src}.label`, () =>
converterFor(ctx, p.fields, src, ks, true))))];
case 'tuple':
return converterForTuple(ctx, p.patterns, src, recordFields, void 0, ks);
case 'tuple*':
return converterForTuple(ctx, p.fixed, src, recordFields, p.variable, ks);
case 'setof':
case 'dictof':
throw new Error('Internal error: setof and dictof are handled in converterFor()');
case M.$dict: {
const entries = Array.from(p[0]);
case 'dict': {
const entries = Array.from(p.entries);
function loop(i: number): Item[] {
if (i < entries.length) {
const [k, n] = entries[i];
const tmpSrc = ctx.gentemp();
return [seq(`if ((${tmpSrc} = ${src}.get(${ctx.mod.literal(k)})) !== void 0) `,
ctx.block(() =>
converterFor(ctx, M.addNameIfAbsent(n, k), tmpSrc, () =>
loop(i + 1))))];
converterFor(
ctx,
M.promoteNamedSimplePattern(M.addNameIfAbsent(n, k)),
tmpSrc,
() => loop(i + 1))))];
} else {
return ks();
}

View File

@ -4,21 +4,21 @@ import { ModuleContext } from "./context";
import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type";
export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type {
if (d.label === M.$or) {
if (d._variant === 'or') {
return Type.union(
new Map(d[0].map(a => [a[0], typeForAlternative(mod, a[1])])));
new Map(d.patterns.map(a => [a.variantLabel, typeForAlternative(mod, a.alternative)])));
} else {
return typeForAlternative(mod, d);
return typeForAlternative(mod, d.value);
}
}
function typeForAlternative(mod: ModuleContext, a: M.Alternative): SimpleType {
if (a.label === M.$and) {
if (a._variant === 'and') {
const fs = new Map();
a[0].forEach(n => gatherFields(fs, mod, n));
a.patterns.forEach(n => gatherFields(fs, mod, n));
return Type.record(fs);
} else {
return typeFor(mod, a);
return typeFor(mod, a.value);
}
}
@ -34,19 +34,19 @@ export function dictionaryType(mod: ModuleContext,
}
export function typeFor(mod: ModuleContext, p: M.Pattern): SimpleType {
if (M.isSimplePattern(p)) {
return simpleType(mod, p);
if (p._variant === 'SimplePattern') {
return simpleType(mod, p.value);
} else {
switch (p.label) {
case M.$setof:
return setType(mod, p[0]);
case M.$dictof:
return dictionaryType(mod, p[0], p[1]);
switch (p.value._variant) {
case 'setof':
return setType(mod, p.value.pattern);
case 'dictof':
return dictionaryType(mod, p.value.key, p.value.value);
default: {
const arrayType = M.simpleArray(p);
const arrayType = M.simpleArray(p.value);
if (arrayType === void 0) {
const fs = new Map();
compoundFields(fs, mod, p);
compoundFields(fs, mod, p.value);
return Type.record(fs);
} else {
return Type.array(simpleType(mod, arrayType));
@ -57,29 +57,29 @@ export function typeFor(mod: ModuleContext, p: M.Pattern): SimpleType {
}
export function simpleType(mod: ModuleContext, p: M.SimplePattern): AtomicType {
switch (p.label) {
case M.$any:
switch (p._variant) {
case 'any':
return ANY_TYPE;
case M.$atom:
switch (p[0]) {
case M.$Boolean: return Type.ref(`boolean`);
case M.$Float: return Type.ref(`_.SingleFloat`);
case M.$Double: return Type.ref(`_.DoubleFloat`);
case M.$SignedInteger: return Type.ref(`number`);
case M.$String: return Type.ref(`string`);
case M.$ByteString: return Type.ref(`_.Bytes`);
case M.$Symbol: return Type.ref(`symbol`);
case 'atom':
switch (p.atomKind._variant) {
case 'Boolean': return Type.ref(`boolean`);
case 'Float': return Type.ref(`_.SingleFloat`);
case 'Double': return Type.ref(`_.DoubleFloat`);
case 'SignedInteger': return Type.ref(`number`);
case 'String': return Type.ref(`string`);
case 'ByteString': return Type.ref(`_.Bytes`);
case 'Symbol': return Type.ref(`symbol`);
}
case M.$pointer:
case 'pointer':
return Type.ref(`_ptr`);
case M.$lit:
case 'lit':
return Type.unit();
case M.$ref:
return M.lookup(refPosition(p), p, mod.env,
(_p) => Type.ref(p[1].description!),
case 'Ref':
return M.lookup(refPosition(p.value), p.value, mod.env,
(_p) => Type.ref(p.value.name.description!),
(modId, modPath,_p) => {
mod.imports.add([modId, modPath]);
return Type.ref(`${modId}.${p[1].description!}`);
return Type.ref(`${modId}.${p.value.name.description!}`);
});
default:
((_p: never) => {})(p);
@ -88,27 +88,28 @@ export function simpleType(mod: ModuleContext, p: M.SimplePattern): AtomicType {
}
function compoundFields(fs: FieldMap, mod: ModuleContext, p: M.CompoundPattern): void {
switch (p.label) {
case M.$rec:
gatherFields(fs, mod, p[0]);
gatherFields(fs, mod, p[1]);
switch (p._variant) {
case 'rec':
gatherFields(fs, mod, p.label);
gatherFields(fs, mod, p.fields);
break;
case M.$tuple:
p[0].forEach(pp => gatherFields(fs, mod, pp));
case 'tuple':
p.patterns.forEach(pp => gatherFields(fs, mod, pp));
break;
case M.$tuple_STAR_: {
p[0].forEach(pp => gatherFields(fs, mod, pp));
const n = p[1];
if (n.label === M.$named) {
fs.set(n[0].description!, Type.array(simpleType(mod, n[1])));
case 'tuple*': {
p.fixed.forEach(pp => gatherFields(fs, mod, pp));
const n = p.variable;
if (n._variant === 'named') {
fs.set(n.value.name.description!, Type.array(simpleType(mod, n.value.pattern)));
}
break;
}
case M.$setof:
case M.$dictof:
case 'setof':
case 'dictof':
break;
case M.$dict:
p[0].forEach((n, k) => gatherFields(fs, mod, M.addNameIfAbsent(n, k)));
case 'dict':
p.entries.forEach((n, k) =>
gatherFields(fs, mod, M.promoteNamedSimplePattern(M.addNameIfAbsent(n, k))));
break;
default:
((_p: never) => {})(p);
@ -117,9 +118,9 @@ function compoundFields(fs: FieldMap, mod: ModuleContext, p: M.CompoundPattern):
}
function gatherFields(fs: FieldMap, mod: ModuleContext, n: M.NamedPattern): void {
if (n.label === M.$named) {
fs.set(n[0].description!, simpleType(mod, n[1]));
} else if (M.isCompoundPattern(n)) {
compoundFields(fs, mod, n);
if (n._variant === 'named') {
fs.set(n.value.name.description!, simpleType(mod, n.value.pattern));
} else if (n.value._variant === 'CompoundPattern') {
compoundFields(fs, mod, n.value.value);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -58,14 +58,14 @@ export function lookup<R>(namePos: Position | null,
kOther: (modId: string, modPath: string, p: M.Definition | null) => R): R
{
for (const e of env) {
if (is(e.schemaModulePath, M.Ref._.module(name)) ||
(e.typescriptModulePath === null && M.Ref._.module(name).length === 0))
if (is(e.schemaModulePath, name.module) ||
(e.typescriptModulePath === null && name.module.length === 0))
{
if (e.schema === null) {
// It's an artificial module, not from a schema. Assume the identifier is present.
return kOther(modsymFor(e), e.typescriptModulePath, null);
} else {
const p = M.Schema._._field0(e.schema).get(M.$definitions).get(M.Ref._.name(name));
const p = e.schema.definitions.get(name.name);
if (p !== void 0) {
if (e.typescriptModulePath === null) {
return kLocal(p);
@ -81,28 +81,36 @@ export function lookup<R>(namePos: Position | null,
}
export function formatRef(r: M.Ref): string {
return [... r[0], r[1]].map(s => s.description!).join('.');
return [... r.module, r.name].map(s => s.description!).join('.');
}
export function unname<R extends M.Pattern | M.SimplePattern>(
p: M.NamedSimplePattern_ | R): M.SimplePattern | R
{
return (p.label === M.$named) ? p[1] : p;
export function unnamePattern(p: M.NamedPattern): M.Pattern {
return (p._variant === 'named') ? M.Pattern.SimplePattern(p.value.pattern) : p.value;
}
export function nameFor<R extends M.Pattern | M.SimplePattern>(
p: M.NamedSimplePattern_ | R): string | undefined
{
return (p.label === M.$named) ? p[0].description! : void 0;
export function unnameSimplePattern(p: M.NamedSimplePattern): M.SimplePattern {
return (p._variant === 'named') ? p.value.pattern : p.value;
}
export function addNameIfAbsent(p: M.NamedSimplePattern, k: Input): M.NamedSimplePattern {
if (p.label === M.$named) {
export function promoteNamedSimplePattern(p: M.NamedSimplePattern): M.NamedPattern {
return (p._variant === 'named') ? p : M.NamedPattern.anonymous(M.Pattern.SimplePattern(p.value));
}
export function nameFor(p: M.NamedSimplePattern | M.NamedPattern) : string | undefined {
return (p._variant === 'named') ? p.value.name.description! : void 0;
}
export function anonymousSimplePattern(p: M.SimplePattern): M.NamedPattern {
return M.NamedPattern.anonymous(M.Pattern.SimplePattern(p));
}
export function addNameIfAbsent(p: M.NamedSimplePattern, k: M._val): M.NamedSimplePattern {
if (p._variant === 'named') {
return p;
} else {
const s = namelike(k);
if (s !== void 0) {
return M.NamedSimplePattern_(Symbol.for(s), p);
return M.NamedSimplePattern.named(M.NamedSimplePattern__(Symbol.for(s), p.value));
} else {
return p;
}
@ -112,8 +120,8 @@ export function addNameIfAbsent(p: M.NamedSimplePattern, k: Input): M.NamedSimpl
// Simple arrays at toplevel for convenience
//
export function simpleArray(p: M.CompoundPattern): M.SimplePattern | undefined {
if (p.label === M.$tuple_STAR_ && p[0].length === 0 && p[1].label !== M.$named) {
return p[1];
if (p._variant === 'tuple*' && p.fixed.length === 0 && p.variable._variant !== 'named') {
return p.variable.value;
} else {
return void 0;
}

View File

@ -1,17 +1,17 @@
import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, ReaderOptions, stringify, isCompound } from '@preserves/core';
import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, ReaderOptions, stringify, isCompound, KeyedDictionary } from '@preserves/core';
import { Input, Pattern, Schema, Alternative, Definition, CompoundPattern, SimplePattern } from './meta';
import * as M from './meta';
import { SchemaSyntaxError } from './error';
const positionTable = new WeakMap<Input & object, Position>();
const positionTable = new WeakMap<object, Position>();
export function recordPosition<X extends Input & object>(v: X, pos: Position | null): X {
export function recordPosition<X extends object>(v: X, pos: Position | null): X {
if (pos === null) { console.error('Internal error in Schema reader: null source position for', v); }
if (pos !== null) positionTable.set(v, pos);
return v;
}
export function refPosition(v: Input & object): Position | null {
export function refPosition(v: object): Position | null {
return positionTable.get(v) ?? null;
}
@ -65,8 +65,8 @@ export function parseSchema(toplevelTokens: Array<Input>,
options: ReaderOptions<never> & SchemaReaderOptions): Schema
{
let version: M.Version | undefined = void 0;
let pointer: M.PointerName = false;
let definitions = new Dictionary<never, Definition>();
let pointer: M.PointerName = M.PointerName.$false();
let definitions = new KeyedDictionary<symbol, Definition, M._ptr>();
function process(toplevelTokens: Array<Input>): void {
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT);
@ -89,10 +89,13 @@ export function parseSchema(toplevelTokens: Array<Input>,
} else if (clause.length === 2 && is(clause[0], M.$pointer)) {
const pos = position(clause[1]);
const stx = peel(clause[1]);
const quasiName = 'pointer name specification';
pointer = M.asPointerName((stx === false) ? stx
: (typeof stx === 'symbol') ? parseRef(quasiName, pos, stx)
: invalidPattern(quasiName, stx, pos));
if (stx === false) {
pointer = M.PointerName.$false();
} else if (typeof stx === 'symbol') {
pointer = M.PointerName.Ref(parseRef(stx.description!, pos));
} else {
invalidPattern('pointer name specification', stx, pos);
}
} else if (clause.length === 2 && is(clause[0], M.INCLUDE)) {
const pos = position(clause[1]);
const path = peel(clause[1]);
@ -115,11 +118,7 @@ export function parseSchema(toplevelTokens: Array<Input>,
throw new SchemaSyntaxError("Schema: missing version declaration.", null);
}
return M.asSchema(Record(M.$schema, [new Dictionary<never>([
[M.$version, version],
[M.$pointer, pointer],
[M.$definitions, definitions],
])]));
return M.Schema(M.Version(), pointer, definitions);
}
function namedMustBeSimple(p: Position | null): never {
@ -129,32 +128,45 @@ function namedMustBeSimple(p: Position | null): never {
function parseDefinition(name: symbol, body: Array<Input>): Definition {
let nextAnonymousAlternativeNumber = 0;
function alternativeName([input, p]: readonly [Array<Input>, Alternative])
: [string, Alternative]
: M.NamedAlternative
{
const n = findName(input) || findName(input[0]);
if (n !== false) {
return [n.description!, p];
return M.NamedAlternative(n.description!, p);
}
if (p.label === M.$rec && p[0].label === M.$lit && typeof p[0][0] === 'symbol') {
return [p[0][0].description!, p];
if (p._variant === 'Pattern' &&
p.value._variant === 'CompoundPattern' &&
p.value.value._variant === 'rec' &&
p.value.value.label._variant === 'anonymous' &&
p.value.value.label.value._variant === 'SimplePattern' &&
p.value.value.label.value.value._variant === 'lit' &&
typeof p.value.value.label.value.value.value === 'symbol')
{
return M.NamedAlternative(p.value.value.label.value.value.value.description!, p);
}
if (p.label === M.$ref) {
return [p[1].description!, p];
if (p._variant === 'Pattern' &&
p.value._variant === 'SimplePattern' &&
p.value.value._variant === 'Ref')
{
return M.NamedAlternative(p.value.value.value.name.description!, p);
}
if (p.label === M.$lit) {
const s = M.namelike(p[0]);
if (s !== void 0) return [s, p];
if (p._variant === 'Pattern' &&
p.value._variant === 'SimplePattern' &&
p.value.value._variant === 'lit')
{
const s = M.namelike(p.value.value.value);
if (s !== void 0) return M.NamedAlternative(s, p);
}
return ['_anonymous' + nextAnonymousAlternativeNumber++, p];
return M.NamedAlternative('_anonymous' + nextAnonymousAlternativeNumber++, p);
}
function patternName([input, p]: readonly [Array<Input>, Pattern]) : M.NamedPattern {
const n = findName(input) || findName(input[0]);
if (n !== false) {
if (!M.isSimplePattern(p)) namedMustBeSimple(position(input[0]));
return Record(M.$named, [n, p]);
if (p._variant !== 'SimplePattern') namedMustBeSimple(position(input[0]));
return M.NamedPattern.named(M.NamedSimplePattern__(n, p.value));
}
return p;
return M.NamedPattern.anonymous(p);
}
// TODO: deal with situation where there's an or of ands, where
@ -167,29 +179,32 @@ function parseDefinition(name: symbol, body: Array<Input>): Definition {
p => [p, parseOp(p,
M.ANDSYM,
p => [p, parsePattern(name, p)] as const,
ps => Record(M.$and, [ps.map(patternName)]),
p => p[1] as Alternative)] as const,
ps => Record(M.$or, [ps.map(alternativeName)]),
p => p[1] as Definition);
ps => M.Alternative.and(ps.map(patternName)),
p => M.Alternative.Pattern(p[1]))] as const,
ps => M.Definition.or(ps.map(alternativeName)),
p => M.Definition.Alternative(p[1]));
}
function parsePattern(name: symbol, body0: Array<Input>): Pattern {
function parseSimple<A>(item0: Input, kf: () => A): SimplePattern | A {
function parseSimple<A>(item0: Input, ks: (p: SimplePattern) => A, kf: () => A): A {
const pos = position(item0);
const item = peel(item0);
function complain(): never { invalidPattern(stringify(name), item, pos); }
if (typeof item === 'symbol') {
switch (item) {
case Symbol.for('any'): return Record<typeof M.$any, []>(M.$any, []);
case Symbol.for('bool'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Boolean]);
case Symbol.for('float'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Float]);
case Symbol.for('double'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Double]);
case Symbol.for('int'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$SignedInteger]);
case Symbol.for('string'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$String]);
case Symbol.for('bytes'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$ByteString]);
case Symbol.for('symbol'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Symbol]);
case Symbol.for('ref'): return Record<typeof M.$pointer, []>(M.$pointer, []);
default: return parseRef(stringify(name), pos, item);
const str = item.description!;
switch (str) {
case 'any': return ks(M.SimplePattern.any());
case 'bool': return ks(M.SimplePattern.atom(M.AtomKind.Boolean()));
case 'float': return ks(M.SimplePattern.atom(M.AtomKind.Float()));
case 'double': return ks(M.SimplePattern.atom(M.AtomKind.Double()));
case 'int': return ks(M.SimplePattern.atom(M.AtomKind.SignedInteger()));
case 'string': return ks(M.SimplePattern.atom(M.AtomKind.String()));
case 'bytes': return ks(M.SimplePattern.atom(M.AtomKind.ByteString()));
case 'symbol': return ks(M.SimplePattern.atom(M.AtomKind.Symbol()));
case 'ref': return ks(M.SimplePattern.pointer());
default: return ks((str[0] === '=')
? M.SimplePattern.lit(Symbol.for(str.slice(1)))
: M.SimplePattern.Ref(parseRef(str, pos)));
}
} else if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
const label = item.label;
@ -198,7 +213,7 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
switch (label.label) {
case M.$lit:
if (item.length !== 1) complain();
return Record(M.$lit, [item[0]]);
return ks(M.SimplePattern.lit(item[0]));
default:
return kf();
}
@ -208,7 +223,7 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
} else if (isCompound(item)) {
return kf();
} else {
return Record(M.$lit, [strip(item)]);
return ks(M.SimplePattern.lit(strip(item)));
}
}
@ -217,14 +232,16 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
const item = peel(item0);
function complain(): never { invalidPattern(stringify(name), item, pos); }
const walkSimple = (b: Input): SimplePattern => parseSimple(b, () => {
const walkSimple = (b: Input): SimplePattern => parseSimple(b, p => p, () => {
throw new SchemaSyntaxError(`Compound patterns not accepted here`, position(b));
});
const walk = (b: Input): Pattern => parsePattern(name, [b]);
function _maybeNamed<R>(
recur: (b: Input) => R,
literalName?: Input): (b: Input) => M.NamedSimplePattern_ | R
function _maybeNamed<R,P>(
named: (p: M.NamedSimplePattern_) => R,
anonymous: (p: P) => R,
recur: (b: Input) => P,
literalName?: Input): (b: Input) => R
{
return (b: Input) => {
let name = findName(b);
@ -234,14 +251,15 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
}
}
if (name === false) {
return recur(b);
return anonymous(recur(b));
}
return Record(M.$named, [name, parseSimple(b, () =>
namedMustBeSimple(position(b)))]);
return named(M.NamedSimplePattern__(name, parseSimple(b, p => p, () =>
namedMustBeSimple(position(b)))));
};
}
const maybeNamed = _maybeNamed(walk);
const maybeNamedSimple = _maybeNamed(walkSimple);
const maybeNamed = _maybeNamed(M.NamedPattern.named, M.NamedPattern.anonymous, walk);
const maybeNamedSimple =
_maybeNamed(M.NamedSimplePattern.named, M.NamedSimplePattern.anonymous, walkSimple);
if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
const label = item.label;
@ -250,37 +268,47 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
switch (label.label) {
case M.$rec:
if (item.length !== 2) complain();
return Record(M.$rec, [walk(item[0]), walk(item[1])]);
return M.CompoundPattern.rec(maybeNamed(item[0]), maybeNamed(item[1]));
default:
complain();
}
} else {
return Record(M.$rec, [Record(M.$lit, [label]), Record(M.$tuple, [item.map(maybeNamed)])]);
return M.CompoundPattern.rec(
M.NamedPattern.anonymous(M.Pattern.SimplePattern(M.SimplePattern.lit(label))),
M.NamedPattern.anonymous(M.Pattern.CompoundPattern(
M.CompoundPattern.tuple(item.map(maybeNamed)))));
}
} else if (Array.isArray(item)) {
if (is(item[item.length - 1], M.DOTDOTDOT)) {
if (item.length < 2) complain();
return Record(M.$tuple_STAR_, [
return M.CompoundPattern.tuple_STAR_(
item.slice(0, item.length - 2).map(maybeNamed),
maybeNamedSimple(item[item.length - 2]),
]);
maybeNamedSimple(item[item.length - 2]));
} else {
return Record(M.$tuple, [item.map(maybeNamed)]);
return M.CompoundPattern.tuple(item.map(maybeNamed));
}
} else if (Dictionary.isDictionary<never, Input>(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, [walkSimple(kp), walkSimple(vp)]);
return M.CompoundPattern.dictof(walkSimple(kp), walkSimple(vp));
} else {
return Record(M.$dict, [item.mapEntries<M.NamedSimplePattern, Input, never>(
([k, vp]) => [strip(k), _maybeNamed(walkSimple, k)(vp)])]);
return M.CompoundPattern.dict(
M.DictionaryEntries(item.mapEntries<M.NamedSimplePattern, Input, M._ptr>(
([k, vp]) => [
strip(k),
_maybeNamed(
M.NamedSimplePattern.named,
M.NamedSimplePattern.anonymous,
walkSimple,
k)(vp)
])));
}
} else if (Set.isSet<never>(item)) {
if (item.size !== 1) complain();
const [vp] = item.entries();
return Record(M.$setof, [walkSimple(vp)]);
return M.CompoundPattern.setof(walkSimple(vp));
} else {
complain();
}
@ -290,7 +318,9 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
if (body.length !== 1) {
invalidPattern(stringify(name), body, body.length > 0 ? position(body[0]) : position(body));
}
return parseSimple(body[0], () => parseCompound(body[0]));
return parseSimple(body[0],
M.Pattern.SimplePattern,
() => M.Pattern.CompoundPattern(parseCompound(body[0])));
}
function parseOp<Each, Combined>(body: Array<Input>,
@ -312,13 +342,9 @@ function findName(x: Input): symbol | false {
return false;
}
function parseRef(name: string, pos: Position | null, item: symbol): SimplePattern {
const s = item.description;
if (s === void 0) invalidPattern(name, item, pos);
if (s[0] === '=') return Record(M.$lit, [Symbol.for(s.slice(1))]);
function parseRef(s: string, pos: Position | null): M.Ref {
const pieces = s.split('.');
return recordPosition(Record(M.$ref, [
pieces.slice(0, pieces.length - 1).map(Symbol.for),
Symbol.for(pieces[pieces.length - 1])
]), pos);
return recordPosition(M.Ref(
M.ModulePath(pieces.slice(0, pieces.length - 1).map(Symbol.for)),
Symbol.for(pieces[pieces.length - 1])), pos);
}