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 { export function compile(env: M.Environment, schema: M.Schema, options: CompilerOptions = {}): string {
const mod = new ModuleContext(env, schema, options); 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 = `, mod.defineType(seq(`export type _ptr = `,
renderType(pointerName === false renderType(pointerName._variant === 'false'
? EMPTY_TYPE ? 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.defineType(`export type _val = _.Value<_ptr>;`);
mod.defineFunction(ctx => mod.defineFunction(ctx =>
seq(`export const _decodePtr = `, seq(`export const _decodePtr = `,
(pointerName === false (pointerName._variant === 'false'
? '() => { throw new _.DecodeError("Pointers forbidden"); }' ? '() => { throw new _.DecodeError("Pointers forbidden"); }'
: seq(`(d: _.TypedDecoder<_ptr>) => `, ctx.block(() => [ : seq(`(d: _.TypedDecoder<_ptr>) => `, ctx.block(() => [
seq(`let result`), seq(`let result`),
@ -30,7 +30,7 @@ export function compile(env: M.Environment, schema: M.Schema, options: CompilerO
seq(`return result`)]))), 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 t = typeForDefinition(mod, def);
const nameStr = stringify(name); 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; const name = name0 as symbol;
mod.defineFunction(ctx => mod.defineFunction(ctx =>

View File

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

View File

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

View File

@ -4,21 +4,21 @@ import { ModuleContext } from "./context";
import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type"; import { ANY_TYPE, AtomicType, CollectionType, FieldMap, SimpleType, Type } from "./type";
export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type { export function typeForDefinition(mod: ModuleContext, d: M.Definition): Type {
if (d.label === M.$or) { if (d._variant === 'or') {
return Type.union( 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 { } else {
return typeForAlternative(mod, d); return typeForAlternative(mod, d.value);
} }
} }
function typeForAlternative(mod: ModuleContext, a: M.Alternative): SimpleType { function typeForAlternative(mod: ModuleContext, a: M.Alternative): SimpleType {
if (a.label === M.$and) { if (a._variant === 'and') {
const fs = new Map(); const fs = new Map();
a[0].forEach(n => gatherFields(fs, mod, n)); a.patterns.forEach(n => gatherFields(fs, mod, n));
return Type.record(fs); return Type.record(fs);
} else { } 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 { export function typeFor(mod: ModuleContext, p: M.Pattern): SimpleType {
if (M.isSimplePattern(p)) { if (p._variant === 'SimplePattern') {
return simpleType(mod, p); return simpleType(mod, p.value);
} else { } else {
switch (p.label) { switch (p.value._variant) {
case M.$setof: case 'setof':
return setType(mod, p[0]); return setType(mod, p.value.pattern);
case M.$dictof: case 'dictof':
return dictionaryType(mod, p[0], p[1]); return dictionaryType(mod, p.value.key, p.value.value);
default: { default: {
const arrayType = M.simpleArray(p); const arrayType = M.simpleArray(p.value);
if (arrayType === void 0) { if (arrayType === void 0) {
const fs = new Map(); const fs = new Map();
compoundFields(fs, mod, p); compoundFields(fs, mod, p.value);
return Type.record(fs); return Type.record(fs);
} else { } else {
return Type.array(simpleType(mod, arrayType)); 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 { export function simpleType(mod: ModuleContext, p: M.SimplePattern): AtomicType {
switch (p.label) { switch (p._variant) {
case M.$any: case 'any':
return ANY_TYPE; return ANY_TYPE;
case M.$atom: case 'atom':
switch (p[0]) { switch (p.atomKind._variant) {
case M.$Boolean: return Type.ref(`boolean`); case 'Boolean': return Type.ref(`boolean`);
case M.$Float: return Type.ref(`_.SingleFloat`); case 'Float': return Type.ref(`_.SingleFloat`);
case M.$Double: return Type.ref(`_.DoubleFloat`); case 'Double': return Type.ref(`_.DoubleFloat`);
case M.$SignedInteger: return Type.ref(`number`); case 'SignedInteger': return Type.ref(`number`);
case M.$String: return Type.ref(`string`); case 'String': return Type.ref(`string`);
case M.$ByteString: return Type.ref(`_.Bytes`); case 'ByteString': return Type.ref(`_.Bytes`);
case M.$Symbol: return Type.ref(`symbol`); case 'Symbol': return Type.ref(`symbol`);
} }
case M.$pointer: case 'pointer':
return Type.ref(`_ptr`); return Type.ref(`_ptr`);
case M.$lit: case 'lit':
return Type.unit(); return Type.unit();
case M.$ref: case 'Ref':
return M.lookup(refPosition(p), p, mod.env, return M.lookup(refPosition(p.value), p.value, mod.env,
(_p) => Type.ref(p[1].description!), (_p) => Type.ref(p.value.name.description!),
(modId, modPath,_p) => { (modId, modPath,_p) => {
mod.imports.add([modId, modPath]); mod.imports.add([modId, modPath]);
return Type.ref(`${modId}.${p[1].description!}`); return Type.ref(`${modId}.${p.value.name.description!}`);
}); });
default: default:
((_p: never) => {})(p); ((_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 { function compoundFields(fs: FieldMap, mod: ModuleContext, p: M.CompoundPattern): void {
switch (p.label) { switch (p._variant) {
case M.$rec: case 'rec':
gatherFields(fs, mod, p[0]); gatherFields(fs, mod, p.label);
gatherFields(fs, mod, p[1]); gatherFields(fs, mod, p.fields);
break; break;
case M.$tuple: case 'tuple':
p[0].forEach(pp => gatherFields(fs, mod, pp)); p.patterns.forEach(pp => gatherFields(fs, mod, pp));
break; break;
case M.$tuple_STAR_: { case 'tuple*': {
p[0].forEach(pp => gatherFields(fs, mod, pp)); p.fixed.forEach(pp => gatherFields(fs, mod, pp));
const n = p[1]; const n = p.variable;
if (n.label === M.$named) { if (n._variant === 'named') {
fs.set(n[0].description!, Type.array(simpleType(mod, n[1]))); fs.set(n.value.name.description!, Type.array(simpleType(mod, n.value.pattern)));
} }
break; break;
} }
case M.$setof: case 'setof':
case M.$dictof: case 'dictof':
break; break;
case M.$dict: case 'dict':
p[0].forEach((n, k) => gatherFields(fs, mod, M.addNameIfAbsent(n, k))); p.entries.forEach((n, k) =>
gatherFields(fs, mod, M.promoteNamedSimplePattern(M.addNameIfAbsent(n, k))));
break; break;
default: default:
((_p: never) => {})(p); ((_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 { function gatherFields(fs: FieldMap, mod: ModuleContext, n: M.NamedPattern): void {
if (n.label === M.$named) { if (n._variant === 'named') {
fs.set(n[0].description!, simpleType(mod, n[1])); fs.set(n.value.name.description!, simpleType(mod, n.value.pattern));
} else if (M.isCompoundPattern(n)) { } else if (n.value._variant === 'CompoundPattern') {
compoundFields(fs, mod, n); 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 kOther: (modId: string, modPath: string, p: M.Definition | null) => R): R
{ {
for (const e of env) { for (const e of env) {
if (is(e.schemaModulePath, M.Ref._.module(name)) || if (is(e.schemaModulePath, name.module) ||
(e.typescriptModulePath === null && M.Ref._.module(name).length === 0)) (e.typescriptModulePath === null && name.module.length === 0))
{ {
if (e.schema === null) { if (e.schema === null) {
// It's an artificial module, not from a schema. Assume the identifier is present. // It's an artificial module, not from a schema. Assume the identifier is present.
return kOther(modsymFor(e), e.typescriptModulePath, null); return kOther(modsymFor(e), e.typescriptModulePath, null);
} else { } 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 (p !== void 0) {
if (e.typescriptModulePath === null) { if (e.typescriptModulePath === null) {
return kLocal(p); return kLocal(p);
@ -81,28 +81,36 @@ export function lookup<R>(namePos: Position | null,
} }
export function formatRef(r: M.Ref): string { 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>( export function unnamePattern(p: M.NamedPattern): M.Pattern {
p: M.NamedSimplePattern_ | R): M.SimplePattern | R return (p._variant === 'named') ? M.Pattern.SimplePattern(p.value.pattern) : p.value;
{
return (p.label === M.$named) ? p[1] : p;
} }
export function nameFor<R extends M.Pattern | M.SimplePattern>( export function unnameSimplePattern(p: M.NamedSimplePattern): M.SimplePattern {
p: M.NamedSimplePattern_ | R): string | undefined return (p._variant === 'named') ? p.value.pattern : p.value;
{
return (p.label === M.$named) ? p[0].description! : void 0;
} }
export function addNameIfAbsent(p: M.NamedSimplePattern, k: Input): M.NamedSimplePattern { export function promoteNamedSimplePattern(p: M.NamedSimplePattern): M.NamedPattern {
if (p.label === M.$named) { 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; return p;
} else { } else {
const s = namelike(k); const s = namelike(k);
if (s !== void 0) { if (s !== void 0) {
return M.NamedSimplePattern_(Symbol.for(s), p); return M.NamedSimplePattern.named(M.NamedSimplePattern__(Symbol.for(s), p.value));
} else { } else {
return p; return p;
} }
@ -112,8 +120,8 @@ export function addNameIfAbsent(p: M.NamedSimplePattern, k: Input): M.NamedSimpl
// Simple arrays at toplevel for convenience // Simple arrays at toplevel for convenience
// //
export function simpleArray(p: M.CompoundPattern): M.SimplePattern | undefined { export function simpleArray(p: M.CompoundPattern): M.SimplePattern | undefined {
if (p.label === M.$tuple_STAR_ && p[0].length === 0 && p[1].label !== M.$named) { if (p._variant === 'tuple*' && p.fixed.length === 0 && p.variable._variant !== 'named') {
return p[1]; return p.variable.value;
} else { } else {
return void 0; 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 { Input, Pattern, Schema, Alternative, Definition, CompoundPattern, SimplePattern } from './meta';
import * as M from './meta'; import * as M from './meta';
import { SchemaSyntaxError } from './error'; 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) { console.error('Internal error in Schema reader: null source position for', v); }
if (pos !== null) positionTable.set(v, pos); if (pos !== null) positionTable.set(v, pos);
return v; return v;
} }
export function refPosition(v: Input & object): Position | null { export function refPosition(v: object): Position | null {
return positionTable.get(v) ?? null; return positionTable.get(v) ?? null;
} }
@ -65,8 +65,8 @@ export function parseSchema(toplevelTokens: Array<Input>,
options: ReaderOptions<never> & SchemaReaderOptions): Schema options: ReaderOptions<never> & SchemaReaderOptions): Schema
{ {
let version: M.Version | undefined = void 0; let version: M.Version | undefined = void 0;
let pointer: M.PointerName = false; let pointer: M.PointerName = M.PointerName.$false();
let definitions = new Dictionary<never, Definition>(); let definitions = new KeyedDictionary<symbol, Definition, M._ptr>();
function process(toplevelTokens: Array<Input>): void { function process(toplevelTokens: Array<Input>): void {
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT); 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)) { } else if (clause.length === 2 && is(clause[0], M.$pointer)) {
const pos = position(clause[1]); const pos = position(clause[1]);
const stx = peel(clause[1]); const stx = peel(clause[1]);
const quasiName = 'pointer name specification'; if (stx === false) {
pointer = M.asPointerName((stx === false) ? stx pointer = M.PointerName.$false();
: (typeof stx === 'symbol') ? parseRef(quasiName, pos, stx) } else if (typeof stx === 'symbol') {
: invalidPattern(quasiName, stx, pos)); 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)) { } else if (clause.length === 2 && is(clause[0], M.INCLUDE)) {
const pos = position(clause[1]); const pos = position(clause[1]);
const path = peel(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); throw new SchemaSyntaxError("Schema: missing version declaration.", null);
} }
return M.asSchema(Record(M.$schema, [new Dictionary<never>([ return M.Schema(M.Version(), pointer, definitions);
[M.$version, version],
[M.$pointer, pointer],
[M.$definitions, definitions],
])]));
} }
function namedMustBeSimple(p: Position | null): never { function namedMustBeSimple(p: Position | null): never {
@ -129,32 +128,45 @@ function namedMustBeSimple(p: Position | null): never {
function parseDefinition(name: symbol, body: Array<Input>): Definition { function parseDefinition(name: symbol, body: Array<Input>): Definition {
let nextAnonymousAlternativeNumber = 0; let nextAnonymousAlternativeNumber = 0;
function alternativeName([input, p]: readonly [Array<Input>, Alternative]) function alternativeName([input, p]: readonly [Array<Input>, Alternative])
: [string, Alternative] : M.NamedAlternative
{ {
const n = findName(input) || findName(input[0]); const n = findName(input) || findName(input[0]);
if (n !== false) { 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') { if (p._variant === 'Pattern' &&
return [p[0][0].description!, p]; 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) { if (p._variant === 'Pattern' &&
return [p[1].description!, p]; p.value._variant === 'SimplePattern' &&
p.value.value._variant === 'Ref')
{
return M.NamedAlternative(p.value.value.value.name.description!, p);
} }
if (p.label === M.$lit) { if (p._variant === 'Pattern' &&
const s = M.namelike(p[0]); p.value._variant === 'SimplePattern' &&
if (s !== void 0) return [s, p]; 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 { function patternName([input, p]: readonly [Array<Input>, Pattern]) : M.NamedPattern {
const n = findName(input) || findName(input[0]); const n = findName(input) || findName(input[0]);
if (n !== false) { if (n !== false) {
if (!M.isSimplePattern(p)) namedMustBeSimple(position(input[0])); if (p._variant !== 'SimplePattern') namedMustBeSimple(position(input[0]));
return Record(M.$named, [n, p]); 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 // 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, p => [p, parseOp(p,
M.ANDSYM, M.ANDSYM,
p => [p, parsePattern(name, p)] as const, p => [p, parsePattern(name, p)] as const,
ps => Record(M.$and, [ps.map(patternName)]), ps => M.Alternative.and(ps.map(patternName)),
p => p[1] as Alternative)] as const, p => M.Alternative.Pattern(p[1]))] as const,
ps => Record(M.$or, [ps.map(alternativeName)]), ps => M.Definition.or(ps.map(alternativeName)),
p => p[1] as Definition); p => M.Definition.Alternative(p[1]));
} }
function parsePattern(name: symbol, body0: Array<Input>): Pattern { 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 pos = position(item0);
const item = peel(item0); const item = peel(item0);
function complain(): never { invalidPattern(stringify(name), item, pos); } function complain(): never { invalidPattern(stringify(name), item, pos); }
if (typeof item === 'symbol') { if (typeof item === 'symbol') {
switch (item) { const str = item.description!;
case Symbol.for('any'): return Record<typeof M.$any, []>(M.$any, []); switch (str) {
case Symbol.for('bool'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Boolean]); case 'any': return ks(M.SimplePattern.any());
case Symbol.for('float'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Float]); case 'bool': return ks(M.SimplePattern.atom(M.AtomKind.Boolean()));
case Symbol.for('double'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Double]); case 'float': return ks(M.SimplePattern.atom(M.AtomKind.Float()));
case Symbol.for('int'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$SignedInteger]); case 'double': return ks(M.SimplePattern.atom(M.AtomKind.Double()));
case Symbol.for('string'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$String]); case 'int': return ks(M.SimplePattern.atom(M.AtomKind.SignedInteger()));
case Symbol.for('bytes'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$ByteString]); case 'string': return ks(M.SimplePattern.atom(M.AtomKind.String()));
case Symbol.for('symbol'): return Record<typeof M.$atom, [M.AtomKind]>(M.$atom, [M.$Symbol]); case 'bytes': return ks(M.SimplePattern.atom(M.AtomKind.ByteString()));
case Symbol.for('ref'): return Record<typeof M.$pointer, []>(M.$pointer, []); case 'symbol': return ks(M.SimplePattern.atom(M.AtomKind.Symbol()));
default: return parseRef(stringify(name), pos, item); 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)) { } else if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
const label = item.label; const label = item.label;
@ -198,7 +213,7 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
switch (label.label) { switch (label.label) {
case M.$lit: case M.$lit:
if (item.length !== 1) complain(); if (item.length !== 1) complain();
return Record(M.$lit, [item[0]]); return ks(M.SimplePattern.lit(item[0]));
default: default:
return kf(); return kf();
} }
@ -208,7 +223,7 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
} else if (isCompound(item)) { } else if (isCompound(item)) {
return kf(); return kf();
} else { } 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); const item = peel(item0);
function complain(): never { invalidPattern(stringify(name), item, pos); } 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)); throw new SchemaSyntaxError(`Compound patterns not accepted here`, position(b));
}); });
const walk = (b: Input): Pattern => parsePattern(name, [b]); const walk = (b: Input): Pattern => parsePattern(name, [b]);
function _maybeNamed<R>( function _maybeNamed<R,P>(
recur: (b: Input) => R, named: (p: M.NamedSimplePattern_) => R,
literalName?: Input): (b: Input) => M.NamedSimplePattern_ | R anonymous: (p: P) => R,
recur: (b: Input) => P,
literalName?: Input): (b: Input) => R
{ {
return (b: Input) => { return (b: Input) => {
let name = findName(b); let name = findName(b);
@ -234,14 +251,15 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
} }
} }
if (name === false) { if (name === false) {
return recur(b); return anonymous(recur(b));
} }
return Record(M.$named, [name, parseSimple(b, () => return named(M.NamedSimplePattern__(name, parseSimple(b, p => p, () =>
namedMustBeSimple(position(b)))]); namedMustBeSimple(position(b)))));
}; };
} }
const maybeNamed = _maybeNamed(walk); const maybeNamed = _maybeNamed(M.NamedPattern.named, M.NamedPattern.anonymous, walk);
const maybeNamedSimple = _maybeNamed(walkSimple); const maybeNamedSimple =
_maybeNamed(M.NamedSimplePattern.named, M.NamedSimplePattern.anonymous, walkSimple);
if (Record.isRecord<Input, Tuple<Input>, never>(item)) { if (Record.isRecord<Input, Tuple<Input>, never>(item)) {
const label = item.label; const label = item.label;
@ -250,37 +268,47 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
switch (label.label) { switch (label.label) {
case M.$rec: case M.$rec:
if (item.length !== 2) complain(); 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: default:
complain(); complain();
} }
} else { } 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)) { } else if (Array.isArray(item)) {
if (is(item[item.length - 1], M.DOTDOTDOT)) { if (is(item[item.length - 1], M.DOTDOTDOT)) {
if (item.length < 2) complain(); if (item.length < 2) complain();
return Record(M.$tuple_STAR_, [ return M.CompoundPattern.tuple_STAR_(
item.slice(0, item.length - 2).map(maybeNamed), item.slice(0, item.length - 2).map(maybeNamed),
maybeNamedSimple(item[item.length - 2]), maybeNamedSimple(item[item.length - 2]));
]);
} else { } else {
return Record(M.$tuple, [item.map(maybeNamed)]); return M.CompoundPattern.tuple(item.map(maybeNamed));
} }
} else if (Dictionary.isDictionary<never, Input>(item)) { } else if (Dictionary.isDictionary<never, Input>(item)) {
if (item.size === 2 && item.has(M.DOTDOTDOT)) { if (item.size === 2 && item.has(M.DOTDOTDOT)) {
const v = item.clone(); const v = item.clone();
v.delete(M.DOTDOTDOT); v.delete(M.DOTDOTDOT);
const [[kp, vp]] = v.entries(); const [[kp, vp]] = v.entries();
return Record(M.$dictof, [walkSimple(kp), walkSimple(vp)]); return M.CompoundPattern.dictof(walkSimple(kp), walkSimple(vp));
} else { } else {
return Record(M.$dict, [item.mapEntries<M.NamedSimplePattern, Input, never>( return M.CompoundPattern.dict(
([k, vp]) => [strip(k), _maybeNamed(walkSimple, k)(vp)])]); 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)) { } else if (Set.isSet<never>(item)) {
if (item.size !== 1) complain(); if (item.size !== 1) complain();
const [vp] = item.entries(); const [vp] = item.entries();
return Record(M.$setof, [walkSimple(vp)]); return M.CompoundPattern.setof(walkSimple(vp));
} else { } else {
complain(); complain();
} }
@ -290,7 +318,9 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
if (body.length !== 1) { if (body.length !== 1) {
invalidPattern(stringify(name), body, body.length > 0 ? position(body[0]) : position(body)); 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>, function parseOp<Each, Combined>(body: Array<Input>,
@ -312,13 +342,9 @@ function findName(x: Input): symbol | false {
return false; return false;
} }
function parseRef(name: string, pos: Position | null, item: symbol): SimplePattern { function parseRef(s: string, pos: Position | null): M.Ref {
const s = item.description;
if (s === void 0) invalidPattern(name, item, pos);
if (s[0] === '=') return Record(M.$lit, [Symbol.for(s.slice(1))]);
const pieces = s.split('.'); const pieces = s.split('.');
return recordPosition(Record(M.$ref, [ return recordPosition(M.Ref(
pieces.slice(0, pieces.length - 1).map(Symbol.for), M.ModulePath(pieces.slice(0, pieces.length - 1).map(Symbol.for)),
Symbol.for(pieces[pieces.length - 1]) Symbol.for(pieces[pieces.length - 1])), pos);
]), pos);
} }