1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | |
5 | #include <cstdlib> |
6 | #include <cstring> |
7 | #include "util.hpp" |
8 | #include "eval.hpp" |
9 | #include "operators.hpp" |
10 | #include "sass/values.h" |
11 | #include "sass_values.hpp" |
12 | |
13 | extern "C" { |
14 | using namespace Sass; |
15 | |
16 | // Return the sass tag for a generic sass value |
17 | enum Sass_Tag ADDCALL sass_value_get_tag(const union Sass_Value* v) { return v->unknown.tag; } |
18 | |
19 | // Check value for specified type |
20 | bool ADDCALL sass_value_is_null(const union Sass_Value* v) { return v->unknown.tag == SASS_NULL; } |
21 | bool ADDCALL sass_value_is_number(const union Sass_Value* v) { return v->unknown.tag == SASS_NUMBER; } |
22 | bool ADDCALL sass_value_is_string(const union Sass_Value* v) { return v->unknown.tag == SASS_STRING; } |
23 | bool ADDCALL sass_value_is_boolean(const union Sass_Value* v) { return v->unknown.tag == SASS_BOOLEAN; } |
24 | bool ADDCALL sass_value_is_color(const union Sass_Value* v) { return v->unknown.tag == SASS_COLOR; } |
25 | bool ADDCALL sass_value_is_list(const union Sass_Value* v) { return v->unknown.tag == SASS_LIST; } |
26 | bool ADDCALL sass_value_is_map(const union Sass_Value* v) { return v->unknown.tag == SASS_MAP; } |
27 | bool ADDCALL sass_value_is_error(const union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; } |
28 | bool ADDCALL sass_value_is_warning(const union Sass_Value* v) { return v->unknown.tag == SASS_WARNING; } |
29 | |
30 | // Getters and setters for Sass_Number |
31 | double ADDCALL sass_number_get_value(const union Sass_Value* v) { return v->number.value; } |
32 | void ADDCALL sass_number_set_value(union Sass_Value* v, double value) { v->number.value = value; } |
33 | const char* ADDCALL sass_number_get_unit(const union Sass_Value* v) { return v->number.unit; } |
34 | void ADDCALL sass_number_set_unit(union Sass_Value* v, char* unit) { v->number.unit = unit; } |
35 | |
36 | // Getters and setters for Sass_String |
37 | const char* ADDCALL sass_string_get_value(const union Sass_Value* v) { return v->string.value; } |
38 | void ADDCALL sass_string_set_value(union Sass_Value* v, char* value) { v->string.value = value; } |
39 | bool ADDCALL sass_string_is_quoted(const union Sass_Value* v) { return v->string.quoted; } |
40 | void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted) { v->string.quoted = quoted; } |
41 | |
42 | // Getters and setters for Sass_Boolean |
43 | bool ADDCALL sass_boolean_get_value(const union Sass_Value* v) { return v->boolean.value; } |
44 | void ADDCALL sass_boolean_set_value(union Sass_Value* v, bool value) { v->boolean.value = value; } |
45 | |
46 | // Getters and setters for Sass_Color |
47 | double ADDCALL sass_color_get_r(const union Sass_Value* v) { return v->color.r; } |
48 | void ADDCALL sass_color_set_r(union Sass_Value* v, double r) { v->color.r = r; } |
49 | double ADDCALL sass_color_get_g(const union Sass_Value* v) { return v->color.g; } |
50 | void ADDCALL sass_color_set_g(union Sass_Value* v, double g) { v->color.g = g; } |
51 | double ADDCALL sass_color_get_b(const union Sass_Value* v) { return v->color.b; } |
52 | void ADDCALL sass_color_set_b(union Sass_Value* v, double b) { v->color.b = b; } |
53 | double ADDCALL sass_color_get_a(const union Sass_Value* v) { return v->color.a; } |
54 | void ADDCALL sass_color_set_a(union Sass_Value* v, double a) { v->color.a = a; } |
55 | |
56 | // Getters and setters for Sass_List |
57 | size_t ADDCALL sass_list_get_length(const union Sass_Value* v) { return v->list.length; } |
58 | enum Sass_Separator ADDCALL sass_list_get_separator(const union Sass_Value* v) { return v->list.separator; } |
59 | void ADDCALL sass_list_set_separator(union Sass_Value* v, enum Sass_Separator separator) { v->list.separator = separator; } |
60 | bool ADDCALL sass_list_get_is_bracketed(const union Sass_Value* v) { return v->list.is_bracketed; } |
61 | void ADDCALL sass_list_set_is_bracketed(union Sass_Value* v, bool is_bracketed) { v->list.is_bracketed = is_bracketed; } |
62 | // Getters and setters for Sass_List values |
63 | union Sass_Value* ADDCALL sass_list_get_value(const union Sass_Value* v, size_t i) { return v->list.values[i]; } |
64 | void ADDCALL sass_list_set_value(union Sass_Value* v, size_t i, union Sass_Value* value) { v->list.values[i] = value; } |
65 | |
66 | // Getters and setters for Sass_Map |
67 | size_t ADDCALL sass_map_get_length(const union Sass_Value* v) { return v->map.length; } |
68 | // Getters and setters for Sass_List keys and values |
69 | union Sass_Value* ADDCALL sass_map_get_key(const union Sass_Value* v, size_t i) { return v->map.pairs[i].key; } |
70 | union Sass_Value* ADDCALL sass_map_get_value(const union Sass_Value* v, size_t i) { return v->map.pairs[i].value; } |
71 | void ADDCALL sass_map_set_key(union Sass_Value* v, size_t i, union Sass_Value* key) { v->map.pairs[i].key = key; } |
72 | void ADDCALL sass_map_set_value(union Sass_Value* v, size_t i, union Sass_Value* val) { v->map.pairs[i].value = val; } |
73 | |
74 | // Getters and setters for Sass_Error |
75 | char* ADDCALL sass_error_get_message(const union Sass_Value* v) { return v->error.message; }; |
76 | void ADDCALL sass_error_set_message(union Sass_Value* v, char* msg) { v->error.message = msg; }; |
77 | |
78 | // Getters and setters for Sass_Warning |
79 | char* ADDCALL sass_warning_get_message(const union Sass_Value* v) { return v->warning.message; }; |
80 | void ADDCALL sass_warning_set_message(union Sass_Value* v, char* msg) { v->warning.message = msg; }; |
81 | |
82 | // Creator functions for all value types |
83 | |
84 | union Sass_Value* ADDCALL sass_make_boolean(bool val) |
85 | { |
86 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
87 | if (v == 0) return 0; |
88 | v->boolean.tag = SASS_BOOLEAN; |
89 | v->boolean.value = val; |
90 | return v; |
91 | } |
92 | |
93 | union Sass_Value* ADDCALL sass_make_number(double val, const char* unit) |
94 | { |
95 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
96 | if (v == 0) return 0; |
97 | v->number.tag = SASS_NUMBER; |
98 | v->number.value = val; |
99 | v->number.unit = unit ? sass_copy_c_string(str: unit) : 0; |
100 | if (v->number.unit == 0) { free(ptr: v); return 0; } |
101 | return v; |
102 | } |
103 | |
104 | union Sass_Value* ADDCALL sass_make_color(double r, double g, double b, double a) |
105 | { |
106 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
107 | if (v == 0) return 0; |
108 | v->color.tag = SASS_COLOR; |
109 | v->color.r = r; |
110 | v->color.g = g; |
111 | v->color.b = b; |
112 | v->color.a = a; |
113 | return v; |
114 | } |
115 | |
116 | union Sass_Value* ADDCALL sass_make_string(const char* val) |
117 | { |
118 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
119 | if (v == 0) return 0; |
120 | v->string.quoted = false; |
121 | v->string.tag = SASS_STRING; |
122 | v->string.value = val ? sass_copy_c_string(str: val) : 0; |
123 | if (v->string.value == 0) { free(ptr: v); return 0; } |
124 | return v; |
125 | } |
126 | |
127 | union Sass_Value* ADDCALL sass_make_qstring(const char* val) |
128 | { |
129 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
130 | if (v == 0) return 0; |
131 | v->string.quoted = true; |
132 | v->string.tag = SASS_STRING; |
133 | v->string.value = val ? sass_copy_c_string(str: val) : 0; |
134 | if (v->string.value == 0) { free(ptr: v); return 0; } |
135 | return v; |
136 | } |
137 | |
138 | union Sass_Value* ADDCALL sass_make_list(size_t len, enum Sass_Separator sep, bool is_bracketed) |
139 | { |
140 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
141 | if (v == 0) return 0; |
142 | v->list.tag = SASS_LIST; |
143 | v->list.length = len; |
144 | v->list.separator = sep; |
145 | v->list.is_bracketed = is_bracketed; |
146 | v->list.values = (union Sass_Value**) calloc(nmemb: len, size: sizeof(union Sass_Value*)); |
147 | if (v->list.values == 0) { free(ptr: v); return 0; } |
148 | return v; |
149 | } |
150 | |
151 | union Sass_Value* ADDCALL sass_make_map(size_t len) |
152 | { |
153 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
154 | if (v == 0) return 0; |
155 | v->map.tag = SASS_MAP; |
156 | v->map.length = len; |
157 | v->map.pairs = (struct Sass_MapPair*) calloc(nmemb: len, size: sizeof(struct Sass_MapPair)); |
158 | if (v->map.pairs == 0) { free(ptr: v); return 0; } |
159 | return v; |
160 | } |
161 | |
162 | union Sass_Value* ADDCALL sass_make_null(void) |
163 | { |
164 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
165 | if (v == 0) return 0; |
166 | v->null.tag = SASS_NULL; |
167 | return v; |
168 | } |
169 | |
170 | union Sass_Value* ADDCALL sass_make_error(const char* msg) |
171 | { |
172 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
173 | if (v == 0) return 0; |
174 | v->error.tag = SASS_ERROR; |
175 | v->error.message = msg ? sass_copy_c_string(str: msg) : 0; |
176 | if (v->error.message == 0) { free(ptr: v); return 0; } |
177 | return v; |
178 | } |
179 | |
180 | union Sass_Value* ADDCALL sass_make_warning(const char* msg) |
181 | { |
182 | union Sass_Value* v = (Sass_Value*) calloc(nmemb: 1, size: sizeof(Sass_Value)); |
183 | if (v == 0) return 0; |
184 | v->warning.tag = SASS_WARNING; |
185 | v->warning.message = msg ? sass_copy_c_string(str: msg) : 0; |
186 | if (v->warning.message == 0) { free(ptr: v); return 0; } |
187 | return v; |
188 | } |
189 | |
190 | // will free all associated sass values |
191 | void ADDCALL sass_delete_value(union Sass_Value* val) { |
192 | |
193 | size_t i; |
194 | if (val == 0) return; |
195 | switch(val->unknown.tag) { |
196 | case SASS_NULL: { |
197 | } break; |
198 | case SASS_BOOLEAN: { |
199 | } break; |
200 | case SASS_NUMBER: { |
201 | free(ptr: val->number.unit); |
202 | } break; |
203 | case SASS_COLOR: { |
204 | } break; |
205 | case SASS_STRING: { |
206 | free(ptr: val->string.value); |
207 | } break; |
208 | case SASS_LIST: { |
209 | for (i=0; i<val->list.length; i++) { |
210 | sass_delete_value(val: val->list.values[i]); |
211 | } |
212 | free(ptr: val->list.values); |
213 | } break; |
214 | case SASS_MAP: { |
215 | for (i=0; i<val->map.length; i++) { |
216 | sass_delete_value(val: val->map.pairs[i].key); |
217 | sass_delete_value(val: val->map.pairs[i].value); |
218 | } |
219 | free(ptr: val->map.pairs); |
220 | } break; |
221 | case SASS_ERROR: { |
222 | free(ptr: val->error.message); |
223 | } break; |
224 | case SASS_WARNING: { |
225 | free(ptr: val->error.message); |
226 | } break; |
227 | default: break; |
228 | } |
229 | |
230 | free(ptr: val); |
231 | |
232 | } |
233 | |
234 | // Make a deep cloned copy of the given sass value |
235 | union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val) |
236 | { |
237 | |
238 | size_t i; |
239 | if (val == 0) return 0; |
240 | switch(val->unknown.tag) { |
241 | case SASS_NULL: { |
242 | return sass_make_null(); |
243 | } |
244 | case SASS_BOOLEAN: { |
245 | return sass_make_boolean(val: val->boolean.value); |
246 | } |
247 | case SASS_NUMBER: { |
248 | return sass_make_number(val: val->number.value, unit: val->number.unit); |
249 | } |
250 | case SASS_COLOR: { |
251 | return sass_make_color(r: val->color.r, g: val->color.g, b: val->color.b, a: val->color.a); |
252 | } |
253 | case SASS_STRING: { |
254 | return sass_string_is_quoted(v: val) ? sass_make_qstring(val: val->string.value) : sass_make_string(val: val->string.value); |
255 | } |
256 | case SASS_LIST: { |
257 | union Sass_Value* list = sass_make_list(len: val->list.length, sep: val->list.separator, is_bracketed: val->list.is_bracketed); |
258 | for (i = 0; i < list->list.length; i++) { |
259 | list->list.values[i] = sass_clone_value(val: val->list.values[i]); |
260 | } |
261 | return list; |
262 | } |
263 | case SASS_MAP: { |
264 | union Sass_Value* map = sass_make_map(len: val->map.length); |
265 | for (i = 0; i < val->map.length; i++) { |
266 | map->map.pairs[i].key = sass_clone_value(val: val->map.pairs[i].key); |
267 | map->map.pairs[i].value = sass_clone_value(val: val->map.pairs[i].value); |
268 | } |
269 | return map; |
270 | } |
271 | case SASS_ERROR: { |
272 | return sass_make_error(msg: val->error.message); |
273 | } |
274 | case SASS_WARNING: { |
275 | return sass_make_warning(msg: val->warning.message); |
276 | } |
277 | default: break; |
278 | } |
279 | |
280 | return 0; |
281 | |
282 | } |
283 | |
284 | union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* v, bool compressed, int precision) |
285 | { |
286 | ValueObj val = sass_value_to_ast_node(val: v); |
287 | Sass_Inspect_Options options(compressed ? COMPRESSED : NESTED, precision); |
288 | sass::string str(val->to_string(opt: options)); |
289 | return sass_make_qstring(val: str.c_str()); |
290 | } |
291 | |
292 | union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b) |
293 | { |
294 | |
295 | Sass::ValueObj rv; |
296 | |
297 | try { |
298 | |
299 | ValueObj lhs = sass_value_to_ast_node(val: a); |
300 | ValueObj rhs = sass_value_to_ast_node(val: b); |
301 | struct Sass_Inspect_Options options(NESTED, 5); |
302 | |
303 | // see if it's a relational expression |
304 | switch(op) { |
305 | case Sass_OP::EQ: return sass_make_boolean(val: Operators::eq(lhs, rhs)); |
306 | case Sass_OP::NEQ: return sass_make_boolean(val: Operators::neq(lhs, rhs)); |
307 | case Sass_OP::GT: return sass_make_boolean(val: Operators::gt(lhs, rhs)); |
308 | case Sass_OP::GTE: return sass_make_boolean(val: Operators::gte(lhs, rhs)); |
309 | case Sass_OP::LT: return sass_make_boolean(val: Operators::lt(lhs, rhs)); |
310 | case Sass_OP::LTE: return sass_make_boolean(val: Operators::lte(lhs, rhs)); |
311 | case Sass_OP::AND: return ast_node_to_sass_value(val: lhs->is_false() ? lhs : rhs); |
312 | case Sass_OP::OR: return ast_node_to_sass_value(val: lhs->is_false() ? rhs : lhs); |
313 | default: break; |
314 | } |
315 | |
316 | if (sass_value_is_number(v: a) && sass_value_is_number(v: b)) { |
317 | const Number* l_n = Cast<Number>(ptr: lhs); |
318 | const Number* r_n = Cast<Number>(ptr: rhs); |
319 | rv = Operators::op_numbers(op, *l_n, *r_n, opt: options, pstate: l_n->pstate()); |
320 | } |
321 | else if (sass_value_is_number(v: a) && sass_value_is_color(v: a)) { |
322 | const Number* l_n = Cast<Number>(ptr: lhs); |
323 | // Direct HSLA operations are not supported |
324 | // All color maths will be deprecated anyway |
325 | Color_RGBA_Obj r_c = Cast<Color>(ptr: rhs)->toRGBA(); |
326 | rv = Operators::op_number_color(op, *l_n, *r_c, opt: options, pstate: l_n->pstate()); |
327 | } |
328 | else if (sass_value_is_color(v: a) && sass_value_is_number(v: b)) { |
329 | // Direct HSLA operations are not supported |
330 | // All color maths will be deprecated anyway |
331 | Color_RGBA_Obj l_c = Cast<Color>(ptr: lhs)->toRGBA(); |
332 | const Number* r_n = Cast<Number>(ptr: rhs); |
333 | rv = Operators::op_color_number(op, *l_c, *r_n, opt: options, pstate: l_c->pstate()); |
334 | } |
335 | else if (sass_value_is_color(v: a) && sass_value_is_color(v: b)) { |
336 | // Direct HSLA operations are not supported |
337 | // All color maths will be deprecated anyway |
338 | Color_RGBA_Obj l_c = Cast<Color>(ptr: lhs)->toRGBA(); |
339 | Color_RGBA_Obj r_c = Cast<Color>(ptr: rhs)->toRGBA(); |
340 | rv = Operators::op_colors(op, *l_c, *r_c, opt: options, pstate: l_c->pstate()); |
341 | } |
342 | else /* convert other stuff to string and apply operation */ { |
343 | rv = Operators::op_strings(op, *lhs, *rhs, opt: options, pstate: lhs->pstate()); |
344 | } |
345 | |
346 | // ToDo: maybe we should return null value? |
347 | if (!rv) return sass_make_error(msg: "invalid return value" ); |
348 | |
349 | // convert result back to ast node |
350 | return ast_node_to_sass_value(val: rv.ptr()); |
351 | } |
352 | |
353 | // simply pass the error message back to the caller for now |
354 | catch (Exception::InvalidSass& e) { return sass_make_error(msg: e.what()); } |
355 | catch (std::bad_alloc&) { return sass_make_error(msg: "memory exhausted" ); } |
356 | catch (std::exception& e) { return sass_make_error(msg: e.what()); } |
357 | catch (sass::string& e) { return sass_make_error(msg: e.c_str()); } |
358 | catch (const char* e) { return sass_make_error(msg: e); } |
359 | catch (...) { return sass_make_error(msg: "unknown" ); } |
360 | } |
361 | |
362 | } |
363 | |