From 7dad8fcf297e38ceaadd67a27413256cace20964 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Tue, 10 Sep 2024 21:51:12 +0300 Subject: [PATCH] Match on variables instead of expressions (#877) * Match on variables instead of expressions * Apply suggestions from code review Co-authored-by: Addison Phillips * Apply suggestions from code review * Add missing test changes noticed during implementation * Empty commit to re-trigger CLA check --------- Co-authored-by: Addison Phillips --- README.md | 3 +- exploration/registry-xml/README.md | 3 +- exploration/selection-declaration.md | 8 ++- spec/data-model/README.md | 2 +- spec/data-model/message.dtd | 2 +- spec/data-model/message.json | 2 +- spec/errors.md | 50 ++++++++++++------- spec/formatting.md | 41 +++++++-------- spec/message.abnf | 6 +-- spec/registry.md | 18 ++++--- spec/syntax.md | 37 ++++++-------- test/tests/data-model-errors.json | 22 ++++----- test/tests/functions/integer.json | 2 +- test/tests/functions/string.json | 8 +-- test/tests/pattern-selection.json | 74 +++++++++------------------- test/tests/syntax-errors.json | 38 +++++++------- test/tests/syntax.json | 14 +++--- 17 files changed, 157 insertions(+), 173 deletions(-) diff --git a/README.md b/README.md index 73801f8a6..8e2333427 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,8 @@ Functions can optionally take _options_: Messages can use a _selector_ to choose between different _variants_, which correspond to the grammatical (or other) requirements of the language: - .match {$count :integer} + .input {$count :integer} + .match $count 0 {{You have no notifications.}} one {{You have {$count} notification.}} * {{You have {$count} notifications.}} diff --git a/exploration/registry-xml/README.md b/exploration/registry-xml/README.md index 75b049041..a3a3a6890 100644 --- a/exploration/registry-xml/README.md +++ b/exploration/registry-xml/README.md @@ -163,7 +163,8 @@ For the sake of brevity, only `locales="en"` is considered. Given the above description, the `:number` function is defined to work both in a selector and a placeholder: ``` -.match {$count :number} +.input {$count :number} +.match $count 1 {{One new message}} * {{{$count :number} new messages}} ``` diff --git a/exploration/selection-declaration.md b/exploration/selection-declaration.md index 8035b2943..39215cc18 100644 --- a/exploration/selection-declaration.md +++ b/exploration/selection-declaration.md @@ -1,6 +1,6 @@ # Effect of Selectors on Subsequent Placeholders -Status: **Proposed, Ballot Requested** +Status: **Accepted**
Metadata @@ -12,6 +12,12 @@ Status: **Proposed, Ballot Requested**
Pull Requests
#755
#824
+
#860
+
#867
+
#877
+
Ballot
+
#872 (discussion)
+
#873 (voting)
diff --git a/spec/data-model/README.md b/spec/data-model/README.md index 23ae649e2..1548a20d9 100644 --- a/spec/data-model/README.md +++ b/spec/data-model/README.md @@ -84,7 +84,7 @@ interface PatternMessage { interface SelectMessage { type: "select"; declarations: Declaration[]; - selectors: Expression[]; + selectors: VariableRef[]; variants: Variant[]; } ``` diff --git a/spec/data-model/message.dtd b/spec/data-model/message.dtd index 2990da1a3..bc51dd159 100644 --- a/spec/data-model/message.dtd +++ b/spec/data-model/message.dtd @@ -10,7 +10,7 @@ name NMTOKEN #REQUIRED > - + diff --git a/spec/data-model/message.json b/spec/data-model/message.json index bcf3cd8d7..b669af462 100644 --- a/spec/data-model/message.json +++ b/spec/data-model/message.json @@ -139,7 +139,7 @@ "declarations": { "$ref": "#/$defs/declarations" }, "selectors": { "type": "array", - "items": { "$ref": "#/$defs/expression" } + "items": { "$ref": "#/$defs/variable" } }, "variants": { "type": "array", diff --git a/spec/errors.md b/spec/errors.md index 619f22558..a625b1c4e 100644 --- a/spec/errors.md +++ b/spec/errors.md @@ -46,9 +46,10 @@ or contains some error which leads to further errors, an implementation which does not emit all of the errors SHOULD prioritise _Syntax Errors_ and _Data Model Errors_ over others. -When an error occurs within a _selector_, +When an error occurs while resolving a _selector_ +or calling MatchSelectorKeys with its resolved value, the _selector_ MUST NOT match any _variant_ _key_ other than the catch-all `*` -and a _Resolution Error_ or a _Message Function Error_ MUST be emitted. +and a _Bad Selector_ error MUST be emitted. ## Syntax Errors @@ -85,13 +86,16 @@ does not equal the number of _selectors_. > Example invalid messages resulting in a _Variant Key Mismatch_ error: > > ``` -> .match {$one :func} +> .input {$one :func} +> .match $one > 1 2 {{Too many}} > * {{Otherwise}} > ``` > > ``` -> .match {$one :func} {$two :func} +> .input {$one :func} +> .input {$two :func} +> .match $one $two > 1 2 {{Two keys}} > * {{Missing a key}} > * * {{Otherwise}} @@ -105,13 +109,16 @@ does not include a _variant_ with only catch-all keys. > Example invalid messages resulting in a _Missing Fallback Variant_ error: > > ``` -> .match {$one :func} +> .input {$one :func} +> .match $one > 1 {{Value is one}} > 2 {{Value is two}} > ``` > > ``` -> .match {$one :func} {$two :func} +> .input {$one :func} +> .input {$two :func} +> .match $one $two > 1 * {{First is one}} > * 1 {{Second is one}} > ``` @@ -119,27 +126,27 @@ does not include a _variant_ with only catch-all keys. ### Missing Selector Annotation A **_Missing Selector Annotation_** error occurs when the _message_ -contains a _selector_ that does not have an _annotation_, -or contains a _variable_ that does not directly or indirectly reference a _declaration_ with an _annotation_. +contains a _selector_ that does not +directly or indirectly reference a _declaration_ with a _function_. > Examples of invalid messages resulting in a _Missing Selector Annotation_ error: > > ``` -> .match {$one} +> .match $one > 1 {{Value is one}} > * {{Value is not one}} > ``` > > ``` > .local $one = {|The one|} -> .match {$one} +> .match $one > 1 {{Value is one}} > * {{Value is not one}} > ``` > > ``` > .input {$one} -> .match {$one} +> .match $one > 1 {{Value is one}} > * {{Value is not one}} > ``` @@ -199,13 +206,16 @@ same list of _keys_ is used for more than one _variant_. > Examples of invalid messages resulting in a _Duplicate Variant_ error: > > ``` -> .match {$var :string} +> .input {$var :string} +> .match $var > * {{The first default}} > * {{The second default}} > ``` > > ``` -> .match {$x :string} {$y :string} +> .input {$x :string} +> .input {$y :string} +> .match $x $y > * foo {{The first "foo" variant}} > bar * {{The "bar" variant}} > * |foo| {{The second "foo" variant}} @@ -230,7 +240,8 @@ An **_Unresolved Variable_** error occurs when a variable reference c > ``` > > ``` -> .match {$var :func} +> .input {$var :func} +> .match $var > 1 {{The value is one.}} > * {{The value is not one.}} > ``` @@ -249,7 +260,8 @@ a reference to a function which cannot be resolved. > ``` > > ``` -> .match {|horse| :func} +> .local $horse = {|horse| :func} +> .match $horse > 1 {{The value is one.}} > * {{The value is not one.}} > ``` @@ -264,7 +276,7 @@ with a resolved value which does not support selection. > > ``` > .local $day = {|2024-05-01| :date} -> .match {$day} +> .match $day > * {{The due date is {$day}}} > ``` @@ -326,7 +338,8 @@ for that specific _function_. > ``` > > ``` -> .match {|horse| :number} +> .local $horse = {|horse| :number} +> .match $horse > 1 {{The value is one.}} > * {{The value is not one.}} > ``` @@ -363,7 +376,8 @@ does not match the expected implementation-defined format. > which is a requirement of the `:number` function: > > ``` -> .match {42 :number} +> .local $answer = {42 :number} +> .match $answer > 1 {{The value is one.}} > horse {{The value is a horse.}} > * {{The value is not one.}} diff --git a/spec/formatting.md b/spec/formatting.md index 7d54b790f..34b5c5f02 100644 --- a/spec/formatting.md +++ b/spec/formatting.md @@ -53,9 +53,9 @@ nor be made available to function implementations. > value of a given _expression_ until it is actually used by a > selection or formatting process. > However, when an _expression_ is resolved, it MUST behave as if all preceding -> _declarations_ and _selectors_ affecting _variables_ referenced by that _expression_ +> _declarations_ affecting _variables_ referenced by that _expression_ > have already been evaluated in the order in which the relevant _declarations_ -> and _selectors_ appear in the _message_. +> appear in the _message_. ## Formatting Context @@ -85,7 +85,7 @@ Implementations MAY include additional fields in their _formatting context_. ## Expression and Markup Resolution -_Expressions_ are used in _declarations_, _selectors_, and _patterns_. +_Expressions_ are used in _declarations_ and _patterns_. _Markup_ is only used in _patterns_. In a _declaration_, the resolved value of the _expression_ is bound to a _variable_, @@ -97,8 +97,6 @@ In an _input-declaration_, the _variable_ operand of the _variable-expression_ identifies not only the name of the external input value, but also the _variable_ to which the resolved value of the _variable-expression_ is bound. -In _selectors_, the resolved value of an _expression_ is used for _pattern selection_. - In a _pattern_, the resolved value of an _expression_ or _markup_ is used in its _formatting_. The form that resolved values take is implementation-dependent, @@ -429,7 +427,8 @@ according to their _key_ values and selecting the first one. > > For example, in the `pl` (Polish) locale, this _message_ cannot reach > > the `*` _variant_: > > ``` -> > .match {$num :integer} +> > .input {$num :integer} +> > .match $num > > 0 {{ }} > > one {{ }} > > few {{ }} @@ -449,13 +448,16 @@ Each _key_ corresponds to a _selector_ by its position in the _variant_. > For example, in this message: > > ``` -> .match {:one} {:two} {:three} +> .input {$one :number} +> .input {$two :number} +> .input {$three :number} +> .match $one $two $three > 1 2 3 {{ ... }} > ``` > -> The first _key_ `1` corresponds to the first _selector_ (`{:one}`), -> the second _key_ `2` to the second _selector_ (`{:two}`), -> and the third _key_ `3` to the third _selector_ (`{:three}`). +> The first _key_ `1` corresponds to the first _selector_ (`$one`), +> the second _key_ `2` to the second _selector_ (`$two`), +> and the third _key_ `3` to the third _selector_ (`$three`). To determine which _variant_ best matches a given set of inputs, each _selector_ is used in turn to order and filter the list of _variants_. @@ -468,15 +470,6 @@ Earlier _selectors_ in the _matcher_'s list of _selectors_ have a higher priorit When all of the _selectors_ have been processed, the earliest-sorted _variant_ in the remaining list of _variants_ is selected. -> [!NOTE] -> A _selector_ is not a _declaration_. -> Even when the same _function_ can be used for both formatting and selection -> of a given _operand_ -> the _annotation_ that appears in a _selector_ has no effect on subsequent -> _selectors_ nor on the formatting used in _placeholders_. -> To use the same value for selection and formatting, -> set its value with a `.input` or `.local` _declaration_. - This selection method is defined in more detail below. An implementation MAY use any pattern selection method, as long as its observable behavior matches the results of the method defined here. @@ -600,7 +593,9 @@ the variable reference `$bar` resolves to the string `'bar'`, pattern selection proceeds as follows for this message: ``` -.match {$foo :string} {$bar :string} +.input {$foo :string} +.input {$bar :string} +.match $foo $bar bar bar {{All bar}} foo foo {{All foo}} * * {{Otherwise}} @@ -631,7 +626,9 @@ Alternatively, with the same implementation and formatting context as in Example pattern selection would proceed as follows for this message: ``` -.match {$foo :string} {$bar :string} +.input {$foo :string} +.input {$bar :string} +.match $foo $bar * bar {{Any and bar}} foo * {{Foo and any}} foo bar {{Foo and bar}} @@ -680,7 +677,7 @@ the pattern selection proceeds as follows for this message: ``` .input {$count :number} -.match {$count} +.match $count one {{Category match for {$count}}} 1 {{Exact match for {$count}}} * {{Other match for {$count}}} diff --git a/spec/message.abnf b/spec/message.abnf index 8d26906ba..a5966ee0b 100644 --- a/spec/message.abnf +++ b/spec/message.abnf @@ -14,9 +14,9 @@ local-declaration = local s variable [s] "=" [s] expression quoted-pattern = "{{" pattern "}}" -matcher = match-statement 1*([s] variant) -match-statement = match 1*([s] selector) -selector = expression +matcher = match-statement s variant *([s] variant) +match-statement = match 1*(s selector) +selector = variable variant = key *(s key) [s] quoted-pattern key = literal / "*" diff --git a/spec/registry.md b/spec/registry.md index 9abedbb91..e4c9ede1e 100644 --- a/spec/registry.md +++ b/spec/registry.md @@ -51,9 +51,9 @@ The function `:string` has no options. #### Selection When implementing [`MatchSelectorKeys(resolvedSelector, keys)`](/spec/formatting.md#resolve-preferences) -where `resolvedSelector` is the resolved value of a _selector_ _expression_ +where `resolvedSelector` is the resolved value of a _selector_ and `keys` is a list of strings, -the `:string` selector performs as described below. +the `:string` selector function performs as described below. 1. Let `compare` be the string value of `resolvedSelector`. 1. Let `result` be a new empty list of strings. @@ -80,7 +80,8 @@ the `:string` selector performs as described below. > > For example: > ``` -> .match {$string :string} +> .input {$string :string} +> .match $string > | space key | {{Matches the string " space key "}} > * {{Matches the string "space key"}} > ``` @@ -195,7 +196,8 @@ but can cause problems in target locales that the original developer is not cons > For example, a naive developer might use a special message for the value `1` without > considering a locale's need for a `one` plural: > ``` -> .match {$var :number} +> .input {$var :number} +> .match $var > 1 {{You have one last chance}} > one {{You have {$var} chance remaining}} > * {{You have {$var} chances remaining}} @@ -311,7 +313,8 @@ but can cause problems in target locales that the original developer is not cons > For example, a naive developer might use a special message for the value `1` without > considering a locale's need for a `one` plural: > ``` -> .match {$var :integer} +> .input {$var :integer} +> .match $var > 1 {{You have one last chance}} > one {{You have {$var} chance remaining}} > * {{You have {$var} chances remaining}} @@ -398,7 +401,7 @@ Number selection has three modes: followed by an ordinal rule category if there is no explicit match When implementing [`MatchSelectorKeys(resolvedSelector, keys)`](/spec/formatting.md#resolve-preferences) -where `resolvedSelector` is the resolved value of a _selector_ _expression_ +where `resolvedSelector` is the resolved value of a _selector_ and `keys` is a list of strings, numeric selectors perform as described below. @@ -462,7 +465,8 @@ for examples. > > A message in Czech might be: > ``` -> .match {$numDays :number} +> .input {$numDays :number} +> .match $numDays > one {{{$numDays} den}} > few {{{$numDays} dny}} > many {{{$numDays} dne}} diff --git a/spec/syntax.md b/spec/syntax.md index da212e21a..aef672068 100644 --- a/spec/syntax.md +++ b/spec/syntax.md @@ -211,12 +211,13 @@ external input value does not appear in a previous _declaration_. > [!NOTE] > These restrictions only apply to _declarations_. -> A _placeholder_ or _selector_ can apply a different _function_ to a _variable_ +> A _placeholder_ can apply a different _function_ to a _variable_ > than one applied to the same _variable_ named in a _declaration_. > For example, this message is _valid_: > ``` > .input {$var :number maximumFractionDigits=0} -> .match {$var :number maximumFractionDigits=2} +> .local $var2 = {$var :number maximumFractionDigits=2} +> .match $var2 > 0 {{The selector can apply a different function to {$var} for the purposes of selection}} > * {{A placeholder in a pattern can apply a different function to {$var :number maximumFractionDigits=3}}} > ``` @@ -342,24 +343,24 @@ otherwise, a corresponding _Data Model Error_ will be produced during processing The number of _keys_ on each _variant_ MUST be equal to the number of _selectors_. - _Missing Fallback Variant_: At least one _variant_ MUST exist whose _keys_ are all equal to the "catch-all" key `*`. -- _Missing Selector Function_: - Each _selector_ MUST have a _function_, - or contain a _variable_ that directly or indirectly references a _declaration_ with a _function_. +- _Missing Selector Annotation_: + Each _selector_ MUST be a _variable_ that + directly or indirectly references a _declaration_ with a _function_. - _Duplicate Variant_: Each _variant_ MUST use a list of _keys_ that is unique from that of all other _variants_ in the _message_. _Literal_ _keys_ are compared by their contents, not their syntactical appearance. ```abnf -matcher = match-statement 1*([s] variant) -match-statement = match 1*([s] selector) +matcher = match-statement s variant *([s] variant) +match-statement = match 1*(s selector) ``` > A _message_ with a _matcher_: > > ``` > .input {$count :number} -> .match {$count} +> .match $count > one {{You have {$count} notification.}} > * {{You have {$count} notifications.}} > ``` @@ -367,18 +368,18 @@ match-statement = match 1*([s] selector) > A _message_ containing a _matcher_ formatted on a single line: > > ``` -> .match {:platform} windows {{Settings}} * {{Preferences}} +> .local $os = {:platform} .match $os windows {{Settings}} * {{Preferences}} > ``` ### Selector -A **_selector_** is an _expression_ that ranks or excludes the +A **_selector_** is a _variable_ whose resolved value ranks or excludes the _variants_ based on the value of the corresponding _key_ in each _variant_. The combination of _selectors_ in a _matcher_ thus determines which _pattern_ will be used during formatting. ```abnf -selector = expression +selector = variable ``` There MUST be at least one _selector_ in a _matcher_. @@ -389,7 +390,8 @@ There MAY be any number of additional _selectors_. > based on grammatical case: > > ``` -> .match {$userName :hasCase} +> .local $hasCase = {$userName :hasCase} +> .match $hasCase > vocative {{Hello, {$userName :person case=vocative}!}} > accusative {{Please welcome {$userName :person case=accusative}!}} > * {{Hello!}} @@ -400,7 +402,7 @@ There MAY be any number of additional _selectors_. > ``` > .input {$numLikes :integer} > .input {$numShares :integer} -> .match {$numLikes} {$numShares} +> .match $numLikes $numShares > 0 0 {{Your item has no likes and has not been shared.}} > 0 one {{Your item has no likes and has been shared {$numShares} time.}} > 0 * {{Your item has no likes and has been shared {$numShares} times.}} @@ -468,8 +470,7 @@ There are several types of _expression_ that can appear in a _message_. All _expressions_ share a common syntax. The types of _expression_ are: 1. The value of a _local-declaration_ -2. A _selector_ -3. A kind of _placeholder_ in a _pattern_ +2. A kind of _placeholder_ in a _pattern_ Additionally, an _input-declaration_ can contain a _variable-expression_. @@ -482,12 +483,6 @@ Additionally, an _input-declaration_ can contain a _variable-expression_. > .local $y = {|This is an expression|} > ``` > -> Selectors: -> -> ``` -> .match {$selector :functionRequired} -> ``` -> > Placeholders: > > ``` diff --git a/test/tests/data-model-errors.json b/test/tests/data-model-errors.json index bbc00fe80..dc79683ce 100644 --- a/test/tests/data-model-errors.json +++ b/test/tests/data-model-errors.json @@ -6,7 +6,7 @@ }, "tests": [ { - "src": ".match {$foo :x} * * {{foo}}", + "src": ".input {$foo :x} .match $foo * * {{foo}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -14,7 +14,7 @@ ] }, { - "src": ".match {$foo :x} {$bar :x} * {{foo}}", + "src": ".input {$foo :x} input {$bar :x} .match $foo $bar * {{foo}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -22,7 +22,7 @@ ] }, { - "src": ".match {:foo} 1 {{_}}", + "src": ".input {$foo :x} .match $foo 1 {{_}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -30,7 +30,7 @@ ] }, { - "src": ".match {:foo} other {{_}}", + "src": ".input {$foo :x} .match $foo other {{_}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -38,7 +38,7 @@ ] }, { - "src": ".match {:foo} {:bar} * 1 {{_}} 1 * {{_}}", + "src": ".input {$foo :x} input {$bar :x} .match $foo $bar * 1 {{_}} 1 * {{_}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -46,7 +46,7 @@ ] }, { - "src": ".match {$foo} one {{one}} * {{other}}", + "src": ".input {$foo} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -54,7 +54,7 @@ ] }, { - "src": ".input {$foo} .match {$foo} one {{one}} * {{other}}", + "src": ".local $foo = {$bar} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -62,7 +62,7 @@ ] }, { - "src": ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}", + "src": ".input {$bar} .local $foo = {$bar} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -166,7 +166,7 @@ ] }, { - "src": ".match {$var :string} * {{The first default}} * {{The second default}}", + "src": ".input {$var :string} .match $var * {{The first default}} * {{The second default}}", "expErrors": [ { "type": "duplicate-variant" @@ -174,7 +174,7 @@ ] }, { - "src": ".match {$x :string} {$y :string} * foo {{The first foo variant}} bar * {{The bar variant}} * |foo| {{The second foo variant}} * * {{The default variant}}", + "src": ".input {$x :string} .input {$y :string} .match $x $y * foo {{The first foo variant}} bar * {{The bar variant}} * |foo| {{The second foo variant}} * * {{The default variant}}", "expErrors": [ { "type": "duplicate-variant" @@ -182,7 +182,7 @@ ] }, { - "src": ".match {star :string} |*| {{Literal star}} * {{The default}}", + "src": ".local $star = {star :string} .match $star |*| {{Literal star}} * {{The default}}", "exp": "The default" } ] diff --git a/test/tests/functions/integer.json b/test/tests/functions/integer.json index c8e75077a..4ea96941e 100644 --- a/test/tests/functions/integer.json +++ b/test/tests/functions/integer.json @@ -19,7 +19,7 @@ "exp": "hello 4" }, { - "src": ".match {$foo :integer} one {{one}} * {{other}}", + "src": ".input {$foo :integer} .match $foo 1 {{one}} * {{other}}", "params": [ { "name": "foo", diff --git a/test/tests/functions/string.json b/test/tests/functions/string.json index fab459541..3543e7844 100644 --- a/test/tests/functions/string.json +++ b/test/tests/functions/string.json @@ -7,7 +7,7 @@ }, "tests": [ { - "src": ".match {$foo :string} |1| {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo |1| {{one}} * {{other}}", "params": [ { "name": "foo", @@ -17,7 +17,7 @@ "exp": "one" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "params": [ { "name": "foo", @@ -27,7 +27,7 @@ "exp": "one" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "params": [ { "name": "foo", @@ -37,7 +37,7 @@ "exp": "other" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "exp": "other", "expErrors": [ { diff --git a/test/tests/pattern-selection.json b/test/tests/pattern-selection.json index 314d2da78..29dc146c1 100644 --- a/test/tests/pattern-selection.json +++ b/test/tests/pattern-selection.json @@ -7,88 +7,58 @@ }, "tests": [ { - "src": ".match {1 :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".local $x = {1 :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "1" }, { - "src": ".match {0 :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".local $x = {0 :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "other" }, { - "src": ".match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "params": [{ "name": "x", "value": 1 }], "exp": "1" }, { - "src": ".match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "params": [{ "name": "x", "value": 2 }], "exp": "other" }, { - "src": ".input {$x} .match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select} .local $y = {$x} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", "params": [{ "name": "x", "value": 1 }], "exp": "1" }, { - "src": ".input {$x} .match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select} .local $y = {$x} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", "params": [{ "name": "x", "value": 2 }], "exp": "other" }, { - "src": ".input {$x :test:select} .match {$x} 1.0 {{1.0}} 1 {{1}} * {{other}}", - "params": [{ "name": "x", "value": 1 }], - "exp": "1" - }, - { - "src": ".input {$x :test:select} .match {$x} 1.0 {{1.0}} 1 {{1}} * {{other}}", - "params": [{ "name": "x", "value": 2 }], - "exp": "other" - }, - { - "src": ".input {$x :test:select} .local $y = {$x} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", - "params": [{ "name": "x", "value": 1 }], - "exp": "1" - }, - { - "src": ".input {$x :test:select} .local $y = {$x} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", - "params": [{ "name": "x", "value": 2 }], - "exp": "other" - }, - { - "src": ".match {1 :test:select decimalPlaces=1} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".local $x = {1 :test:select decimalPlaces=1} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "1.0" }, { - "src": ".match {1 :test:select decimalPlaces=1} 1 {{1}} 1.0 {{1.0}} * {{other}}", + "src": ".local $x = {1 :test:select decimalPlaces=1} .match $x 1 {{1}} 1.0 {{1.0}} * {{other}}", "exp": "1.0" }, { - "src": ".match {1 :test:select decimalPlaces=9} 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", + "src": ".local $x = {1 :test:select decimalPlaces=9} .match $x 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", "exp": "bad-option-value", "expErrors": [{ "type": "bad-option" }, { "type": "bad-selector" }] }, { - "src": ".input {$x :test:select} .match {$x :test:select decimalPlaces=1} 1.0 {{1.0}} 1 {{1}} * {{other}}", - "params": [{ "name": "x", "value": 1 }], - "exp": "1.0" - }, - { - "src": ".input {$x :test:select decimalPlaces=1} .match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", - "params": [{ "name": "x", "value": 1 }], - "exp": "1.0" - }, - { - "src": ".input {$x :test:select} .local $y = {$x :test:select decimalPlaces=1} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select} .local $y = {$x :test:select decimalPlaces=1} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", "params": [{ "name": "x", "value": 1 }], "exp": "1.0" }, { - "src": ".input {$x :test:select decimalPlaces=1} .local $y = {$x :test:select} .match {$y} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select decimalPlaces=1} .local $y = {$x :test:select} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", "params": [{ "name": "x", "value": 1 }], "exp": "1.0" }, { - "src": ".input {$x :test:select decimalPlaces=9} .match {$x :test:select decimalPlaces=1} 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", + "src": ".input {$x :test:select decimalPlaces=9} .local $y = {$x :test:select decimalPlaces=1} .match $y 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", "params": [{ "name": "x", "value": 1 }], "exp": "bad-option-value", "expErrors": [ @@ -98,21 +68,21 @@ ] }, { - "src": ".match {1 :test:select fails=select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".local $x = {1 :test:select fails=select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "other", "expErrors": [{ "type": "bad-selector" }] }, { - "src": ".match {1 :test:select fails=format} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".local $x = {1 :test:select fails=format} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "1" }, { - "src": ".match {1 :test:format} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".local $x = {1 :test:format} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "other", "expErrors": [{ "type": "bad-selector" }] }, { - "src": ".match {$x :test:select} 1.0 {{1.0}} 1 {{1}} * {{other}}", + "src": ".input {$x :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", "exp": "other", "expErrors": [ { "type": "unresolved-variable" }, @@ -121,28 +91,28 @@ ] }, { - "src": ".match {1 :test:select} {1 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "src": ".local $x = {1 :test:select} .local $y = {1 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", "exp": "1,1" }, { - "src": ".match {1 :test:select} {0 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "src": ".local $x = {1 :test:select} .local $y = {0 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", "exp": "1,*" }, { - "src": ".match {0 :test:select} {1 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "src": ".local $x = {0 :test:select} .local $y = {1 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", "exp": "*,1" }, { - "src": ".match {0 :test:select} {0 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "src": ".local $x = {0 :test:select} .local $y = {0 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", "exp": "*,*" }, { - "src": ".match {1 :test:select fails=select} {1 :test:select} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "src": ".local $x = {1 :test:select fails=select} .local $y = {1 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", "exp": "*,1", "expErrors": [{ "type": "bad-selector" }] }, { - "src": ".match {1 :test:select} {1 :test:format} 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "src": ".local $x = {1 :test:select} .local $y = {1 :test:format} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", "exp": "1,*", "expErrors": [{ "type": "bad-selector" }] } diff --git a/test/tests/syntax-errors.json b/test/tests/syntax-errors.json index f01f5f1dc..00d0420f4 100644 --- a/test/tests/syntax-errors.json +++ b/test/tests/syntax-errors.json @@ -158,27 +158,23 @@ { "src": ".local $bar = |foo| {{_}}" }, - { - "src": ".match {#foo} * {{foo}}" - }, - { - "src": ".match {} * {{foo}}" - }, - { - "src": ".match {|foo| :x} {|bar| :x} ** {{foo}}" - }, - { - "src": ".match * {{foo}}" - }, - { - "src": ".match {|x| :x} * foo" - }, - { - "src": ".match {|x| :x} * {{foo}} extra" - }, - { - "src": ".match |x| * {{foo}}" - }, + { "src": ".match {{foo}}" }, + { "src": ".match * {{foo}}" }, + { "src": ".match x * {{foo}}" }, + { "src": ".match |x| * {{foo}}" }, + { "src": ".match :x * {{foo}}" }, + { "src": ".match {$foo} * {{foo}}" }, + { "src": ".match {#foo} * {{foo}}" }, + { "src": ".input {$x :x} .match {$x} * {{foo}}" }, + { "src": ".input {$x :x} .match$x * {{foo}}" }, + { "src": ".input {$x :x} .match $x* {{foo}}" }, + { "src": ".input {$x :x} .match $x|x| {{foo}} * {{foo}}" }, + { "src": ".input {$x :x} .local $y = {y :y} .match $x$y * * {{foo}}" }, + { "src": ".input {$x :x} .local $y = {y :y} .match $x $y ** {{foo}}" }, + { "src": ".input {$x :x} .match $x" }, + { "src": ".input {$x :x} .match $x *" }, + { "src": ".input {$x :x} .match $x * foo" }, + { "src": ".input {$x :x} .match $x * {{foo}} extra" }, { "src": ".n{a}{{}}" }, { "src": "{^}" }, { "src": "{!}" }, diff --git a/test/tests/syntax.json b/test/tests/syntax.json index 2bb634680..27b74b2f3 100644 --- a/test/tests/syntax.json +++ b/test/tests/syntax.json @@ -170,8 +170,8 @@ "exp": "" }, { - "description": "message -> complex-message -> complex-body -> matcher -> match-statement variant -> match selector key quoted-pattern -> \".match\" expression literal quoted-pattern", - "src": ".match{a :f}a{{}}*{{}}", + "description": "message -> complex-message -> complex-body -> ... -> matcher -> match-statement variant -> match selector key quoted-pattern -> \".match\" variable literal quoted-pattern", + "src": ".local $a={a :f}.match $a a{{}}*{{}}", "exp": "", "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, @@ -209,7 +209,7 @@ }, { "description": "... matcher -> match-statement [s] variant -> match 1*([s] selector) variant -> match selector selector variant -> match selector selector variant key s key quoted-pattern", - "src": ".match{a :f}{b :f}a b{{}}* *{{}}", + "src": ".local $a={a :f}.local $b={b :f}.match $a $b a b{{}}* *{{}}", "exp": "", "expErrors": [ { "type": "unknown-function" }, @@ -220,19 +220,19 @@ }, { "description": "... matcher -> match-statement [s] variant -> match 1*([s] selector) variant -> match selector variant variant ...", - "src": ".match{a :f}a{{}}b{{}}*{{}}", + "src": ".local $a={a :f}.match $a a{{}}b{{}}*{{}}", "exp": "", "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, { "description": "... variant -> key s quoted-pattern -> ...", - "src": ".match{a :f}a {{}}*{{}}", + "src": ".local $a={a :f}.match $a a {{}}*{{}}", "exp": "", "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, { "description": "... variant -> key s key s quoted-pattern -> ...", - "src": ".match{a :f}{b :f}a b {{}}* *{{}}", + "src": ".local $a={a :f}.local $b={b :f}.match $a $b a b {{}}* *{{}}", "exp": "", "expErrors": [ { "type": "unknown-function" }, @@ -243,7 +243,7 @@ }, { "description": "... key -> \"*\" ...", - "src": ".match{a :f}*{{}}", + "src": ".local $a={a :f}.match $a *{{}}", "exp": "", "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] },