preserves/implementations/javascript/packages/core/src/annotated.ts

130 lines
3.5 KiB
TypeScript

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 '<unknown>';
} else if (typeof p === 'string') {
return p;
} else {
return `${p.name ?? ''}:${p.line ?? ''}:${p.column ?? ''}:${p.pos}`;
}
}
export class Annotated<T = GenericEmbedded> implements Preservable<T>, PreserveWritable<T> {
readonly annotations: Array<Value<T>>;
readonly pos: Position | null;
readonly item: Value<T>;
constructor(item: Value<T>, pos?: Position) {
this.annotations = [];
this.pos = pos ?? null;
this.item = item;
}
__as__preserve__(): Value<T> {
return this;
}
static __from_preserve__<T>(v: Value<T>): undefined | Annotated<T> {
return isAnnotated<T>(v) ? v : void 0;
}
__preserve_on__(encoder: Encoder<T>): 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<T>): 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<T = GenericEmbedded>(x: any): x is Annotated<T> {
return isAnnotated(x);
}
}
export function annotate<T = GenericEmbedded>(v0: Value<T>, ...anns: Value<T>[]): Annotated<T> {
const v = Annotated.isAnnotated<T>(v0) ? v0 : new Annotated(v0);
anns.forEach((a) => v.annotations.push(a));
return v;
}
export function annotations<T = GenericEmbedded>(v: Value<T>): Array<Value<T>> {
return Annotated.isAnnotated<T>(v) ? v.annotations : [];
}
export function position<T = GenericEmbedded>(v: Value<T>): Position | null {
return Annotated.isAnnotated<T>(v) ? v.pos : null;
}