import { Encoder } from "./encoder"; import { Tag } from "./constants"; import { AsPreserve, PreserveOn } from "./symbols"; import { Value } from "./values"; import { is, isAnnotated, IsPreservesAnnotated } from "./is"; import { stringify } from "./text"; import { GenericEmbedded } from "./embedded"; 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 { readonly annotations: Array>; readonly pos: Position | null; readonly item: Value; constructor(item: Value, pos?: Position) { this.annotations = []; this.pos = pos ?? null; this.item = item; } [AsPreserve](): Value { return this; } [PreserveOn](encoder: Encoder) { if (encoder.includeAnnotations) { for (const a of this.annotations) { encoder.state.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) => '@' + stringify(a)).join(' '); return (anns ? anns + ' ' : anns) + stringify(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 position(v: Value): Position | null { return Annotated.isAnnotated(v) ? v.pos : null; }