Skip to content

Commit 3db08a1

Browse files
authored
Add FunctionReference (#210)
1 parent 53d8803 commit 3db08a1

File tree

8 files changed

+263
-78
lines changed

8 files changed

+263
-78
lines changed

spec/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@
9191
Junk represents a literal slice of unparsed content and shouldn't have
9292
its line endings normalized to LF.
9393
94+
- Add the `FunctionReference` production. (#210)
95+
96+
Function references in `CallExpressions` are now stored as
97+
`FunctionReference` AST nodes, with an `id` field which is an
98+
`Identifier`.
99+
100+
The `Function` production and its corresponding AST node have been
101+
removed. The logic validating that function names are all upper-case has
102+
been moved to `abstract.mjs`.
103+
94104
## 0.7.0 (October 15, 2018)
95105
96106
- Relax the indentation requirement. (#87)

spec/fluent.ebnf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ NumberLiteral ::= "-"? digit+ ("." digit+)?
6464
MessageReference ::= Identifier
6565
TermReference ::= "-" Identifier
6666
VariableReference ::= "$" Identifier
67-
CallExpression ::= Function blank? "(" blank? argument_list blank? ")"
67+
FunctionReference ::= Identifier
68+
CallExpression ::= FunctionReference blank? "(" blank? argument_list blank? ")"
6869
argument_list ::= (Argument blank? "," blank?)* Argument?
6970
Argument ::= NamedArgument
7071
| InlineExpression
@@ -79,9 +80,8 @@ Variant ::= line_end blank? VariantKey blank_inline? Value
7980
DefaultVariant ::= line_end blank? "*" VariantKey blank_inline? Value
8081
VariantKey ::= "[" blank? (NumberLiteral | Identifier) blank? "]"
8182

82-
/* Identifiers */
83+
/* Identifier */
8384
Identifier ::= [a-zA-Z] [a-zA-Z0-9_-]*
84-
Function ::= [A-Z] [A-Z_?-]*
8585

8686
/* Content Characters
8787
*

syntax/abstract.mjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function list_into(Type) {
7272
|| (selector instanceof FTL.AttributeExpression
7373
&& selector.ref instanceof FTL.MessageReference);
7474
if (invalid_selector_found) {
75-
return never("Invalid selector type: ${selector.type}.");
75+
return never(`Invalid selector type: ${selector.type}.`);
7676
}
7777
let invalid_variants_found = variants.some(
7878
variant => variant.value instanceof FTL.VariantList);
@@ -94,14 +94,24 @@ export function list_into(Type) {
9494

9595
export function into(Type) {
9696
switch (Type) {
97+
case FTL.FunctionReference:
98+
const VALID_FUNCTION_NAME = /^[A-Z][A-Z0-9_?-]*$/;
99+
return identifier => {
100+
if (!VALID_FUNCTION_NAME.test(identifier.name)) {
101+
return never(
102+
`Invalid function name: ${identifier.name}. ` +
103+
"Function names must be upper-case.");
104+
}
105+
return always(new Type(identifier));
106+
};
97107
case FTL.Placeable:
98108
return expression => {
99109
let invalid_expression_found =
100110
expression instanceof FTL.AttributeExpression
101111
&& expression.ref instanceof FTL.TermReference;
102112
if (invalid_expression_found) {
103113
return never(
104-
"Invalid expression type: ${expression.type}.");
114+
`Invalid expression type: ${expression.type}.`);
105115
}
106116
return always(new Type(expression));
107117
};

syntax/ast.mjs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ export class VariableReference extends Expression {
121121
}
122122
}
123123

124+
export class FunctionReference extends Expression {
125+
constructor(id) {
126+
super();
127+
this.type = "FunctionReference";
128+
this.id = id;
129+
}
130+
}
131+
124132
export class SelectExpression extends Expression {
125133
constructor(selector, variants) {
126134
super();
@@ -222,13 +230,6 @@ export class ResourceComment extends BaseComment {
222230
}
223231
}
224232

225-
export class Function extends Identifier {
226-
constructor(name) {
227-
super(name);
228-
this.type = "Function";
229-
}
230-
}
231-
232233
export class Junk extends SyntaxNode {
233234
constructor(content) {
234235
super();

syntax/grammar.mjs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,12 @@ let VariableReference = defer(() =>
238238
.map(element_at(1))
239239
.chain(into(FTL.VariableReference)));
240240

241+
let FunctionReference = defer(() =>
242+
Identifier.chain(into(FTL.FunctionReference)));
243+
241244
let CallExpression = defer(() =>
242245
sequence(
243-
Function.abstract,
246+
FunctionReference.abstract,
244247
maybe(blank),
245248
string("("),
246249
maybe(blank),
@@ -350,8 +353,8 @@ let VariantKey = defer(() =>
350353
string("]"))
351354
.map(element_at(2)));
352355

353-
/* ----------- */
354-
/* Identifiers */
356+
/* ---------- */
357+
/* Identifier */
355358

356359
let Identifier =
357360
sequence(
@@ -362,15 +365,6 @@ let Identifier =
362365
.map(join)
363366
.chain(into(FTL.Identifier));
364367

365-
let Function =
366-
sequence(
367-
charset("A-Z"),
368-
repeat(
369-
charset("A-Z_?-")))
370-
.map(flatten(1))
371-
.map(join)
372-
.chain(into(FTL.Function));
373-
374368
/* -------------------------------------------------------------------------- */
375369
/* Content Characters
376370
*

test/fixtures/call_expressions.ftl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## Callees
2+
3+
function-callee = {FUNCTION()}
4+
5+
# ERROR Equivalent to a MessageReference callee.
6+
mixed-case-callee = {Function()}
7+
8+
# ERROR MessageReference is not a valid callee.
9+
message-callee = {message()}
10+
# ERROR TermReference is not a valid callee.
11+
term-callee = {-term()}
12+
# ERROR VariableReference is not a valid callee.
13+
variable-callee = {$variable()}
14+
15+
## Arguments
16+
117
positional-args = {FUN(1, "a", msg)}
218
named-args = {FUN(x: 1, y: "Y")}
319
dense-named-args = {FUN(x:1, y:"Y")}

0 commit comments

Comments
 (0)