Skip to content
← Back

src/syntax/parsers/expressions/tuple.ghul

1
namespace Syntax.Parsers.Expressions is
2
use IO.Std
3
4
use Source
5
6
use Ghul.Pipes
7
8
// Parses a parenthesised group in expression position. The group's
9
// reading is decided by the token that follows what has already
10
// been parsed, never by speculating ahead:
11
//
12
// `,` / `)` / `:` — a tuple, a parenthesised expression, or a
13
// lambda's formal parameters (the enclosing
14
// parser rewrites on a following `=>`).
15
// `;` — a block expression `(statement; ...; value)`:
16
// the element already parsed becomes the first
17
// statement, and the rest of the group parses
18
// as a statement list. Same construct as
19
// `val ... lav`, in its parenthesised spelling.
20
// `=` at top level — an assignment statement opening a block
21
// expression; the parsed element is its target.
22
//
23
// A token that can only open a statement (`let`, `try`, `return`,
24
// ...) commits the group as a block immediately.
25
class TUPLE(
26
expression_list_parser: Parser[Trees.Expressions.LIST],
27
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
28
expression_parser: Parser[Trees.Expressions.Expression],
29
paren_statement_list_parser: Parser[Trees.Statements.LIST]
30
): Base[Trees.Expressions.Expression] is
31
description: string => "tuple"
32
33
// Tokens that open a statement but can never open a tuple
34
// element, so the group is a block from its first token. `let`
35
// and `assert` do have expression forms (`let ... in`,
36
// `assert ... in`); the statement parser produces exactly those
37
// expressions when the `in` is present, and the single-
38
// expression unwrap below hands them back unwrapped, so routing
39
// them here changes nothing for the existing forms.
40
_statement_only_tokens: Collections.LIST[Lexical.TOKEN]
41
42
super()
43
44
init(..) is
45
_statement_only_tokens = Collections.LIST([
46
Lexical.TOKEN.LET,
47
Lexical.TOKEN.ASSERT,
48
Lexical.TOKEN.TRY,
49
Lexical.TOKEN.RETURN,
50
Lexical.TOKEN.THROW,
51
Lexical.TOKEN.BREAK,
52
Lexical.TOKEN.CONTINUE,
53
Lexical.TOKEN.YIELD
54
])
55
si
56
57
parse(context: CONTEXT) -> Trees.Expressions.Expression is
58
let start = context.location
59
60
// Parsing the elements runs the enclosing list loop again,
61
// which clears `allow_tuple_element`, so capture here
62
// whether this group is itself in a position that could be
63
// a lambda's formal parameter.
64
let could_be_formal_argument = context.allow_tuple_element
65
66
context.next_token(Lexical.TOKEN.PAREN_OPEN, syntax_error_message)
67
68
if _statement_only_tokens.contains(context.current.token) then
69
return _parse_block(context, start, null)
70
fi
71
72
let expressions: Trees.Expressions.LIST mut
73
74
if context.current.token != Lexical.TOKEN.PAREN_CLOSE then
75
expressions = expression_list_parser.parse(context)!
76
else
77
expressions = Trees.Expressions.LIST(context.location, Collections.LIST[Trees.Expressions.Expression]())
78
fi
79
80
if expressions.expressions.count == 1 then
81
if context.current.token == Lexical.TOKEN.SEMICOLON then
82
return _parse_block(context, start, _statement_from_element(context, expressions.expressions[0]))
83
elif context.current.token == Lexical.TOKEN.ASSIGN then
84
// A top-level `=` whose target is not a simple name
85
// (`x.y = e`, `xs[i] = e`): a simple name's `=` was
86
// consumed into a VARIABLE element already, so only
87
// an assignment statement reads this way.
88
let left = expressions.expressions[0]
89
90
context.next_token()
91
92
let right = expression_parser.parse(context)!
93
94
return _parse_block(
95
context,
96
start,
97
Trees.Statements.ASSIGNMENT(
98
left.location::right.location,
99
left.rewrite_as_assignment_left(),
100
right
101
)
102
)
103
fi
104
105
// A compound statement (`if`, `case`, `for`, `while`,
106
// `do`) followed by anything that cannot continue its
107
// expression commits the group as a block: what follows
108
// can only be a further statement or the block's tail.
109
// An operator-headed tail never reaches here — the
110
// precedence climb absorbs it into the element as an
111
// infix operand, so `(if … fi - 1)` keeps its
112
// expression reading; the block reading of that spelling
113
// needs a `;` after the closing keyword.
114
let element = expressions.expressions[0]
115
116
if
117
isa Trees.Expressions.STATEMENT(element) /\
118
context.current.token != Lexical.TOKEN.COMMA /\
119
context.current.token != Lexical.TOKEN.PAREN_CLOSE /\
120
context.current.token != Lexical.TOKEN.COLON
121
then
122
return _parse_block(context, start, _statement_from_element(context, element))
123
fi
124
125
// With optional statement terminators, a complete element
126
// followed by a line break commits the group as a block the
127
// same way a `;` does: what opens the new line can only be
128
// a further statement. A top-level `,` has always arrived
129
// first in a tuple by this point, so the tuple reading is
130
// unaffected. A line-start operator is excluded — gluing it
131
// into a fresh statement would silently misread `(a` ... `+ b)`
132
// — so that spelling stays a syntax error at the `)` check
133
// below.
134
if
135
context.at_inferred_terminator /\
136
context.current.token != Lexical.TOKEN.PAREN_CLOSE /\
137
context.current.token != Lexical.TOKEN.COMMA /\
138
context.current.token != Lexical.TOKEN.COLON /\
139
context.current.token != Lexical.TOKEN.OPERATOR
140
then
141
context.note_inferred_terminator()
142
143
return _parse_block(context, start, _statement_from_element(context, element))
144
fi
145
fi
146
147
expressions.rewrite_as_tuple_elements()
148
149
context.next_token(Lexical.TOKEN.PAREN_CLOSE, syntax_error_message)
150
151
// The end of the `)` itself: once a terminator is inferred at the
152
// end of the line, the current token is already the next line's.
153
let end mut = context.previous_end
154
155
// `(a, b): T` - a type ascription on a parenthesised group.
156
// Only meaningful where the group could be a lambda's
157
// formal parameter, which is exactly where
158
// `allow_tuple_element` is set, so restrict it to there and
159
// leave the `:` for the caller to report anywhere else. A
160
// group destructures anything positionally, not just a
161
// tuple, so the ascription takes the full type syntax.
162
let type_expression: Trees.TypeExpressions.TypeExpression? mut = null
163
164
// ... and only when every element could be a pattern
165
// element. `(1, 2): T` cannot be a parameter however it is
166
// used, so leave its `:` to be reported where it always
167
// was rather than consuming it and dropping the type.
168
let could_be_pattern =
169
expressions.expressions.count > 1 /\
170
expressions.expressions |> all(e => e.try_copy_as_variable_left()?)
171
172
if
173
could_be_formal_argument /\
174
could_be_pattern /\
175
context.current.token == Lexical.TOKEN.COLON
176
then
177
context.next_token()
178
179
type_expression = type_parser.parse(context)!
180
end = type_expression.location
181
fi
182
183
let result = Trees.Expressions.TUPLE(start::end, expressions, false)
184
185
if type_expression? then
186
result.set_type_expression(type_expression)
187
fi
188
189
return result
190
si
191
192
// The already-parsed first element of a group that a `;` has
193
// just committed as a block, recast as the block's first
194
// statement. A compound statement parsed in expression position
195
// arrives wrapped (`Expressions.STATEMENT`); unwrap it rather
196
// than wrapping twice. A `name = e` element parsed as a
197
// VARIABLE was an assignment all along.
198
_statement_from_element(context: CONTEXT, element: Trees.Expressions.Expression) -> Trees.Statements.Statement is
199
if isa Trees.Expressions.STATEMENT(element) then
200
return element.statement
201
fi
202
203
if isa Trees.Expressions.VARIABLE(element) then
204
if let initializer = element.initializer /\ isa Trees.TypeExpressions.INFER(element.type_expression) then
205
return Trees.Statements.ASSIGNMENT(
206
element.location,
207
Trees.Expressions.IDENTIFIER(element.name.location, element.name).rewrite_as_assignment_left(),
208
initializer
209
)
210
fi
211
212
// `x: T = e;` or `x: T;` — a typed declaration needs `let`.
213
context.error(element.location, "a local variable definition needs let")
214
215
return Trees.Statements.EXPRESSION(
216
element.location,
217
Trees.Expressions.Literals.NONE(element.location)
218
)
219
fi
220
221
return Trees.Statements.EXPRESSION(element.location, element)
222
si
223
224
// Parse the remainder of the group as statements through to the
225
// `)`, then deliver: the single already-parsed expression
226
// unwrapped where the group turned out to be an ordinary
227
// parenthesised expression after all (`(let x = e in b)`), a
228
// VAL_BLOCK otherwise.
229
_parse_block(
230
context: CONTEXT,
231
start: LOCATION,
232
first: Trees.Statements.Statement?
233
) -> Trees.Expressions.Expression is
234
// Statements are a fresh context: an enclosing argument
235
// list's element flag must not leak into their expressions.
236
context.allow_tuple_element = false
237
238
let rest = paren_statement_list_parser.parse(context)!
239
240
let end = context.location
241
242
context.next_token(Lexical.TOKEN.PAREN_CLOSE, syntax_error_message)
243
244
if !first? /\ (rest.statements |> count()) == 1 /\ !rest.last_was_terminated then
245
// A statement-only opener that parsed to a single
246
// unterminated expression statement is that expression,
247
// parenthesised: `(let x = e in b)`, `(assert c in v)`.
248
// Handing it back unwrapped keeps the tree those forms
249
// have always produced.
250
if let statement: Trees.Statements.EXPRESSION = rest.last then
251
return statement.expression
252
fi
253
fi
254
255
let statements = Collections.LIST[Trees.Statements.Statement]()
256
257
if first? then
258
statements.add(first)
259
fi
260
261
for statement in rest.statements do
262
statements.add(statement)
263
od
264
265
let body = Trees.Statements.LIST(start::end, statements)
266
267
// An empty remainder means the first statement was followed
268
// directly by `;)` or `)`: nothing unterminated remains, so
269
// the block has no tail value.
270
body.last_was_terminated =
271
if rest.is_empty then
272
true
273
else
274
rest.last_was_terminated
275
fi
276
277
let result = Trees.Expressions.VAL_BLOCK(start::end, body)
278
279
result.is_parenthesised = true
280
281
return result
282
si
283
si
284
si