12 #include "context.hpp"
13 #include "plugins.hpp"
14 #include "constants.hpp"
17 #include "inspect.hpp"
21 #include "check_nesting.hpp"
23 #include "listize.hpp"
25 #include "remove_placeholders.hpp"
26 #include "functions.hpp"
27 #include "sass_functions.hpp"
28 #include "backtrace.hpp"
29 #include "sass2scss.h"
30 #include "prelexer.hpp"
31 #include "emitter.hpp"
34 using namespace Constants;
38 inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
39 { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
41 static std::string safe_input(const char* in_path)
43 // enforce some safe defaults
44 // used to create relative file links
45 std::string safe_path(in_path ? in_path : "");
46 return safe_path == "" ? "stdin" : safe_path;
49 static std::string safe_output(const char* out_path, const std::string& input_path = "")
51 std::string safe_path(out_path ? out_path : "");
52 // maybe we can extract an output path from input path
53 if (safe_path == "" && input_path != "") {
54 int lastindex = static_cast<int>(input_path.find_last_of("."));
55 return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";
57 // enforce some safe defaults
58 // used to create relative file links
59 return safe_path == "" ? "stdout" : safe_path;
62 Context::Context(struct Sass_Context& c_ctx)
63 : CWD(File::get_cwd()),
76 c_headers (std::vector<Sass_Importer_Entry>()),
77 c_importers (std::vector<Sass_Importer_Entry>()),
78 c_functions (std::vector<Sass_Function_Entry>()),
80 indent (safe_str(c_options.indent, " ")),
81 linefeed (safe_str(c_options.linefeed, "\n")),
83 input_path (make_canonical_path(safe_input(c_options.input_path))),
84 output_path (make_canonical_path(safe_output(c_options.output_path, input_path))),
85 source_map_file (make_canonical_path(safe_str(c_options.source_map_file, ""))),
86 source_map_root (make_canonical_path(safe_str(c_options.source_map_root, "")))
90 // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default.
91 // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment.
92 // include_paths.push_back(CWD);
94 // collect more paths from different options
95 collect_include_paths(c_options.include_path);
96 collect_include_paths(c_options.include_paths);
97 collect_plugin_paths(c_options.plugin_path);
98 collect_plugin_paths(c_options.plugin_paths);
100 // load plugins and register custom behaviors
101 for(auto plug : plugin_paths) plugins.load_plugins(plug);
102 for(auto fn : plugins.get_headers()) c_headers.push_back(fn);
103 for(auto fn : plugins.get_importers()) c_importers.push_back(fn);
104 for(auto fn : plugins.get_functions()) c_functions.push_back(fn);
106 // sort the items by priority (lowest first)
107 sort (c_headers.begin(), c_headers.end(), sort_importers);
108 sort (c_importers.begin(), c_importers.end(), sort_importers);
110 emitter.set_filename(abs2rel(output_path, source_map_file, CWD));
114 void Context::add_c_function(Sass_Function_Entry function)
116 c_functions.push_back(function);
118 void Context::add_c_header(Sass_Importer_Entry header)
120 c_headers.push_back(header);
121 // need to sort the array afterwards (no big deal)
122 sort (c_headers.begin(), c_headers.end(), sort_importers);
124 void Context::add_c_importer(Sass_Importer_Entry importer)
126 c_importers.push_back(importer);
127 // need to sort the array afterwards (no big deal)
128 sort (c_importers.begin(), c_importers.end(), sort_importers);
133 // resources were allocated by strdup or malloc
134 for (size_t i = 0; i < resources.size(); ++i) {
135 free(resources[i].contents);
136 free(resources[i].srcmap);
138 // free all strings we kept alive during compiler execution
139 for (size_t n = 0; n < strings.size(); ++n) free(strings[n]);
140 // everything that gets put into sources will be freed by us
141 // this shouldn't have anything in it anyway!?
142 for (size_t m = 0; m < import_stack.size(); ++m) {
143 sass_import_take_source(import_stack[m]);
144 sass_import_take_srcmap(import_stack[m]);
145 sass_delete_import(import_stack[m]);
147 // clear inner structures (vectors) and input source
148 resources.clear(); import_stack.clear();
149 subset_map.clear(), sheets.clear();
152 Data_Context::~Data_Context()
154 // --> this will be freed by resources
155 // make sure we free the source even if not processed!
156 // if (resources.size() == 0 && source_c_str) free(source_c_str);
157 // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);
158 // source_c_str = 0; srcmap_c_str = 0;
161 File_Context::~File_Context()
165 void Context::collect_include_paths(const char* paths_str)
168 const char* beg = paths_str;
169 const char* end = Prelexer::find_first<PATH_SEP>(beg);
172 std::string path(beg, end - beg);
174 if (*path.rbegin() != '/') path += '/';
175 include_paths.push_back(path);
178 end = Prelexer::find_first<PATH_SEP>(beg);
181 std::string path(beg);
183 if (*path.rbegin() != '/') path += '/';
184 include_paths.push_back(path);
189 void Context::collect_include_paths(string_list* paths_array)
193 collect_include_paths(paths_array->string);
194 paths_array = paths_array->next;
198 void Context::collect_plugin_paths(const char* paths_str)
201 const char* beg = paths_str;
202 const char* end = Prelexer::find_first<PATH_SEP>(beg);
205 std::string path(beg, end - beg);
207 if (*path.rbegin() != '/') path += '/';
208 plugin_paths.push_back(path);
211 end = Prelexer::find_first<PATH_SEP>(beg);
214 std::string path(beg);
216 if (*path.rbegin() != '/') path += '/';
217 plugin_paths.push_back(path);
222 void Context::collect_plugin_paths(string_list* paths_array)
226 collect_plugin_paths(paths_array->string);
227 paths_array = paths_array->next;
231 // resolve the imp_path in base_path or include_paths
232 // looks for alternatives and returns a list from one directory
233 std::vector<Include> Context::find_includes(const Importer& import)
235 // make sure we resolve against an absolute path
236 std::string base_path(rel2abs(import.base_path));
237 // first try to resolve the load path relative to the base path
238 std::vector<Include> vec(resolve_includes(base_path, import.imp_path));
239 // then search in every include path (but only if nothing found yet)
240 for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)
242 // call resolve_includes and individual base path and append all results
243 std::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path));
244 if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end());
251 // register include with resolved path and its content
252 // memory of the resources will be freed by us on exit
253 void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate)
256 // do not parse same resource twice
257 // maybe raise an error in this case
258 // if (sheets.count(inc.abs_path)) {
259 // free(res.contents); free(res.srcmap);
260 // throw std::runtime_error("duplicate resource registered");
264 // get index for this resource
265 size_t idx = resources.size();
267 // tell emitter about new resource
268 emitter.add_source_index(idx);
270 // put resources under our control
271 // the memory will be freed later
272 resources.push_back(res);
274 // add a relative link to the working directory
275 included_files.push_back(inc.abs_path);
276 // add a relative link to the source map output file
277 srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD));
279 // get pointer to the loaded content
280 Sass_Import_Entry import = sass_make_import(
281 inc.imp_path.c_str(),
282 inc.abs_path.c_str(),
286 // add the entry to the stack
287 import_stack.push_back(import);
289 // get pointer to the loaded content
290 const char* contents = resources[idx].contents;
291 // keep a copy of the path around (for parserstates)
292 // ToDo: we clean it, but still not very elegant!?
293 strings.push_back(sass_copy_c_string(inc.abs_path.c_str()));
294 // create the initial parser state from resource
295 ParserState pstate(strings.back(), contents, idx);
297 // check existing import stack for possible recursion
298 for (size_t i = 0; i < import_stack.size() - 2; ++i) {
299 auto parent = import_stack[i];
300 if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
301 std::string stack("An @import loop has been found:");
302 for (size_t n = 1; n < i + 2; ++n) {
303 stack += "\n " + std::string(import_stack[n]->imp_path) +
304 " imports " + std::string(import_stack[n+1]->imp_path);
306 // implement error throw directly until we
307 // decided how to handle full stack traces
308 ParserState state = prstate ? *prstate : pstate;
309 throw Exception::InvalidSyntax(state, stack, &import_stack);
310 // error(stack, prstate ? *prstate : pstate, import_stack);
314 // create a parser instance from the given c_str buffer
315 Parser p(Parser::from_c_str(contents, *this, pstate));
316 // do not yet dispose these buffers
317 sass_import_take_source(import);
318 sass_import_take_srcmap(import);
319 // then parse the root block
320 Block_Obj root = p.parse();
321 // delete memory of current stack frame
322 sass_delete_import(import_stack.back());
323 // remove current stack frame
324 import_stack.pop_back();
325 // create key/value pair for ast node
326 std::pair<const std::string, StyleSheet>
327 ast_pair(inc.abs_path, { res, root });
328 // register resulting resource
329 sheets.insert(ast_pair);
333 // Add a new import to the context (called from `import_url`)
334 Include Context::load_import(const Importer& imp, ParserState pstate)
337 // search for valid imports (ie. partials) on the filesystem
338 // this may return more than one valid result (ambiguous imp_path)
339 const std::vector<Include> resolved(find_includes(imp));
341 // error nicely on ambiguous imp_path
342 if (resolved.size() > 1) {
343 std::stringstream msg_stream;
344 msg_stream << "It's not clear which file to import for ";
345 msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";
346 msg_stream << "Candidates:" << "\n";
347 for (size_t i = 0, L = resolved.size(); i < L; ++i)
348 { msg_stream << " " << resolved[i].imp_path << "\n"; }
349 msg_stream << "Please delete or rename all but one of these files." << "\n";
350 error(msg_stream.str(), pstate);
353 // process the resolved entry
354 else if (resolved.size() == 1) {
355 bool use_cache = c_importers.size() == 0;
356 // use cache for the resource loading
357 if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0];
358 // try to read the content of the resolved file entry
359 // the memory buffer returned must be freed by us!
360 if (char* contents = read_file(resolved[0].abs_path)) {
361 // register the newly resolved file resource
362 register_resource(resolved[0], { contents, 0 }, &pstate);
363 // return resolved entry
373 void Context::import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path) {
375 ParserState pstate(imp->pstate());
376 std::string imp_path(unquote(load_path));
377 std::string protocol("file");
379 using namespace Prelexer;
380 if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {
382 protocol = std::string(imp_path.c_str(), proto - 3);
383 // if (protocol.compare("file") && true) { }
386 // add urls (protocol other than file) and urls without procotol to `urls` member
387 // ToDo: if ctx_path is already a file resource, we should not add it here?
388 if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {
389 imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path));
391 else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {
392 String_Constant_Ptr loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));
393 Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);
394 Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);
395 loc_args->append(loc_arg);
396 Function_Call_Ptr new_url = SASS_MEMORY_NEW(Function_Call, pstate, "url", loc_args);
397 imp->urls().push_back(new_url);
400 const Importer importer(imp_path, ctx_path);
401 Include include(load_import(importer, pstate));
402 if (include.abs_path.empty()) {
403 error("File to import not found or unreadable: " + imp_path + ".\nParent style sheet: " + ctx_path, pstate);
405 imp->incs().push_back(include);
411 // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet
412 bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one)
416 // need one correct import
417 bool has_import = false;
418 // process all custom importers (or custom headers)
419 for (Sass_Importer_Entry& importer : importers) {
420 // int priority = sass_importer_get_priority(importer);
421 Sass_Importer_Fn fn = sass_importer_get_function(importer);
422 // skip importer if it returns NULL
423 if (Sass_Import_List includes =
424 fn(load_path.c_str(), importer, c_compiler)
426 // get c pointer copy to iterate over
427 Sass_Import_List it_includes = includes;
428 while (*it_includes) { ++count;
429 // create unique path to use as key
430 std::string uniq_path = load_path;
431 if (!only_one && count) {
432 std::stringstream path_strm;
433 path_strm << uniq_path << ":" << count;
434 uniq_path = path_strm.str();
436 // create the importer struct
437 Importer importer(uniq_path, ctx_path);
438 // query data from the current include
439 Sass_Import_Entry include = *it_includes;
440 char* source = sass_import_take_source(include);
441 char* srcmap = sass_import_take_srcmap(include);
442 size_t line = sass_import_get_error_line(include);
443 size_t column = sass_import_get_error_column(include);
444 const char *abs_path = sass_import_get_abs_path(include);
445 // handle error message passed back from custom importer
446 // it may (or may not) override the line and column info
447 if (const char* err_message = sass_import_get_error_message(include)) {
448 if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate);
449 if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);
450 else error(err_message, ParserState(ctx_path, source, Position(line, column)));
452 // content for import was set
454 // resolved abs_path should be set by custom importer
455 // use the created uniq_path as fallback (maybe enforce)
456 std::string path_key(abs_path ? abs_path : uniq_path);
457 // create the importer struct
458 Include include(importer, path_key);
459 // attach information to AST node
460 imp->incs().push_back(include);
461 // register the resource buffers
462 register_resource(include, { source, srcmap }, &pstate);
464 // only a path was retuned
465 // try to load it like normal
467 // checks some urls to preserve
468 // `http://`, `https://` and `//`
469 // or dispatchs to `import_file`
470 // which will check for a `.css` extension
471 // or resolves the file on the filesystem
472 // added and resolved via `add_file`
473 // finally stores everything on `imp`
474 import_url(imp, abs_path, ctx_path);
479 // deallocate the returned memory
480 sass_delete_import_list(includes);
491 void register_function(Context&, Signature sig, Native_Function f, Env* env);
492 void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env);
493 void register_overload_stub(Context&, std::string name, Env* env);
494 void register_built_in_functions(Context&, Env* env);
495 void register_c_functions(Context&, Env* env, Sass_Function_List);
496 void register_c_function(Context&, Env* env, Sass_Function_Entry);
498 char* Context::render(Block_Obj root)
500 // check for valid block
502 // start the render process
503 root->perform(&emitter);
504 // finish emitter stream
506 // get the resulting buffer from stream
507 OutputBuffer emitted = emitter.get_buffer();
508 // should we append a source map url?
509 if (!c_options.omit_source_map_url) {
510 // generate an embeded source map
511 if (c_options.source_map_embed) {
512 emitted.buffer += linefeed;
513 emitted.buffer += format_embedded_source_map();
515 // or just link the generated one
516 else if (source_map_file != "") {
517 emitted.buffer += linefeed;
518 emitted.buffer += format_source_mapping_url(source_map_file);
521 // create a copy of the resulting buffer string
522 // this must be freed or taken over by implementor
523 return sass_copy_c_string(emitted.buffer.c_str());
526 void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, ParserState pstate)
528 // create a custom import to resolve headers
529 Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);
530 // dispatch headers which will add custom functions
531 // custom headers are added to the import instance
532 call_headers(entry_path, ctx_path, pstate, imp);
533 // increase head count to skip later
534 head_imports += resources.size() - 1;
535 // add the statement if we have urls
536 if (!imp->urls().empty()) root->append(imp);
537 // process all other resources (add Import_Stub nodes)
538 for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
539 root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));
543 Block_Obj File_Context::parse()
546 // check if entry file is given
547 if (input_path.empty()) return 0;
549 // create absolute path from input filename
550 // ToDo: this should be resolved via custom importers
551 std::string abs_path(rel2abs(input_path, CWD));
553 // try to load the entry file
554 char* contents = read_file(abs_path);
556 // alternatively also look inside each include path folder
557 // I think this differs from ruby sass (IMO too late to remove)
558 for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {
559 // build absolute path for this include path entry
560 abs_path = rel2abs(input_path, include_paths[i]);
561 // try to load the resulting path
562 contents = read_file(abs_path);
565 // abort early if no content could be loaded (various reasons)
566 if (!contents) throw std::runtime_error("File to read not found or unreadable: " + input_path);
569 entry_path = abs_path;
571 // create entry only for import stack
572 Sass_Import_Entry import = sass_make_import(
578 // add the entry to the stack
579 import_stack.push_back(import);
581 // create the source entry for file entry
582 register_resource({{ input_path, "." }, abs_path }, { contents, 0 });
584 // create root ast tree node
589 Block_Obj Data_Context::parse()
592 // check if source string is given
593 if (!source_c_str) return 0;
595 // convert indented sass syntax
596 if(c_options.is_indented_syntax_src) {
597 // call sass2scss to convert the string
598 char * converted = sass2scss(source_c_str,
599 // preserve the structure as much as possible
600 SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
601 // replace old source_c_str with converted
602 free(source_c_str); source_c_str = converted;
605 // remember entry path (defaults to stdin for string)
606 entry_path = input_path.empty() ? "stdin" : input_path;
608 // ToDo: this may be resolved via custom importers
609 std::string abs_path(rel2abs(entry_path));
610 char* abs_path_c_str = sass_copy_c_string(abs_path.c_str());
611 strings.push_back(abs_path_c_str);
613 // create entry only for the import stack
614 Sass_Import_Entry import = sass_make_import(
620 // add the entry to the stack
621 import_stack.push_back(import);
623 // register a synthetic resource (path does not really exist, skip in includes)
624 register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str });
626 // create root ast tree node
632 // parse root block from includes
633 Block_Obj Context::compile()
635 // abort if there is no data
636 if (resources.size() == 0) return 0;
637 // get root block from the first style sheet
638 Block_Obj root = sheets.at(entry_path).root;
639 // abort on invalid root
640 if (root.isNull()) return 0;
641 Env global; // create root environment
642 // register built-in functions on env
643 register_built_in_functions(*this, &global);
644 // register custom functions (defined via C-API)
645 for (size_t i = 0, S = c_functions.size(); i < S; ++i)
646 { register_c_function(*this, &global, c_functions[i]); }
647 // create initial backtrace entry
648 Backtrace backtrace(0, ParserState("", 0), "");
649 // create crtp visitor objects
650 Expand expand(*this, &global, &backtrace);
651 Cssize cssize(*this, &backtrace);
652 CheckNesting check_nesting;
655 // expand and eval the tree
659 // merge and bubble certain rules
661 // should we extend something?
662 if (!subset_map.empty()) {
663 // create crtp visitor object
664 Extend extend(*this, subset_map);
669 // clean up by removing empty placeholders
670 // ToDo: maybe we can do this somewhere else?
671 Remove_Placeholders remove_placeholders;
672 root->perform(&remove_placeholders);
673 // return processed tree
678 std::string Context::format_embedded_source_map()
680 std::string map = emitter.render_srcmap(*this);
681 std::istringstream is( map );
682 std::ostringstream buffer;
684 E.encode(is, buffer);
685 std::string url = "data:application/json;base64," + buffer.str();
686 url.erase(url.size() - 1);
687 return "/*# sourceMappingURL=" + url + " */";
690 std::string Context::format_source_mapping_url(const std::string& file)
692 std::string url = abs2rel(file, output_path, CWD);
693 return "/*# sourceMappingURL=" + url + " */";
696 char* Context::render_srcmap()
698 if (source_map_file == "") return 0;
700 std::string map = emitter.render_srcmap(*this);
701 result = sass_copy_c_string(map.c_str());
706 // for data context we want to start after "stdin"
707 // we probably always want to skip the header includes?
708 std::vector<std::string> Context::get_included_files(bool skip, size_t headers)
710 // create a copy of the vector for manipulations
711 std::vector<std::string> includes = included_files;
712 if (includes.size() == 0) return includes;
713 if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); }
714 else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); }
715 includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() );
716 std::sort( includes.begin() + (skip ? 0 : 1), includes.end() );
720 void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)
722 Definition_Ptr def = make_native_function(sig, f, ctx);
723 def->environment(env);
724 (*env)[def->name() + "[f]"] = def;
727 void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env)
729 Definition_Ptr def = make_native_function(sig, f, ctx);
730 std::stringstream ss;
731 ss << def->name() << "[f]" << arity;
732 def->environment(env);
733 (*env)[ss.str()] = def;
736 void register_overload_stub(Context& ctx, std::string name, Env* env)
738 Definition_Ptr stub = SASS_MEMORY_NEW(Definition,
739 ParserState("[built-in function]"),
745 (*env)[name + "[f]"] = stub;
749 void register_built_in_functions(Context& ctx, Env* env)
751 using namespace Functions;
753 register_function(ctx, rgb_sig, rgb, env);
754 register_overload_stub(ctx, "rgba", env);
755 register_function(ctx, rgba_4_sig, rgba_4, 4, env);
756 register_function(ctx, rgba_2_sig, rgba_2, 2, env);
757 register_function(ctx, red_sig, red, env);
758 register_function(ctx, green_sig, green, env);
759 register_function(ctx, blue_sig, blue, env);
760 register_function(ctx, mix_sig, mix, env);
762 register_function(ctx, hsl_sig, hsl, env);
763 register_function(ctx, hsla_sig, hsla, env);
764 register_function(ctx, hue_sig, hue, env);
765 register_function(ctx, saturation_sig, saturation, env);
766 register_function(ctx, lightness_sig, lightness, env);
767 register_function(ctx, adjust_hue_sig, adjust_hue, env);
768 register_function(ctx, lighten_sig, lighten, env);
769 register_function(ctx, darken_sig, darken, env);
770 register_function(ctx, saturate_sig, saturate, env);
771 register_function(ctx, desaturate_sig, desaturate, env);
772 register_function(ctx, grayscale_sig, grayscale, env);
773 register_function(ctx, complement_sig, complement, env);
774 register_function(ctx, invert_sig, invert, env);
776 register_function(ctx, alpha_sig, alpha, env);
777 register_function(ctx, opacity_sig, alpha, env);
778 register_function(ctx, opacify_sig, opacify, env);
779 register_function(ctx, fade_in_sig, opacify, env);
780 register_function(ctx, transparentize_sig, transparentize, env);
781 register_function(ctx, fade_out_sig, transparentize, env);
782 // Other Color Functions
783 register_function(ctx, adjust_color_sig, adjust_color, env);
784 register_function(ctx, scale_color_sig, scale_color, env);
785 register_function(ctx, change_color_sig, change_color, env);
786 register_function(ctx, ie_hex_str_sig, ie_hex_str, env);
788 register_function(ctx, unquote_sig, sass_unquote, env);
789 register_function(ctx, quote_sig, sass_quote, env);
790 register_function(ctx, str_length_sig, str_length, env);
791 register_function(ctx, str_insert_sig, str_insert, env);
792 register_function(ctx, str_index_sig, str_index, env);
793 register_function(ctx, str_slice_sig, str_slice, env);
794 register_function(ctx, to_upper_case_sig, to_upper_case, env);
795 register_function(ctx, to_lower_case_sig, to_lower_case, env);
797 register_function(ctx, percentage_sig, percentage, env);
798 register_function(ctx, round_sig, round, env);
799 register_function(ctx, ceil_sig, ceil, env);
800 register_function(ctx, floor_sig, floor, env);
801 register_function(ctx, abs_sig, abs, env);
802 register_function(ctx, min_sig, min, env);
803 register_function(ctx, max_sig, max, env);
804 register_function(ctx, random_sig, random, env);
806 register_function(ctx, length_sig, length, env);
807 register_function(ctx, nth_sig, nth, env);
808 register_function(ctx, set_nth_sig, set_nth, env);
809 register_function(ctx, index_sig, index, env);
810 register_function(ctx, join_sig, join, env);
811 register_function(ctx, append_sig, append, env);
812 register_function(ctx, zip_sig, zip, env);
813 register_function(ctx, list_separator_sig, list_separator, env);
814 register_function(ctx, is_bracketed_sig, is_bracketed, env);
816 register_function(ctx, map_get_sig, map_get, env);
817 register_function(ctx, map_merge_sig, map_merge, env);
818 register_function(ctx, map_remove_sig, map_remove, env);
819 register_function(ctx, map_keys_sig, map_keys, env);
820 register_function(ctx, map_values_sig, map_values, env);
821 register_function(ctx, map_has_key_sig, map_has_key, env);
822 register_function(ctx, keywords_sig, keywords, env);
823 // Introspection Functions
824 register_function(ctx, type_of_sig, type_of, env);
825 register_function(ctx, unit_sig, unit, env);
826 register_function(ctx, unitless_sig, unitless, env);
827 register_function(ctx, comparable_sig, comparable, env);
828 register_function(ctx, variable_exists_sig, variable_exists, env);
829 register_function(ctx, global_variable_exists_sig, global_variable_exists, env);
830 register_function(ctx, function_exists_sig, function_exists, env);
831 register_function(ctx, mixin_exists_sig, mixin_exists, env);
832 register_function(ctx, feature_exists_sig, feature_exists, env);
833 register_function(ctx, call_sig, call, env);
835 register_function(ctx, not_sig, sass_not, env);
836 register_function(ctx, if_sig, sass_if, env);
838 register_function(ctx, inspect_sig, inspect, env);
839 register_function(ctx, unique_id_sig, unique_id, env);
840 // Selector functions
841 register_function(ctx, selector_nest_sig, selector_nest, env);
842 register_function(ctx, selector_append_sig, selector_append, env);
843 register_function(ctx, selector_extend_sig, selector_extend, env);
844 register_function(ctx, selector_replace_sig, selector_replace, env);
845 register_function(ctx, selector_unify_sig, selector_unify, env);
846 register_function(ctx, is_superselector_sig, is_superselector, env);
847 register_function(ctx, simple_selectors_sig, simple_selectors, env);
848 register_function(ctx, selector_parse_sig, selector_parse, env);
851 void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)
853 while (descrs && *descrs) {
854 register_c_function(ctx, env, *descrs);
858 void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)
860 Definition_Ptr def = make_c_function(descr, ctx);
861 def->environment(env);
862 (*env)[def->name() + "[f]"] = def;