Compare commits

...

81 Commits

Author SHA1 Message Date
Tony Garnock-Jones 685302f547 Fix table 2024-05-25 17:43:20 +02:00
Tony Garnock-Jones f83e67899e Update test suites 2024-05-25 17:18:07 +02:00
Tony Garnock-Jones f2331c0e1e Add a 130-byte symbol to the tests 2024-05-25 17:13:38 +02:00
Tony Garnock-Jones f628e7d31f Correct typo 2024-05-23 14:09:30 +02:00
Tony Garnock-Jones b767fa4eb0 Floats are no more 2024-05-23 13:50:34 +02:00
Tony Garnock-Jones 58110e7c0c Publish
- @preserves/core@0.995.206
 - @preserves/schema-cli@0.995.206
 - @preserves/schema@0.995.206
2024-05-15 10:49:46 +02:00
Tony Garnock-Jones 58ebc93eb5 Repair comment reading and trailing comments in pexprs 2024-05-15 10:49:30 +02:00
Tony Garnock-Jones f18ba9c9d4 Publish
- @preserves/core@0.995.205
 - @preserves/schema-cli@0.995.205
 - @preserves/schema@0.995.205
2024-05-15 09:36:17 +02:00
Tony Garnock-Jones 87ecdb7efe Make Annotations preserveable 2024-05-15 09:34:28 +02:00
Tony Garnock-Jones 0533840bc0 Publish
- @preserves/core@0.995.204
 - @preserves/schema-cli@0.995.204
 - @preserves/schema@0.995.204
2024-05-14 17:16:37 +02:00
Tony Garnock-Jones 77c16df89b Make ReaderState.DELIMITERS accessible 2024-05-14 17:16:06 +02:00
Tony Garnock-Jones 35e6ba2e82 Publish
- @preserves/core@0.995.203
 - @preserves/schema-cli@0.995.204
 - @preserves/schema@0.995.204
2024-05-14 12:16:21 +02:00
Tony Garnock-Jones 536e32b0e8 Repair isJsDictionary 2024-05-14 12:16:02 +02:00
Tony Garnock-Jones 9192bdea7e Publish
- @preserves/core@0.995.202
 - @preserves/schema-cli@0.995.203
 - @preserves/schema@0.995.203
2024-05-09 21:58:16 +02:00
Tony Garnock-Jones 19ac8d16c8 .px for P-expressions files 2024-05-08 12:53:16 +02:00
Tony Garnock-Jones 7f284a9d52 Remove type annotation on stringifyEmbeddedWrite to permit more flexible usage 2024-05-08 11:32:09 +02:00
Tony Garnock-Jones cadf54b927 Update pexpr tests 2024-05-08 11:21:05 +02:00
Tony Garnock-Jones a8b300e57d Better treatment of embedded conversion 2024-05-08 11:20:57 +02:00
Tony Garnock-Jones 4e5e64f0a6 Actually parse groups (!) 2024-05-08 11:20:46 +02:00
Tony Garnock-Jones c8ce125192 Use python3 explicitly in .envrc 2024-05-06 14:20:25 +02:00
Tony Garnock-Jones c9fa9c590b Error handling for asPreserves 2024-05-04 22:08:52 +02:00
Tony Garnock-Jones d568fc56ce Better treatment of embedded types in asPreserves 2024-05-04 21:37:05 +02:00
Tony Garnock-Jones 42f4672446 Pexpr.asPreserves 2024-05-04 13:12:00 +02:00
Tony Garnock-Jones 1c86d8b7c5 Positioned<I>; iterator 2024-05-04 10:28:18 +02:00
Tony Garnock-Jones 64c1090938 Convert P-expressions to preserves values 2024-05-02 21:26:48 +02:00
Tony Garnock-Jones dc61963e16 Core implementation of P-expressions for TypeScript 2024-05-02 17:05:40 +02:00
Tony Garnock-Jones a33786e469 Repair error wrt interpreter lines in pexpr text 2024-05-02 16:21:47 +02:00
Tony Garnock-Jones 23ba2e5a59 Clarify the strange idea of canonical-order-but-with-annotations 2024-04-18 11:30:52 +02:00
Tony Garnock-Jones 7b8e0ff4b6 Publish
- @preserves/core@0.995.201
 - @preserves/schema-cli@0.995.202
 - @preserves/schema@0.995.202
2024-04-12 13:14:09 +02:00
Tony Garnock-Jones 3f7819fafa Allow optional handling of non-integer numbers in fromJS 2024-04-12 13:13:42 +02:00
Tony Garnock-Jones f5d76a847b Repair types of Dictionary.from and DictionaryMap.from 2024-04-12 13:13:25 +02:00
Tony Garnock-Jones 443406a7d7 Publish
- @preserves/schema-cli@0.995.201
 - @preserves/schema@0.995.201
2024-04-04 13:43:40 +02:00
Tony Garnock-Jones 05103e9825 Update JavaScript implementation for schema spec 0.4 2024-04-04 13:43:20 +02:00
Tony Garnock-Jones 4f75d6d5a3 Bring racket schema impl into line with spec 0.4 2024-04-04 13:37:12 +02:00
Tony Garnock-Jones 07b7739d00 Python docs 2024-04-04 13:29:58 +02:00
Tony Garnock-Jones 8f3d22adf1 Bump python version 2024-04-04 13:29:11 +02:00
Tony Garnock-Jones 8222675b6b Update python implementation to schema spec 0.4 2024-04-04 13:28:27 +02:00
Tony Garnock-Jones fca4b3a22e Note on extensibility and alternation 2024-04-04 13:19:46 +02:00
Tony Garnock-Jones c5dd2d749a Reflow; improve language 2024-04-04 13:14:50 +02:00
Tony Garnock-Jones c986ca76cf Specify extensibility. 2024-04-04 13:05:07 +02:00
Tony Garnock-Jones 3e67c75427 Publish
- @preserves/core@0.995.200
 - @preserves/schema-cli@0.995.200
 - @preserves/schema@0.995.200
2024-04-03 22:38:54 +02:00
Tony Garnock-Jones 6bc159e3c6 Another small refinement 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 99d1acdec7 Accept fewer `Object`s as `JsDictionary` 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 3093b89f0d Bootstrap following JsDictionary support in schema 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 00c0de40ea Support for JsDictionary in schema 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 7657952993 asJsDictionary, asKeyedDictionary 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 9ecbd0bdd1 First re-bootstrap of schema 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 297e1630a8 First (pre-bootstrap) step to getting schema working with the new core 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 85ca0b6c0a Update schema-cli 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 7c9c410a9b Add simplifiedValue() and from() methods 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones cbbc6c50c0 Great simplification by introducing DictionaryMap 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones eb4f456550 Sensible default second type argument for Dictionary.from 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 4f4ff6e108 Mark Embeddable objects specially, so that plain JS objects can be used as symbol-to-value maps. 2024-03-29 12:28:23 +01:00
Tony Garnock-Jones 055a7f90e9 Include git revision info in debian package version 2024-03-29 12:26:35 +01:00
Tony Garnock-Jones b2f6149042 Python preserves packaging 2024-03-29 12:26:35 +01:00
Tony Garnock-Jones 1bd4a3cdb4 Factor out common constructor-building logic in interpreter 2024-03-27 14:47:14 +01:00
Tony Garnock-Jones dc0ddf95dd Remove absent script from watchall 2024-03-27 14:28:57 +01:00
Tony Garnock-Jones eeace57670 Repair schema interpreter keywords and variant constructor argument handling 2024-03-27 14:28:33 +01:00
Tony Garnock-Jones 0aa39da971 New test case and fixes 2024-03-27 10:57:19 +01:00
Tony Garnock-Jones f0815ce4eb Publish
- @preserves/core@0.995.101
 - @preserves/schema-cli@0.995.100
 - @preserves/schema@0.995.100
2024-03-23 11:14:02 +01:00
Tony Garnock-Jones afba8a0bff "latin1" quasi-encoding Bytes utility 2024-03-23 11:13:08 +01:00
Tony Garnock-Jones d9ec3bfb14 Publish
- @preserves/core@0.995.100
 - @preserves/schema-cli@0.995.100
 - @preserves/schema@0.995.100
2024-03-16 17:37:17 +01:00
Tony Garnock-Jones 95ac4b13df Add Embedded<T> 2024-03-12 22:47:50 +01:00
Tony Garnock-Jones 3eeee5f090 Repair Schema tests 2024-03-12 22:47:33 +01:00
Tony Garnock-Jones aeacce22fc Update schema 2024-03-12 20:52:28 +01:00
Tony Garnock-Jones 0726684ab5 Use bare embedded values. 2024-03-12 19:14:41 +01:00
Tony Garnock-Jones f74c4ebaf0 Publish
- @preserves/schema-cli@0.995.1
 - @preserves/schema@0.995.1
2024-03-08 15:41:02 +01:00
Tony Garnock-Jones 48a063539a Repair bugs in schema interpreter relating to unit-typed definitions 2024-03-08 15:40:28 +01:00
Tony Garnock-Jones db96fcc95a Python docs 2024-03-08 10:05:00 +01:00
Tony Garnock-Jones cee4a25460 Publish
- @preserves/core@0.995.0
 - @preserves/schema-cli@0.995.0
 - @preserves/schema@0.995.0
2024-03-08 10:00:59 +01:00
Tony Garnock-Jones 7948ad4260 Javascript interpreter-specification line implementation 2024-03-08 10:00:42 +01:00
Tony Garnock-Jones 3d3c79e617 Python interpreter-specification line implementation 2024-03-08 09:49:35 +01:00
Tony Garnock-Jones b925b53756 Racket interpreter-specification line implementation 2024-03-08 09:46:06 +01:00
Tony Garnock-Jones c0289e0a05 Add sample/test for interpreter specification line 2024-03-08 09:45:43 +01:00
Tony Garnock-Jones 41189f551d Update spec date 2024-03-08 09:32:44 +01:00
Tony Garnock-Jones cc8313cf25 Bump spec version to 0.995 2024-03-08 09:31:32 +01:00
Tony Garnock-Jones bfbff65bb6 Note about multiple #! lines 2024-03-07 18:40:59 +01:00
Tony Garnock-Jones 442a987523 Spec out interpreter-lines. 2024-03-07 14:18:22 +01:00
Tony Garnock-Jones f45b136ef5 Update years 2024-03-07 14:18:12 +01:00
Tony Garnock-Jones 73c6593f84 Python 0.994 docs 2024-02-05 23:01:01 +01:00
Tony Garnock-Jones a9e226f759 Bump python version to 0.994.0 2024-02-05 22:58:16 +01:00
276 changed files with 70322 additions and 816 deletions

2
NOTICE
View File

@ -1,2 +1,2 @@
Preserves: an Expressive Data Language
Copyright 2018-2022 Tony Garnock-Jones
Copyright 2018-2024 Tony Garnock-Jones

View File

