1 #include "sass.hpp" 2 #include "bind.hpp" 3 #include "ast.hpp" 4 #include "backtrace.hpp" 5 #include "context.hpp" 6 #include "expand.hpp" 7 #include "eval.hpp" 8 #include <map> 9 #include <iostream> 10 #include <sstream> 11 12 namespace Sass { 13 bind(sass::string type,sass::string name,Parameters_Obj ps,Arguments_Obj as,Env * env,Eval * eval,Backtraces & traces)14 void bind(sass::string type, sass::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces) 15 { 16 sass::string callee(type + " " + name); 17 18 std::map<sass::string, Parameter_Obj> param_map; 19 List_Obj varargs = SASS_MEMORY_NEW(List, as->pstate()); 20 varargs->is_arglist(true); // enable keyword size handling 21 22 for (size_t i = 0, L = as->length(); i < L; ++i) { 23 if (auto str = Cast<String_Quoted>((*as)[i]->value())) { 24 // force optional quotes (only if needed) 25 if (str->quote_mark()) { 26 str->quote_mark('*'); 27 } 28 } 29 } 30 31 // Set up a map to ensure named arguments refer to actual parameters. Also 32 // eval each default value left-to-right, wrt env, populating env as we go. 33 for (size_t i = 0, L = ps->length(); i < L; ++i) { 34 Parameter_Obj p = ps->at(i); 35 param_map[p->name()] = p; 36 // if (p->default_value()) { 37 // env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env)); 38 // } 39 } 40 41 // plug in all args; if we have leftover params, deal with it later 42 size_t ip = 0, LP = ps->length(); 43 size_t ia = 0, LA = as->length(); 44 while (ia < LA) { 45 Argument_Obj a = as->at(ia); 46 if (ip >= LP) { 47 // skip empty rest arguments 48 if (a->is_rest_argument()) { 49 if (List_Obj l = Cast<List>(a->value())) { 50 if (l->length() == 0) { 51 ++ ia; continue; 52 } 53 } 54 } 55 sass::ostream msg; 56 msg << "wrong number of arguments (" << LA << " for " << LP << ")"; 57 msg << " for `" << name << "'"; 58 return error(msg.str(), as->pstate(), traces); 59 } 60 Parameter_Obj p = ps->at(ip); 61 62 // If the current parameter is the rest parameter, process and break the loop 63 if (p->is_rest_parameter()) { 64 // The next argument by coincidence provides a rest argument 65 if (a->is_rest_argument()) { 66 67 // We should always get a list for rest arguments 68 if (List_Obj rest = Cast<List>(a->value())) { 69 // create a new list object for wrapped items 70 List* arglist = SASS_MEMORY_NEW(List, 71 p->pstate(), 72 0, 73 rest->separator(), 74 true); 75 // wrap each item from list as an argument 76 for (ExpressionObj item : rest->elements()) { 77 if (Argument_Obj arg = Cast<Argument>(item)) { 78 arglist->append(SASS_MEMORY_COPY(arg)); // copy 79 } else { 80 arglist->append(SASS_MEMORY_NEW(Argument, 81 item->pstate(), 82 item, 83 "", 84 false, 85 false)); 86 } 87 } 88 // assign new arglist to environment 89 env->local_frame()[p->name()] = arglist; 90 } 91 // invalid state 92 else { 93 throw std::runtime_error("invalid state"); 94 } 95 } else if (a->is_keyword_argument()) { 96 97 // expand keyword arguments into their parameters 98 List* arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); 99 env->local_frame()[p->name()] = arglist; 100 Map_Obj argmap = Cast<Map>(a->value()); 101 for (auto key : argmap->keys()) { 102 if (String_Constant_Obj str = Cast<String_Constant>(key)) { 103 sass::string param = unquote(str->value()); 104 arglist->append(SASS_MEMORY_NEW(Argument, 105 key->pstate(), 106 argmap->at(key), 107 "$" + param, 108 false, 109 false)); 110 } else { 111 traces.push_back(Backtrace(key->pstate())); 112 throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); 113 } 114 } 115 116 } else { 117 118 // create a new list object for wrapped items 119 List_Obj arglist = SASS_MEMORY_NEW(List, 120 p->pstate(), 121 0, 122 SASS_COMMA, 123 true); 124 // consume the next args 125 while (ia < LA) { 126 // get and post inc 127 a = (*as)[ia++]; 128 // maybe we have another list as argument 129 List_Obj ls = Cast<List>(a->value()); 130 // skip any list completely if empty 131 if (ls && ls->empty() && a->is_rest_argument()) continue; 132 133 ExpressionObj value = a->value(); 134 if (Argument_Obj arg = Cast<Argument>(value)) { 135 arglist->append(arg); 136 } 137 // check if we have rest argument 138 else if (a->is_rest_argument()) { 139 // preserve the list separator from rest args 140 if (List_Obj rest = Cast<List>(a->value())) { 141 arglist->separator(rest->separator()); 142 143 for (size_t i = 0, L = rest->length(); i < L; ++i) { 144 ExpressionObj obj = rest->value_at_index(i); 145 arglist->append(SASS_MEMORY_NEW(Argument, 146 obj->pstate(), 147 obj, 148 "", 149 false, 150 false)); 151 } 152 } 153 // no more arguments 154 break; 155 } 156 // wrap all other value types into Argument 157 else { 158 arglist->append(SASS_MEMORY_NEW(Argument, 159 a->pstate(), 160 a->value(), 161 a->name(), 162 false, 163 false)); 164 } 165 } 166 // assign new arglist to environment 167 env->local_frame()[p->name()] = arglist; 168 } 169 // consumed parameter 170 ++ip; 171 // no more parameters 172 break; 173 } 174 175 // If the current argument is the rest argument, extract a value for processing 176 else if (a->is_rest_argument()) { 177 // normal param and rest arg 178 List_Obj arglist = Cast<List>(a->value()); 179 if (!arglist) { 180 if (ExpressionObj arg = Cast<Expression>(a->value())) { 181 arglist = SASS_MEMORY_NEW(List, a->pstate(), 1); 182 arglist->append(arg); 183 } 184 } 185 186 // empty rest arg - treat all args as default values 187 if (!arglist || !arglist->length()) { 188 break; 189 } else { 190 if (arglist->length() > LP - ip && !ps->has_rest_parameter()) { 191 size_t arg_count = (arglist->length() + LA - 1); 192 sass::ostream msg; 193 msg << callee << " takes " << LP; 194 msg << (LP == 1 ? " argument" : " arguments"); 195 msg << " but " << arg_count; 196 msg << (arg_count == 1 ? " was passed" : " were passed."); 197 deprecated_bind(msg.str(), as->pstate()); 198 199 while (arglist->length() > LP - ip) { 200 arglist->elements().erase(arglist->elements().end() - 1); 201 } 202 } 203 } 204 // otherwise move one of the rest args into the param, converting to argument if necessary 205 ExpressionObj obj = arglist->at(0); 206 if (!(a = Cast<Argument>(obj))) { 207 Expression* a_to_convert = obj; 208 a = SASS_MEMORY_NEW(Argument, 209 a_to_convert->pstate(), 210 a_to_convert, 211 "", 212 false, 213 false); 214 } 215 arglist->elements().erase(arglist->elements().begin()); 216 if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { 217 ++ia; 218 } 219 220 } else if (a->is_keyword_argument()) { 221 Map_Obj argmap = Cast<Map>(a->value()); 222 223 for (auto key : argmap->keys()) { 224 String_Constant* val = Cast<String_Constant>(key); 225 if (val == NULL) { 226 traces.push_back(Backtrace(key->pstate())); 227 throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); 228 } 229 sass::string param = "$" + unquote(val->value()); 230 231 if (!param_map.count(param)) { 232 sass::ostream msg; 233 msg << callee << " has no parameter named " << param; 234 error(msg.str(), a->pstate(), traces); 235 } 236 env->local_frame()[param] = argmap->at(key); 237 } 238 ++ia; 239 continue; 240 } else { 241 ++ia; 242 } 243 244 if (a->name().empty()) { 245 if (env->has_local(p->name())) { 246 sass::ostream msg; 247 msg << "parameter " << p->name() 248 << " provided more than once in call to " << callee; 249 error(msg.str(), a->pstate(), traces); 250 } 251 // ordinal arg -- bind it to the next param 252 env->local_frame()[p->name()] = a->value(); 253 ++ip; 254 } 255 else { 256 // named arg -- bind it to the appropriately named param 257 if (!param_map.count(a->name())) { 258 if (ps->has_rest_parameter()) { 259 varargs->append(a); 260 } else { 261 sass::ostream msg; 262 msg << callee << " has no parameter named " << a->name(); 263 error(msg.str(), a->pstate(), traces); 264 } 265 } 266 if (param_map[a->name()]) { 267 if (param_map[a->name()]->is_rest_parameter()) { 268 sass::ostream msg; 269 msg << "argument " << a->name() << " of " << callee 270 << "cannot be used as named argument"; 271 error(msg.str(), a->pstate(), traces); 272 } 273 } 274 if (env->has_local(a->name())) { 275 sass::ostream msg; 276 msg << "parameter " << p->name() 277 << "provided more than once in call to " << callee; 278 error(msg.str(), a->pstate(), traces); 279 } 280 env->local_frame()[a->name()] = a->value(); 281 } 282 } 283 // EO while ia 284 285 // If we make it here, we're out of args but may have leftover params. 286 // That's only okay if they have default values, or were already bound by 287 // named arguments, or if it's a single rest-param. 288 for (size_t i = ip; i < LP; ++i) { 289 Parameter_Obj leftover = ps->at(i); 290 // cerr << "env for default params:" << endl; 291 // env->print(); 292 // cerr << "********" << endl; 293 if (!env->has_local(leftover->name())) { 294 if (leftover->is_rest_parameter()) { 295 env->local_frame()[leftover->name()] = varargs; 296 } 297 else if (leftover->default_value()) { 298 Expression* dv = leftover->default_value()->perform(eval); 299 env->local_frame()[leftover->name()] = dv; 300 } 301 else { 302 // param is unbound and has no default value -- error 303 throw Exception::MissingArgument(as->pstate(), traces, name, leftover->name(), type); 304 } 305 } 306 } 307 308 return; 309 } 310 311 312 } 313