forked from syndicate-lang/preserves
Different approach to pointer codec; support custom schema-driven decode
This commit is contained in:
parent
4ee9f99529
commit
1cc0325007
|
@ -6,20 +6,60 @@ import { DoubleFloat, SingleFloat } from "./float";
|
||||||
import { Record } from "./record";
|
import { Record } from "./record";
|
||||||
import { Bytes, BytesLike, underlying } from "./bytes";
|
import { Bytes, BytesLike, underlying } from "./bytes";
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
|
import { is } from "./is";
|
||||||
|
|
||||||
export interface DecoderOptions<T> {
|
export interface DecoderOptions {
|
||||||
includeAnnotations?: boolean;
|
includeAnnotations?: boolean;
|
||||||
decodePointer?: (v: Value<T>) => T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Decoder<T> {
|
export interface DecoderPointerOptions<T> extends DecoderOptions {
|
||||||
packet: Uint8Array;
|
decodePointer?(d: TypedDecoder<T>): T | undefined;
|
||||||
index: number;
|
}
|
||||||
options: DecoderOptions<T>;
|
|
||||||
|
|
||||||
constructor(packet: BytesLike = new Uint8Array(0), options: DecoderOptions<T> = {}) {
|
export interface TypedDecoder<T> {
|
||||||
|
atEnd(): boolean;
|
||||||
|
|
||||||
|
mark(): any;
|
||||||
|
restoreMark(m: any): void;
|
||||||
|
|
||||||
|
skip(): void;
|
||||||
|
next(): Value<T>;
|
||||||
|
withPointerDecoder<S, R>(decodePointer: (d: TypedDecoder<S>) => S | undefined,
|
||||||
|
body: (d: TypedDecoder<S>) => R): R;
|
||||||
|
|
||||||
|
nextBoolean(): boolean | undefined;
|
||||||
|
nextFloat(): SingleFloat | undefined;
|
||||||
|
nextDouble(): DoubleFloat | undefined;
|
||||||
|
nextPointer(): T | undefined;
|
||||||
|
nextSignedInteger(): number | undefined;
|
||||||
|
nextString(): string | undefined;
|
||||||
|
nextByteString(): Bytes | undefined;
|
||||||
|
nextSymbol(): symbol | undefined;
|
||||||
|
|
||||||
|
openRecord(): boolean;
|
||||||
|
openSequence(): boolean;
|
||||||
|
openSet(): boolean;
|
||||||
|
openDictionary(): boolean;
|
||||||
|
|
||||||
|
closeCompound(): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkIs<T, E extends Value<T>>(actual: Value<T>, expected: E): E | undefined {
|
||||||
|
return is(actual, expected) ? expected : void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _defaultDecodePointer<T>(_d: TypedDecoder<T>): T | undefined {
|
||||||
|
throw new DecodeError("No decodePointer function supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Decoder<T = never> implements TypedDecoder<T> {
|
||||||
|
packet: Uint8Array;
|
||||||
|
index = 0;
|
||||||
|
options: DecoderOptions;
|
||||||
|
decodePointer: ((d: TypedDecoder<T>) => T | undefined) = _defaultDecodePointer;
|
||||||
|
|
||||||
|
constructor(packet: BytesLike = new Uint8Array(0), options: DecoderOptions = {}) {
|
||||||
this.packet = underlying(packet);
|
this.packet = underlying(packet);
|
||||||
this.index = 0;
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,16 +77,24 @@ export class Decoder<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextbyte(): number {
|
nextbyte(): number {
|
||||||
if (this.index >= this.packet.length) throw new ShortPacket("Short packet");
|
if (this.atEnd()) throw new ShortPacket("Short packet");
|
||||||
// ^ NOTE: greater-than-or-equal-to, not greater-than.
|
return this.packet[this.advance()];
|
||||||
return this.packet[this.index++];
|
}
|
||||||
|
|
||||||
|
peekbyte(): number {
|
||||||
|
if (this.atEnd()) throw new ShortPacket("Short packet");
|
||||||
|
return this.packet[this.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(): number {
|
||||||
|
return this.index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextbytes(n: number): DataView {
|
nextbytes(n: number): DataView {
|
||||||
const start = this.index;
|
const start = this.index;
|
||||||
this.index += n;
|
this.index += n;
|
||||||
if (this.index > this.packet.length) throw new ShortPacket("Short packet");
|
if (this.index > this.packet.length) throw new ShortPacket("Short packet");
|
||||||
// ^ NOTE: greater-than, not greater-than-or-equal-to.
|
// ^ NOTE: greater-than, not greater-than-or-equal-to - this makes atEnd() inappropriate
|
||||||
return new DataView(this.packet.buffer, this.packet.byteOffset + start, n);
|
return new DataView(this.packet.buffer, this.packet.byteOffset + start, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +106,7 @@ export class Decoder<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
peekend(): boolean {
|
peekend(): boolean {
|
||||||
const matched = this.nextbyte() === Tag.End;
|
return (this.peekbyte() === Tag.End) && (this.advance(), true);
|
||||||
if (!matched) this.index--;
|
|
||||||
return matched;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextvalues(): Value<T>[] {
|
nextvalues(): Value<T>[] {
|
||||||
|
@ -112,11 +158,11 @@ export class Decoder<T> {
|
||||||
return this.unshiftAnnotation(a, v);
|
return this.unshiftAnnotation(a, v);
|
||||||
}
|
}
|
||||||
case Tag.Pointer: {
|
case Tag.Pointer: {
|
||||||
const d = this.options.decodePointer;
|
const v = this.decodePointer(this);
|
||||||
if (d === void 0) {
|
if (v === void 0) {
|
||||||
throw new DecodeError("No decodePointer function supplied");
|
throw new DecodeError("decodePointer function failed");
|
||||||
}
|
}
|
||||||
return this.wrap(d(this.next()));
|
return this.wrap(v);
|
||||||
}
|
}
|
||||||
case Tag.SignedInteger: return this.wrap(this.nextint(this.varint()));
|
case Tag.SignedInteger: return this.wrap(this.nextint(this.varint()));
|
||||||
case Tag.String: return this.wrap(Bytes.from(this.nextbytes(this.varint())).fromUtf8());
|
case Tag.String: return this.wrap(Bytes.from(this.nextbytes(this.varint())).fromUtf8());
|
||||||
|
@ -131,41 +177,219 @@ export class Decoder<T> {
|
||||||
case Tag.Set: return this.wrap(new Set(this.nextvalues()));
|
case Tag.Set: return this.wrap(new Set(this.nextvalues()));
|
||||||
case Tag.Dictionary: return this.wrap(Decoder.dictionaryFromArray(this.nextvalues()));
|
case Tag.Dictionary: return this.wrap(Decoder.dictionaryFromArray(this.nextvalues()));
|
||||||
default: {
|
default: {
|
||||||
if (tag >= Tag.SmallInteger_lo && tag <= Tag.SmallInteger_lo + 15) {
|
const v = this.nextSmallOrMediumInteger(tag);
|
||||||
const v = tag - Tag.SmallInteger_lo;
|
if (v === void 0) {
|
||||||
return this.wrap(v > 12 ? v - 16 : v);
|
throw new DecodeError("Unsupported Preserves tag: " + tag);
|
||||||
}
|
}
|
||||||
if (tag >= Tag.MediumInteger_lo && tag <= Tag.MediumInteger_lo + 15) {
|
return this.wrap(v);
|
||||||
const n = tag - Tag.MediumInteger_lo;
|
|
||||||
return this.wrap(this.nextint(n + 1));
|
|
||||||
}
|
|
||||||
throw new DecodeError("Unsupported Preserves tag: " + tag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try_next(): Value<T> | undefined {
|
nextSmallOrMediumInteger(tag: number): number | undefined {
|
||||||
const start = this.index;
|
if (tag >= Tag.SmallInteger_lo && tag <= Tag.SmallInteger_lo + 15) {
|
||||||
|
const v = tag - Tag.SmallInteger_lo;
|
||||||
|
return v > 12 ? v - 16 : v;
|
||||||
|
}
|
||||||
|
if (tag >= Tag.MediumInteger_lo && tag <= Tag.MediumInteger_lo + 15) {
|
||||||
|
const n = tag - Tag.MediumInteger_lo;
|
||||||
|
return this.nextint(n + 1);
|
||||||
|
}
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (start >= this.packet.length) return void 0;
|
shortGuard<R>(body: () => R, short: () => R): R {
|
||||||
|
if (this.atEnd()) return short();
|
||||||
// ^ important somewhat-common case optimization - avoid the exception
|
// ^ important somewhat-common case optimization - avoid the exception
|
||||||
|
|
||||||
|
const start = this.mark();
|
||||||
try {
|
try {
|
||||||
return this.next();
|
return body();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (ShortPacket.isShortPacket(e)) {
|
if (ShortPacket.isShortPacket(e)) {
|
||||||
this.index = start;
|
this.restoreMark(start);
|
||||||
return void 0;
|
return short();
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try_next(): Value<T> | undefined {
|
||||||
|
return this.shortGuard(() => this.next(), () => void 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
atEnd(): boolean {
|
||||||
|
return this.index >= this.packet.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark(): number {
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreMark(m: number): void {
|
||||||
|
this.index = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip(): void {
|
||||||
|
// TODO: be more efficient
|
||||||
|
this.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
replacePointerDecoder<S>(decodePointer: (d: TypedDecoder<S>) => S | undefined): Decoder<S> {
|
||||||
|
const replacement = new Decoder<S>(this.packet, this.options);
|
||||||
|
replacement.index = this.index;
|
||||||
|
replacement.decodePointer = decodePointer;
|
||||||
|
this.packet = new Uint8Array();
|
||||||
|
this.index = 0;
|
||||||
|
this.decodePointer = _defaultDecodePointer;
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
withPointerDecoder<S, R>(decodePointer: (d: TypedDecoder<S>) => S | undefined,
|
||||||
|
body: (d: TypedDecoder<S>) => R): R
|
||||||
|
{
|
||||||
|
const oldDecodePointer = this.decodePointer;
|
||||||
|
const disguised = this as unknown as Decoder<S>;
|
||||||
|
disguised.decodePointer = decodePointer;
|
||||||
|
try {
|
||||||
|
return body(disguised);
|
||||||
|
} finally {
|
||||||
|
this.decodePointer = oldDecodePointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skipAnnotations(): void {
|
||||||
|
while (this.peekbyte() === Tag.Annotation) {
|
||||||
|
this.advance();
|
||||||
|
this.skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextBoolean(): boolean | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.False: this.advance(); return false;
|
||||||
|
case Tag.True: this.advance(); return true;
|
||||||
|
default: return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFloat(): SingleFloat | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.Float:
|
||||||
|
this.advance();
|
||||||
|
return new SingleFloat(this.nextbytes(4).getFloat32(0, false));
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextDouble(): DoubleFloat | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.Double:
|
||||||
|
this.advance();
|
||||||
|
return new DoubleFloat(this.nextbytes(8).getFloat64(0, false));
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPointer(): T | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.Pointer: {
|
||||||
|
this.advance();
|
||||||
|
const M = this.mark();
|
||||||
|
const v = this.decodePointer(this);
|
||||||
|
if (v === void 0) this.restoreMark(M);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSignedInteger(): number | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
const b = this.nextbyte();
|
||||||
|
switch (b) {
|
||||||
|
case Tag.SignedInteger:
|
||||||
|
return this.nextint(this.varint());
|
||||||
|
default: {
|
||||||
|
const v = this.nextSmallOrMediumInteger(b);
|
||||||
|
if (v === void 0) this.index--; // ugh
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextString(): string | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.String:
|
||||||
|
this.advance();
|
||||||
|
return Bytes.from(this.nextbytes(this.varint())).fromUtf8();
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextByteString(): Bytes | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.ByteString:
|
||||||
|
this.advance();
|
||||||
|
return Bytes.from(this.nextbytes(this.varint()));
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSymbol(): symbol | undefined {
|
||||||
|
this.skipAnnotations();
|
||||||
|
switch (this.peekbyte()) {
|
||||||
|
case Tag.Symbol:
|
||||||
|
this.advance();
|
||||||
|
return Symbol.for(Bytes.from(this.nextbytes(this.varint())).fromUtf8());
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openRecord(): boolean {
|
||||||
|
this.skipAnnotations();
|
||||||
|
return (this.peekbyte() === Tag.Record) && (this.advance(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
openSequence(): boolean {
|
||||||
|
this.skipAnnotations();
|
||||||
|
return (this.peekbyte() === Tag.Sequence) && (this.advance(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
openSet(): boolean {
|
||||||
|
this.skipAnnotations();
|
||||||
|
return (this.peekbyte() === Tag.Set) && (this.advance(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
openDictionary(): boolean {
|
||||||
|
this.skipAnnotations();
|
||||||
|
return (this.peekbyte() === Tag.Dictionary) && (this.advance(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeCompound(): boolean {
|
||||||
|
return this.peekend();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decode<T>(bs: BytesLike, options?: DecoderOptions<T>) {
|
export function decode<T>(bs: BytesLike, options: DecoderPointerOptions<T> = {}): Value<T> {
|
||||||
return new Decoder(bs, options).next();
|
return new Decoder<T>(bs, options).withPointerDecoder<T, Value<T>>(
|
||||||
|
options.decodePointer ?? _defaultDecodePointer,
|
||||||
|
d => d.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodeWithAnnotations<T>(bs: BytesLike, options: DecoderOptions<T> = {}): Annotated<T> {
|
export function decodeWithAnnotations<T>(bs: BytesLike,
|
||||||
|
options: DecoderPointerOptions<T> = {}): Annotated<T> {
|
||||||
return decode(bs, { ... options, includeAnnotations: true }) as Annotated<T>;
|
return decode(bs, { ... options, includeAnnotations: true }) as Annotated<T>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,13 @@ export function isPreservable<T>(v: any): v is Preservable<T> {
|
||||||
return typeof v === 'object' && v !== null && typeof v[PreserveOn] === 'function';
|
return typeof v === 'object' && v !== null && typeof v[PreserveOn] === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EncoderOptions<T> {
|
export interface EncoderOptions {
|
||||||
canonical?: boolean;
|
canonical?: boolean;
|
||||||
includeAnnotations?: boolean;
|
includeAnnotations?: boolean;
|
||||||
encodePointer?: (v: T) => Value<T>;
|
}
|
||||||
|
|
||||||
|
export interface EncoderPointerOptions<T> extends EncoderOptions {
|
||||||
|
encodePointer?: (e: Encoder<T>, v: T) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function chunkStr(bs: Uint8Array): string {
|
function chunkStr(bs: Uint8Array): string {
|
||||||
|
@ -30,19 +33,38 @@ function isIterable<T>(v: any): v is Iterable<T> {
|
||||||
return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function';
|
return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _defaultEncodePointer<T>(e: Encoder<T>, v: T): void {
|
||||||
|
e.push(pointerId(v));
|
||||||
|
}
|
||||||
|
|
||||||
export class Encoder<T> {
|
export class Encoder<T> {
|
||||||
chunks: Array<Uint8Array>;
|
chunks: Array<Uint8Array>;
|
||||||
view: DataView;
|
view: DataView;
|
||||||
index: number;
|
index: number;
|
||||||
options: EncoderOptions<T>;
|
options: EncoderOptions;
|
||||||
|
encodePointer: ((e: Encoder<T>, v: T) => void) = _defaultEncodePointer;
|
||||||
|
|
||||||
constructor(options: EncoderOptions<T> = {}) {
|
constructor(options: EncoderOptions = {}) {
|
||||||
this.chunks = [];
|
this.chunks = [];
|
||||||
this.view = new DataView(new ArrayBuffer(256));
|
this.view = new DataView(new ArrayBuffer(256));
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withPointerEncoder<S>(encodePointer: (e: Encoder<S>, v: S) => void,
|
||||||
|
body: (e: Encoder<S>) => void): this
|
||||||
|
{
|
||||||
|
const oldEncodePointer = this.encodePointer;
|
||||||
|
const disguised = this as unknown as Encoder<S>;
|
||||||
|
disguised.encodePointer = encodePointer;
|
||||||
|
try {
|
||||||
|
body(disguised);
|
||||||
|
return this;
|
||||||
|
} finally {
|
||||||
|
this.encodePointer = oldEncodePointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get canonical(): boolean {
|
get canonical(): boolean {
|
||||||
return this.options.canonical ?? true;
|
return this.options.canonical ?? true;
|
||||||
}
|
}
|
||||||
|
@ -183,16 +205,17 @@ export class Encoder<T> {
|
||||||
this.encodevalues(Tag.Sequence, v as Iterable<Value<T>>);
|
this.encodevalues(Tag.Sequence, v as Iterable<Value<T>>);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const e = this.options.encodePointer ?? pointerId;
|
|
||||||
this.emitbyte(Tag.Pointer);
|
this.emitbyte(Tag.Pointer);
|
||||||
this.push(e(v));
|
this.encodePointer(this, v);
|
||||||
}
|
}
|
||||||
return this; // for chaining
|
return this; // for chaining
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encode<T>(v: Encodable<T>, options?: EncoderOptions<T>): Bytes {
|
export function encode<T>(v: Encodable<T>, options: EncoderPointerOptions<T> = {}): Bytes {
|
||||||
return new Encoder(options).push(v).contents();
|
return new Encoder(options).withPointerEncoder<T>(
|
||||||
|
options.encodePointer ?? _defaultEncodePointer,
|
||||||
|
e => e.push(v)).contents();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _nextId = 0;
|
let _nextId = 0;
|
||||||
|
@ -208,7 +231,8 @@ export function pointerId(v: any): number {
|
||||||
|
|
||||||
const _canonicalEncoder = new Encoder({ canonical: true });
|
const _canonicalEncoder = new Encoder({ canonical: true });
|
||||||
let _usingCanonicalEncoder = false;
|
let _usingCanonicalEncoder = false;
|
||||||
export function canonicalEncode(v: Encodable<any>, options?: EncoderOptions<any>): Bytes {
|
export function canonicalEncode(v: Encodable<any>, options?: EncoderPointerOptions<any>): Bytes
|
||||||
|
{
|
||||||
if (options === void 0 && !_usingCanonicalEncoder) {
|
if (options === void 0 && !_usingCanonicalEncoder) {
|
||||||
_usingCanonicalEncoder = true;
|
_usingCanonicalEncoder = true;
|
||||||
const bs = _canonicalEncoder.push(v).contents();
|
const bs = _canonicalEncoder.push(v).contents();
|
||||||
|
@ -220,9 +244,17 @@ export function canonicalEncode(v: Encodable<any>, options?: EncoderOptions<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canonicalString(v: Encodable<any>): string {
|
export function canonicalString(v: Encodable<any>): string {
|
||||||
return _canonicalEncoder.push(v).contentsString();
|
if (!_usingCanonicalEncoder) {
|
||||||
|
_usingCanonicalEncoder = true;
|
||||||
|
const s = _canonicalEncoder.push(v).contentsString();
|
||||||
|
_usingCanonicalEncoder = false;
|
||||||
|
return s;
|
||||||
|
} else {
|
||||||
|
return new Encoder({ canonical: true }).push(v).contentsString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encodeWithAnnotations<T>(v: Encodable<T>, options: EncoderOptions<T> = {}): Bytes {
|
export function encodeWithAnnotations<T>(v: Encodable<T>,
|
||||||
|
options: EncoderPointerOptions<T> = {}): Bytes {
|
||||||
return encode(v, { ... options, includeAnnotations: true });
|
return encode(v, { ... options, includeAnnotations: true });
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ export class Reader<T> {
|
||||||
if (!Bytes.isBytes(bs)) this.error('ByteString must follow #=',
|
if (!Bytes.isBytes(bs)) this.error('ByteString must follow #=',
|
||||||
startPos);
|
startPos);
|
||||||
return decode<T>(bs, {
|
return decode<T>(bs, {
|
||||||
decodePointer: this.options.decodePointer,
|
decodePointer: d => this.options.decodePointer?.(d.next()),
|
||||||
includeAnnotations: this.options.includeAnnotations,
|
includeAnnotations: this.options.includeAnnotations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Value } from './values';
|
import type { Value } from './values';
|
||||||
|
|
||||||
export function stringify(x: any): string {
|
export function stringify(x: any): string {
|
||||||
if (typeof x?.asPreservesText === 'function') {
|
if (typeof x?.asPreservesText === 'function') {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
preserves,
|
preserves,
|
||||||
fromJS,
|
fromJS,
|
||||||
Constants,
|
Constants,
|
||||||
|
TypedDecoder,
|
||||||
|
Encoder,
|
||||||
} from '../src/index';
|
} from '../src/index';
|
||||||
const { Tag } = Constants;
|
const { Tag } = Constants;
|
||||||
import './test-utils';
|
import './test-utils';
|
||||||
|
@ -104,9 +106,9 @@ describe('encoding and decoding pointers', () => {
|
||||||
expect(encode(
|
expect(encode(
|
||||||
[A, B],
|
[A, B],
|
||||||
{
|
{
|
||||||
encodePointer(v: object): Value<object> {
|
encodePointer(e: Encoder<object>, v: object): void {
|
||||||
objects.push(v);
|
objects.push(v);
|
||||||
return objects.length - 1;
|
e.push(objects.length - 1);
|
||||||
}
|
}
|
||||||
})).is(Bytes.from([Tag.Sequence,
|
})).is(Bytes.from([Tag.Sequence,
|
||||||
Tag.Pointer, Tag.SmallInteger_lo,
|
Tag.Pointer, Tag.SmallInteger_lo,
|
||||||
|
@ -124,7 +126,8 @@ describe('encoding and decoding pointers', () => {
|
||||||
Tag.Pointer, Tag.SmallInteger_lo + 1,
|
Tag.Pointer, Tag.SmallInteger_lo + 1,
|
||||||
Tag.End
|
Tag.End
|
||||||
]), {
|
]), {
|
||||||
decodePointer(v: Value<object>): object {
|
decodePointer(d: TypedDecoder<object>): object {
|
||||||
|
const v = d.next();
|
||||||
if (typeof v !== 'number' || v < 0 || v >= objects.length) {
|
if (typeof v !== 'number' || v < 0 || v >= objects.length) {
|
||||||
throw new Error("Unknown pointer target");
|
throw new Error("Unknown pointer target");
|
||||||
}
|
}
|
||||||
|
@ -135,7 +138,7 @@ describe('encoding and decoding pointers', () => {
|
||||||
it('should store pointers embedded in map keys correctly', () => {
|
it('should store pointers embedded in map keys correctly', () => {
|
||||||
const A1 = ({a: 1});
|
const A1 = ({a: 1});
|
||||||
const A2 = ({a: 1});
|
const A2 = ({a: 1});
|
||||||
const m = new Dictionary();
|
const m = new Dictionary<number, object>();
|
||||||
m.set([A1], 1);
|
m.set([A1], 1);
|
||||||
m.set([A2], 2);
|
m.set([A2], 2);
|
||||||
expect(m.get(A1)).toBeUndefined();
|
expect(m.get(A1)).toBeUndefined();
|
||||||
|
@ -179,19 +182,19 @@ describe('common test suite', () => {
|
||||||
back: 5 },
|
back: 5 },
|
||||||
annotation5: {
|
annotation5: {
|
||||||
forward: annotate<Pointer>(
|
forward: annotate<Pointer>(
|
||||||
Record<symbol, any, Pointer>(Symbol.for('R'),
|
Record<symbol, any>(Symbol.for('R'),
|
||||||
[annotate<Pointer>(Symbol.for('f'),
|
[annotate<Pointer>(Symbol.for('f'),
|
||||||
Symbol.for('af'))]),
|
Symbol.for('af'))]),
|
||||||
Symbol.for('ar')),
|
Symbol.for('ar')),
|
||||||
back: Record<Value<Pointer>, any, Pointer>(Symbol.for('R'), [Symbol.for('f')])
|
back: Record<Value<Pointer>, any>(Symbol.for('R'), [Symbol.for('f')])
|
||||||
},
|
},
|
||||||
annotation6: {
|
annotation6: {
|
||||||
forward: Record<Value<Pointer>, any, Pointer>(
|
forward: Record<Value<Pointer>, any>(
|
||||||
annotate<Pointer>(Symbol.for('R'),
|
annotate<Pointer>(Symbol.for('R'),
|
||||||
Symbol.for('ar')),
|
Symbol.for('ar')),
|
||||||
[annotate<Pointer>(Symbol.for('f'),
|
[annotate<Pointer>(Symbol.for('f'),
|
||||||
Symbol.for('af'))]),
|
Symbol.for('af'))]),
|
||||||
back: Record<symbol, any, Pointer>(Symbol.for('R'), [Symbol.for('f')])
|
back: Record<symbol, any>(Symbol.for('R'), [Symbol.for('f')])
|
||||||
},
|
},
|
||||||
annotation7: {
|
annotation7: {
|
||||||
forward: annotate<Pointer>([], Symbol.for('a'), Symbol.for('b'), Symbol.for('c')),
|
forward: annotate<Pointer>([], Symbol.for('a'), Symbol.for('b'), Symbol.for('c')),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Bytes, Decoder, encode, Reader } from '../src/index';
|
import { Bytes, Decoder, encode, Reader } from '../src/index';
|
||||||
import './test-utils';
|
import './test-utils';
|
||||||
import { decodePointer, encodePointer } from './test-utils';
|
import { decodePointer, encodePointer, readPointer } from './test-utils';
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
@ -9,19 +9,23 @@ describe('reading common test suite', () => {
|
||||||
const samples_txt = fs.readFileSync(__dirname + '/../../../../../tests/samples.txt', 'utf-8');
|
const samples_txt = fs.readFileSync(__dirname + '/../../../../../tests/samples.txt', 'utf-8');
|
||||||
|
|
||||||
it('should read equal to decoded binary without annotations', () => {
|
it('should read equal to decoded binary without annotations', () => {
|
||||||
const s1 = new Reader(samples_txt, { decodePointer, includeAnnotations: false }).next();
|
const s1 = new Reader(samples_txt, { decodePointer: readPointer, includeAnnotations: false }).next();
|
||||||
const s2 = new Decoder(samples_bin, { decodePointer, includeAnnotations: false }).next();
|
const s2 = new Decoder(samples_bin, { includeAnnotations: false }).withPointerDecoder(
|
||||||
|
decodePointer,
|
||||||
|
d => d.next());
|
||||||
expect(s1).is(s2);
|
expect(s1).is(s2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read equal to decoded binary with annotations', () => {
|
it('should read equal to decoded binary with annotations', () => {
|
||||||
const s1 = new Reader(samples_txt, { decodePointer, includeAnnotations: true }).next();
|
const s1 = new Reader(samples_txt, { decodePointer: readPointer, includeAnnotations: true }).next();
|
||||||
const s2 = new Decoder(samples_bin, { decodePointer, includeAnnotations: true }).next();
|
const s2 = new Decoder(samples_bin, { includeAnnotations: true }).withPointerDecoder(
|
||||||
|
decodePointer,
|
||||||
|
d => d.next());
|
||||||
expect(s1).is(s2);
|
expect(s1).is(s2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read and encode back to binary with annotations', () => {
|
it('should read and encode back to binary with annotations', () => {
|
||||||
const s = new Reader(samples_txt, { decodePointer, includeAnnotations: true }).next();
|
const s = new Reader(samples_txt, { decodePointer: readPointer, includeAnnotations: true }).next();
|
||||||
const bs = Bytes.toIO(encode(s, {
|
const bs = Bytes.toIO(encode(s, {
|
||||||
encodePointer,
|
encodePointer,
|
||||||
includeAnnotations: true,
|
includeAnnotations: true,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Value, is, preserves, strip } from '../src/index';
|
import { Value, is, preserves, strip, TypedDecoder, Encoder } from '../src/index';
|
||||||
import '../src/node_support';
|
import '../src/node_support';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -51,10 +51,14 @@ export class Pointer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodePointer(v: Value<Pointer>): Pointer {
|
export function readPointer(v: Value<Pointer>): Pointer {
|
||||||
return new Pointer(strip(v));
|
return new Pointer(strip(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function encodePointer(w: Pointer): Value<Pointer> {
|
export function decodePointer(d: TypedDecoder<Pointer>): Pointer {
|
||||||
return w.v;
|
return readPointer(d.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodePointer(e: Encoder<Pointer>, w: Pointer): void {
|
||||||
|
e.push(w.v);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,119 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
|
||||||
return varname;
|
return varname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decoderFor(p: Pattern, recordFields = false): Item {
|
||||||
|
switch (p.label) {
|
||||||
|
case M.$atom:
|
||||||
|
switch (p[0]) {
|
||||||
|
case M.$Boolean: return `d.nextBoolean()`;
|
||||||
|
case M.$Float: return `d.nextFloat()`;
|
||||||
|
case M.$Double: return `d.nextDouble()`;
|
||||||
|
case M.$SignedInteger: return `d.nextSignedInteger()`;
|
||||||
|
case M.$String: return `d.nextString()`;
|
||||||
|
case M.$ByteString: return `d.nextByteString()`;
|
||||||
|
case M.$Symbol: return `d.nextSymbol()`;
|
||||||
|
}
|
||||||
|
case M.$lit:
|
||||||
|
switch (typeof p[0]) {
|
||||||
|
case 'boolean': return `_.checkIs(d.nextBoolean(), ${literal(p[0])})`;
|
||||||
|
case 'string': return `_.checkIs(d.nextString(), ${literal(p[0])})`;
|
||||||
|
case 'number': return `_.checkIs(d.nextSignedInteger(), ${literal(p[0])})`;
|
||||||
|
case 'symbol': return `_.checkIs(d.nextSymbol(), ${literal(p[0])})`;
|
||||||
|
default: return `_.checkIs(d.next(), ${literal(p[0])})`;
|
||||||
|
}
|
||||||
|
case M.$ref:
|
||||||
|
return lookup(refPosition(p), p, env,
|
||||||
|
(_p) => `decode${p[1].description!}(d)`,
|
||||||
|
(p) => decoderFor(p),
|
||||||
|
(mod, modPath,_p) => {
|
||||||
|
imports.add([mod, modPath]);
|
||||||
|
return `${mod}.decode${p[1].description!}(d)`;
|
||||||
|
});
|
||||||
|
case M.$or:
|
||||||
|
return opseq('void 0', ' ?? ', ... p[0].map(pp => decoderFor(pp)));
|
||||||
|
case M.$and:
|
||||||
|
switch (p[0].length) {
|
||||||
|
case 0: return `d.next()`;
|
||||||
|
case 1: return decoderFor(p[0][0]);
|
||||||
|
default: {
|
||||||
|
const tmp = gentemp();
|
||||||
|
const [pp0, ... ppN] = p[0];
|
||||||
|
const otherChecks =
|
||||||
|
opseq('false', ' && ', ... ppN.map(pp => predicateFor(tmp, pp)));
|
||||||
|
return seq(`((${tmp} = `, decoderFor(pp0), `) != void 0) && `,
|
||||||
|
otherChecks, ` ? ${tmp} : void 0`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case M.$pointer:
|
||||||
|
return `_decodePtr(d)`;
|
||||||
|
case M.$rec:
|
||||||
|
return fnblock(
|
||||||
|
seq(`const M = d.mark()`),
|
||||||
|
seq(`if (!d.openRecord()) return void 0`),
|
||||||
|
seq(`const L = `, decoderFor(p[0])),
|
||||||
|
seq(`if (L === void 0) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`const Fs = (`, decoderFor(p[1], true), `) as any`),
|
||||||
|
seq(`if (Fs === void 0) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`return _.Record`,
|
||||||
|
anglebrackets(typeFor(p[0]), typeFor(p[1])),
|
||||||
|
parens(`L`, `Fs`)));
|
||||||
|
case M.$tuple:
|
||||||
|
return fnblock(
|
||||||
|
seq(`const M = d.mark()`),
|
||||||
|
... recordFields ? [] : [seq(`if (!d.openSequence()) return void 0`)],
|
||||||
|
... p[0].map((pp, i) =>
|
||||||
|
seq(`const v${i} = `, decoderFor(unname(pp)), `; `,
|
||||||
|
`if (v${i} === void 0) { d.restoreMark(M); return void 0; }`)),
|
||||||
|
seq(`if (!d.closeCompound()) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`return [${p[0].map((_pp, i) => `v${i}`).join(', ')}] as `, typeFor(p)));
|
||||||
|
case M.$tuple_STAR_:
|
||||||
|
return fnblock(
|
||||||
|
seq(`const M = d.mark()`),
|
||||||
|
... recordFields ? [] : [seq(`if (!d.openSequence()) return void 0`)],
|
||||||
|
... p[0].map((pp, i) =>
|
||||||
|
seq(`const v${i} = `, decoderFor(unname(pp)), `; `,
|
||||||
|
`if (v${i} === void 0) { d.restoreMark(M); return void 0; }`)),
|
||||||
|
seq(`const vN: Array<`, typeFor(unname(p[1])), `> = []`),
|
||||||
|
seq(`let tmp: undefined | `, typeFor(unname(p[1]))),
|
||||||
|
seq(`while ((tmp = `, decoderFor(unname(p[1])), `) !== void 0) vN.push(tmp)`),
|
||||||
|
seq(`if (!d.closeCompound()) { d.restoreMark(M); return void 0; }`),
|
||||||
|
(p[0].length === 0
|
||||||
|
? seq(`return vN`)
|
||||||
|
: seq(`return [${p[0].map((_pp, i) => `v${i}`).join(', ')}, ... vN] as `,
|
||||||
|
typeFor(p))));
|
||||||
|
case M.$setof:
|
||||||
|
return fnblock(
|
||||||
|
seq(`const M = d.mark()`),
|
||||||
|
seq(`if (!d.openSet()) return void 0`),
|
||||||
|
seq(`const r: `, typeFor(p), ` = new _.KeyedSet()`),
|
||||||
|
seq(`let tmp: undefined | `, typeFor(p[0])),
|
||||||
|
seq(`while ((tmp = `, decoderFor(p[0]), `) !== void 0) r.add(tmp)`),
|
||||||
|
seq(`if (!d.closeCompound()) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`return r`));
|
||||||
|
case M.$dictof:
|
||||||
|
return fnblock(
|
||||||
|
seq(`const M = d.mark()`),
|
||||||
|
seq(`if (!d.openDictionary()) return void 0`),
|
||||||
|
seq(`const r: `, typeFor(p), ` = new _.KeyedDictionary()`),
|
||||||
|
seq(`let K: undefined | `, typeFor(p[0])),
|
||||||
|
seq(`while ((K = `, decoderFor(p[0]), `) !== void 0) `, block(
|
||||||
|
seq(`const V = `, decoderFor(p[1])),
|
||||||
|
seq(`if (V === void 0) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`r.set(K, V)`))),
|
||||||
|
seq(`if (!d.closeCompound()) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`return r`));
|
||||||
|
case M.$dict:
|
||||||
|
return fnblock(
|
||||||
|
seq(`const M = d.mark()`),
|
||||||
|
seq(`const r = d.next()`),
|
||||||
|
seq(`if (!(`, predicateFor('r', p), `)) { d.restoreMark(M); return void 0; }`),
|
||||||
|
seq(`return r`));
|
||||||
|
default:
|
||||||
|
((_p: never) => {})(p);
|
||||||
|
throw new Error("Unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function typeFor(p: Pattern): Item {
|
function typeFor(p: Pattern): Item {
|
||||||
switch (p.label) {
|
switch (p.label) {
|
||||||
case M.$atom:
|
case M.$atom:
|
||||||
|
@ -205,23 +318,37 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
|
||||||
types.push(
|
types.push(
|
||||||
seq(`export type ${name.description!} = `, typeFor(pattern), `;`));
|
seq(`export type ${name.description!} = `, typeFor(pattern), `;`));
|
||||||
functions.push(
|
functions.push(
|
||||||
seq('export function ', `is${name.description!}`,
|
seq(`export function is${name.description!}`,
|
||||||
'(v: any): v is ', name.description!, ' ',
|
'(v: any): v is ', name.description!, ' ',
|
||||||
block(
|
block(
|
||||||
... temps.length > 0 ? [seq('let ', commas(... temps), ': any')] : [],
|
... temps.length > 0 ? [seq('let ', commas(... temps), ': any')] : [],
|
||||||
seq('return ', recognizer))));
|
seq('return ', recognizer))));
|
||||||
functions.push(
|
functions.push(
|
||||||
seq('export function ', `as${name.description!}`,
|
seq(`export function as${name.description!}`,
|
||||||
'(v: any): ', name.description!, ' ',
|
'(v: any): ', name.description!, ' ',
|
||||||
block(
|
block(
|
||||||
seq(`if (!is${name.description!}(v)) `,
|
seq(`if (!is${name.description!}(v)) `,
|
||||||
block(`throw new TypeError(\`Invalid ${name.description!}: \${_.stringify(v)}\`)`),
|
block(`throw new TypeError(\`Invalid ${name.description!}: \${_.stringify(v)}\`)`),
|
||||||
' else ',
|
' else ',
|
||||||
block(`return v`)))));
|
block(`return v`)))));
|
||||||
|
|
||||||
|
temps = [];
|
||||||
|
const decoder = decoderFor(pattern);
|
||||||
|
functions.push(
|
||||||
|
seq(`export function decode${name.description!}`,
|
||||||
|
`(d: _.TypedDecoder<_ptr>): ${name.description!} | undefined `,
|
||||||
|
block(
|
||||||
|
... temps.length > 0 ? [seq('let ', commas(... temps), ': any')] : [],
|
||||||
|
seq(`return `, decoder))));
|
||||||
}
|
}
|
||||||
|
|
||||||
types.push(seq('export type _ptr = ', pointerName === false ? 'never' : typeFor(pointerName), `;`));
|
types.push(seq('export type _ptr = ', pointerName === false ? 'never' : typeFor(pointerName), `;`));
|
||||||
types.push(`export type _val = _.Value<_ptr>;`);
|
types.push(`export type _val = _.Value<_ptr>;`);
|
||||||
|
functions.push(seq(`export const _decodePtr = `,
|
||||||
|
(pointerName === false
|
||||||
|
? '() => { throw new _.DecodeError("Pointers forbidden"); }'
|
||||||
|
: seq(`(d: _.TypedDecoder<_ptr>) => `, decoderFor(pointerName))),
|
||||||
|
`;`));
|
||||||
|
|
||||||
const f = new Formatter();
|
const f = new Formatter();
|
||||||
f.write(`import * as _ from ${JSON.stringify(options.preservesModule ?? '@preserves/core')};\n`);
|
f.write(`import * as _ from ${JSON.stringify(options.preservesModule ?? '@preserves/core')};\n`);
|
||||||
|
|
|
@ -137,18 +137,87 @@ export function asSchema(v: any): Schema {
|
||||||
if (!isSchema(v)) {throw new TypeError(`Invalid Schema: ${_.stringify(v)}`);} else {return v;};
|
if (!isSchema(v)) {throw new TypeError(`Invalid Schema: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeSchema(d: _.TypedDecoder<_ptr>): Schema | undefined {
|
||||||
|
let _tmp0, _tmp1, _tmp2: any;
|
||||||
|
return ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $schema);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
const r = d.next();
|
||||||
|
if (!((
|
||||||
|
_.Dictionary.isDictionary<_val, _ptr>(r) &&
|
||||||
|
((_tmp0 = r.get($version)) !== void 0 && isVersion(_tmp0)) &&
|
||||||
|
((_tmp1 = r.get($pointer)) !== void 0 && isPointerName(_tmp1)) &&
|
||||||
|
(
|
||||||
|
(_tmp2 = r.get($definitions)) !== void 0 && (
|
||||||
|
_.Dictionary.isDictionary<_val, _ptr>(_tmp2) &&
|
||||||
|
((() => {
|
||||||
|
for (const e of _tmp2) {
|
||||||
|
if (!(typeof e[0] === 'symbol')) return false;
|
||||||
|
if (!(isPattern(e[1]))) return false;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
})())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))) { d.restoreMark(M); return void 0; };
|
||||||
|
return r;
|
||||||
|
})()); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [
|
||||||
|
(
|
||||||
|
{
|
||||||
|
get(k: typeof $version): Version;
|
||||||
|
get(k: typeof $pointer): PointerName;
|
||||||
|
get(k: typeof $definitions): _.KeyedDictionary<symbol, Pattern, _ptr>;
|
||||||
|
has(k: typeof $version): true;
|
||||||
|
has(k: typeof $pointer): true;
|
||||||
|
has(k: typeof $definitions): true;
|
||||||
|
} & _.Dictionary<_val, _ptr>
|
||||||
|
)
|
||||||
|
];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<
|
||||||
|
(typeof $schema),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
{
|
||||||
|
get(k: typeof $version): Version;
|
||||||
|
get(k: typeof $pointer): PointerName;
|
||||||
|
get(k: typeof $definitions): _.KeyedDictionary<symbol, Pattern, _ptr>;
|
||||||
|
has(k: typeof $version): true;
|
||||||
|
has(k: typeof $pointer): true;
|
||||||
|
has(k: typeof $definitions): true;
|
||||||
|
} & _.Dictionary<_val, _ptr>
|
||||||
|
)
|
||||||
|
]
|
||||||
|
>(L, Fs);
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
export function isVersion(v: any): v is Version {return _.is(v, $1);}
|
export function isVersion(v: any): v is Version {return _.is(v, $1);}
|
||||||
|
|
||||||
export function asVersion(v: any): Version {
|
export function asVersion(v: any): Version {
|
||||||
if (!isVersion(v)) {throw new TypeError(`Invalid Version: ${_.stringify(v)}`);} else {return v;};
|
if (!isVersion(v)) {throw new TypeError(`Invalid Version: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeVersion(d: _.TypedDecoder<_ptr>): Version | undefined {return _.checkIs(d.nextSignedInteger(), $1);}
|
||||||
|
|
||||||
export function isPointerName(v: any): v is PointerName {return (isRef(v) || _.is(v, __lit5));}
|
export function isPointerName(v: any): v is PointerName {return (isRef(v) || _.is(v, __lit5));}
|
||||||
|
|
||||||
export function asPointerName(v: any): PointerName {
|
export function asPointerName(v: any): PointerName {
|
||||||
if (!isPointerName(v)) {throw new TypeError(`Invalid PointerName: ${_.stringify(v)}`);} else {return v;};
|
if (!isPointerName(v)) {throw new TypeError(`Invalid PointerName: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodePointerName(d: _.TypedDecoder<_ptr>): PointerName | undefined {return (decodeRef(d) ?? _.checkIs(d.nextBoolean(), __lit5));}
|
||||||
|
|
||||||
export function isPattern(v: any): v is Pattern {
|
export function isPattern(v: any): v is Pattern {
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
|
@ -267,6 +336,254 @@ export function asPattern(v: any): Pattern {
|
||||||
if (!isPattern(v)) {throw new TypeError(`Invalid Pattern: ${_.stringify(v)}`);} else {return v;};
|
if (!isPattern(v)) {throw new TypeError(`Invalid Pattern: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodePattern(d: _.TypedDecoder<_ptr>): Pattern | undefined {
|
||||||
|
return (
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $atom);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = (
|
||||||
|
_.checkIs(d.nextSymbol(), $Boolean) ??
|
||||||
|
_.checkIs(d.nextSymbol(), $Float) ??
|
||||||
|
_.checkIs(d.nextSymbol(), $Double) ??
|
||||||
|
_.checkIs(d.nextSymbol(), $SignedInteger) ??
|
||||||
|
_.checkIs(d.nextSymbol(), $String) ??
|
||||||
|
_.checkIs(d.nextSymbol(), $ByteString) ??
|
||||||
|
_.checkIs(d.nextSymbol(), $Symbol)
|
||||||
|
); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [
|
||||||
|
(
|
||||||
|
(typeof $Boolean) |
|
||||||
|
(typeof $Float) |
|
||||||
|
(typeof $Double) |
|
||||||
|
(typeof $SignedInteger) |
|
||||||
|
(typeof $String) |
|
||||||
|
(typeof $ByteString) |
|
||||||
|
(typeof $Symbol)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<
|
||||||
|
(typeof $atom),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
(typeof $Boolean) |
|
||||||
|
(typeof $Float) |
|
||||||
|
(typeof $Double) |
|
||||||
|
(typeof $SignedInteger) |
|
||||||
|
(typeof $String) |
|
||||||
|
(typeof $ByteString) |
|
||||||
|
(typeof $Symbol)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $pointer);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [] as [];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $pointer), []>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $lit);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = d.next(); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [_val];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $lit), [_val]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
decodeRef(d) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $or);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const vN: Array<Pattern> = [];
|
||||||
|
let tmp: undefined | Pattern;
|
||||||
|
while ((tmp = decodePattern(d)) !== void 0) vN.push(tmp);
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return vN;
|
||||||
|
})()); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [Array<Pattern>];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $or), [Array<Pattern>]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $and);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const vN: Array<Pattern> = [];
|
||||||
|
let tmp: undefined | Pattern;
|
||||||
|
while ((tmp = decodePattern(d)) !== void 0) vN.push(tmp);
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return vN;
|
||||||
|
})()); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [Array<Pattern>];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $and), [Array<Pattern>]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $rec);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = decodePattern(d); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const v1 = decodePattern(d); if (v1 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0, v1] as [Pattern, Pattern];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $rec), [Pattern, Pattern]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $tuple);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const vN: Array<NamedPattern> = [];
|
||||||
|
let tmp: undefined | NamedPattern;
|
||||||
|
while ((tmp = decodeNamedPattern(d)) !== void 0) vN.push(tmp);
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return vN;
|
||||||
|
})()); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [Array<NamedPattern>];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $tuple), [Array<NamedPattern>]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $tuple_STAR_);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const vN: Array<NamedPattern> = [];
|
||||||
|
let tmp: undefined | NamedPattern;
|
||||||
|
while ((tmp = decodeNamedPattern(d)) !== void 0) vN.push(tmp);
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return vN;
|
||||||
|
})()); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const v1 = decodeNamedPattern(d); if (v1 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0, v1] as [Array<NamedPattern>, NamedPattern];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $tuple_STAR_), [Array<NamedPattern>, NamedPattern]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $setof);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = decodePattern(d); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [Pattern];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $setof), [Pattern]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $dictof);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = decodePattern(d); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const v1 = decodePattern(d); if (v1 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0, v1] as [Pattern, Pattern];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $dictof), [Pattern, Pattern]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $dict);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openDictionary()) return void 0;
|
||||||
|
const r: _.KeyedDictionary<_val, Pattern, _ptr> = new _.KeyedDictionary();
|
||||||
|
let K: undefined | _val;
|
||||||
|
while ((K = d.next()) !== void 0) {
|
||||||
|
const V = decodePattern(d);
|
||||||
|
if (V === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
r.set(K, V);
|
||||||
|
};
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return r;
|
||||||
|
})()); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0] as [_.KeyedDictionary<_val, Pattern, _ptr>];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $dict), [_.KeyedDictionary<_val, Pattern, _ptr>]>(L, Fs);
|
||||||
|
})())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function isNamedPattern(v: any): v is NamedPattern {
|
export function isNamedPattern(v: any): v is NamedPattern {
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
|
@ -282,6 +599,28 @@ export function asNamedPattern(v: any): NamedPattern {
|
||||||
if (!isNamedPattern(v)) {throw new TypeError(`Invalid NamedPattern: ${_.stringify(v)}`);} else {return v;};
|
if (!isNamedPattern(v)) {throw new TypeError(`Invalid NamedPattern: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeNamedPattern(d: _.TypedDecoder<_ptr>): NamedPattern | undefined {
|
||||||
|
return (
|
||||||
|
((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $named);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = d.nextSymbol(); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const v1 = decodePattern(d); if (v1 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0, v1] as [symbol, Pattern];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $named), [symbol, Pattern]>(L, Fs);
|
||||||
|
})()) ??
|
||||||
|
decodePattern(d)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function isRef(v: any): v is Ref {
|
export function isRef(v: any): v is Ref {
|
||||||
return (
|
return (
|
||||||
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
|
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
|
||||||
|
@ -294,12 +633,33 @@ export function asRef(v: any): Ref {
|
||||||
if (!isRef(v)) {throw new TypeError(`Invalid Ref: ${_.stringify(v)}`);} else {return v;};
|
if (!isRef(v)) {throw new TypeError(`Invalid Ref: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeRef(d: _.TypedDecoder<_ptr>): Ref | undefined {
|
||||||
|
return ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openRecord()) return void 0;
|
||||||
|
const L = _.checkIs(d.nextSymbol(), $ref);
|
||||||
|
if (L === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const Fs = (((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const v0 = decodeModuleRef(d); if (v0 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
const v1 = d.nextSymbol(); if (v1 === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return [v0, v1] as [ModuleRef, symbol];
|
||||||
|
})())) as any;
|
||||||
|
if (Fs === void 0) { d.restoreMark(M); return void 0; };
|
||||||
|
return _.Record<(typeof $ref), [ModuleRef, symbol]>(L, Fs);
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
export function isModuleRef(v: any): v is ModuleRef {return (_.is(v, $thisModule) || isModulePath(v));}
|
export function isModuleRef(v: any): v is ModuleRef {return (_.is(v, $thisModule) || isModulePath(v));}
|
||||||
|
|
||||||
export function asModuleRef(v: any): ModuleRef {
|
export function asModuleRef(v: any): ModuleRef {
|
||||||
if (!isModuleRef(v)) {throw new TypeError(`Invalid ModuleRef: ${_.stringify(v)}`);} else {return v;};
|
if (!isModuleRef(v)) {throw new TypeError(`Invalid ModuleRef: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeModuleRef(d: _.TypedDecoder<_ptr>): ModuleRef | undefined {return (_.checkIs(d.nextSymbol(), $thisModule) ?? decodeModulePath(d));}
|
||||||
|
|
||||||
export function isModulePath(v: any): v is ModulePath {
|
export function isModulePath(v: any): v is ModulePath {
|
||||||
return (
|
return (
|
||||||
_.Array.isArray(v) &&
|
_.Array.isArray(v) &&
|
||||||
|
@ -313,3 +673,17 @@ export function asModulePath(v: any): ModulePath {
|
||||||
if (!isModulePath(v)) {throw new TypeError(`Invalid ModulePath: ${_.stringify(v)}`);} else {return v;};
|
if (!isModulePath(v)) {throw new TypeError(`Invalid ModulePath: ${_.stringify(v)}`);} else {return v;};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function decodeModulePath(d: _.TypedDecoder<_ptr>): ModulePath | undefined {
|
||||||
|
return ((() => {
|
||||||
|
const M = d.mark();
|
||||||
|
if (!d.openSequence()) return void 0;
|
||||||
|
const vN: Array<symbol> = [];
|
||||||
|
let tmp: undefined | symbol;
|
||||||
|
while ((tmp = d.nextSymbol()) !== void 0) vN.push(tmp);
|
||||||
|
if (!d.closeCompound()) { d.restoreMark(M); return void 0; };
|
||||||
|
return vN;
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const _decodePtr = () => { throw new _.DecodeError("Pointers forbidden"); };
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue