1 | #include "ast.hpp" |
2 | #include "expand.hpp" |
3 | #include "fn_utils.hpp" |
4 | #include "fn_miscs.hpp" |
5 | #include "util_string.hpp" |
6 | |
7 | namespace Sass { |
8 | |
9 | namespace Functions { |
10 | |
11 | ////////////////////////// |
12 | // INTROSPECTION FUNCTIONS |
13 | ////////////////////////// |
14 | |
15 | Signature type_of_sig = "type-of($value)" ; |
16 | BUILT_IN(type_of) |
17 | { |
18 | Expression* v = ARG("$value" , Expression); |
19 | return SASS_MEMORY_NEW(String_Quoted, pstate, v->type()); |
20 | } |
21 | |
22 | Signature variable_exists_sig = "variable-exists($name)" ; |
23 | BUILT_IN(variable_exists) |
24 | { |
25 | sass::string s = Util::normalize_underscores(str: unquote(ARG("$name" , String_Constant)->value())); |
26 | |
27 | if(d_env.has(key: "$" +s)) { |
28 | return SASS_MEMORY_NEW(Boolean, pstate, true); |
29 | } |
30 | else { |
31 | return SASS_MEMORY_NEW(Boolean, pstate, false); |
32 | } |
33 | } |
34 | |
35 | Signature global_variable_exists_sig = "global-variable-exists($name)" ; |
36 | BUILT_IN(global_variable_exists) |
37 | { |
38 | sass::string s = Util::normalize_underscores(str: unquote(ARG("$name" , String_Constant)->value())); |
39 | |
40 | if(d_env.has_global(key: "$" +s)) { |
41 | return SASS_MEMORY_NEW(Boolean, pstate, true); |
42 | } |
43 | else { |
44 | return SASS_MEMORY_NEW(Boolean, pstate, false); |
45 | } |
46 | } |
47 | |
48 | Signature function_exists_sig = "function-exists($name)" ; |
49 | BUILT_IN(function_exists) |
50 | { |
51 | String_Constant* ss = Cast<String_Constant>(ptr: env["$name" ]); |
52 | if (!ss) { |
53 | error(msg: "$name: " + (env["$name" ]->to_string()) + " is not a string for `function-exists'" , pstate, traces); |
54 | } |
55 | |
56 | sass::string name = Util::normalize_underscores(str: unquote(ss->value())); |
57 | |
58 | if(d_env.has(key: name+"[f]" )) { |
59 | return SASS_MEMORY_NEW(Boolean, pstate, true); |
60 | } |
61 | else { |
62 | return SASS_MEMORY_NEW(Boolean, pstate, false); |
63 | } |
64 | } |
65 | |
66 | Signature mixin_exists_sig = "mixin-exists($name)" ; |
67 | BUILT_IN(mixin_exists) |
68 | { |
69 | sass::string s = Util::normalize_underscores(str: unquote(ARG("$name" , String_Constant)->value())); |
70 | |
71 | if(d_env.has(key: s+"[m]" )) { |
72 | return SASS_MEMORY_NEW(Boolean, pstate, true); |
73 | } |
74 | else { |
75 | return SASS_MEMORY_NEW(Boolean, pstate, false); |
76 | } |
77 | } |
78 | |
79 | Signature feature_exists_sig = "feature-exists($feature)" ; |
80 | BUILT_IN(feature_exists) |
81 | { |
82 | sass::string s = unquote(ARG("$feature" , String_Constant)->value()); |
83 | |
84 | static const auto *const features = new std::unordered_set<sass::string> { |
85 | "global-variable-shadowing" , |
86 | "extend-selector-pseudoclass" , |
87 | "at-error" , |
88 | "units-level-3" , |
89 | "custom-property" |
90 | }; |
91 | return SASS_MEMORY_NEW(Boolean, pstate, features->find(s) != features->end()); |
92 | } |
93 | |
94 | Signature call_sig = "call($function, $args...)" ; |
95 | BUILT_IN(call) |
96 | { |
97 | sass::string function; |
98 | Function* ff = Cast<Function>(ptr: env["$function" ]); |
99 | String_Constant* ss = Cast<String_Constant>(ptr: env["$function" ]); |
100 | |
101 | if (ss) { |
102 | function = Util::normalize_underscores(str: unquote(ss->value())); |
103 | std::cerr << "DEPRECATION WARNING: " ; |
104 | std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl; |
105 | std::cerr << "in Sass 4.0. Use call(get-function(" + quote(function) + ")) instead." << std::endl; |
106 | std::cerr << std::endl; |
107 | } else if (ff) { |
108 | function = ff->name(); |
109 | } |
110 | |
111 | List_Obj arglist = SASS_MEMORY_COPY(ARG("$args" , List)); |
112 | |
113 | Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); |
114 | // sass::string full_name(name + "[f]"); |
115 | // Definition* def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0; |
116 | // Parameters* params = def ? def->parameters() : 0; |
117 | // size_t param_size = params ? params->length() : 0; |
118 | for (size_t i = 0, L = arglist->length(); i < L; ++i) { |
119 | ExpressionObj expr = arglist->value_at_index(i); |
120 | // if (params && params->has_rest_parameter()) { |
121 | // Parameter_Obj p = param_size > i ? (*params)[i] : 0; |
122 | // List* list = Cast<List>(expr); |
123 | // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; |
124 | // } |
125 | if (arglist->is_arglist()) { |
126 | ExpressionObj obj = arglist->at(i); |
127 | Argument_Obj arg = (Argument*) obj.ptr(); // XXX |
128 | args->append(SASS_MEMORY_NEW(Argument, |
129 | pstate, |
130 | expr, |
131 | arg ? arg->name() : "" , |
132 | arg ? arg->is_rest_argument() : false, |
133 | arg ? arg->is_keyword_argument() : false)); |
134 | } else { |
135 | args->append(SASS_MEMORY_NEW(Argument, pstate, expr)); |
136 | } |
137 | } |
138 | Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, function, args); |
139 | |
140 | Expand expand(ctx, &d_env, &selector_stack, &original_stack); |
141 | func->via_call(via_call__: true); // calc invoke is allowed |
142 | if (ff) func->func(func__: ff); |
143 | return Cast<PreValue>(ptr: func->perform(op: &expand.eval)); |
144 | } |
145 | |
146 | //////////////////// |
147 | // BOOLEAN FUNCTIONS |
148 | //////////////////// |
149 | |
150 | Signature not_sig = "not($value)" ; |
151 | BUILT_IN(sass_not) |
152 | { |
153 | return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value" , Expression)->is_false()); |
154 | } |
155 | |
156 | Signature if_sig = "if($condition, $if-true, $if-false)" ; |
157 | BUILT_IN(sass_if) |
158 | { |
159 | Expand expand(ctx, &d_env, &selector_stack, &original_stack); |
160 | ExpressionObj cond = ARG("$condition" , Expression)->perform(op: &expand.eval); |
161 | bool is_true = !cond->is_false(); |
162 | ExpressionObj res = ARG(is_true ? "$if-true" : "$if-false" , Expression); |
163 | ValueObj qwe = Cast<Value>(ptr: res->perform(op: &expand.eval)); |
164 | // res = res->perform(&expand.eval.val_eval); |
165 | qwe->set_delayed(false); // clone? |
166 | return qwe.detach(); |
167 | } |
168 | |
169 | ////////////////////////// |
170 | // MISCELLANEOUS FUNCTIONS |
171 | ////////////////////////// |
172 | |
173 | Signature inspect_sig = "inspect($value)" ; |
174 | BUILT_IN(inspect) |
175 | { |
176 | Expression* v = ARG("$value" , Expression); |
177 | if (v->concrete_type() == Expression::NULL_VAL) { |
178 | return SASS_MEMORY_NEW(String_Constant, pstate, "null" ); |
179 | } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) { |
180 | return SASS_MEMORY_NEW(String_Constant, pstate, "false" ); |
181 | } else if (v->concrete_type() == Expression::STRING) { |
182 | String_Constant *s = Cast<String_Constant>(ptr: v); |
183 | if (s->quote_mark()) { |
184 | return SASS_MEMORY_NEW(String_Constant, pstate, quote(s->value(), s->quote_mark())); |
185 | } else { |
186 | return s; |
187 | } |
188 | } else { |
189 | // ToDo: fix to_sass for nested parentheses |
190 | Sass_Output_Style old_style; |
191 | old_style = ctx.c_options.output_style; |
192 | ctx.c_options.output_style = TO_SASS; |
193 | Emitter emitter(ctx.c_options); |
194 | Inspect i(emitter); |
195 | i.in_declaration = false; |
196 | v->perform(op: &i); |
197 | ctx.c_options.output_style = old_style; |
198 | return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer()); |
199 | } |
200 | } |
201 | |
202 | Signature content_exists_sig = "content-exists()" ; |
203 | BUILT_IN(content_exists) |
204 | { |
205 | if (!d_env.has_global(key: "is_in_mixin" )) { |
206 | error(msg: "Cannot call content-exists() except within a mixin." , pstate, traces); |
207 | } |
208 | return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]" )); |
209 | } |
210 | |
211 | Signature get_function_sig = "get-function($name, $css: false)" ; |
212 | BUILT_IN(get_function) |
213 | { |
214 | String_Constant* ss = Cast<String_Constant>(ptr: env["$name" ]); |
215 | if (!ss) { |
216 | error(msg: "$name: " + (env["$name" ]->to_string()) + " is not a string for `get-function'" , pstate, traces); |
217 | } |
218 | |
219 | sass::string name = Util::normalize_underscores(str: unquote(ss->value())); |
220 | sass::string full_name = name + "[f]" ; |
221 | |
222 | Boolean_Obj css = ARG("$css" , Boolean); |
223 | if (!css->is_false()) { |
224 | Definition* def = SASS_MEMORY_NEW(Definition, |
225 | pstate, |
226 | name, |
227 | SASS_MEMORY_NEW(Parameters, pstate), |
228 | SASS_MEMORY_NEW(Block, pstate, 0, false), |
229 | Definition::FUNCTION); |
230 | return SASS_MEMORY_NEW(Function, pstate, def, true); |
231 | } |
232 | |
233 | |
234 | if (!d_env.has_global(key: full_name)) { |
235 | error(msg: "Function not found: " + name, pstate, traces); |
236 | } |
237 | |
238 | Definition* def = Cast<Definition>(ptr: d_env[full_name]); |
239 | return SASS_MEMORY_NEW(Function, pstate, def, false); |
240 | } |
241 | |
242 | } |
243 | |
244 | } |
245 | |