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