Javascript pretty-printer
This commit is contained in:
parent
6cd8cb2c37
commit
0129901dab
|
@ -1,10 +1,9 @@
|
||||||
import { Encoder } from "./encoder";
|
|
||||||
import { Tag } from "./constants";
|
import { Tag } from "./constants";
|
||||||
import { Value } from "./values";
|
|
||||||
import { is, isAnnotated, IsPreservesAnnotated } from "./is";
|
import { is, isAnnotated, IsPreservesAnnotated } from "./is";
|
||||||
import { stringify } from "./text";
|
import type { GenericEmbedded } from "./embedded";
|
||||||
import { GenericEmbedded } from "./embedded";
|
import type { Value } from "./values";
|
||||||
import type { Preservable } from "./encoder";
|
import type { Encoder, Preservable } from "./encoder";
|
||||||
|
import type { Writer, PreserveWritable } from "./writer";
|
||||||
|
|
||||||
export interface Position {
|
export interface Position {
|
||||||
line?: number;
|
line?: number;
|
||||||
|
@ -53,7 +52,7 @@ export function formatPosition(p: Position | null | string): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Annotated<T = GenericEmbedded> implements Preservable<T> {
|
export class Annotated<T = GenericEmbedded> implements Preservable<T>, PreserveWritable<T> {
|
||||||
readonly annotations: Array<Value<T>>;
|
readonly annotations: Array<Value<T>>;
|
||||||
readonly pos: Position | null;
|
readonly pos: Position | null;
|
||||||
readonly item: Value<T>;
|
readonly item: Value<T>;
|
||||||
|
@ -82,6 +81,22 @@ export class Annotated<T = GenericEmbedded> implements Preservable<T> {
|
||||||
encoder.push(this.item);
|
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 {
|
equals(other: any): boolean {
|
||||||
return is(this.item, Annotated.isAnnotated(other) ? other.item : other);
|
return is(this.item, Annotated.isAnnotated(other) ? other.item : other);
|
||||||
}
|
}
|
||||||
|
@ -90,15 +105,6 @@ export class Annotated<T = GenericEmbedded> implements Preservable<T> {
|
||||||
// return hash(this.item);
|
// 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 {
|
get [IsPreservesAnnotated](): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Tag } from './constants';
|
import { Tag } from './constants';
|
||||||
|
import { GenericEmbedded } from './embedded';
|
||||||
import { Encoder, Preservable } from './encoder';
|
import { Encoder, Preservable } from './encoder';
|
||||||
import { Value } from './values';
|
import { Value } from './values';
|
||||||
import { GenericEmbedded } from './embedded';
|
import { Writer, PreserveWritable } from './writer';
|
||||||
|
|
||||||
const textEncoder = new TextEncoder();
|
const textEncoder = new TextEncoder();
|
||||||
const textDecoder = new TextDecoder();
|
const textDecoder = new TextDecoder();
|
||||||
|
@ -10,7 +11,7 @@ export const IsPreservesBytes = Symbol.for('IsPreservesBytes');
|
||||||
|
|
||||||
export type BytesLike = Bytes | Uint8Array;
|
export type BytesLike = Bytes | Uint8Array;
|
||||||
|
|
||||||
export class Bytes implements Preservable<never> {
|
export class Bytes implements Preservable<any>, PreserveWritable<any> {
|
||||||
readonly _view: Uint8Array;
|
readonly _view: Uint8Array;
|
||||||
|
|
||||||
constructor(maybeByteIterable: any = new Uint8Array()) {
|
constructor(maybeByteIterable: any = new Uint8Array()) {
|
||||||
|
@ -122,10 +123,6 @@ export class Bytes implements Preservable<never> {
|
||||||
return textDecoder.decode(this._view);
|
return textDecoder.decode(this._view);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
return this.asPreservesText();
|
|
||||||
}
|
|
||||||
|
|
||||||
__as_preserve__<T = GenericEmbedded>(): Value<T> {
|
__as_preserve__<T = GenericEmbedded>(): Value<T> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -134,26 +131,6 @@ export class Bytes implements Preservable<never> {
|
||||||
return Bytes.isBytes(v) ? v : void 0;
|
return Bytes.isBytes(v) ? v : void 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
asPreservesText(): string {
|
|
||||||
return '#"' + this.__asciify() + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
__asciify(): string {
|
|
||||||
const pieces = [];
|
|
||||||
const v = this._view;
|
|
||||||
for (let i = 0; i < v.length; i++) {
|
|
||||||
const b = v[i];
|
|
||||||
if (b === 92 || b === 34) {
|
|
||||||
pieces.push('\\' + String.fromCharCode(b));
|
|
||||||
} else if (b >= 32 && b <= 126) {
|
|
||||||
pieces.push(String.fromCharCode(b));
|
|
||||||
} else {
|
|
||||||
pieces.push('\\x' + hexDigit(b >> 4) + hexDigit(b & 15));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pieces.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
toHex(): string {
|
toHex(): string {
|
||||||
var nibbles = [];
|
var nibbles = [];
|
||||||
for (let i = 0; i < this.length; i++) {
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
@ -168,12 +145,16 @@ export class Bytes implements Preservable<never> {
|
||||||
return this.toHex();
|
return this.toHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
__preserve_on__(encoder: Encoder<never>) {
|
__preserve_on__(encoder: Encoder<any>) {
|
||||||
encoder.state.emitbyte(Tag.ByteString);
|
encoder.state.emitbyte(Tag.ByteString);
|
||||||
encoder.state.varint(this.length);
|
encoder.state.varint(this.length);
|
||||||
encoder.state.emitbytes(this._view);
|
encoder.state.emitbytes(this._view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__preserve_text_on__(w: Writer<any>) {
|
||||||
|
w.state.writeBytes(this._view);
|
||||||
|
}
|
||||||
|
|
||||||
get [IsPreservesBytes](): boolean {
|
get [IsPreservesBytes](): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import { Encoder, canonicalEncode, canonicalString } from "./encoder";
|
import { Encoder, canonicalEncode, canonicalString } from "./encoder";
|
||||||
import { Tag } from "./constants";
|
import { Tag } from "./constants";
|
||||||
import { FlexMap, FlexSet, _iterMap } from "./flex";
|
import { FlexMap, FlexSet, _iterMap } from "./flex";
|
||||||
import { stringify } from "./text";
|
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
import { Bytes } from './bytes';
|
import { Bytes } from './bytes';
|
||||||
import { GenericEmbedded } from "./embedded";
|
import { GenericEmbedded } from "./embedded";
|
||||||
import type { Preservable } from "./encoder";
|
import type { Preservable } from "./encoder";
|
||||||
|
import type { Writer, PreserveWritable } from "./writer";
|
||||||
|
import { annotations, Annotated } from "./annotated";
|
||||||
|
|
||||||
export type DictionaryType = 'Dictionary' | 'Set';
|
export type DictionaryType = 'Dictionary' | 'Set';
|
||||||
export const DictionaryType = Symbol.for('DictionaryType');
|
export const DictionaryType = Symbol.for('DictionaryType');
|
||||||
|
|
||||||
export class KeyedDictionary<K extends Value<T>, V, T = GenericEmbedded> extends FlexMap<K, V> implements Preservable<T> {
|
export class KeyedDictionary<K extends Value<T>, V, T = GenericEmbedded> extends FlexMap<K, V>
|
||||||
|
implements Preservable<T>, PreserveWritable<T>
|
||||||
|
{
|
||||||
get [DictionaryType](): DictionaryType {
|
get [DictionaryType](): DictionaryType {
|
||||||
return 'Dictionary';
|
return 'Dictionary';
|
||||||
}
|
}
|
||||||
|
@ -34,21 +37,10 @@ export class KeyedDictionary<K extends Value<T>, V, T = GenericEmbedded> extends
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
asPreservesText(): string {
|
|
||||||
return '{' +
|
|
||||||
Array.from(_iterMap(this.entries(), ([k, v]) =>
|
|
||||||
stringify(k) + ': ' + stringify(v))).join(', ') +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
clone(): KeyedDictionary<K, V, T> {
|
clone(): KeyedDictionary<K, V, T> {
|
||||||
return new KeyedDictionary(this);
|
return new KeyedDictionary(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
return this.asPreservesText();
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag]() { return 'Dictionary'; }
|
get [Symbol.toStringTag]() { return 'Dictionary'; }
|
||||||
|
|
||||||
__preserve_on__(encoder: Encoder<T>) {
|
__preserve_on__(encoder: Encoder<T>) {
|
||||||
|
@ -72,6 +64,22 @@ export class KeyedDictionary<K extends Value<T>, V, T = GenericEmbedded> extends
|
||||||
encoder.state.emitbyte(Tag.End);
|
encoder.state.emitbyte(Tag.End);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__preserve_text_on__(w: Writer<T>) {
|
||||||
|
w.state.writeSeq('{', '}', this.entries(), ([k, v]) => {
|
||||||
|
w.push(k);
|
||||||
|
if (Annotated.isAnnotated<T>(v) && (annotations(v).length > 1) && w.state.isIndenting) {
|
||||||
|
w.state.pieces.push(':');
|
||||||
|
w.state.indentCount++;
|
||||||
|
w.state.writeIndent();
|
||||||
|
w.push(v);
|
||||||
|
w.state.indentCount--;
|
||||||
|
} else {
|
||||||
|
w.state.pieces.push(': ');
|
||||||
|
w.push(v as unknown as Value<T>); // Suuuuuuuper unsound
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Dictionary<T = GenericEmbedded, V = Value<T>> extends KeyedDictionary<Value<T>, V, T> {
|
export class Dictionary<T = GenericEmbedded, V = Value<T>> extends KeyedDictionary<Value<T>, V, T> {
|
||||||
|
@ -84,7 +92,9 @@ export class Dictionary<T = GenericEmbedded, V = Value<T>> extends KeyedDictiona
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyedSet<K extends Value<T>, T = GenericEmbedded> extends FlexSet<K> implements Preservable<T> {
|
export class KeyedSet<K extends Value<T>, T = GenericEmbedded> extends FlexSet<K>
|
||||||
|
implements Preservable<T>, PreserveWritable<T>
|
||||||
|
{
|
||||||
get [DictionaryType](): DictionaryType {
|
get [DictionaryType](): DictionaryType {
|
||||||
return 'Set';
|
return 'Set';
|
||||||
}
|
}
|
||||||
|
@ -107,16 +117,6 @@ export class KeyedSet<K extends Value<T>, T = GenericEmbedded> extends FlexSet<K
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
return this.asPreservesText();
|
|
||||||
}
|
|
||||||
|
|
||||||
asPreservesText(): string {
|
|
||||||
return '#{' +
|
|
||||||
Array.from(_iterMap(this.values(), stringify)).join(', ') +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
clone(): KeyedSet<K, T> {
|
clone(): KeyedSet<K, T> {
|
||||||
return new KeyedSet(this);
|
return new KeyedSet(this);
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,10 @@ export class KeyedSet<K extends Value<T>, T = GenericEmbedded> extends FlexSet<K
|
||||||
encoder.encodevalues(Tag.Set, this);
|
encoder.encodevalues(Tag.Set, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__preserve_text_on__(w: Writer<T>) {
|
||||||
|
w.state.writeSeq('#{', '}', this, vv => w.push(vv));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Set<T = GenericEmbedded> extends KeyedSet<Value<T>, T> {
|
export class Set<T = GenericEmbedded> extends KeyedSet<Value<T>, T> {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { EncoderState } from "./encoder";
|
|
||||||
import type { DecoderState } from "./decoder";
|
import type { DecoderState } from "./decoder";
|
||||||
|
import type { EncoderState } from "./encoder";
|
||||||
import type { Value } from "./values";
|
import type { Value } from "./values";
|
||||||
import { ReaderStateOptions } from "./reader";
|
import { ReaderStateOptions } from "./reader";
|
||||||
|
|
||||||
|
@ -26,12 +26,8 @@ export class Embedded<T> {
|
||||||
return isEmbedded<T>(other) && is(this.embeddedValue, other.embeddedValue);
|
return isEmbedded<T>(other) && is(this.embeddedValue, other.embeddedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
asPreservesText(): string {
|
toString(): string {
|
||||||
try {
|
return '#!' + (this.embeddedValue as any).toString();
|
||||||
return '#!' + (this.embeddedValue as any).asPreservesText();
|
|
||||||
} catch {
|
|
||||||
return '#!' + (this.embeddedValue as any).toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__as_preserve__<R>(): T extends R ? Value<R> : never {
|
__as_preserve__<R>(): T extends R ? Value<R> : never {
|
||||||
|
@ -62,7 +58,7 @@ export class GenericEmbedded {
|
||||||
return typeof other === 'object' && 'generic' in other && is(this.generic, other.generic);
|
return typeof other === 'object' && 'generic' in other && is(this.generic, other.generic);
|
||||||
}
|
}
|
||||||
|
|
||||||
asPreservesText(): string {
|
toString(): string {
|
||||||
return this.generic.asPreservesText();
|
return this.generic.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,15 @@
|
||||||
import { GenericEmbedded, EmbeddedType, EmbeddedTypeDecode, EmbeddedTypeEncode } from "./embedded";
|
import { identityEmbeddedTypeEncode } from "./encoder";
|
||||||
import { Encoder, EncoderState, identityEmbeddedTypeEncode } from "./encoder";
|
|
||||||
import { genericEmbeddedTypeDecode, ReaderStateOptions } from "./reader";
|
import { genericEmbeddedTypeDecode, ReaderStateOptions } from "./reader";
|
||||||
|
import { genericEmbeddedTypeEncode, neverEmbeddedTypeEncode } from "./writer";
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
import { DecoderState, neverEmbeddedTypeDecode } from "./decoder";
|
import { DecoderState, neverEmbeddedTypeDecode } from "./decoder";
|
||||||
|
import type { GenericEmbedded, EmbeddedType, EmbeddedTypeDecode } from "./embedded";
|
||||||
export const genericEmbeddedTypeEncode: EmbeddedTypeEncode<GenericEmbedded> = {
|
|
||||||
encode(s: EncoderState, v: GenericEmbedded): void {
|
|
||||||
new Encoder(s, this).push(v.generic);
|
|
||||||
},
|
|
||||||
|
|
||||||
toValue(v: GenericEmbedded): Value<GenericEmbedded> {
|
|
||||||
return v.generic;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const genericEmbeddedType: EmbeddedType<GenericEmbedded> =
|
export const genericEmbeddedType: EmbeddedType<GenericEmbedded> =
|
||||||
Object.assign({},
|
Object.assign({},
|
||||||
genericEmbeddedTypeDecode,
|
genericEmbeddedTypeDecode,
|
||||||
genericEmbeddedTypeEncode);
|
genericEmbeddedTypeEncode);
|
||||||
|
|
||||||
export const neverEmbeddedTypeEncode: EmbeddedTypeEncode<never> = {
|
|
||||||
encode(_s: EncoderState, _v: never): void {
|
|
||||||
throw new Error("Embeddeds not permitted encoding Preserves document");
|
|
||||||
},
|
|
||||||
|
|
||||||
toValue(_v: never): Value<GenericEmbedded> {
|
|
||||||
throw new Error("Embeddeds not permitted encoding Preserves document");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const neverEmbeddedType: EmbeddedType<never> =
|
export const neverEmbeddedType: EmbeddedType<never> =
|
||||||
Object.assign({},
|
Object.assign({},
|
||||||
neverEmbeddedTypeDecode,
|
neverEmbeddedTypeDecode,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Value } from "./values";
|
||||||
import { EncodeError } from "./codec";
|
import { EncodeError } from "./codec";
|
||||||
import { Record, Tuple } from "./record";
|
import { Record, Tuple } from "./record";
|
||||||
import { GenericEmbedded, EmbeddedTypeEncode } from "./embedded";
|
import { GenericEmbedded, EmbeddedTypeEncode } from "./embedded";
|
||||||
|
import type { Embedded } from "./embedded";
|
||||||
|
|
||||||
export type Encodable<T> =
|
export type Encodable<T> =
|
||||||
Value<T> | Preservable<T> | Iterable<Value<T>> | ArrayBufferView;
|
Value<T> | Preservable<T> | Iterable<Value<T>> | ArrayBufferView;
|
||||||
|
@ -248,15 +249,14 @@ export class Encoder<T = object> {
|
||||||
for (let i of v) { this.push(i); }
|
for (let i of v) { this.push(i); }
|
||||||
this.state.emitbyte(Tag.End);
|
this.state.emitbyte(Tag.End);
|
||||||
}
|
}
|
||||||
else if (Array.isArray(v)) {
|
else if (isIterable<Value<T>>(v)) {
|
||||||
this.encodevalues(Tag.Sequence, v);
|
this.encodevalues(Tag.Sequence, v);
|
||||||
}
|
}
|
||||||
else if (isIterable<Value<T>>(v)) {
|
|
||||||
this.encodevalues(Tag.Sequence, v as Iterable<Value<T>>);
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
this.state.emitbyte(Tag.Embedded);
|
((v: Embedded<T>) => {
|
||||||
this.embeddedEncode.encode(this.state, v.embeddedValue);
|
this.state.emitbyte(Tag.Embedded);
|
||||||
|
this.embeddedEncode.encode(this.state, v.embeddedValue);
|
||||||
|
})(v);
|
||||||
}
|
}
|
||||||
return this; // for chaining
|
return this; // for chaining
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { Encoder, Preservable } from "./encoder";
|
|
||||||
import { Tag } from "./constants";
|
import { Tag } from "./constants";
|
||||||
|
import { stringify } from "./text";
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
import { GenericEmbedded } from "./embedded";
|
import type { GenericEmbedded } from "./embedded";
|
||||||
|
import type { Encoder, Preservable } from "./encoder";
|
||||||
|
import type { Writer, PreserveWritable } from "./writer";
|
||||||
|
|
||||||
export type FloatType = 'Single' | 'Double';
|
export type FloatType = 'Single' | 'Double';
|
||||||
export const FloatType = Symbol.for('FloatType');
|
export const FloatType = Symbol.for('FloatType');
|
||||||
|
@ -14,7 +16,7 @@ export abstract class Float {
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.asPreservesText();
|
return stringify(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
equals(other: any): boolean {
|
equals(other: any): boolean {
|
||||||
|
@ -25,7 +27,6 @@ export abstract class Float {
|
||||||
return (this.value | 0); // TODO: something better?
|
return (this.value | 0); // TODO: something better?
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract asPreservesText(): string;
|
|
||||||
abstract get [FloatType](): FloatType;
|
abstract get [FloatType](): FloatType;
|
||||||
|
|
||||||
static isFloat = (x: any): x is Float => x?.[FloatType] !== void 0;
|
static isFloat = (x: any): x is Float => x?.[FloatType] !== void 0;
|
||||||
|
@ -43,7 +44,7 @@ export function floatValue(f: any): number {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SingleFloat extends Float implements Preservable<any> {
|
export class SingleFloat extends Float implements Preservable<any>, PreserveWritable<any> {
|
||||||
__as_preserve__<T = GenericEmbedded>(): Value<T> {
|
__as_preserve__<T = GenericEmbedded>(): Value<T> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -59,12 +60,12 @@ export class SingleFloat extends Float implements Preservable<any> {
|
||||||
encoder.state.index += 4;
|
encoder.state.index += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
get [FloatType](): 'Single' {
|
__preserve_text_on__(w: Writer<any>) {
|
||||||
return 'Single';
|
w.state.pieces.push('' + this.value + 'f');
|
||||||
}
|
}
|
||||||
|
|
||||||
asPreservesText(): string {
|
get [FloatType](): 'Single' {
|
||||||
return '' + this.value + 'f';
|
return 'Single';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ export function Single(value: number | Float): SingleFloat {
|
||||||
return new SingleFloat(value);
|
return new SingleFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DoubleFloat extends Float implements Preservable<any> {
|
export class DoubleFloat extends Float implements Preservable<any>, PreserveWritable<any> {
|
||||||
__as_preserve__<T = GenericEmbedded>(): Value<T> {
|
__as_preserve__<T = GenericEmbedded>(): Value<T> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -88,12 +89,12 @@ export class DoubleFloat extends Float implements Preservable<any> {
|
||||||
encoder.state.index += 8;
|
encoder.state.index += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
get [FloatType](): 'Double' {
|
__preserve_text_on__(w: Writer<any>) {
|
||||||
return 'Double';
|
w.state.pieces.push('' + this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
asPreservesText(): string {
|
get [FloatType](): 'Double' {
|
||||||
return '' + this.value;
|
return 'Double';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,13 @@
|
||||||
import { Annotated } from './annotated';
|
import { Annotated } from './annotated';
|
||||||
import { Bytes } from './bytes';
|
import { Bytes } from './bytes';
|
||||||
import { Set, Dictionary } from './dictionary';
|
import { Set, Dictionary } from './dictionary';
|
||||||
import { Record } from './record';
|
import { stringify } from './text';
|
||||||
|
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
|
|
||||||
[Bytes, Annotated, Set, Dictionary].forEach((C) => {
|
[Bytes, Annotated, Set, Dictionary].forEach((C) => {
|
||||||
(C as any).prototype[util.inspect.custom] =
|
(C as any).prototype[util.inspect.custom] =
|
||||||
function (_depth: any, _options: any) {
|
function (_depth: any, _options: any) {
|
||||||
return this.asPreservesText();
|
return stringify(this, { indent: 2 });
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
Record.fallbackToString = util.inspect;
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class ReaderState {
|
||||||
if (this.atEnd()) {
|
if (this.atEnd()) {
|
||||||
this.buffer = data;
|
this.buffer = data;
|
||||||
} else {
|
} else {
|
||||||
this.buffer = this.buffer.substr(this.index) + data;
|
this.buffer = this.buffer.substring(this.index) + data;
|
||||||
}
|
}
|
||||||
this.discarded += this.index;
|
this.discarded += this.index;
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { GenericEmbedded } from "./embedded";
|
import { GenericEmbedded } from "./embedded";
|
||||||
import { is } from "./is";
|
import { is } from "./is";
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
|
import { Writer } from "./writer";
|
||||||
|
|
||||||
export type Tuple<T> = Array<T> | [T];
|
export type Tuple<T> = Array<T> | [T];
|
||||||
|
|
||||||
|
@ -50,10 +51,6 @@ export namespace Record {
|
||||||
return Array.isArray(x) && 'label' in x;
|
return Array.isArray(x) && 'label' in x;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fallbackToString (_f: Value<any>): string {
|
|
||||||
return '<unprintable_preserves_field_value>';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function constructorInfo<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T = GenericEmbedded>(
|
export function constructorInfo<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T = GenericEmbedded>(
|
||||||
r: Record<L, FieldsType, T>): RecordConstructorInfo<L, T>
|
r: Record<L, FieldsType, T>): RecordConstructorInfo<L, T>
|
||||||
{
|
{
|
||||||
|
@ -83,19 +80,3 @@ export namespace Record {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.prototype.asPreservesText = function (): string {
|
|
||||||
if ('label' in (this as any)) {
|
|
||||||
const r = this as Record<Value, Tuple<Value>, GenericEmbedded>;
|
|
||||||
return '<' + r.label.asPreservesText() + (r.length > 0 ? ' ': '') +
|
|
||||||
r.map(f => {
|
|
||||||
try {
|
|
||||||
return f.asPreservesText();
|
|
||||||
} catch (e) {
|
|
||||||
return Record.fallbackToString(f);
|
|
||||||
}
|
|
||||||
}).join(' ') + '>';
|
|
||||||
} else {
|
|
||||||
return '[' + this.map(i => i.asPreservesText()).join(', ') + ']';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -18,3 +18,4 @@ export * from './record';
|
||||||
export * from './strip';
|
export * from './strip';
|
||||||
export * from './text';
|
export * from './text';
|
||||||
export * from './values';
|
export * from './values';
|
||||||
|
export * from './writer';
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
|
import type { GenericEmbedded } from './embedded';
|
||||||
import type { Value } from './values';
|
import type { Value } from './values';
|
||||||
|
|
||||||
export function stringify(x: any): string {
|
import { Annotated } from './annotated';
|
||||||
if (typeof x?.asPreservesText === 'function') {
|
import { Bytes } from './bytes';
|
||||||
return x.asPreservesText();
|
import { KeyedDictionary, KeyedSet } from './dictionary';
|
||||||
} else {
|
import { Writer, Writable, WriterOptions } from './writer';
|
||||||
try {
|
|
||||||
return JSON.stringify(x);
|
export function stringify<T = GenericEmbedded>(x: any, options?: WriterOptions<T>): string {
|
||||||
} catch (_e) {
|
return Writer.stringify(x as Writable<T>, options);
|
||||||
return ('' + x).asPreservesText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function preserves<T>(pieces: TemplateStringsArray, ...values: Value<T>[]): string {
|
export function preserves<T>(pieces: TemplateStringsArray, ...values: Value<T>[]): string {
|
||||||
|
@ -21,32 +19,6 @@ export function preserves<T>(pieces: TemplateStringsArray, ...values: Value<T>[]
|
||||||
return result.join('');
|
return result.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Annotated, Bytes, KeyedDictionary, KeyedSet].forEach((C) => {
|
||||||
declare global {
|
C.prototype.toString = function () { return stringify(this); };
|
||||||
interface Object { asPreservesText(): string; }
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(Object.prototype, 'asPreservesText', {
|
|
||||||
enumerable: false,
|
|
||||||
writable: true,
|
|
||||||
value: function(): string {
|
|
||||||
return JSON.stringify(this);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Boolean.prototype.asPreservesText = function (): string {
|
|
||||||
return this ? '#t' : '#f';
|
|
||||||
};
|
|
||||||
|
|
||||||
Number.prototype.asPreservesText = function (): string {
|
|
||||||
return '' + this;
|
|
||||||
};
|
|
||||||
|
|
||||||
String.prototype.asPreservesText = function (): string {
|
|
||||||
return JSON.stringify(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Symbol.prototype.asPreservesText = function (): string {
|
|
||||||
// TODO: escaping
|
|
||||||
return this.description ?? '||';
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,328 @@
|
||||||
|
import { isAnnotated } from './is';
|
||||||
|
import { Record, Tuple } from "./record";
|
||||||
|
import type { GenericEmbedded, Embedded, EmbeddedTypeEncode } from "./embedded";
|
||||||
|
import { Encoder, EncoderState } from "./encoder";
|
||||||
|
import type { Value } from "./values";
|
||||||
|
|
||||||
|
export type Writable<T> =
|
||||||
|
Value<T> | PreserveWritable<T> | Iterable<Value<T>> | ArrayBufferView;
|
||||||
|
|
||||||
|
export interface PreserveWritable<T> {
|
||||||
|
__preserve_text_on__(writer: Writer<T>): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPreserveWritable<T>(v: any): v is PreserveWritable<T> {
|
||||||
|
return typeof v === 'object' && v !== null && '__preserve_text_on__' in v && typeof v.__preserve_text_on__ === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIterable<T>(v: any): v is Iterable<T> {
|
||||||
|
return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const genericEmbeddedTypeEncode: EmbeddedTypeEncode<GenericEmbedded> = {
|
||||||
|
encode(s: EncoderState, v: GenericEmbedded): void {
|
||||||
|
new Encoder(s, this).push(v.generic);
|
||||||
|
},
|
||||||
|
|
||||||
|
toValue(v: GenericEmbedded): Value<GenericEmbedded> {
|
||||||
|
return v.generic;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const neverEmbeddedTypeEncode: EmbeddedTypeEncode<never> = {
|
||||||
|
encode(_s: EncoderState, _v: never): void {
|
||||||
|
throw new Error("Embeddeds not permitted encoding Preserves document");
|
||||||
|
},
|
||||||
|
|
||||||
|
toValue(_v: never): Value<GenericEmbedded> {
|
||||||
|
throw new Error("Embeddeds not permitted encoding Preserves document");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface WriterStateOptions {
|
||||||
|
includeAnnotations?: boolean;
|
||||||
|
indent?: number;
|
||||||
|
maxBinaryAsciiLength?: number;
|
||||||
|
maxBinaryAsciiProportion?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WriterOptions<T> extends WriterStateOptions {
|
||||||
|
embeddedEncode?: EmbeddedTypeEncode<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WriterState {
|
||||||
|
pieces: string[] = [];
|
||||||
|
options: WriterStateOptions;
|
||||||
|
indentDelta: string;
|
||||||
|
indentCount = 0;
|
||||||
|
|
||||||
|
constructor (options: WriterStateOptions) {
|
||||||
|
this.options = options;
|
||||||
|
this.indentDelta = ' '.repeat(options.indent ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isIndenting(): boolean {
|
||||||
|
return this.indentDelta.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get includeAnnotations(): boolean {
|
||||||
|
return this.options.includeAnnotations ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeIndent() {
|
||||||
|
if (this.isIndenting) {
|
||||||
|
this.pieces.push('\n');
|
||||||
|
for (let i = 0; i < this.indentCount; i++) {
|
||||||
|
this.pieces.push(this.indentDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeIndentSpace() {
|
||||||
|
if (this.isIndenting) {
|
||||||
|
this.writeIndent();
|
||||||
|
} else {
|
||||||
|
this.pieces.push(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
escapeStringlikeChar(c: string, k: (c: string) => string = (c) => c): string {
|
||||||
|
switch (c) {
|
||||||
|
case "\\": return "\\\\";
|
||||||
|
case "\x08": return "\\b";
|
||||||
|
case "\x0c": return "\\f";
|
||||||
|
case "\x0a": return "\\n";
|
||||||
|
case "\x0d": return "\\r";
|
||||||
|
case "\x09": return "\\t";
|
||||||
|
default: return k(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
escapeStringlike(s: string, quoteChar: string): string {
|
||||||
|
let buf = quoteChar;
|
||||||
|
for (let c of s) {
|
||||||
|
buf = buf + ((c === quoteChar) ? "\\" + quoteChar : this.escapeStringlikeChar(c));
|
||||||
|
}
|
||||||
|
return buf + quoteChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSeq<V>(opener: string, closer: string, vs: Iterable<V>, appender: (v: V) => void) {
|
||||||
|
let iter = vs[Symbol.iterator]();
|
||||||
|
this.pieces.push(opener);
|
||||||
|
const first_i = iter.next();
|
||||||
|
if (first_i.done !== true) {
|
||||||
|
const first_v = first_i.value;
|
||||||
|
const second_i = iter.next();
|
||||||
|
if (second_i.done === true) {
|
||||||
|
appender(first_v);
|
||||||
|
} else {
|
||||||
|
this.indentCount++;
|
||||||
|
this.writeIndent();
|
||||||
|
appender(first_v);
|
||||||
|
this.writeIndentSpace();
|
||||||
|
appender(second_i.value);
|
||||||
|
let i: IteratorResult<V>;
|
||||||
|
while ((i = iter.next()).done !== true) {
|
||||||
|
this.writeIndentSpace();
|
||||||
|
appender(i.value);
|
||||||
|
}
|
||||||
|
this.indentCount--;
|
||||||
|
this.writeIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pieces.push(closer);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBytes(bs: Uint8Array) {
|
||||||
|
const limit = this.options.maxBinaryAsciiLength ?? 1024;
|
||||||
|
const proportion = this.options.maxBinaryAsciiProportion ?? 0.75;
|
||||||
|
if (bs.length >= limit) {
|
||||||
|
this.writeBase64(bs);
|
||||||
|
} else {
|
||||||
|
let count = 0;
|
||||||
|
let sampleSize = Math.min(bs.length, limit);
|
||||||
|
for (let i = 0; i < sampleSize; i++) {
|
||||||
|
const b = bs[i];
|
||||||
|
switch (b) {
|
||||||
|
case 9:
|
||||||
|
case 10:
|
||||||
|
case 13:
|
||||||
|
count++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (b >= 32 && b <= 126) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sampleSize === 0 || (count / sampleSize) >= proportion) {
|
||||||
|
this.writeBinaryStringlike(bs);
|
||||||
|
} else {
|
||||||
|
this.writeBase64(bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBase64(bs: Uint8Array) {
|
||||||
|
this.pieces.push("#[", encodeBase64(bs), "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBinaryStringlike(bs: Uint8Array) {
|
||||||
|
let buf = '#"';
|
||||||
|
for (let b of bs) {
|
||||||
|
if (b === 0x22) {
|
||||||
|
buf = buf + '\\"';
|
||||||
|
} else {
|
||||||
|
buf = buf + this.escapeStringlikeChar(String.fromCharCode(b), c => {
|
||||||
|
if ((b >= 0x20 && b <= 0x7e) && (b !== 0x5c)) {
|
||||||
|
return c;
|
||||||
|
} else {
|
||||||
|
return '\\x' + ('0' + b.toString(16)).slice(-2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pieces.push(buf + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
couldBeFlat<T>(vs: Writable<T>[]): boolean {
|
||||||
|
let seenCompound = false;
|
||||||
|
for (let v of vs) {
|
||||||
|
if (Array.isArray(v) || Set.isSet(v) || Map.isMap(v)) {
|
||||||
|
if (seenCompound) return false;
|
||||||
|
seenCompound = true;
|
||||||
|
}
|
||||||
|
if (this.includeAnnotations && isAnnotated(v) && v.annotations.length > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||||
|
|
||||||
|
export function encodeBase64(bs: Uint8Array): string {
|
||||||
|
let s = '';
|
||||||
|
let buffer = 0;
|
||||||
|
let bitcount = 0;
|
||||||
|
for (let b of bs) {
|
||||||
|
buffer = ((buffer & 0x3f) << 8) | b;
|
||||||
|
bitcount += 8;
|
||||||
|
while (bitcount >= 6) {
|
||||||
|
bitcount -= 6;
|
||||||
|
const v = (buffer >> bitcount) & 0x3f;
|
||||||
|
s = s + BASE64[v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bitcount > 0) {
|
||||||
|
const v = (buffer << (6 - bitcount)) & 0x3f;
|
||||||
|
s = s + BASE64[v];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Writer<T> {
|
||||||
|
state: WriterState;
|
||||||
|
embeddedType: EmbeddedTypeEncode<T>;
|
||||||
|
|
||||||
|
constructor(state: WriterState, embeddedType: EmbeddedTypeEncode<T>);
|
||||||
|
constructor(options?: WriterOptions<T>);
|
||||||
|
constructor(
|
||||||
|
state_or_options: (WriterState | WriterOptions<T>) = {},
|
||||||
|
embeddedType?: EmbeddedTypeEncode<T>
|
||||||
|
) {
|
||||||
|
if (state_or_options instanceof WriterState) {
|
||||||
|
this.state = state_or_options;
|
||||||
|
this.embeddedType = embeddedType!;
|
||||||
|
} else {
|
||||||
|
this.state = new WriterState(state_or_options);
|
||||||
|
this.embeddedType = state_or_options.embeddedEncode ?? neverEmbeddedTypeEncode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static stringify<T>(v: Writable<T>, options?: WriterOptions<T>): string {
|
||||||
|
const w = new Writer(options);
|
||||||
|
w.push(v);
|
||||||
|
return w.contents();
|
||||||
|
}
|
||||||
|
|
||||||
|
contents(): string {
|
||||||
|
return this.state.pieces.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
get includeAnnotations(): boolean {
|
||||||
|
return this.state.includeAnnotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
push(v: Writable<T>) {
|
||||||
|
switch (typeof v) {
|
||||||
|
case 'boolean':
|
||||||
|
this.state.pieces.push(v ? '#t' : '#f');
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
this.state.pieces.push(this.state.escapeStringlike(v, '"'));
|
||||||
|
break;
|
||||||
|
case 'symbol': {
|
||||||
|
const s = v.description!;
|
||||||
|
// FIXME: This regular expression is conservatively correct, but Anglo-chauvinistic.
|
||||||
|
const m = /^[a-zA-Z~!$%^&*?_=+/.][-a-zA-Z~!$%^&*?_=+/.0-9]*$/.exec(s);
|
||||||
|
if (m) {
|
||||||
|
this.state.pieces.push(s);
|
||||||
|
} else {
|
||||||
|
this.state.pieces.push(this.state.escapeStringlike(s, '|'));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'number':
|
||||||
|
this.state.pieces.push('' + v);
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
if (isPreserveWritable<unknown>(v)) {
|
||||||
|
v.__preserve_text_on__(this);
|
||||||
|
}
|
||||||
|
else if (isPreserveWritable<T>(v)) {
|
||||||
|
v.__preserve_text_on__(this);
|
||||||
|
}
|
||||||
|
else if (ArrayBuffer.isView(v)) {
|
||||||
|
if (v instanceof Uint8Array) {
|
||||||
|
this.state.writeBytes(v);
|
||||||
|
} else {
|
||||||
|
const bs = new Uint8Array(v.buffer, v.byteOffset, v.byteLength);
|
||||||
|
this.state.writeBytes(bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(v)) {
|
||||||
|
const flat = this.state.couldBeFlat(v);
|
||||||
|
this.state.pieces.push('<');
|
||||||
|
this.push(v.label);
|
||||||
|
if (!flat) this.state.indentCount++;
|
||||||
|
for (let i of v) {
|
||||||
|
if (flat) {
|
||||||
|
this.state.pieces.push(' ');
|
||||||
|
} else {
|
||||||
|
this.state.writeIndentSpace();
|
||||||
|
}
|
||||||
|
this.push(i);
|
||||||
|
}
|
||||||
|
if (!flat) this.state.indentCount--;
|
||||||
|
this.state.pieces.push('>');
|
||||||
|
}
|
||||||
|
else if (isIterable(v)) {
|
||||||
|
this.state.writeSeq('[', ']', v, vv => this.push(vv));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
((v: Embedded<T>) => {
|
||||||
|
this.state.pieces.push('#!');
|
||||||
|
new Writer(this.state, genericEmbeddedTypeEncode)
|
||||||
|
.push(this.embeddedType.toValue(v.embeddedValue));
|
||||||
|
})(v);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Internal error: unhandled in Preserves Writer.push for ${v}`);
|
||||||
|
}
|
||||||
|
return this; // for chaining
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { stringify } from '@preserves/core';
|
||||||
import * as M from './meta';
|
import * as M from './meta';
|
||||||
|
|
||||||
export function checkSchema(schema: M.Schema): (
|
export function checkSchema(schema: M.Schema): (
|
||||||
|
@ -121,7 +122,7 @@ class Checker {
|
||||||
this.checkNamedPattern(
|
this.checkNamedPattern(
|
||||||
scope,
|
scope,
|
||||||
M.promoteNamedSimplePattern(np),
|
M.promoteNamedSimplePattern(np),
|
||||||
`entry ${key.asPreservesText()} in dictionary in ${context}`));
|
`entry ${stringify(key)} in dictionary in ${context}`));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})(p.value);
|
})(p.value);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Dictionary, KeyedSet, FlexSet, Position, stringify, is } from "@preserves/core";
|
import { Dictionary, KeyedSet, FlexSet, Position, stringify } from "@preserves/core";
|
||||||
import { refPosition } from "../reader";
|
import { refPosition } from "../reader";
|
||||||
import * as M from "../meta";
|
import * as M from "../meta";
|
||||||
import { anglebrackets, block, braces, commas, formatItems, Item, keyvalue, seq, opseq } from "./block";
|
import { anglebrackets, block, braces, commas, formatItems, Item, keyvalue, seq, opseq } from "./block";
|
||||||
|
@ -58,7 +58,7 @@ export class ModuleContext {
|
||||||
literal(v: M.Input): Item {
|
literal(v: M.Input): Item {
|
||||||
let varname = this.literals.get(v);
|
let varname = this.literals.get(v);
|
||||||
if (varname === void 0) {
|
if (varname === void 0) {
|
||||||
varname = M.jsId('$' + v.asPreservesText(), () => '__lit' + this.literals.size);
|
varname = M.jsId('$' + stringify(v), () => '__lit' + this.literals.size);
|
||||||
this.literals.set(v, varname);
|
this.literals.set(v, varname);
|
||||||
}
|
}
|
||||||
return varname;
|
return varname;
|
||||||
|
|
Loading…
Reference in New Issue