1// sass.hpp must go before all system headers to get the
2// __EXTENSIONS__ fix on Solaris.
3#include "sass.hpp"
4#include "ast.hpp"
5
6#include "remove_placeholders.hpp"
7#include "sass_functions.hpp"
8#include "check_nesting.hpp"
9#include "fn_selectors.hpp"
10#include "fn_strings.hpp"
11#include "fn_numbers.hpp"
12#include "fn_colors.hpp"
13#include "fn_miscs.hpp"
14#include "fn_lists.hpp"
15#include "fn_maps.hpp"
16#include "context.hpp"
17#include "expand.hpp"
18#include "parser.hpp"
19#include "cssize.hpp"
20#include "source.hpp"
21
22namespace Sass {
23 using namespace Constants;
24 using namespace File;
25 using namespace Sass;
26
27 inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
28 { return sass_importer_get_priority(cb: i) > sass_importer_get_priority(cb: j); }
29
30 static sass::string safe_input(const char* in_path)
31 {
32 if (in_path == nullptr || in_path[0] == '\0') return "stdin";
33 return in_path;
34 }
35
36 static sass::string safe_output(const char* out_path, sass::string input_path)
37 {
38 if (out_path == nullptr || out_path[0] == '\0') {
39 if (input_path.empty()) return "stdout";
40 return input_path.substr(pos: 0, n: input_path.find_last_of(s: ".")) + ".css";
41 }
42 return out_path;
43 }
44
45 Context::Context(struct Sass_Context& c_ctx)
46 : CWD(File::get_cwd()),
47 c_options(c_ctx),
48 entry_path(""),
49 head_imports(0),
50 plugins(),
51 emitter(c_options),
52
53 ast_gc(),
54 strings(),
55 resources(),
56 sheets(),
57 import_stack(),
58 callee_stack(),
59 traces(),
60 extender(Extender::NORMAL, traces),
61 c_compiler(NULL),
62
63 c_headers (sass::vector<Sass_Importer_Entry>()),
64 c_importers (sass::vector<Sass_Importer_Entry>()),
65 c_functions (sass::vector<Sass_Function_Entry>()),
66
67 indent (safe_str(c_options.indent, " ")),
68 linefeed (safe_str(c_options.linefeed, "\n")),
69
70 input_path (make_canonical_path(path: safe_input(in_path: c_options.input_path))),
71 output_path (make_canonical_path(path: safe_output(out_path: c_options.output_path, input_path))),
72 source_map_file (make_canonical_path(path: safe_str(c_options.source_map_file, ""))),
73 source_map_root (make_canonical_path(path: safe_str(c_options.source_map_root, "")))
74
75 {
76
77 // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default.
78 // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment.
79 // include_paths.push_back(CWD);
80
81 // collect more paths from different options
82 collect_include_paths(paths_str: c_options.include_path);
83 collect_include_paths(paths_array: c_options.include_paths);
84 collect_plugin_paths(paths_str: c_options.plugin_path);
85 collect_plugin_paths(paths_array: c_options.plugin_paths);
86
87 // load plugins and register custom behaviors
88 for(auto plug : plugin_paths) plugins.load_plugins(path: plug);
89 for(auto fn : plugins.get_headers()) c_headers.push_back(x: fn);
90 for(auto fn : plugins.get_importers()) c_importers.push_back(x: fn);
91 for(auto fn : plugins.get_functions()) c_functions.push_back(x: fn);
92
93 // sort the items by priority (lowest first)
94 sort (first: c_headers.begin(), last: c_headers.end(), comp: sort_importers);
95 sort (first: c_importers.begin(), last: c_importers.end(), comp: sort_importers);
96
97 emitter.set_filename(abs2rel(path: output_path, base: source_map_file, cwd: CWD));
98
99 }
100
101 void Context::add_c_function(Sass_Function_Entry function)
102 {
103 c_functions.push_back(x: function);
104 }
105 void Context::add_c_header(Sass_Importer_Entry header)
106 {
107 c_headers.push_back(x: header);
108 // need to sort the array afterwards (no big deal)
109 sort (first: c_headers.begin(), last: c_headers.end(), comp: sort_importers);
110 }
111 void Context::add_c_importer(Sass_Importer_Entry importer)
112 {
113 c_importers.push_back(x: importer);
114 // need to sort the array afterwards (no big deal)
115 sort (first: c_importers.begin(), last: c_importers.end(), comp: sort_importers);
116 }
117
118 Context::~Context()
119 {
120 // resources were allocated by malloc
121 for (size_t i = 0; i < resources.size(); ++i) {
122 free(ptr: resources[i].contents);
123 free(ptr: resources[i].srcmap);
124 }
125 // free all strings we kept alive during compiler execution
126 for (size_t n = 0; n < strings.size(); ++n) free(ptr: strings[n]);
127 // everything that gets put into sources will be freed by us
128 // this shouldn't have anything in it anyway!?
129 for (size_t m = 0; m < import_stack.size(); ++m) {
130 sass_import_take_source(import_stack[m]);
131 sass_import_take_srcmap(import_stack[m]);
132 sass_delete_import(import_stack[m]);
133 }
134 // clear inner structures (vectors) and input source
135 resources.clear(); import_stack.clear();
136 sheets.clear();
137 }
138
139 Data_Context::~Data_Context()
140 {
141 // --> this will be freed by resources
142 // make sure we free the source even if not processed!
143 // if (resources.size() == 0 && source_c_str) free(source_c_str);
144 // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);
145 // source_c_str = 0; srcmap_c_str = 0;
146 }
147
148 File_Context::~File_Context()
149 {
150 }
151
152 void Context::collect_include_paths(const char* paths_str)
153 {
154 if (paths_str) {
155 const char* beg = paths_str;
156 const char* end = Prelexer::find_first<PATH_SEP>(src: beg);
157
158 while (end) {
159 sass::string path(beg, end - beg);
160 if (!path.empty()) {
161 if (*path.rbegin() != '/') path += '/';
162 include_paths.push_back(x: path);
163 }
164 beg = end + 1;
165 end = Prelexer::find_first<PATH_SEP>(src: beg);
166 }
167
168 sass::string path(beg);
169 if (!path.empty()) {
170 if (*path.rbegin() != '/') path += '/';
171 include_paths.push_back(x: path);
172 }
173 }
174 }
175
176 void Context::collect_include_paths(string_list* paths_array)
177 {
178 while (paths_array)
179 {
180 collect_include_paths(paths_str: paths_array->string);
181 paths_array = paths_array->next;
182 }
183 }
184
185 void Context::collect_plugin_paths(const char* paths_str)
186 {
187 if (paths_str) {
188 const char* beg = paths_str;
189 const char* end = Prelexer::find_first<PATH_SEP>(src: beg);
190
191 while (end) {
192 sass::string path(beg, end - beg);
193 if (!path.empty()) {
194 if (*path.rbegin() != '/') path += '/';
195 plugin_paths.push_back(x: path);
196 }
197 beg = end + 1;
198 end = Prelexer::find_first<PATH_SEP>(src: beg);
199 }
200
201 sass::string path(beg);
202 if (!path.empty()) {
203 if (*path.rbegin() != '/') path += '/';
204 plugin_paths.push_back(x: path);
205 }
206 }
207 }
208
209 void Context::collect_plugin_paths(string_list* paths_array)
210 {
211 while (paths_array)
212 {
213 collect_plugin_paths(paths_str: paths_array->string);
214 paths_array = paths_array->next;
215 }
216 }
217
218 // resolve the imp_path in base_path or include_paths
219 // looks for alternatives and returns a list from one directory
220 sass::vector<Include> Context::find_includes(const Importer& import)
221 {
222 // make sure we resolve against an absolute path
223 sass::string base_path(rel2abs(path: import.base_path));
224 // first try to resolve the load path relative to the base path
225 sass::vector<Include> vec(resolve_includes(root: base_path, file: import.imp_path));
226 // then search in every include path (but only if nothing found yet)
227 for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)
228 {
229 // call resolve_includes and individual base path and append all results
230 sass::vector<Include> resolved(resolve_includes(root: include_paths[i], file: import.imp_path));
231 if (resolved.size()) vec.insert(position: vec.end(), first: resolved.begin(), last: resolved.end());
232 }
233 // return vector
234 return vec;
235 }
236
237 // register include with resolved path and its content
238 // memory of the resources will be freed by us on exit
239 void Context::register_resource(const Include& inc, const Resource& res)
240 {
241
242 // do not parse same resource twice
243 // maybe raise an error in this case
244 // if (sheets.count(inc.abs_path)) {
245 // free(res.contents); free(res.srcmap);
246 // throw std::runtime_error("duplicate resource registered");
247 // return;
248 // }
249
250 // get index for this resource
251 size_t idx = resources.size();
252
253 // tell emitter about new resource
254 emitter.add_source_index(idx);
255
256 // put resources under our control
257 // the memory will be freed later
258 resources.push_back(x: res);
259
260 // add a relative link to the working directory
261 included_files.push_back(x: inc.abs_path);
262 // add a relative link to the source map output file
263 srcmap_links.push_back(x: abs2rel(path: inc.abs_path, base: source_map_file, cwd: CWD));
264
265 // get pointer to the loaded content
266 Sass_Import_Entry import = sass_make_import(
267 imp_path: inc.imp_path.c_str(),
268 abs_base: inc.abs_path.c_str(),
269 source: res.contents,
270 srcmap: res.srcmap
271 );
272 // add the entry to the stack
273 import_stack.push_back(x: import);
274
275 // get pointer to the loaded content
276 const char* contents = resources[idx].contents;
277 SourceFileObj source = SASS_MEMORY_NEW(SourceFile,
278 inc.abs_path.c_str(), contents, idx);
279
280 // create the initial parser state from resource
281 SourceSpan pstate(source);
282
283 // check existing import stack for possible recursion
284 for (size_t i = 0; i < import_stack.size() - 2; ++i) {
285 auto parent = import_stack[i];
286 if (std::strcmp(s1: parent->abs_path, s2: import->abs_path) == 0) {
287 sass::string cwd(File::get_cwd());
288 // make path relative to the current directory
289 sass::string stack("An @import loop has been found:");
290 for (size_t n = 1; n < i + 2; ++n) {
291 stack += "\n " + sass::string(File::abs2rel(path: import_stack[n]->abs_path, base: cwd, cwd)) +
292 " imports " + sass::string(File::abs2rel(path: import_stack[n+1]->abs_path, base: cwd, cwd));
293 }
294 // implement error throw directly until we
295 // decided how to handle full stack traces
296 throw Exception::InvalidSyntax(pstate, traces, stack);
297 // error(stack, prstate ? *prstate : pstate, import_stack);
298 }
299 }
300
301 // create a parser instance from the given c_str buffer
302 Parser p(source, *this, traces);
303 // do not yet dispose these buffers
304 sass_import_take_source(import);
305 sass_import_take_srcmap(import);
306 // then parse the root block
307 Block_Obj root = p.parse();
308 // delete memory of current stack frame
309 sass_delete_import(import_stack.back());
310 // remove current stack frame
311 import_stack.pop_back();
312 // create key/value pair for ast node
313 std::pair<const sass::string, StyleSheet>
314 ast_pair(inc.abs_path, { res, root });
315 // register resulting resource
316 sheets.insert(x&: ast_pair);
317 }
318
319 // register include with resolved path and its content
320 // memory of the resources will be freed by us on exit
321 void Context::register_resource(const Include& inc, const Resource& res, SourceSpan& prstate)
322 {
323 traces.push_back(x: Backtrace(prstate));
324 register_resource(inc, res);
325 traces.pop_back();
326 }
327
328 // Add a new import to the context (called from `import_url`)
329 Include Context::load_import(const Importer& imp, SourceSpan pstate)
330 {
331
332 // search for valid imports (ie. partials) on the filesystem
333 // this may return more than one valid result (ambiguous imp_path)
334 const sass::vector<Include> resolved(find_includes(import: imp));
335
336 // error nicely on ambiguous imp_path
337 if (resolved.size() > 1) {
338 sass::ostream msg_stream;
339 msg_stream << "It's not clear which file to import for ";
340 msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";
341 msg_stream << "Candidates:" << "\n";
342 for (size_t i = 0, L = resolved.size(); i < L; ++i)
343 { msg_stream << " " << resolved[i].imp_path << "\n"; }
344 msg_stream << "Please delete or rename all but one of these files." << "\n";
345 error(msg: msg_stream.str(), pstate, traces);
346 }
347
348 // process the resolved entry
349 else if (resolved.size() == 1) {
350 bool use_cache = c_importers.size() == 0;
351 // use cache for the resource loading
352 if (use_cache && sheets.count(x: resolved[0].abs_path)) return resolved[0];
353 // try to read the content of the resolved file entry
354 // the memory buffer returned must be freed by us!
355 if (char* contents = read_file(file: resolved[0].abs_path)) {
356 // register the newly resolved file resource
357 register_resource(inc: resolved[0], res: { contents, 0 }, prstate&: pstate);
358 // return resolved entry
359 return resolved[0];
360 }
361 }
362
363 // nothing found
364 return { imp, "" };
365
366 }
367
368 void Context::import_url (Import* imp, sass::string load_path, const sass::string& ctx_path) {
369
370 SourceSpan pstate(imp->pstate());
371 sass::string imp_path(unquote(load_path));
372 sass::string protocol("file");
373
374 using namespace Prelexer;
375 if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(src: imp_path.c_str())) {
376
377 protocol = sass::string(imp_path.c_str(), proto - 3);
378 // if (protocol.compare("file") && true) { }
379 }
380
381 // add urls (protocol other than file) and urls without protocol to `urls` member
382 // ToDo: if ctx_path is already a file resource, we should not add it here?
383 if (imp->import_queries() || protocol != "file" || imp_path.substr(pos: 0, n: 2) == "//") {
384 imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path));
385 }
386 else if (imp_path.length() > 4 && imp_path.substr(pos: imp_path.length() - 4, n: 4) == ".css") {
387 String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));
388 Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);
389 Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);
390 loc_args->append(element: loc_arg);
391 Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), loc_args);
392 imp->urls().push_back(x: new_url);
393 }
394 else {
395 const Importer importer(imp_path, ctx_path);
396 Include include(load_import(imp: importer, pstate));
397 if (include.abs_path.empty()) {
398 error(msg: "File to import not found or unreadable: " + imp_path + ".", pstate, traces);
399 }
400 imp->incs().push_back(x: include);
401 }
402
403 }
404
405
406 // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet
407 bool Context::call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector<Sass_Importer_Entry> importers, bool only_one)
408 {
409 // unique counter
410 size_t count = 0;
411 // need one correct import
412 bool has_import = false;
413 // process all custom importers (or custom headers)
414 for (Sass_Importer_Entry& importer_ent : importers) {
415 // int priority = sass_importer_get_priority(importer);
416 Sass_Importer_Fn fn = sass_importer_get_function(cb: importer_ent);
417 // skip importer if it returns NULL
418 if (Sass_Import_List includes =
419 fn(load_path.c_str(), importer_ent, c_compiler)
420 ) {
421 // get c pointer copy to iterate over
422 Sass_Import_List it_includes = includes;
423 while (*it_includes) { ++count;
424 // create unique path to use as key
425 sass::string uniq_path = load_path;
426 if (!only_one && count) {
427 sass::ostream path_strm;
428 path_strm << uniq_path << ":" << count;
429 uniq_path = path_strm.str();
430 }
431 // create the importer struct
432 Importer importer(uniq_path, ctx_path);
433 // query data from the current include
434 Sass_Import_Entry include_ent = *it_includes;
435 char* source = sass_import_take_source(include_ent);
436 char* srcmap = sass_import_take_srcmap(include_ent);
437 size_t line = sass_import_get_error_line(include_ent);
438 size_t column = sass_import_get_error_column(include_ent);
439 const char *abs_path = sass_import_get_abs_path(include_ent);
440 // handle error message passed back from custom importer
441 // it may (or may not) override the line and column info
442 if (const char* err_message = sass_import_get_error_message(include_ent)) {
443 if (source || srcmap) register_resource(inc: { importer, uniq_path }, res: { source, srcmap }, prstate&: pstate);
444 if (line == sass::string::npos && column == sass::string::npos) error(msg: err_message, pstate, traces);
445 else { error(msg: err_message, pstate: { pstate.source, { line, column } }, traces); }
446 }
447 // content for import was set
448 else if (source) {
449 // resolved abs_path should be set by custom importer
450 // use the created uniq_path as fallback (maybe enforce)
451 sass::string path_key(abs_path ? abs_path : uniq_path);
452 // create the importer struct
453 Include include(importer, path_key);
454 // attach information to AST node
455 imp->incs().push_back(x: include);
456 // register the resource buffers
457 register_resource(inc: include, res: { source, srcmap }, prstate&: pstate);
458 }
459 // only a path was retuned
460 // try to load it like normal
461 else if(abs_path) {
462 // checks some urls to preserve
463 // `http://`, `https://` and `//`
464 // or dispatchs to `import_file`
465 // which will check for a `.css` extension
466 // or resolves the file on the filesystem
467 // added and resolved via `add_file`
468 // finally stores everything on `imp`
469 import_url(imp, load_path: abs_path, ctx_path);
470 }
471 // move to next
472 ++it_includes;
473 }
474 // deallocate the returned memory
475 sass_delete_import_list(includes);
476 // set success flag
477 has_import = true;
478 // break out of loop
479 if (only_one) break;
480 }
481 }
482 // return result
483 return has_import;
484 }
485
486 void register_function(Context&, Signature sig, Native_Function f, Env* env);
487 void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env);
488 void register_overload_stub(Context&, sass::string name, Env* env);
489 void register_built_in_functions(Context&, Env* env);
490 void register_c_functions(Context&, Env* env, Sass_Function_List);
491 void register_c_function(Context&, Env* env, Sass_Function_Entry);
492
493 char* Context::render(Block_Obj root)
494 {
495 // check for valid block
496 if (!root) return 0;
497 // start the render process
498 root->perform(op: &emitter);
499 // finish emitter stream
500 emitter.finalize();
501 // get the resulting buffer from stream
502 OutputBuffer emitted = emitter.get_buffer();
503 // should we append a source map url?
504 if (!c_options.omit_source_map_url) {
505 // generate an embedded source map
506 if (c_options.source_map_embed) {
507 emitted.buffer += linefeed;
508 emitted.buffer += format_embedded_source_map();
509 }
510 // or just link the generated one
511 else if (source_map_file != "") {
512 emitted.buffer += linefeed;
513 emitted.buffer += format_source_mapping_url(out_path: source_map_file);
514 }
515 }
516 // create a copy of the resulting buffer string
517 // this must be freed or taken over by implementor
518 return sass_copy_c_string(str: emitted.buffer.c_str());
519 }
520
521 void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, SourceSpan pstate)
522 {
523 // create a custom import to resolve headers
524 Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);
525 // dispatch headers which will add custom functions
526 // custom headers are added to the import instance
527 call_headers(load_path: entry_path, ctx_path, pstate, imp);
528 // increase head count to skip later
529 head_imports += resources.size() - 1;
530 // add the statement if we have urls
531 if (!imp->urls().empty()) root->append(element: imp);
532 // process all other resources (add Import_Stub nodes)
533 for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
534 root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));
535 }
536 }
537
538 Block_Obj File_Context::parse()
539 {
540
541 // check if entry file is given
542 if (input_path.empty()) return {};
543
544 // create absolute path from input filename
545 // ToDo: this should be resolved via custom importers
546 sass::string abs_path(rel2abs(path: input_path, base: CWD));
547
548 // try to load the entry file
549 char* contents = read_file(file: abs_path);
550
551 // alternatively also look inside each include path folder
552 // I think this differs from ruby sass (IMO too late to remove)
553 for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {
554 // build absolute path for this include path entry
555 abs_path = rel2abs(path: input_path, base: include_paths[i]);
556 // try to load the resulting path
557 contents = read_file(file: abs_path);
558 }
559
560 // abort early if no content could be loaded (various reasons)
561 if (!contents) throw std::runtime_error(
562 "File to read not found or unreadable: "
563 + std::string(input_path.c_str()));
564
565 // store entry path
566 entry_path = abs_path;
567
568 // create entry only for import stack
569 Sass_Import_Entry import = sass_make_import(
570 imp_path: input_path.c_str(),
571 abs_base: entry_path.c_str(),
572 source: contents,
573 srcmap: 0
574 );
575 // add the entry to the stack
576 import_stack.push_back(x: import);
577
578 // create the source entry for file entry
579 register_resource(inc: {{ input_path, "." }, abs_path }, res: { contents, 0 });
580
581 // create root ast tree node
582 return compile();
583
584 }
585
586 Block_Obj Data_Context::parse()
587 {
588
589 // check if source string is given
590 if (!source_c_str) return {};
591
592 // convert indented sass syntax
593 if(c_options.is_indented_syntax_src) {
594 // call sass2scss to convert the string
595 char * converted = sass2scss(sass: source_c_str,
596 // preserve the structure as much as possible
597 SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
598 // replace old source_c_str with converted
599 free(ptr: source_c_str); source_c_str = converted;
600 }
601
602 // remember entry path (defaults to stdin for string)
603 entry_path = input_path.empty() ? "stdin" : input_path;
604
605 // ToDo: this may be resolved via custom importers
606 sass::string abs_path(rel2abs(path: entry_path));
607 char* abs_path_c_str = sass_copy_c_string(str: abs_path.c_str());
608 strings.push_back(x: abs_path_c_str);
609
610 // create entry only for the import stack
611 Sass_Import_Entry import = sass_make_import(
612 imp_path: entry_path.c_str(),
613 abs_base: abs_path_c_str,
614 source: source_c_str,
615 srcmap: srcmap_c_str
616 );
617 // add the entry to the stack
618 import_stack.push_back(x: import);
619
620 // register a synthetic resource (path does not really exist, skip in includes)
621 register_resource(inc: {{ input_path, "." }, input_path }, res: { source_c_str, srcmap_c_str });
622
623 // create root ast tree node
624 return compile();
625 }
626
627 // parse root block from includes
628 Block_Obj Context::compile()
629 {
630 // abort if there is no data
631 if (resources.size() == 0) return {};
632 // get root block from the first style sheet
633 Block_Obj root = sheets.at(k: entry_path).root;
634 // abort on invalid root
635 if (root.isNull()) return {};
636 Env global; // create root environment
637 // register built-in functions on env
638 register_built_in_functions(*this, env: &global);
639 // register custom functions (defined via C-API)
640 for (size_t i = 0, S = c_functions.size(); i < S; ++i)
641 { register_c_function(*this, env: &global, c_functions[i]); }
642 // create initial backtrace entry
643 // create crtp visitor objects
644 Expand expand(*this, &global);
645 Cssize cssize(*this);
646 CheckNesting check_nesting;
647 // check nesting in all files
648 for (auto sheet : sheets) {
649 auto styles = sheet.second;
650 check_nesting(styles.root);
651 }
652 // expand and eval the tree
653 root = expand(root);
654
655 Extension unsatisfied;
656 // check that all extends were used
657 if (extender.checkForUnsatisfiedExtends(unsatisfied)) {
658 throw Exception::UnsatisfiedExtend(traces, unsatisfied);
659 }
660
661 // check nesting
662 check_nesting(root);
663 // merge and bubble certain rules
664 root = cssize(root);
665
666 // clean up by removing empty placeholders
667 // ToDo: maybe we can do this somewhere else?
668 Remove_Placeholders remove_placeholders;
669 root->perform(op: &remove_placeholders);
670
671 // return processed tree
672 return root;
673 }
674 // EO compile
675
676 sass::string Context::format_embedded_source_map()
677 {
678 sass::string map = emitter.render_srcmap(ctx&: *this);
679 sass::istream is( map.c_str() );
680 sass::ostream buffer;
681 base64::encoder E;
682 E.encode(istream_in&: is, ostream_in&: buffer);
683 sass::string url = "data:application/json;base64," + buffer.str();
684 url.erase(pos: url.size() - 1);
685 return "/*# sourceMappingURL=" + url + " */";
686 }
687
688 sass::string Context::format_source_mapping_url(const sass::string& file)
689 {
690 sass::string url = abs2rel(path: file, base: output_path, cwd: CWD);
691 return "/*# sourceMappingURL=" + url + " */";
692 }
693
694 char* Context::render_srcmap()
695 {
696 if (source_map_file == "") return 0;
697 sass::string map = emitter.render_srcmap(ctx&: *this);
698 return sass_copy_c_string(str: map.c_str());
699 }
700
701
702 // for data context we want to start after "stdin"
703 // we probably always want to skip the header includes?
704 sass::vector<sass::string> Context::get_included_files(bool skip, size_t headers)
705 {
706 // create a copy of the vector for manipulations
707 sass::vector<sass::string> includes = included_files;
708 if (includes.size() == 0) return includes;
709 if (skip) { includes.erase( first: includes.begin(), last: includes.begin() + 1 + headers); }
710 else { includes.erase( first: includes.begin() + 1, last: includes.begin() + 1 + headers); }
711 includes.erase( first: std::unique( first: includes.begin(), last: includes.end() ), last: includes.end() );
712 std::sort( first: includes.begin() + (skip ? 0 : 1), last: includes.end() );
713 return includes;
714 }
715
716 void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)
717 {
718 Definition* def = make_native_function(sig, f, ctx);
719 def->environment(environment__: env);
720 (*env)[def->name() + "[f]"] = def;
721 }
722
723 void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env)
724 {
725 Definition* def = make_native_function(sig, f, ctx);
726 sass::ostream ss;
727 ss << def->name() << "[f]" << arity;
728 def->environment(environment__: env);
729 (*env)[ss.str()] = def;
730 }
731
732 void register_overload_stub(Context& ctx, sass::string name, Env* env)
733 {
734 Definition* stub = SASS_MEMORY_NEW(Definition,
735 SourceSpan{ "[built-in function]" },
736 nullptr,
737 name,
738 Parameters_Obj{},
739 nullptr,
740 true);
741 (*env)[name + "[f]"] = stub;
742 }
743
744
745 void register_built_in_functions(Context& ctx, Env* env)
746 {
747 using namespace Functions;
748 // RGB Functions
749 register_function(ctx, sig: rgb_sig, f: rgb, env);
750 register_overload_stub(ctx, name: "rgba", env);
751 register_function(ctx, sig: rgba_4_sig, f: rgba_4, arity: 4, env);
752 register_function(ctx, sig: rgba_2_sig, f: rgba_2, arity: 2, env);
753 register_function(ctx, sig: red_sig, f: red, env);
754 register_function(ctx, sig: green_sig, f: green, env);
755 register_function(ctx, sig: blue_sig, f: blue, env);
756 register_function(ctx, sig: mix_sig, f: mix, env);
757 // HSL Functions
758 register_function(ctx, sig: hsl_sig, f: hsl, env);
759 register_function(ctx, sig: hsla_sig, f: hsla, env);
760 register_function(ctx, sig: hue_sig, f: hue, env);
761 register_function(ctx, sig: saturation_sig, f: saturation, env);
762 register_function(ctx, sig: lightness_sig, f: lightness, env);
763 register_function(ctx, sig: adjust_hue_sig, f: adjust_hue, env);
764 register_function(ctx, sig: lighten_sig, f: lighten, env);
765 register_function(ctx, sig: darken_sig, f: darken, env);
766 register_function(ctx, sig: saturate_sig, f: saturate, env);
767 register_function(ctx, sig: desaturate_sig, f: desaturate, env);
768 register_function(ctx, sig: grayscale_sig, f: grayscale, env);
769 register_function(ctx, sig: complement_sig, f: complement, env);
770 register_function(ctx, sig: invert_sig, f: invert, env);
771 // Opacity Functions
772 register_function(ctx, sig: alpha_sig, f: alpha, env);
773 register_function(ctx, sig: opacity_sig, f: alpha, env);
774 register_function(ctx, sig: opacify_sig, f: opacify, env);
775 register_function(ctx, sig: fade_in_sig, f: opacify, env);
776 register_function(ctx, sig: transparentize_sig, f: transparentize, env);
777 register_function(ctx, sig: fade_out_sig, f: transparentize, env);
778 // Other Color Functions
779 register_function(ctx, sig: adjust_color_sig, f: adjust_color, env);
780 register_function(ctx, sig: scale_color_sig, f: scale_color, env);
781 register_function(ctx, sig: change_color_sig, f: change_color, env);
782 register_function(ctx, sig: ie_hex_str_sig, f: ie_hex_str, env);
783 // String Functions
784 register_function(ctx, sig: unquote_sig, f: sass_unquote, env);
785 register_function(ctx, sig: quote_sig, f: sass_quote, env);
786 register_function(ctx, sig: str_length_sig, f: str_length, env);
787 register_function(ctx, sig: str_insert_sig, f: str_insert, env);
788 register_function(ctx, sig: str_index_sig, f: str_index, env);
789 register_function(ctx, sig: str_slice_sig, f: str_slice, env);
790 register_function(ctx, sig: to_upper_case_sig, f: to_upper_case, env);
791 register_function(ctx, sig: to_lower_case_sig, f: to_lower_case, env);
792 // Number Functions
793 register_function(ctx, sig: percentage_sig, f: percentage, env);
794 register_function(ctx, sig: round_sig, f: round, env);
795 register_function(ctx, sig: ceil_sig, f: ceil, env);
796 register_function(ctx, sig: floor_sig, f: floor, env);
797 register_function(ctx, sig: abs_sig, f: abs, env);
798 register_function(ctx, sig: min_sig, f: min, env);
799 register_function(ctx, sig: max_sig, f: max, env);
800 register_function(ctx, sig: random_sig, f: random, env);
801 // List Functions
802 register_function(ctx, sig: length_sig, f: length, env);
803 register_function(ctx, sig: nth_sig, f: nth, env);
804 register_function(ctx, sig: set_nth_sig, f: set_nth, env);
805 register_function(ctx, sig: index_sig, f: index, env);
806 register_function(ctx, sig: join_sig, f: join, env);
807 register_function(ctx, sig: append_sig, f: append, env);
808 register_function(ctx, sig: zip_sig, f: zip, env);
809 register_function(ctx, sig: list_separator_sig, f: list_separator, env);
810 register_function(ctx, sig: is_bracketed_sig, f: is_bracketed, env);
811 // Map Functions
812 register_function(ctx, sig: map_get_sig, f: map_get, env);
813 register_function(ctx, sig: map_merge_sig, f: map_merge, env);
814 register_function(ctx, sig: map_remove_sig, f: map_remove, env);
815 register_function(ctx, sig: map_keys_sig, f: map_keys, env);
816 register_function(ctx, sig: map_values_sig, f: map_values, env);
817 register_function(ctx, sig: map_has_key_sig, f: map_has_key, env);
818 register_function(ctx, sig: keywords_sig, f: keywords, env);
819 // Introspection Functions
820 register_function(ctx, sig: type_of_sig, f: type_of, env);
821 register_function(ctx, sig: unit_sig, f: unit, env);
822 register_function(ctx, sig: unitless_sig, f: unitless, env);
823 register_function(ctx, sig: comparable_sig, f: comparable, env);
824 register_function(ctx, sig: variable_exists_sig, f: variable_exists, env);
825 register_function(ctx, sig: global_variable_exists_sig, f: global_variable_exists, env);
826 register_function(ctx, sig: function_exists_sig, f: function_exists, env);
827 register_function(ctx, sig: mixin_exists_sig, f: mixin_exists, env);
828 register_function(ctx, sig: feature_exists_sig, f: feature_exists, env);
829 register_function(ctx, sig: call_sig, f: call, env);
830 register_function(ctx, sig: content_exists_sig, f: content_exists, env);
831 register_function(ctx, sig: get_function_sig, f: get_function, env);
832 // Boolean Functions
833 register_function(ctx, sig: not_sig, f: sass_not, env);
834 register_function(ctx, sig: if_sig, f: sass_if, env);
835 // Misc Functions
836 register_function(ctx, sig: inspect_sig, f: inspect, env);
837 register_function(ctx, sig: unique_id_sig, f: unique_id, env);
838 // Selector functions
839 register_function(ctx, sig: selector_nest_sig, f: selector_nest, env);
840 register_function(ctx, sig: selector_append_sig, f: selector_append, env);
841 register_function(ctx, sig: selector_extend_sig, f: selector_extend, env);
842 register_function(ctx, sig: selector_replace_sig, f: selector_replace, env);
843 register_function(ctx, sig: selector_unify_sig, f: selector_unify, env);
844 register_function(ctx, sig: is_superselector_sig, f: is_superselector, env);
845 register_function(ctx, sig: simple_selectors_sig, f: simple_selectors, env);
846 register_function(ctx, sig: selector_parse_sig, f: selector_parse, env);
847 }
848
849 void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)
850 {
851 while (descrs && *descrs) {
852 register_c_function(ctx, env, *descrs);
853 ++descrs;
854 }
855 }
856 void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)
857 {
858 Definition* def = make_c_function(c_func: descr, ctx);
859 def->environment(environment__: env);
860 (*env)[def->name() + "[f]"] = def;
861 }
862
863}
864

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