Skip to content

Commit ccbfb83

Browse files
committed
Parse Comments line by line
1 parent cedb05d commit ccbfb83

File tree

3 files changed

+61
-79
lines changed

3 files changed

+61
-79
lines changed

spec/fluent.ebnf

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
Resource ::= (Entry | blank_block | junk_line)*
44
Entry ::= Message
55
| Term
6-
| (ResourceComment | GroupComment | Comment)
7-
Message ::= Comment? Identifier blank_inline? "=" ((Pattern Attribute*) | (Attribute+))
8-
Term ::= Comment? TermIdentifier blank_inline? "=" Value Attribute*
9-
Comment ::= ("#" ("\u0020" /.*/)? line_end)+
10-
GroupComment ::= ("##" ("\u0020" /.*/)? line_end)+
11-
ResourceComment ::= ("###" ("\u0020" /.*/)? line_end)+
6+
| CommentLine
7+
Message ::= Identifier blank_inline? "=" ((Pattern Attribute*) | (Attribute+))
8+
Term ::= TermIdentifier blank_inline? "=" Value Attribute*
9+
CommentLine ::= ("###" | "##" | "#") ("\u0020" /.*/)? line_end
1210

1311
/* Adjacent junk_lines should be joined into FTL.Junk during the AST
1412
construction. */

syntax/abstract.mjs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ import {always, never} from "../lib/combinators.mjs";
1010

