Skip to content
← Back

src/syntax/process/named_argument_binder.ghul

1
namespace Syntax.Process is
2
use Source;
3
use Logging;
4
5
use Symbols = Semantic.Symbols;
6
7
use Ghul.Pipes;
8
9
// The overloads a named-argument call resolved to, together with
10
// the permutation that reorders the supplied arguments into
11
// parameter order: `permutation[formal_index]` is the source
12
// index of the supplied argument that fills that parameter.
13
// More than one overload can survive name matching (overloads
14
// that differ only in parameter types); the type-driven overload
15
// resolver disambiguates between them.
16
class NAMED_ARGUMENT_BINDING is
17
targets: Collections.List[Symbols.Function];
18
permutation: Collections.List[int];
19
20
init(targets: Collections.List[Symbols.Function], permutation: Collections.List[int]) is
21
super.init();
22
23
self.targets = targets;
24
self.permutation = permutation;
25
si
26
si
27
28
// Resolves a named-argument call (`f(width = 800, height = 600)`)
29
// against a function group: culls to the overloads whose formal
30
// parameter names match the supplied names and computes the
31
// permutation into parameter order. Name matching needs only the
32
// formal names, never the argument types, so it runs ahead of the
33
// type-driven overload resolver, which is then restricted to the
34
// surviving overloads.
35
class NAMED_ARGUMENT_BINDER is
36
_logger: Logger;
37
38
init(logger: Logger) is
39
super.init();
40
41
_logger = logger;
42
si
43
44
bind(
45
location: LOCATION,
46
group: Symbols.FUNCTION_GROUP,
47
argument_names: Collections.List[Trees.Identifiers.Identifier],
48
want_instance: bool
49
) -> NAMED_ARGUMENT_BINDING? is
50
// An overload survives name-culling when every supplied
51
// name is one of its parameters. `exact` overloads have no
52
// other parameters; `omitting` ones have parameters left
53
// unsupplied (filled with `default` per the call site).
54
let exact = Collections.LIST[Symbols.Function]();
55
let omitting = Collections.LIST[Symbols.Function]();
56
57
for f in group.functions do
58
if (want_instance \/ !f.is_instance) /\ _covers_all_names(f, argument_names) then
59
if f.argument_names.count == argument_names.count then
60
exact.add(f);
61
elif _omitted_have_defaults(f, argument_names) then
62
omitting.add(f);
63
fi
64
fi
65
od
66
67
let supplied = argument_names |> map(n => n.name) |> join();
68
69
// An exact overload always beats one that needs defaults.
70
if exact.count > 0 then
71
let permutation = _permutation_for(exact[0], argument_names);
72
73
if permutation |> any(source_index => source_index < 0) then
74
_logger.error(location, "call to {group.name} with named arguments {supplied} cannot be bound");
75
76
return null;
77
fi
78
79
// Overloads that share a name set but order it
80
// differently (`f(a, b)` and `f(b, a)`) would bind the
81
// same call two ways - genuinely ambiguous.
82
for f in exact do
83
if !_same_permutation(permutation, _permutation_for(f, argument_names)) then
84
_logger.error(location, "call to {group.name} with named arguments {supplied} is ambiguous");
85
86
return null;
87
fi
88
od
89
90
return NAMED_ARGUMENT_BINDING(exact, permutation);
91
fi
92
93
if omitting.count == 0 then
94
_logger.error(location, "no overload of {group.name} takes named arguments {supplied}");
95
96
return null;
97
fi
98
99
// More than one overload would need defaulting - which
100
// parameters are omitted is genuinely ambiguous.
101
if omitting.count > 1 then
102
_logger.error(location, "call to {group.name} with named arguments {supplied} is ambiguous");
103
104
return null;
105
fi
106
107
return NAMED_ARGUMENT_BINDING(omitting, _permutation_for(omitting[0], argument_names));
108
si
109
110
// True when every supplied name is a parameter of `f`. `f` may
111
// still have further parameters the call leaves unsupplied.
112
_covers_all_names(
113
f: Symbols.Function,
114
argument_names: Collections.List[Trees.Identifiers.Identifier]
115
) -> bool is
116
if !f.are_arguments_declared then
117
return false;
118
fi
119
120
for supplied in argument_names do
121
if !(f.argument_names |> any(formal => formal =~ supplied.name)) then
122
return false;
123
fi
124
od
125
126
return true;
127
si
128
129
// True when every parameter of `f` that the call does not
130
// supply by name declares a default value, so the call may
131
// legitimately omit it.
132
_omitted_have_defaults(
133
f: Symbols.Function,
134
argument_names: Collections.List[Trees.Identifiers.Identifier]
135
) -> bool is
136
for formal_index in 0..f.argument_names.count do
137
let formal = f.argument_names[formal_index];
138
139
if !(argument_names |> any(supplied => supplied.name =~ formal)) then
140
if formal_index >= f.argument_defaults.count \/ !f.argument_defaults[formal_index]? then
141
return false;
142
fi
143
fi
144
od
145
146
return true;
147
si
148
149
_permutation_for(
150
f: Symbols.Function,
151
argument_names: Collections.List[Trees.Identifiers.Identifier]
152
) -> Collections.List[int] is
153
let permutation = Collections.LIST[int]();
154
155
for formal in f.argument_names do
156
permutation.add(_find_supplied(argument_names, formal));
157
od
158
159
return permutation;
160
si
161
162
_find_supplied(
163
argument_names: Collections.List[Trees.Identifiers.Identifier],
164
formal: string
165
) -> int is
166
for source_index in 0..argument_names.count do
167
if argument_names[source_index].name =~ formal then
168
return source_index;
169
fi
170
od
171
172
return -1;
173
si
174
175
_same_permutation(a: Collections.List[int], b: Collections.List[int]) -> bool is
176
if a.count != b.count then
177
return false;
178
fi
179
180
for index in 0..a.count do
181
if a[index] != b[index] then
182
return false;
183
fi
184
od
185
186
return true;
187
si
188
si
189
si