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