1111
export function list_into(Type) {
1212
switch (Type) {
13+
case FTL.BaseComment:
14+
return ([sigil, content = ""]) => {
15+
switch (sigil) {
16+
case "#":
17+
return always(new FTL.Comment(content));
18+
case "##":
19+
return always(new FTL.GroupComment(content));
20+
case "###":
21+
return always(new FTL.ResourceComment(content));
22+
default:
23+
return never(`Unknown comment sigil: ${sigil}`);
24+
}
25+
};
1326
case FTL.CallExpression:
1427
return ([callee, args]) => {
1528
let positional_args = [];
@@ -32,9 +45,6 @@ export function list_into(Type) {
3245
let named_args = Array.from(named_map.values());
3346
return always(new Type(callee, positional_args, named_args));
3447
};
35-
case FTL.Message:
36-
return ([comment, ...args]) =>
37-
always(new Type(...args, comment));
3848
case FTL.Pattern:
3949
return elements =>
4050
always(new FTL.Pattern(
@@ -46,7 +56,12 @@ export function list_into(Type) {
4656
return entries =>
4757
always(new FTL.Resource(
4858
entries
49-
.reduce(join_adjacent(FTL.Junk), [])
59+
.reduce(join_adjacent(
60+
FTL.Junk,
61+
FTL.Comment,
62+
FTL.GroupComment,
63+
FTL.ResourceComment), [])
64+
.reduce(attach_comments, [])
5065
.filter(remove_blank_lines)));
5166
case FTL.SelectExpression:
5267
return ([selector, variants]) => {
@@ -68,9 +83,6 @@ export function list_into(Type) {
6883
}
6984
return always(new Type(selector, variants));
7085
};
71-
case FTL.Term:
72-
return ([comment, ...args]) =>
73-
always(new Type(...args, comment));
7486
case FTL.VariantList:
7587
return ([variants]) =>
7688
always(new Type(variants));
@@ -82,21 +94,6 @@ export function list_into(Type) {
8294

8395
export function into(Type) {
8496
switch (Type) {
85-
case FTL.Comment:
86-
case FTL.GroupComment:
87-
case FTL.ResourceComment:
88-
return content => {
89-
if (!content.endsWith("\n")) {
90-
// The comment ended with the EOF; don't trim it.
91-
return always(new Type(content));
92-
}
93-
if (content.endsWith("\r\n")) {
94-
// Trim the CRLF from the end of the comment.
95-
return always(new Type(content.slice(0, -2)));
96-
}
97-
// Trim the LF from the end of the comment.
98-
return always(new Type(content.slice(0, -1)));
99-
};
10097
case FTL.Placeable:
10198
return expression => {
10299
let invalid_expression_found =
@@ -121,15 +118,16 @@ export function into(Type) {
121118
}
122119
}
123120

124-
function join_adjacent(Type) {
121+
function join_adjacent(...types) {
125122
return function(acc, cur) {
126123
let prev = acc[acc.length - 1];
127-
if (prev instanceof Type && cur instanceof Type) {
128-
join_of_type(Type, prev, cur);
129-
return acc;
130-
} else {
131-
return acc.concat(cur);
124+
for (let Type of types) {
125+
if (prev instanceof Type && cur instanceof Type) {
126+
join_of_type(Type, prev, cur);
127+
return acc;
128+
}
132129
}
130+
return acc.concat(cur);
133131
};
134132
}
135133

@@ -139,12 +137,30 @@ function join_of_type(Type, ...elements) {
139137
case FTL.TextElement:
140138
return elements.reduce((a, b) =>
141139
(a.value += b.value, a));
140+
case FTL.Comment:
141+
case FTL.GroupComment:
142+
case FTL.ResourceComment:
143+
return elements.reduce((a, b) =>
144+
(a.content += `\n${b.content}`, a));
142145
case FTL.Junk:
143146
return elements.reduce((a, b) =>
144147
(a.content += b.content, a));
145148
}
146149
}
147150

151+
function attach_comments(acc, cur) {
152+
let prev = acc[acc.length - 1];
153+
if (prev instanceof FTL.Comment
154+
&& (cur instanceof FTL.Message
155+
|| cur instanceof FTL.Term)) {
156+
cur.comment = prev;
157+
acc[acc.length - 1] = cur;
158+
return acc;
159+
} else {
160+
return acc.concat(cur);
161+
}
162+
}
163+
148164
function trim_text_at_extremes(element, index, array) {
149165
if (element instanceof FTL.TextElement) {
150166
if (index === 0) {

syntax/grammar.mjs

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,10 @@ let Entry = defer(() =>
2424
either(
2525
Message,
2626
Term,
27-
either(
28-
ResourceComment,
29-
GroupComment,
30-
Comment)));
27+
CommentLine));
3128

3229
let Message = defer(() =>
3330
sequence(
34-
maybe(Comment).abstract,
3531
Identifier.abstract,
3632
maybe(blank_inline),
3733
string("="),
@@ -48,7 +44,6 @@ let Message = defer(() =>
4844

4945
let Term = defer(() =>
5046
sequence(
51-
maybe(Comment).abstract,
5247
TermIdentifier.abstract,
5348
maybe(blank_inline),
5449
string("="),
@@ -57,47 +52,20 @@ let Term = defer(() =>
5752
.map(keep_abstract)
5853
.chain(list_into(FTL.Term)));
5954

60-
let Comment = defer(() =>
61-
repeat1(
62-
sequence(
63-
string("#"),
64-
maybe(
65-
sequence(
66-
string(" "),
67-
regex(/.*/).abstract)),
68-
line_end.abstract))
69-
.map(flatten(2))
70-
.map(keep_abstract)
71-
.map(join)
72-
.chain(into(FTL.Comment)));
73-
74-
let GroupComment = defer(() =>
75-
repeat1(
76-
sequence(
77-
string("##"),
78-
maybe(
79-
sequence(
80-
string(" "),
81-
regex(/.*/).abstract)),
82-
line_end.abstract))
83-
.map(flatten(2))
84-
.map(keep_abstract)
85-
.map(join)
86-
.chain(into(FTL.GroupComment)));
87-
88-
let ResourceComment = defer(() =>
89-
repeat1(
90-
sequence(
55+
let CommentLine = defer(() =>
56+
sequence(
57+
either(
9158
string("###"),
92-
maybe(
93-
sequence(
94-
string(" "),
95-
regex(/.*/).abstract)),
96-
line_end.abstract))
97-
.map(flatten(2))
59+
string("##"),
60+
string("#")).abstract,
61+
maybe(
62+
sequence(
63+
string(" "),
64+
regex(/.*/).abstract)),
65+
line_end)
66+
.map(flatten(1))
9867
.map(keep_abstract)
99-
.map(join)
100-
.chain(into(FTL.ResourceComment)));
68+
.chain(list_into(FTL.BaseComment)));
10169

10270
/* ----------------------------------------------------------------- */
10371
/* Adjacent junk_lines should be joined into FTL.Junk during the AST

0 commit comments

Comments
 (0)