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