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
12namespace Sass {
13
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(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>(ptr: (*as)[i]->value())) {
24 // force optional quotes (only if needed)
25 if (str->quote_mark()) {
26 str->quote_mark(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(i: ia);
46 if (ip >= LP) {
47 // skip empty rest arguments
48 if (a->is_rest_argument()) {
49 if (List_Obj l = Cast<List>(ptr: 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: msg.str(), pstate: as->pstate(), traces);
59 }
60 Parameter_Obj p = ps->at(i: 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>(ptr: 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>(ptr: 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>(ptr: a->value());
101 for (auto key : argmap->keys()) {
102 if (String_Constant_Obj str = Cast<String_Constant>(ptr: 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(x: 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>(ptr: 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>(ptr: value)) {
135 arglist->append(element: 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>(ptr: a->value())) {
141 arglist->separator(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>(ptr: a->value());
179 if (!arglist) {
180 if (ExpressionObj arg = Cast<Expression>(ptr: a->value())) {
181 arglist = SASS_MEMORY_NEW(List, a->pstate(), 1);
182 arglist->append(element: 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: msg.str(), pstate: as->pstate());
198
199 while (arglist->length() > LP - ip) {
200 arglist->elements().erase(position: 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(i: 0);
206 if (!(a = Cast<Argument>(ptr: 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(position: 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>(ptr: a->value());
222
223 for (auto key : argmap->keys()) {
224 String_Constant* val = Cast<String_Constant>(ptr: key);
225 if (val == NULL) {
226 traces.push_back(x: 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(x: param)) {
232 sass::ostream msg;
233 msg << callee << " has no parameter named " << param;
234 error(msg: msg.str(), pstate: a->pstate(), traces);
235 }
236 env->local_frame()[param] = argmap->at(k: key);
237 }
238 ++ia;
239 continue;
240 } else {
241 ++ia;
242 }
243
244 if (a->name().empty()) {
245 if (env->has_local(key: p->name())) {
246 sass::ostream msg;
247 msg << "parameter " << p->name()
248 << " provided more than once in call to " << callee;
249 error(msg: msg.str(), pstate: 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(x: a->name())) {
258 if (ps->has_rest_parameter()) {
259 varargs->append(element: a);
260 } else {
261 sass::ostream msg;
262 msg << callee << " has no parameter named " << a->name();
263 error(msg: msg.str(), pstate: 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: msg.str(), pstate: a->pstate(), traces);
272 }
273 }
274 if (env->has_local(key: a->name())) {
275 sass::ostream msg;
276 msg << "parameter " << p->name()
277 << "provided more than once in call to " << callee;
278 error(msg: msg.str(), pstate: 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(key: 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(op: 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

source code of gtk/subprojects/libsass/src/bind.cpp