From 2e84614b3b9a628baa87d02fc56649723812a9f2 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Fri, 3 Nov 2023 10:31:44 +0100 Subject: [PATCH] Allow "sufficiently identifierlike" values in dictionaries too --- .../javascript/packages/schema/src/meta.ts | 3 +- .../javascript/packages/schema/src/reader.ts | 17 ++++++--- .../preserves/preserves-schema/reader.rkt | 7 ++-- preserves-schema.md | 38 ++++++++++++++++--- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/implementations/javascript/packages/schema/src/meta.ts b/implementations/javascript/packages/schema/src/meta.ts index acae08f..8d45e21 100644 --- a/implementations/javascript/packages/schema/src/meta.ts +++ b/implementations/javascript/packages/schema/src/meta.ts @@ -95,7 +95,6 @@ export function anonymousSimplePattern(p: M.SimplePattern): M.NamedPattern { export function namelike(x: Input): string | undefined { if (typeof x === 'string') return x; if (typeof x === 'symbol') return x.description!; - if (typeof x === 'number') return '' + x; - if (typeof x === 'boolean') return '' + x; + if (typeof x === 'boolean') return x ? 'true' : 'false'; return void 0; } diff --git a/implementations/javascript/packages/schema/src/reader.ts b/implementations/javascript/packages/schema/src/reader.ts index 7017ff7..ae66eb2 100644 --- a/implementations/javascript/packages/schema/src/reader.ts +++ b/implementations/javascript/packages/schema/src/reader.ts @@ -161,7 +161,12 @@ function parseDefinition(name: symbol, pos: Position | null, body: Array) p.value._variant === 'lit') { const s = M.namelike(p.value.value); - if (s !== void 0) return M.NamedAlternative({ variantLabel: s, pattern: p }); + if (s !== void 0) { + if (M.isValidToken(s)) { + return M.NamedAlternative({ variantLabel: s, pattern: p }); + } + throw new SchemaSyntaxError(preserves`Invalid name ${s} inferred for alternative: ${input}`, pos); + } } throw new SchemaSyntaxError(preserves`Name missing for alternative: ${input}`, pos); } @@ -338,11 +343,11 @@ function parsePattern(name: symbol, body0: Array): Pattern { return (b: Input) => { let name = findName(b); if (name === false) { - if (literalName !== void 0 && - typeof literalName === 'symbol' && - M.isValidToken(literalName.description!)) - { - name = literalName; + if (literalName !== void 0) { + const s = M.namelike(literalName); + if (s !== void 0 && M.isValidToken(s)) { + name = Symbol.for(s); + } } } if (name === false) { diff --git a/implementations/racket/preserves/preserves-schema/reader.rkt b/implementations/racket/preserves/preserves-schema/reader.rkt index ba9101a..2ecffcf 100644 --- a/implementations/racket/preserves/preserves-schema/reader.rkt +++ b/implementations/racket/preserves/preserves-schema/reader.rkt @@ -80,9 +80,8 @@ (define (namelike v) (match v - [(? string? s) (string->symbol s)] [(? symbol? s) s] - [(? number? n) (string->symbol (number->string n))] + [(? string? s) (string->symbol s)] [(? boolean? b) (if b 'true 'false)] [_ #f])) @@ -194,9 +193,9 @@ values (lambda () (error 'parse-simple-dsl "Compound patterns not accepted here: ~a" (input->string stx))))) - (define ((maybe-named* knamed kanonymous recur [literal-name #f]) b) + (define ((maybe-named* knamed kanonymous recur [literal-name '#:none]) b) (define n (or (find-name b) - (and (valid-id? literal-name) literal-name))) + (let ((n (namelike literal-name))) (and (valid-id? n) n)))) (if n (let ((p (parse-simple-dsl b diff --git a/preserves-schema.md b/preserves-schema.md index e4a20fb..c616533 100644 --- a/preserves-schema.md +++ b/preserves-schema.md @@ -4,9 +4,11 @@ title: "Preserves Schema" --- Tony Garnock-Jones -October 2023. Version 0.3.3. +October 2023. Version 0.3.4. [abnf]: https://tools.ietf.org/html/rfc7405 + [identifierlike]: #sufficiently-identifierlike-values + [valid identifier]: #identifiers-and-capitalization-conventions This document proposes a Schema language for the [Preserves data model](./preserves.html). @@ -210,8 +212,9 @@ a member of a tagged union type). A variant name can either be given explicitly as `@name` or inferred.[^variant-names-unlike-binding-names] It can only be inferred from the label of a record pattern, from the name of a reference to -another definition, or from the text of a "sufficiently identifierlike" -literal pattern - one that matches a string, symbol, number or boolean: +another definition, or from the text of a "[sufficiently +identifierlike][identifierlike]" literal pattern - one that matches a +string, symbol or boolean: AltPattern = "@" id Pattern / "<" id PatternSequence ">" @@ -405,8 +408,10 @@ fixed-position patterns. A dictionary pattern matches specific literal keys in an input dictionary. If no explicit name is given for a particular -`NamedSimplePattern`, but the key for the pattern is a symbol, then -that symbol is used as the name for that dictionary entry. +`NamedSimplePattern`, but the key for the pattern is "[sufficiently +identifierlike][identifierlike]" (a string, symbol or boolean), then a +symbol formed from that key is used as the name for that dictionary +entry. DictionaryPattern = "{" *(value ":" NamedSimplePattern) "}" @@ -423,6 +428,29 @@ the given name in the overall record type for a definition. The type of value contained in the field will correspond to the `Pattern` or `SimplePattern` given. +### "Sufficiently Identifierlike" Values + +In some places in a schema, names can be inferred from some nearby +literal pattern element. In an `OrPattern`, variant names can be +inferred; in a `DictionaryPattern`, names for dictionary entries can be +inferred. + +The rules are simple: if the literal pattern would match a specific +symbol or string, then that specific value is converted to a symbol and +used as the name. If the pattern would match `#t`, the name will be +`true`; if it would match `#f`, the name will be `false`. + +For example, in the following grammar, the names for the variants of +`Example1` are the symbols `foo` and `bar` and `false`, and the names +for the two fields in `Example2` are `example` and `|testing strings|`. +Note that `|testing strings|` is a symbol whose name contains a space, +which will be rejected because it is not a [valid identifier][]. + +```preserves-schema +Example1 = =foo / "bar" / #f . +Example2 = { "testing strings": int, example: string } . +``` + ## Semantics Having covered concrete syntax, we now give semantics for the schema