Switch from `#!` to `#:` for embedded values
This commit is contained in:
parent
83697b0e56
commit
e923d87fa5
|
@ -13,5 +13,5 @@ defaults:
|
|||
layout: page
|
||||
|
||||
title: "Preserves"
|
||||
version_date: "January 2024"
|
||||
version: "0.993.0"
|
||||
version_date: "February 2024"
|
||||
version: "0.994.0"
|
||||
|
|
|
@ -5,7 +5,7 @@ For a value `V`, we write `«V»` for the binary encoding of `V`.
|
|||
«#t» = [0x81]
|
||||
|
||||
«@W V» = [0x85] ++ «W» ++ «V»
|
||||
«#!V» = [0x86] ++ «V»
|
||||
«#:V» = [0x86] ++ «V»
|
||||
|
||||
«V» if V ∈ Double = [0x87, 0x08] ++ binary64(V)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ class="postcard-grammar binarysyntax">*V*</span>.
|
|||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«`@`*W* *V*» | = | `85` «*W*» «*V*»
|
||||
«`#!`*V*» | = | `86` «*V*»
|
||||
«`#:`*V*» | = | `86` «*V*»
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«*V*» | = | `87``08` **binary64**(*V*) | if *V* ∈ Double
|
||||
|
|
|
@ -15,7 +15,7 @@ Set := `#{` Expr* Trailer ws `}`
|
|||
|
||||
Trailer := (ws Annotation)*
|
||||
|
||||
Embedded := `#!` SimpleExpr
|
||||
Embedded := `#:` SimpleExpr
|
||||
Annotated := Annotation SimpleExpr
|
||||
Annotation := `@` SimpleExpr | `#` ((space | tab) linecomment) (cr | lf)
|
||||
```
|
||||
|
|
|
@ -18,6 +18,6 @@ The definitions of `Atom`, `ws`, and `linecomment` are as given in the Preserves
|
|||
| *Trailer* | := | (**ws** *Annotation*)<sup>⋆</sup>
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Embedded* | := | `#!` *SimpleExpr*
|
||||
| *Embedded* | := | `#:` *SimpleExpr*
|
||||
| *Annotated* | := | *Annotation* *SimpleExpr*
|
||||
| *Annotation* | := | `@` *SimpleExpr* | `#` ((**space** | **tab**) *linecomment*) (**cr** | **lf**)
|
||||
|
|
|
@ -9,7 +9,7 @@ Set := `#{` (commas Value)* commas `}`
|
|||
Dictionary := `{` (commas Value ws `:` Value)* commas `}`
|
||||
commas := (ws `,`)* ws
|
||||
|
||||
Embedded := `#!` Value
|
||||
Embedded := `#:` Value
|
||||
Annotated := Annotation Value
|
||||
Annotation := `@` Value | `#` ((space | tab) linecomment) (cr | lf)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
| **commas** | := | (**ws** `,`)<sup>⋆</sup> **ws** |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Embedded* | := | `#!`*Value* |
|
||||
| *Embedded* | := | `#:`*Value* |
|
||||
| *Annotated* | := | *Annotation* *Value* |
|
||||
| *Annotation* | := | `@`*Value* |`#` ((**space** | **tab**) *linecomment*) (**cr** | **lf**) |
|
||||
|
||||
|
|
|
@ -11,6 +11,6 @@ syntax](https://preserves.dev/preserves-text.html):
|
|||
Sequence : [value1 value2 ...]
|
||||
Set : #{value1 value2 ...}
|
||||
Dictionary : {key1: value1 key2: value2 ...: ...}
|
||||
Embedded : #!value
|
||||
Embedded : #:value
|
||||
|
||||
Commas are optional in sequences, sets, and dictionaries.
|
||||
|
|
|
@ -41,7 +41,7 @@ let render
|
|||
)
|
||||
m
|
||||
++ " }"
|
||||
, embedded = λ(value : Text) → "#!${value}"
|
||||
, embedded = λ(value : Text) → "#:${value}"
|
||||
}
|
||||
|
||||
let Preserves/boolean = ./boolean.dhall
|
||||
|
@ -94,7 +94,7 @@ let example0 =
|
|||
)}
|
||||
''
|
||||
≡ ''
|
||||
{ a: 1 b: [ 2 3 ] c: { d: 1.0 e: -1.0 } d: #!#t e: <capture <_>> }
|
||||
{ a: 1 b: [ 2 3 ] c: { d: 1.0 e: -1.0 } d: #:#t e: <capture <_>> }
|
||||
''
|
||||
|
||||
in render
|
||||
|
|
|
@ -26,7 +26,7 @@ export class Embedded<T> {
|
|||
}
|
||||
|
||||
toString(): string {
|
||||
return '#!' + (this.embeddedValue as any).toString();
|
||||
return '#:' + (this.embeddedValue as any).toString();
|
||||
}
|
||||
|
||||
__as_preserve__<R>(): T extends R ? Value<R> : never {
|
||||
|
|
|
@ -354,7 +354,7 @@ 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 embed(this.embeddedType.fromValue(
|
||||
new Reader<GenericEmbedded>(this.state, genericEmbeddedTypeDecode).next(),
|
||||
this.state.options));
|
||||
default:
|
||||
|
|
|
@ -297,7 +297,7 @@ export class Writer<T> {
|
|||
}
|
||||
else {
|
||||
((v: Embedded<T>) => {
|
||||
this.state.pieces.push('#!');
|
||||
this.state.pieces.push('#:');
|
||||
if ('write' in this.embeddedWrite) {
|
||||
this.embeddedWrite.write(this.state, v.embeddedValue);
|
||||
} else {
|
||||
|
|
|
@ -18,7 +18,7 @@ describe('reader schema', () => {
|
|||
expect(s.embeddedType._variant).toBe('false');
|
||||
});
|
||||
it('understands patterns under embed', () => {
|
||||
const s = readSchema('version 1 . X = #!0 .');
|
||||
const s = readSchema('version 1 . X = #:0 .');
|
||||
const def: Meta.Definition = s.definitions.get(Symbol.for('X'))!;
|
||||
if (def._variant !== 'Pattern') fail('bad definition 1');
|
||||
if (def.value._variant !== 'SimplePattern') fail ('bad definition 2');
|
||||
|
|
|
@ -9,7 +9,7 @@ def map_embeddeds(f, v):
|
|||
|
||||
```python
|
||||
>>> map_embeddeds(lambda w: Embedded(f'w={w}'), ['a', Embedded(123), {'z': 6.0}])
|
||||
('a', #!'w=123', {'z': 6.0})
|
||||
('a', #:'w=123', {'z': 6.0})
|
||||
|
||||
```
|
||||
"""
|
||||
|
|
|
@ -356,7 +356,7 @@ class Parser(TextCodec):
|
|||
if c == 'd': return self.wrap(self.read_hex_float())
|
||||
raise DecodeError('Invalid #x syntax')
|
||||
if c == '[': return self.wrap(self.read_base64_binary())
|
||||
if c == '!':
|
||||
if c == ':':
|
||||
if self.parse_embedded is None:
|
||||
raise DecodeError('No parse_embedded function supplied')
|
||||
return self.wrap(Embedded(self.parse_embedded(self.next())))
|
||||
|
|
|
@ -575,7 +575,7 @@ class Embedded:
|
|||
>>> import io
|
||||
>>> e = Embedded(io.StringIO('some text'))
|
||||
>>> e # doctest: +ELLIPSIS
|
||||
#!<_io.StringIO object at ...>
|
||||
#:<_io.StringIO object at ...>
|
||||
>>> e.embeddedValue # doctest: +ELLIPSIS
|
||||
<_io.StringIO object at ...>
|
||||
|
||||
|
@ -588,7 +588,7 @@ class Embedded:
|
|||
...
|
||||
TypeError: Cannot preserves-format: None
|
||||
>>> print(preserves.stringify(Embedded(None), format_embedded=lambda x: 'abcdef'))
|
||||
#!"abcdef"
|
||||
#:"abcdef"
|
||||
|
||||
```
|
||||
|
||||
|
@ -610,12 +610,12 @@ class Embedded:
|
|||
return hash(self.embeddedValue)
|
||||
|
||||
def __repr__(self):
|
||||
return '#!%r' % (self.embeddedValue,)
|
||||
return '#:%r' % (self.embeddedValue,)
|
||||
|
||||
def __preserve_write_binary__(self, encoder):
|
||||
encoder.buffer.append(0x86)
|
||||
encoder.append(encoder.encode_embedded(self.embeddedValue))
|
||||
|
||||
def __preserve_write_text__(self, formatter):
|
||||
formatter.chunks.append('#!')
|
||||
formatter.chunks.append('#:')
|
||||
formatter.append(formatter.format_embedded(self.embeddedValue))
|
||||
|
|
|
@ -167,9 +167,9 @@
|
|||
list11: <Test #x"b5b0010184" [01]>
|
||||
list12: <Test #x"b5b0010c84" [12]>
|
||||
noinput0: @"No input at all" <DecodeEOF #x"">
|
||||
embed0: <Test #x"86b000" #!0>
|
||||
embed1: <Test #x"8686b000" #!#!0>
|
||||
embed2: <Test #x"b586b00086b10568656c6c6f84" [#!0 #!"hello"]>
|
||||
embed0: <Test #x"86b000" #:0>
|
||||
embed1: <Test #x"8686b000" #:#:0>
|
||||
embed2: <Test #x"b586b00086b10568656c6c6f84" [#:0 #:"hello"]>
|
||||
record1: <Test #x"b4 b30763617074757265 b4 b30764697363617264 84 84" <capture <discard>>>
|
||||
record2: <Test #x"b4 b3076f627365727665 b4 b305737065616b b4 b30764697363617264 84 b4 b30763617074757265 b4 b30764697363617264 84 84 84 84" <observe <speak <discard> <capture <discard>>>>>
|
||||
record2a: @"Commas not allowed in records" <ParseError "<observe <speak <discard>, <capture <discard>>>>">
|
||||
|
|
|
@ -41,7 +41,7 @@ SimplePattern =
|
|||
# special builtins: bool, double, int, string, bytes, symbol
|
||||
/ <atom @atomKind AtomKind>
|
||||
|
||||
# matches an embedded value in the input: #!p
|
||||
# matches an embedded value in the input: #:p
|
||||
/ <embedded @interface SimplePattern>
|
||||
|
||||
# =symbol, <<lit> any>, or plain non-symbol atom
|
||||
|
|
|
@ -279,7 +279,7 @@
|
|||
[(BLOCK ps ...) (grouped (convert-inner ps) " " "" "{ " " }")]
|
||||
[(SET ps ...) (grouped (convert-inner ps) " " "" "#{ " " }")]
|
||||
[(TRAILER-ANCHOR) ""]
|
||||
[(embedded v) (list "#!" (convert (encode-embedded v)))]
|
||||
[(embedded v) (list "#:" (convert (encode-embedded v)))]
|
||||
[(strip-annotations (record 'p (list s))) (symbol->string s)]
|
||||
[v (preserve->string v)]))
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
[#\d (read-hex-float)]
|
||||
[c (parse-error* "Invalid #x syntax: ~v" c)])]
|
||||
[#\[ (read-base64-binary '())]
|
||||
[#\! (embedded (decode-embedded (next)))]
|
||||
[#\: (embedded (decode-embedded (next)))]
|
||||
[c (on-hash c)])]
|
||||
|
||||
[c (on-char c)]))
|
||||
|
|
|
@ -167,9 +167,9 @@
|
|||
list11: <Test #x"b5b0010184" [01]>
|
||||
list12: <Test #x"b5b0010c84" [12]>
|
||||
noinput0: @"No input at all" <DecodeEOF #x"">
|
||||
embed0: <Test #x"86b000" #!0>
|
||||
embed1: <Test #x"8686b000" #!#!0>
|
||||
embed2: <Test #x"b586b00086b10568656c6c6f84" [#!0 #!"hello"]>
|
||||
embed0: <Test #x"86b000" #:0>
|
||||
embed1: <Test #x"8686b000" #:#:0>
|
||||
embed2: <Test #x"b586b00086b10568656c6c6f84" [#:0 #:"hello"]>
|
||||
record1: <Test #x"b4 b30763617074757265 b4 b30764697363617264 84 84" <capture <discard>>>
|
||||
record2: <Test #x"b4 b3076f627365727665 b4 b305737065616b b4 b30764697363617264 84 b4 b30763617074757265 b4 b30764697363617264 84 84 84 84" <observe <speak <discard> <capture <discard>>>>>
|
||||
record2a: @"Commas not allowed in records" <ParseError "<observe <speak <discard>, <capture <discard>>>>">
|
||||
|
|
|
@ -176,7 +176,7 @@
|
|||
[(? set?) (write-sequence distance "#{" (if commas? "," "") "}" write-value (set->list v))]
|
||||
[(? dict?) (write-sequence distance "{" (if commas? "," "") "}" write-key-value (dict->list v))]
|
||||
[(embedded value)
|
||||
(! "#!")
|
||||
(! "#:")
|
||||
(write-value distance (encode-embedded value))]
|
||||
[other (error 'write-preserve/text "Attempt to serialize non-preserve: ~v" other)]))
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ representation of `D`.
|
|||
|
||||
### Embeddeds.
|
||||
|
||||
«#!V» = [0x86] ++ «V»
|
||||
«#:V» = [0x86] ++ «V»
|
||||
|
||||
The `Repr` of an `Embedded` is the `Repr` of a `Value` chosen to
|
||||
represent the denoted object, prefixed with `[0x86]`.
|
||||
|
|
|
@ -3,7 +3,7 @@ title: "P-expressions"
|
|||
---
|
||||
|
||||
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||
October 2023. Version 0.3.0.
|
||||
February 2024. Version 0.3.1.
|
||||
|
||||
[text syntax]: preserves-text.html
|
||||
|
||||
|
@ -56,7 +56,7 @@ except special punctuation.
|
|||
Embedded and annotated values are as in the text syntax, differing only
|
||||
in that uses of `Value` are replaced with `SimpleExpr`.
|
||||
|
||||
Embedded = "#!" SimpleExpr
|
||||
Embedded = "#:" SimpleExpr
|
||||
Annotated = Annotation SimpleExpr
|
||||
Annotation = "@" SimpleExpr / "#" [(%x20 / %x09) linecomment] (CR / LF)
|
||||
linecomment = *<any unicode scalar value except CR or LF>
|
||||
|
@ -100,7 +100,7 @@ We write ⌜*p*⌝ for the encoding into Preserves of a P-expression *p*.
|
|||
| ⌜`{`*p* ...`}`⌝ | = | `<b` ⌜*p*⌝ ...`>` |
|
||||
| ⌜`(`*p* ...`)`⌝ | = | `<g` ⌜*p*⌝ ...`>` |
|
||||
| ⌜`#{`*p* ...`}`⌝ | = | `<s `⌜*p*⌝ ...`>` |
|
||||
| ⌜`#!`*p*⌝ | = | `#!`⌜*p*⌝ |
|
||||
| ⌜`#:`*p*⌝ | = | `#:`⌜*p*⌝ |
|
||||
| ⌜`@`*p* *q*⌝ | = | `@`⌜*p*⌝ ⌜*q*⌝ |
|
||||
| ⌜*p*⌝ | = | *p* | when *p* ∈ **Atom** |
|
||||
| ⌜`,`⌝ | = | `<p |,|>` |
|
||||
|
@ -196,9 +196,9 @@ text-syntax encodings.
|
|||
```
|
||||
|
||||
```preserves
|
||||
⌜[1 + 2.0, print "Hello", predicate: #t, foo, #!remote, bar]⌝
|
||||
⌜[1 + 2.0, print "Hello", predicate: #t, foo, #:remote, bar]⌝
|
||||
= [1 + 2.0 <p |,|> print "Hello" <p |,|> predicate <p |:|> #t <p |,|>
|
||||
foo <p |,|> #!remote <p |,|> bar]
|
||||
foo <p |,|> #:remote <p |,|> bar]
|
||||
```
|
||||
|
||||
```preserves
|
||||
|
@ -295,7 +295,7 @@ P-expression *p* ∈ `Expr` − {`,`}.
|
|||
| **uncomma**(`{`*p* ...`}`) | = | `{`**uncomma**(*p*) ...`}` | omitting any *p* = `,` |
|
||||
| **uncomma**(`(`*p* ...`)`) | = | `(`**uncomma**(*p*) ...`)` | omitting any *p* = `,` |
|
||||
| **uncomma**(`#{`*p* ...`}`) | = | `#{`**uncomma**(*p*) ...`}` | omitting any *p* = `,` |
|
||||
| **uncomma**(`#!`*p*) | = | `#!`**uncomma**(*p*) | |
|
||||
| **uncomma**(`#:`*p*) | = | `#:`**uncomma**(*p*) | |
|
||||
| **uncomma**(`@`*p* *q*) | = | `@`**uncomma**(*p*) **uncomma**(*q*) | |
|
||||
| **uncomma**(*p*) | = | *p* | if *p* ∈ **Atom** ∪ **Punct** − {`,`} |
|
||||
|
||||
|
@ -308,7 +308,7 @@ P-expression *p* ∈ `Expr` − {`,`} to a corresponding Preserves `Value`.
|
|||
| ⌞`<`ℓ *p* ...`>`⌟ | = | `<`⌞ℓ⌟ ⌞*p*⌟ ...`>` | |
|
||||
| ⌞`{`*k*`:`*v* ...`}`⌟ | = | `{`⌞*k*⌟`:`⌞*v*⌟ ...`}` | if all ⌞*k*⌟ ... are distinct |
|
||||
| ⌞`#{`*p* ...`}`⌟ | = | `#{`⌞*p*⌟ ...`}` | if all ⌞*p*⌟ ... are distinct |
|
||||
| ⌞`#!`*p*⌟ | = | `#!`⌞*p*⌟ | |
|
||||
| ⌞`#:`*p*⌟ | = | `#:`⌞*p*⌟ | |
|
||||
| ⌞`@`*p* *q*⌟ | = | `@`⌞*p*⌟ ⌞*q*⌟ | |
|
||||
| ⌞*p*⌟ | = | *p* | when *p* ∈ **Atom** |
|
||||
|
||||
|
|
|
@ -327,11 +327,11 @@ Specifying the name of a kind of `Atom` matches that kind of atom:
|
|||
AtomKindPattern = "bool" / "double" / "int" / "string" / "bytes" / "symbol"
|
||||
|
||||
Embedded input `Value`s are matched with embedded patterns. The
|
||||
portion under the `#!` prefix is the *interface* schema for the
|
||||
portion under the `#:` prefix is the *interface* schema for the
|
||||
embedded value.[^interface-schema] The result of a match is an
|
||||
instance of the schema-wide `embeddedType`, if one is supplied.
|
||||
|
||||
EmbeddedPattern = "#!" SimplePattern
|
||||
EmbeddedPattern = "#:" SimplePattern
|
||||
|
||||
A literal pattern may be expressed in any of three ways: non-symbol
|
||||
atoms stand for themselves directly; symbols, prefixed with an equal
|
||||
|
@ -368,9 +368,9 @@ identifier.
|
|||
the interface schema associated with an embedded value describes
|
||||
the messages that may be sent to that actor.
|
||||
|
||||
**Examples.** `#!any` may denote a reference to an Actor able to
|
||||
receive any value as a message; `#!#t`, a reference to an Actor
|
||||
expecting *only* the "true" message; `#!Session`, a reference to
|
||||
**Examples.** `#:any` may denote a reference to an Actor able to
|
||||
receive any value as a message; `#:#t`, a reference to an Actor
|
||||
expecting *only* the "true" message; `#:Session`, a reference to
|
||||
an Actor expecting any message matching a schema defined as
|
||||
`Session` in this file.
|
||||
|
||||
|
@ -628,7 +628,7 @@ Simple patterns are as described above:
|
|||
# special builtins: bool, double, int, string, bytes, symbol
|
||||
/ <atom @atomKind AtomKind>
|
||||
|
||||
# matches an embedded value in the input: #!p
|
||||
# matches an embedded value in the input: #:p
|
||||
/ <embedded @interface SimplePattern>
|
||||
|
||||
# =symbol, <<lit> any>, or plain non-symbol atom
|
||||
|
@ -973,16 +973,16 @@ definitions for the metaschema.
|
|||
further; also, consideration of *dependent* schemas (analogous to
|
||||
dependent contracts) could be of interest.
|
||||
|
||||
**Example.** In the following fragment, `#!Session` is the handle a
|
||||
**Example.** In the following fragment, `#:Session` is the handle a
|
||||
connected user uses to interact with a chatroom. In the
|
||||
implementation, `Says` messages are dropped if their `who` doesn't
|
||||
match the `uid` supplied in the `Join` assertion. It'd be nice to
|
||||
capture that using a dependent schema, passing in the specific
|
||||
`uid` value to the `Session` constructor, something like
|
||||
`#!(Session uid)`.
|
||||
`#:(Session uid)`.
|
||||
|
||||
Join = <joinedUser @uid UserId @handle #!Session>.
|
||||
Session = @observeSpeech <Observe =says @observer #!Says> / Says .
|
||||
Join = <joinedUser @uid UserId @handle #:Session>.
|
||||
Session = @observeSpeech <Observe =says @observer #:Says> / Says .
|
||||
Says = <says @who UserId @what string>.
|
||||
|
||||
|
||||
|
|
|
@ -263,9 +263,9 @@ grammar above.[^rationale-no-general-machine-syntax]
|
|||
annotations potentially contained within machine-encoded values.
|
||||
|
||||
Finally, an `Embedded` is written as a `Value` chosen to represent the
|
||||
denoted object, prefixed with `#!`.
|
||||
denoted object, prefixed with `#:`.
|
||||
|
||||
Embedded = "#!" Value
|
||||
Embedded = "#:" Value
|
||||
|
||||
## <a id="annotations"></a>Annotations and Comments
|
||||
|
||||
|
|
|
@ -214,8 +214,8 @@ sequences use [the Preserves binary encoding](preserves-binary.html).
|
|||
|
||||
The total ordering specified [above](#total-order) means that the following statements are true:
|
||||
|
||||
- `"bzz"` < `"c"` < `"caa"` < `#!"a"`
|
||||
- `#t` < `3.0f` < `3.0` < `3` < `"3"` < `|3|` < `[]` < `#!#t`
|
||||
- `"bzz"` < `"c"` < `"caa"` < `#:"a"`
|
||||
- `#t` < `3.0f` < `3.0` < `3` < `"3"` < `|3|` < `[]` < `#:#t`
|
||||
- `[#f]` < `[foo]`, because `Boolean` appears before `Symbol` in the kind ordering
|
||||
- `[x]` < `[x y]`, because there is no element remaining to compare against `y`
|
||||
- `[a b]` < `[x]`, because `a` is smaller than `x`
|
||||
|
|
|
@ -41,7 +41,7 @@ SimplePattern =
|
|||
# special builtins: bool, double, int, string, bytes, symbol
|
||||
/ <atom @atomKind AtomKind>
|
||||
|
||||
# matches an embedded value in the input: #!p
|
||||
# matches an embedded value in the input: #:p
|
||||
/ <embedded @interface SimplePattern>
|
||||
|
||||
# =symbol, <<lit> any>, or plain non-symbol atom
|
||||
|
|
|
@ -167,9 +167,9 @@
|
|||
list11: <Test #x"b5b0010184" [01]>
|
||||
list12: <Test #x"b5b0010c84" [12]>
|
||||
noinput0: @"No input at all" <DecodeEOF #x"">
|
||||
embed0: <Test #x"86b000" #!0>
|
||||
embed1: <Test #x"8686b000" #!#!0>
|
||||
embed2: <Test #x"b586b00086b10568656c6c6f84" [#!0 #!"hello"]>
|
||||
embed0: <Test #x"86b000" #:0>
|
||||
embed1: <Test #x"8686b000" #:#:0>
|
||||
embed2: <Test #x"b586b00086b10568656c6c6f84" [#:0 #:"hello"]>
|
||||
record1: <Test #x"b4 b30763617074757265 b4 b30764697363617264 84 84" <capture <discard>>>
|
||||
record2: <Test #x"b4 b3076f627365727665 b4 b305737065616b b4 b30764697363617264 84 b4 b30763617074757265 b4 b30764697363617264 84 84 84 84" <observe <speak <discard> <capture <discard>>>>>
|
||||
record2a: @"Commas not allowed in records" <ParseError "<observe <speak <discard>, <capture <discard>>>>">
|
||||
|
|
Loading…
Reference in New Issue