import { Tag } from "./constants"; import { is, isAnnotated, IsPreservesAnnotated } from "./is"; import type { GenericEmbedded } from "./embedded"; import type { Value } from "./values"; import type { Encoder, Preservable } from "./encoder"; import type { Writer, PreserveWritable } from "./writer"; export interface Position { line?: number; column?: number; pos: number; name?: string; } export function newPosition(name?: string): Position { return { line: 1, column: 0, pos: 0, name }; } export function updatePosition(p: Position, ch: string): boolean { p.pos++; if (p.line === void 0) { return false; } else { let advancedLine = false; switch (ch) { case '\t': p.column = (p.column! + 8) & ~7; break; case '\n': p.column = 0; p.line++; advancedLine = true; break; case '\r': p.column = 0; break; default: p.column!++; break; } return advancedLine; } } export function formatPosition(p: Position | null | string): string { if (p === null) { return ''; } else if (typeof p === 'string') { return p; } else { return `${p.name ?? ''}:${p.line ?? ''}:${p.column ?? ''}:${p.pos}`; } } export class Annotated implements Preservable, PreserveWritable { readonly annotations: Array>; readonly pos: Position | null; readonly item: Value; constructor(item: Value, pos?: Position) { this.annotations = []; this.pos = pos ?? null; this.item = item; } __as_preserve__(): Value { return this; } static __from_preserve__(v: Value): undefined | Annotated { return isAnnotated(v) ? v : void 0; } __preserve_on__(encoder: Encoder): void { if (encoder.includeAnnotations) { for (const a of this.annotations) { encoder.state.emitbyte(Tag.Annotation); encoder.push(a); } } encoder.push(this.item); } __preserve_text_on__(w: Writer): void { if (w.includeAnnotations) { const flat = this.annotations.length <= 1; for (const a of this.annotations) { w.state.pieces.push("@"); w.push(a); if (flat) { w.state.pieces.push(" "); } else { w.state.writeIndentSpace(); } } } w.push(this.item); } equals(other: any): boolean { return is(this.item, Annotated.isAnnotated(other) ? other.item : other); } // hashCode(): number { // return hash(this.item); // } get [IsPreservesAnnotated](): boolean { return true; } static isAnnotated(x: any): x is Annotated { return isAnnotated(x); } } 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; } export function annotations(v: Value): Array> { return Annotated.isAnnotated(v) ? v.annotations : []; } export function position(v: Value): Position | null { return Annotated.isAnnotated(v) ? v.pos : null; }