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
|
/*****
* 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, size_t evalIndex);
// 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
|