Skip to content

Add Parameterized Terms #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions spec/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,59 @@
on the second line of its value.
```

- Introduce parameterized `Terms`. (#176)

References to `Terms` can now receive parameters which will be used by
the runtime as values of variables referenced from within the `Term`.
This allows `Terms` to use regular `Patterns` as values, rather than
`VariantLists`:

```properties
# A Term with a VariantList as a value.
-thing = {
*[definite] the thing
*[indefinite] a thing
}

this = This is { -term[indefinite] }.
```

```properties
# A parametrized Term with a Pattern as a value.
-thing = { $article ->
*[definite] the thing
*[indefinite] a thing
}

this = This is { -thing(article: "indefinite") }.
```

Since `Patterns` can be nested, this feature allows more complex
hierarchies of term values:

```properties
# A parametrized Term with nested Patterns.
-thing = { $article ->
*[definite] { $first-letter ->
*[lower] the thing
[upper] The thing
}
[indefinite] { $first-letter ->
*[lower] a thing
[upper] A thing
}
}

this = This is { -term(first-letter: "lower", article: "indefinite") }.
```

Parameters must be named; positional parameters are ignored. If a
parameter is omitted then the regular default variant logic applies. The
above example could thus be written as `{-term(article: "indefinite")}`
and the `lower` variant would be used because it is marked as the default
one. If no parameters are specified, the paranthesis can be omitted:
`{-term()}` and `{-term}` are functionally the same.

- Support astral Unicode characters. (#174)

Unicode characters from outside of the Basic Multilingual Plane can now
Expand Down
4 changes: 3 additions & 1 deletion spec/fluent.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ MessageReference ::= Identifier
TermReference ::= "-" Identifier
VariableReference ::= "$" Identifier
FunctionReference ::= Identifier
CallExpression ::= FunctionReference blank? "(" blank? argument_list blank? ")"
CallExpression ::= Callee blank? "(" blank? argument_list blank? ")"
Callee ::= FunctionReference
| TermReference
argument_list ::= (Argument blank? "," blank?)* Argument?
Argument ::= NamedArgument
| InlineExpression
Expand Down
1 change: 1 addition & 0 deletions spec/valid.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ Invalid Select Expressions:

SelectExpression.selector > MessageReference
SelectExpression.selector > TermReference
SelectExpression.selector > CallExpression.callee > TermReference
SelectExpression.selector > VariantExpression
SelectExpression.selector > AttributeExpression.ref > MessageReference
2 changes: 2 additions & 0 deletions syntax/abstract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export function list_into(Type) {
let invalid_selector_found =
selector instanceof FTL.MessageReference
|| selector instanceof FTL.TermReference
|| (selector instanceof FTL.CallExpression
&& selector.callee instanceof FTL.TermReference)
|| selector instanceof FTL.VariantExpression
|| (selector instanceof FTL.AttributeExpression
&& selector.ref instanceof FTL.MessageReference);
Expand Down
7 changes: 6 additions & 1 deletion syntax/grammar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ let FunctionReference = defer(() =>

let CallExpression = defer(() =>
sequence(
FunctionReference.abstract,
Callee.abstract,
maybe(blank),
string("("),
maybe(blank),
Expand All @@ -253,6 +253,11 @@ let CallExpression = defer(() =>
.map(keep_abstract)
.chain(list_into(FTL.CallExpression)));

let Callee =
either(
FunctionReference,
TermReference);

let argument_list = defer(() =>
sequence(
repeat(
Expand Down
3 changes: 1 addition & 2 deletions test/fixtures/call_expressions.ftl
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
## Callees

function-callee = {FUNCTION()}
term-callee = {-term()}

# ERROR Equivalent to a MessageReference callee.
mixed-case-callee = {Function()}

# ERROR MessageReference is not a valid callee.
message-callee = {message()}
# ERROR TermReference is not a valid callee.
term-callee = {-term()}
# ERROR VariableReference is not a valid callee.
variable-callee = {$variable()}

Expand Down
38 changes: 29 additions & 9 deletions test/fixtures/call_expressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,35 @@
"attributes": [],
"comment": null
},
{
"type": "Message",
"id": {
"type": "Identifier",
"name": "term-callee"
},
"value": {
"type": "Pattern",
"elements": [
{
"type": "Placeable",
"expression": {
"type": "CallExpression",
"callee": {
"type": "TermReference",
"id": {
"type": "Identifier",
"name": "term"
}
},
"positional": [],
"named": []
}
}
]
},
"attributes": [],
"comment": null
},
{
"type": "Comment",
"content": "ERROR Equivalent to a MessageReference callee."
Expand All @@ -52,15 +81,6 @@
"annotations": [],
"content": "message-callee = {message()}\n"
},
{
"type": "Comment",
"content": "ERROR TermReference is not a valid callee."
},
{
"type": "Junk",
"annotations": [],
"content": "term-callee = {-term()}\n"
},
{
"type": "Comment",
"content": "ERROR VariableReference is not a valid callee."
Expand Down
16 changes: 14 additions & 2 deletions test/fixtures/select_expressions.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@ new-messages =
*[other] {""}Other
}

valid-selector =
valid-selector-term-attribute =
{ -term.case ->
*[key] value
}

# ERROR
invalid-selector =
invalid-selector-term-value =
{ -term ->
*[key] value
}

# ERROR
invalid-selector-term-variant =
{ -term[case] ->
*[key] value
}

# ERROR
invalid-selector-term-call =
{ -term(case: "nominative") ->
*[key] value
}

empty-variant =
{ 1 ->
*[one] {""}
Expand Down
22 changes: 20 additions & 2 deletions test/fixtures/select_expressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"type": "Message",
"id": {
"type": "Identifier",
"name": "valid-selector"
"name": "valid-selector-term-attribute"
},
"value": {
"type": "Pattern",
Expand Down Expand Up @@ -137,7 +137,25 @@
{
"type": "Junk",
"annotations": [],
"content": "invalid-selector =\n { -term[case] ->\n *[key] value\n }\n"
"content": "invalid-selector-term-value =\n { -term ->\n *[key] value\n }\n"
},
{
"type": "Comment",
"content": "ERROR"
},
{
"type": "Junk",
"annotations": [],
"content": "invalid-selector-term-variant =\n { -term[case] ->\n *[key] value\n }\n"
},
{
"type": "Comment",
"content": "ERROR"
},
{
"type": "Junk",
"annotations": [],
"content": "invalid-selector-term-call =\n { -term(case: \"nominative\") ->\n *[key] value\n }\n"
},
{
"type": "Message",
Expand Down
8 changes: 8 additions & 0 deletions test/fixtures/term_parameters.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-term = { $arg ->
*[key] Value
}

key01 = { -term }
key02 = { -term() }
key03 = { -term(arg: 1) }
key04 = { -term("positional", narg1: 1, narg2: 2) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are positional arguments intended to be legal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're not used by the runtime to resolve the term so even if they're passed, they're just ignored. We could forbid them in abstract for now, in case we find a use for them in the future. How does that sound to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered about abstact, too, but no idea if there's going to be a use or not. I'm not a friend of "for now", either, so, well. Yeah, I filed a documentation bug. No strong opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave them as valid, then. Thanks for filing the issue about the docs.

Loading