Skip to content
← Back

src/syntax/trees/expressions/call.ghul

1
namespace Syntax.Trees.Expressions is
2
3
use Source;
4
use Logging;
5
6
use Ghul.Pipes;
7
8
class CALL(location: LOCATION, function: Expression, arguments: LIST): Expression is
9
right_location: LOCATION => function.right_location;
10
could_be_nested_function_definition: bool => function.is_unqualified_identifier;
11
12
// Constraint pushed in by the parent context. For a constructor
13
// call (e.g. `Box()` against `let b: Box[int]`) the visitor uses
14
// it to bind the owner generic arguments when they can't be
15
// inferred from the supplied constructor arguments alone.
16
17
// Parallel to `arguments` when the call uses named-argument
18
// syntax (`f(width = 800, height = 600)`); null for a
19
// positional call. Set by `rewrite_named_arguments`, which
20
// also unwraps each argument to its value expression so the
21
// rest of the pipeline sees an ordinary positional list.
22
argument_names: Collections.List[Identifiers.Identifier]?;
23
24
// True for the `Ghul.Pipes.pipe(x)` call synthesised by the
25
// postfix `|` operator. When the operand is already a Pipe[T],
26
// compile-expressions short-circuits the wrap to the operand —
27
// `x |` is then a no-op (a compile-time decision on x's type).
28
is_pipe_wrap: bool public;
29
30
// True for a `|>` thread-first call. The subject has been inserted
31
// as the first element of `arguments`, so the call resolves and
32
// emits like any other; the flag only tells the formatter to
33
// render `arguments[0] |> function(rest)` and lets the constructor
34
// and named-argument checks reject the forms `|>` disallows.
35
is_thread_first: bool public;
36
37
super(location);
38
39
set_expected_type(expected_type: Semantic.Types.Type?, error_message: string?) is
40
compile_expressions_state.set_expected_type(expected_type, error_message);
41
si
42
43
// `f(a = 1, b = 2)` parses with each argument as a `VARIABLE`:
44
// the shared list parser treats `name = value` like a
45
// tuple-element precursor. Detect that shape, record the
46
// names, and unwrap each argument to its value expression so
47
// every later pass sees an ordinary positional list. A call
48
// must be all-named or all-positional.
49
rewrite_named_arguments(logger: Logger) is
50
let elements = arguments.expressions;
51
52
if elements.count == 0 then
53
return;
54
fi
55
56
let named_count = elements |> filter(e => _is_named_argument(e)) |> count();
57
58
if named_count == 0 then
59
return;
60
fi
61
62
if named_count < elements.count then
63
logger.error(location, "cannot mix named and positional arguments");
64
65
for index in 0..elements.count do
66
if _is_named_argument(elements[index]) then
67
elements[index] = (cast VARIABLE?(elements[index])!).initializer!;
68
fi
69
od
70
71
return;
72
fi
73
74
let names = Collections.LIST[Identifiers.Identifier]();
75
76
for index in 0..elements.count do
77
let variable = cast VARIABLE?(elements[index])!;
78
79
if names |> any(seen => seen.name =~ variable.name.name) then
80
logger.error(variable.name.location, "named argument {variable.name.name} supplied more than once");
81
fi
82
83
names.add(variable.name);
84
elements[index] = variable.initializer!;
85
od
86
87
argument_names = names;
88
si
89
90
// A named argument is `name = value`: a VARIABLE node carrying
91
// an initializer. A bare `name: type` (a VARIABLE with no
92
// initializer) is not a named argument - it only arises from
93
// parser error recovery, and is left for the normal error
94
// paths rather than reported here.
95
_is_named_argument(e: Expression) -> bool =>
96
isa VARIABLE(e) /\ (cast VARIABLE(e)).initializer?;
97
98
accept(visitor: Visitor) is
99
visitor.visit(self);
100
si
101
102
walk(visitor: Visitor) is
103
// enter/leave_node wrap the whole visit so the ambient
104
// location covers Values built by `pre()` overrides that
105
// take control of the walk — `pre(CALL)` returns true and
106
// `visit_call` does its own resolver-driven walk, skipping
107
// the default recursion below.
108
visitor.enter_node(self);
109
try
110
if !visitor.pre(self) then
111
arguments.walk(visitor);
112
function.walk(visitor);
113
fi
114
115
accept(visitor);
116
finally
117
visitor.leave_node(self);
118
yrt
119
si
120
121
replace_child(old: Expression, replacement: Expression) is
122
if function == old then
123
function = replacement;
124
fi
125
// Argument elements are replaced via arguments.replace_element
126
// by callers that have the index — no parent-driven lookup
127
// here (it would require a linear scan; the SPILL_AWAITS
128
// pass already has the index).
129
si
130
si
131
si