// Preserves Values. import { AsPreserve } from './symbols'; import { Bytes } from './bytes'; import { DoubleFloat, SingleFloat } from './float'; import { Record } from './record'; import { Annotated } from './annotated'; import { Set, Dictionary } from './dictionary'; import { Tuple } from './tuple'; export * from './bytes'; export * from './float'; export * from './record'; export * from './annotated'; export * from './dictionary'; export * from './tuple'; export type DefaultPointer = object export type Value = Atom | Compound | T | Annotated; export type Atom = boolean | SingleFloat | DoubleFloat | number | string | Bytes | symbol; export type Compound = Record, T> | Array> | Set | Dictionary, T>; export function fromJS(x: any): Value { switch (typeof x) { case 'number': if (!Number.isInteger(x)) { // We require that clients be explicit about integer vs. non-integer types. throw new TypeError("Refusing to autoconvert non-integer number to Single or Double"); } // FALL THROUGH case 'string': case 'symbol': case 'boolean': return x; case 'undefined': case 'function': case 'bigint': break; case 'object': if (x === null) { break; } if (typeof x[AsPreserve] === 'function') { return x[AsPreserve](); } if (Record.isRecord(x)) { return x; } if (Array.isArray(x)) { return (x as Array>).map>(fromJS); } if (ArrayBuffer.isView(x) || x instanceof ArrayBuffer) { return Bytes.from(x); } // Just... assume it's a T. return (x as T); default: break; } throw new TypeError("Cannot represent JavaScript value as Preserves: " + x); } export function is(a: any, b: any): boolean { if (Annotated.isAnnotated(a)) a = a.item; if (Annotated.isAnnotated(b)) b = b.item; if (Object.is(a, b)) return true; if (typeof a !== typeof b) return false; if (typeof a === 'object') { if (a === null || b === null) return false; if ('equals' in a && typeof a.equals === 'function') return a.equals(b, is); if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (!is(a[i], b[i])) return false; return true; } } return false; } declare global { 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 ?? '||'; }; Array.prototype.asPreservesText = function (): string { return '[' + this.map((i: Value) => i.asPreservesText()).join(', ') + ']'; };