import { Encoder } from "./codec"; import { Tag } from "./constants"; import { AsPreserve, PreserveOn } from "./symbols"; import { DefaultPointer, is, Value } from "./values"; import { Record } from './record'; import { Dictionary, Set } from './dictionary'; export const IsPreservesAnnotated = Symbol.for('IsPreservesAnnotated'); export class Annotated { readonly annotations: Array>; readonly item: Value; constructor(item: Value) { this.annotations = []; this.item = item; } [AsPreserve](): Value { return this; } [PreserveOn](encoder: Encoder) { if (encoder.includeAnnotations) { for (const a of this.annotations) { encoder.emitbyte(Tag.Annotation); encoder.push(a); } } encoder.push(this.item); } equals(other: any): boolean { return is(this.item, Annotated.isAnnotated(other) ? other.item : other); } // hashCode(): number { // return hash(this.item); // } toString(): string { return this.asPreservesText(); } asPreservesText(): string { const anns = this.annotations.map((a) => '@' + a.asPreservesText()).join(' '); return (anns ? anns + ' ' : anns) + this.item.asPreservesText(); } get [IsPreservesAnnotated](): boolean { return true; } static isAnnotated(x: any): x is Annotated { return !!x?.[IsPreservesAnnotated]; } } export function peel(v: Value): Value { return strip(v, 1); } export function strip(v: Value, depth: number = Infinity): Value { function step(v: Value, depth: number): Value { if (depth === 0) return v; if (!Annotated.isAnnotated(v)) return v; const nextDepth = depth - 1; function walk(v: Value): Value { return step(v, nextDepth); } if (Record.isRecord(v.item)) { return Record(step(v.item.label, depth), v.item.map(walk)); } else if (Array.isArray(v.item)) { return (v.item as Array>).map(walk); } else if (Set.isSet(v.item)) { return v.item.map(walk); } else if (Dictionary.isDictionary, T>(v.item)) { return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]); } else if (Annotated.isAnnotated(v.item)) { throw new Error("Improper annotation structure"); } else { return v.item; } } return step(v, depth); } export function annotate(v0: Value, ...anns: Value[]): Annotated { const v = Annotated.isAnnotated(v0) ? v0 : new Annotated(v0); anns.forEach((a) => v.annotations.push(a)); return v; }