Skip to content
← Back

src/syntax/parsers/definitions/function.ghul

1
namespace Syntax.Parsers.Definitions is
2
3
use Source;
4
use Logging;
5
6
use Ghul.Pipes;
7
8
class FUNCTION(
9
identifier_function_name_parser: Parser[Trees.Identifiers.Identifier],
10
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
11
type_list_parser: Parser[Trees.TypeExpressions.LIST],
12
modifier_list_parser: Parser[Trees.Modifiers.LIST],
13
body_parser: Parser[Trees.Bodies.Body],
14
variable_list_parser: Parser[Trees.Variables.LIST]
15
): Base[Trees.Definitions.FUNCTION] is
16
super();
17
18
parse_generic_arguments(
19
context: CONTEXT
20
) -> Syntax.Trees.TypeExpressions.LIST is
21
if context.current.token == Lexical.TOKEN.SQUARE_OPEN then
22
context.next_token();
23
24
let generic_arguments = type_list_parser.parse(context)!;
25
generic_arguments.check_no_reference_types(context.logger);
26
27
let is_poisoned mut = generic_arguments.is_poisoned;
28
29
if !is_poisoned \/ context.current.token == Lexical.TOKEN.SQUARE_CLOSE then
30
is_poisoned = !context.next_token(Lexical.TOKEN.SQUARE_CLOSE) \/ is_poisoned;
31
fi
32
33
generic_arguments.poison(is_poisoned);
34
35
return generic_arguments;
36
fi
37
return Syntax.Trees.TypeExpressions.LIST(context.location, Collections.LIST[Syntax.Trees.TypeExpressions.TypeExpression](0));
38
si
39
40
parse_formal_arguments(
41
context: CONTEXT,
42
start: Source.LOCATION,
43
name: Syntax.Trees.Identifiers.Identifier?,
44
generic_arguments: Syntax.Trees.TypeExpressions.LIST
45
) ->
46
(arguments: Trees.Variables.LIST, function: Trees.Definitions.FUNCTION?)
47
is
48
let is_poisoned mut = false;
49
let arguments_open_parenthesis_location = context.location;
50
51
context.next_token(Lexical.TOKEN.PAREN_OPEN);
52
53
let use arguments_tokenizer_snapshot = context.tokenizer_speculate_then_commit();
54
let use arguments_logger_snaphot = context.logger_speculate_then_commit();
55
56
let arguments: Trees.Variables.LIST mut = Trees.Variables.LIST(context.location, Collections.LIST[Trees.Variables.VARIABLE](0));
57
let function: Trees.Definitions.FUNCTION? mut = null;
58
59
let any_bad_arguments mut = false;
60
61
if context.current_token != Lexical.TOKEN.PAREN_CLOSE then
62
if
63
context.current.token != Lexical.TOKEN.IDENTIFIER /\
64
context.current.token != Lexical.TOKEN.AT
65
then
66
is_poisoned = true;
67
fi
68
69
let in_init = name? /\ name.name =~ "init";
70
let previous_in_init_arguments = context.in_init_arguments;
71
let previous_in_formal_arguments = context.in_formal_arguments;
72
73
if in_init then
74
context.in_init_arguments = true;
75
fi
76
77
context.in_formal_arguments = true;
78
79
try
80
arguments = variable_list_parser.parse(context)!;
81
finally
82
context.in_init_arguments = previous_in_init_arguments;
83
context.in_formal_arguments = previous_in_formal_arguments;
84
yrt
85
86
any_bad_arguments = arguments.is_poisoned;
87
88
let last_valid_argument_line mut = 0;
89
90
for (index, a) in arguments |> index() do
91
if !a.is_splice then
92
a.mark_argument();
93
a.left.mark_argument_recursive();
94
fi
95
96
if a.is_poisoned then
97
is_poisoned = true;
98
any_bad_arguments = true;
99
100
elif a.is_splice then
101
// The `..` splice marker carries no type and
102
// no name. The rewrite-primary-constructors
103
// pass expands it into the surrounding class's
104
// primary parameters; skip the
105
// explicit-argument-type check for it. The
106
// variable parser already rejected `..` in any
107
// non-init position via `in_init_arguments`,
108
// so reaching here means we're in an init.
109
last_valid_argument_line = a.location.start_line;
110
elif
111
!a.left.is_simple_name /\
112
a.left.has_named_group
113
then
114
context.error(a.location, "named destructuring is not supported in a formal argument list");
115
a.poison(true);
116
is_poisoned = true;
117
any_bad_arguments = true;
118
elif a.type_expression.is_inferred then
119
context.error(a.location, "explicit argument type required");
120
a.poison(true);
121
is_poisoned = true;
122
any_bad_arguments = true;
123
else
124
if a.initializer? /\ !isa Trees.Expressions.DEFAULT(a.initializer) then
125
context.error(a.initializer.location, "default value of an argument must be _");
126
fi
127
128
last_valid_argument_line =
129
if a.left.is_simple_name then a.name!.location.start_line else a.location.start_line fi;
130
fi
131
od
132
133
if
134
context.current_token == Lexical.TOKEN.PAREN_OPEN \/
135
(context.current_token == Lexical.TOKEN.IDENTIFIER /\ context.location.start_line > arguments.location.end_line)
136
then
137
// we might have run off the end of an incomplete function
138
// signature into the start of a following function
139
// signature
140
141
let could_be_next_function mut = false;
142
143
// TODO not sure all these conditions are necessary/meaningful
144
if arguments.variables.count == 0 then
145
could_be_next_function = true;
146
elif any_bad_arguments /\ context.location.start_line > arguments.location.end_line then
147
could_be_next_function = true;
148
elif last_valid_argument_line < context.location.start_line then
149
could_be_next_function = true;
150
fi
151
152
if could_be_next_function then
153
arguments_logger_snaphot.backtrack();
154
155
for a in arguments do
156
a.poison(true);
157
od
158
159
let roll_forwards_to_line = context.location.start_line;
160
161
arguments_tokenizer_snapshot.backtrack();
162
163
while context.location.start_line < roll_forwards_to_line do
164
context.next_token();
165
od
166
167
function =
168
Trees.Definitions.FUNCTION(
169
start::(name?.location ?? context.location),
170
name,
171
generic_arguments,
172
arguments,
173
Trees.TypeExpressions.INFER(context.location),
174
Trees.Modifiers.LIST(context.location, null, null),
175
Trees.Bodies.NULL(context.location)
176
);
177
178
context.logger.error(
179
if arguments.variables.count > 0 then
180
arguments.location
181
else
182
arguments_open_parenthesis_location
183
fi,
184
"syntax error: incomplete formal arguments"
185
);
186
187
return (arguments, function);
188
fi
189
fi
190
fi
191
192
arguments_tokenizer_snapshot.commit();
193
arguments_logger_snaphot.commit();
194
195
// TODO: could recognize that no closing parenthesis followed by a line then
196
// an identifier and an opening paren is probably another function signature
197
198
if !is_poisoned \/ context.current.token == Lexical.TOKEN.PAREN_CLOSE then
199
is_poisoned = !context.next_token(Lexical.TOKEN.PAREN_CLOSE) \/ is_poisoned;
200
fi
201
202
arguments.poison(is_poisoned);
203
204
return (arguments, function);
205
si
206
207
parse_return_type(
208
context: CONTEXT,
209
start: Source.LOCATION,
210
name: Syntax.Trees.Identifiers.Identifier?,
211
generic_arguments: Syntax.Trees.TypeExpressions.LIST,
212
arguments: Trees.Variables.LIST
213
) ->
214
(type_expression: Trees.TypeExpressions.TypeExpression, function: Trees.Definitions.FUNCTION?)
215
is
216
let is_poisoned mut = false;
217
let type_expression: Trees.TypeExpressions.TypeExpression mut;
218
let function: Trees.Definitions.FUNCTION? mut = null;
219
220
if context.current.token == Lexical.TOKEN.ARROW_THIN then
221
let arrow_location = context.location;
222
context.next_token();
223
224
let use return_type_tokenizer_snapshot = context.tokenizer_speculate_then_commit();
225
226
type_expression = type_parser.parse(context)!;
227
type_expression.check_is_not_reference(context.logger, "function cannot return a reference");
228
229
is_poisoned = is_poisoned \/ type_expression.is_poisoned;
230
231
if context.current_token == Lexical.TOKEN.PAREN_OPEN then
232
return_type_tokenizer_snapshot.backtrack();
233
234
function =
235
Trees.Definitions.FUNCTION(
236
start::name!.location,
237
name,
238
generic_arguments,
239
arguments,
240
Trees.TypeExpressions.INFER(context.location),
241
Trees.Modifiers.LIST(context.location, null, null),
242
Trees.Bodies.NULL(context.location)
243
);
244
245
context.logger.error(arrow_location, "syntax error: incomplete return type");
246
247
return (type_expression, function);
248
fi
249
250
return_type_tokenizer_snapshot.commit();
251
else
252
type_expression = Trees.TypeExpressions.INFER(context.location);
253
fi
254
255
return (type_expression, function);
256
si
257
258
parse(context: CONTEXT) -> Trees.Definitions.FUNCTION is
259
if context.in_classy then
260
context.in_member = true;
261
context.member_indent = context.location.start_column
262
else
263
context.in_global_function = true;
264
context.global_indent = context.location.start_column;
265
fi
266
267
try
268
let is_poisoned mut = false;
269
let start = context.location;
270
let name = identifier_function_name_parser.parse(context);
271
272
let generic_arguments = parse_generic_arguments(context);
273
274
is_poisoned = generic_arguments.is_poisoned;
275
276
let af = parse_formal_arguments(context, start, name, generic_arguments);
277
278
if af.function? then
279
return af.function!;
280
fi
281
282
let arguments = af.arguments;
283
284
is_poisoned = is_poisoned \/ arguments.is_poisoned;
285
286
let rt = parse_return_type(context, start, name, generic_arguments, arguments);
287
288
if rt.function? then
289
return rt.function!;
290
fi
291
292
let type_expression = rt.type_expression;
293
294
is_poisoned = is_poisoned \/ type_expression.is_poisoned;
295
296
let modifiers = modifier_list_parser.parse(context)!;
297
let expect_semicolon = context.current.token != Lexical.TOKEN.IS;
298
299
let body: Trees.Bodies.Body mut = Trees.Bodies.NULL(context.location);
300
301
try
302
body = body_parser.parse(context)!
303
catch ue: UNWIND_TO_MEMBER_EXCEPTION
304
body = Trees.Bodies.NULL(context.location);
305
yrt
306
307
let result =
308
Trees.Definitions.FUNCTION(
309
start::body.location,
310
name,
311
generic_arguments,
312
arguments,
313
type_expression,
314
modifiers,
315
body
316
);
317
318
if expect_semicolon then
319
if !is_poisoned \/ context.current.token == Lexical.TOKEN.SEMICOLON then
320
is_poisoned = !context.next_token(Lexical.TOKEN.SEMICOLON) \/ is_poisoned;
321
fi
322
fi
323
324
result.poison(is_poisoned);
325
326
return result;
327
328
finally
329
if context.in_classy then
330
context.in_member = false;
331
else
332
context.in_global_function = false;
333
fi
334
yrt
335
si
336
si
337
si