Error handling for asPreserves

This commit is contained in:
Tony Garnock-Jones 2024-05-04 22:08:52 +02:00
parent d568fc56ce
commit c9fa9c590b
1 changed files with 11 additions and 6 deletions

View File

@ -282,25 +282,30 @@ export class Reader extends ReaderBase<never> {
export interface AsPreservesOptions<T extends Embeddable> {
onGroup?: (g: Positioned<Group>) => Value<T>;
onEmbedded?: (v: Value<T>) => Value<T>;
error?: (tag: string, position: Position) => Value<T>;
}
export function asPreserves<T extends Embeddable>(
p: Positioned<Expr>,
options: AsPreservesOptions<T> = {},
): Value<T> {
const error = options.error ?? ((tag, position) => {
throw new Error(formatPosition(position) + ": " + tag);
});
function nonCommas(p: Compound): Positioned<Expr>[] {
return Array.from(p).filter(p => !Punct.isComma(p.item));
}
function walk(p: Positioned<Expr>): Value<T> {
if (p.item instanceof Punct) {
throw new Error(formatPosition(p.position) + ": Invalid punctuation: " + p.item.text);
return error('invalid-punctuation', p.position);
} else if (p.item instanceof Embedded) {
const e = walk({ position: p.position, item: p.item.expr });
if (options.onEmbedded) {
return options.onEmbedded(e);
} else {
throw new Error(formatPosition(p.position) + ": Cannot convert embedded values");
return error('unexpected-embedded', p.position);
}
} else if (p.item instanceof Compound) {
switch (p.item.variant) {
@ -309,7 +314,7 @@ export function asPreserves<T extends Embeddable>(
case 'record': {
const vs = nonCommas(p.item).map(walk);
if (vs.length < 1) {
throw new Error(formatPosition(p.position) + ": Invalid record");
return error('invalid-record', p.position);
}
const r = vs.slice(1) as unknown as VRecord<Value<T>, Value<T>[], T>;
r.label = vs[0];
@ -319,11 +324,11 @@ export function asPreserves<T extends Embeddable>(
const d = new DictionaryMap<T>();
const vs = nonCommas(p.item);
if ((vs.length % 3) !== 0) {
throw new Error(formatPosition(p.position) + ": Invalid dictionary: invalid length");
return error('invalid-dictionary', p.position);
}
for (let i = 0; i < vs.length; i += 3) {
if (!Punct.isColon(vs[i + 1].item)) {
throw new Error(formatPosition(vs[i + 1].position) + ": Expected colon");
return error('missing-colon', vs[i + 1].position);
}
const k = walk(vs[i]);
const v = walk(vs[i + 2]);
@ -335,7 +340,7 @@ export function asPreserves<T extends Embeddable>(
if (options.onGroup) {
return options.onGroup(p as Positioned<Group>);
} else {
throw new Error(formatPosition(p.position) + ": Unexpected group");
return error('unexpected-group', p.position);
}
}
case 'set':