@ -88,6 +88,6 @@ Tony Garnock-Jones <tonyg@leastfixedpoint.com>
The contents of this repository are made available to you under the
[Apache License, version 2.0](LICENSE)
(<http://www.apache.org/licenses/LICENSE-2.0>), and are Copyright
2018-2022 Tony Garnock-Jones.
2018-2024 Tony Garnock-Jones.
## Notes

View File

@ -13,5 +13,5 @@ defaults:
layout: page
title: "Preserves"
version_date: "February 2024"
version: "0.994.0"
version_date: "March 2024"
version: "0.995.0"

View File

@ -1,5 +1,8 @@
[
{"version":"0.993.0","title":"0.993.0","aliases":["latest"]},
{"version":"0.995.1","title":"0.995.1","aliases":["latest"]},
{"version":"0.995.0","title":"0.995.0","aliases":[]},
{"version":"0.994.0","title":"0.994.0","aliases":[]},
{"version":"0.993.0","title":"0.993.0","aliases":[]},
{"version":"0.992.2","title":"0.992.2","aliases":[]},
{"version":"0.992.1","title":"0.992.1","aliases":[]},
{"version":"0.992.0","title":"0.992.0","aliases":[]},

View File

@ -17,5 +17,5 @@ Trailer := (ws Annotation)*
Embedded := `#:` SimpleExpr
Annotated := Annotation SimpleExpr
Annotation := `@` SimpleExpr | `#` ((space | tab) linecomment) (cr | lf)
Annotation := `@` SimpleExpr | `#` ((space | tab | `!`) linecomment) (cr | lf)
```

View File

@ -20,4 +20,4 @@ The definitions of `Atom`, `ws`, and `linecomment` are as given in the Preserves
{:.postcard-grammar.textsyntax}
| *Embedded* | := | `#:` *SimpleExpr*
| *Annotated* | := | *Annotation* *SimpleExpr*
| *Annotation* | := | `@` *SimpleExpr* &#124; `#` ((**space** &#124; **tab**) *linecomment*) (**cr** &#124; **lf**)
| *Annotation* | := | `@` *SimpleExpr* &#124; `#` ((**space** &#124; **tab** &#124; `!`) *linecomment*) (**cr** &#124; **lf**)

View File

@ -11,7 +11,7 @@ commas := (ws `,`)* ws
Embedded := `#:` Value
Annotated := Annotation Value
Annotation := `@` Value | `#` ((space | tab) linecomment) (cr | lf)
Annotation := `@` Value | `#` ((space | tab | `!`) linecomment) (cr | lf)
Atom := Boolean | ByteString | String | QuotedSymbol | Symbol | Number
Boolean := `#t` | `#f`

View File

@ -13,7 +13,7 @@
{:.postcard-grammar.textsyntax}
| *Embedded* | := | `#:`*Value* |
| *Annotated* | := | *Annotation* *Value* |
| *Annotation* | := | `@`*Value* &#124;`#` ((**space** &#124; **tab**) *linecomment*) (**cr** &#124; **lf**) |
| *Annotation* | := | `@`*Value* &#124;`#` ((**space** &#124; **tab** &#124; `!`) *linecomment*) (**cr** &#124; **lf**) |
{:.postcard-grammar.textsyntax}
| *Atom* | := | *Boolean* &#124; *ByteString* &#124; *String* &#124; *QuotedSymbol* &#124; *Symbol* &#124; *Number* |

View File

@ -99,6 +99,22 @@ the usual `@`-prefixed annotation notation can also be used.
#x"0C0D0E0F"
]
## Interpreter specification lines ("shebang" lines).
Unix systems interpret `#!` at the beginning of an executable file specially. The text
following `#!` on the first line is interpreted as a specification for an interpreter for the
executable file. Preserves offers special support for `#!`, reading it similarly to a comment,
but producing an `<interpreter ...>` annotation instead of a string.
For example,
#!/usr/bin/preserves-tool convert
[1, 2, 3]
is read as
@<interpreter "/usr/bin/preserves-tool convert"> [1, 2, 3]
## MIME-type tagged binary data.
Many internet protocols use

2
debian-packages/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build.python/
/*.deb

10
debian-packages/Makefile Normal file
View File

@ -0,0 +1,10 @@
PYTHON_PACKAGEVERSION := $(shell ../implementations/python/print-package-version)
all: python3-preserves_$(PYTHON_PACKAGEVERSION)_all.deb
python3-preserves_%_all.deb:
./build-python-deb
clean:
rm -f *.deb
rm -rf build.*

View File

@ -0,0 +1,37 @@
#!/bin/sh
set -e
PYTHON_PACKAGEVERSION=$(../implementations/python/print-package-version)
DIRTY=
if ! git diff-index --quiet HEAD --
then
DIRTY=+
fi
GITSUFFIX=$(git log --date=format:%Y%m%d%H%M%S --pretty=~git%cd.%h -1)
VERSION=${PYTHON_PACKAGEVERSION}${GITSUFFIX}${DIRTY}
echo "Building deb for ${VERSION}"
(cd ../implementations/python && . ./.envrc && make build-only)
rm -rf build.python
mkdir build.python
(
cd build.python
tar -zxvf ../../implementations/python/dist/preserves-${PYTHON_PACKAGEVERSION}.tar.gz
(
cd preserves-${PYTHON_PACKAGEVERSION}
cp -a ../../python ./debian
cat > ./debian/changelog <<EOF
preserves (${VERSION}) UNRELEASED; urgency=low
* Unofficial debian packaging of Python Preserves
-- Tony Garnock-Jones <tonyg@leastfixedpoint.com> $(date --rfc-email)
EOF
dpkg-buildpackage
)
cp *.deb ..
)

View File

@ -0,0 +1,14 @@
Source: preserves
Section: python
Priority: optional
Maintainer: Tony Garnock-Jones <tonyg@leastfixedpoint.com>
Build-Depends: debhelper-compat (= 12), python3, dh-python, pybuild-plugin-pyproject
Standards-Version: 3.9.3
Homepage: https://preserves.dev/
Vcs-Git: https://gitlab.com/preserves/preserves.git
Vcs-Browser: https://gitlab.com/preserves/preserves
Package: python3-preserves
Architecture: all
Depends: ${python3:Depends}
Description: Python implementation of the Preserves data language

6
debian-packages/python/rules Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/make -f
#export DH_VERBOSE=1
export PYBUILD_NAME=preserves
%:
dh $@ --with python3 --buildsystem=pybuild

View File

@ -1,6 +1,6 @@
{
"name": "@preserves/core",
"version": "0.994.0",
"version": "0.995.206",
"description": "Preserves data serialization format",
"homepage": "https://gitlab.com/preserves/preserves",
"license": "Apache-2.0",

View File

@ -1,6 +1,6 @@
import { Tag } from "./constants";
import { is, isAnnotated, IsPreservesAnnotated } from "./is";
import type { GenericEmbedded } from "./embedded";
import type { Embeddable, GenericEmbedded } from "./embedded";
import type { Value } from "./values";
import type { Encoder, Preservable } from "./encoder";
import type { Writer, PreserveWritable } from "./writer";
@ -52,7 +52,7 @@ export function formatPosition(p: Position | null | string): string {
}
}
export class Annotated<T = GenericEmbedded> implements Preservable<T>, PreserveWritable<T> {
export class Annotated<T extends Embeddable = GenericEmbedded> implements Preservable<T>, PreserveWritable<T> {
readonly annotations: Array<Value<T>>;
readonly pos: Position | null;
readonly item: Value<T>;
@ -67,7 +67,7 @@ export class Annotated<T = GenericEmbedded> implements Preservable<T>, PreserveW
return this;
}
static __from_preserve__<T>(v: Value<T>): undefined | Annotated<T> {
static __from_preserve__<T extends Embeddable>(v: Value<T>): undefined | Annotated<T> {
return isAnnotated<T>(v) ? v : void 0;
}
@ -109,21 +109,24 @@ export class Annotated<T = GenericEmbedded> implements Preservable<T>, PreserveW
return true;
}
static isAnnotated<T = GenericEmbedded>(x: any): x is Annotated<T> {
static isAnnotated<T extends Embeddable = GenericEmbedded>(x: any): x is Annotated<T> {
return isAnnotated(x);
}
}
export function annotate<T = GenericEmbedded>(v0: Value<T>, ...anns: Value<T>[]): Annotated<T> {
export function annotate<T extends Embeddable = GenericEmbedded>(
v0: Value<T>,
...anns: Value<T>[]
): Annotated<T> {
const v = Annotated.isAnnotated<T>(v0) ? v0 : new Annotated(v0);
anns.forEach((a) => v.annotations.push(a));
return v;
}
export function annotations<T = GenericEmbedded>(v: Value<T>): Array<Value<T>> {
export function annotations<T extends Embeddable = GenericEmbedded>(v: Value<T>): Array<Value<T>> {
return Annotated.isAnnotated<T>(v) ? v.annotations : [];
}
export function position<T = GenericEmbedded>(v: Value<T>): Position | null {
export function position<T extends Embeddable = GenericEmbedded>(v: Value<T>): Position | null {
return Annotated.isAnnotated<T>(v) ? v.pos : null;
}

View File

@ -1,5 +1,5 @@
import { Tag } from './constants';
import { GenericEmbedded } from './embedded';
import type { Embeddable, GenericEmbedded } from './embedded';
import { Encoder, Preservable } from './encoder';
import { Value } from './values';
import type { Writer, PreserveWritable } from './writer';
@ -52,6 +52,18 @@ export class Bytes implements Preservable<any>, PreserveWritable<any> {
return new Bytes(Uint8Array.of(...bytes));
}
static fromLatin1(s: string): Bytes {
// Takes codepoints in [0..255] from s, treats them as bytes.
// Codepoints outside that range trigger an exception.
const result = new Bytes(s.length); // assume all the codepoints are OK
for (let i = 0; i < s.length; i++) {
const n = s.charCodeAt(i);
if (n >= 256) throw new Error("Codepoint out of range for 'latin1' byte encoding");
result._view[i] = n;
}
return result;
}
static fromBase64(s: string): Bytes {
return new Bytes(decodeBase64(s));
}
@ -136,14 +148,18 @@ export class Bytes implements Preservable<any>, PreserveWritable<any> {
return textDecoder.decode(this._view);
}
__as_preserve__<T = GenericEmbedded>(): Value<T> {
__as_preserve__<T extends Embeddable = GenericEmbedded>(): Value<T> {
return this;
}
static __from_preserve__<T>(v: Value<T>): undefined | Bytes {
static __from_preserve__<T extends Embeddable>(v: Value<T>): undefined | Bytes {
return Bytes.isBytes(v) ? v : void 0;
}
toLatin1(): string {
return String.fromCharCode.apply(null, this._view as any as number[]);
}
toBase64(): string {
return encodeBase64(this._view);
}

View File

@ -1,12 +1,12 @@
import type { Compound, Value } from "./values";
import type { GenericEmbedded } from "./embedded";
import type { Embeddable, GenericEmbedded } from "./embedded";
import { Dictionary, Set } from "./dictionary";
export function isCompound<T = GenericEmbedded>(x: Value<T>): x is Compound<T>
export function isCompound<T extends Embeddable = GenericEmbedded>(x: Value<T>): x is Compound<T>
{
return (Array.isArray(x) || Set.isSet(x) || Dictionary.isDictionary(x));
}
export function isSequence<T = GenericEmbedded>(x: Value<T>): x is Array<Value<T>> {
export function isSequence<T extends Embeddable = GenericEmbedded>(x: Value<T>): x is Array<Value<T>> {
return (Array.isArray(x) && !('label' in x));
}

View File

@ -1,24 +1,25 @@
import { Annotated } from "./annotated";
import { DecodeError, ShortPacket } from "./codec";
import { Tag } from "./constants";
import { Set, Dictionary } from "./dictionary";
import { Set, Dictionary, DictionaryMap } from "./dictionary";
import { DoubleFloat } from "./float";
import { Record } from "./record";
import { Bytes, BytesLike, underlying, hexDigit } from "./bytes";
import { Value } from "./values";
import { is } from "./is";
import { embed, GenericEmbedded, Embedded, EmbeddedTypeDecode } from "./embedded";
import { ReaderStateOptions } from "reader";
import { GenericEmbedded, Embeddable, EmbeddedTypeDecode } from "./embedded";
import { ReaderStateOptions } from "./reader";
import { stringify } from "./text";
export interface DecoderOptions {
includeAnnotations?: boolean;
}
export interface DecoderEmbeddedOptions<T> extends DecoderOptions {
export interface DecoderEmbeddedOptions<T extends Embeddable> extends DecoderOptions {
embeddedDecode?: EmbeddedTypeDecode<T>;
}
export interface TypedDecoder<T> {
export interface TypedDecoder<T extends Embeddable> {
atEnd(): boolean;
mark(): any;
@ -26,13 +27,13 @@ export interface TypedDecoder<T> {
skip(): void;
next(): Value<T>;
withEmbeddedDecode<S, R>(
withEmbeddedDecode<S extends Embeddable, R>(
embeddedDecode: EmbeddedTypeDecode<S>,
body: (d: TypedDecoder<S>) => R): R;
nextBoolean(): boolean | undefined;
nextDouble(): DoubleFloat | undefined;
nextEmbedded(): Embedded<T> | undefined;
nextEmbedded(): T | undefined;
nextSignedInteger(): number | bigint | undefined;
nextString(): string | undefined;
nextByteString(): Bytes | undefined;
@ -46,7 +47,7 @@ export interface TypedDecoder<T> {
closeCompound(): boolean;
}
export function asLiteral<T, E extends Exclude<Value<T>, Annotated<T>>>(
export function asLiteral<T extends Embeddable, E extends Exclude<Value<T>, Annotated<T>>>(
actual: Value<T>,
expected: E): E | undefined
{
@ -165,11 +166,11 @@ export class DecoderState {
}
}
wrap<T>(v: Value<T>): Value<T> {
wrap<T extends Embeddable>(v: Value<T>): Value<T> {
return this.includeAnnotations ? new Annotated(v) : v;
}
unshiftAnnotation<T>(a: Value<T>, v: Annotated<T>): Annotated<T> {
unshiftAnnotation<T extends Embeddable>(a: Value<T>, v: Annotated<T>): Annotated<T> {
if (this.includeAnnotations) {
v.annotations.unshift(a);
}
@ -187,7 +188,7 @@ export const neverEmbeddedTypeDecode: EmbeddedTypeDecode<never> = {
},
};
export class Decoder<T = never> implements TypedDecoder<T> {
export class Decoder<T extends Embeddable = never> implements TypedDecoder<T> {
state: DecoderState;
embeddedDecode: EmbeddedTypeDecode<T>;
@ -217,13 +218,14 @@ export class Decoder<T = never> implements TypedDecoder<T> {
return result;
}
static dictionaryFromArray<T>(vs: Value<T>[]): Dictionary<T> {
const d = new Dictionary<T>();
static dictionaryFromArray<T extends Embeddable>(vs: Value<T>[]): Dictionary<T> {
const d = new DictionaryMap<T>();
if (vs.length % 2) throw new DecodeError("Missing dictionary value");
for (let i = 0; i < vs.length; i += 2) {
if (d.has(vs[i])) throw new DecodeError(`Duplicate key: ${stringify(vs[i])}`);
d.set(vs[i], vs[i+1]);
}
return d;
return d.simplifiedValue();
}
next(): Value<T> {
@ -237,7 +239,7 @@ export class Decoder<T = never> implements TypedDecoder<T> {
const v = this.next() as Annotated<T>;
return this.state.unshiftAnnotation(a, v);
}
case Tag.Embedded: return this.state.wrap<T>(embed(this.embeddedDecode.decode(this.state)));
case Tag.Embedded: return this.state.wrap<T>(this.embeddedDecode.decode(this.state));
case Tag.Ieee754:
switch (this.state.varint()) {
case 8: return this.state.wrap<T>(DoubleFloat.fromBytes(this.state.nextbytes(8)));
@ -253,7 +255,14 @@ export class Decoder<T = never> implements TypedDecoder<T> {
return this.state.wrap<T>(Record(vs[0], vs.slice(1)));
}
case Tag.Sequence: return this.state.wrap<T>(this.nextvalues());
case Tag.Set: return this.state.wrap<T>(new Set(this.nextvalues()));
case Tag.Set: {
const s = new Set<T>();
for (const v of this.nextvalues()) {
if (s.has(v)) throw new DecodeError(`Duplicate value: ${stringify(v)}`);
s.add(v);
}
return this.state.wrap<T>(s);
}
case Tag.Dictionary: return this.state.wrap<T>(Decoder.dictionaryFromArray(this.nextvalues()));
default: throw new DecodeError("Unsupported Preserves tag: " + tag);
}
@ -280,7 +289,7 @@ export class Decoder<T = never> implements TypedDecoder<T> {
this.next();
}
withEmbeddedDecode<S, R>(
withEmbeddedDecode<S extends Embeddable, R>(
embeddedDecode: EmbeddedTypeDecode<S>,
body: (d: TypedDecoder<S>) => R): R
{
@ -314,10 +323,10 @@ export class Decoder<T = never> implements TypedDecoder<T> {
});
}
nextEmbedded(): Embedded<T> | undefined {
nextEmbedded(): T | undefined {
return this.skipAnnotations((reset) => {
switch (this.state.nextbyte()) {
case Tag.Embedded: return embed(this.embeddedDecode.decode(this.state));
case Tag.Embedded: return this.embeddedDecode.decode(this.state);
default: return reset();
}
});
@ -386,11 +395,16 @@ export class Decoder<T = never> implements TypedDecoder<T> {
}
}
export function decode<T>(bs: BytesLike, options: DecoderEmbeddedOptions<T> = {}): Value<T> {
export function decode<T extends Embeddable>(
bs: BytesLike,
options: DecoderEmbeddedOptions<T> = {},
): Value<T> {
return new Decoder(bs, options).next();
}
export function decodeWithAnnotations<T>(bs: BytesLike,
options: DecoderEmbeddedOptions<T> = {}): Annotated<T> {
export function decodeWithAnnotations<T extends Embeddable>(
bs: BytesLike,
options: DecoderEmbeddedOptions<T> = {},
): Annotated<T> {
return decode(bs, { ... options, includeAnnotations: true }) as Annotated<T>;
}

View File

@ -1,19 +1,22 @@
import { Encoder, canonicalString } from "./encoder";
import { Tag } from "./constants";
import { FlexMap, FlexSet, _iterMap, IdentitySet } from "./flex";
import { FlexMap, FlexSet, _iterMap, IdentitySet, Equivalence, IsMap } from "./flex";
import { Value } from "./values";
import { Bytes } from './bytes';
import { GenericEmbedded } from "./embedded";
import { Embeddable, GenericEmbedded, isEmbedded } from "./embedded";
import type { Preservable } from "./encoder";
import type { Writer, PreserveWritable } from "./writer";
import { annotations, Annotated } from "./annotated";
import { Float } from "./float";
import { JsDictionary } from "./jsdictionary";
import { unannotate } from "./strip";
export type DictionaryType = 'Dictionary' | 'Set';
export const DictionaryType = Symbol.for('DictionaryType');
export type CompoundKey<T> = Value<T> | (Preservable<T> & PreserveWritable<T>);
export type CompoundKey<T extends Embeddable> = Value<T> | (Preservable<T> & PreserveWritable<T>);
export class EncodableDictionary<K, V, T = GenericEmbedded> extends FlexMap<K, V>
export class EncodableDictionary<T extends Embeddable, K, V> extends FlexMap<K, V>
implements Preservable<T>, PreserveWritable<T>
{
constructor(
@ -39,16 +42,16 @@ export class EncodableDictionary<K, V, T = GenericEmbedded> extends FlexMap<K, V
}
}
export class KeyedDictionary<K extends CompoundKey<T>, V, T = GenericEmbedded>
extends EncodableDictionary<K, V, T>
export class KeyedDictionary<T extends Embeddable = GenericEmbedded, K extends CompoundKey<T> = Value<T>, V = Value<T>>
extends EncodableDictionary<T, K, V>
{
get [DictionaryType](): DictionaryType {
return 'Dictionary';
}
static isKeyedDictionary<K extends CompoundKey<T>, V, T = GenericEmbedded>(
static isKeyedDictionary<T extends Embeddable = GenericEmbedded, K extends CompoundKey<T> = Value<T>, V = Value<T>>(
x: any,
): x is KeyedDictionary<K, V, T> {
): x is KeyedDictionary<T, K, V> {
return x?.[DictionaryType] === 'Dictionary';
}
@ -58,33 +61,204 @@ export class KeyedDictionary<K extends CompoundKey<T>, V, T = GenericEmbedded>
super(k => k, v => v as CompoundKey<T>, items);
}
mapEntries<W, S extends Value<R>, R = GenericEmbedded>(f: (entry: [K, V]) => [S, W]): KeyedDictionary<S, W, R> {
const result = new KeyedDictionary<S, W, R>();
for (let oldEntry of this.entries()) {
const newEntry = f(oldEntry);
result.set(newEntry[0], newEntry[1])
}
return result;
}
clone(): KeyedDictionary<K, V, T> {
clone(): KeyedDictionary<T, K, V> {
return new KeyedDictionary(this);
}
get [Symbol.toStringTag]() { return 'Dictionary'; }
equals(otherAny: any, eqv: Equivalence<V> = (v1, v2) => v1 === v2): boolean {
const otherMap = Dictionary.asMap(otherAny);
if (!otherMap) return false;
return super.equals(otherMap, eqv);
}
}
export class Dictionary<T = GenericEmbedded, V = Value<T>> extends KeyedDictionary<Value<T>, V, T> {
static isDictionary<T = GenericEmbedded, V = Value<T>>(x: any): x is Dictionary<T, V> {
return x?.[DictionaryType] === 'Dictionary';
export type Dictionary<T extends Embeddable = GenericEmbedded, V = Value<T>> =
JsDictionary<V> | KeyedDictionary<T, Value<T>, V>;
export class DictionaryMap<T extends Embeddable = GenericEmbedded, V = Value<T>> implements Map<Value<T>, V> {
get [IsMap](): boolean { return true; }
j: JsDictionary<V> | undefined;
k: KeyedDictionary<T, Value<T>, V> | undefined;
constructor(input?: Dictionary<T, V>) {
if (input === void 0) {
this.j = {};
this.k = void 0;
} else if (DictionaryType in input) {
this.j = void 0;
this.k = input;
} else {
this.j = input;
this.k = void 0;
}
}
static __from_preserve__<T>(v: Value<T>): undefined | Dictionary<T> {
static from<T extends Embeddable = GenericEmbedded, V = Value<T>>(
entries: [Value<T>, V][] | Iterable<[Value<T>, V]>,
): DictionaryMap<T, V> {
const r = new DictionaryMap<T, V>();
for (const [key, value] of entries) r.set(key, value);
return r;
}
clear(): void {
if (this.j) {
JsDictionary.clear(this.j);
} else {
this.k!.clear();
}
}
delete(key: Value<T>): boolean {
if (this.j) {
key = unannotate(key);
if (typeof key !== 'symbol') return false;
return JsDictionary.remove(this.j, key);
} else {
return this.k!.delete(key);
}
}
forEach(callbackfn: (value: V, key: Value<T>, map: Map<Value<T>, V>) => void, thisArg?: any): void {
if (this.j) {
JsDictionary.forEach(this.j, (v, k) => callbackfn.call(thisArg, v, k, this));
} else {
this.k!.forEach(callbackfn, thisArg);
}
}
get(key: Value<T>): V | undefined {
if (this.j) {
key = unannotate(key);
if (typeof key !== 'symbol') return void 0;
return JsDictionary.get(this.j, key);
} else {
return this.k!.get(key);
}
}
has(key: Value<T>): boolean {
if (this.j) {
key = unannotate(key);
if (typeof key !== 'symbol') return false;
return JsDictionary.has(this.j, key);
} else {
return this.k!.has(key);
}
}
set(key: Value<T>, value: V): this {
if (this.j) {
if (typeof key === 'symbol') {
JsDictionary.set(this.j, key, value);
return this;
}
this.k = new KeyedDictionary<T, Value<T>, V>(JsDictionary.entries(this.j));
this.j = void 0;
}
this.k!.set(key, value);
return this;
}
get size(): number {
return this.j ? JsDictionary.size(this.j) : this.k!.size;
}
entries(): IterableIterator<[Value<T>, V]> {
return this.j ? JsDictionary.entries(this.j) : this.k!.entries();
}
keys(): IterableIterator<Value<T>> {
return this.j ? JsDictionary.keys(this.j) : this.k!.keys();
}
values(): IterableIterator<V> {
return this.j ? JsDictionary.values(this.j) : this.k!.values();
}
[Symbol.iterator](): IterableIterator<[Value<T>, V]> {
return this.entries();
}
get [Symbol.toStringTag](): string {
return 'DictionaryMap';
}
clone(): DictionaryMap<T, V> {
return new DictionaryMap<T, V>(this.j ? JsDictionary.clone(this.j) : this.k!.clone());
}
get value(): Dictionary<T, V> {
return this.j ?? this.k!;
}
simplify(): void {
if (!this.j) {
const r: JsDictionary<V> = {};
for (const [key, value] of this.k!.entries()) {
if (typeof key !== 'symbol') return;
r[key.description!] = value;
}
this.j = r;
this.k = void 0;
}
}
simplifiedValue(): Dictionary<T, V> {
this.simplify();
return this.value;
}
asJsDictionary(): JsDictionary<V> {
this.simplify();
if (!this.j) throw new Error("Cannot represent general dictionary as JsDictionary");
return this.j;
}
asKeyedDictionary(): KeyedDictionary<T, Value<T>, V> {
return this.k ?? new KeyedDictionary<T, Value<T>, V>(JsDictionary.entries(this.j!));
}
}
export namespace Dictionary {
export function isDictionary<T extends Embeddable = GenericEmbedded, V = Value<T>>(
x: any
): x is Dictionary<T, V> {
if (typeof x !== 'object' || x === null) return false;
switch (x[DictionaryType]) {
case 'Dictionary': return true;
case void 0: return JsDictionary.isJsDictionary(x);
default: return false;
}
}
export function asMap<T extends Embeddable = GenericEmbedded, V = Value<T>>(
x: Dictionary<T, V>
): DictionaryMap<T, V>;
export function asMap<T extends Embeddable = GenericEmbedded, V = Value<T>>(
x: any
): DictionaryMap<T, V> | undefined;
export function asMap<T extends Embeddable = GenericEmbedded, V = Value<T>>(
x: any
): DictionaryMap<T, V> | undefined {
return isDictionary<T, V>(x) ? new DictionaryMap(x) : void 0;
}
export function from<T extends Embeddable = GenericEmbedded, V = Value<T>>(
entries: [Value<T>, V][] | Iterable<[Value<T>, V]>,
): Dictionary<T, V> {
return DictionaryMap.from(entries).simplifiedValue();
}
export function __from_preserve__<T extends Embeddable>(v: Value<T>): undefined | Dictionary<T> {
return Dictionary.isDictionary<T>(v) ? v : void 0;
}
}
export function encodeDictionaryOn<K, V, T>(
export function encodeDictionaryOn<T extends Embeddable, K, V>(
dict: Map<K, V>,
encoder: Encoder<T>,
encodeK: (k: K, encoder: Encoder<T>) => void,
@ -114,7 +288,7 @@ export function encodeDictionaryOn<K, V, T>(
}
}
export function writeDictionaryOn<K, V, T>(
export function writeDictionaryOn<T extends Embeddable, K, V>(
dict: Map<K, V>,
w: Writer<T>,
writeK: (k: K, w: Writer<T>) => void,
@ -135,7 +309,7 @@ export function writeDictionaryOn<K, V, T>(
});
}
export class EncodableSet<V, T = GenericEmbedded> extends FlexSet<V>
export class EncodableSet<T extends Embeddable, V> extends FlexSet<V>
implements Preservable<T>, PreserveWritable<T>
{
constructor(
@ -154,16 +328,16 @@ export class EncodableSet<V, T = GenericEmbedded> extends FlexSet<V>
}
}
export class KeyedSet<K extends CompoundKey<T>, T = GenericEmbedded>
extends EncodableSet<K, T>
export class KeyedSet<T extends Embeddable = GenericEmbedded, K extends CompoundKey<T> = Value<T>>
extends EncodableSet<T, K>
{
get [DictionaryType](): DictionaryType {
return 'Set';
}
static isKeyedSet<K extends CompoundKey<T>, T = GenericEmbedded>(
static isKeyedSet<T extends Embeddable = GenericEmbedded, K extends CompoundKey<T> = Value<T>>(
x: any,
): x is KeyedSet<K, T> {
): x is KeyedSet<T, K> {
return x?.[DictionaryType] === 'Set';
}
@ -171,34 +345,36 @@ export class KeyedSet<K extends CompoundKey<T>, T = GenericEmbedded>
super(k => k, items);
}
map<S extends Value<R>, R = GenericEmbedded>(f: (value: K) => S): KeyedSet<S, R> {
map<R extends Embeddable = GenericEmbedded, S extends Value<R> = Value<R>>(
f: (value: K) => S,
): KeyedSet<R, S> {
return new KeyedSet(_iterMap(this[Symbol.iterator](), f));
}
filter(f: (value: K) => boolean): KeyedSet<K, T> {
const result = new KeyedSet<K, T>();
filter(f: (value: K) => boolean): KeyedSet<T, K> {
const result = new KeyedSet<T, K>();
for (let k of this) if (f(k)) result.add(k);
return result;
}
clone(): KeyedSet<K, T> {
clone(): KeyedSet<T, K> {
return new KeyedSet(this);
}
get [Symbol.toStringTag]() { return 'Set'; }
}
export class Set<T = GenericEmbedded> extends KeyedSet<Value<T>, T> {
static isSet<T = GenericEmbedded>(x: any): x is Set<T> {
export class Set<T extends Embeddable = GenericEmbedded> extends KeyedSet<T> {
static isSet<T extends Embeddable = GenericEmbedded>(x: any): x is Set<T> {
return x?.[DictionaryType] === 'Set';
}
static __from_preserve__<T>(v: Value<T>): undefined | Set<T> {
static __from_preserve__<T extends Embeddable>(v: Value<T>): undefined | Set<T> {
return Set.isSet<T>(v) ? v : void 0;
}
}
export function encodeSetOn<V, T>(
export function encodeSetOn<T extends Embeddable, V>(
s: IdentitySet<V>,
encoder: Encoder<T>,
encodeV: (v: V, encoder: Encoder<T>) => void,
@ -219,7 +395,7 @@ export function encodeSetOn<V, T>(
}
}
export function writeSetOn<V, T>(
export function writeSetOn<T extends Embeddable, V>(
s: IdentitySet<V>,
w: Writer<T>,
writeV: (v: V, w: Writer<T>) => void,

View File

@ -3,7 +3,17 @@ import type { EncoderState } from "./encoder";
import type { Value } from "./values";
import { ReaderStateOptions } from "./reader";
export type EmbeddedTypeEncode<T> = {
export const IsEmbedded = Symbol.for('IsEmbedded');
export interface Embeddable {
readonly [IsEmbedded]: true;
}
export function isEmbedded<T extends Embeddable>(v: any): v is T {
return !!v?.[IsEmbedded];
}
export type EmbeddedTypeEncode<T extends Embeddable> = {
encode(s: EncoderState, v: T): void;
}
@ -12,46 +22,22 @@ export type EmbeddedTypeDecode<T> = {
fromValue(v: Value<GenericEmbedded>, options: ReaderStateOptions): T;
}
export type EmbeddedType<T> = EmbeddedTypeEncode<T> & EmbeddedTypeDecode<T>;
export type EmbeddedType<T extends Embeddable> = EmbeddedTypeEncode<T> & EmbeddedTypeDecode<T>;
export class Embedded<T> {
embeddedValue: T;
get [IsEmbedded](): true { return true; }
constructor(embeddedValue: T) {
this.embeddedValue = embeddedValue;
constructor(public readonly value: T) {}
equals(other: any): boolean {
return typeof other === 'object' && 'value' in other && Object.is(this.value, other.value);
}
equals(other: any, is: (a: any, b: any) => boolean) {
return isEmbedded<T>(other) && is(this.embeddedValue, other.embeddedValue);
}
toString(): string {
return '#:' + (this.embeddedValue as any).toString();
}
__as_preserve__<R>(): T extends R ? Value<R> : never {
return this as any;
}
static __from_preserve__<T>(v: Value<T>): undefined | Embedded<T> {
return isEmbedded<T>(v) ? v : void 0;
}
}
export function embed<T>(embeddedValue: T): Embedded<T> {
return new Embedded(embeddedValue);
}
export function isEmbedded<T>(v: Value<T>): v is Embedded<T> {
return typeof v === 'object' && 'embeddedValue' in v;
}
export class GenericEmbedded {
generic: Value;
get [IsEmbedded](): true { return true; }
constructor(generic: Value) {
this.generic = generic;
}
constructor(public readonly generic: Value) {}
equals(other: any, is: (a: any, b: any) => boolean) {
return typeof other === 'object' && 'generic' in other && is(this.generic, other.generic);

View File

@ -3,17 +3,18 @@ import { Bytes, unhexDigit } from "./bytes";
import { Value } from "./values";
import { EncodeError } from "./codec";
import { Record, Tuple } from "./record";
import { EmbeddedTypeEncode } from "./embedded";
import type { Embedded } from "./embedded";
import { EmbeddedTypeEncode, isEmbedded } from "./embedded";
import type { Embeddable } from "./embedded";
import { DictionaryMap, encodeDictionaryOn } from "./dictionary";
export type Encodable<T> =
export type Encodable<T extends Embeddable> =
Value<T> | Preservable<T> | Iterable<Value<T>> | ArrayBufferView;
export interface Preservable<T> {
export interface Preservable<T extends Embeddable> {
__preserve_on__(encoder: Encoder<T>): void;
}
export function isPreservable<T>(v: any): v is Preservable<T> {
export function isPreservable<T extends Embeddable>(v: any): v is Preservable<T> {
return typeof v === 'object' && v !== null && '__preserve_on__' in v && typeof v.__preserve_on__ === 'function';
}
@ -22,11 +23,11 @@ export interface EncoderOptions {
includeAnnotations?: boolean;
}
export interface EncoderEmbeddedOptions<T> extends EncoderOptions {
export interface EncoderEmbeddedOptions<T extends Embeddable> extends EncoderOptions {
embeddedEncode?: EmbeddedTypeEncode<T>;
}
export function asLatin1(bs: Uint8Array): string {
function asLatin1(bs: Uint8Array): string {
return String.fromCharCode.apply(null, bs as any as number[]);
}
@ -199,7 +200,7 @@ export class EncoderState {
}
}
export class Encoder<T = object> {
export class Encoder<T extends Embeddable> {
state: EncoderState;
embeddedEncode: EmbeddedTypeEncode<T>;
@ -218,7 +219,7 @@ export class Encoder<T = object> {
}
}
withEmbeddedEncode<S>(
withEmbeddedEncode<S extends Embeddable>(
embeddedEncode: EmbeddedTypeEncode<S>,
body: (e: Encoder<S>) => void): this
{
@ -249,7 +250,7 @@ export class Encoder<T = object> {
}
push(v: Encodable<T>) {
if (isPreservable<unknown>(v)) {
if (isPreservable<any>(v)) {
v.__preserve_on__(this);
}
else if (isPreservable<T>(v)) {
@ -288,17 +289,21 @@ export class Encoder<T = object> {
for (let i of v) this.push(i);
});
}
else if (isEmbedded<T>(v)) {
this.state.emitbyte(Tag.Embedded);
this.embeddedEncode.encode(this.state, v);
}
else {
((v: Embedded<T>) => {
this.state.emitbyte(Tag.Embedded);
this.embeddedEncode.encode(this.state, v.embeddedValue);
})(v);
encodeDictionaryOn(new DictionaryMap<T>(v),
this,
(k, e) => e.push(k),
(v, e) => e.push(v));
}
return this; // for chaining
}
}
export function encode<T>(
export function encode<T extends Embeddable>(
v: Encodable<T>,
options: EncoderEmbeddedOptions<T> = {}): Bytes
{
@ -332,7 +337,9 @@ export function canonicalString(v: Encodable<any>): string {
}
}
export function encodeWithAnnotations<T>(v: Encodable<T>,
options: EncoderEmbeddedOptions<T> = {}): Bytes {
export function encodeWithAnnotations<T extends Embeddable>(
v: Encodable<T>,
options: EncoderEmbeddedOptions<T> = {},
): Bytes {
return encode(v, { ... options, includeAnnotations: true });
}

View File

@ -1,6 +1,6 @@
import { Tag } from "./constants";
import { Value } from "./values";
import type { GenericEmbedded } from "./embedded";
import type { Embeddable, GenericEmbedded } from "./embedded";
import type { Encoder, Preservable } from "./encoder";
import type { Writer, PreserveWritable } from "./writer";
import { Bytes, dataview } from "./bytes";
@ -93,7 +93,7 @@ export function floatlikeString(f: number): string {
// }
export class DoubleFloat extends Float implements Preservable<any>, PreserveWritable<any> {
__as_preserve__<T = GenericEmbedded>(): Value<T> {
__as_preserve__<T extends Embeddable = GenericEmbedded>(): Value<T> {
return this;
}
@ -101,7 +101,7 @@ export class DoubleFloat extends Float implements Preservable<any>, PreserveWrit
return new DoubleFloat(dataview(bs).getFloat64(0, false));
}
static __from_preserve__<T>(v: Value<T>): undefined | DoubleFloat {
static __from_preserve__<T extends Embeddable>(v: Value<T>): undefined | DoubleFloat {
return Float.isDouble(v) ? v : void 0;
}

View File

@ -1,10 +1,10 @@
import { Record, Tuple } from "./record";
import { Bytes } from "./bytes";
import { Value } from "./values";
import { Set, Dictionary } from "./dictionary";
import { Set, KeyedDictionary, Dictionary, DictionaryMap } from "./dictionary";
import { annotate, Annotated } from "./annotated";
import { Double, Float } from "./float";
import { Embedded } from "./embedded";
import { Embeddable, isEmbedded } from "./embedded";
export enum ValueClass {
Boolean,
@ -21,9 +21,9 @@ export enum ValueClass {
Annotated, // quasi-class
}
export type Fold<T, R = Value<T>> = (v: Value<T>) => R;
export type Fold<T extends Embeddable, R = Value<T>> = (v: Value<T>) => R;
export interface FoldMethods<T, R> {
export interface FoldMethods<T extends Embeddable, R> {
boolean(b: boolean): R;
double(f: number): R;
integer(i: number | bigint): R;
@ -34,39 +34,39 @@ export interface FoldMethods<T, R> {
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, R>): R;
array(a: Array<Value<T>>, k: Fold<T, R>): R;
set(s: Set<T>, k: Fold<T, R>): R;
dictionary(d: Dictionary<T>, k: Fold<T, R>): R;
dictionary(d: DictionaryMap<T>, k: Fold<T, R>): R;
annotated(a: Annotated<T>, k: Fold<T, R>): R;
embedded(t: Embedded<T>, k: Fold<T, R>): R;
embedded(t: T, k: Fold<T, R>): R;
}
export class VoidFold<T> implements FoldMethods<T, void> {
boolean(b: boolean): void {}
double(f: number): void {}
integer(i: number | bigint): void {}
string(s: string): void {}
bytes(b: Bytes): void {}
symbol(s: symbol): void {}
export class VoidFold<T extends Embeddable> implements FoldMethods<T, void> {
boolean(_b: boolean): void {}
double(_f: number): void {}
integer(_i: number | bigint): void {}
string(_s: string): void {}
bytes(_b: Bytes): void {}
symbol(_s: symbol): void {}
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, void>): void {
k(r.label);
r.forEach(k);
}
array(a: Value<T>[], k: Fold<T, void>): void { a.forEach(k); }
set(s: Set<T>, k: Fold<T, void>): void { s.forEach(k); }
dictionary(d: Dictionary<T>, k: Fold<T, void>): void {
dictionary(d: DictionaryMap<T>, k: Fold<T, void>): void {
d.forEach((value, key) => { k(key); k(value); });
}
annotated(a: Annotated<T>, k: Fold<T, void>): void { k(a.item); a.annotations.forEach(k); }
embedded(_t: Embedded<T>, _k: Fold<T, void>): void {}
embedded(_t: T, _k: Fold<T, void>): void {}
}
export class ForEachEmbedded<T> extends VoidFold<T> {
export class ForEachEmbedded<T extends Embeddable> extends VoidFold<T> {
constructor(public readonly f: (t: T, k: Fold<T, void>) => void) { super(); }
embedded(t: Embedded<T>, k: Fold<T, void>): void { this.f(t.embeddedValue, k); }
embedded(t: T, k: Fold<T, void>): void { this.f(t, k); }
}
export abstract class ValueFold<T, R = T> implements FoldMethods<T, Value<R>> {
export abstract class ValueFold<T extends Embeddable, R extends Embeddable = T> implements FoldMethods<T, Value<R>> {
boolean(b: boolean): Value<R> {
return b;
}
@ -94,22 +94,24 @@ export abstract class ValueFold<T, R = T> implements FoldMethods<T, Value<R>> {
set(s: Set<T>, k: Fold<T, Value<R>>): Value<R> {
return s.map(k);
}
dictionary(d: Dictionary<T>, k: Fold<T, Value<R>>): Value<R> {
return d.mapEntries(([key, value]) => [k(key), k(value)]);
dictionary(d: DictionaryMap<T>, k: Fold<T, Value<R>>): Value<R> {
const result = new DictionaryMap<R>();
d.forEach((value, key) => result.set(k(key), k(value)));
return result.simplifiedValue();
}
annotated(a: Annotated<T>, k: Fold<T, Value<R>>): Value<R> {
return annotate(k(a.item), ...a.annotations.map(k));
}
abstract embedded(t: Embedded<T>, k: Fold<T, Value<R>>): Value<R>;
abstract embedded(t: T, k: Fold<T, Value<R>>): Value<R>;
}
export class IdentityFold<T> extends ValueFold<T, T> {
embedded(t: Embedded<T>, _k: Fold<T, Value<T>>): Value<T> {
export class IdentityFold<T extends Embeddable> extends ValueFold<T, T> {
embedded(t: T, _k: Fold<T, Value<T>>): Value<T> {
return t;
}
}
export class MapFold<T, R> extends ValueFold<T, R> {
export class MapFold<T extends Embeddable, R extends Embeddable> extends ValueFold<T, R> {
readonly f: (t: T) => Value<R>;
constructor(f: (t: T) => Value<R>) {
@ -117,12 +119,12 @@ export class MapFold<T, R> extends ValueFold<T, R> {
this.f = f;
}
embedded(t: Embedded<T>, _k: Fold<T, Value<R>>): Value<R> {
return this.f(t.embeddedValue);
embedded(t: T, _k: Fold<T, Value<R>>): Value<R> {
return this.f(t);
}
}
export function valueClass<T>(v: Value<T>): ValueClass {
export function valueClass<T extends Embeddable>(v: Value<T>): ValueClass {
switch (typeof v) {
case 'boolean':
return ValueClass.Boolean;
@ -154,7 +156,7 @@ export function valueClass<T>(v: Value<T>): ValueClass {
} else if (Float.isDouble(v)) {
return ValueClass.Double;
} else {
return ValueClass.Embedded;
return ((_v: T) => ValueClass.Embedded)(v);
}
default:
((_v: never): never => { throw new Error("Internal error"); })(v);
@ -163,7 +165,7 @@ export function valueClass<T>(v: Value<T>): ValueClass {
export const IDENTITY_FOLD = new IdentityFold<any>();
export function fold<T, R>(v: Value<T>, o: FoldMethods<T, R>): R {
export function fold<T extends Embeddable, R>(v: Value<T>, o: FoldMethods<T, R>): R {
const walk = (v: Value<T>): R => {
switch (typeof v) {
case 'boolean':
@ -188,16 +190,16 @@ export function fold<T, R>(v: Value<T>, o: FoldMethods<T, R>): R {
return o.array(v, walk);
} else if (Set.isSet<T>(v)) {
return o.set(v, walk);
} else if (Dictionary.isDictionary<T>(v)) {
return o.dictionary(v, walk);
} else if (isEmbedded(v)) {
return o.embedded(v, walk);
} else if (Annotated.isAnnotated<T>(v)) {
return o.annotated(v, walk);
} else if (Bytes.isBytes(v)) {
return o.bytes(v);
} else if (Float.isDouble(v)) {
return o.double(v.value);
} else {
return o.embedded(v, walk);
} else if (Dictionary.isDictionary<T>(v)) {
return o.dictionary(new DictionaryMap(v), walk);
}
default:
((_v: never): never => { throw new Error("Internal error"); })(v);
@ -206,7 +208,7 @@ export function fold<T, R>(v: Value<T>, o: FoldMethods<T, R>): R {
return walk(v);
}
export function mapEmbeddeds<T, R>(
export function mapEmbeddeds<T extends Embeddable, R extends Embeddable>(
v: Value<T>,
f: (t: T) => Value<R>,
): Value<R>
@ -214,6 +216,9 @@ export function mapEmbeddeds<T, R>(
return fold(v, new MapFold(f));
}
export function forEachEmbedded<T>(v: Value<T>, f: (t: T, k: Fold<T, void>) => void): void {
export function forEachEmbedded<T extends Embeddable>(
v: Value<T>,
f: (t: T, k: Fold<T, void>) => void,
): void {
return fold(v, new ForEachEmbedded(f));
}

View File

@ -1,72 +1,97 @@
import { embed, GenericEmbedded } from "./embedded";
import { Embeddable, GenericEmbedded, isEmbedded } from "./embedded";
import { Bytes } from "./bytes";
import { Record, Tuple } from "./record";
import { Value } from "./values";
import { Dictionary, Set } from "./dictionary";
import { Dictionary, KeyedDictionary, Set } from "./dictionary";
import { JsDictionary } from "./jsdictionary";
export function fromJS<T = GenericEmbedded>(x: any): Value<T> {
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 Double");
}
export interface FromJSOptions<T extends Embeddable = GenericEmbedded> {
onNonInteger?(n: number): Value<T> | undefined;
}
export function fromJS<T extends Embeddable = GenericEmbedded>(x: any): Value<T> {
return fromJS_options(x);
}
export function fromJS_options<T extends Embeddable = GenericEmbedded>(x: any, options?: FromJSOptions<T>): Value<T> {
function walk(x: any): Value<T> {
switch (typeof x) {
case 'number':
if (!Number.isInteger(x)) {
// We require that clients be explicit about integer vs. non-integer types.
const converted = options?.onNonInteger?.(x) ?? void 0;
if (converted !== void 0) return converted;
throw new TypeError("Refusing to autoconvert non-integer number to Double");
}
// FALL THROUGH
case 'bigint':
case 'string':
case 'symbol':
case 'boolean':
return x;
case 'undefined':
case 'function':
break;
case 'object':
if (x === null) {
break;
}
if (typeof x.__as_preserve__ === 'function') {
return x.__as_preserve__();
}
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(x)) {
case 'bigint':
case 'string':
case 'symbol':
case 'boolean':
return x;
}
if (Array.isArray(x)) {
return x.map<Value<T>>(fromJS);
}
if (ArrayBuffer.isView(x) || x instanceof ArrayBuffer) {
return Bytes.from(x);
}
if (Map.isMap(x)) {
const d = new Dictionary<T>();
x.forEach((v, k) => d.set(fromJS(k), fromJS(v)));
return d;
}
if (Set.isSet(x)) {
const s = new Set<T>();
x.forEach(v => s.add(fromJS(v)));
return s;
}
// Just... assume it's a T.
return embed(x as T);
default:
break;
case 'undefined':
case 'function':
break;
case 'object':
if (x === null) {
break;
}
if (typeof x.__as_preserve__ === 'function') {
return x.__as_preserve__();
}
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(x)) {
return x;
}
if (Array.isArray(x)) {
return x.map<Value<T>>(walk);
}
if (ArrayBuffer.isView(x) || x instanceof ArrayBuffer) {
return Bytes.from(x);
}
if (Map.isMap(x)) {
const d = new KeyedDictionary<T>();
x.forEach((v, k) => d.set(walk(k), walk(v)));
return d;
}
if (Set.isSet(x)) {
const s = new Set<T>();
x.forEach(v => s.add(walk(v)));
return s;
}
if (isEmbedded<T>(x)) {
return x;
}
// Handle plain JS objects to build a JsDictionary
{
const r: JsDictionary<Value<T>> = {};
Object.entries(x).forEach(([k, v]) => r[k] = walk(v));
return r;
}
default:
break;
}
throw new TypeError("Cannot represent JavaScript value as Preserves: " + x);
}
throw new TypeError("Cannot represent JavaScript value as Preserves: " + x);
return walk(x);
}
declare module "./dictionary" {
namespace Dictionary {
export function fromJS<T = GenericEmbedded, V = GenericEmbedded>(x: object): Dictionary<T, Value<V>>;
export function stringMap<T extends Embeddable = GenericEmbedded>(
x: object
): KeyedDictionary<T, string, Value<T>>;
}
}
Dictionary.fromJS = function <T = GenericEmbedded, V = GenericEmbedded>(x: object): Dictionary<T, Value<V>> {
if (Dictionary.isDictionary<T, Value<V>>(x)) return x;
const d = new Dictionary<T, Value<V>>();
Object.entries(x).forEach(([key, value]) => d.set(key, fromJS(value)));
return d;
Dictionary.stringMap = function <T extends Embeddable = GenericEmbedded>(
x: object
): KeyedDictionary<T, string, Value<T>> {
const r = new KeyedDictionary<T, string, Value<T>>();
Object.entries(x).forEach(([key, value]) => r.set(key, fromJS(value)));
return r;
};

View File

@ -1,15 +1,17 @@
export * from './runtime';
export * as Constants from './constants';
export * as Pexpr from './pexpr';
import type { Embeddable } from './embedded';
import type { Value } from './values';
declare global {
interface ArrayConstructor {
__from_preserve__<T>(v: Value<T>): undefined | Array<Value<T>>;
__from_preserve__<T extends Embeddable>(v: Value<T>): undefined | Array<Value<T>>;
}
}
Array.__from_preserve__ = <T>(v: Value<T>) => {
Array.__from_preserve__ = <T extends Embeddable>(v: Value<T>) => {
return Array.isArray(v) ? v : void 0;
};

View File

@ -1,9 +1,10 @@
import type { GenericEmbedded } from "./embedded";
import type { Embeddable, GenericEmbedded } from "./embedded";
import type { Annotated } from "./annotated";
import { Dictionary } from "./dictionary";
export const IsPreservesAnnotated = Symbol.for('IsPreservesAnnotated');
export function isAnnotated<T = GenericEmbedded>(x: any): x is Annotated<T>
export function isAnnotated<T extends Embeddable = GenericEmbedded>(x: any): x is Annotated<T>
{
return !!x?.[IsPreservesAnnotated];
}
@ -30,6 +31,17 @@ export function is(a: any, b: any): boolean {
for (let i = 0; i < a.length; i++) if (!is(a[i], b[i])) return false;
return true;
}
{
const aMap = Dictionary.asMap(a);
const bMap = Dictionary.asMap(b);
if (!aMap || !bMap) return false;
if (aMap.size !== bMap.size) return false;
for (const k of aMap.keys()) {
if (!bMap.has(k)) return false;
if (!is(aMap.get(k), bMap.get(k))) return false;
}
return true;
}
}
return false;
}

View File

@ -0,0 +1,89 @@
import { isEmbedded } from './embedded';
import { Equivalence, _iterMap } from './flex';
export interface JsDictionary<V> {
[key: string]: V;
}
export namespace JsDictionary {
export function isJsDictionary<V>(x: any): x is JsDictionary<V> {
// We accept only literal objects and objects created via `new Object` as dictionaries.
// Furthermore, we require no function-valued `__as_preserve__` property to exist.
return typeof x === 'object'
&& x !== null
&& Object.getPrototypeOf(Object.getPrototypeOf(x)) === null
&& typeof x.__as_preserve__ !== 'function'
&& !isEmbedded(x);
}
export function from<V>(entries: Iterable<[symbol, V]>): JsDictionary<V> {
const r: JsDictionary<V> = {};
for (const [key, value] of entries) r[key.description!] = value;
return r;
}
export function clear<V>(j: JsDictionary<V>): void {
for (const key in j) delete j[key];
}
export function remove<V>(j: JsDictionary<V>, key: symbol): boolean {
const result = has(j, key);
delete j[key.description!];
return result;
}
export function forEach<V>(
j: JsDictionary<V>,
callbackfn: (value: V, key: symbol) => void,
): void {
Object.entries(j).forEach(([key, val]) => callbackfn(val, Symbol.for(key)));
}
export function get<V>(j: JsDictionary<V>, key: symbol): V | undefined {
return j[key.description!];
}
export function has<V>(j: JsDictionary<V>, key: symbol): boolean {
return Object.hasOwnProperty.call(j, key.description!);
}
export function set<V>(j: JsDictionary<V>, key: symbol, value: V): JsDictionary<V> {
j[key.description!] = value;
return j;
}
export function size<V>(j: JsDictionary<V>): number {
return Object.keys(j).length;
}
export function entries<V>(j: JsDictionary<V>): IterableIterator<[symbol, V]> {
return _iterMap(Object.entries(j).values(), ([k, v]) => [Symbol.for(k), v]);
}
export function keys<V>(j: JsDictionary<V>): IterableIterator<symbol> {
return _iterMap(Object.keys(j).values(), k => Symbol.for(k));
}
export function values<V>(j: JsDictionary<V>): IterableIterator<V> {
return Object.values(j).values();
}
export function clone<V>(j: JsDictionary<V>): JsDictionary<V> {
const r: JsDictionary<V> = {};
Object.keys(j).forEach(k => r[k] = j[k]);
return r;
}
export function equals<V>(
j1: JsDictionary<V>,
j2: JsDictionary<V>,
eqv: Equivalence<V> = (v1, v2) => v1 === v2,
): boolean {
if (size(j1) !== size(j2)) return false;
for (let [k, v] of entries(j1)) {
if (!has(j2, k)) return false;
if (!eqv(v, get(j2, k)!)) return false;
}
return true;
}
}

View File

@ -3,13 +3,15 @@ import { Bytes } from "./bytes";
import { fold } from "./fold";
import { is } from "./is";
import { Value } from "./values";
import { Set, Dictionary } from "./dictionary";
import { Set, Dictionary, KeyedDictionary, DictionaryMap } from "./dictionary";
import { Annotated } from "./annotated";
import { unannotate } from "./strip";
import { embed, isEmbedded, Embedded } from "./embedded";
import { isEmbedded } from "./embedded";
import { isCompound } from "./compound";
import type { Embeddable } from "./embedded";
import { JsDictionary } from "./jsdictionary";
export function merge<T>(
export function merge<T extends Embeddable>(
mergeEmbeddeds: (a: T, b: T) => T | undefined,
item0: Value<T>,
... items: Array<Value<T>>): Value<T>
@ -42,33 +44,38 @@ export function merge<T>(
if (!Record.isRecord<Value<T>, Tuple<Value<T>>, T>(b)) die();
return Record(walk(r.label, b.label), walkMany(r, b));
},
array(a: Array<Value<T>>) {
if (!Array.isArray(b) || Record.isRecord(b)) die();
return walkMany(a, b);
},
set(_s: Set<T>) { die(); },
dictionary(d: Dictionary<T>) {
if (!Dictionary.isDictionary<T>(b)) die();
const r = new Dictionary<T>();
d.forEach((av,ak) => {
const bv = b.get(ak);
dictionary(aMap: DictionaryMap<T>) {
const bMap = Dictionary.asMap<T>(b);
if (bMap === void 0) die();
const r = new DictionaryMap<T>();
aMap.forEach((av,ak) => {
const bv = bMap.get(ak);
r.set(ak, bv === void 0 ? av : walk(av, bv));
});
b.forEach((bv, bk) => {
if (!d.has(bk)) r.set(bk, bv);
bMap.forEach((bv, bk) => {
if (!aMap.has(bk)) r.set(bk, bv);
});
return r;
return r.simplifiedValue();
},
annotated(a: Annotated<T>) {
return walk(a, unannotate(b));
},
embedded(t: Embedded<T>) {
embedded(t: T) {
if (!isEmbedded<T>(b)) die();
const r = mergeEmbeddeds(t.embeddedValue, b.embeddedValue);
const r = mergeEmbeddeds(t, b);
if (r === void 0) die();
return embed(r);
return r;
},
});
}

View File

@ -2,12 +2,12 @@
import { Annotated } from './annotated';
import { Bytes } from './bytes';
import { Set, Dictionary } from './dictionary';
import { Set, EncodableDictionary } from './dictionary';
import { stringify } from './text';
import * as util from 'util';
[Bytes, Annotated, Set, Dictionary].forEach((C) => {
[Bytes, Annotated, Set, EncodableDictionary].forEach((C) => {
(C as any).prototype[util.inspect.custom] =
function (_depth: any, _options: any) {
return stringify(this, { indent: 2 });

View File

@ -1,13 +1,14 @@
import { is, isAnnotated } from './is';
import { Bytes } from './bytes';
import { Set, Dictionary } from './dictionary';
import { Embedded, isEmbedded } from './embedded';
import { isEmbedded } from './embedded';
import { Float } from './float';
import { Value } from './values';
import { Record } from './record';
import type { Embeddable } from './embedded';
export function typeCode<V>(v: Value<V>): number {
if (isAnnotated<V>(v)) v = v.item;
export function typeCode<T extends Embeddable>(v: Value<T>): number {
if (isAnnotated<T>(v)) v = v.item;
switch (typeof v) {
case 'boolean':
return 0;
@ -24,23 +25,23 @@ export function typeCode<V>(v: Value<V>): number {
if (Array.isArray(v)) {
return ('label' in v) ? 7 : 8;
}
if (Set.isSet<V>(v)) return 9;
if (Dictionary.isDictionary<V>(v)) return 10;
if (Set.isSet<T>(v)) return 9;
if (Dictionary.isDictionary<T>(v)) return 10;
if (isEmbedded(v)) return 11;
/* fall through */
default:
throw new Error("Invalid Value<V> in typeCode");
throw new Error("Invalid Value<T> in typeCode");
}
}
export function compare<V>(
a: Value<V>,
b: Value<V>,
compare_embedded: (a: V, b: V) => number = (a, b) => is(a, b) ? 0 : a < b ? -1 : 1,
export function compare<T extends Embeddable>(
a: Value<T>,
b: Value<T>,
compare_embedded: (a: T, b: T) => number = (a, b) => is(a, b) ? 0 : a < b ? -1 : 1,
): number {
function cmp(a: Value<V>, b: Value<V>): number {
if (isAnnotated<V>(a)) a = a.item;
if (isAnnotated<V>(b)) b = b.item;
function cmp(a: Value<T>, b: Value<T>): number {
if (isAnnotated<T>(a)) a = a.item;
if (isAnnotated<T>(b)) b = b.item;
const ta = typeCode(a);
const tb = typeCode(b);
if (ta < tb) return -1;
@ -67,14 +68,14 @@ export function compare<V>(
return va < vb ? -1 : va > vb ? 1 : 0;
}
case 7: {
const lr = cmp((a as Record<Value<V>, Value<V>[], V>).label,
(b as Record<Value<V>, Value<V>[], V>).label);
const lr = cmp((a as Record<Value<T>, Value<T>[], T>).label,
(b as Record<Value<T>, Value<T>[], T>).label);
if (lr !== 0) return lr;
/* fall through */
}
case 8: {
const va = a as Value<V>[];
const vb = b as Value<V>[];
const va = a as Value<T>[];
const vb = b as Value<T>[];
const l = Math.min(va.length, vb.length)
for (let i = 0; i < l; i++) {
const c = cmp(va[i], vb[i]);
@ -83,18 +84,17 @@ export function compare<V>(
return va.length < vb.length ? -1 : va.length > vb.length ? 1 : 0;
}
case 9: {
const va = Array.from(a as Set<V>).sort(cmp);
const vb = Array.from(b as Set<V>).sort(cmp);
const va = Array.from(a as Set<T>).sort(cmp);
const vb = Array.from(b as Set<T>).sort(cmp);
return cmp(va, vb);
}
case 10: {
const va = Array.from(a as Dictionary<V>).sort(cmp);
const vb = Array.from(b as Dictionary<V>).sort(cmp);
const va = Array.from(Dictionary.asMap<T>(a)!.entries()).sort(cmp);
const vb = Array.from(Dictionary.asMap<T>(b)!.entries()).sort(cmp);
return cmp(va, vb);
}
case 11:
return compare_embedded((a as Embedded<V>).embeddedValue,
(b as Embedded<V>).embeddedValue);
return compare_embedded(a as T, b as T);
default:
throw new Error("Invalid typeCode: " + ta);
}

View File

@ -0,0 +1,353 @@
// Preserves-Expressions. https://preserves.dev/preserves-expressions.html
import { ReaderBase } from './reader';
import { Atom, Value } from './values';
import { Position, annotate, formatPosition } from './annotated';
import { Record as VRecord } from './record';
import { Embeddable, GenericEmbedded } from './embedded';
import { fromJS } from './fromjs';
import { DictionaryMap, Set as VSet } from './dictionary';
export type Expr = SimpleExpr | Punct;
export type SimpleExpr = Atom | Compound | Embedded;
export type Positioned<I> = { position: Position, item: I, annotations?: Annotations };
export class Punct {
constructor(public text: string) {}
__as_preserve__(): Value { return VRecord(Symbol.for('p'), [Symbol.for(this.text)]); }
isComma(): boolean { return this.text === ','; }
static isComma(v: Expr): boolean { return v instanceof Punct && v.isComma(); }
isColon(n = 1): boolean { return this.text === ':'.repeat(n); }
static isColon(v: Expr, n = 1): boolean { return v instanceof Punct && v.isColon(n); }
}
export class Embedded {
constructor(public expr: SimpleExpr, public annotations?: Annotations) {}
__as_preserve__(): Value {
const v = fromJS(this.expr);
return new GenericEmbedded(this.annotations?.wrap(v) ?? v);
}
}
export class BaseCompound<I> {
positions: Position[] = [];
exprs: I[] = [];
annotations?: Annotations[] = void 0; // sparse array when non-void
get(i: number): Positioned<I> | undefined {
if (i >= this.exprs.length) return void 0;
return {
position: this.positions[i],
item: this.exprs[i],
annotations: this.annotations && this.annotations[i],
};
}
push(p: Positioned<I>): true;
push(expr: I, position: Position, annotations?: Annotations): true;
push(v: Positioned<I> | I, position?: Position, annotations?: Annotations) {
if (position === void 0) {
const p = v as Positioned<I>;
if (p.annotations) this._ensureAnnotations()[this.exprs.length] = p.annotations;
this.positions.push(p.position);
this.exprs.push(p.item);
} else {
if (annotations) this._ensureAnnotations()[this.exprs.length] = annotations;
this.positions.push(position);
this.exprs.push(v as I);
}
return true;
}
_ensureAnnotations(): Annotations[] {
if (this.annotations === void 0) this.annotations = [];
return this.annotations;
}
_annotationsAt(index: number): Annotations {
return this._ensureAnnotations()[index] ??= new Annotations();
}
preservesValues(): Value[] {
return this.exprs.map((p, i) => {
const v = fromJS(p);
if (this.annotations?.[i] !== void 0) {
return this.annotations[i].wrap(v);
} else {
return v;
}
});
}
__as_preserve__(): Value {
return this.preservesValues();
}
map<R>(f: (item: Positioned<I>, index: number) => R, offset = 0): R[] {
const result: R[] = [];
for (let i = offset; i < this.exprs.length; i++) {
result.push(f(this.get(i)!, i));
}
return result;
}
[Symbol.iterator](): IterableIterator<Positioned<I>> {
let c = this;
let i = 0;
return {
next(): IteratorResult<Positioned<I>> {
if (i < c.exprs.length) {
return { done: false, value: c.get(i++)! };
} else {
return { done: true, value: void 0 };
}
},
[Symbol.iterator]() { return c[Symbol.iterator](); }
};
}
}
export class Document extends BaseCompound<Expr> {}
export class Annotations extends BaseCompound<SimpleExpr> {
wrap(v: Value): Value {
return annotate(v, ... this.preservesValues());
}
}
export type CompoundVariant = 'sequence' | 'record' | 'block' | 'group' | 'set';
export abstract class Compound extends BaseCompound<Expr> {
abstract get variant(): CompoundVariant;
__as_preserve__(): Value {
const vs = this.preservesValues();
switch (this.variant) {
case 'sequence': return vs;
case 'record': return VRecord(Symbol.for('r'), vs);
case 'block': return VRecord(Symbol.for('b'), vs);
case 'group': return VRecord(Symbol.for('g'), vs);
case 'set': return VRecord(Symbol.for('s'), vs);
}
}
}
export class Sequence extends Compound {
get variant(): CompoundVariant { return 'sequence'; }
}
export class Record extends Compound {
get variant(): CompoundVariant { return 'record'; }
}
export class Block extends Compound {
get variant(): CompoundVariant { return 'block'; }
}
export class Group extends Compound {
get variant(): CompoundVariant { return 'group'; }
}
export class Set extends Compound {
get variant(): CompoundVariant { return 'set'; }
}
export class Reader extends ReaderBase<never> {
nextDocument(howMany: 'all' | 'one' = 'all'): Document {
const doc = new Document();
this.readExpr(doc);
if (howMany === 'all') {
while (this.readExpr(doc)) {}
}
return doc;
}
readCompound(c: Compound, terminator: string): Compound {
while (this.readExpr(c, terminator)) {}
return c;
}
readSimpleExpr(c: BaseCompound<SimpleExpr>): boolean {
return this._readInto(c, false);
}
readExpr(c: BaseCompound<Expr>, terminator: string | null = null): boolean {
return this._readInto(c as BaseCompound<SimpleExpr> /* yuck */, true, terminator);
}
_checkTerminator(actual: string, expected: string | null, startPos: Position): false {
if (actual === expected) return false;
this.state.error('Unexpected ' + actual, startPos);
}
_readInto(c: BaseCompound<SimpleExpr>, acceptPunct: boolean, terminator: string | null = null): boolean {
while (true) {
this.state.skipws();
if (this.state.atEnd() && terminator === null) return false;
const startPos = this.state.copyPos();
const ch = this.state.nextchar();
switch (ch) {
case '"':
return c.push(this.state.readString('"'), startPos);
case '|':
return c.push(Symbol.for(this.state.readString('|')), startPos);
case ';':
if (acceptPunct) {
return (c as BaseCompound<Expr>).push(new Punct(';'), startPos);
} else {
this.state.error('Semicolon is not permitted at this location', startPos);
}
case '@':
if (!this.readSimpleExpr(c._annotationsAt(c.exprs.length))) {
this.state.error('Missing annotation', startPos);
}
continue;
case ':': {
let colons: string = ch;
while (!this.state.atEnd() && this.state.peek() === ':') {
colons = colons + ':';
this.state.advance();
}
if (acceptPunct) {
return (c as BaseCompound<Expr>).push(new Punct(colons), startPos);
} else {
this.state.error('Colons are not permitted at this location', startPos);
}
}
case '#': {
const ch = this.state.nextchar();
switch (ch) {
case ' ': case '\t': {
const here = this.state.copyPos();
c._annotationsAt(c.exprs.length).push(this.state.readCommentLine(), here);
continue;
}
case '\n': case '\r': {
const here = this.state.copyPos();
c._annotationsAt(c.exprs.length).push('', here);
continue;
}
case '!': {
const here = this.state.copyPos();
const r = new Record();
r.push(Symbol.for('interpreter'), here);
r.push(this.state.readCommentLine(), here);
c._annotationsAt(c.exprs.length).push(r, here);
continue;
}
case 'f': this.state.requireDelimiter('#f'); return c.push(false, startPos);
case 't': this.state.requireDelimiter('#t'); return c.push(true, startPos);
case '{': return c.push(this.readCompound(new Set(), '}'), startPos);
case '"': return c.push(this.state.readLiteralBinary(), startPos);
case 'x': switch (this.state.nextchar()) {
case '"': return c.push(this.state.readHexBinary(), startPos);
case 'd': return c.push(this.state.readHexFloat(), startPos);
default: this.state.error('Invalid #x syntax', startPos);
}
case '[': return c.push(this.state.readBase64Binary(), startPos);
case ':': {
const r = new BaseCompound<SimpleExpr>();
if (!this.readSimpleExpr(r)) return false;
const e = new Embedded(r.exprs[0], r.annotations && r.annotations[0]);
return c.push(e, startPos);
}
default:
this.state.error(`Invalid # syntax: ${ch}`, startPos);
}
}
case '(': return c.push(this.readCompound(new Group(), ')'), startPos);
case '<': return c.push(this.readCompound(new Record(), '>'), startPos);
case '[': return c.push(this.readCompound(new Sequence(), ']'), startPos);
case '{': return c.push(this.readCompound(new Block(), '}'), startPos);
case ')': return this._checkTerminator(ch, terminator, startPos);
case '>': return this._checkTerminator(ch, terminator, startPos);
case ']': return this._checkTerminator(ch, terminator, startPos);
case '}': return this._checkTerminator(ch, terminator, startPos);
case ',':
if (acceptPunct) {
return (c as BaseCompound<Expr>).push(new Punct(','), startPos);
} else {
this.state.error('Comma is not permitted at this location', startPos);
}
default:
return c.push(this.state.readRawSymbolOrNumber(ch), startPos);
}
}
}
}
export interface AsPreservesOptions<T extends Embeddable> {
onGroup?: (g: Positioned<Group>) => Value<T>;
onEmbedded?: (e: Positioned<Expr>, walk: (p: Positioned<Expr>) => Value<T>) => Value<T>;
error?: (tag: string, position: Position) => Value<T>;
}
export function asPreserves<T extends Embeddable>(
p: Positioned<Expr>,
options: AsPreservesOptions<T> = {},
): Value<T> {
const error = options.error ?? ((tag, position) => {
throw new Error(formatPosition(position) + ": " + tag);
});
function nonCommas(p: Compound): Positioned<Expr>[] {
return Array.from(p).filter(p => !Punct.isComma(p.item));
}
function walk(p: Positioned<Expr>): Value<T> {
if (p.item instanceof Punct) {
return error('invalid-punctuation', p.position);
} else if (p.item instanceof Embedded) {
if (options.onEmbedded) {
return options.onEmbedded({ position: p.position, item: p.item.expr }, walk);
} else {
return error('unexpected-embedded', p.position);
}
} else if (p.item instanceof Compound) {
switch (p.item.variant) {
case 'sequence':
return nonCommas(p.item).map(walk);
case 'record': {
const vs = nonCommas(p.item).map(walk);
if (vs.length < 1) {
return error('invalid-record', p.position);
}
const r = vs.slice(1) as unknown as VRecord<Value<T>, Value<T>[], T>;
r.label = vs[0];
return r;
}
case 'block': {
const d = new DictionaryMap<T>();
const vs = nonCommas(p.item);
if ((vs.length % 3) !== 0) {
return error('invalid-dictionary', p.position);
}
for (let i = 0; i < vs.length; i += 3) {
if (!Punct.isColon(vs[i + 1].item)) {
return error('missing-colon', vs[i + 1].position);
}
const k = walk(vs[i]);
const v = walk(vs[i + 2]);
d.set(k, v);
}
return d.simplifiedValue();
}
case 'group': {
if (options.onGroup) {
return options.onGroup(p as Positioned<Group>);
} else {
return error('unexpected-group', p.position);
}
}
case 'set':
return new VSet(nonCommas(p.item).map(walk));
}
} else {
return p.item;
}
}
return walk(p);
}

View File

@ -2,15 +2,15 @@
import type { Value } from './values';
import { DecodeError, ShortPacket } from './codec';
import { Dictionary, Set } from './dictionary';
import { Dictionary, DictionaryMap, Set } from './dictionary';
import { strip } from './strip';
import { Bytes, unhexDigit } from './bytes';
import { Decoder, DecoderState, neverEmbeddedTypeDecode } from './decoder';
import { Record } from './record';
import { Annotated, newPosition, Position, updatePosition } from './annotated';
import { Double, DoubleFloat, FloatType } from './float';
import { Double, DoubleFloat } from './float';
import { stringify } from './text';
import { embed, GenericEmbedded, EmbeddedTypeDecode } from './embedded';
import { Embeddable, GenericEmbedded, EmbeddedTypeDecode } from './embedded';
export interface ReaderStateOptions {
includeAnnotations?: boolean;
@ -155,13 +155,15 @@ export class ReaderState {
this.error(`Delimiter must follow ${prefix}`, this.pos);
}
static readonly DELIMITERS = '(){}[]<>";,@#:|';
delimiterFollows(): boolean {
if (this.atEnd()) return true;
const ch = this.peek();
return ('(){}[]<>";,@#:|'.indexOf(ch) !== -1) || isSpace(ch);
return (ReaderState.DELIMITERS.indexOf(ch) !== -1) || isSpace(ch);
}
readRawSymbolOrNumber<T>(acc: string): Value<T> {
readRawSymbolOrNumber(acc: string): number | bigint | symbol | DoubleFloat {
while (!this.delimiterFollows()) acc = acc + this.nextchar();
const m = NUMBER_RE.exec(acc);
if (m) {
@ -248,6 +250,16 @@ export class ReaderState {
'x',
() => this.readHex2());
}
readCommentLine(): string {
let acc = '';
while (true) {
if (this.atEnd()) return acc;
const c = this.nextchar();
if (c === '\n' || c === '\r') return acc;
acc = acc + c;
}
}
}
export const genericEmbeddedTypeDecode: EmbeddedTypeDecode<GenericEmbedded> = {
@ -260,7 +272,7 @@ export const genericEmbeddedTypeDecode: EmbeddedTypeDecode<GenericEmbedded> = {
},
};
export class Reader<T> {
export class ReaderBase<T extends Embeddable> {
state: ReaderState;
embeddedType: EmbeddedTypeDecode<T>;
@ -283,17 +295,13 @@ export class Reader<T> {
write(data: string) {
this.state.write(data);
}
}
export class Reader<T extends Embeddable> extends ReaderBase<T> {
readCommentLine(): Value<T> {
const startPos = this.state.copyPos();
let acc = '';
while (true) {
const c = this.state.nextchar();
if (c === '\n' || c === '\r') {
return this.wrap(acc, startPos);
}
acc = acc + c;
}
return this.wrap(this.state.readCommentLine(), startPos);
}
wrap(v: Value<T>, pos: Position): Value<T> {
@ -344,6 +352,9 @@ export class Reader<T> {
switch (c) {
case ' ': case '\t': return this.annotateNextWith(this.readCommentLine());
case '\n': case '\r': return this.annotateNextWith('');
case '!':
return this.annotateNextWith(
Record(Symbol.for('interpreter'), [this.readCommentLine()]));
case 'f': this.state.requireDelimiter('#f'); return false;
case 't': this.state.requireDelimiter('#t'); return true;
case '{': return this.readSet();
@ -354,9 +365,9 @@ export class Reader<T> {
default: this.state.error('Invalid #x syntax', startPos);
}
case '[': return this.state.readBase64Binary();
case ':': return embed(this.embeddedType.fromValue(
case ':': return this.embeddedType.fromValue(
new Reader<GenericEmbedded>(this.state, genericEmbeddedTypeDecode).next(),
this.state.options));
this.state.options);
default:
this.state.error(`Invalid # syntax: ${c}`, startPos);
}
@ -395,22 +406,18 @@ export class Reader<T> {
}
readDictionary(): Dictionary<T> {
return this.seq(true,
new Dictionary<T>(),
(k, acc) => {
this.state.skipws();
switch (this.state.peek()) {
case ':':
if (acc.has(k)) this.state.error(
`Duplicate key: ${stringify(k)}`, this.state.pos);
this.state.advance();
acc.set(k, this.next());
break;
default:
this.state.error('Missing key/value separator', this.state.pos);
}
},
'}');
return this.seq(true, new DictionaryMap<T>(), (k, acc) => {
this.state.skipws();
switch (this.state.peek()) {
case ':':
this.state.advance();
if (acc.has(k)) this.state.error(`Duplicate key: ${stringify(k)}`, this.state.pos);
acc.set(k, this.next());
break;
default:
this.state.error('Missing key/value separator', this.state.pos);
}
}, '}').simplifiedValue();
}
readSet(): Set<T> {

View File

@ -1,11 +1,10 @@
import { GenericEmbedded } from "./embedded";
import { Embeddable, GenericEmbedded } from "./embedded";
import { is } from "./is";
import { Value } from "./values";
import { Writer } from "./writer";
export type Tuple<T> = Array<T> | [T];
export type Record<LabelType extends Value<T>, FieldsType extends Tuple<Value<T>>, T = GenericEmbedded>
export type Record<LabelType extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends Embeddable = GenericEmbedded>
= FieldsType & { label: LabelType };
export type RecordGetters<Fs, R> = {
@ -15,7 +14,7 @@ export type RecordGetters<Fs, R> = {
export type CtorTypes<Fs, Names extends Tuple<keyof Fs>> =
{ [K in keyof Names]: Fs[keyof Fs & Names[K]] } & any[];
export interface RecordConstructor<L extends Value<T>, Fs, Names extends Tuple<keyof Fs>, T = GenericEmbedded> {
export interface RecordConstructor<L extends Value<T>, Fs, Names extends Tuple<keyof Fs>, T extends Embeddable = GenericEmbedded> {
(...fields: CtorTypes<Fs, Names>): Record<L, CtorTypes<Fs, Names>, T>;
constructorInfo: RecordConstructorInfo<L, T>;
isClassOf(v: any): v is Record<L, CtorTypes<Fs, Names>, T>;
@ -23,7 +22,7 @@ export interface RecordConstructor<L extends Value<T>, Fs, Names extends Tuple<k
fieldNumbers: { [K in string & keyof Fs]: number };
};
export interface RecordConstructorInfo<L extends Value<T>, T = GenericEmbedded> {
export interface RecordConstructorInfo<L extends Value<T>, T extends Embeddable = GenericEmbedded> {
label: L;
arity: number;
}
@ -48,23 +47,23 @@ export function Record<L, FieldsType extends Tuple<any>>(
}
export namespace Record {
export function isRecord<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T = GenericEmbedded>(x: any): x is Record<L, FieldsType, T> {
export function isRecord<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends Embeddable = GenericEmbedded>(x: any): x is Record<L, FieldsType, T> {
return Array.isArray(x) && 'label' in x;
}
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 extends Embeddable = GenericEmbedded>(
r: Record<L, FieldsType, T>): RecordConstructorInfo<L, T>
{
return { label: r.label, arity: r.length };
}
export function isClassOf<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T = GenericEmbedded>(
export function isClassOf<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends Embeddable = GenericEmbedded>(
ci: RecordConstructorInfo<L, T>, v: any): v is Record<L, FieldsType, T>
{
return (Record.isRecord(v)) && is(ci.label, v.label) && (ci.arity === v.length);
}
export function makeConstructor<Fs, T = GenericEmbedded>()
export function makeConstructor<Fs, T extends Embeddable = GenericEmbedded>()
: (<L extends Value<T>, Names extends Tuple<keyof Fs>>(label: L, fieldNames: Names) =>
RecordConstructor<L, Fs, Names, T>)
{

View File

@ -13,6 +13,7 @@ export * from './float';
export * from './fold';
export * from './fromjs';
export * from './is';
export * from './jsdictionary';
export * from './merge';
export * from './order';
export * from './reader';

View File

@ -1,18 +1,18 @@
import { Value } from "./values";
import { Annotated } from "./annotated";
import { Record, Tuple } from "./record";
import { Set, Dictionary } from "./dictionary";
import type { GenericEmbedded } from "./embedded";
import { Set, Dictionary, DictionaryMap } from "./dictionary";
import type { Embeddable, GenericEmbedded } from "./embedded";
export function unannotate<T = GenericEmbedded>(v: Value<T>): Value<T> {
export function unannotate<T extends Embeddable = GenericEmbedded>(v: Value<T>): Value<T> {
return Annotated.isAnnotated<T>(v) ? v.item : v;
}
export function peel<T = GenericEmbedded>(v: Value<T>): Value<T> {
export function peel<T extends Embeddable = GenericEmbedded>(v: Value<T>): Value<T> {
return strip(v, 1);
}
export function strip<T = GenericEmbedded>(
export function strip<T extends Embeddable = GenericEmbedded>(
v: Value<T>,
depth: number = Infinity): Value<T>
{
@ -34,7 +34,9 @@ export function strip<T = GenericEmbedded>(
} else if (Set.isSet<T>(v.item)) {
return v.item.map(walk);
} else if (Dictionary.isDictionary<T>(v.item)) {
return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]);
const result = new DictionaryMap<T>();
new DictionaryMap<T>(v.item).forEach((val, key) => result.set(walk(key), walk(val)));
return result.simplifiedValue();
} else {
return v.item;
}

View File

@ -1,4 +1,4 @@
import { Embedded, GenericEmbedded } from './embedded';
import { Embeddable, GenericEmbedded, isEmbedded } from './embedded';
import type { Value } from './values';
import { Annotated } from './annotated';
@ -8,22 +8,28 @@ import { Writer, WriterOptions, EmbeddedWriter, WriterState } from './writer';
import { fromJS } from './fromjs';
import { Reader, ReaderOptions } from './reader';
export function parse<T = GenericEmbedded>(buffer: string, options?: ReaderOptions<T>): Value<T> {
export function parse<T extends Embeddable = GenericEmbedded>(
buffer: string,
options?: ReaderOptions<T>,
): Value<T> {
return new Reader<T>(buffer, options).next();
}
export function parseAll<T = GenericEmbedded>(buffer: string, options?: ReaderOptions<T>): Value<T>[] {
export function parseAll<T extends Embeddable = GenericEmbedded>(
buffer: string,
options?: ReaderOptions<T>,
): Value<T>[] {
return new Reader<T>(buffer, options).readToEnd();
}
export const stringifyEmbeddedWrite: EmbeddedWriter<any> = {
export const stringifyEmbeddedWrite = {
write(s: WriterState, v: any): void {
if (v instanceof GenericEmbedded) {
new Writer(s, this).push(v.generic);
} else {
try {
const j = fromJS(v);
if (!(j instanceof Embedded)) {
if (!isEmbedded(j)) {
new Writer(s, this).push(j);
return;
}
@ -37,7 +43,7 @@ export const stringifyEmbeddedWrite: EmbeddedWriter<any> = {
}
};
export function stringify<T = GenericEmbedded>(x: any, options?: WriterOptions<T>): string {
export function stringify<T extends Embeddable = GenericEmbedded>(x: any, options?: WriterOptions<T>): string {
options = { ... (options ?? {}) };
options.embeddedWrite = options.embeddedWrite ?? stringifyEmbeddedWrite;
return Writer.stringify(fromJS<T>(x), options);

View File

@ -3,13 +3,14 @@
import type { Bytes } from './bytes';
import type { DoubleFloat } from './float';
import type { Annotated } from './annotated';
import type { Set, Dictionary } from './dictionary';
import type { Embedded, GenericEmbedded } from './embedded';
import type { JsDictionary } from './jsdictionary';
import { Set, KeyedDictionary } from './dictionary';
import type { Embeddable, GenericEmbedded } from './embedded';
export type Value<T = GenericEmbedded> =
export type Value<T extends Embeddable = GenericEmbedded> =
| Atom
| Compound<T>
| Embedded<T>
| T
| Annotated<T>;
export type Atom =
| boolean
@ -18,7 +19,7 @@ export type Atom =
| string
| Bytes
| symbol;
export type Compound<T = GenericEmbedded> =
export type Compound<T extends Embeddable = GenericEmbedded> =
| (Array<Value<T>> | [Value<T>]) & { label: Value<T> }
// ^ expanded from definition of Record<> in record.ts,
// because if we use Record<Value<T>, Tuple<Value<T>>, T>,
@ -27,4 +28,7 @@ export type Compound<T = GenericEmbedded> =
// Value<T> to any.
| Array<Value<T>>
| Set<T>
| Dictionary<T>;
// v Expanded from definition of Dictionary<> in dictionary.ts,
// because of circular-use-of-Value<T> issues.
| JsDictionary<Value<T>>
| KeyedDictionary<T>;

View File

@ -1,19 +1,20 @@
import { isAnnotated } from './is';
import { Record, Tuple } from "./record";
import type { GenericEmbedded, Embedded, EmbeddedTypeEncode } from "./embedded";
import { Embeddable, GenericEmbedded, EmbeddedTypeEncode, isEmbedded } from "./embedded";
import { Encoder, EncoderState } from "./encoder";
import type { Value } from "./values";
import { NUMBER_RE } from './reader';
import { encodeBase64 } from './base64';
import { DictionaryMap, writeDictionaryOn } from './dictionary';
export type Writable<T> =
export type Writable<T extends Embeddable> =
Value<T> | PreserveWritable<T> | Iterable<Value<T>> | ArrayBufferView;
export interface PreserveWritable<T> {
export interface PreserveWritable<T extends Embeddable> {
__preserve_text_on__(writer: Writer<T>): void;
}
export function isPreserveWritable<T>(v: any): v is PreserveWritable<T> {
export function isPreserveWritable<T extends Embeddable>(v: any): v is PreserveWritable<T> {
return typeof v === 'object' && v !== null && '__preserve_text_on__' in v && typeof v.__preserve_text_on__ === 'function';
}
@ -191,7 +192,7 @@ export class WriterState {
this.pieces.push(buf + '"');
}
couldBeFlat<T>(vs: Writable<T>[]): boolean {
couldBeFlat<T extends Embeddable>(vs: Writable<T>[]): boolean {
let seenCompound = false;
for (let v of vs) {
if (Array.isArray(v) || Set.isSet(v) || Map.isMap(v)) {
@ -206,7 +207,7 @@ export class WriterState {
}
}
export class Writer<T> {
export class Writer<T extends Embeddable> {
state: WriterState;
embeddedWrite: EmbeddedWriter<T>;
@ -225,7 +226,7 @@ export class Writer<T> {
}
}
static stringify<T>(v: Writable<T>, options?: WriterOptions<T>): string {
static stringify<T extends Embeddable>(v: Writable<T>, options?: WriterOptions<T>): string {
const w = new Writer(options);
w.push(v);
return w.contents();
@ -262,7 +263,10 @@ export class Writer<T> {
this.state.pieces.push('' + v);
break;
case 'object':
if (isPreserveWritable<unknown>(v)) {
if (v === null) {
throw new Error("Cannot encode null in Preserves Writer.push");
}
else if (isPreserveWritable<any>(v)) {
v.__preserve_text_on__(this);
}
else if (isPreserveWritable<T>(v)) {
@ -295,16 +299,19 @@ export class Writer<T> {
else if (isIterable(v)) {
this.state.writeSeq('[', ']', v, vv => this.push(vv));
}
else {
((v: Embedded<T>) => {
this.state.pieces.push('#:');
if ('write' in this.embeddedWrite) {
this.embeddedWrite.write(this.state, v.embeddedValue);
} else {
new Writer(this.state, genericEmbeddedTypeEncode)
.push(this.embeddedWrite.toValue(v.embeddedValue));
}
})(v);
else if (isEmbedded(v)) {
this.state.pieces.push('#:');
if ('write' in this.embeddedWrite) {
this.embeddedWrite.write(this.state, v);
} else {
new Writer(this.state, genericEmbeddedTypeEncode)
.push(this.embeddedWrite.toValue(v));
}
} else {
writeDictionaryOn(new DictionaryMap<T>(v),
this,
(k, w) => w.push(k),
(v, w) => w.push(v));
}
break;
default:

View File

@ -118,3 +118,11 @@ describe('base64 decoder', () => {
it('gQ==', () => expect(d64('gQ==')).is(Bytes.of(0x81)));
});
});
describe('latin1 quasi-decoder', () => {
it('decodes ascii', () => expect(Bytes.fromLatin1('abc')).is(Bytes.of(97, 98, 99)));
it('encodes ascii', () => expect(Bytes.of(97, 98, 99).toLatin1()).is('abc'));
it('decodes unprintables', () => expect(Bytes.fromLatin1('\x00\x01a\xfe\xff')).is(Bytes.of(0, 1, 97, 254, 255)));
it('encodes unprintables', () => expect(Bytes.of(0, 1, 97, 254, 255).toLatin1()).is('\x00\x01a\xfe\xff'));
it('rejects out-of-bounds', () => expect(() => Bytes.fromLatin1('ac╔b')).toThrowError('Codepoint out of range'));
});

View File

@ -4,7 +4,6 @@ import {
decode, decodeWithAnnotations, encode, canonicalEncode,
DecodeError, ShortPacket,
Bytes, Record,
annotate,
strip, peel,
preserves,
stringify,
@ -16,11 +15,11 @@ import {
EmbeddedType,
DecoderState,
Decoder,
Embedded,
embed,
genericEmbeddedTypeDecode,
genericEmbeddedTypeEncode,
parse,
Embedded,
KeyedDictionary,
} from '../src/index';
const { Tag } = Constants;
import './test-utils';
@ -78,7 +77,7 @@ describe('parsing from subarray', () => {
describe('reusing buffer space', () => {
it('should be done safely, even with nested dictionaries', () => {
expect(canonicalEncode(fromJS(['aaa', Dictionary.fromJS({a: 1}), 'zzz'])).toHex()).is(
expect(canonicalEncode(fromJS(['aaa', Dictionary.stringMap({a: 1}), 'zzz'])).toHex()).is(
`b5
b103616161
b7
@ -90,33 +89,29 @@ describe('reusing buffer space', () => {
});
describe('encoding and decoding embeddeds', () => {
class LookasideEmbeddedType implements EmbeddedType<object> {
readonly objects: object[];
class LookasideEmbeddedType implements EmbeddedType<Embedded<object>> {
readonly objects: Embedded<object>[];
constructor(objects: object[]) {
constructor(objects: Embedded<object>[]) {
this.objects = objects;
}
decode(d: DecoderState): object {
decode(d: DecoderState): Embedded<object> {
return this.fromValue(new Decoder<GenericEmbedded>(d).next());
}
encode(e: EncoderState, v: object): void {
encode(e: EncoderState, v: Embedded<object>): void {
new Encoder(e).push(this.toValue(v));
}
equals(a: object, b: object): boolean {
return Object.is(a, b);
}
fromValue(v: Value<GenericEmbedded>): object {
fromValue(v: Value<GenericEmbedded>): Embedded<object> {
if (typeof v !== 'number' || v < 0 || v >= this.objects.length) {
throw new Error("Unknown embedded target");
throw new Error(`Unknown embedded target: ${stringify(v)}`);
}
return this.objects[v];
}
toValue(v: object): number {
toValue(v: Embedded<object>): number {
let i = this.objects.indexOf(v);
if (i !== -1) return i;
this.objects.push(v);
@ -125,8 +120,8 @@ describe('encoding and decoding embeddeds', () => {
}
it('should encode using embeddedId when no function has been supplied', () => {
const A1 = embed({a: 1});
const A2 = embed({a: 1});
const A1 = new Embedded({a: 1});
const A2 = new Embedded({a: 1});
const bs1 = canonicalEncode(A1);
const bs2 = canonicalEncode(A2);
const bs3 = canonicalEncode(A1);
@ -143,24 +138,24 @@ describe('encoding and decoding embeddeds', () => {
.toThrow("Embeddeds not permitted at this point in Preserves document");
});
it('should encode properly', () => {
const objects: object[] = [];
const objects: Embedded<object>[] = [];
const pt = new LookasideEmbeddedType(objects);
const A = embed({a: 1});
const B = embed({b: 2});
const A = new Embedded({a: 1});
const B = new Embedded({b: 2});
expect(encode([A, B], { embeddedEncode: pt })).is(
Bytes.from([Tag.Sequence,
Tag.Embedded, Tag.SignedInteger, 0,
Tag.Embedded, Tag.SignedInteger, 1, 1,
Tag.End]));
expect(objects).toEqual([A.embeddedValue, B.embeddedValue]);
expect(objects).toEqual([A, B]);
});
it('should decode properly', () => {
const objects: object[] = [];
const objects: Embedded<object>[] = [];
const pt = new LookasideEmbeddedType(objects);
const X: Embedded<object> = embed({x: 123});
const Y: Embedded<object> = embed({y: 456});
objects.push(X.embeddedValue);
objects.push(Y.embeddedValue);
const X = new Embedded({x: 123});
const Y = new Embedded({y: 456});
objects.push(X);
objects.push(Y);
expect(decode(Bytes.from([
Tag.Sequence,
Tag.Embedded, Tag.SignedInteger, 0,
@ -169,17 +164,17 @@ describe('encoding and decoding embeddeds', () => {
]), { embeddedDecode: pt })).is([X, Y]);
});
it('should store embeddeds embedded in map keys correctly', () => {
const A1a = {a: 1};
const A1: Embedded<object> = embed(A1a);
const A2: Embedded<object> = embed({a: 1});
const m = new Dictionary<object, number>();
const A1a = new Embedded({a: 1});
const A1 = A1a;
const A2 = new Embedded({a: 1});
const m = new KeyedDictionary<Embedded<object>, Value<Embedded<object>>, number>();
m.set([A1], 1);
m.set([A2], 2);
expect(m.get(A1)).toBeUndefined();
expect(m.get([A1])).toBe(1);
expect(m.get([A2])).toBe(2);
expect(m.get([embed({a: 1})])).toBeUndefined();
A1a.a = 3;
expect(m.get([{a: 1}])).toBeUndefined();
A1a.value.a = 3;
expect(m.get([A1])).toBe(1);
});
});
@ -309,7 +304,7 @@ describe('common test suite', () => {
const tests = (peel(TestCases._.cases(peel(samples) as TestCases)) as
Dictionary<GenericEmbedded>);
tests.forEach((t0: Value<GenericEmbedded>, tName0: Value<GenericEmbedded>) => {
Dictionary.asMap(tests).forEach((t0, tName0) => {
const tName = Symbol.keyFor(strip(tName0) as symbol)!;
const t = peel(t0) as Record<symbol, any, GenericEmbedded>;
switch (t.label) {

View File

@ -0,0 +1,45 @@
import { Pexpr, Value, fromJS, parse, stringify } from '../src/index';
import './test-utils';
function P(s: string): Value<any>[] {
return parse(s, { includeAnnotations: true });
}
describe('basics', () => {
it('simple example', () => {
const r = new Pexpr.Reader('#!foo\n<bar {zot ::quux}, [a; b; c;]>');
const d = r.nextDocument();
expect(fromJS(d)).is(P(`[\n#!foo\n
<r bar <b zot <p |::|> quux> <p |,|> [a <p |;|> b <p |;|> c <p |;|>]>]`));
});
it('simple group', () => {
const r = new Pexpr.Reader('(+ 1 2)');
expect(fromJS(r.nextDocument())).is(P('[<g + 1 2>]'));
});
it('asPreserves', () => {
const s = '{a: b, c: d, e: [1, 2 <r 3 4>]}';
const d = new Pexpr.Reader(s).nextDocument();
const v = Pexpr.asPreserves(d.get(0)!);
expect(v).is(P(s));
});
});
describe('trailing comments', () => {
it('basics 1', () => {
const d = new Pexpr.Reader('# a comment with nothing after').nextDocument();
expect(d.annotations?.[d.exprs.length].get(0)?.item).toBe('a comment with nothing after');
});
it('basics 2', () => {
const d = new Pexpr.Reader('# a comment with nothing after\n').nextDocument();
expect(d.annotations?.[d.exprs.length].get(0)?.item).toBe('a comment with nothing after');
});
it('inside a sequence', () => {
const d = new Pexpr.Reader('[\n1\n# a comment with nothing after\n]\n').nextDocument();
const seq = d.get(0)?.item as Pexpr.Compound;
expect(seq.annotations?.[seq.exprs.length].get(0)?.item).toBe('a comment with nothing after');
});
});

View File

@ -1,4 +1,4 @@
import { Bytes, Decoder, genericEmbeddedType, encode, Reader, Double } from '../src/index';
import { Bytes, Decoder, genericEmbeddedType, encode, Reader, Double, KeyedDictionary, Value } from '../src/index';
import './test-utils';
import * as fs from 'fs';
@ -36,4 +36,23 @@ describe('reading common test suite', () => {
expect(new Reader('123.0').next()).toEqual(Double(123.0));
expect(new Reader('123.00').next()).toEqual(Double(123.0));
});
it('should produce a sensible JS object for symbol-keyed dictionaries', () => {
expect(new Reader('{a: 1, b: 2}').next()).toEqual({a: 1, b: 2});
});
it('should produce a sensible dictionary for mixed-keyed dictionaries', () => {
expect(new Reader('{a: 1, "b": 2}').next()).is(
new KeyedDictionary([[Symbol.for('a'), 1], ["b", 2]] as [Value, Value][]));
});
it('should produce a sensible dictionary for string-keyed dictionaries', () => {
expect(new Reader('{"a": 1, "b": 2}').next()).is(
new KeyedDictionary([["a", 1], ["b", 2]] as [Value, Value][]));
});
it('should produce a sensible dictionary for integer-keyed dictionaries', () => {
expect(new Reader('{9: 1, 8: 2}').next()).is(
new KeyedDictionary([[9, 1], [8, 2]] as [Value, Value][]));
});
});

View File

@ -1,10 +1,10 @@
import { Value, is, preserves } from '../src/index';
import { Value, is, preserves, Embeddable } from '../src/index';
import '../src/node_support';
declare global {
namespace jest {
interface Matchers<R> {
is<T>(expected: Value<T>): R;
is<T extends Embeddable>(expected: Value<T>): R;
toThrowFilter(f: (e: Error) => boolean): R;
}
}

View File

@ -1,4 +1,4 @@
import { Double, fromJS, Dictionary, IDENTITY_FOLD, fold, mapEmbeddeds, Value, embed, preserves } from '../src/index';
import { Double, fromJS, IDENTITY_FOLD, fold, mapEmbeddeds, Value, preserves, KeyedDictionary, Embeddable, Embedded } from '../src/index';
import './test-utils';
describe('Double', () => {
@ -8,13 +8,13 @@ describe('Double', () => {
});
describe('fold', () => {
function mkv<T extends object>(t: T): Value<T> {
function mkv<T extends Embeddable>(t: T): Value<T> {
return fromJS<T>([
1,
2,
new Dictionary([[[3, 4], fromJS([5, 6])],
['a', 1],
['b', true]]),
new KeyedDictionary<T>([[[3, 4], fromJS([5, 6])],
['a', 1],
['b', true]]),
Double(3.4),
t,
]);
@ -22,12 +22,12 @@ describe('fold', () => {
it('should support identity', () => {
const w = new Date();
const v = mkv(w);
const v = mkv(new Embedded(w));
expect(fold(v, IDENTITY_FOLD)).is(v);
const w1 = new Date();
const v1 = mkv(w1);
const v1 = mkv(new Embedded(w1));
expect(fold(v, IDENTITY_FOLD)).not.is(v1);
expect(mapEmbeddeds(v, _t => embed(w1))).is(v1);
expect(mapEmbeddeds(v, _t => new Embedded(w1))).is(v1);
});
});
@ -69,6 +69,12 @@ describe('is()', () => {
expect(c).not.toBe(3);
expect(c).not.is(3);
});
it('should compare equivalent JsDictionary and KeyedDictionary values sensibly', () => {
const a = {a: 1, b: 2};
const b = new KeyedDictionary(
[[Symbol.for('a'), 1], [Symbol.for('b'), 2]] as [Value, Value][]);
expect(a).is(b);
});
});
describe('`preserves` formatter', () => {
@ -82,4 +88,18 @@ describe('`preserves` formatter', () => {
expect(preserves`>${BigInt("12345678123456781234567812345678")}<`)
.toBe('>12345678123456781234567812345678<');
});
it('should format regular JS objects', () => {
expect(preserves`>${({a: 1, b: 2})}<`)
.toBe('>{a: 1 b: 2}<');
});
it('should format dictionaries with string keys', () => {
const v = new KeyedDictionary([["a", 1], ["b", 2]]);
expect(preserves`>${v}<`)
.toBe('>{"a": 1 "b": 2}<');
});
it('should format dictionaries with symbol keys', () => {
const v = new KeyedDictionary([[Symbol.for("a"), 1], [Symbol.for("b"), 2]]);
expect(preserves`>${v}<`)
.toBe('>{a: 1 b: 2}<');
});
});

View File

@ -1,6 +1,6 @@
{
"name": "@preserves/schema-cli",
"version": "0.994.0",
"version": "0.995.206",
"description": "Command-line tools for Preserves Schema",
"homepage": "https://gitlab.com/preserves/preserves",
"license": "Apache-2.0",
@ -26,8 +26,8 @@
"@types/minimatch": "^3.0"
},
"dependencies": {
"@preserves/core": "^0.994.0",
"@preserves/schema": "^0.994.0",
"@preserves/core": "^0.995.206",
"@preserves/schema": "^0.995.206",
"chalk": "^4.1",
"chokidar": "^3.5",
"commander": "^7.2",

View File

@ -25,7 +25,7 @@ export function run(options: CommandLineArguments): void {
if (failures.length === 0) {
if (options.bundle) {
fs.writeSync(1, underlying(canonicalEncode(M.fromBundle(M.Bundle(
new KeyedDictionary<M.ModulePath, M.Schema, M.InputEmbedded>(
new KeyedDictionary<M.InputEmbedded, M.ModulePath, M.Schema>(
inputFiles.map(i => [i.modulePath, i.schema])))))));
} else {
fs.writeSync(1, underlying(canonicalEncode(M.fromSchema(inputFiles[0].schema))));

View File

@ -1,6 +1,6 @@
{
"name": "@preserves/schema",
"version": "0.994.0",
"version": "0.995.206",
"description": "Schema support for Preserves data serialization format",
"homepage": "https://gitlab.com/preserves/preserves",
"license": "Apache-2.0",
@ -26,6 +26,10 @@
"veryclean": "yarn run clean && rm -rf node_modules"
},
"dependencies": {
"@preserves/core": "^0.994.0"
"@preserves/core": "^0.995.206"
},
"devDependencies": {
"@types/js-beautify": "1.14",
"js-beautify": "1.15"
}
}

View File

@ -68,33 +68,9 @@
}
}
// I.env.forEach((_schema, path) => console.log(PreservesSchema.Meta.formatModulePath(path)));
for (const [modulePath, schema] of I.env) {
let container = Schema;
modulePath.slice(0, -1).forEach(n => {
if (!(n.description in container)) container[n.description] = {};
container = container[n.description];
});
container[modulePath[modulePath.length - 1].description] = moduleFor(modulePath, schema);
}
I.moduleTree(Schema);
schemaReady();
}
function moduleFor(modulePath, schema) {
const { Meta, GenType, Type } = PreservesSchema;
const mod = {};
schema.definitions.forEach((d, n) => {
const definitionName = n.description;
const definitionId = Meta.jsId(definitionName);
mod[`${definitionId}`] = I.definitionConstructor(modulePath, n);
mod[`from${definitionId}`] = I.unparser(modulePath, n);
mod[`to${definitionId}`] = v => I.tryParse(modulePath, n, v);
mod[`as${definitionId}`] = v => I.parse(modulePath, n, v);
});
return mod;
}
window.addEventListener('DOMContentLoaded', translateScripts);
})();

View File

@ -1,11 +1,11 @@
import { stringify } from '@preserves/core';
import { JsDictionary, stringify } from '@preserves/core';
import * as M from './meta';
export function checkSchema(schema: M.Schema): (
{ ok: true, schema: M.Schema } | { ok: false, problems: Array<string> })
{
const checker = new Checker();
schema.definitions.forEach(checker.checkDefinition.bind(checker));
JsDictionary.forEach(schema.definitions, checker.checkDefinition.bind(checker));
if (checker.problems.length > 0) {
return { ok: false, problems: checker.problems };
} else {

View File

@ -1,4 +1,4 @@
import { encode, stringify } from "@preserves/core";
import { encode, JsDictionary, stringify } from "@preserves/core";
import * as M from "./meta";
import { CompilerOptions, ModuleContext } from "./compiler/context";
import { Formatter, block, seq, braces } from "./compiler/block";
@ -29,7 +29,7 @@ export function compile(
mod.defineType(seq(`export type _embedded = `, mod.embeddedType, `;`));
}
for (const [name, def] of schema.definitions) {
for (const [name, def] of JsDictionary.entries(schema.definitions)) {
const t = typeForDefinition(mod.resolver(), def);
const nameStr = stringify(name);
const resultTypeItem = seq(nameStr, mod.genericArgsFor(t));
@ -49,7 +49,7 @@ export function compile(
}
}
for (const [name0, def] of schema.definitions) {
for (const [name0, def] of JsDictionary.entries(schema.definitions)) {
const t = typeForDefinition(mod.resolver(), def);
const name = name0 as symbol;
const nameStr = name0.description!;

View File

@ -1,4 +1,4 @@
import { Dictionary, KeyedSet, FlexSet, Position, stringify } from "@preserves/core";
import { KeyedSet, FlexSet, Position, stringify, DictionaryMap, JsDictionary } from "@preserves/core";
import { refPosition } from "../reader";
import * as M from "../meta";
import { anglebrackets, block, braces, commas, formatItems, Item, keyvalue, seq } from "./block";
@ -27,11 +27,11 @@ export class ModuleContext {
readonly options: CompilerOptions;
readonly embeddedType: Item;
readonly literals = new Dictionary<M.InputEmbedded, string>();
readonly literals = new DictionaryMap<M.InputEmbedded, string>();
readonly preamble: Item[] = [];
readonly typedefs: Item[] = [];
readonly functiondefs: Item[] = [];
readonly imports = new KeyedSet<[M.ModulePath, string, string, string]>();
readonly imports = new KeyedSet<M.InputEmbedded, [M.ModulePath, string, string, string]>();
constructor(
env: M.Environment,
@ -137,7 +137,7 @@ export class ModuleContext {
null,
null);
} else {
const p = e.schema.definitions.get(name.name);
const p = JsDictionary.get(e.schema.definitions, name.name);
if (p !== void 0) {
let t = () => typeForDefinition(this.resolver(soughtModule), p);
if (name.module.length) {
@ -158,7 +158,7 @@ export class ModuleContext {
}
genericParameters(): Item {
return anglebrackets(seq('_embedded = ', this.embeddedType));
return anglebrackets(seq('_embedded extends _.Embeddable = ', this.embeddedType));
}
genericParametersFor(t: Type): Item {

View File

@ -2,7 +2,7 @@ import { FunctionContext } from "./context";
import * as M from '../meta';
import { Item, seq, parens, anglebrackets } from "./block";
import { simpleType, typeFor } from "./gentype";
import { ANY_TYPE, Type } from "./type";
import { ANY_TYPE, isSymbolType, Type } from "./type";
import { renderType } from "./rendertype";
export function converterForDefinition(
@ -87,7 +87,7 @@ function converterForTuple(ctx: FunctionContext,
}
const lengthCheck = variablePattern === void 0
? seq(` && ${src}.length === ${ps.length}`)
? seq(` && ${src}.length >= ${ps.length}`)
: ((ps.length === 0) ? '' : seq(` && ${src}.length >= ${ps.length}`));
return knownArray
@ -163,7 +163,7 @@ export function converterForSimple(
return [seq(`${dest} = `, test, ` ? `, valexp, ` : void 0`)];
}
case 'embedded':
return [`${dest} = _.isEmbedded<_embedded>(${src}) ? ${src}.embeddedValue : void 0`];
return [`${dest} = _.isEmbedded<_embedded>(${src}) ? ${src} : void 0`];
case 'lit':
return [`${dest} = _.is(${src}, ${ctx.mod.literal(p.value)}) ? {} : void 0`];
@ -192,7 +192,7 @@ export function converterForSimple(
const v = ctx.gentempname();
return [
seq(`${dest} = new _.EncodableSet`,
anglebrackets(renderType(ctx.mod, vt), '_embedded'),
anglebrackets('_embedded', renderType(ctx.mod, vt)),
parens(encoderForSimplePattern(ctx, p.pattern) ?? `v => v`)),
seq(`for (const ${v} of ${src}) `, ctx.block(() => [
... converterFor(ctx, M.anonymousSimplePattern(p.pattern), v, vv =>
@ -203,20 +203,30 @@ export function converterForSimple(
case 'dictof':
return [`${dest} = void 0`,
seq(`if (_.Dictionary.isDictionary<_embedded>(${src})) `, ctx.block(() => {
const srcMap = ctx.gentempname();
const resolver = ctx.mod.resolver();
const kt = simpleType(resolver, p.key);
const vt = simpleType(resolver, p.value);
const k = ctx.gentempname();
const v = ctx.gentempname();
const symbolKeyed = isSymbolType(kt);
return [
seq(`${dest} = new _.EncodableDictionary`,
anglebrackets(renderType(ctx.mod, kt), renderType(ctx.mod, vt), '_embedded'),
parens(encoderForSimplePattern(ctx, p.key) ?? `k => k`,
encoderForSimplePattern(ctx, p.value) ?? `v => v`)),
seq(`for (const [${k}, ${v}] of ${src}) `, ctx.block(() => [
seq(`const ${srcMap} = new _.DictionaryMap(${src})`),
(symbolKeyed
? seq(`${dest} = {}`)
: seq(`${dest} = new _.EncodableDictionary`,
anglebrackets('_embedded', renderType(ctx.mod, kt), renderType(ctx.mod, vt)),
parens(encoderForSimplePattern(ctx, p.key) ?? `k => k`,
encoderForSimplePattern(ctx, p.value) ?? `v => v`))),
seq(`for (const [${k}, ${v}] of ${srcMap}) `, ctx.block(() => [
... converterFor(ctx, M.anonymousSimplePattern(p.key), k, kk =>
converterFor(ctx, M.anonymousSimplePattern(p.value), v, vv =>
[`${dest}.set(${kk}, ${vv})`, `continue`])),
[
(symbolKeyed
? `${dest}[${kk}.description!] = ${vv}`
: `${dest}.set(${kk}, ${vv})`),
`continue`
])),
seq(`${dest} = void 0`),
seq(`break`)]))];
}))];
@ -251,12 +261,13 @@ function converterForCompound(
case 'tuplePrefix':
return converterForTuple(ctx, p.fixed, src, knownArray, p.variable, ks);
case 'dict': {
const srcMap = ctx.gentempname();
const entries = Array.from(p.entries);
function loop(i: number): Item[] {
if (i < entries.length) {
const [k, n] = entries[i];
const tmpSrc = ctx.gentemp();
return [seq(`if ((${tmpSrc} = ${src}.get(${ctx.mod.literal(k)})) !== void 0) `,
return [seq(`if ((${tmpSrc} = ${srcMap}.get(${ctx.mod.literal(k)})) !== void 0) `,
ctx.block(() =>
converterFor(
ctx,
@ -267,7 +278,9 @@ function converterForCompound(
return ks();
}
}
return [seq(`if (_.Dictionary.isDictionary<_embedded>(${src})) `, ctx.block(() => loop(0)))];
return [seq(`if (_.Dictionary.isDictionary<_embedded>(${src})) `, ctx.block(() => [
seq(`const ${srcMap} = new _.DictionaryMap(${src})`),
... loop(0)]))];
}
default:
((_p: never) => {})(p);

View File

@ -46,7 +46,7 @@ function unconverterFor(ctx: FunctionContext, p: M.Pattern, src: string): Item {
case 'lit':
return ctx.mod.literal(p.value);
case 'embedded':
return `_.embed(${src})`;
return `${src}`;
case 'seqof':
return seq(`${src}.map(v => `,
unconverterFor(ctx, M.Pattern.SimplePattern(p.pattern), 'v'),
@ -57,8 +57,10 @@ function unconverterFor(ctx: FunctionContext, p: M.Pattern, src: string): Item {
unconverterFor(ctx, M.Pattern.SimplePattern(p.pattern), 'v'),
`)`)));
case 'dictof':
return seq(`new _.Dictionary<_embedded>`, parens(seq(
`_.Array.from(${src}.entries()).map(([k, v]) => `,
return seq(`_.Dictionary.from<_embedded>`, parens(seq(
`_.Array.from(`,
M.isSymbolPattern(p.key) ? `_.JsDictionary.entries(${src})` : `${src}.entries()`,
`).map(([k, v]) => `,
brackets(
unconverterFor(ctx, M.Pattern.SimplePattern(p.key), 'k'),
unconverterFor(ctx, M.Pattern.SimplePattern(p.value), 'v')),
@ -95,7 +97,7 @@ function unconverterFor(ctx: FunctionContext, p: M.Pattern, src: string): Item {
}
}
case 'dict':
return seq(`new _.Dictionary<_embedded>`, parens(
return seq(`_.Dictionary.from<_embedded>`, parens(
brackets(... Array.from(p.entries.entries()).map(([k, n]) =>
brackets(
ctx.mod.literal(k),

View File

@ -1,4 +1,4 @@
import { SimpleType, Type } from "./type";
import { isSymbolType, SimpleType, Type } from "./type";
import { anglebrackets, braces, Item, keyvalue, opseq, seq } from "./block";
import { ModuleContext } from "./context";
@ -47,12 +47,17 @@ export function renderType(ctxt: ModuleContext, t: Type): Item {
return seq(t.typeName, ctxt.genericArgsFor(t));
}
case 'set': return seq('_.EncodableSet', anglebrackets(
renderType(ctxt, t.type),
'_embedded'));
case 'dictionary': return seq('_.EncodableDictionary', anglebrackets(
renderType(ctxt, t.key),
renderType(ctxt, t.value),
'_embedded'));
'_embedded',
renderType(ctxt, t.type)));
case 'dictionary':
if (isSymbolType(t.key)) {
return seq('_.JsDictionary', anglebrackets(renderType(ctxt, t.value)));
} else {
return seq('_.EncodableDictionary', anglebrackets(
'_embedded',
renderType(ctxt, t.key),
renderType(ctxt, t.value)));
}
case 'array': return seq('Array', anglebrackets(renderType(ctxt, t.type)));
case 'record': return braces(... simpleTypeFields(ctxt, t));
default:

View File

@ -35,3 +35,7 @@ export namespace Type {
}
export const ANY_TYPE: FieldType = Type.ref('_.Value', null);
export function isSymbolType(ty: FieldType): ty is { kind: 'ref', typeName: 'symbol', ref: null } {
return ty.kind === 'ref' && ty.typeName === 'symbol' && ty.ref === null;
}

View File

@ -1,4 +1,4 @@
import { Annotated, Bytes, Set, Dictionary, Fold, fold, Record, Tuple, Value, stringify, Embedded } from "@preserves/core";
import { Annotated, Bytes, Set, Fold, fold, Record, Tuple, Value, stringify, DictionaryMap } from "@preserves/core";
import { brackets, Item, parens, seq } from "./block";
import * as M from '../meta';
@ -22,8 +22,8 @@ export function sourceCodeFor(v: Value<M.InputEmbedded>): Item {
set(s: Set<M.InputEmbedded>, k: Fold<M.InputEmbedded, Item>): Item {
return seq('new _.Set<_.Value<_embedded>>', parens(brackets(... Array.from(s).map(k))));
},
dictionary(d: Dictionary<M.InputEmbedded>, k: Fold<M.InputEmbedded, Item>): Item {
return seq('new _.Dictionary<_embedded>', parens(brackets(... Array.from(d).map(([kk,vv]) =>
dictionary(d: DictionaryMap<M.InputEmbedded>, k: Fold<M.InputEmbedded, Item>): Item {
return seq('_.Dictionary.from<_embedded>', parens(brackets(... Array.from(d).map(([kk,vv]) =>
brackets(k(kk), k(vv))))));
},
@ -31,7 +31,7 @@ export function sourceCodeFor(v: Value<M.InputEmbedded>): Item {
return seq('_.annotate<_embedded>', parens(k(a.item), ... a.annotations.map(k)));
},
embedded(t: Embedded<M.InputEmbedded>, _k: Fold<M.InputEmbedded, Item>): Item {
embedded(t: M.InputEmbedded, _k: Fold<M.InputEmbedded, Item>): Item {
throw new Error(`Cannot emit source code for construction of embedded ${stringify(t)}`);
},
});

View File

@ -30,14 +30,18 @@ export type Definition = (
) &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type Variant = (
{"label": symbol, "type": Simple} &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type Simple = (
@ -47,21 +51,27 @@ export type Simple = (
) &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type Record = (
{"fields": Array<NamedField>} &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type NamedField = (
{"name": symbol, "type": Field} &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type Field = (
@ -77,7 +87,9 @@ export type Field = (
) &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
@ -337,13 +349,13 @@ export namespace Field {
};
}
export function asDefinition<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Definition {
export function asDefinition<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Definition {
let result = toDefinition(v);
if (result === void 0) throw new TypeError(`Invalid Definition: ${_.stringify(v)}`);
return result;
}
export function toDefinition<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Definition {
export function toDefinition<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Definition {
let result: undefined | Definition;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -390,22 +402,22 @@ export function toDefinition<_embedded = _.GenericEmbedded>(v: _.Value<_embedded
export namespace Definition {export const __from_preserve__ = toDefinition;}
export function fromDefinition<_embedded = _.GenericEmbedded>(_v: Definition): _.Value<_embedded> {
export function fromDefinition<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Definition): _.Value<_embedded> {
switch (_v._variant) {
case "union": {return _.Record($union, [_v["variants"].map(v => fromVariant<_embedded>(v))]);};
case "Simple": {return fromSimple<_embedded>(_v.value);};
};
}
export function asVariant<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Variant {
export function asVariant<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Variant {
let result = toVariant(v);
if (result === void 0) throw new TypeError(`Invalid Variant: ${_.stringify(v)}`);
return result;
}
export function toVariant<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Variant {
export function toVariant<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Variant {
let result: undefined | Variant;
if (_.isSequence(v) && v.length === 2) {
if (_.isSequence(v) && v.length >= 2) {
let _tmp0: (symbol) | undefined;
_tmp0 = typeof v[0] === 'symbol' ? v[0] : void 0;
if (_tmp0 !== void 0) {
@ -427,15 +439,15 @@ export function toVariant<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Variant.__from_preserve__ = toVariant;
export function fromVariant<_embedded = _.GenericEmbedded>(_v: Variant): _.Value<_embedded> {return [_v["label"], fromSimple<_embedded>(_v["type"])];}
export function fromVariant<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Variant): _.Value<_embedded> {return [_v["label"], fromSimple<_embedded>(_v["type"])];}
export function asSimple<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Simple {
export function asSimple<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Simple {
let result = toSimple(v);
if (result === void 0) throw new TypeError(`Invalid Simple: ${_.stringify(v)}`);
return result;
}
export function toSimple<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Simple {
export function toSimple<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Simple {
let _tmp0: (Field) | undefined;
let result: undefined | Simple;
_tmp0 = toField(v);
@ -466,20 +478,20 @@ export function toSimple<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
export namespace Simple {export const __from_preserve__ = toSimple;}
export function fromSimple<_embedded = _.GenericEmbedded>(_v: Simple): _.Value<_embedded> {
export function fromSimple<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Simple): _.Value<_embedded> {
switch (_v._variant) {
case "Field": {return fromField<_embedded>(_v.value);};
case "Record": {return fromRecord<_embedded>(_v.value);};
};
}
export function asRecord<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Record {
export function asRecord<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Record {
let result = toRecord(v);
if (result === void 0) throw new TypeError(`Invalid Record: ${_.stringify(v)}`);
return result;
}
export function toRecord<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Record {
export function toRecord<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Record {
let result: undefined | Record;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -512,17 +524,17 @@ export function toRecord<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Record.__from_preserve__ = toRecord;
export function fromRecord<_embedded = _.GenericEmbedded>(_v: Record): _.Value<_embedded> {return _.Record($rec, [_v["fields"].map(v => fromNamedField<_embedded>(v))]);}
export function fromRecord<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Record): _.Value<_embedded> {return _.Record($rec, [_v["fields"].map(v => fromNamedField<_embedded>(v))]);}
export function asNamedField<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): NamedField {
export function asNamedField<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): NamedField {
let result = toNamedField(v);
if (result === void 0) throw new TypeError(`Invalid NamedField: ${_.stringify(v)}`);
return result;
}
export function toNamedField<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedField {
export function toNamedField<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedField {
let result: undefined | NamedField;
if (_.isSequence(v) && v.length === 2) {
if (_.isSequence(v) && v.length >= 2) {
let _tmp0: (symbol) | undefined;
_tmp0 = typeof v[0] === 'symbol' ? v[0] : void 0;
if (_tmp0 !== void 0) {
@ -544,15 +556,15 @@ export function toNamedField<_embedded = _.GenericEmbedded>(v: _.Value<_embedded
NamedField.__from_preserve__ = toNamedField;
export function fromNamedField<_embedded = _.GenericEmbedded>(_v: NamedField): _.Value<_embedded> {return [_v["name"], fromField<_embedded>(_v["type"])];}
export function fromNamedField<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: NamedField): _.Value<_embedded> {return [_v["name"], fromField<_embedded>(_v["type"])];}
export function asField<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Field {
export function asField<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Field {
let result = toField(v);
if (result === void 0) throw new TypeError(`Invalid Field: ${_.stringify(v)}`);
return result;
}
export function toField<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Field {
export function toField<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Field {
let _tmp0: ({}) | undefined;
let result: undefined | Field;
_tmp0 = _.is(v, $unit) ? {} : void 0;
@ -687,7 +699,7 @@ export function toField<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): u
export namespace Field {export const __from_preserve__ = toField;}
export function fromField<_embedded = _.GenericEmbedded>(_v: Field): _.Value<_embedded> {
export function fromField<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Field): _.Value<_embedded> {
switch (_v._variant) {
case "unit": {return $unit;};
case "any": {return $any;};

View File

@ -41,16 +41,16 @@ export function _schema() {
export const _imports = {}
export type Bundle<_embedded = _.GenericEmbedded> = (
export type Bundle<_embedded extends _.Embeddable = _.GenericEmbedded> = (
{"modules": Modules<_embedded>} &
_.Preservable<_embedded> &
_.PreserveWritable<_embedded> &
{__as_preserve__(): _.Value<_embedded>}
);
export type Modules<_embedded = _.GenericEmbedded> = _.EncodableDictionary<ModulePath, Schema<_embedded>, _embedded>;
export type Modules<_embedded extends _.Embeddable = _.GenericEmbedded> = _.EncodableDictionary<_embedded, ModulePath, Schema<_embedded>>;
export type Schema<_embedded = _.GenericEmbedded> = (
export type Schema<_embedded extends _.Embeddable = _.GenericEmbedded> = (
{
"version": Version,
"embeddedType": EmbeddedTypeName,
@ -65,19 +65,23 @@ export type Version = (
{} &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type EmbeddedTypeName = (
({"_variant": "false"} | {"_variant": "Ref", "value": Ref}) &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type Definitions<_embedded = _.GenericEmbedded> = _.EncodableDictionary<symbol, Definition<_embedded>, _embedded>;
export type Definitions<_embedded extends _.Embeddable = _.GenericEmbedded> = _.JsDictionary<Definition<_embedded>>;
export type Definition<_embedded = _.GenericEmbedded> = (
export type Definition<_embedded extends _.Embeddable = _.GenericEmbedded> = (
(
{
"_variant": "or",
@ -98,7 +102,7 @@ export type Definition<_embedded = _.GenericEmbedded> = (
{__as_preserve__(): _.Value<_embedded>}
);
export type Pattern<_embedded = _.GenericEmbedded> = (
export type Pattern<_embedded extends _.Embeddable = _.GenericEmbedded> = (
(
{"_variant": "SimplePattern", "value": SimplePattern<_embedded>} |
{"_variant": "CompoundPattern", "value": CompoundPattern<_embedded>}
@ -108,7 +112,7 @@ export type Pattern<_embedded = _.GenericEmbedded> = (
{__as_preserve__(): _.Value<_embedded>}
);
export type SimplePattern<_embedded = _.GenericEmbedded> = (
export type SimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded> = (
(
{"_variant": "any"} |
{"_variant": "atom", "atomKind": AtomKind} |
@ -128,7 +132,7 @@ export type SimplePattern<_embedded = _.GenericEmbedded> = (
{__as_preserve__(): _.Value<_embedded>}
);
export type CompoundPattern<_embedded = _.GenericEmbedded> = (
export type CompoundPattern<_embedded extends _.Embeddable = _.GenericEmbedded> = (
(
{
"_variant": "rec",
@ -148,7 +152,7 @@ export type CompoundPattern<_embedded = _.GenericEmbedded> = (
{__as_preserve__(): _.Value<_embedded>}
);
export type DictionaryEntries<_embedded = _.GenericEmbedded> = _.EncodableDictionary<_.Value<_embedded>, NamedSimplePattern<_embedded>, _embedded>;
export type DictionaryEntries<_embedded extends _.Embeddable = _.GenericEmbedded> = _.EncodableDictionary<_embedded, _.Value<_embedded>, NamedSimplePattern<_embedded>>;
export type AtomKind = (
(
@ -161,17 +165,19 @@ export type AtomKind = (
) &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type NamedAlternative<_embedded = _.GenericEmbedded> = (
export type NamedAlternative<_embedded extends _.Embeddable = _.GenericEmbedded> = (
{"variantLabel": string, "pattern": Pattern<_embedded>} &
_.Preservable<_embedded> &
_.PreserveWritable<_embedded> &
{__as_preserve__(): _.Value<_embedded>}
);
export type NamedSimplePattern<_embedded = _.GenericEmbedded> = (
export type NamedSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded> = (
(
{"_variant": "named", "value": Binding<_embedded>} |
{"_variant": "anonymous", "value": SimplePattern<_embedded>}
@ -181,7 +187,7 @@ export type NamedSimplePattern<_embedded = _.GenericEmbedded> = (
{__as_preserve__(): _.Value<_embedded>}
);
export type NamedPattern<_embedded = _.GenericEmbedded> = (
export type NamedPattern<_embedded extends _.Embeddable = _.GenericEmbedded> = (
(
{"_variant": "named", "value": Binding<_embedded>} |
{"_variant": "anonymous", "value": Pattern<_embedded>}
@ -191,7 +197,7 @@ export type NamedPattern<_embedded = _.GenericEmbedded> = (
{__as_preserve__(): _.Value<_embedded>}
);
export type Binding<_embedded = _.GenericEmbedded> = (
export type Binding<_embedded extends _.Embeddable = _.GenericEmbedded> = (
{"name": symbol, "pattern": SimplePattern<_embedded>} &
_.Preservable<_embedded> &
_.PreserveWritable<_embedded> &
@ -202,13 +208,15 @@ export type Ref = (
{"module": ModulePath, "name": symbol} &
_.Preservable<any> &
_.PreserveWritable<any> &
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
{
__as_preserve__<_embedded extends _.Embeddable = _.GenericEmbedded>(): _.Value<_embedded>
}
);
export type ModulePath = Array<symbol>;
export function Bundle<_embedded = _.GenericEmbedded>(modules: Modules<_embedded>): Bundle<_embedded> {
export function Bundle<_embedded extends _.Embeddable = _.GenericEmbedded>(modules: Modules<_embedded>): Bundle<_embedded> {
return {
"modules": modules,
__as_preserve__() {return fromBundle(this);},
@ -221,13 +229,13 @@ Bundle.schema = function () {
return {schema: _schema(), imports: _imports, definitionName: _.Symbol.for("Bundle")};
}
export function Modules<_embedded = _.GenericEmbedded>(value: _.EncodableDictionary<ModulePath, Schema<_embedded>, _embedded>): Modules<_embedded> {return value;}
export function Modules<_embedded extends _.Embeddable = _.GenericEmbedded>(value: _.EncodableDictionary<_embedded, ModulePath, Schema<_embedded>>): Modules<_embedded> {return value;}
Modules.schema = function () {
return {schema: _schema(), imports: _imports, definitionName: _.Symbol.for("Modules")};
}
export function Schema<_embedded = _.GenericEmbedded>(
export function Schema<_embedded extends _.Embeddable = _.GenericEmbedded>(
{version, embeddedType, definitions}: {
version: Version,
embeddedType: EmbeddedTypeName,
@ -296,7 +304,7 @@ export namespace EmbeddedTypeName {
};
}
export function Definitions<_embedded = _.GenericEmbedded>(value: _.EncodableDictionary<symbol, Definition<_embedded>, _embedded>): Definitions<_embedded> {return value;}
export function Definitions<_embedded extends _.Embeddable = _.GenericEmbedded>(value: _.JsDictionary<Definition<_embedded>>): Definitions<_embedded> {return value;}
Definitions.schema = function () {
return {
@ -307,7 +315,7 @@ Definitions.schema = function () {
}
export namespace Definition {
export function or<_embedded = _.GenericEmbedded>(
export function or<_embedded extends _.Embeddable = _.GenericEmbedded>(
{pattern0, pattern1, patternN}: {
pattern0: NamedAlternative<_embedded>,
pattern1: NamedAlternative<_embedded>,
@ -332,7 +340,7 @@ export namespace Definition {
variant: _.Symbol.for("or")
};
};
export function and<_embedded = _.GenericEmbedded>(
export function and<_embedded extends _.Embeddable = _.GenericEmbedded>(
{pattern0, pattern1, patternN}: {
pattern0: NamedPattern<_embedded>,
pattern1: NamedPattern<_embedded>,
@ -357,7 +365,7 @@ export namespace Definition {
variant: _.Symbol.for("and")
};
};
export function Pattern<_embedded = _.GenericEmbedded>(value: Pattern<_embedded>): Definition<_embedded> {
export function Pattern<_embedded extends _.Embeddable = _.GenericEmbedded>(value: Pattern<_embedded>): Definition<_embedded> {
return {
"_variant": "Pattern",
"value": value,
@ -377,7 +385,7 @@ export namespace Definition {
}
export namespace Pattern {
export function SimplePattern<_embedded = _.GenericEmbedded>(value: SimplePattern<_embedded>): Pattern<_embedded> {
export function SimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(value: SimplePattern<_embedded>): Pattern<_embedded> {
return {
"_variant": "SimplePattern",
"value": value,
@ -394,7 +402,7 @@ export namespace Pattern {
variant: _.Symbol.for("SimplePattern")
};
};
export function CompoundPattern<_embedded = _.GenericEmbedded>(value: CompoundPattern<_embedded>): Pattern<_embedded> {
export function CompoundPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(value: CompoundPattern<_embedded>): Pattern<_embedded> {
return {
"_variant": "CompoundPattern",
"value": value,
@ -414,7 +422,7 @@ export namespace Pattern {
}
export namespace SimplePattern {
export function any<_embedded = _.GenericEmbedded>(): SimplePattern<_embedded> {
export function any<_embedded extends _.Embeddable = _.GenericEmbedded>(): SimplePattern<_embedded> {
return {
"_variant": "any",
__as_preserve__() {return fromSimplePattern(this);},
@ -430,7 +438,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("any")
};
};
export function atom<_embedded = _.GenericEmbedded>(atomKind: AtomKind): SimplePattern<_embedded> {
export function atom<_embedded extends _.Embeddable = _.GenericEmbedded>(atomKind: AtomKind): SimplePattern<_embedded> {
return {
"_variant": "atom",
"atomKind": atomKind,
@ -447,7 +455,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("atom")
};
};
export function embedded<_embedded = _.GenericEmbedded>($interface: SimplePattern<_embedded>): SimplePattern<_embedded> {
export function embedded<_embedded extends _.Embeddable = _.GenericEmbedded>($interface: SimplePattern<_embedded>): SimplePattern<_embedded> {
return {
"_variant": "embedded",
"interface": $interface,
@ -464,7 +472,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("embedded")
};
};
export function lit<_embedded = _.GenericEmbedded>(value: _.Value<_embedded>): SimplePattern<_embedded> {
export function lit<_embedded extends _.Embeddable = _.GenericEmbedded>(value: _.Value<_embedded>): SimplePattern<_embedded> {
return {
"_variant": "lit",
"value": value,
@ -481,7 +489,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("lit")
};
};
export function seqof<_embedded = _.GenericEmbedded>(pattern: SimplePattern<_embedded>): SimplePattern<_embedded> {
export function seqof<_embedded extends _.Embeddable = _.GenericEmbedded>(pattern: SimplePattern<_embedded>): SimplePattern<_embedded> {
return {
"_variant": "seqof",
"pattern": pattern,
@ -498,7 +506,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("seqof")
};
};
export function setof<_embedded = _.GenericEmbedded>(pattern: SimplePattern<_embedded>): SimplePattern<_embedded> {
export function setof<_embedded extends _.Embeddable = _.GenericEmbedded>(pattern: SimplePattern<_embedded>): SimplePattern<_embedded> {
return {
"_variant": "setof",
"pattern": pattern,
@ -515,7 +523,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("setof")
};
};
export function dictof<_embedded = _.GenericEmbedded>({key, value}: {key: SimplePattern<_embedded>, value: SimplePattern<_embedded>}): SimplePattern<_embedded> {
export function dictof<_embedded extends _.Embeddable = _.GenericEmbedded>({key, value}: {key: SimplePattern<_embedded>, value: SimplePattern<_embedded>}): SimplePattern<_embedded> {
return {
"_variant": "dictof",
"key": key,
@ -533,7 +541,7 @@ export namespace SimplePattern {
variant: _.Symbol.for("dictof")
};
};
export function Ref<_embedded = _.GenericEmbedded>(value: Ref): SimplePattern<_embedded> {
export function Ref<_embedded extends _.Embeddable = _.GenericEmbedded>(value: Ref): SimplePattern<_embedded> {
return {
"_variant": "Ref",
"value": value,
@ -553,7 +561,7 @@ export namespace SimplePattern {
}
export namespace CompoundPattern {
export function rec<_embedded = _.GenericEmbedded>(
export function rec<_embedded extends _.Embeddable = _.GenericEmbedded>(
{label, fields}: {label: NamedPattern<_embedded>, fields: NamedPattern<_embedded>}
): CompoundPattern<_embedded> {
return {
@ -573,7 +581,7 @@ export namespace CompoundPattern {
variant: _.Symbol.for("rec")
};
};
export function tuple<_embedded = _.GenericEmbedded>(patterns: Array<NamedPattern<_embedded>>): CompoundPattern<_embedded> {
export function tuple<_embedded extends _.Embeddable = _.GenericEmbedded>(patterns: Array<NamedPattern<_embedded>>): CompoundPattern<_embedded> {
return {
"_variant": "tuple",
"patterns": patterns,
@ -590,7 +598,7 @@ export namespace CompoundPattern {
variant: _.Symbol.for("tuple")
};
};
export function tuplePrefix<_embedded = _.GenericEmbedded>(
export function tuplePrefix<_embedded extends _.Embeddable = _.GenericEmbedded>(
{fixed, variable}: {fixed: Array<NamedPattern<_embedded>>, variable: NamedSimplePattern<_embedded>}
): CompoundPattern<_embedded> {
return {
@ -610,7 +618,7 @@ export namespace CompoundPattern {
variant: _.Symbol.for("tuplePrefix")
};
};
export function dict<_embedded = _.GenericEmbedded>(entries: DictionaryEntries<_embedded>): CompoundPattern<_embedded> {
export function dict<_embedded extends _.Embeddable = _.GenericEmbedded>(entries: DictionaryEntries<_embedded>): CompoundPattern<_embedded> {
return {
"_variant": "dict",
"entries": entries,
@ -629,8 +637,8 @@ export namespace CompoundPattern {
};
}
export function DictionaryEntries<_embedded = _.GenericEmbedded>(
value: _.EncodableDictionary<_.Value<_embedded>, NamedSimplePattern<_embedded>, _embedded>
export function DictionaryEntries<_embedded extends _.Embeddable = _.GenericEmbedded>(
value: _.EncodableDictionary<_embedded, _.Value<_embedded>, NamedSimplePattern<_embedded>>
): DictionaryEntries<_embedded> {return value;}
DictionaryEntries.schema = function () {
@ -740,7 +748,7 @@ export namespace AtomKind {
};
}
export function NamedAlternative<_embedded = _.GenericEmbedded>({variantLabel, pattern}: {variantLabel: string, pattern: Pattern<_embedded>}): NamedAlternative<_embedded> {
export function NamedAlternative<_embedded extends _.Embeddable = _.GenericEmbedded>({variantLabel, pattern}: {variantLabel: string, pattern: Pattern<_embedded>}): NamedAlternative<_embedded> {
return {
"variantLabel": variantLabel,
"pattern": pattern,
@ -759,7 +767,7 @@ NamedAlternative.schema = function () {
}
export namespace NamedSimplePattern {
export function named<_embedded = _.GenericEmbedded>(value: Binding<_embedded>): NamedSimplePattern<_embedded> {
export function named<_embedded extends _.Embeddable = _.GenericEmbedded>(value: Binding<_embedded>): NamedSimplePattern<_embedded> {
return {
"_variant": "named",
"value": value,
@ -776,7 +784,7 @@ export namespace NamedSimplePattern {
variant: _.Symbol.for("named")
};
};
export function anonymous<_embedded = _.GenericEmbedded>(value: SimplePattern<_embedded>): NamedSimplePattern<_embedded> {
export function anonymous<_embedded extends _.Embeddable = _.GenericEmbedded>(value: SimplePattern<_embedded>): NamedSimplePattern<_embedded> {
return {
"_variant": "anonymous",
"value": value,
@ -796,7 +804,7 @@ export namespace NamedSimplePattern {
}
export namespace NamedPattern {
export function named<_embedded = _.GenericEmbedded>(value: Binding<_embedded>): NamedPattern<_embedded> {
export function named<_embedded extends _.Embeddable = _.GenericEmbedded>(value: Binding<_embedded>): NamedPattern<_embedded> {
return {
"_variant": "named",
"value": value,
@ -813,7 +821,7 @@ export namespace NamedPattern {
variant: _.Symbol.for("named")
};
};
export function anonymous<_embedded = _.GenericEmbedded>(value: Pattern<_embedded>): NamedPattern<_embedded> {
export function anonymous<_embedded extends _.Embeddable = _.GenericEmbedded>(value: Pattern<_embedded>): NamedPattern<_embedded> {
return {
"_variant": "anonymous",
"value": value,
@ -832,7 +840,7 @@ export namespace NamedPattern {
};
}
export function Binding<_embedded = _.GenericEmbedded>({name, pattern}: {name: symbol, pattern: SimplePattern<_embedded>}): Binding<_embedded> {
export function Binding<_embedded extends _.Embeddable = _.GenericEmbedded>({name, pattern}: {name: symbol, pattern: SimplePattern<_embedded>}): Binding<_embedded> {
return {
"name": name,
"pattern": pattern,
@ -870,13 +878,13 @@ ModulePath.schema = function () {
};
}
export function asBundle<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Bundle<_embedded> {
export function asBundle<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Bundle<_embedded> {
let result = toBundle(v);
if (result === void 0) throw new TypeError(`Invalid Bundle: ${_.stringify(v)}`);
return result;
}
export function toBundle<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Bundle<_embedded> {
export function toBundle<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Bundle<_embedded> {
let result: undefined | Bundle<_embedded>;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -899,27 +907,28 @@ export function toBundle<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Bundle.__from_preserve__ = toBundle;
export function fromBundle<_embedded = _.GenericEmbedded>(_v: Bundle<_embedded>): _.Value<_embedded> {return _.Record($bundle, [fromModules<_embedded>(_v["modules"])]);}
export function fromBundle<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Bundle<_embedded>): _.Value<_embedded> {return _.Record($bundle, [fromModules<_embedded>(_v["modules"])]);}
export function asModules<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Modules<_embedded> {
export function asModules<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Modules<_embedded> {
let result = toModules(v);
if (result === void 0) throw new TypeError(`Invalid Modules: ${_.stringify(v)}`);
return result;
}
export function toModules<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Modules<_embedded> {
let _tmp0: (_.EncodableDictionary<ModulePath, Schema<_embedded>, _embedded>) | undefined;
export function toModules<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Modules<_embedded> {
let _tmp0: (_.EncodableDictionary<_embedded, ModulePath, Schema<_embedded>>) | undefined;
let result: undefined | Modules<_embedded>;
_tmp0 = void 0;
if (_.Dictionary.isDictionary<_embedded>(v)) {
_tmp0 = new _.EncodableDictionary<ModulePath, Schema<_embedded>, _embedded>(fromModulePath, fromSchema<_embedded>);
for (const [_tmp1, _tmp2] of v) {
let _tmp3: (ModulePath) | undefined;
_tmp3 = toModulePath(_tmp1);
if (_tmp3 !== void 0) {
let _tmp4: (Schema<_embedded>) | undefined;
_tmp4 = toSchema(_tmp2);
if (_tmp4 !== void 0) {_tmp0.set(_tmp3, _tmp4); continue;};
const _tmp1 = new _.DictionaryMap(v);
_tmp0 = new _.EncodableDictionary<_embedded, ModulePath, Schema<_embedded>>(fromModulePath, fromSchema<_embedded>);
for (const [_tmp2, _tmp3] of _tmp1) {
let _tmp4: (ModulePath) | undefined;
_tmp4 = toModulePath(_tmp2);
if (_tmp4 !== void 0) {
let _tmp5: (Schema<_embedded>) | undefined;
_tmp5 = toSchema(_tmp3);
if (_tmp5 !== void 0) {_tmp0.set(_tmp4, _tmp5); continue;};
};
_tmp0 = void 0;
break;
@ -931,44 +940,45 @@ export function toModules<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Modules.__from_preserve__ = toModules;
export function fromModules<_embedded = _.GenericEmbedded>(_v: Modules<_embedded>): _.Value<_embedded> {
return new _.Dictionary<_embedded>(
export function fromModules<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Modules<_embedded>): _.Value<_embedded> {
return _.Dictionary.from<_embedded>(
_.Array.from(_v.entries()).map(([k, v]) => [fromModulePath<_embedded>(k), fromSchema<_embedded>(v)])
);
}
export function asSchema<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Schema<_embedded> {
export function asSchema<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Schema<_embedded> {
let result = toSchema(v);
if (result === void 0) throw new TypeError(`Invalid Schema: ${_.stringify(v)}`);
return result;
}
export function toSchema<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Schema<_embedded> {
export function toSchema<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Schema<_embedded> {
let result: undefined | Schema<_embedded>;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
_tmp0 = _.is(v.label, $schema) ? {} : void 0;
if (_tmp0 !== void 0) {
if (_.Dictionary.isDictionary<_embedded>(v[0])) {
let _tmp1: (_.Value<_embedded>) | undefined;
if ((_tmp1 = v[0].get($version)) !== void 0) {
let _tmp2: (Version) | undefined;
_tmp2 = toVersion(_tmp1);
if (_tmp2 !== void 0) {
let _tmp3: (_.Value<_embedded>) | undefined;
if ((_tmp3 = v[0].get($embeddedType)) !== void 0) {
let _tmp4: (EmbeddedTypeName) | undefined;
_tmp4 = toEmbeddedTypeName(_tmp3);
if (_tmp4 !== void 0) {
let _tmp5: (_.Value<_embedded>) | undefined;
if ((_tmp5 = v[0].get($definitions)) !== void 0) {
let _tmp6: (Definitions<_embedded>) | undefined;
_tmp6 = toDefinitions(_tmp5);
if (_tmp6 !== void 0) {
let _tmp2: (_.Value<_embedded>) | undefined;
const _tmp1 = new _.DictionaryMap(v[0]);
if ((_tmp2 = _tmp1.get($version)) !== void 0) {
let _tmp3: (Version) | undefined;
_tmp3 = toVersion(_tmp2);
if (_tmp3 !== void 0) {
let _tmp4: (_.Value<_embedded>) | undefined;
if ((_tmp4 = _tmp1.get($embeddedType)) !== void 0) {
let _tmp5: (EmbeddedTypeName) | undefined;
_tmp5 = toEmbeddedTypeName(_tmp4);
if (_tmp5 !== void 0) {
let _tmp6: (_.Value<_embedded>) | undefined;
if ((_tmp6 = _tmp1.get($definitions)) !== void 0) {
let _tmp7: (Definitions<_embedded>) | undefined;
_tmp7 = toDefinitions(_tmp6);
if (_tmp7 !== void 0) {
result = {
"version": _tmp2,
"embeddedType": _tmp4,
"definitions": _tmp6,
"version": _tmp3,
"embeddedType": _tmp5,
"definitions": _tmp7,
__as_preserve__() {return fromSchema(this);},
__preserve_on__(e) { e.push(fromSchema(this)); },
__preserve_text_on__(w) { w.push(fromSchema(this)); }
@ -987,11 +997,11 @@ export function toSchema<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Schema.__from_preserve__ = toSchema;
export function fromSchema<_embedded = _.GenericEmbedded>(_v: Schema<_embedded>): _.Value<_embedded> {
export function fromSchema<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Schema<_embedded>): _.Value<_embedded> {
return _.Record(
$schema,
[
new _.Dictionary<_embedded>(
_.Dictionary.from<_embedded>(
[
[$version, fromVersion<_embedded>(_v["version"])],
[$embeddedType, fromEmbeddedTypeName<_embedded>(_v["embeddedType"])],
@ -1002,13 +1012,13 @@ export function fromSchema<_embedded = _.GenericEmbedded>(_v: Schema<_embedded>)
);
}
export function asVersion<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Version {
export function asVersion<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Version {
let result = toVersion(v);
if (result === void 0) throw new TypeError(`Invalid Version: ${_.stringify(v)}`);
return result;
}
export function toVersion<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Version {
export function toVersion<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Version {
let _tmp0: ({}) | undefined;
let result: undefined | Version;
_tmp0 = _.is(v, $1) ? {} : void 0;
@ -1024,15 +1034,15 @@ export function toVersion<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Version.__from_preserve__ = toVersion;
export function fromVersion<_embedded = _.GenericEmbedded>(_v: Version): _.Value<_embedded> {return $1;}
export function fromVersion<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Version): _.Value<_embedded> {return $1;}
export function asEmbeddedTypeName<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): EmbeddedTypeName {
export function asEmbeddedTypeName<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): EmbeddedTypeName {
let result = toEmbeddedTypeName(v);
if (result === void 0) throw new TypeError(`Invalid EmbeddedTypeName: ${_.stringify(v)}`);
return result;
}
export function toEmbeddedTypeName<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | EmbeddedTypeName {
export function toEmbeddedTypeName<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | EmbeddedTypeName {
let _tmp0: ({}) | undefined;
let result: undefined | EmbeddedTypeName;
_tmp0 = _.is(v, __lit6) ? {} : void 0;
@ -1062,32 +1072,33 @@ export function toEmbeddedTypeName<_embedded = _.GenericEmbedded>(v: _.Value<_em
export namespace EmbeddedTypeName {export const __from_preserve__ = toEmbeddedTypeName;}
export function fromEmbeddedTypeName<_embedded = _.GenericEmbedded>(_v: EmbeddedTypeName): _.Value<_embedded> {
export function fromEmbeddedTypeName<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: EmbeddedTypeName): _.Value<_embedded> {
switch (_v._variant) {
case "false": {return __lit6;};
case "Ref": {return fromRef<_embedded>(_v.value);};
};
}
export function asDefinitions<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Definitions<_embedded> {
export function asDefinitions<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Definitions<_embedded> {
let result = toDefinitions(v);
if (result === void 0) throw new TypeError(`Invalid Definitions: ${_.stringify(v)}`);
return result;
}
export function toDefinitions<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Definitions<_embedded> {
let _tmp0: (_.EncodableDictionary<symbol, Definition<_embedded>, _embedded>) | undefined;
export function toDefinitions<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Definitions<_embedded> {
let _tmp0: (_.JsDictionary<Definition<_embedded>>) | undefined;
let result: undefined | Definitions<_embedded>;
_tmp0 = void 0;
if (_.Dictionary.isDictionary<_embedded>(v)) {
_tmp0 = new _.EncodableDictionary<symbol, Definition<_embedded>, _embedded>(k => k, fromDefinition<_embedded>);
for (const [_tmp1, _tmp2] of v) {
let _tmp3: (symbol) | undefined;
_tmp3 = typeof _tmp1 === 'symbol' ? _tmp1 : void 0;
if (_tmp3 !== void 0) {
let _tmp4: (Definition<_embedded>) | undefined;
_tmp4 = toDefinition(_tmp2);
if (_tmp4 !== void 0) {_tmp0.set(_tmp3, _tmp4); continue;};
const _tmp1 = new _.DictionaryMap(v);
_tmp0 = {};
for (const [_tmp2, _tmp3] of _tmp1) {
let _tmp4: (symbol) | undefined;
_tmp4 = typeof _tmp2 === 'symbol' ? _tmp2 : void 0;
if (_tmp4 !== void 0) {
let _tmp5: (Definition<_embedded>) | undefined;
_tmp5 = toDefinition(_tmp3);
if (_tmp5 !== void 0) {_tmp0[_tmp4.description!] = _tmp5; continue;};
};
_tmp0 = void 0;
break;
@ -1099,17 +1110,19 @@ export function toDefinitions<_embedded = _.GenericEmbedded>(v: _.Value<_embedde
Definitions.__from_preserve__ = toDefinitions;
export function fromDefinitions<_embedded = _.GenericEmbedded>(_v: Definitions<_embedded>): _.Value<_embedded> {
return new _.Dictionary<_embedded>(_.Array.from(_v.entries()).map(([k, v]) => [k, fromDefinition<_embedded>(v)]));
export function fromDefinitions<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Definitions<_embedded>): _.Value<_embedded> {
return _.Dictionary.from<_embedded>(
_.Array.from(_.JsDictionary.entries(_v)).map(([k, v]) => [k, fromDefinition<_embedded>(v)])
);
}
export function asDefinition<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Definition<_embedded> {
export function asDefinition<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Definition<_embedded> {
let result = toDefinition(v);
if (result === void 0) throw new TypeError(`Invalid Definition: ${_.stringify(v)}`);
return result;
}
export function toDefinition<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Definition<_embedded> {
export function toDefinition<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Definition<_embedded> {
let result: undefined | Definition<_embedded>;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -1207,7 +1220,7 @@ export function toDefinition<_embedded = _.GenericEmbedded>(v: _.Value<_embedded
export namespace Definition {export const __from_preserve__ = toDefinition;}
export function fromDefinition<_embedded = _.GenericEmbedded>(_v: Definition<_embedded>): _.Value<_embedded> {
export function fromDefinition<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Definition<_embedded>): _.Value<_embedded> {
switch (_v._variant) {
case "or": {
return _.Record(
@ -1237,13 +1250,13 @@ export function fromDefinition<_embedded = _.GenericEmbedded>(_v: Definition<_em
};
}
export function asPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Pattern<_embedded> {
export function asPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Pattern<_embedded> {
let result = toPattern(v);
if (result === void 0) throw new TypeError(`Invalid Pattern: ${_.stringify(v)}`);
return result;
}
export function toPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Pattern<_embedded> {
export function toPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Pattern<_embedded> {
let _tmp0: (SimplePattern<_embedded>) | undefined;
let result: undefined | Pattern<_embedded>;
_tmp0 = toSimplePattern(v);
@ -1274,20 +1287,20 @@ export function toPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
export namespace Pattern {export const __from_preserve__ = toPattern;}
export function fromPattern<_embedded = _.GenericEmbedded>(_v: Pattern<_embedded>): _.Value<_embedded> {
export function fromPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Pattern<_embedded>): _.Value<_embedded> {
switch (_v._variant) {
case "SimplePattern": {return fromSimplePattern<_embedded>(_v.value);};
case "CompoundPattern": {return fromCompoundPattern<_embedded>(_v.value);};
};
}
export function asSimplePattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): SimplePattern<_embedded> {
export function asSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): SimplePattern<_embedded> {
let result = toSimplePattern(v);
if (result === void 0) throw new TypeError(`Invalid SimplePattern: ${_.stringify(v)}`);
return result;
}
export function toSimplePattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | SimplePattern<_embedded> {
export function toSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | SimplePattern<_embedded> {
let _tmp0: ({}) | undefined;
let result: undefined | SimplePattern<_embedded>;
_tmp0 = _.is(v, $any) ? {} : void 0;
@ -1436,7 +1449,7 @@ export function toSimplePattern<_embedded = _.GenericEmbedded>(v: _.Value<_embed
export namespace SimplePattern {export const __from_preserve__ = toSimplePattern;}
export function fromSimplePattern<_embedded = _.GenericEmbedded>(_v: SimplePattern<_embedded>): _.Value<_embedded> {
export function fromSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: SimplePattern<_embedded>): _.Value<_embedded> {
switch (_v._variant) {
case "any": {return $any;};
case "atom": {return _.Record($atom, [fromAtomKind<_embedded>(_v["atomKind"])]);};
@ -1457,13 +1470,13 @@ export function fromSimplePattern<_embedded = _.GenericEmbedded>(_v: SimplePatte
};
}
export function asCompoundPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): CompoundPattern<_embedded> {
export function asCompoundPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): CompoundPattern<_embedded> {
let result = toCompoundPattern(v);
if (result === void 0) throw new TypeError(`Invalid CompoundPattern: ${_.stringify(v)}`);
return result;
}
export function toCompoundPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | CompoundPattern<_embedded> {
export function toCompoundPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | CompoundPattern<_embedded> {
let result: undefined | CompoundPattern<_embedded>;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -1574,7 +1587,7 @@ export function toCompoundPattern<_embedded = _.GenericEmbedded>(v: _.Value<_emb
export namespace CompoundPattern {export const __from_preserve__ = toCompoundPattern;}
export function fromCompoundPattern<_embedded = _.GenericEmbedded>(_v: CompoundPattern<_embedded>): _.Value<_embedded> {
export function fromCompoundPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: CompoundPattern<_embedded>): _.Value<_embedded> {
switch (_v._variant) {
case "rec": {
return _.Record(
@ -1601,25 +1614,26 @@ export function fromCompoundPattern<_embedded = _.GenericEmbedded>(_v: CompoundP
};
}
export function asDictionaryEntries<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): DictionaryEntries<_embedded> {
export function asDictionaryEntries<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): DictionaryEntries<_embedded> {
let result = toDictionaryEntries(v);
if (result === void 0) throw new TypeError(`Invalid DictionaryEntries: ${_.stringify(v)}`);
return result;
}
export function toDictionaryEntries<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | DictionaryEntries<_embedded> {
let _tmp0: (_.EncodableDictionary<_.Value<_embedded>, NamedSimplePattern<_embedded>, _embedded>) | undefined;
export function toDictionaryEntries<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | DictionaryEntries<_embedded> {
let _tmp0: (_.EncodableDictionary<_embedded, _.Value<_embedded>, NamedSimplePattern<_embedded>>) | undefined;
let result: undefined | DictionaryEntries<_embedded>;
_tmp0 = void 0;
if (_.Dictionary.isDictionary<_embedded>(v)) {
_tmp0 = new _.EncodableDictionary<_.Value<_embedded>, NamedSimplePattern<_embedded>, _embedded>(k => k, fromNamedSimplePattern<_embedded>);
for (const [_tmp1, _tmp2] of v) {
let _tmp3: (_.Value<_embedded>) | undefined;
_tmp3 = _tmp1;
if (_tmp3 !== void 0) {
let _tmp4: (NamedSimplePattern<_embedded>) | undefined;
_tmp4 = toNamedSimplePattern(_tmp2);
if (_tmp4 !== void 0) {_tmp0.set(_tmp3, _tmp4); continue;};
const _tmp1 = new _.DictionaryMap(v);
_tmp0 = new _.EncodableDictionary<_embedded, _.Value<_embedded>, NamedSimplePattern<_embedded>>(k => k, fromNamedSimplePattern<_embedded>);
for (const [_tmp2, _tmp3] of _tmp1) {
let _tmp4: (_.Value<_embedded>) | undefined;
_tmp4 = _tmp2;
if (_tmp4 !== void 0) {
let _tmp5: (NamedSimplePattern<_embedded>) | undefined;
_tmp5 = toNamedSimplePattern(_tmp3);
if (_tmp5 !== void 0) {_tmp0.set(_tmp4, _tmp5); continue;};
};
_tmp0 = void 0;
break;
@ -1631,19 +1645,19 @@ export function toDictionaryEntries<_embedded = _.GenericEmbedded>(v: _.Value<_e
DictionaryEntries.__from_preserve__ = toDictionaryEntries;
export function fromDictionaryEntries<_embedded = _.GenericEmbedded>(_v: DictionaryEntries<_embedded>): _.Value<_embedded> {
return new _.Dictionary<_embedded>(
export function fromDictionaryEntries<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: DictionaryEntries<_embedded>): _.Value<_embedded> {
return _.Dictionary.from<_embedded>(
_.Array.from(_v.entries()).map(([k, v]) => [k, fromNamedSimplePattern<_embedded>(v)])
);
}
export function asAtomKind<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): AtomKind {
export function asAtomKind<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): AtomKind {
let result = toAtomKind(v);
if (result === void 0) throw new TypeError(`Invalid AtomKind: ${_.stringify(v)}`);
return result;
}
export function toAtomKind<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | AtomKind {
export function toAtomKind<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | AtomKind {
let _tmp0: ({}) | undefined;
let result: undefined | AtomKind;
_tmp0 = _.is(v, $Boolean) ? {} : void 0;
@ -1720,7 +1734,7 @@ export function toAtomKind<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>)
export namespace AtomKind {export const __from_preserve__ = toAtomKind;}
export function fromAtomKind<_embedded = _.GenericEmbedded>(_v: AtomKind): _.Value<_embedded> {
export function fromAtomKind<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: AtomKind): _.Value<_embedded> {
switch (_v._variant) {
case "Boolean": {return $Boolean;};
case "Double": {return $Double;};
@ -1731,15 +1745,15 @@ export function fromAtomKind<_embedded = _.GenericEmbedded>(_v: AtomKind): _.Val
};
}
export function asNamedAlternative<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): NamedAlternative<_embedded> {
export function asNamedAlternative<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): NamedAlternative<_embedded> {
let result = toNamedAlternative(v);
if (result === void 0) throw new TypeError(`Invalid NamedAlternative: ${_.stringify(v)}`);
return result;
}
export function toNamedAlternative<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedAlternative<_embedded> {
export function toNamedAlternative<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedAlternative<_embedded> {
let result: undefined | NamedAlternative<_embedded>;
if (_.isSequence(v) && v.length === 2) {
if (_.isSequence(v) && v.length >= 2) {
let _tmp0: (string) | undefined;
_tmp0 = typeof v[0] === 'string' ? v[0] : void 0;
if (_tmp0 !== void 0) {
@ -1761,15 +1775,15 @@ export function toNamedAlternative<_embedded = _.GenericEmbedded>(v: _.Value<_em
NamedAlternative.__from_preserve__ = toNamedAlternative;
export function fromNamedAlternative<_embedded = _.GenericEmbedded>(_v: NamedAlternative<_embedded>): _.Value<_embedded> {return [_v["variantLabel"], fromPattern<_embedded>(_v["pattern"])];}
export function fromNamedAlternative<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: NamedAlternative<_embedded>): _.Value<_embedded> {return [_v["variantLabel"], fromPattern<_embedded>(_v["pattern"])];}
export function asNamedSimplePattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): NamedSimplePattern<_embedded> {
export function asNamedSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): NamedSimplePattern<_embedded> {
let result = toNamedSimplePattern(v);
if (result === void 0) throw new TypeError(`Invalid NamedSimplePattern: ${_.stringify(v)}`);
return result;
}
export function toNamedSimplePattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedSimplePattern<_embedded> {
export function toNamedSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedSimplePattern<_embedded> {
let _tmp0: (Binding<_embedded>) | undefined;
let result: undefined | NamedSimplePattern<_embedded>;
_tmp0 = toBinding(v);
@ -1800,20 +1814,20 @@ export function toNamedSimplePattern<_embedded = _.GenericEmbedded>(v: _.Value<_
export namespace NamedSimplePattern {export const __from_preserve__ = toNamedSimplePattern;}
export function fromNamedSimplePattern<_embedded = _.GenericEmbedded>(_v: NamedSimplePattern<_embedded>): _.Value<_embedded> {
export function fromNamedSimplePattern<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: NamedSimplePattern<_embedded>): _.Value<_embedded> {
switch (_v._variant) {
case "named": {return fromBinding<_embedded>(_v.value);};
case "anonymous": {return fromSimplePattern<_embedded>(_v.value);};
};
}
export function asNamedPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): NamedPattern<_embedded> {
export function asNamedPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): NamedPattern<_embedded> {
let result = toNamedPattern(v);
if (result === void 0) throw new TypeError(`Invalid NamedPattern: ${_.stringify(v)}`);
return result;
}
export function toNamedPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedPattern<_embedded> {
export function toNamedPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | NamedPattern<_embedded> {
let _tmp0: (Binding<_embedded>) | undefined;
let result: undefined | NamedPattern<_embedded>;
_tmp0 = toBinding(v);
@ -1844,20 +1858,20 @@ export function toNamedPattern<_embedded = _.GenericEmbedded>(v: _.Value<_embedd
export namespace NamedPattern {export const __from_preserve__ = toNamedPattern;}
export function fromNamedPattern<_embedded = _.GenericEmbedded>(_v: NamedPattern<_embedded>): _.Value<_embedded> {
export function fromNamedPattern<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: NamedPattern<_embedded>): _.Value<_embedded> {
switch (_v._variant) {
case "named": {return fromBinding<_embedded>(_v.value);};
case "anonymous": {return fromPattern<_embedded>(_v.value);};
};
}
export function asBinding<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Binding<_embedded> {
export function asBinding<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Binding<_embedded> {
let result = toBinding(v);
if (result === void 0) throw new TypeError(`Invalid Binding: ${_.stringify(v)}`);
return result;
}
export function toBinding<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Binding<_embedded> {
export function toBinding<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Binding<_embedded> {
let result: undefined | Binding<_embedded>;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -1885,17 +1899,17 @@ export function toBinding<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>):
Binding.__from_preserve__ = toBinding;
export function fromBinding<_embedded = _.GenericEmbedded>(_v: Binding<_embedded>): _.Value<_embedded> {
export function fromBinding<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Binding<_embedded>): _.Value<_embedded> {
return _.Record($named, [_v["name"], fromSimplePattern<_embedded>(_v["pattern"])]);
}
export function asRef<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): Ref {
export function asRef<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): Ref {
let result = toRef(v);
if (result === void 0) throw new TypeError(`Invalid Ref: ${_.stringify(v)}`);
return result;
}
export function toRef<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Ref {
export function toRef<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | Ref {
let result: undefined | Ref;
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
let _tmp0: ({}) | undefined;
@ -1923,15 +1937,15 @@ export function toRef<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): und
Ref.__from_preserve__ = toRef;
export function fromRef<_embedded = _.GenericEmbedded>(_v: Ref): _.Value<_embedded> {return _.Record($ref, [fromModulePath<_embedded>(_v["module"]), _v["name"]]);}
export function fromRef<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: Ref): _.Value<_embedded> {return _.Record($ref, [fromModulePath<_embedded>(_v["module"]), _v["name"]]);}
export function asModulePath<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): ModulePath {
export function asModulePath<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): ModulePath {
let result = toModulePath(v);
if (result === void 0) throw new TypeError(`Invalid ModulePath: ${_.stringify(v)}`);
return result;
}
export function toModulePath<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | ModulePath {
export function toModulePath<_embedded extends _.Embeddable = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | ModulePath {
let _tmp0: (Array<symbol>) | undefined;
let result: undefined | ModulePath;
_tmp0 = void 0;
@ -1951,5 +1965,5 @@ export function toModulePath<_embedded = _.GenericEmbedded>(v: _.Value<_embedded
ModulePath.__from_preserve__ = toModulePath;
export function fromModulePath<_embedded = _.GenericEmbedded>(_v: ModulePath): _.Value<_embedded> {return _v.map(v => v);}
export function fromModulePath<_embedded extends _.Embeddable = _.GenericEmbedded>(_v: ModulePath): _.Value<_embedded> {return _v.map(v => v);}

View File

@ -1,11 +1,11 @@
import { compare } from '@preserves/core';
import { compare, Embeddable } from '@preserves/core';
import * as M from './meta';
import * as H from './gen/host';
export * from './gen/host';
export function definitionType<V>(p: M.Definition<V>): H.Definition {
export function definitionType<V extends Embeddable>(p: M.Definition<V>): H.Definition {
switch (p._variant) {
case 'or': return H.Definition.union([p.pattern0, p.pattern1, ... p.patternN].map(p =>
H.Variant({ label: Symbol.for(p.variantLabel), type: patternType(p.pattern) })));
@ -16,7 +16,7 @@ export function definitionType<V>(p: M.Definition<V>): H.Definition {
}
}
export function patternType<V>(p: M.Pattern<V>): H.Simple {
export function patternType<V extends Embeddable>(p: M.Pattern<V>): H.Simple {
switch (p._variant) {
case 'SimplePattern':
return H.Simple.Field(fieldType(p.value));
@ -25,7 +25,7 @@ export function patternType<V>(p: M.Pattern<V>): H.Simple {
}
}
export function fieldType<V>(p: M.SimplePattern<V>): H.Field {
export function fieldType<V extends Embeddable>(p: M.SimplePattern<V>): H.Field {
switch (p._variant) {
case 'any': return H.Field.any();
case 'atom': return H.Field.AtomKind(p.atomKind);
@ -38,19 +38,19 @@ export function fieldType<V>(p: M.SimplePattern<V>): H.Field {
}
}
export function productType<V>(ps: M.NamedPattern<V>[]): H.Simple {
export function productType<V extends Embeddable>(ps: M.NamedPattern<V>[]): H.Simple {
const gathered: H.NamedField[] = [];
ps.forEach(p => gather(p, gathered));
if (gathered.length === 0) return H.Simple.Field(H.Field.unit());
return H.Simple.Record(H.Record(gathered));
}
function promote<V>(p: M.NamedSimplePattern<V>): M.NamedPattern<V> {
function promote<V extends Embeddable>(p: M.NamedSimplePattern<V>): M.NamedPattern<V> {
if (p._variant === 'named') return p;
return M.NamedPattern.anonymous(M.Pattern.SimplePattern(p.value));
}
function gather<V>(p: M.NamedPattern<V>, into: H.NamedField[]) {
function gather<V extends Embeddable>(p: M.NamedPattern<V>, into: H.NamedField[]) {
switch (p._variant) {
case 'named': {
const t = fieldType(p.value.pattern);

View File

@ -1,35 +1,38 @@
import { EncodableDictionary, KeyedDictionary, Dictionary, Value, is, Record, Float, Bytes, isEmbedded, isSequence, Set, Atom, Embedded, merge as plainMerge, Preservable, PreserveWritable, _iterMap, stringify, fromJS } from '@preserves/core';
import { EncodableDictionary, KeyedDictionary, Dictionary, Value, is, Record, Float, Bytes, isEmbedded, isSequence, Set, Atom, merge as plainMerge, Preservable, PreserveWritable, _iterMap, stringify, fromJS, Embeddable, DictionaryMap, JsDictionary } from '@preserves/core';
import { SchemaDefinition } from './reflection';
import * as M from './meta';
import * as H from './host';
export const UNIT: true = true;
export type Parsed<V> = Atom | V | Parsed<V>[] | DictOf<V> | Bindings<V>;
export type TopParsed<V> = Atom | V | Parsed<V>[] | DictOf<V> | TopBindings<V>;
export type Parsed<V extends Embeddable> = Atom | V | Parsed<V>[] | DictOf<V> | Bindings<V>;
export type TopParsed<V extends Embeddable> = Atom | V | Parsed<V>[] | DictOf<V> | TopBindings<V>;
export type Top<V> = Preservable<V> & PreserveWritable<V> & { __as_preserve__(): Value<V> };
export type Top<V extends Embeddable> =
& Preservable<V>
& PreserveWritable<V>
& { __as_preserve__(): Value<V> };
export type DictOf<V> = EncodableDictionary<Parsed<V>, Parsed<V>, V>;
export type DictOf<V extends Embeddable> = EncodableDictionary<V, Parsed<V>, Parsed<V>>;
export type BindingName = string;
export type Bindings<V> = { [key: BindingName]: Parsed<V> };
export type TopBindings<V> = Bindings<V> & Top<V>;
export type Bindings<V extends Embeddable> = { [key: BindingName]: Parsed<V> };
export type TopBindings<V extends Embeddable> = Bindings<V> & Top<V>;
export type SingleConstructor<V> = ((input: any) => Parsed<V>) & { schema(): SchemaDefinition };
export type MultipleConstructors<V> = { [key: string]: SingleConstructor<V> };
export type DefinitionConstructors<V> = SingleConstructor<V> | MultipleConstructors<V>;
export type SingleConstructor<V extends Embeddable> = ((input: any) => Parsed<V>) & { schema(): SchemaDefinition };
export type MultipleConstructors<V extends Embeddable> = { [key: string]: SingleConstructor<V> };
export type DefinitionConstructors<V extends Embeddable> = SingleConstructor<V> | MultipleConstructors<V>;
export namespace Bindings {
export function empty<V>(): Bindings<V> {
export function empty<V extends Embeddable>(): Bindings<V> {
return {};
}
export function single<V>(k: BindingName, v: Parsed<V>): Bindings<V> {
export function single<V extends Embeddable>(k: BindingName, v: Parsed<V>): Bindings<V> {
const bs = empty<V>();
bs[k] = v;
return bs;
}
export function merge<V>(... vs: Bindings<V>[]): Bindings<V> {
export function merge<V extends Embeddable>(... vs: Bindings<V>[]): Bindings<V> {
const acc = empty<V>();
for (const v of vs) {
Object.entries(v).forEach(([kw, vw]) => acc[kw] = vw);
@ -38,29 +41,29 @@ export namespace Bindings {
}
}
export type DynField<V> =
export type DynField<V extends Embeddable> =
| { type: 'simple', value: Parsed<V> }
| { type: 'compound', values: Bindings<V> }
;
export namespace DynField {
export function unwrap<V>(f: DynField<V>): Parsed<V> {
export function unwrap<V extends Embeddable>(f: DynField<V>): Parsed<V> {
if (f.type === 'simple') return f.value;
return f.values;
}
export function unwrap_compound<V>(f: DynField<V>): Bindings<V> {
export function unwrap_compound<V extends Embeddable>(f: DynField<V>): Bindings<V> {
if (f.type === 'simple') throw new Error("Cannot unwrap DynField.simple to compound fields");
return f.values;
}
export function simple<V>(value: Parsed<V>): DynField<V> {
export function simple<V extends Embeddable>(value: Parsed<V>): DynField<V> {
return { type: 'simple', value };
}
export function maybeSimple<V>(value: Parsed<V> | null): DynField<V> {
export function maybeSimple<V extends Embeddable>(value: Parsed<V> | null): DynField<V> {
return value === null ? compound(Bindings.empty()) : simple(value);
}
export function compound<V>(values: Bindings<V>): DynField<V> {
export function compound<V extends Embeddable>(values: Bindings<V>): DynField<V> {
return { type: 'compound', values };
}
export function promote<V>(f: DynField<V>, key?: symbol): Bindings<V> {
export function promote<V extends Embeddable>(f: DynField<V>, key?: symbol): Bindings<V> {
if (f.type === 'compound') return f.values;
return key ? Bindings.single(M.jsId(key.description!), f.value) : Bindings.empty();
}
@ -71,11 +74,27 @@ function optmap<A,B>(a: A | undefined, f: (a: A) => B): B | undefined {
return f(a);
}
export type Unparseable<V> = TopParsed<V>;
export type Unparser<V> = (v: Parsed<V>) => Value<V>;
export type UnparserCompound<V> = (v: Bindings<V>) => Value<V>;
export type Unparseable<V extends Embeddable> = TopParsed<V>;
export type Unparser<V extends Embeddable> = (v: Parsed<V>) => Value<V>;
export type UnparserCompound<V extends Embeddable> = (v: Bindings<V>) => Value<V>;
export class SchemaInterpreter<V> {
function attachSchema<V extends Embeddable>(
schema: M.Schema<V>,
name: symbol,
f: (input: any) => Parsed<V>,
variant?: symbol,
): SingleConstructor<V> {
const g = f as SingleConstructor<V>;
g.schema = () => ({
schema: fromJS(schema),
imports: {}, // TODO
definitionName: name,
variant,
});
return g;
}
export class SchemaInterpreter<V extends Embeddable> {
activeModule: M.ModulePath = [];
unparserCache: { [key: string]: [Unparser<V>] } = {};
@ -116,7 +135,7 @@ export class SchemaInterpreter<V> {
): R {
const { resolved, schema } = this._findModule(modulePath);
return this._withModule(resolved, () => {
const definition = schema.definitions.get(name);
const definition = JsDictionary.get(schema.definitions, name);
if (definition === void 0) {
throw new Error(`No such preserves-schema definition: ${[... modulePath, name].map(s => s.description!).join('.')}`);
}
@ -132,65 +151,62 @@ export class SchemaInterpreter<V> {
return result;
}
buildConstructor(
modulePath: M.ModulePath,
name: symbol,
schema: M.Schema<V>,
ty: H.Simple,
variant?: symbol,
): SingleConstructor<V> {
const flatName = M.formatModulePath([
... modulePath, name, ... (variant === void 0 ? [] : [variant])]);
const mkBase = (variant === void 0)
? () => ({})
: () => ({ _variant: variant.description! });
switch (ty._variant) {
case 'Field': {
const tmp =
ty.value._variant === 'unit'
? { [flatName]: () => this.makeTop(modulePath, name, mkBase()) }
: (variant === void 0
? { [flatName]: (value: any) => value }
: { [flatName]: (value: any) => this.makeTop(
modulePath, name, { ... mkBase(), value }) });
return attachSchema(schema, name, tmp[flatName], variant);
}
case 'Record': {
const rec = ty.value;
if (rec.fields.length > 1) {
const tmp = { [flatName]: (fields: Bindings<V>) =>
this.makeTop(modulePath, name, { ... mkBase(), ... fields }) };
return attachSchema(schema, name, tmp[flatName], variant);
} else {
const tmp = { [flatName]: (field: Parsed<V>) =>
this.makeTop(modulePath, name, {
... mkBase(),
[M.jsId(rec.fields[0].name.description!)]: field,
}) };
return attachSchema(schema, name, tmp[flatName], variant);
}
}
}
}
definitionConstructor(
modulePath: M.ModulePath,
name: symbol,
): DefinitionConstructors<V> {
return this._lookup(modulePath, name, (definition, schema): DefinitionConstructors<V> => {
function attachSchema(
f: (input: any) => Parsed<V>,
variant?: symbol,
): SingleConstructor<V> {
const g = f as SingleConstructor<V>;
g.schema = () => ({
schema: fromJS(schema),
imports: {}, // TODO
definitionName: name,
variant,
});
return g;
}
const ty = H.definitionType(definition);
if (ty._variant === 'union') {
const multiple: MultipleConstructors<V> = {};
ty.variants.forEach(v => {
const _variant = v.label.description!;
switch (v.type._variant) {
case 'Field':
multiple[_variant] = attachSchema(
(value: any) => this.makeTop(modulePath, name, { _variant, value }),
v.label);
break;
case 'Record':
multiple[_variant] = attachSchema(
(fields: object) => this.makeTop(modulePath, name, { _variant, ... fields }),
v.label);
break;
}
multiple[M.jsId(v.label.description!)] = this.buildConstructor(
modulePath, name, schema, v.type, v.label);
});
return multiple;
} else {
const flatName = M.formatModulePath([... modulePath, name]);
switch (ty.value._variant) {
case 'Field': {
const tmp = { [flatName]: (value: any) => value };
return attachSchema(tmp[flatName]);
}
case 'Record': {
const rec = ty.value.value;
if (rec.fields.length > 1) {
const tmp = { [flatName]: (fields: Bindings<V>) =>
this.makeTop(modulePath, name, fields) };
return attachSchema(tmp[flatName]);
} else {
const tmp = { [flatName]: (field: Parsed<V>) =>
this.makeTop(modulePath, name, {
[M.jsId(rec.fields[0].name.description!)]: field,
}) };
return attachSchema(tmp[flatName]);
}
}
}
return this.buildConstructor(modulePath, name, schema, ty.value);
}
});
}
@ -219,7 +235,11 @@ export class SchemaInterpreter<V> {
if (ty._variant === 'union' || ty.value._variant === 'Record') {
return this.makeTop(modulePath, name, result0 as Bindings<V>);
} else {
return result0 as Exclude<Parsed<V>, Bindings<V>>;
if (ty.value.value._variant === 'unit') {
return this.makeTop(modulePath, name, {});
} else {
return result0 as Exclude<Parsed<V>, Bindings<V>>;
}
}
}));
}
@ -291,7 +311,7 @@ export class SchemaInterpreter<V> {
case 'ByteString': return inputIf(Bytes.isBytes(input));
case 'Symbol': return inputIf(typeof input === 'symbol');
}
case 'embedded': return isEmbedded(input) ? input.embeddedValue : void 0;
case 'embedded': return isEmbedded(input) ? input : void 0;
case 'lit': return is(input, p.value) ? null : void 0;
case 'seqof': {
if (!isSequence(input)) return void 0;
@ -315,17 +335,29 @@ export class SchemaInterpreter<V> {
}
case 'dictof': {
if (!Dictionary.isDictionary<V>(input)) return void 0;
const result: DictOf<V> = new EncodableDictionary(
this.unparserSimplePattern(p.key),
this.unparserSimplePattern(p.value));
for (const [k, v] of input) {
const kw = this.parseSimplePattern(p.key, k);
if (kw === void 0) return void 0;
const vw = this.parseSimplePattern(p.value, v);
if (vw === void 0) return void 0;
result.set(kw === null ? UNIT : kw, vw === null ? UNIT : vw);
if (M.isSymbolPattern(p.key)) {
const result: Bindings<V> = {};
for (const [k, v] of Dictionary.asMap<V>(input)) {
const kw = this.parseSimplePattern(p.key, k);
if (kw === void 0 || typeof kw !== 'symbol') return void 0;
const vw = this.parseSimplePattern(p.value, v);
if (vw === void 0) return void 0;
result[kw.description!] = vw === null ? UNIT : vw;
}
return result;
} else {
const result: DictOf<V> = new EncodableDictionary(
this.unparserSimplePattern(p.key),
this.unparserSimplePattern(p.value));
for (const [k, v] of Dictionary.asMap<V>(input)) {
const kw = this.parseSimplePattern(p.key, k);
if (kw === void 0) return void 0;
const vw = this.parseSimplePattern(p.value, v);
if (vw === void 0) return void 0;
result.set(kw === null ? UNIT : kw, vw === null ? UNIT : vw);
}
return result;
}
return result;
}
case 'Ref': return this.tryParse(p.value.module, p.value.name, input);
}
@ -340,7 +372,7 @@ export class SchemaInterpreter<V> {
fsw => Bindings.merge(lw, fsw)));
case 'tuple': {
if (!isSequence(input)) return void 0;
if (input.length !== p.patterns.length) return void 0;
if (input.length < p.patterns.length) return void 0;
let results: Bindings<V>[] = [];
for (let i = 0; i < p.patterns.length; i++) {
const w = this.parseNamedPattern(p.patterns[i], input[i]);
@ -365,10 +397,11 @@ export class SchemaInterpreter<V> {
});
}
case 'dict': {
if (!Dictionary.isDictionary<V>(input)) return void 0;
const inputMap = Dictionary.asMap<V>(input);
if (!inputMap) return void 0;
const results: Bindings<V>[] = [];
for (const [key, vp] of p.entries) {
const v = input.get(key);
const v = inputMap.get(key);
if (v === void 0) return void 0;
const vw = this.parseNamedSimplePattern(vp, v);
if (vw === void 0) return void 0;
@ -467,7 +500,7 @@ export class SchemaInterpreter<V> {
switch (p._variant) {
case 'any': return v => v as Value<V>; // ?!
case 'atom': return v => v as Atom;
case 'embedded': return v => new Embedded(v as V);
case 'embedded': return v => v as V;
case 'lit': return _v => p.value;
case 'seqof': {
const up = this.unparserSimplePattern(p.pattern);
@ -480,8 +513,17 @@ export class SchemaInterpreter<V> {
case 'dictof': {
const kp = this.unparserSimplePattern(p.key);
const vp = this.unparserSimplePattern(p.value);
return vs => new Dictionary<V>(_iterMap(
(vs as DictOf<V>).entries(), ([k, v]) => [kp(k), vp(v)]));
return vs => {
const d = new DictionaryMap<V>();
for (const [k, v] of
(Map.isMap(vs)
? vs.entries()
: JsDictionary.entries(vs as Bindings<V>)))
{
d.set(kp(k), vp(v));
}
return M.isSymbolPattern(p.key) ? d.asJsDictionary() : d.asKeyedDictionary();
};
}
case 'Ref': {
const up = this._unparser(p.value.module, p.value.name);
@ -510,11 +552,11 @@ export class SchemaInterpreter<V> {
const ups: [Value<V>, Unparser<V>][] = Array.from(p.entries.entries()).map(
([key, vp]) => [key, this.unparserNamedSimplePattern(vp)]);
return bs => {
const result = new Dictionary<V>();
const result = new DictionaryMap<V>();
for (const [key, up] of ups) {
result.set(key, up(bs));
}
return result;
return result.simplifiedValue();
};
}
}
@ -531,4 +573,32 @@ export class SchemaInterpreter<V> {
return this.unparserSimplePattern(p.value);
}
}
moduleFor(modulePath: M.ModulePath): { [key: string]: any } | undefined {
const schema = this.env.get(modulePath);
if (schema === void 0) return void 0;
const mod: { [key: string]: any } = {};
JsDictionary.forEach(schema.definitions, (_d, n) => {
const definitionName = n.description!;
const definitionId = M.jsId(definitionName);
mod[`${definitionId}`] = this.definitionConstructor(modulePath, n);
mod[`from${definitionId}`] = this.unparser(modulePath, n);
mod[`to${definitionId}`] = (v: Value<V>) => this.tryParse(modulePath, n, v);
mod[`as${definitionId}`] = (v: Value<V>) => this.parse(modulePath, n, v);
});
return mod;
}
moduleTree(tree: { [key: string]: any } = {}): { [key: string]: any } {
for (const modulePath of this.env.keys()) {
let container = tree;
modulePath.slice(0, -1).forEach(n => {
if (!(n.description! in container)) container[n.description!] = {};
container = container[n.description!];
});
container[modulePath[modulePath.length - 1].description!] =
this.moduleFor(modulePath)!;
}
return tree;
}
}

View File

@ -1,4 +1,4 @@
import { GenericEmbedded, is, Value } from '@preserves/core';
import { Embeddable, GenericEmbedded, is, Value } from '@preserves/core';
import * as M from './gen/schema';
import { isJsKeyword } from './compiler/jskw';
@ -98,3 +98,8 @@ export function namelike(x: Input): string | undefined {
if (typeof x === 'boolean') return x ? 'true' : 'false';
return void 0;
}
export function isSymbolPattern<T extends Embeddable>(p: M.SimplePattern<T>): boolean {
return p._variant === 'atom'
&& p.atomKind._variant === 'Symbol';
}

View File

@ -1,4 +1,4 @@
import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, stringify, isCompound, EncodableDictionary, annotate, annotations, isEmbedded, GenericEmbedded, genericEmbeddedTypeDecode } from '@preserves/core';
import { Reader, Annotated, Dictionary, is, peel, preserves, Record, strip, Tuple, Position, position, stringify, isCompound, EncodableDictionary, annotate, annotations, isEmbedded, GenericEmbedded, genericEmbeddedTypeDecode, JsDictionary, KeyedDictionary } from '@preserves/core';
import { Input, Pattern, Schema, Definition, CompoundPattern, SimplePattern } from './meta';
import * as M from './meta';
import { SchemaSyntaxError } from './error';
@ -70,7 +70,7 @@ export function parseSchema(toplevelTokens: Array<Input>, options: SchemaReaderO
{
let version: M.Version | undefined = void 0;
let embeddedType: M.EmbeddedTypeName = M.EmbeddedTypeName.$false();
let definitions: M.Definitions = new EncodableDictionary(k => k, M.fromDefinition);
let definitions: M.Definitions = {};
function process(toplevelTokens: Array<Input>): void {
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT);
@ -82,10 +82,10 @@ export function parseSchema(toplevelTokens: Array<Input>, options: SchemaReaderO
if (!M.isValidToken(name.description!)) {
throw new SchemaSyntaxError(preserves`Invalid definition name: ${name}`, pos);
}
if (definitions.has(name)) {
if (JsDictionary.has(definitions, name)) {
throw new SchemaSyntaxError(preserves`Duplicate definition: ${clause}`, pos);
}
definitions.set(name, parseDefinition(name, pos, clause.slice(2)));
JsDictionary.set(definitions, name, parseDefinition(name, pos, clause.slice(2)));
} else if (clause.length === 2 && is(clause[0], M.$version)) {
version = M.asVersion(peel(clause[1]));
} else if (clause.length === 2 && is(clause[0], M.$embeddedType)) {
@ -232,7 +232,7 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
case 'symbol': return ks(M.SimplePattern.atom(M.AtomKind.Symbol()));
default: {
if (str[0] === '=') {
return ks(M.SimplePattern.lit(Symbol.for(str.slice(1))));
return ks(M.SimplePattern.lit<GenericEmbedded>(Symbol.for(str.slice(1))));
} else if (M.isValidQid(str)) {
return ks(M.SimplePattern.Ref(parseRef(str, pos)));
} else {
@ -260,20 +260,20 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
if (item.size !== 1) complain();
const [vp] = item.values();
return ks(M.SimplePattern.setof(walkSimple(vp)));
} else if (Dictionary.isDictionary<M.InputEmbedded, Input>(item)
&& item.size === 2
&& item.has(M.DOTDOTDOT))
{
const v = item.clone();
v.delete(M.DOTDOTDOT);
const [[kp, vp]] = v.entries();
return ks(M.SimplePattern.dictof({ key: walkSimple(kp), value: walkSimple(vp) }));
} else if (isCompound(item)) {
return kf();
} else if (isEmbedded(item)) {
return ks(M.SimplePattern.embedded(walkSimple(item.embeddedValue.generic)));
} else {
return ks(M.SimplePattern.lit(strip(item)));
const itemMap = Dictionary.asMap<M.InputEmbedded, Input>(item);
if (itemMap && itemMap.size === 2 && itemMap.has(M.DOTDOTDOT)) {
const v = itemMap.clone();
v.delete(M.DOTDOTDOT);
const [[kp, vp]] = v.entries();
return ks(M.SimplePattern.dictof({ key: walkSimple(kp), value: walkSimple(vp) }));
} else if (isCompound(item)) {
return kf();
} else if (isEmbedded(item)) {
return ks(M.SimplePattern.embedded(walkSimple(item.generic)));
} else {
return ks(M.SimplePattern.lit(strip(item)));
}
}
}
@ -312,19 +312,20 @@ function parsePattern(name: symbol, body0: Array<Input>): Pattern {
});
} else if (Array.isArray(item)) {
return M.CompoundPattern.tuple(item.map(maybeNamed));
} else if (Dictionary.isDictionary<M.InputEmbedded, Input>(item) && !item.has(M.DOTDOTDOT)) {
return M.CompoundPattern.dict(
M.DictionaryEntries(item.mapEntries<M.NamedSimplePattern, Input, M.InputEmbedded>(
([k, vp]) => [
strip(k),
_maybeNamed(
M.NamedSimplePattern.named,
M.NamedSimplePattern.anonymous,
walkSimple,
strip(k))(vp)
])));
} else {
complain();
const itemMap = Dictionary.asMap<M.InputEmbedded, Input>(item);
if (itemMap && !itemMap.has(M.DOTDOTDOT)) {
const entries = new KeyedDictionary<M.InputEmbedded, Input, M.NamedSimplePattern>();
itemMap.forEach((vp, k) => entries.set(
strip(k),
_maybeNamed(M.NamedSimplePattern.named,
M.NamedSimplePattern.anonymous,
walkSimple,
strip(k))(vp)));
return M.CompoundPattern.dict(M.DictionaryEntries(entries));
} else {
complain();
}
}
}

View File

@ -0,0 +1,29 @@
import { } from '@preserves/core';
import { compile, Meta as M, readSchema } from '../src/index';
import './test-utils';
import { js as format } from 'js-beautify';
function compileSingleSchema(source: string): string {
return format(compile([], [Symbol.for('test')], readSchema(`version 1 . ${source}`), {}));
}
describe('compiler', () => {
it('basics', () => {
expect(compileSingleSchema(`X = int .`)).toContain(`\nexport type X = number;\n`);
});
it('symbol-keyed dictionary', () => {
const s = compileSingleSchema(`D = { symbol:any ...:... } .`);
expect(s).toMatch(/^export type D.*= _.JsDictionary < _.Value < _embedded >> ;$/m);
});
it('string-keyed dictionary', () => {
const s = compileSingleSchema(`D = { string:any ...:... } .`);
expect(s).toMatch(/^export type D.*= _.EncodableDictionary < _embedded, string, _.Value < _embedded >> ;$/m);
});
it('any-keyed dictionary', () => {
const s = compileSingleSchema(`D = { any:any ...:... } .`);
expect(s).toMatch(/^export type D.*= _.EncodableDictionary < _embedded, _.Value < _embedded > , _.Value < _embedded >> ;$/m);
});
});

View File

@ -0,0 +1,111 @@
import { GenericEmbedded, stringify, fromJS, parse, EncodableDictionary, KeyedDictionary } from '@preserves/core';
import { SchemaInterpreter, readSchema } from '../src/index';
import './test-utils';
describe('interpreter', () => {
const I = new SchemaInterpreter<GenericEmbedded>();
const X_schema = readSchema(`
version 1 .
A = <foo> .
B = <bar @v int> .
C = <zot @v int @w int> .
N = int .
U = <class @a int @b int> / <true @c int @d int> .
V = <public @n int> / <private @n int> .
W = <const> / <let> .
D1 = { symbol:A ...:... } .
D2 = { any:A ...:... } .
D3 = { string:A ...:... } .
`);
const X = Symbol.for('X');
I.env.set([X], X_schema);
const E = I.moduleTree();
it('basically works', () => {
const a = E.X.A();
const b = E.X.B(22);
const c = E.X.C({v: 33, w: 44});
const n = E.X.N(22);
expect(stringify(a)).toBe('<foo>');
expect(stringify(b)).toBe('<bar 22>');
expect(stringify(c)).toBe('<zot 33 44>');
expect(stringify(n)).toBe('22');
expect(parse('<foo>')).is(fromJS(a));
expect(parse('<bar 22>')).is(fromJS(b));
expect(parse('<zot 33 44>')).is(fromJS(c));
expect(parse('22')).is(fromJS(n));
expect(stringify(E.X.asA(fromJS(a)))).toBe('<foo>');
expect(stringify(E.X.asB(fromJS(b)))).toBe('<bar 22>');
expect(stringify(E.X.asC(fromJS(c)))).toBe('<zot 33 44>');
expect(stringify(E.X.asN(fromJS(n)))).toBe('22');
});
it('escapes JS keywords', () => {
expect(Object.keys(E.X.U)).toEqual(['$class', '$true']);
expect(Object.keys(E.X.V)).toEqual(['$public', '$private']);
expect(Object.keys(E.X.W)).toEqual(['$const', '$let']);
});
const STANDARD_METHODS = ['__as_preserve__', '__preserve_on__', '__preserve_text_on__'];
it('accepts correct arguments to n-ary variant ctors', () => {
expect(stringify(E.X.U.$class({ a: 123, b: 234 }))).toBe('<class 123 234>');
expect(stringify(E.X.U.$true({ c: 123, d: 234 }))).toBe('<true 123 234>');
expect(Object.keys(E.X.U.$class({ a: 123, b: 234 }))).toEqual([
'_variant', 'a', 'b', ... STANDARD_METHODS]);
});
it('accepts correct arguments to unary variant ctors', () => {
expect(stringify(E.X.V.$public(123))).toBe('<public 123>');
expect(stringify(E.X.V.$private(123))).toBe('<private 123>');
expect(Object.keys(E.X.V.$public(123))).toEqual([
'_variant', 'n', ... STANDARD_METHODS]);
});
it('accepts correct arguments to nullary variant ctors', () => {
expect(stringify(E.X.W.$const())).toBe('<const>');
expect(stringify(E.X.W.$let())).toBe('<let>');
expect(Object.keys(E.X.W.$const())).toEqual([
'_variant', ... STANDARD_METHODS]);
});
it('produces JsDictionary for symbol-keyed dicts', () => {
const v = E.X.asD1(parse('{ a: <foo>, b: <foo>}'));
expect(Object.keys(v)).toEqual(['a', 'b']);
expect(stringify(E.X.fromA(v.a))).toBe('<foo>');
expect(stringify(E.X.fromA(v.b))).toBe('<foo>');
});
it('produces EncodableDictionary for any-keyed dicts', () => {
const d2 = E.X.asD2(parse('{ a: <foo>, b: <foo>}'));
expect(d2 instanceof EncodableDictionary).toBe(true);
expect(Array.from(d2.keys())).toEqual([Symbol.for('a'), Symbol.for('b')]);
expect(fromJS(d2.get(Symbol.for('a')))).is(fromJS(E.X.A()));
});
it('accepts either kind of dictionary for symbol-keyed dicts', () => {
const v = { a: E.X.A(), b: E.X.A() };
expect(stringify(v)).toBe('{a: <foo> b: <foo>}');
expect(E.X.fromD1(v)).is(parse('{a: <foo> b: <foo>}'));
expect(E.X.fromD1(E.X.D1(v))).is(parse('{a: <foo> b: <foo>}'));
expect(E.X.fromD1(E.X.D1(new KeyedDictionary([
[parse('a'), parse('<foo>')],
[parse('b'), parse('<foo>')],
])))).is(parse('{a: <foo> b: <foo>}'));
});
it('accepts either kind of dictionary for any-keyed dicts', () => {
const v = { a: E.X.A(), b: E.X.A() };
expect(stringify(v)).toBe('{a: <foo> b: <foo>}');
expect(E.X.fromD2(v)).is(parse('{a: <foo> b: <foo>}'));
expect(E.X.fromD2(E.X.D2(v))).is(parse('{a: <foo> b: <foo>}'));
expect(E.X.fromD2(E.X.D2(new KeyedDictionary([
[parse('a'), parse('<foo>')],
[parse('b'), parse('<foo>')],
])))).is(parse('{a: <foo> b: <foo>}'));
});
});

View File

@ -1,3 +1,4 @@
import { JsDictionary } from '@preserves/core';
import { readSchema, Meta } from '../src/index';
describe('reader schema', () => {
@ -14,12 +15,12 @@ describe('reader schema', () => {
'__preserve_on__',
'__preserve_text_on__',
]);
expect(s.definitions.size).toBe(0);
expect(JsDictionary.size(s.definitions)).toBe(0);
expect(s.embeddedType._variant).toBe('false');
});
it('understands patterns under embed', () => {
const s = readSchema('version 1 . X = #:0 .');
const def: Meta.Definition = s.definitions.get(Symbol.for('X'))!;
const def: Meta.Definition = JsDictionary.get(s.definitions, Symbol.for('X'))!;
if (def._variant !== 'Pattern') fail('bad definition 1');
if (def.value._variant !== 'SimplePattern') fail ('bad definition 2');
if (def.value.value._variant !== 'embedded') fail('bad definition 3');

View File

@ -1,9 +1,9 @@
import { Value, is, preserves } from '@preserves/core';
import { Value, is, preserves, Embeddable } from '@preserves/core';
declare global {
namespace jest {
interface Matchers<R> {
is<T>(expected: Value<T>): R;
is<T extends Embeddable>(expected: Value<T>): R;
toThrowFilter(f: (e: Error) => boolean): R;
}
}

View File

@ -21,6 +21,5 @@ open "cd packages/schema; yarn run compile:watch"
open "cd packages/schema; yarn run rollup:watch"
open "cd packages/schema; yarn run test:watch"
open "cd packages/schema-cli; yarn run compile:watch"
open "cd packages/schema-cli; yarn run rollup:watch"
tmux select-layout even-vertical

View File

@ -308,6 +308,18 @@
resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340"
integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
dependencies:
string-width "^5.1.2"
string-width-cjs "npm:string-width@^4.2.0"
strip-ansi "^7.0.1"
strip-ansi-cjs "npm:strip-ansi@^6.0.1"
wrap-ansi "^8.1.0"
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@ -1417,6 +1429,16 @@
dependencies:
"@octokit/openapi-types" "^12.11.0"
"@one-ini/wasm@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323"
integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==
"@pkgjs/parseargs@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@rollup/plugin-terser@^0.4":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-terser/-/plugin-terser-0.4.0.tgz#4c76249ad337f3eb04ab409332f23717af2c1fbf"
@ -1540,6 +1562,11 @@
jest-matcher-utils "^27.0.0"
pretty-format "^27.0.0"
"@types/js-beautify@1.14":
version "1.14.3"
resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.14.3.tgz#6ced76f79935e37e0d613110dea369881d93c1ff"
integrity sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==
"@types/minimatch@*":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
@ -1627,6 +1654,11 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
abbrev@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==
acorn-globals@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
@ -1716,6 +1748,11 @@ ansi-regex@^5.0.1:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-regex@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@ -1735,6 +1772,11 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
ansi-styles@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
anymatch@^3.0.3, anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
@ -1942,6 +1984,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@ -2232,6 +2281,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
commander@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@ -2265,7 +2319,7 @@ concat-stream@^2.0.0:
readable-stream "^3.0.2"
typedarray "^0.0.6"
config-chain@^1.1.12:
config-chain@^1.1.12, config-chain@^1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
@ -2391,7 +2445,7 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-spawn@^7.0.3:
cross-spawn@^7.0.0, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@ -2601,6 +2655,11 @@ dynamic-dedupe@^0.3.0:
dependencies:
xtend "^4.0.0"
eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
@ -2609,6 +2668,16 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
editorconfig@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-1.0.4.tgz#040c9a8e9a6c5288388b87c2db07028aa89f53a3"
integrity sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==
dependencies:
"@one-ini/wasm" "0.1.1"
commander "^10.0.0"
minimatch "9.0.1"
semver "^7.5.3"
electron-to-chromium@^1.4.284:
version "1.4.286"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz#0e039de59135f44ab9a8ec9025e53a9135eba11f"
@ -2624,6 +2693,11 @@ emoji-regex@^8.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
emoji-regex@^9.2.2:
version "9.2.2"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
encoding@^0.1.12:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
@ -2897,6 +2971,14 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
foreground-child@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d"
integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==
dependencies:
cross-spawn "^7.0.0"
signal-exit "^4.0.1"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@ -3103,6 +3185,17 @@ glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2:
dependencies:
is-glob "^4.0.1"
glob@^10.3.3:
version "10.3.10"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==
dependencies:
foreground-child "^3.1.0"
jackspeak "^2.3.5"
minimatch "^9.0.1"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-scurry "^1.10.1"
glob@^7.1, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
@ -3698,6 +3791,15 @@ istanbul-reports@^3.1.3:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"
jackspeak@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==
dependencies:
"@isaacs/cliui" "^8.0.2"
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
jest-changed-files@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5"
@ -4103,6 +4205,22 @@ jest@^27.4:
import-local "^3.0.2"
jest-cli "^27.5.1"
js-beautify@1.15:
version "1.15.1"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.1.tgz#4695afb508c324e1084ee0b952a102023fc65b64"
integrity sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==
dependencies:
config-chain "^1.1.13"
editorconfig "^1.0.4"
glob "^10.3.3"
js-cookie "^3.0.5"
nopt "^7.2.0"
js-cookie@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -4370,6 +4488,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
"lru-cache@^9.1.1 || ^10.0.0":
version "10.2.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==
make-dir@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@ -4507,6 +4630,13 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
minimatch@9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253"
integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==
dependencies:
brace-expansion "^2.0.1"
minimatch@^3.0, minimatch@^3.0.4, minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@ -4514,6 +4644,13 @@ minimatch@^3.0, minimatch@^3.0.4, minimatch@^3.1.1:
dependencies:
brace-expansion "^1.1.7"
minimatch@^9.0.1:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
dependencies:
brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@ -4595,6 +4732,11 @@ minipass@^4.0.0:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.1.tgz#2b9408c6e81bb8b338d600fb3685e375a370a057"
integrity sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA==
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0":
version "7.0.4"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
minizlib@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
@ -4742,6 +4884,13 @@ nopt@^5.0.0:
dependencies:
abbrev "1"
nopt@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7"
integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==
dependencies:
abbrev "^2.0.0"
normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@ -5158,6 +5307,14 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-scurry@^1.10.1:
version "1.10.1"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698"
integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==
dependencies:
lru-cache "^9.1.1 || ^10.0.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@ -5667,6 +5824,13 @@ semver@^6.0.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.5.3:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
dependencies:
lru-cache "^6.0.0"
serialize-javascript@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c"
@ -5712,6 +5876,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
signal-exit@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@ -5887,6 +6056,15 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@ -5905,6 +6083,15 @@ string-width@^1.0.1:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
dependencies:
eastasianwidth "^0.2.0"
emoji-regex "^9.2.2"
strip-ansi "^7.0.1"
string.prototype.trimend@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
@ -5937,6 +6124,13 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@ -5951,6 +6145,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
dependencies:
ansi-regex "^6.0.1"
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@ -6605,6 +6806,15 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@ -6614,6 +6824,15 @@ wrap-ansi@^7.0.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
dependencies:
ansi-styles "^6.1.0"
string-width "^5.0.1"
strip-ansi "^7.0.1"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"

View File

@ -1,6 +1,6 @@
if ! [ -d .venv ]
then
python -m venv .venv
python3 -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
else

View File

@ -1,6 +1,6 @@
# This used to just be
# PACKAGEVERSION := "`python3 setup.py --version`"
PACKAGEVERSION := $(shell python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
PACKAGEVERSION := $(shell ./print-package-version)
all: test build-docs

View File

@ -217,7 +217,11 @@ class Decoder(BinaryCodec):
if not vs: raise DecodeError('Too few elements in encoded record')
return self.wrap(Record(vs[0], vs[1:]))
if tag == 0xb5: return self.wrap(tuple(self.nextvalues()))
if tag == 0xb6: return self.wrap(frozenset(self.nextvalues()))
if tag == 0xb6:
vs = self.nextvalues()
s = frozenset(vs)
if len(s) != len(vs): raise DecodeError('Duplicate value')
return self.wrap(s)
if tag == 0xb7: return self.wrap(ImmutableDict.from_kvs(self.nextvalues()))
raise DecodeError('Invalid tag: ' + hex(tag))

View File

@ -59,13 +59,16 @@ The result of evaluating it on `testdata` is as follows:
"In each test, let stripped = strip(annotatedValue),"
" encodeBinary(·) produce canonical ordering and no annotations,"
" looseEncodeBinary(·) produce any ordering, but with annotations,"
" annotatedBinary(·) produce canonical ordering, but with annotations,"
" annotatedBinary(·) produce canonical ordering, but with annotations,"
" decodeBinary(·) include annotations,"
" encodeText(·) include annotations,"
" decodeText(·) include annotations,"
"and check the following numbered expectations according to the table above:"
"Implementations may vary in their treatment of the difference between expectations"
"21/22 and 31/32, depending on how they wish to treat end-of-stream conditions."
"The idea of canonical-ordering-with-annotations is to encode, say, sets with their elements"
"in sorted order of their canonical annotationless binary encoding, but then actually"
"*serialized* with the annotations present."
```

View File

@ -505,7 +505,7 @@ class SchemaObject:
return ()
if p.key == TUPLE:
if not sequenceish(v): raise SchemaDecodeFailed(cls, p, v)
if len(v) != len(p[0]): raise SchemaDecodeFailed(cls, p, v)
if len(v) < len(p[0]): raise SchemaDecodeFailed(cls, p, v)
i = 0
for pp in p[0]:
cls.parse(pp, v[i], args)

View File

@ -346,6 +346,10 @@ class Parser(TextCodec):
c = self.nextchar()
if c in ' \t': return self.unshift_annotation(self.comment_line(), self.next())
if c in '\n\r': return self.unshift_annotation('', self.next())
if c == '!':
return self.unshift_annotation(
Record(Symbol('interpreter'), [self.comment_line()]),
self.next())
if c == 'f': self.require_delimiter('#f'); return self.wrap(False)
if c == 't': self.require_delimiter('#t'); return self.wrap(True)
if c == '{': return self.wrap(self.read_set())

View File

@ -0,0 +1,5 @@
#!/bin/sh
cd "$(dirname "$0")"
. ./.envrc
exec python -c \
'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])'

View File

@ -1,6 +1,6 @@
[project]
name = "preserves"
version = "0.993.0"
version = "0.995.1"
description = "Data serialization format"
readme = "README.md"
requires-python = ">=3.6, <4"

View File

@ -14,7 +14,7 @@
"In each test, let stripped = strip(annotatedValue),"
" encodeBinary(·) produce canonical ordering and no annotations,"
" looseEncodeBinary(·) produce any ordering, but with annotations,"
" annotatedBinary(·) produce canonical ordering, but with annotations,"
" annotatedBinary(·) produce canonical ordering, but with annotations,"
" decodeBinary(·) include annotations,"
" encodeText(·) include annotations,"
" decodeText(·) include annotations,"
@ -39,6 +39,10 @@
}>
"Implementations may vary in their treatment of the difference between expectations"
"21/22 and 31/32, depending on how they wish to treat end-of-stream conditions."
"The idea of canonical-ordering-with-annotations is to encode, say, sets with their elements"
"in sorted order of their canonical annotationless binary encoding, but then actually"
"*serialized* with the annotations present."
]>
<TestCases {
annotation1: <Test #x"85 B103616263 B00109" @"abc" 9>
@ -62,6 +66,10 @@
<Test #x"85 B100 85 B1066e6f726d616c B000" #
# normal
0>
annotation12: @"Interpreter-specification annotations"
<Test #"\x85\xb4\xb3\x0binterpreter\xb1\x0a/some/path\x84\xb3\x05value"
#!/some/path
value>
bytes2: <Test #x"B20568656c6c6f" #"hello">
bytes2a: <Test @"Internal whitespace is allowed, not including commas" #x"B2 05 68 65 6c 6c 6f" #"hello">
bytes2b: @"Commas forbidden in internal whitespace" <ParseError "#x\"B2, 05, 68, 65, 6c, 6c, 6f\"">
@ -88,6 +96,7 @@
dict2: @"Missing close brace" <ParseShort "{ a: b, c: d ">
dict2a: @"Missing close brace" <ParseShort "{">
dict3: @"Duplicate key" <ParseError "{ a: 1, a: 2 }">
dict3a: @"Duplicate key" <DecodeError #x"b7 b00101 b00102 b00101 b00103 84">
dict4: @"Unexpected close brace" <ParseError "}">
dict5: @"Missing value" <DecodeError #x"b7 b00101 b00102 b00103 84">
dict6: @"Comma not allowed between key and colon" <ParseError "{ a,: 1, b: 2 }">
@ -187,6 +196,7 @@
set2: @"Missing close brace" <ParseShort "#{ 1 2 3 ">
set2a: @"Missing close brace" <ParseShort "#{">
set3: @"Duplicate value" <ParseError "#{a a}">
set3a: @"Duplicate value" <DecodeError #x"b6 b00101 b00101 84">
string0: <Test #x"b100" "">
string3: <Test #x"b10568656c6c6f" "hello">
string4: <Test #x"b1 14 616263e6b0b4e6b0b45c2f22080c0a0d0978797a" "abc\u6c34\u6C34\\/\"\b\f\n\r\txyz">
@ -221,6 +231,14 @@
symbol12: <Test #x"b3042d2d2d31" ---1>
symbol13: <Test #x"b3042b312e78" +1.x>
symbol14: <Test #x"b304f09d849e" |\uD834\uDD1E|>
symbol15: @"A 130-byte symbol"
<Test #x"b3 8201
6161616161616161616161616161616161616161616161616161616161616161
6161616161616161616161616161616161616161616161616161616161616161
6161616161616161616161616161616161616161616161616161616161616161
6161616161616161616161616161616161616161616161616161616161616161
6161"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>
tag0: @"Unexpected end tag" <DecodeError #x"84">
tag1: @"Invalid tag" <DecodeError #x"10">
tag2: @"Invalid tag" <DecodeError #x"61b10110">

View File

@ -105,7 +105,9 @@
'named
(list
(and ?name (? symbol?))
(app parse-SimplePattern (and ?pattern (not (== eof)))))))
(app parse-SimplePattern (and ?pattern (not (== eof))))
_
...)))
(Binding ?name ?pattern))
(_ eof)))
(define parse-Binding! (parse-success-or-error 'parse-Binding parse-Binding))
@ -126,7 +128,7 @@
((and dest
(record
'bundle
(list (app parse-Modules (and ?modules (not (== eof)))))))
(list (app parse-Modules (and ?modules (not (== eof)))) _ ...)))
(Bundle ?modules))
(_ eof)))
(define parse-Bundle! (parse-success-or-error 'parse-Bundle parse-Bundle))
@ -198,28 +200,34 @@
'rec
(list
(app parse-NamedPattern (and ?label (not (== eof))))
(app parse-NamedPattern (and ?fields (not (== eof)))))))
(app parse-NamedPattern (and ?fields (not (== eof))))
_
...)))
(CompoundPattern-rec ?label ?fields))
((and dest
(record
'tuple
(list
(list
(app parse-NamedPattern (and ?patterns (not (== eof))))
...))))
(list (app parse-NamedPattern (and ?patterns (not (== eof)))) ...)
_
...)))
(CompoundPattern-tuple ?patterns))
((and dest
(record
'tuplePrefix
(list
(list (app parse-NamedPattern (and ?fixed (not (== eof)))) ...)
(app parse-NamedSimplePattern (and ?variable (not (== eof)))))))
(app parse-NamedSimplePattern (and ?variable (not (== eof))))
_
...)))
(CompoundPattern-tuplePrefix ?fixed ?variable))
((and dest
(record
'dict
(list
(app parse-DictionaryEntries (and ?entries (not (== eof)))))))
(app parse-DictionaryEntries (and ?entries (not (== eof))))
_
...)))
(CompoundPattern-dict ?entries))
(_ eof)))
(define parse-CompoundPattern!
@ -283,7 +291,9 @@
(app parse-NamedAlternative (and ?pattern1 (not (== eof))))
(list
(app parse-NamedAlternative (and ?patternN (not (== eof))))
...)))))
...))
_
...)))
(Definition-or ?pattern0 ?pattern1 ?patternN))
((and dest
(record
@ -294,7 +304,9 @@
(app parse-NamedPattern (and ?pattern1 (not (== eof))))
(list
(app parse-NamedPattern (and ?patternN (not (== eof))))
...)))))
...))
_
...)))
(Definition-and ?pattern0 ?pattern1 ?patternN))
((app parse-Pattern (and dest (not (== eof)))) (Definition-Pattern dest))
(_ eof)))
@ -452,7 +464,9 @@
((and dest
(list
(and ?variantLabel (? string?))
(app parse-Pattern (and ?pattern (not (== eof))))))
(app parse-Pattern (and ?pattern (not (== eof))))
_
...))
(NamedAlternative ?variantLabel ?pattern))
(_ eof)))
(define parse-NamedAlternative!
@ -569,7 +583,9 @@
'ref
(list
(app parse-ModulePath (and ?module (not (== eof))))
(and ?name (? symbol?)))))
(and ?name (? symbol?))
_
...)))
(Ref ?module ?name))
(_ eof)))
(define parse-Ref! (parse-success-or-error 'parse-Ref parse-Ref))
@ -608,7 +624,9 @@
(app parse-EmbeddedTypeName (and ?embeddedType (not (== eof)))))
('version (app parse-Version (and ?version (not (== eof)))))
(_ _)
...))))
...)
_
...)))
(Schema ?definitions ?embeddedType ?version))
(_ eof)))
(define parse-Schema! (parse-success-or-error 'parse-Schema parse-Schema))
@ -718,30 +736,41 @@
((and dest
(record
'atom
(list (app parse-AtomKind (and ?atomKind (not (== eof)))))))
(list (app parse-AtomKind (and ?atomKind (not (== eof)))) _ ...)))
(SimplePattern-atom ?atomKind))
((and dest
(record
'embedded
(list (app parse-SimplePattern (and ?interface (not (== eof)))))))
(list
(app parse-SimplePattern (and ?interface (not (== eof))))
_
...)))
(SimplePattern-embedded ?interface))
((and dest (record 'lit (list ?value))) (SimplePattern-lit ?value))
((and dest (record 'lit (list ?value _ ...))) (SimplePattern-lit ?value))
((and dest
(record
'seqof
(list (app parse-SimplePattern (and ?pattern (not (== eof)))))))
(list
(app parse-SimplePattern (and ?pattern (not (== eof))))
_
...)))
(SimplePattern-seqof ?pattern))
((and dest
(record
'setof
(list (app parse-SimplePattern (and ?pattern (not (== eof)))))))
(list
(app parse-SimplePattern (and ?pattern (not (== eof))))
_
...)))
(SimplePattern-setof ?pattern))
((and dest
(record
'dictof
(list
(app parse-SimplePattern (and ?key (not (== eof))))
(app parse-SimplePattern (and ?value (not (== eof)))))))
(app parse-SimplePattern (and ?value (not (== eof))))
_
...)))
(SimplePattern-dictof ?key ?value))
((app parse-Ref (and dest (not (== eof)))) (SimplePattern-Ref dest))
(_ eof)))

