Skip to content

Commit cc93bf6

Browse files
eemeliaphillips
andauthored
Add column-first pattern selection (#372)
* Add first-column pattern selection * Fix typo Co-authored-by: Addison Phillips <[email protected]> * Add a third example --------- Co-authored-by: Addison Phillips <[email protected]>
1 parent 99f856e commit cc93bf6

File tree

1 file changed

+233
-0
lines changed

1 file changed

+233
-0
lines changed

spec/formatting.md

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,239 @@ the local variable takes precedence.
1919
It is an error for a local variable definition to
2020
refer to a local variable that's defined after it in the message.
2121

22+
## Pattern Selection
23+
24+
When formatting a message with one or more _selectors_,
25+
the _pattern_ of one of the _variants_ must be selected for formatting.
26+
27+
When a message has a single _selector_,
28+
an implementation-defined method compares each key to the _selector_
29+
and determines which of the keys match, and in what order of preference.
30+
A catch-all key will always match, but is always the least preferred choice.
31+
During selection, the _variant_ with the best-matching key is selected.
32+
33+
In a message with more than one _selector_,
34+
each _variant_ also has a corresponding number of keys.
35+
These correspond to _selectors_ by position.
36+
The same implementation-defined method as above is used to compare
37+
the corresponding key of each _variant_ to its _selector_,
38+
to determine which of the keys match, and in what order of preference.
39+
In order to select a single _variant_,
40+
the full list of _variants_ will need to be filtered and sorted.
41+
First, each _variant_ with a key that does not match its _selector_ is left out.
42+
Then, the remaining _variants_ are sorted lexicographically by key preference,
43+
with earlier _selectors_ having higher priority than later ones.
44+
Finally, the highest-sorted _variant_ is selected.
45+
46+
This selection method is defined in more detail below.
47+
An implementation MAY use any pattern selection method,
48+
as long as its observable behaviour matches the results of the method defined here.
49+
50+
### Resolve Selectors
51+
52+
First, resolve the values of each _selector_:
53+
54+
1. Let `res` be a new empty list of resolved values that support selection.
55+
1. For each _expression_ `exp` of the message's _selectors_,
56+
1. Let `rv` be the resolved value of `exp`.
57+
1. If selection is supported for `rv`:
58+
1. Append `rv` as the last element of the list `res`.
59+
1. Else:
60+
1. Let `nomatch` be a resolved value for which selection always fails.
61+
1. Append `nomatch` as the last element of the list `res`.
62+
1. Emit a Selection Error.
63+
64+
The shape of the resolved values is determined by each implementation,
65+
along with the manner of determining their support for selection.
66+
67+
### Resolve Preferences
68+
69+
Next, using `res`, resolve the preferential order for all message keys:
70+
71+
1. Let `pref` be a new empty list of lists of strings.
72+
1. For each index `i` in `res`:
73+
1. Let `keys` be a new empty list of strings.
74+
1. For each _variant_ `var` of the message:
75+
1. Let `key` be the `var` key at position `i`.
76+
1. If `key` is not the catch-all key `'*'`:
77+
1. Let `ks` be the decoded value of `key`.
78+
1. Append `ks` as the last element of the list `keys`.
79+
1. Let `rv` be the resolved value at index `i` of `res`.
80+
1. Let `matches` be the result of calling the method MatchSelectorKeys(`rv`, `keys`)
81+
1. Append `matches` as the last element of the list `pref`.
82+
83+
The method MatchSelectorKeys is determined by the implementation.
84+
It takes as arguments a resolved _selector_ value `rv` and a list of string keys `keys`,
85+
and returns a list of string keys in preferential order.
86+
The returned list must only contain unique elements of the input list `keys`,
87+
and it may be empty.
88+
The most preferred key is first, with the rest sorted by decreasing preference.
89+
90+
### Filter Variants
91+
92+
Then, using the preferential key orders `pref`,
93+
filter the list of _variants_ to the ones that match with some preference:
94+
95+
1. Let `vars` be a new empty list of _variants_.
96+
1. For each _variant_ `var` of the message:
97+
1. For each index `i` in `pref`:
98+
1. Let `key` be the `var` key at position `i`.
99+
1. If `key` is the catch-all key `'*'`:
100+
1. Continue the inner loop on `pref`.
101+
1. Let `ks` be the decoded value of `key`.
102+
1. Let `matches` be the list of strings at index `i` of `pref`.
103+
1. If `matches` includes `ks`:
104+
1. Continue the inner loop on `pref`.
105+
1. Else:
106+
1. Continue the outer loop on message _variants_.
107+
1. Append `var` as the last element of the list `vars`.
108+
109+
### Sort Variants
110+
111+
Finally, sort the list of variants `vars` and select the _pattern_:
112+
113+
1. Let `sortable` be a new empty list of (integer, _variant_) tuples.
114+
1. For each _variant_ `var` of `vars`:
115+
1. Let `tuple` be a new tuple (-1, `var`).
116+
1. Append `tuple` as the last element of the list `sortable`.
117+
1. Let `len` be the integer count of items in `pref`.
118+
1. Let `i` be `len` - 1.
119+
1. While `i` >= 0:
120+
1. Let `matches` be the list of strings at index `i` of `pref`.
121+
1. Let `minpref` be the integer count of items in `matches`.
122+
1. For each tuple `tuple` of `sortable`:
123+
1. Let `matchpref` be an integer with the value `minpref`.
124+
1. Let `key` be the `tuple` _variant_ key at position `i`.
125+
1. If `key` is not the catch-all key `'*'`:
126+
1. Let `ks` be the decoded value of `key`.
127+
1. Let `matchpref` be the integer position of `ks` in `matches`.
128+
1. Set the `tuple` integer value as `matchpref`.
129+
1. Call the method SortVariants(`sortable`).
130+
1. Set `i` to be `i` - 1.
131+
1. Let `var` be the _variant_ element of the first element of `sortable`.
132+
1. Select the _pattern_ of `var`.
133+
134+
The method SortVariants is determined by the implementation.
135+
It takes as an argument a `sortable` list of (integer, _variant_) tuples,
136+
which it modifies in place using some stable sorting algorithm.
137+
The method does not return anything.
138+
The list is sorted according to the tuple's first integer element,
139+
such that a lower number is sorted before a higher one,
140+
and entries that have the same number retain their order.
141+
142+
### Examples
143+
144+
#### Example 1
145+
146+
Presuming a minimal implementation which only supports string values
147+
and matches keys by using string comparison,
148+
and a formatting context in which
149+
the variable reference `$foo` resolves to the string `'foo'` and
150+
the variable reference `$bar` resolves to the string `'bar'`,
151+
pattern selection proceeds as follows for this message:
152+
153+
```
154+
match {$foo} {$bar}
155+
when bar bar {All bar}
156+
when foo foo {All foo}
157+
when * * {Otherwise}
158+
```
159+
160+
1. For the first selector:<br>
161+
The value of the selector is resolved to be `'foo'`.<br>
162+
The available keys « `'bar'`, `'foo'` » are compared to `'foo'`,<br>
163+
resulting in a list « `'foo'` » of matching keys.
164+
165+
2. For the second selector:<br>
166+
The value of the selector is resolved to be `'bar'`.<br>
167+
The available keys « `'bar'`, `'foo'` » are compared to `'bar'`,<br>
168+
resulting in a list « `'bar'` » of matching keys.
169+
170+
3. Creating the list `vars` of variants matching all keys:<br>
171+
The first variant `bar bar` is discarded as its first key does not match the first selector.<br>
172+
The second variant `foo foo` is discarded as its second key does not match the second selector.<br>
173+
The catch-all keys of the third variant `* *` always match, and this is added to `vars`,<br>
174+
resulting in a list « `* *` » of variants.
175+
176+
4. As the list `vars` only has one entry, it does not need to be sorted.<br>
177+
The pattern `{Otherwise}` of the third variant is selected.
178+
179+
#### Example 2
180+
181+
Alternatively, with the same implementation and formatting context as in Example 1,
182+
pattern selection would proceed as follows for this message:
183+
184+
```
185+
match {$foo} {$bar}
186+
when * bar {Any and bar}
187+
when foo * {Foo and any}
188+
when foo bar {Foo and bar}
189+
when * * {Otherwise}
190+
```
191+
192+
1. For the first selector:<br>
193+
The value of the selector is resolved to be `'foo'`.<br>
194+
The available keys « `'foo'` » are compared to `'foo'`,<br>
195+
resulting in a list « `'foo'` » of matching keys.
196+
197+
2. For the second selector:<br>
198+
The value of the selector is resolved to be `'bar'`.<br>
199+
The available keys « `'bar'` » are compared to `'bar'`,<br>
200+
resulting in a list « `'bar'` » of matching keys.
201+
202+
3. Creating the list `vars` of variants matching all keys:<br>
203+
The keys of all variants either match each selector exactly, or via the catch-all key,<br>
204+
resulting in a list « `* bar`, `foo *`, `foo bar`, `* *` » of variants.
205+
206+
4. Sorting the variants:<br>
207+
The list `sortable` is first set with the variants in their source order
208+
and scores determined by the second selector:<br>
209+
« ( 0, `* bar` ), ( 1, `foo *` ), ( 0, `foo bar` ), ( 1, `* *` ) »<br>
210+
This is then sorted as:<br>
211+
« ( 0, `* bar` ), ( 0, `foo bar` ), ( 1, `foo *` ), ( 1, `* *` ) ».<br>
212+
To sort according to the first selector, the scores are updated to:<br>
213+
« ( 1, `* bar` ), ( 0, `foo bar` ), ( 0, `foo *` ), ( 1, `* *` ) ».<br>
214+
This is then sorted as:<br>
215+
« ( 0, `foo bar` ), ( 0, `foo *` ), ( 1, `* bar` ), ( 1, `* *` ) ».<br>
216+
217+
5. The pattern `{Foo and bar}` of the most preferred `foo bar` variant is selected.
218+
219+
#### Example 3
220+
221+
Presuming a more powerful implementation which supports selection on numerical values,
222+
and provides a `:plural` function that matches keys by their exact value
223+
as well as their plural category (preferring the former, if possible),
224+
and an Enligh-language formatting context in which
225+
the variable reference `$count` resolves to the number `1`,
226+
pattern selection proceeds as follows for this message:
227+
228+
```
229+
match {$count :plural}
230+
when one {Category match}
231+
when 1 {Exact match}
232+
when * {Other match}
233+
```
234+
235+
1. For the selector:<br>
236+
The value of the selector is resolved to an implementation-defined value
237+
that is capable of performing English plural category selection on the value `1`.<br>
238+
The available keys « `'one'`, `'1'` » are passed to
239+
the implementation's MatchSelectorKeys method,<br>
240+
resulting in a list « `'1'`, `'one'` » of matching keys.
241+
242+
2. Creating the list `vars` of variants matching all keys:<br>
243+
The keys of all variants are included in the list of matching keys, or use the catch-all key,<br>
244+
resulting in a list « `one`, `1`, `*` » of variants.
245+
246+
3. Sorting the variants:<br>
247+
The list `sortable` is first set with the variants in their source order
248+
and scores determined by the selector key order:<br>
249+
« ( 1, `one` ), ( 0, `1` ), ( 2, `*` ) »<br>
250+
This is then sorted as:<br>
251+
« ( 0, `1` ), ( 1, `one` ), ( 2, `*` ) »<br>
252+
253+
5. The pattern `{Exact match}` of the most preferred `1` variant is selected.
254+
22255
## Error Handling
23256

24257
Errors in messages and their formatting may occur and be detected

0 commit comments

Comments
 (0)