From 8fd18fec1e29705a5405d2231a1168ebec996518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 6 Nov 2018 11:45:27 +0100 Subject: [PATCH 1/2] Add Parameterized Terms --- spec/fluent.ebnf | 4 +- spec/valid.md | 1 + syntax/abstract.mjs | 2 + syntax/grammar.mjs | 7 +- test/fixtures/call_expressions.ftl | 3 +- test/fixtures/call_expressions.json | 38 +++-- test/fixtures/select_expressions.ftl | 16 +- test/fixtures/select_expressions.json | 22 ++- test/fixtures/term_parameters.ftl | 8 + test/fixtures/term_parameters.json | 203 ++++++++++++++++++++++++++ 10 files changed, 287 insertions(+), 17 deletions(-) create mode 100644 test/fixtures/term_parameters.ftl create mode 100644 test/fixtures/term_parameters.json diff --git a/spec/fluent.ebnf b/spec/fluent.ebnf index c6b1cf1..ad0eef8 100644 --- a/spec/fluent.ebnf +++ b/spec/fluent.ebnf @@ -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 diff --git a/spec/valid.md b/spec/valid.md index a0e0e10..8460200 100644 --- a/spec/valid.md +++ b/spec/valid.md @@ -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 diff --git a/syntax/abstract.mjs b/syntax/abstract.mjs index eeb9a91..ad50430 100644 --- a/syntax/abstract.mjs +++ b/syntax/abstract.mjs @@ -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); diff --git a/syntax/grammar.mjs b/syntax/grammar.mjs index 7f87f63..0432634 100644 --- a/syntax/grammar.mjs +++ b/syntax/grammar.mjs @@ -243,7 +243,7 @@ let FunctionReference = defer(() => let CallExpression = defer(() => sequence( - FunctionReference.abstract, + Callee.abstract, maybe(blank), string("("), maybe(blank), @@ -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( diff --git a/test/fixtures/call_expressions.ftl b/test/fixtures/call_expressions.ftl index 9ed69bd..a4f61da 100644 --- a/test/fixtures/call_expressions.ftl +++ b/test/fixtures/call_expressions.ftl @@ -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()} diff --git a/test/fixtures/call_expressions.json b/test/fixtures/call_expressions.json index 0a3ae06..4cc36d4 100644 --- a/test/fixtures/call_expressions.json +++ b/test/fixtures/call_expressions.json @@ -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." @@ -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." diff --git a/test/fixtures/select_expressions.ftl b/test/fixtures/select_expressions.ftl index 859c01a..ac88826 100644 --- a/test/fixtures/select_expressions.ftl +++ b/test/fixtures/select_expressions.ftl @@ -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] {""} diff --git a/test/fixtures/select_expressions.json b/test/fixtures/select_expressions.json index 61e18fa..7cce407 100644 --- a/test/fixtures/select_expressions.json +++ b/test/fixtures/select_expressions.json @@ -81,7 +81,7 @@ "type": "Message", "id": { "type": "Identifier", - "name": "valid-selector" + "name": "valid-selector-term-attribute" }, "value": { "type": "Pattern", @@ -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", diff --git a/test/fixtures/term_parameters.ftl b/test/fixtures/term_parameters.ftl new file mode 100644 index 0000000..6144236 --- /dev/null +++ b/test/fixtures/term_parameters.ftl @@ -0,0 +1,8 @@ +-term = { $arg -> + *[key] Value +} + +key01 = { -term } +key02 = { -term() } +key03 = { -term(arg: 1) } +key04 = { -term("positional", narg1: 1, narg2: 2) } diff --git a/test/fixtures/term_parameters.json b/test/fixtures/term_parameters.json new file mode 100644 index 0000000..f9f0961 --- /dev/null +++ b/test/fixtures/term_parameters.json @@ -0,0 +1,203 @@ +{ + "type": "Resource", + "body": [ + { + "type": "Term", + "id": { + "type": "Identifier", + "name": "term" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "Placeable", + "expression": { + "type": "SelectExpression", + "selector": { + "type": "VariableReference", + "id": { + "type": "Identifier", + "name": "arg" + } + }, + "variants": [ + { + "type": "Variant", + "key": { + "type": "Identifier", + "name": "key" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "TextElement", + "value": "Value" + } + ] + }, + "default": true + } + ] + } + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "key01" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "Placeable", + "expression": { + "type": "TermReference", + "id": { + "type": "Identifier", + "name": "term" + } + } + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "key02" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "Placeable", + "expression": { + "type": "CallExpression", + "callee": { + "type": "TermReference", + "id": { + "type": "Identifier", + "name": "term" + } + }, + "positional": [], + "named": [] + } + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "key03" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "Placeable", + "expression": { + "type": "CallExpression", + "callee": { + "type": "TermReference", + "id": { + "type": "Identifier", + "name": "term" + } + }, + "positional": [], + "named": [ + { + "type": "NamedArgument", + "name": { + "type": "Identifier", + "name": "arg" + }, + "value": { + "type": "NumberLiteral", + "value": "1" + } + } + ] + } + } + ] + }, + "attributes": [], + "comment": null + }, + { + "type": "Message", + "id": { + "type": "Identifier", + "name": "key04" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "Placeable", + "expression": { + "type": "CallExpression", + "callee": { + "type": "TermReference", + "id": { + "type": "Identifier", + "name": "term" + } + }, + "positional": [ + { + "type": "StringLiteral", + "raw": "positional", + "value": "positional" + } + ], + "named": [ + { + "type": "NamedArgument", + "name": { + "type": "Identifier", + "name": "narg1" + }, + "value": { + "type": "NumberLiteral", + "value": "1" + } + }, + { + "type": "NamedArgument", + "name": { + "type": "Identifier", + "name": "narg2" + }, + "value": { + "type": "NumberLiteral", + "value": "2" + } + } + ] + } + } + ] + }, + "attributes": [], + "comment": null + } + ] +} From 7415f2d7bd4be2cd2ecc5fba677ecb4869022d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 8 Nov 2018 14:55:22 +0100 Subject: [PATCH 2/2] Update CHANGELOG --- spec/CHANGELOG.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/spec/CHANGELOG.md b/spec/CHANGELOG.md index 68152ce..abdb8d6 100644 --- a/spec/CHANGELOG.md +++ b/spec/CHANGELOG.md @@ -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