130 lines
3.5 KiB
TypeScript
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;
|
|
}
|