Skip to content
← Back

src/syntax/parsers/expressions/secondary.ghul

1
namespace Syntax.Parsers.Expressions is
2
use IO.Std
3
use Ghul.Disposable
4
5
use Source
6
use Logging
7
8
class SECONDARY(
9
identifier_parser: Parser[Trees.Identifiers.Identifier],
10
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
11
type_list_parser: Parser[Trees.TypeExpressions.LIST],
12
expression_parser: Parser[Trees.Expressions.Expression],
13
expression_primary_parser: Parser[Trees.Expressions.Expression],
14
expression_list_parser: Parser[Trees.Expressions.LIST],
15
body_parser: Parser[Trees.Bodies.Body]
16
): Base[Trees.Expressions.Expression] is
17
description: string => "secondary expression"
18
19
super()
20
21
// A token sits in identifier position after a `.` if it
22
// looks like a word (its `value_string` opens with a letter
23
// or underscore) and it shares a line with the dot. The
24
// line guard matters: a `.` at the end of a line followed
25
// by a structural keyword on the next (`namespace Foo.\n is`)
26
// must not eat that keyword.
27
looks_like_partial_member_keyword(context: CONTEXT, dot_location: LOCATION) -> bool is
28
if context.current.token == Lexical.TOKEN.IDENTIFIER then
29
return false
30
fi
31
32
let s = context.current.value_string
33
34
// value_string is null for a value-less token
35
@suppress("presence-test-non-optional")
36
if !s? \/ s.length == 0 then
37
return false
38
fi
39
40
let first = s[0]
41
42
if !((first >= 'a' /\ first <= 'z') \/ (first >= 'A' /\ first <= 'Z') \/ first == '_') then
43
return false
44
fi
45
46
return context.current.location.start_line == dot_location.start_line
47
si
48
49
// An operator token sits in member-name position after a `.`
50
// when whitespace separates the two: the tokenizer scans a run
51
// of operator characters greedily and `.` is one of them, so
52
// `a.=~(b)` lexes as the single operator `.=~` and never
53
// reaches here — only `a. =~(b)` does. The backtick escape
54
// (`a.`=~(b)`) arrives as an ordinary IDENTIFIER and takes the
55
// branch above.
56
//
57
// The same line guard as the keyword case, for the same
58
// reason: a `.` ending a line must not swallow an operator
59
// leading the next one.
60
looks_like_operator_member_name(context: CONTEXT, dot_location: LOCATION) -> bool is
61
if context.current.token != Lexical.TOKEN.OPERATOR then
62
return false
63
fi
64
65
let s = context.current.value_string
66
67
// value_string is null for a value-less token
68
@suppress("presence-test-non-optional")
69
if !s? \/ s.length == 0 then
70
return false
71
fi
72
73
return context.current.location.start_line == dot_location.start_line
74
si
75
76
// What may continue a `|>` onto the next line: a plain name, or a
77
// function named with operator characters.
78
_can_lead_thread_first_right_side(context: CONTEXT) -> bool static =>
79
context.current.token == Lexical.TOKEN.IDENTIFIER \/
80
context.current.token == Lexical.TOKEN.OPERATOR
81
82
// The stand-in for a `|>` right side that has not been typed
83
// yet. An empty, poisoned name resolves to nothing and reports
84
// nothing, the same recovery a bare `.` at the end of a line
85
// gets.
86
_missing_thread_first_function(location: LOCATION) -> Trees.Expressions.Expression static is
87
let name = Trees.Expressions.IDENTIFIER(location, Trees.Identifiers.Identifier(location, ""))
88
89
name.poison()
90
91
return name
92
si
93
94
// Build the thread-first call for a right side that is not a call:
95
// either nothing at all, or a name with no argument list. The
96
// subject is threaded in as it is for a complete call, so the
97
// node the completer meets has the same shape whether or not the
98
// user has finished typing.
99
_incomplete_thread_first(
100
start: LOCATION,
101
subject: Trees.Expressions.Expression,
102
completion_target: LOCATION,
103
function: Trees.Expressions.Expression
104
) -> Trees.Expressions.CALL static is
105
let call =
106
Trees.Expressions.CALL(
107
start::function.location,
108
function,
109
Trees.Expressions.LIST(subject.location, [subject])
110
)
111
112
call.is_thread_first = true
113
call.completion_target = completion_target
114
call.poison()
115
116
return call
117
si
118
119
parse(context: CONTEXT) -> Trees.Expressions.Expression is
120
let use tokenizer_state = context.tokenizer_speculate_then_commit()
121
122
let start = context.location
123
let result = expression_primary_parser.parse(context)!
124
125
return _parse_trailers(context, start, result, tokenizer_state)
126
si
127
128
// The right side of `|>` or `~>`, with `subject` threaded in as
129
// the first argument of its call. Reached from the expression
130
// parser, where both operators are a binary precedence level: the
131
// subject is whatever that level gathered on the left, and the
132
// call is a closed form, so an operator after it applies to the
133
// whole chain.
134
parse_thread_first(
135
context: CONTEXT,
136
subject: Trees.Expressions.Expression,
137
propagating: bool,
138
spelling: string
139
) -> Trees.Expressions.Expression is
140
let use tokenizer_state = context.tokenizer_speculate_then_commit()
141
142
return _parse_thread_first(context, subject.location, subject, tokenizer_state, propagating, spelling)
143
si
144
145
// Parse the right side as a function call with the subject
146
// threaded in as its first argument, so the call resolves and
147
// emits like any other, then apply any trailers that follow the
148
// call to its result. `propagating` marks a `~>` call, whose
149
// evaluation the expression passes lower inside a presence test
150
// on the subject.
151
_parse_thread_first(
152
context: CONTEXT,
153
start: Source.LOCATION,
154
subject: Trees.Expressions.Expression,
155
tokenizer_state: Syntax.Parsers.TOKEN_LOOKAHEAD_SPECULATE_THEN_COMMIT,
156
propagating: bool,
157
spelling: string
158
) -> Trees.Expressions.Expression is
159
let operator_location = context.location
160
161
context.next_token()
162
163
let pipe_start = context.location
164
165
// A thread-first operator at the end of a line usually
166
// carries its call over to the next one, which is how a
167
// wrapped chain is written. What it must not do is reach
168
// across the boundary for something that was never a call:
169
// an unfinished `xs |>` is followed by the next statement,
170
// and reading that as the right side loses it and reports
171
// the loss somewhere else entirely. Only a name continues
172
// the chain, so anything else on a fresh line means the
173
// call has not been typed yet - hand back the placeholder
174
// the completer anchors on.
175
if context.current.first_on_line /\ !_can_lead_thread_first_right_side(context) then
176
context.error(operator_location, "expected a function call after {spelling}")
177
178
return _incomplete_thread_first(
179
start,
180
subject,
181
operator_location::pipe_start,
182
_missing_thread_first_function(operator_location)
183
)
184
fi
185
186
// A function named with an operator is not a primary
187
// expression, so `xs |> $` needs its own step here.
188
// With no argument list after the name the threaded
189
// value is the only argument, which is the point of
190
// the spelling; an argument list is still accepted
191
// and threads the value in ahead of it as usual.
192
let rhs: Trees.Expressions.Expression mut
193
194
if context.current.token == Lexical.TOKEN.OPERATOR then
195
let name_location = context.location
196
197
let name =
198
Trees.Expressions.IDENTIFIER(
199
name_location,
200
Trees.Identifiers.Identifier(name_location, context.current.value_string)
201
)
202
203
context.next_token()
204
205
if context.current.token == Lexical.TOKEN.PAREN_OPEN then
206
rhs = _parse_trailers(context, pipe_start, name, tokenizer_state)
207
else
208
rhs =
209
_parse_trailers(
210
context,
211
pipe_start,
212
Trees.Expressions.CALL(
213
name_location,
214
name,
215
Trees.Expressions.LIST(
216
name_location,
217
Collections.LIST[Trees.Expressions.Expression]()
218
)
219
),
220
tokenizer_state
221
)
222
fi
223
else
224
rhs =
225
_parse_trailers(context, pipe_start, expression_primary_parser.parse(context)!, tokenizer_state)
226
fi
227
228
let rhs_call = _call_to_thread_into(rhs)
229
230
if rhs_call? then
231
// Thread the subject in as the first actual argument
232
// and flag the call, so every later pass sees an
233
// ordinary call.
234
rhs_call.arguments.expressions.insert(0, subject)
235
rhs_call.is_thread_first = true
236
rhs_call.propagates_absence = propagating
237
rhs_call.completion_target = operator_location::rhs_call.function.location
238
239
return rhs
240
fi
241
242
// A name with no argument list is what half-typed code
243
// looks like, so keep the thread-first shape for the
244
// completer instead of handing back the bare right side.
245
// Poisoning the name is what stops the placeholder call
246
// resolving and cascading.
247
context.error(rhs.location, "the right side of {spelling} must be a function call")
248
rhs.poison()
249
250
return _incomplete_thread_first(start, subject, operator_location::rhs.location, rhs)
251
si
252
253
// The call a `|>` right side threads its subject into: the
254
// outermost one, reached through any trailers written after it.
255
// `xs |> f().count` threads into `f()` and reads `count` from the
256
// result, while `xs |> BOX(10).combine(3)` threads into `combine`
257
// with `BOX(10)` as its receiver. Absent when there is no call.
258
_call_to_thread_into(rhs: Trees.Expressions.Expression) -> Trees.Expressions.CALL? static is
259
let current: Trees.Expressions.Expression mut = rhs
260
261
do
262
if let call: Trees.Expressions.CALL = current then
263
return call
264
elif let member: Trees.Expressions.MEMBER = current then
265
current = member.left
266
elif let index: Trees.Expressions.INDEX = current then
267
current = index.left
268
elif let unwrap: Trees.Expressions.UNWRAP = current then
269
current = unwrap.left
270
elif let has_value: Trees.Expressions.HAS_VALUE = current then
271
current = has_value.left
272
else
273
return null
274
fi
275
od
276
si
277
278
// Consume the postfix trailers (`.member`, `(args)`, `[index]`, …)
279
// that follow a primary.
280
_parse_trailers(
281
context: CONTEXT,
282
start: LOCATION,
283
result_in: Trees.Expressions.Expression,
284
tokenizer_state: Syntax.Parsers.TOKEN_LOOKAHEAD_SPECULATE_THEN_COMMIT
285
) -> Trees.Expressions.Expression is
286
let result: Trees.Expressions.Expression mut = result_in
287
let previous_left: Trees.Expressions.Expression? mut = _
288
let previous_identifier: Trees.Identifiers.Identifier? mut = _
289
290
do
291
// A trailer that could equally open a new member or
292
// statement never glues across a line break: a line-start
293
// `(`, `[`, backtick generic-argument list or operator ends
294
// the expression. A line-start `!` is a prefix `not`
295
// opening the next statement, a line-start `=~` the next
296
// operator member, a line-start `rec` the self-reference
297
// call of an enclosing anonymous function rather than a
298
// rec-lambda marker for this expression. Trailers that lead
299
// continuation lines by convention (`.`, `?`, `ref`) still
300
// continue.
301
if
302
context.current.first_on_line /\
303
(
304
context.current.token == Lexical.TOKEN.PAREN_OPEN \/
305
context.current.token == Lexical.TOKEN.SQUARE_OPEN \/
306
context.current.token == Lexical.TOKEN.SQUARE_OPEN_TICK \/
307
context.current.token == Lexical.TOKEN.REC \/
308
context.current.token == Lexical.TOKEN.OPERATOR
309
)
310
then
311
return result
312
fi
313
314
case context.current.token
315
when Lexical.TOKEN.PAREN_OPEN then
316
context.next_token()
317
let arguments: Trees.Expressions.LIST mut
318
if context.current.token != Lexical.TOKEN.PAREN_CLOSE then
319
arguments = expression_list_parser.parse(context)!
320
else
321
arguments = Trees.Expressions.LIST(context.location, Collections.LIST[Trees.Expressions.Expression]())
322
fi
323
let call_expression = Trees.Expressions.CALL(start::context.location, result, arguments)
324
call_expression.rewrite_named_arguments(context.logger)
325
result = call_expression
326
context.next_token(Lexical.TOKEN.PAREN_CLOSE, syntax_error_message)
327
328
when Lexical.TOKEN.SQUARE_OPEN then
329
context.next_token()
330
331
let done mut = false
332
333
let left: Trees.Expressions.Expression? mut = _
334
let identifier: Trees.Identifiers.Identifier? mut = _
335
336
if result.is_member then
337
left = previous_left
338
identifier = previous_identifier
339
340
elif result.is_identifier then
341
left = null
342
identifier = result.try_copy_as_identifer()
343
fi
344
345
if identifier? then
346
let use tokenizer_state = context.tokenizer_speculate_then_commit()
347
let use logger_state = context.logger_speculate_then_commit()
348
349
let type_arguments = type_list_parser.parse(context)
350
351
if type_arguments? /\ !type_arguments.is_poisoned /\ context.next_token(Lexical.TOKEN.SQUARE_CLOSE) then
352
let index_expression: Trees.Expressions.Expression? mut = _
353
354
if type_arguments.count == 1 then
355
let index = type_arguments.elements[0].try_copy_as_value_expression()
356
if index? then
357
index_expression = Trees.Expressions.INDEX(start::context.location, result, index)
358
fi
359
fi
360
361
done = true
362
363
if index_expression? then
364
result = Trees.Expressions.AMBIGUOUS_EXPRESSION(
365
start::context.location,
366
index_expression,
367
left,
368
identifier,
369
type_arguments
370
)
371
else
372
result = Trees.Expressions.GENERIC_APPLICATION(
373
start::context.location,
374
left,
375
identifier,
376
type_arguments
377
)
378
fi
379
380
tokenizer_state.commit()
381
logger_state.commit()
382
383
// FIXME: continue generates incorrect code here
384
// continue;
385
fi
386
387
tokenizer_state.backtrack_if_speculating()
388
logger_state.backtrack_if_speculating()
389
fi
390
391
if !done then
392
let index = expression_parser.parse(context)!
393
394
result = Trees.Expressions.INDEX(start::context.location, result, index)
395
396
context.next_token(Lexical.TOKEN.SQUARE_CLOSE, syntax_error_message)
397
fi
398
399
when Lexical.TOKEN.DOT then
400
let completion_target_start = context.location
401
402
context.next_token()
403
404
if
405
context.current.token == Lexical.TOKEN.IDENTIFIER
406
then
407
let member = identifier_parser.parse(context)!
408
409
previous_left = result
410
previous_identifier = member
411
412
result = Trees.Expressions.MEMBER(start::member.location, result, member, completion_target_start::member.location)
413
elif looks_like_partial_member_keyword(context, completion_target_start) then
414
// Mid-typing case like `value.is` (en route to
415
// `value.is_empty`). The token is a reserved
416
// word the tokenizer has already classified,
417
// but it sits in identifier position after a
418
// `.`; treat it as a partial member name so
419
// the completer has a MEMBER node with a
420
// populated `right` to anchor suggestions on.
421
// The same-line guard prevents this consuming
422
// a structural keyword on the next line
423
// (`namespace Test.\n is`).
424
let name = context.current.value_string
425
let member = Trees.Identifiers.Identifier(context.location, name)
426
context.next_token()
427
428
previous_left = result
429
previous_identifier = member
430
431
result = Trees.Expressions.MEMBER(start::member.location, result, member, completion_target_start::member.location)
432
elif looks_like_operator_member_name(context, completion_target_start) then
433
// `a. =~(b)` — an operator member named after a
434
// dot. Same node as any other member access;
435
// the operator's name is just spelled in
436
// operator characters.
437
let name = context.current.value_string
438
let member = Trees.Identifiers.Identifier(context.location, name)
439
context.next_token()
440
441
previous_left = result
442
previous_identifier = member
443
444
result = Trees.Expressions.MEMBER(start::member.location, result, member, completion_target_start::member.location)
445
elif
446
context.current.token == Lexical.TOKEN.INT_LITERAL \/
447
context.current.token == Lexical.TOKEN.FLOAT_LITERAL \/
448
context.current.token == Lexical.TOKEN.DOUBLE_LITERAL
449
then
450
// `.` followed by a numeric literal, e.g. `p.0`.
451
// Positional tuple members are named by their
452
// index, but a bare number is not an identifier, so
453
// the index must be backtick-escaped (`p.` followed
454
// by a backtick and the number). Point at that, then
455
// consume the number so it doesn't linger and
456
// cascade into a spurious "expected ;" on the rest of
457
// the statement. Recover with a poisoned placeholder
458
// identifier so later passes that assume
459
// MEMBER.identifier is non-null don't crash.
460
context.error(context.location, "a numeric member name must be escaped with a leading backtick")
461
let member_location = context.location
462
context.next_token()
463
result = Trees.Expressions.MEMBER(start::member_location, result, Trees.Identifiers.Identifier(completion_target_start, ""), completion_target_start::member_location)
464
else
465
// `.` not followed by a member name: expect_token
466
// reports it. Recover with a poisoned placeholder
467
// identifier rather than null — later passes assume
468
// MEMBER.identifier is non-null and would otherwise
469
// crash dereferencing it. The offending token is left
470
// for the enclosing context to resynchronise on.
471
context.expect_token(Lexical.TOKEN.IDENTIFIER)
472
result = Trees.Expressions.MEMBER(start..context.location, result, Trees.Identifiers.Identifier(completion_target_start, ""), completion_target_start::context.location)
473
fi
474
475
when Lexical.TOKEN.ARROW_THIN, Lexical.TOKEN.ARROW_FAT, Lexical.TOKEN.IS, Lexical.TOKEN.REC then
476
// we could be in a global function or in a method. If the left part looks like it could be a function or method definition
477
// then it's an error and we need to recover.
478
479
// we'll need a heuristic to try to decide if the user is trying to define a nested function or if they've missed off a closing `si`
480
// and actually this is a method definition (if we're in a classy context) or a global function definition (if we're not in a classy context)
481
482
let is_nested_function_definition mut = false
483
484
if result.could_be_nested_function_definition /\ tokenizer_state.is_speculating then
485
// TODO need same logic but for nested within a global function
486
if context.in_member then
487
if result.location.start_column > context.member_indent then
488
// probably an attempt at nesting a named function definition
489
is_nested_function_definition = true
490
elif
491
result.location.start_column <= context.member_indent /\
492
result.location.start_column > context.global_indent
493
then
494
// probably a missing `si` in a preceding member definition
495
context.error(result.location, "expected 'si' after member definition")
496
497
tokenizer_state.backtrack()
498
499
throw UNWIND_TO_MEMBER_EXCEPTION(null)
500
fi
501
elif context.in_global_function then
502
if result.location.start_column > context.global_indent then
503
// probably an attempt at nesting a named function definition
504
is_nested_function_definition = true
505
else
506
// probably a missing `si` in a preceding member definition
507
context.error(result.location, "expected 'si' after member definition")
508
509
tokenizer_state.backtrack()
510
511
throw UNWIND_TO_GLOBAL_EXCEPTION(null)
512
fi
513
elif context.in_classy then
514
// not clear how we might have ended up here
515
tokenizer_state.backtrack()
516
517
throw UNWIND_TO_GLOBAL_EXCEPTION(null)
518
fi
519
fi
520
521
// it's unlikely to be member or global function definition, so we should
522
// parse it as a function literal, even if it has a name or is otherwise garbled.
523
// In particular, if has a block body delimited with with is / si, then we want to
524
// consume that block, otherwise our view of the block structure will get out of sync
525
// and we'll think we've let the enclosing function or method when we reach the closing
526
// `si` of the function literal
527
528
// A named function among the statements of a body. The
529
// statement parser has already established that the shape
530
// can only be one, so the name and the parenthesised
531
// formals are taken from the call and the literal is built
532
// from them. Everywhere else the same shape is still the
533
// mistake it always was.
534
let nested_name: Trees.Identifiers.Identifier? mut = _
535
let nested_call: Trees.Expressions.CALL? mut = _
536
let should_poison mut = false
537
538
if result.could_be_nested_function_definition then
539
let claimed mut = false
540
541
if context.allow_nested_function then
542
if let call: Trees.Expressions.CALL = result then
543
if let callee: Trees.Expressions.IDENTIFIER = call.function then
544
context.allow_nested_function = false
545
546
nested_call = call
547
nested_name = callee.identifier
548
claimed = true
549
fi
550
fi
551
fi
552
553
if !claimed then
554
should_poison = true
555
context.error(result.location, "nested function definition")
556
fi
557
elif !result.could_be_formal_argument then
558
should_poison = true
559
context.error(result.location, "expected function literal formal arguments")
560
fi
561
562
let type_expression: Trees.TypeExpressions.TypeExpression mut
563
let arguments: Trees.Expressions.LIST mut
564
565
if context.current.token == Lexical.TOKEN.ARROW_THIN then
566
context.next_token()
567
type_expression = type_parser.parse(context)!
568
type_expression.check_is_not_reference(context.logger, "function cannot return a reference")
569
else
570
type_expression = Trees.TypeExpressions.INFER(start::result.location)
571
fi
572
573
if context.expect_token([Lexical.TOKEN.ARROW_FAT, Lexical.TOKEN.IS, Lexical.TOKEN.REC]) then
574
if should_poison then
575
// error already reported
576
arguments = Trees.Expressions.LIST(start::result.location, Collections.LIST[Trees.Expressions.Expression]())
577
elif nested_name? then
578
arguments = nested_call!.arguments
579
arguments.rewrite_as_variables()
580
elif isa Trees.Expressions.TUPLE(result) then
581
arguments = result.elements
582
arguments.rewrite_as_variables()
583
else
584
let elements = Collections.LIST[Trees.Expressions.Expression]()
585
elements.add(result)
586
arguments = Trees.Expressions.LIST(start::result.location, elements)
587
fi
588
589
let is_recursive =
590
if context.current.token == Lexical.TOKEN.REC then
591
context.next_token()
592
true
593
else
594
false
595
fi
596
597
// A lambda body is a fresh statement context. The
598
// enclosing argument list leaves allow_tuple_element
599
// set while an argument expression parses, so without
600
// this reset the body's leading `x = e` statement is
601
// consumed as a formal-argument-style variable
602
// declaration rather than an assignment. A body
603
// written inside a top-level statement is an ordinary
604
// body too, and takes named functions like any other.
605
context.allow_tuple_element = false
606
607
let was_top_level = context.in_top_level_statements
608
609
context.in_top_level_statements = false
610
611
let body: Trees.Bodies.Body mut = _
612
613
try
614
body = body_parser.parse(context)!
615
finally
616
context.in_top_level_statements = was_top_level
617
yrt
618
619
let literal =
620
Trees.Expressions.FUNCTION(
621
start::body.location,
622
arguments,
623
type_expression,
624
body,
625
is_recursive
626
)
627
628
if let name = nested_name then
629
literal.set_nested_name(name)
630
fi
631
632
result = literal
633
634
if should_poison then
635
result.poison()
636
fi
637
638
return result
639
else
640
// TODO: is this possible?
641
return Trees.Expressions.Literals.NONE(start::context.location)
642
fi
643
644
when Lexical.TOKEN.QUESTION then
645
let question_location = context.location
646
tokenizer_state.commit_if_speculating()
647
648
context.next_token()
649
650
if context.current.token == Lexical.TOKEN.DOT then
651
// `?.` coalescing member access. Tokenizer
652
// breaks `?.` into two adjacent tokens (per
653
// the `?`/`!` followed-by-`.` rule in
654
// read_operator), so we recognise the
655
// sequence here.
656
let completion_target_start = question_location
657
context.next_token()
658
659
if context.current.token == Lexical.TOKEN.IDENTIFIER then
660
let member = identifier_parser.parse(context)!
661
662
previous_left = result
663
previous_identifier = member
664
665
let member_node =
666
Trees.Expressions.MEMBER(
667
start::member.location,
668
result,
669
member,
670
completion_target_start::member.location
671
)
672
673
member_node.is_coalesce = true
674
result = member_node
675
else
676
// `?.` not followed by a member name: recover
677
// with a poisoned placeholder identifier, not
678
// null, so passes assuming MEMBER.identifier is
679
// non-null don't crash.
680
context.expect_token(Lexical.TOKEN.IDENTIFIER)
681
682
let member_node =
683
Trees.Expressions.MEMBER(
684
start..context.location,
685
result,
686
Trees.Identifiers.Identifier(completion_target_start, ""),
687
completion_target_start::context.location
688
)
689
690
member_node.is_coalesce = true
691
result = member_node
692
fi
693
else
694
result = Trees.Expressions.HAS_VALUE(start::question_location, result)
695
fi
696
697
when Lexical.TOKEN.REF then
698
result = Trees.Expressions.REFERENCE(start::context.location, result)
699
tokenizer_state.commit_if_speculating()
700
701
context.next_token()
702
703
when Lexical.TOKEN.OPERATOR then
704
tokenizer_state.commit_if_speculating()
705
706
if context.current_string =~ "!" then
707
result = Trees.Expressions.UNWRAP(start::context.location, result)
708
709
context.next_token()
710
else
711
return result
712
fi
713
714
when Lexical.TOKEN.SQUARE_OPEN_TICK then
715
tokenizer_state.commit_if_speculating()
716
717
context.next_token()
718
719
let type_arguments = type_list_parser.parse(context)!
720
721
let left: Trees.Expressions.Expression? mut = _
722
let identifier: Trees.Identifiers.Identifier? mut = _
723
724
if result.is_member then
725
left = previous_left
726
identifier = previous_identifier
727
728
elif result.is_identifier then
729
left = null
730
identifier = result.try_copy_as_identifer()
731
fi
732
733
if identifier? then
734
result = Trees.Expressions.GENERIC_APPLICATION(
735
start::context.location,
736
left,
737
identifier,
738
type_arguments
739
)
740
else
741
result.poison()
742
context.error(result.location, "cannot apply type arguments to this")
743
fi
744
745
context.next_token(Lexical.TOKEN.SQUARE_CLOSE)
746
747
else
748
return result
749
esac
750
od
751
si
752
si
753
si