1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
/*****
* application.h
* Andy Hammerlindl 2005/05/20
*
* An application is a matching of arguments in a call expression to formal
* parameters of a function. Since the language allows default arguments,
* keyword arguments, rest arguments, and anything else we think of, this
* is not a simple mapping.
*****/
#ifndef APPLICATION_H
#define APPLICATION_H
#include "common.h"
#include "types.h"
#include "coenv.h"
#include "exp.h"
// Defined in runtime.in:
namespace run {
void pushDefault(vm::stack *Stack);
}
using absyntax::arglist;
using absyntax::varinit;
using absyntax::arrayinit;
using absyntax::tempExp;
// This is mid-way between trans and absyntax.
namespace trans {
typedef Int score;
typedef mem::vector<score> score_vector;
// This is used during the translation of arguments to store temporary
// expressions for arguments that need to be translated for side-effects at a
// certain point but used later on. The invariant maintained is that if the
// vector has n elements, then the side-effects for the first n arguments have
// been translated. Null is pushed onto the vector to indicate that the
// expression was evaluated directly onto the stack, without the use of a
// temporary.
typedef mem::vector<tempExp *> temp_vector;
struct arg : public gc {
types::ty *t;
arg(types::ty *t)
: t(t) {}
virtual ~arg() {}
virtual void trans(coenv &e, temp_vector &) = 0;
};
struct varinitArg : public arg {
varinit *v;
varinitArg(varinit *v, types::ty *t)
: arg(t), v(v) {}
virtual void trans(coenv &e, temp_vector &) {
// Open signatures can match overloaded variables, but there is no way to
// translate the result, so report an error.
if (t->kind == types::ty_overloaded) {
em.error(v->getPos());
em << "overloaded argument in function call";
}
else
v->transToType(e, t);
}
};
// Pushes a default argument token on the stack as a placeholder for the
// argument.
struct defaultArg : public arg {
defaultArg(types::ty *t)
: arg(t) {}
virtual void trans(coenv &e, temp_vector &) {
//e.c.encode(inst::builtin, run::pushDefault);
e.c.encode(inst::push_default);
}
};
// Handles translation of all the arguments matched to the rest formal.
// NOTE: This code duplicates a lot of arrayinit.
struct restArg : public gc {
mem::list<arg *> inits;
arg *rest;
public:
restArg()
: rest(0) {}
virtual ~restArg()
{}
// Encodes the instructions to make an array from size elements on the stack.
static void transMaker(coenv &e, Int size, bool rest);
void trans(coenv &e, temp_vector &temps);
void add(arg *init) {
inits.push_back(init);
}
void addRest(arg *init) {
rest=init;
}
};
// This class generates sequenced args, args whose side-effects occur in order
// according to their index, regardless of the order they are called. This is
// used to ensure left-to-right order of evaluation of keyword arguments, even
// if they are given out of the order specified in the declaration.
class sequencer {
struct sequencedArg : public varinitArg {
sequencer &parent;
size_t i;
sequencedArg(varinit *v, types::ty *t, sequencer &parent, size_t i)
: varinitArg(v, t), parent(parent), i(i) {}
void trans(coenv &e, temp_vector &temps) {
parent.trans(e, i, temps);
}
};
typedef mem::vector<sequencedArg *> sa_vector;
sa_vector args;
// Makes a temporary for the next argument in the sequence.
void alias(coenv &e, temp_vector &temps) {
size_t n=temps.size();
assert(n < args.size());
sequencedArg *sa=args[n];
assert(sa);
temps.push_back(new tempExp(e, sa->v, sa->t));
}
// Get in a state to translate the i-th argument, aliasing any arguments that
// occur before it in the sequence.
void advance(coenv &e, size_t i, temp_vector &temps) {
while (temps.size() < i)
alias(e,temps);
}
void trans(coenv &e, size_t i, temp_vector &temps) {
if (i < temps.size()) {
// Already translated, use the alias.
assert(temps[i]);
temps[i]->trans(e);
}
else {
// Alias earlier args if necessary.
advance(e, i, temps);
// Translate using the base method.
args[i]->varinitArg::trans(e,temps);
// Push null to indicate the argument has been translated.
temps.push_back(0);
}
}
public:
arg *addArg(varinit *v, types::ty *t, size_t i) {
if (args.size() <= i)
args.resize(i+1);
return args[i]=new sequencedArg(v, t, *this, i);
}
};
class application : public gc {
types::signature *sig;
types::function *t;
// Sequencer to ensure given arguments are evaluated in the proper order.
// Use of this sequencer means that transArgs can only be called once.
sequencer seq;
typedef mem::vector<arg *> arg_vector;
arg_vector args;
restArg *rest;
// Target formal to match with arguments to be packed into the rest array.
types::formal rf;
// During the matching of arguments to an application, this stores the index
// of the first unmatched formal.
size_t index;
// To resolve which is the best application in case of multiple matches of
// overloaded functions, a score is kept for every source argument matched,
// and an application with higher-scoring matches is chosen.
score_vector scores;
void initRest();
application(types::signature *sig)
: sig(sig),
t(0),
args(sig->formals.size()),
rest(0),
rf(0),
index(0)
{ assert(sig); initRest(); }
application(types::function *t)
: sig(t->getSignature()),
t(t),
args(sig->formals.size()),
rest(0),
rf(0),
index(0)
{ assert(sig); initRest(); }
types::formal &getTarget() {
return sig->getFormal(index);
}
// Move the index forward one, then keep going until we're at an unmatched
// argument.
void advanceIndex() {
do {
++index;
} while (index < args.size() && args[index]!=0);
}
// Finds the first unmatched formal of the given name, returning the index.
// The rest formal is not tested. This function returns FAIL if no formals
// match.
Int find(symbol name);
// Match the formal at index to its default argument (if it has one).
bool matchDefault();
// Match the argument to the formal indexed by spot.
bool matchAtSpot(size_t spot, env &e, types::formal &source,
varinit *a, size_t evalIndex);
// Match the argument to be packed into the rest array, if possible.
bool matchArgumentToRest(env &e, types::formal& source,
varinit *a, size_t evalIndex);
// Matches the argument to a formal in the target signature (possibly causing
// other formals in the target to be matched to default values), and updates
// the matchpoint accordingly.
bool matchArgument(env &e, types::formal& source,
varinit *a, size_t evalIndex);
// Match an argument bound to a name, as in f(index=7).
bool matchNamedArgument(env &e, types::formal& source,
varinit *a, size_t evalIndex);
// After all source formals have been matched, checks if the match is
// complete (filling in final default values if necessary).
bool complete();
// Match a rest argument in the calling expression.
bool matchRest(env &e, types::formal& f, varinit *a);
// Match the argument represented in signature to the target signature. On
// success, all of the arguments in args will be properly set up.
bool matchSignature(env &e, types::signature *source, arglist &al);
// Match a signature which is open, meaning that any sequence of arguments is
// matched.
bool matchOpen(env &e, signature *source, arglist &al);
friend class maximizer;
public:
// Attempt to build an application given the target signature and the source
// signature (representing the given arguments). Return 0 if they cannot be
// matched.
static application *match(env &e,
types::function *t,
types::signature *source,
arglist &al);
// Translate the arguments so they appear in order on the stack in
// preparation for a call.
void transArgs(coenv &e);
types::function *getType() {
return t;
}
// This returns true in the special case that the arguments matched without
// casting or packing into the rest formal.
bool exact();
// The next best thing (score-wise) to an exact match. This returns true if
// there are two arguments, one of which is cast and one is matched exactly
// and neither are packed into the rest argument.
bool halfExact();
};
typedef mem::list<application *> app_list;
// Given an overloaded list of types, determines which type to call. If none
// are applicable, returns an empty vector, if there is ambiguity, several will
// be returned.
app_list multimatch(env &e,
types::overloaded *o,
types::signature *source,
arglist &al);
} // namespace trans
#endif
|