1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | |
5 | #include "listize.hpp" |
6 | #include "operators.hpp" |
7 | #include "fn_utils.hpp" |
8 | #include "fn_lists.hpp" |
9 | |
10 | namespace Sass { |
11 | |
12 | namespace Functions { |
13 | |
14 | ///////////////// |
15 | // LIST FUNCTIONS |
16 | ///////////////// |
17 | |
18 | Signature keywords_sig = "keywords($args)" ; |
19 | BUILT_IN(keywords) |
20 | { |
21 | List_Obj arglist = SASS_MEMORY_COPY(ARG("$args" , List)); // copy |
22 | Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1); |
23 | for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { |
24 | ExpressionObj obj = arglist->at(i); |
25 | Argument_Obj arg = (Argument*) obj.ptr(); // XXX |
26 | sass::string name = sass::string(arg->name()); |
27 | name = name.erase(pos: 0, n: 1); // sanitize name (remove dollar sign) |
28 | *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted, |
29 | pstate, name), |
30 | y: arg->value()); |
31 | } |
32 | return result.detach(); |
33 | } |
34 | |
35 | Signature length_sig = "length($list)" ; |
36 | BUILT_IN(length) |
37 | { |
38 | if (SelectorList * sl = Cast<SelectorList>(ptr: env["$list" ])) { |
39 | return SASS_MEMORY_NEW(Number, pstate, (double) sl->length()); |
40 | } |
41 | Expression* v = ARG("$list" , Expression); |
42 | if (v->concrete_type() == Expression::MAP) { |
43 | Map* map = Cast<Map>(ptr: env["$list" ]); |
44 | return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1)); |
45 | } |
46 | if (v->concrete_type() == Expression::SELECTOR) { |
47 | if (CompoundSelector * h = Cast<CompoundSelector>(ptr: v)) { |
48 | return SASS_MEMORY_NEW(Number, pstate, (double)h->length()); |
49 | } else if (SelectorList * ls = Cast<SelectorList>(ptr: v)) { |
50 | return SASS_MEMORY_NEW(Number, pstate, (double)ls->length()); |
51 | } else { |
52 | return SASS_MEMORY_NEW(Number, pstate, 1); |
53 | } |
54 | } |
55 | |
56 | List* list = Cast<List>(ptr: env["$list" ]); |
57 | return SASS_MEMORY_NEW(Number, |
58 | pstate, |
59 | (double)(list ? list->size() : 1)); |
60 | } |
61 | |
62 | Signature nth_sig = "nth($list, $n)" ; |
63 | BUILT_IN(nth) |
64 | { |
65 | double nr = ARGVAL("$n" ); |
66 | Map* m = Cast<Map>(ptr: env["$list" ]); |
67 | if (SelectorList * sl = Cast<SelectorList>(ptr: env["$list" ])) { |
68 | size_t len = m ? m->length() : sl->length(); |
69 | bool empty = m ? m->empty() : sl->empty(); |
70 | if (empty) error(msg: "argument `$list` of `" + sass::string(sig) + "` must not be empty" , pstate, traces); |
71 | double index = std::floor(x: nr < 0 ? len + nr : nr - 1); |
72 | if (index < 0 || index > len - 1) error(msg: "index out of bounds for `" + sass::string(sig) + "`" , pstate, traces); |
73 | return Cast<Value>(ptr: Listize::perform(node: sl->get(i: static_cast<int>(index)))); |
74 | } |
75 | List_Obj l = Cast<List>(ptr: env["$list" ]); |
76 | if (nr == 0) error(msg: "argument `$n` of `" + sass::string(sig) + "` must be non-zero" , pstate, traces); |
77 | // if the argument isn't a list, then wrap it in a singleton list |
78 | if (!m && !l) { |
79 | l = SASS_MEMORY_NEW(List, pstate, 1); |
80 | l->append(ARG("$list" , Expression)); |
81 | } |
82 | size_t len = m ? m->length() : l->length(); |
83 | bool empty = m ? m->empty() : l->empty(); |
84 | if (empty) error(msg: "argument `$list` of `" + sass::string(sig) + "` must not be empty" , pstate, traces); |
85 | double index = std::floor(x: nr < 0 ? len + nr : nr - 1); |
86 | if (index < 0 || index > len - 1) error(msg: "index out of bounds for `" + sass::string(sig) + "`" , pstate, traces); |
87 | |
88 | if (m) { |
89 | l = SASS_MEMORY_NEW(List, pstate, 2); |
90 | l->append(element: m->keys()[static_cast<unsigned int>(index)]); |
91 | l->append(element: m->at(k: m->keys()[static_cast<unsigned int>(index)])); |
92 | return l.detach(); |
93 | } |
94 | else { |
95 | ValueObj rv = l->value_at_index(i: static_cast<int>(index)); |
96 | rv->set_delayed(false); |
97 | return rv.detach(); |
98 | } |
99 | } |
100 | |
101 | Signature set_nth_sig = "set-nth($list, $n, $value)" ; |
102 | BUILT_IN(set_nth) |
103 | { |
104 | Map_Obj m = Cast<Map>(ptr: env["$list" ]); |
105 | List_Obj l = Cast<List>(ptr: env["$list" ]); |
106 | Number_Obj n = ARG("$n" , Number); |
107 | ExpressionObj v = ARG("$value" , Expression); |
108 | if (!l) { |
109 | l = SASS_MEMORY_NEW(List, pstate, 1); |
110 | l->append(ARG("$list" , Expression)); |
111 | } |
112 | if (m) { |
113 | l = m->to_list(pstate); |
114 | } |
115 | if (l->empty()) error(msg: "argument `$list` of `" + sass::string(sig) + "` must not be empty" , pstate, traces); |
116 | double index = std::floor(x: n->value() < 0 ? l->length() + n->value() : n->value() - 1); |
117 | if (index < 0 || index > l->length() - 1) error(msg: "index out of bounds for `" + sass::string(sig) + "`" , pstate, traces); |
118 | List* result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed()); |
119 | for (size_t i = 0, L = l->length(); i < L; ++i) { |
120 | result->append(element: ((i == index) ? v : (*l)[i])); |
121 | } |
122 | return result; |
123 | } |
124 | |
125 | Signature index_sig = "index($list, $value)" ; |
126 | BUILT_IN(index) |
127 | { |
128 | Map_Obj m = Cast<Map>(ptr: env["$list" ]); |
129 | List_Obj l = Cast<List>(ptr: env["$list" ]); |
130 | ExpressionObj v = ARG("$value" , Expression); |
131 | if (!l) { |
132 | l = SASS_MEMORY_NEW(List, pstate, 1); |
133 | l->append(ARG("$list" , Expression)); |
134 | } |
135 | if (m) { |
136 | l = m->to_list(pstate); |
137 | } |
138 | for (size_t i = 0, L = l->length(); i < L; ++i) { |
139 | if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1)); |
140 | } |
141 | return SASS_MEMORY_NEW(Null, pstate); |
142 | } |
143 | |
144 | Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)" ; |
145 | BUILT_IN(join) |
146 | { |
147 | Map_Obj m1 = Cast<Map>(ptr: env["$list1" ]); |
148 | Map_Obj m2 = Cast<Map>(ptr: env["$list2" ]); |
149 | List_Obj l1 = Cast<List>(ptr: env["$list1" ]); |
150 | List_Obj l2 = Cast<List>(ptr: env["$list2" ]); |
151 | String_Constant_Obj sep = ARG("$separator" , String_Constant); |
152 | enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); |
153 | Value* bracketed = ARG("$bracketed" , Value); |
154 | bool is_bracketed = (l1 ? l1->is_bracketed() : false); |
155 | if (!l1) { |
156 | l1 = SASS_MEMORY_NEW(List, pstate, 1); |
157 | l1->append(ARG("$list1" , Expression)); |
158 | sep_val = (l2 ? l2->separator() : SASS_SPACE); |
159 | is_bracketed = (l2 ? l2->is_bracketed() : false); |
160 | } |
161 | if (!l2) { |
162 | l2 = SASS_MEMORY_NEW(List, pstate, 1); |
163 | l2->append(ARG("$list2" , Expression)); |
164 | } |
165 | if (m1) { |
166 | l1 = m1->to_list(pstate); |
167 | sep_val = SASS_COMMA; |
168 | } |
169 | if (m2) { |
170 | l2 = m2->to_list(pstate); |
171 | } |
172 | size_t len = l1->length() + l2->length(); |
173 | sass::string sep_str = unquote(sep->value()); |
174 | if (sep_str == "space" ) sep_val = SASS_SPACE; |
175 | else if (sep_str == "comma" ) sep_val = SASS_COMMA; |
176 | else if (sep_str != "auto" ) error(msg: "argument `$separator` of `" + sass::string(sig) + "` must be `space`, `comma`, or `auto`" , pstate, traces); |
177 | String_Constant_Obj bracketed_as_str = Cast<String_Constant>(ptr: bracketed); |
178 | bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto" ; |
179 | if (!bracketed_is_auto) { |
180 | is_bracketed = !bracketed->is_false(); |
181 | } |
182 | List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed); |
183 | result->concat(v: l1); |
184 | result->concat(v: l2); |
185 | return result.detach(); |
186 | } |
187 | |
188 | Signature append_sig = "append($list, $val, $separator: auto)" ; |
189 | BUILT_IN(append) |
190 | { |
191 | Map_Obj m = Cast<Map>(ptr: env["$list" ]); |
192 | List_Obj l = Cast<List>(ptr: env["$list" ]); |
193 | ExpressionObj v = ARG("$val" , Expression); |
194 | if (SelectorList * sl = Cast<SelectorList>(ptr: env["$list" ])) { |
195 | l = Cast<List>(ptr: Listize::perform(node: sl)); |
196 | } |
197 | String_Constant_Obj sep = ARG("$separator" , String_Constant); |
198 | if (!l) { |
199 | l = SASS_MEMORY_NEW(List, pstate, 1); |
200 | l->append(ARG("$list" , Expression)); |
201 | } |
202 | if (m) { |
203 | l = m->to_list(pstate); |
204 | } |
205 | List* result = SASS_MEMORY_COPY(l); |
206 | sass::string sep_str(unquote(sep->value())); |
207 | if (sep_str != "auto" ) { // check default first |
208 | if (sep_str == "space" ) result->separator(separator__: SASS_SPACE); |
209 | else if (sep_str == "comma" ) result->separator(separator__: SASS_COMMA); |
210 | else error(msg: "argument `$separator` of `" + sass::string(sig) + "` must be `space`, `comma`, or `auto`" , pstate, traces); |
211 | } |
212 | if (l->is_arglist()) { |
213 | result->append(SASS_MEMORY_NEW(Argument, |
214 | v->pstate(), |
215 | v, |
216 | "" , |
217 | false, |
218 | false)); |
219 | |
220 | } else { |
221 | result->append(element: v); |
222 | } |
223 | return result; |
224 | } |
225 | |
226 | Signature zip_sig = "zip($lists...)" ; |
227 | BUILT_IN(zip) |
228 | { |
229 | List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists" , List)); |
230 | size_t shortest = 0; |
231 | for (size_t i = 0, L = arglist->length(); i < L; ++i) { |
232 | List_Obj ith = Cast<List>(ptr: arglist->value_at_index(i)); |
233 | Map_Obj mith = Cast<Map>(ptr: arglist->value_at_index(i)); |
234 | if (!ith) { |
235 | if (mith) { |
236 | ith = mith->to_list(pstate); |
237 | } else { |
238 | ith = SASS_MEMORY_NEW(List, pstate, 1); |
239 | ith->append(element: arglist->value_at_index(i)); |
240 | } |
241 | if (arglist->is_arglist()) { |
242 | Argument_Obj arg = (Argument*)(arglist->at(i).ptr()); // XXX |
243 | arg->value(value__: ith); |
244 | } else { |
245 | (*arglist)[i] = ith; |
246 | } |
247 | } |
248 | shortest = (i ? std::min(a: shortest, b: ith->length()) : ith->length()); |
249 | } |
250 | List* zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA); |
251 | size_t L = arglist->length(); |
252 | for (size_t i = 0; i < shortest; ++i) { |
253 | List* zipper = SASS_MEMORY_NEW(List, pstate, L); |
254 | for (size_t j = 0; j < L; ++j) { |
255 | zipper->append(element: Cast<List>(ptr: arglist->value_at_index(i: j))->at(i)); |
256 | } |
257 | zippers->append(element: zipper); |
258 | } |
259 | return zippers; |
260 | } |
261 | |
262 | Signature list_separator_sig = "list_separator($list)" ; |
263 | BUILT_IN(list_separator) |
264 | { |
265 | List_Obj l = Cast<List>(ptr: env["$list" ]); |
266 | if (!l) { |
267 | l = SASS_MEMORY_NEW(List, pstate, 1); |
268 | l->append(ARG("$list" , Expression)); |
269 | } |
270 | return SASS_MEMORY_NEW(String_Quoted, |
271 | pstate, |
272 | l->separator() == SASS_COMMA ? "comma" : "space" ); |
273 | } |
274 | |
275 | Signature is_bracketed_sig = "is-bracketed($list)" ; |
276 | BUILT_IN(is_bracketed) |
277 | { |
278 | ValueObj value = ARG("$list" , Value); |
279 | List_Obj list = Cast<List>(ptr: value); |
280 | return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed()); |
281 | } |
282 | |
283 | } |
284 | |
285 | } |
286 | |