View File

@ -58,7 +58,8 @@
,(pattern->match-pattern fields-pat '_)))]
[(CompoundPattern-tuple named-pats)
(maybe-dest dest-pat-stx
`(list ,@(map (lambda (p) (pattern->match-pattern p '_)) named-pats)))]
`(list ,@(map (lambda (p) (pattern->match-pattern p '_)) named-pats)
_ ...))]
[(CompoundPattern-tuplePrefix fixed-named-pats variable-named-pat)
(maybe-dest dest-pat-stx
(if (null? fixed-named-pats)

View File

@ -117,6 +117,7 @@
(match (peek-char in-port)
[(or (? eof-object?) #\] #\> #\} #\)) *TRAILER-ANCHOR*]
[_ (next)])))
#:interpreter-annotation (lambda (line) (record 'r (list 'interpreter line)))
#:on-hash (lambda (in-port source next parse-error* default)
(match-lambda
[#\{ (read-sequence 's in-port source next #\})]

View File

@ -83,7 +83,7 @@
[#xB3 (string->symbol (bytes->string/utf-8 (next-bytes (next-varint))))]
[#xB4 (apply (lambda (label . fields) (record label fields)) (next-items))]
[#xB5 (next-items)]
[#xB6 (list->set (next-items))]
[#xB6 (build-set (next-items))]
[#xB7 (build-dictionary (next-items))]
[_ (return (on-fail "Invalid Preserves binary tag: ~v" lead-byte))]))
@ -112,9 +112,17 @@
['#:end '()]
[v (cons (wrap pos0 v) (next-items))]))
(define (build-set items)
(define s (list->set items))
(unless (= (set-count s) (length items)) (return (on-fail "Duplicate value in set")))
s)
(define (build-dictionary items)
(when (not (even? (length items))) (return (on-fail "Odd number of items in dictionary")))
(apply hash items))
(define h (apply hash items))
(unless (= (hash-count h) (quotient (length items) 2))
(return (on-fail "Duplicate key in dictionary")))
h)
(let ((pos0 (pos)))
(match (read-byte in-port)

View File

@ -70,9 +70,10 @@
[(_ re pat) (app (lambda (v) (regexp-try-match re v)) pat)]))
(define ((make-preserve-text-reader #:reader-name reader-name
#:interpreter-annotation interpreter-annotation
#:read-annotated-value read-annotated-value0
#:on-char on-char0
#:on-hash on-hash0
#:read-annotated-value read-annotated-value0)
#:on-hash on-hash0)
in-port source read-syntax? decode-embedded0)
(define read-annotations? read-syntax?)
(define decode-embedded (or decode-embedded0
@ -106,6 +107,7 @@
[#\# (match (next-char*)
[(or #\space #\tab) (annotate-next-with (read-comment-line))]
[(or #\newline #\return) (annotate-next-with "")]
[#\! (annotate-next-with (interpreter-annotation (read-comment-line)))]
[#\f (unless (delimiter-follows?) (parse-error* "Delimiter must follow #f")) #f]
[#\t (unless (delimiter-follows?) (parse-error* "Delimiter must follow #t")) #t]
[#\" (read-literal-binary)]

View File

@ -15,6 +15,7 @@
(make-preserve-text-reader
#:reader-name *reader-name*
#:read-annotated-value (lambda (in-port source next parse-error*) next)
#:interpreter-annotation (lambda (line) `#s(interpreter ,line))
#:on-hash (lambda (in-port source next parse-error* default)
(match-lambda
[#\{ (sequence-fold in-port

View File

@ -14,7 +14,7 @@
"In each test, let stripped = strip(annotatedValue),"
" encodeBinary(·) produce canonical ordering and no annotations,"
" looseEncodeBinary(·) produce any ordering, but with annotations,"
" annotatedBinary(·) produce canonical ordering, but with annotations,"
" annotatedBinary(·) produce canonical ordering, but with annotations,"
" decodeBinary(·) include annotations,"
" encodeText(·) include annotations,"
" decodeText(·) include annotations,"
@ -39,6 +39,10 @@
}>
"Implementations may vary in their treatment of the difference between expectations"
"21/22 and 31/32, depending on how they wish to treat end-of-stream conditions."
"The idea of canonical-ordering-with-annotations is to encode, say, sets with their elements"
"in sorted order of their canonical annotationless binary encoding, but then actually"
"*serialized* with the annotations present."
]>
<TestCases {
annotation1: <Test #x"85 B103616263 B00109" @"abc" 9>
@ -62,6 +66,10 @@
<Test #x"85 B100 85 B1066e6f726d616c B000" #
# normal
0>
annotation12: @"Interpreter-specification annotations"
<Test #"\x85\xb4\xb3\x0binterpreter\xb1\x0a/some/path\x84\xb3\x05value"
#!/some/path
value>
bytes2: <Test #x"B20568656c6c6f" #"hello">
bytes2a: <Test @"Internal whitespace is allowed, not including commas" #x"B2 05 68 65 6c 6c 6f" #"hello">
bytes2b: @"Commas forbidden in internal whitespace" <ParseError "#x\"B2, 05, 68, 65, 6c, 6c, 6f\"">
@ -88,6 +96,7 @@
dict2: @"Missing close brace" <ParseShort "{ a: b, c: d ">
dict2a: @"Missing close brace" <ParseShort "{">
dict3: @"Duplicate key" <ParseError "{ a: 1, a: 2 }">
dict3a: @"Duplicate key" <DecodeError #x"b7 b00101 b00102 b00101 b00103 84">
dict4: @"Unexpected close brace" <ParseError "}">
dict5: @"Missing value" <DecodeError #x"b7 b00101 b00102 b00103 84">
dict6: @"Comma not allowed between key and colon" <ParseError "{ a,: 1, b: 2 }">
@ -187,6 +196,7 @@
set2: @"Missing close brace" <ParseShort "#{ 1 2 3 ">
set2a: @"Missing close brace" <ParseShort "#{">
set3: @"Duplicate value" <ParseError "#{a a}">
set3a: @"Duplicate value" <DecodeError #x"b6 b00101 b00101 84">
string0: <Test #x"b100" "">
string3: <Test #x"b10568656c6c6f" "hello">
string4: <Test #x"b1 14 616263e6b0b4e6b0b45c2f22080c0a0d0978797a" "abc\u6c34\u6C34\\/\"\b\f\n\r\txyz">
@ -221,6 +231,14 @@
symbol12: <Test #x"b3042d2d2d31" ---1>
symbol13: <Test #x"b3042b312e78" +1.x>
symbol14: <Test #x"b304f09d849e" |\uD834\uDD1E|>
symbol15: @"A 130-byte symbol"
<Test #x"b3 8201
6161616161616161616161616161616161616161616161616161616161616161
6161616161616161616161616161616161616161616161616161616161616161
6161616161616161616161616161616161616161616161616161616161616161
6161616161616161616161616161616161616161616161616161616161616161
6161"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>
tag0: @"Unexpected end tag" <DecodeError #x"84">
tag1: @"Invalid tag" <DecodeError #x"10">
tag2: @"Invalid tag" <DecodeError #x"61b10110">

View File

@ -138,6 +138,16 @@ PrecType = "PREC" / "PREC_LEFT" / "PREC_RIGHT" / "PREC_DYNAMIC";
ENDDOC
)
(define U #<<ENDDOC
#!/example/of/a/shebang
{
key: value
# example of a comment at the end of a dictionary
}
# example of a comment at the end of the input file
ENDDOC
)
(parameterize ((pretty-print-columns 80)) (write-pexprs (string->pexprs P #:read-syntax? #t)) (newline))
(parameterize ((pretty-print-columns 10)) (write-pexprs (string->pexprs P #:read-syntax? #t)) (newline))
@ -149,3 +159,5 @@ ENDDOC
(parameterize ((pretty-print-columns 40)) (write-pexprs (string->pexprs S #:read-syntax? #t)) (newline))
(parameterize ((pretty-print-columns 40)) (write-pexprs (string->pexprs T #:read-syntax? #t)) (newline))
(parameterize ((pretty-print-columns 80)) (write-pexprs (string->pexprs U #:read-syntax? #t)) (newline))

View File

@ -197,7 +197,7 @@ a binary-syntax document; otherwise, it should be interpreted as text.
86 - Embedded
87 - Double
B0 - Integer
B0 - SignedInteger
B1 - String
B2 - ByteString
B3 - Symbol

View File

@ -3,7 +3,7 @@ title: "P-expressions"
---
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
February 2024. Version 0.3.1.
May 2024. Version 0.3.2.
[text syntax]: preserves-text.html
@ -58,7 +58,7 @@ in that uses of `Value` are replaced with `SimpleExpr`.
Embedded = "#:" SimpleExpr
Annotated = Annotation SimpleExpr
Annotation = "@" SimpleExpr / "#" [(%x20 / %x09) linecomment] (CR / LF)
Annotation = "@" SimpleExpr / "#" [(%x20 / %x09/ %x21) linecomment] (CR / LF)
linecomment = *<any unicode scalar value except CR or LF>
P-expression special punctuation marks are comma, semicolon, and
@ -223,12 +223,14 @@ text-syntax encodings.
### Whole `Document`s
```preserves
⌜{
⌜#!/example/of/a/shebang
{
key: value
# example of a comment at the end of a dictionary
}
# example of a comment at the end of the input file⌝
= [ <b
= @<r interpreter "/example/of/a/shebang">
[ <b
key <p |:|> value
@"example of a comment at the end of a dictionary" <a>
>

View File

@ -4,7 +4,7 @@ title: "Preserves Schema"
---
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
December 2023. Version 0.3.5.
April 2024. Version 0.4.
[abnf]: https://tools.ietf.org/html/rfc7405
[identifierlike]: #sufficiently-identifierlike-values
@ -198,7 +198,15 @@ The right-hand-side of a definition may supply two or more *alternatives*.
Alternatives are separated by any number of slashes `/`, and leading or
trailing slashes are ignored. When parsing, the alternatives are tried in
order; the result of the first successful alternative is the result of the
entire parse.
entire parse.[^extensibility-and-alternation]
[^extensibility-and-alternation]: This ordered choice becomes important
when combined with the extensibility of e.g. record patterns: A pattern
`<a @b int> / <a @b int @c int>` will always match the left branch
because compound patterns match *prefixes* of records and tuples. A
better way to write it would be `<a @b int @c int> / <a @b int>`, which
tries the at-least-two-fields branch before falling back to the
at-least-one-field branch.
**Host-language types.** The type corresponding to an `OrPattern` is an
algebraic sum type, a union type, a variant type, or a concrete subclass
@ -415,6 +423,18 @@ entry.
DictionaryPattern = "{" *(value ":" NamedSimplePattern) "}"
**Extensibility.** Each apparently fixed-size compound pattern over
records, tuples or dictionaries actually only places a lower bound on the
size of matching data. These patterns thus allow for extensibility by
ignoring additional fields, elements, or dictionary entries. For example,
the pattern `<a @value int>` will match not only `<a 123>` but also `<a 123
"hello">`, while rejecting `<a>` or `<a [x y z]>`. Similarly, `{a: int, b:
int}` matches `{a: 123, b: 234, c: [x y z]}` as well as `{a: 123, b: 234}`.
Each compound pattern places constraints only on the *mentioned* elements
of a datum, namely the leftmost fields of a record, the leftmost elements
of a tuple, and the mentioned keys of a dictionary. Unmentioned elements
are free to be present or absent.
### Identifiers and Bindings: NamedPattern and NamedSimplePattern
Compound patterns specifications contain `NamedPattern`s or

View File

@ -294,6 +294,40 @@ the end of the line is included in the string annotating the `Value`.
Comments that are just hash `#` followed immediately by newline yield an
empty-string annotation.
**Interpreter specification lines.** Unix systems specially interpret
`#!` at the beginning of a file. For convenient use of Preserves syntax
in Unix scripts, we define an interpretation for `#!` lines.
Annotation =/ "#!" linecomment (CR / LF)
Such annotations are read as a record with label `interpreter` and with
a single string field containing the text following the `!` and
preceding the end of the line.[^printing-interpreter-specifications]
[^multiple-interpreter-specifications]
[^printing-interpreter-specifications]: We encourage implementations
to *print* annotations of the form `<interpreter` *string*`>` using
the special `#!` syntax, as well, so long as the string does not
contain a carriage return or line feed character.
[^multiple-interpreter-specifications]: Annotations written with `#!`
are otherwise ordinary. In particular, *multiple* such lines are
nothing special. For example,
```
#!/one
#!/two
# three
#!/four
five
```
reads as
```
@<interpreter "/one"> @<interpreter "/two"> @"three" @<interpreter "/four"> five
```
**Equivalence.** Annotations appear within syntax denoting a `Value`;
however, the annotations are not part of the denoted value. They are
only part of the syntax. Annotations do not play a part in

View File

@ -2,6 +2,7 @@
;; Add code like the following to your .emacs to install:
;; (autoload 'preserves-mode "~/src/preserves/preserves.el" nil t)
;; (add-to-list 'auto-mode-alist '("\\.px\\'" . preserves-mode))
;; (add-to-list 'auto-mode-alist '("\\.pr\\'" . preserves-mode))
;; (add-to-list 'auto-mode-alist '("\\.prs\\'" . preserves-mode))

View File

@ -215,7 +215,7 @@ sequences use [the Preserves binary encoding](preserves-binary.html).
The total ordering specified [above](#total-order) means that the following statements are true:
- `"bzz"` &lt; `"c"` &lt; `"caa"` &lt; `#:"a"`
- `#t` &lt; `3.0f` &lt; `3.0` &lt; `3` &lt; `"3"` &lt; `|3|` &lt; `[]` &lt; `#:#t`
- `#t` &lt; `3.0` &lt; `3` &lt; `"3"` &lt; `|3|` &lt; `[]` &lt; `#:#t`
- `[#f]` &lt; `[foo]`, because `Boolean` appears before `Symbol` in the kind ordering
- `[x]` &lt; `[x y]`, because there is no element remaining to compare against `y`
- `[a b]` &lt; `[x]`, because `a` is smaller than `x`
@ -240,7 +240,6 @@ The total ordering specified [above](#total-order) means that the following stat
| `0` | B0 00 |
| `1` | B0 01 01 |
| `255` | B0 02 00 FF |
| `1.0f` | 87 04 3F 80 00 00 |
| `1.0` | 87 08 3F F0 00 00 00 00 00 00 |
| `-1.202e300` | 87 08 FE 3C B7 B7 59 BF 04 26 |
| `#xd"fff0000000000000"`, negative `Double` infinity | 87 08 FF F0 00 00 00 00 00 00 |
@ -431,7 +430,7 @@ from each that is missing from the other. If the values are incompatible, they h
**Examples.**
- `merge [1, [2], 3] [1, [2, 99], 3, 4, 5] = merge [1, [2, 99], 3, 4, 5]`
- `merge [1, [2], 3] [1, [2, 99], 3, 4, 5] = [1, [2, 99], 3, 4, 5]`
- `merge [1, 2, 3] [1, 5, 3] = ⊥`
- `merge #{a, b, c} #{a, b, c} = ⊥`
- `merge {a: 1, b: [2]} {b: [2, 99] c: 3} = {a: 1, b: [2, 99], c: 3}`

498
python/0.994.0/404.html Normal file
View File

@ -0,0 +1,498 @@
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="icon" href="/python/latest/assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.7">
<title>Python Preserves</title>
<link rel="stylesheet" href="/python/latest/assets/stylesheets/main.f2e4d321.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<link rel="stylesheet" href="/python/latest/assets/_mkdocstrings.css">
<script>__md_scope=new URL("/python/latest/",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
</div>
<div data-md-component="announce">
</div>
<div data-md-color-scheme="default" data-md-component="outdated" hidden>
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="/python/latest/." title="Python Preserves" class="md-header__button md-logo" aria-label="Python Preserves" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Python Preserves
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
</span>
</div>
</div>
</div>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
</button>
</nav>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://gitlab.com/preserves/preserves" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
GitLab
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="/python/latest/." title="Python Preserves" class="md-nav__button md-logo" aria-label="Python Preserves" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
Python Preserves
</label>
<div class="md-nav__source">
<a href="https://gitlab.com/preserves/preserves" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
GitLab
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="/python/latest/." class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/api/" class="md-nav__link">
<span class="md-ellipsis">
The top-level preserves package
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/binary/" class="md-nav__link">
<span class="md-ellipsis">
Machine-oriented binary syntax
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/compare/" class="md-nav__link">
<span class="md-ellipsis">
Comparing Values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/error/" class="md-nav__link">
<span class="md-ellipsis">
Codec errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/fold/" class="md-nav__link">
<span class="md-ellipsis">
Traversing values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/merge/" class="md-nav__link">
<span class="md-ellipsis">
Merging values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/path/" class="md-nav__link">
<span class="md-ellipsis">
Preserves Path
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/schema/" class="md-nav__link">
<span class="md-ellipsis">
Preserves Schema
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/text/" class="md-nav__link">
<span class="md-ellipsis">
Human-readable text syntax
</span>
</a>
</li>
<li class="md-nav__item">
<a href="/python/latest/values/" class="md-nav__link">
<span class="md-ellipsis">
Representations of Values
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1>404 - Not found</h1>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "/python/latest/", "features": [], "search": "/python/latest/assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
<script src="/python/latest/assets/javascripts/bundle.caa56a14.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,801 @@
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="canonical" href="https://preserves.dev/python/latest/api/">
<link rel="prev" href="..">
<link rel="next" href="../binary/">
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.7">
<title>The top-level preserves package - Python Preserves</title>
<link rel="stylesheet" href="../assets/stylesheets/main.f2e4d321.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<link rel="stylesheet" href="../assets/_mkdocstrings.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#the-top-level-preserves-package" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<div data-md-color-scheme="default" data-md-component="outdated" hidden>
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="Python Preserves" class="md-header__button md-logo" aria-label="Python Preserves" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Python Preserves
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
The top-level preserves package
</span>
</div>
</div>
</div>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
</button>
</nav>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://gitlab.com/preserves/preserves" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
GitLab
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="Python Preserves" class="md-nav__button md-logo" aria-label="Python Preserves" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
</a>
Python Preserves
</label>
<div class="md-nav__source">
<a href="https://gitlab.com/preserves/preserves" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
</div>
<div class="md-source__repository">
GitLab
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href=".." class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
The top-level preserves package
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
The top-level preserves package
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#preserves" class="md-nav__link">
<span class="md-ellipsis">
preserves
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#preserves.dumps" class="md-nav__link">
<span class="md-ellipsis">
dumps
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#preserves.loads" class="md-nav__link">
<span class="md-ellipsis">
loads
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../binary/" class="md-nav__link">
<span class="md-ellipsis">
Machine-oriented binary syntax
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../compare/" class="md-nav__link">
<span class="md-ellipsis">
Comparing Values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../error/" class="md-nav__link">
<span class="md-ellipsis">
Codec errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../fold/" class="md-nav__link">
<span class="md-ellipsis">
Traversing values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../merge/" class="md-nav__link">
<span class="md-ellipsis">
Merging values
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../path/" class="md-nav__link">
<span class="md-ellipsis">
Preserves Path
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../schema/" class="md-nav__link">
<span class="md-ellipsis">
Preserves Schema
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../text/" class="md-nav__link">
<span class="md-ellipsis">
Human-readable text syntax
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../values/" class="md-nav__link">
<span class="md-ellipsis">
Representations of Values
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#preserves" class="md-nav__link">
<span class="md-ellipsis">
preserves
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#preserves.dumps" class="md-nav__link">
<span class="md-ellipsis">
dumps
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#preserves.loads" class="md-nav__link">
<span class="md-ellipsis">
loads
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="the-top-level-preserves-package">The top-level preserves package</h1>
<div class="doc doc-object doc-module">
<a id="preserves"></a>
<div class="doc doc-contents first">
<div class="highlight"><pre><span></span><code>import preserves
</code></pre></div>
<p>The main package re-exports a subset of the exports of its constituent modules:</p>
<ul>
<li>
<p>From <a class="autorefs autorefs-internal" href="../values/#preserves.values">preserves.values</a>:</p>
<ul>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.Annotated">Annotated</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.Embedded">Embedded</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.ImmutableDict">ImmutableDict</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.Record">Record</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.Symbol">Symbol</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.annotate">annotate</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.is_annotated">is_annotated</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.preserve">preserve</a></li>
<li><a class="autorefs autorefs-internal" href="../values/#preserves.values.strip_annotations">strip_annotations</a></li>
</ul>
</li>
<li>
<p>From <a class="autorefs autorefs-internal" href="../error/#preserves.error">preserves.error</a>:</p>
<ul>
<li><a class="autorefs autorefs-internal" href="../error/#preserves.error.DecodeError">DecodeError</a></li>
<li><a class="autorefs autorefs-internal" href="../error/#preserves.error.EncodeError">EncodeError</a></li>
<li><a class="autorefs autorefs-internal" href="../error/#preserves.error.ShortPacket">ShortPacket</a></li>
</ul>
</li>
<li>
<p>From <a class="autorefs autorefs-internal" href="../binary/#preserves.binary">preserves.binary</a>:</p>
<ul>
<li><a class="autorefs autorefs-internal" href="../binary/#preserves.binary.Decoder">Decoder</a></li>
<li><a class="autorefs autorefs-internal" href="../binary/#preserves.binary.Encoder">Encoder</a></li>
<li><a class="autorefs autorefs-internal" href="../binary/#preserves.binary.canonicalize">canonicalize</a></li>
<li><a class="autorefs autorefs-internal" href="../binary/#preserves.binary.decode">decode</a></li>
<li><a class="autorefs autorefs-internal" href="../binary/#preserves.binary.decode_with_annotations">decode_with_annotations</a></li>
<li><a class="autorefs autorefs-internal" href="../binary/#preserves.binary.encode">encode</a></li>
</ul>
</li>
<li>
<p>From <a class="autorefs autorefs-internal" href="../text/#preserves.text">preserves.text</a>:</p>
<ul>
<li><a class="autorefs autorefs-internal" href="../text/#preserves.text.Formatter">Formatter</a></li>
<li><a class="autorefs autorefs-internal" href="../text/#preserves.text.Parser">Parser</a></li>
<li><a class="autorefs autorefs-internal" href="../text/#preserves.text.parse">parse</a></li>
<li><a class="autorefs autorefs-internal" href="../text/#preserves.text.parse_with_annotations">parse_with_annotations</a></li>
<li><a class="autorefs autorefs-internal" href="../text/#preserves.text.stringify">stringify</a></li>
</ul>
</li>
<li>
<p>From <a class="autorefs autorefs-internal" href="../compare/#preserves.compare">preserves.compare</a>:</p>
<ul>
<li><a class="autorefs autorefs-internal" href="../compare/#preserves.compare.cmp">cmp</a></li>
</ul>
</li>
<li>
<p>From <a class="autorefs autorefs-internal" href="../merge/#preserves.merge">preserves.merge</a>:</p>
<ul>
<li><a class="autorefs autorefs-internal" href="../merge/#preserves.merge.merge">merge</a></li>
</ul>
</li>
</ul>
<p>It also exports the <a class="autorefs autorefs-internal" href="../compare/#preserves.compare">compare</a> and <a class="autorefs autorefs-internal" href="../fold/#preserves.fold">fold</a> modules themselves,
permitting patterns like</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">preserves</span> <span class="kn">import</span> <span class="o">*</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">compare</span><span class="o">.</span><span class="n">cmp</span><span class="p">(</span><span class="mi">123</span><span class="p">,</span> <span class="mi">234</span><span class="p">)</span>
<span class="o">-</span><span class="mi">1</span>
</code></pre></div>
<p>Finally, it provides a few utility aliases for common tasks:</p>
<div class="doc doc-children">
<div class="doc doc-object doc-attribute">
<h2 id="preserves.dumps" class="doc doc-heading">
<code class="highlight language-python"><span class="n">dumps</span> <span class="o">=</span> <span class="n">stringify</span></code>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-module-attribute"><code>module-attribute</code></small>
</span>
</h2>
<div class="doc doc-contents ">
<p>This alias for <code>stringify</code> provides a familiar pythonesque name for converting a Preserves <code>Value</code> to a string.</p>
</div>
</div>
<div class="doc doc-object doc-attribute">
<h2 id="preserves.loads" class="doc doc-heading">
<code class="highlight language-python"><span class="n">loads</span> <span class="o">=</span> <span class="n">parse</span></code>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-module-attribute"><code>module-attribute</code></small>
</span>
</h2>
<div class="doc doc-contents ">
<p>This alias for <code>parse</code> provides a familiar pythonesque name for converting a string to a Preserves <code>Value</code>.</p>
</div>
</div>
</div>
</div>
</div>
<aside class="md-source-file">
<span class="md-source-file__fact">
<span class="md-icon" title="Last update">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1-2.1-2M12.5 7v5.2l4 2.4-1 1L11 13V7h1.5M11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2v1.8Z"/></svg>
</span>
<span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">March 16, 2023</span>
</span>
<span class="md-source-file__fact">
<span class="md-icon" title="Created">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14.47 15.08 11 13V7h1.5v5.25l3.08 1.83c-.41.28-.79.62-1.11 1m-1.39 4.84c-.36.05-.71.08-1.08.08-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8c0 .37-.03.72-.08 1.08.69.1 1.33.32 1.92.64.1-.56.16-1.13.16-1.72 0-5.5-4.5-10-10-10S2 6.5 2 12s4.47 10 10 10c.59 0 1.16-.06 1.72-.16-.32-.59-.54-1.23-.64-1.92M18 15v3h-3v2h3v3h2v-3h3v-2h-3v-3h-2Z"/></svg>
</span>
<span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">March 16, 2023</span>
</span>
</aside>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"base": "..", "features": [], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
<script src="../assets/javascripts/bundle.caa56a14.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,109 @@
/* Avoid breaking parameter names, etc. in table cells. */
.doc-contents td code {
word-break: normal !important;
}
/* No line break before first paragraph of descriptions. */
.doc-md-description,
.doc-md-description>p:first-child {
display: inline;
}
/* Max width for docstring sections tables. */
.doc .md-typeset__table,
.doc .md-typeset__table table {
display: table !important;
width: 100%;
}
.doc .md-typeset__table tr {
display: table-row;
}
/* Defaults in Spacy table style. */
.doc-param-default {
float: right;
}
/* Symbols in Navigation and ToC. */
:root,
[data-md-color-scheme="default"] {
--doc-symbol-attribute-fg-color: #953800;
--doc-symbol-function-fg-color: #8250df;
--doc-symbol-method-fg-color: #8250df;
--doc-symbol-class-fg-color: #0550ae;
--doc-symbol-module-fg-color: #5cad0f;
--doc-symbol-attribute-bg-color: #9538001a;
--doc-symbol-function-bg-color: #8250df1a;
--doc-symbol-method-bg-color: #8250df1a;
--doc-symbol-class-bg-color: #0550ae1a;
--doc-symbol-module-bg-color: #5cad0f1a;
}
[data-md-color-scheme="slate"] {
--doc-symbol-attribute-fg-color: #ffa657;
--doc-symbol-function-fg-color: #d2a8ff;
--doc-symbol-method-fg-color: #d2a8ff;
--doc-symbol-class-fg-color: #79c0ff;
--doc-symbol-module-fg-color: #baff79;
--doc-symbol-attribute-bg-color: #ffa6571a;
--doc-symbol-function-bg-color: #d2a8ff1a;
--doc-symbol-method-bg-color: #d2a8ff1a;
--doc-symbol-class-bg-color: #79c0ff1a;
--doc-symbol-module-bg-color: #baff791a;
}
code.doc-symbol {
border-radius: .1rem;
font-size: .85em;
padding: 0 .3em;
font-weight: bold;
}
code.doc-symbol-attribute {
color: var(--doc-symbol-attribute-fg-color);
background-color: var(--doc-symbol-attribute-bg-color);
}
code.doc-symbol-attribute::after {
content: "attr";
}
code.doc-symbol-function {
color: var(--doc-symbol-function-fg-color);
background-color: var(--doc-symbol-function-bg-color);
}
code.doc-symbol-function::after {
content: "func";
}
code.doc-symbol-method {
color: var(--doc-symbol-method-fg-color);
background-color: var(--doc-symbol-method-bg-color);
}
code.doc-symbol-method::after {
content: "meth";
}
code.doc-symbol-class {
color: var(--doc-symbol-class-fg-color);
background-color: var(--doc-symbol-class-bg-color);
}
code.doc-symbol-class::after {
content: "class";
}
code.doc-symbol-module {
color: var(--doc-symbol-module-fg-color);
background-color: var(--doc-symbol-module-bg-color);
}
code.doc-symbol-module::after {
content: "mod";
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More