diff --git a/Makefile b/Makefile index d418b2d8e1..99b01f74fc 100644 --- a/Makefile +++ b/Makefile @@ -152,9 +152,11 @@ ifeq (MinGW,$(UNAME)) LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dll endif else - CFLAGS += -fPIC - CXXFLAGS += -fPIC - LDFLAGS += -fPIC + ifneq (Cygwin,$(UNAME)) + CFLAGS += -fPIC + CXXFLAGS += -fPIC + LDFLAGS += -fPIC + endif endif ifeq (MinGW,$(UNAME)) @@ -177,9 +179,11 @@ ifeq (MinGW,$(UNAME)) CXXFLAGS += -D ADD_EXPORTS endif else - CFLAGS += -fPIC - CXXFLAGS += -fPIC - LDFLAGS += -fPIC + ifneq (Cygwin,$(UNAME)) + CFLAGS += -fPIC + CXXFLAGS += -fPIC + LDFLAGS += -fPIC + endif endif OBJECTS = $(addprefix src/,$(SOURCES:.cpp=.o)) diff --git a/script/ci-build-libsass b/script/ci-build-libsass index 389d085871..58b7cbee0c 100755 --- a/script/ci-build-libsass +++ b/script/ci-build-libsass @@ -62,19 +62,19 @@ if [ "x$AUTOTOOLS" == "xyes" ]; then ${SHARED_OPT} echo -en 'travis_fold:end:configure\r' - make clean $MAKE_OPTS + make $MAKE_OPTS clean # install to prefix directory - PREFIX="$PREFIX" make install + PREFIX="$PREFIX" make $MAKE_OPTS install else - make clean $MAKE_OPTS + make $MAKE_OPTS clean fi # install to prefix directory -PREFIX="$PREFIX" make install +PREFIX="$PREFIX" make $MAKE_OPTS install ls -la $PREFIX/* diff --git a/src/ast.cpp b/src/ast.cpp index 8a959b0c45..26da4dbd2b 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -1859,17 +1859,17 @@ namespace Sass { // resolved color std::string res_name = name; - double r = round(cap_channel<0xff>(r_)); - double g = round(cap_channel<0xff>(g_)); - double b = round(cap_channel<0xff>(b_)); + double r = Sass::round(cap_channel<0xff>(r_)); + double g = Sass::round(cap_channel<0xff>(g_)); + double b = Sass::round(cap_channel<0xff>(b_)); double a = cap_channel<1> (a_); // get color from given name (if one was given at all) if (name != "" && name_to_color(name)) { const Color* n = name_to_color(name); - r = round(cap_channel<0xff>(n->r())); - g = round(cap_channel<0xff>(n->g())); - b = round(cap_channel<0xff>(n->b())); + r = Sass::round(cap_channel<0xff>(n->r())); + g = Sass::round(cap_channel<0xff>(n->g())); + b = Sass::round(cap_channel<0xff>(n->b())); a = cap_channel<1> (n->a()); } // otherwise get the possible resolved color name diff --git a/src/ast.hpp b/src/ast.hpp index 989566dfaf..5319e1132f 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -310,6 +310,7 @@ namespace Sass { DECLARATION, ASSIGNMENT, IMPORT_STUB, + IMPORTED, IMPORT, COMMENT, WARNING, @@ -537,30 +538,47 @@ namespace Sass { // necessary to store a list of each in an Import node. //////////////////////////////////////////////////////////////////////////// class Import : public Statement { - std::vector files_; - std::vector urls_; - ADD_PROPERTY(List*, media_queries); + std::vector urls_; + std::vector incs_; + ADD_PROPERTY(List*, media_queries); public: Import(ParserState pstate) : Statement(pstate), - files_(std::vector()), urls_(std::vector()), + incs_(std::vector()), media_queries_(0) { statement_type(IMPORT); } - std::vector& files() { return files_; } - std::vector& urls() { return urls_; } + std::vector& urls() { return urls_; } + std::vector& incs() { return incs_; } ATTACH_OPERATIONS() }; + // not yet resolved single import + // so far we only know requested name class Import_Stub : public Statement { - ADD_PROPERTY(std::string, file_name) + Include resource_; public: - Import_Stub(ParserState pstate, std::string f) - : Statement(pstate), file_name_(f) + std::string abs_path() { return resource_.abs_path; }; + std::string imp_path() { return resource_.imp_path; }; + Include resource() { return resource_; }; + + Import_Stub(ParserState pstate, Include res) + : Statement(pstate), resource_(res) { statement_type(IMPORT_STUB); } ATTACH_OPERATIONS() }; + class Imported : public Has_Block { + ADD_PROPERTY(std::string, file_name) + ADD_PROPERTY(Import_Stub*, import_stub) + public: + Imported(Import_Stub* import_stub, Block* inner) + : Has_Block(import_stub->pstate(), inner), + import_stub_(import_stub) + { statement_type(IMPORTED); } + ATTACH_OPERATIONS() + }; + ////////////////////////////// // The Sass `@warn` directive. ////////////////////////////// diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index a427d84b0a..51dedbfaa5 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -6,8 +6,6 @@ ///////////////////////////////////////////// namespace Sass { - enum Output_Style { NESTED, EXPANDED, COMPACT, COMPRESSED, FORMATTED }; - class AST_Node; // statements class Statement; @@ -23,6 +21,7 @@ namespace Sass { class Declaration; class Assignment; class Import; + class Imported; class Import_Stub; class Warning; class Error; diff --git a/src/backtrace.hpp b/src/backtrace.hpp index 8dbd6a3528..8844135891 100644 --- a/src/backtrace.hpp +++ b/src/backtrace.hpp @@ -33,7 +33,7 @@ namespace Sass { while (this_point->parent) { // make path relative to the current directory - std::string rel_path(Sass::File::resolve_relative_path(this_point->pstate.path, cwd, cwd)); + std::string rel_path(Sass::File::abs2rel(this_point->pstate.path, cwd, cwd)); if (warning) { ss << std::endl diff --git a/src/context.cpp b/src/context.cpp index 462744079a..33f8b92afb 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -27,6 +27,7 @@ #include "listize.hpp" #include "extend.hpp" #include "remove_placeholders.hpp" +#include "sass_context.hpp" #include "functions.hpp" #include "backtrace.hpp" #include "sass2scss.h" @@ -38,88 +39,80 @@ namespace Sass { using namespace File; using namespace Sass; - Sass_Queued::Sass_Queued(const std::string& load_path, const std::string& abs_path, const char* source) + inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) + { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } + + static std::string safe_input(const char* in_path) { - this->load_path = load_path; - this->abs_path = abs_path; - this->source = source; + // enforce some safe defaults + // used to create relative file links + std::string safe_path(in_path ? in_path : ""); + return safe_path == "" ? "stdin" : safe_path; } - inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) - { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } + static std::string safe_output(const char* out_path, const std::string& input_path = "") + { + std::string safe_path(out_path ? out_path : ""); + // maybe we can extract an output path from input path + if (safe_path == "" && input_path != "") { + int lastindex = static_cast(input_path.find_last_of(".")); + return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; + } + // enforce some safe defaults + // used to create relative file links + return safe_path == "" ? "stdout" : safe_path; + } - Context::Context(Context::Data initializers) - : // Output(this), + Context::Context(struct Sass_Context* c_ctx) + : CWD(File::get_cwd()), + entry_path(""), head_imports(0), mem(Memory_Manager()), - c_options (initializers.c_options()), - c_compiler (initializers.c_compiler()), - source_c_str (initializers.source_c_str()), - sources (std::vector()), - strings (std::vector()), - plugin_paths (initializers.plugin_paths()), - include_paths (initializers.include_paths()), - queue (std::vector()), - style_sheets (std::map()), - emitter (this), + plugins(), + emitter(this), + + strings(), + resources(), + sheets(), + subset_map(), + import_stack(), + + c_options (c_ctx), + c_headers (std::vector()), c_importers (std::vector()), c_functions (std::vector()), - indent (initializers.indent()), - linefeed (initializers.linefeed()), - input_path (make_canonical_path(initializers.input_path())), - output_path (make_canonical_path(initializers.output_path())), - source_comments (initializers.source_comments()), - output_style (initializers.output_style()), - source_map_file (make_canonical_path(initializers.source_map_file())), - source_map_root (initializers.source_map_root()), // pass-through - source_map_embed (initializers.source_map_embed()), - source_map_contents (initializers.source_map_contents()), - omit_source_map_url (initializers.omit_source_map_url()), - is_indented_syntax_src (initializers.is_indented_syntax_src()), - precision (initializers.precision()), - plugins(), - subset_map (Subset_Map >()) - { - cwd = get_cwd(); + indent (safe_str(c_options->indent, " ")), + linefeed (safe_str(c_options->linefeed, "\n")), - // enforce some safe defaults - // used to create relative file links - if (input_path == "") input_path = "stdin"; - if (output_path == "") output_path = "stdout"; + input_path (make_canonical_path(safe_input(c_options->input_path))), + output_path (make_canonical_path(safe_output(c_options->output_path, input_path))), + source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))), + source_map_root (make_canonical_path(safe_str(c_options->source_map_root, ""))) + + { + + // add cwd to include paths + include_paths.push_back(CWD); - include_paths.push_back(cwd); - collect_include_paths(initializers.include_paths_c_str()); + // collect more paths from different options + collect_include_paths(sass_option_get_include_path(c_options)); // collect_include_paths(initializers.include_paths_array()); - collect_plugin_paths(initializers.plugin_paths_c_str()); + collect_plugin_paths(sass_option_get_plugin_path(c_options)); // collect_plugin_paths(initializers.plugin_paths_array()); - for (size_t i = 0, S = plugin_paths.size(); i < S; ++i) { - plugins.load_plugins(plugin_paths[i]); - } - - for(auto fn : plugins.get_functions()) { - c_functions.push_back(fn); - } - for(auto fn : plugins.get_headers()) { - c_headers.push_back(fn); - } - for(auto fn : plugins.get_importers()) { - c_importers.push_back(fn); - } + // load plugins and register custom behaviors + for(auto plug : plugin_paths) plugins.load_plugins(plug); + for(auto fn : plugins.get_headers()) c_headers.push_back(fn); + for(auto fn : plugins.get_importers()) c_importers.push_back(fn); + for(auto fn : plugins.get_functions()) c_functions.push_back(fn); + // sort the items by priority (lowest first) sort (c_headers.begin(), c_headers.end(), sort_importers); sort (c_importers.begin(), c_importers.end(), sort_importers); - std::string entry_point = initializers.entry_point(); - if (!entry_point.empty()) { - std::string result(add_file(entry_point, true)); - if (result.empty()) { - throw "File to read not found or unreadable: " + entry_point; - } - } - emitter.set_filename(resolve_relative_path(output_path, source_map_file, cwd)); + emitter.set_filename(abs2rel(output_path, source_map_file, CWD)); } @@ -142,16 +135,29 @@ namespace Sass { Context::~Context() { - // make sure we free the source even if not processed! - if (sources.size() == 0 && source_c_str) free(source_c_str); - // sources are allocated by strdup or malloc (overtaken from C code) - for (size_t i = 0; i < sources.size(); ++i) free(sources[i]); + // resources were allocated by strdup or malloc + for (size_t i = 0; i < resources.size(); ++i) { + free(resources[i].contents); + free(resources[i].srcmap); + } // free all strings we kept alive during compiler execution for (size_t n = 0; n < strings.size(); ++n) free(strings[n]); // everything that gets put into sources will be freed by us for (size_t m = 0; m < import_stack.size(); ++m) sass_delete_import(import_stack[m]); // clear inner structures (vectors) and input source - sources.clear(); import_stack.clear(); source_c_str = 0; + resources.clear(); import_stack.clear(); + } + + Data_Context::~Data_Context() + { + // make sure we free the source even if not processed! + if (resources.size() == 0 && source_c_str) free(source_c_str); + if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str); + source_c_str = 0; srcmap_c_str = 0; + } + + File_Context::~File_Context() + { } void Context::collect_include_paths(const char* paths_str) @@ -181,10 +187,9 @@ namespace Sass { void Context::collect_include_paths(const char** paths_array) { - if (paths_array) { - for (size_t i = 0; paths_array[i]; i++) { - collect_include_paths(paths_array[i]); - } + if (!paths_array) return; + for (size_t i = 0; paths_array[i]; i++) { + collect_include_paths(paths_array[i]); } } @@ -215,72 +220,263 @@ namespace Sass { void Context::collect_plugin_paths(const char** paths_array) { - if (paths_array) { - for (size_t i = 0; paths_array[i]; i++) { - collect_plugin_paths(paths_array[i]); - } + if (!paths_array) return; + for (size_t i = 0; paths_array[i]; i++) { + collect_plugin_paths(paths_array[i]); } } - void Context::add_source(std::string load_path, std::string abs_path, char* contents) + + + // resolve the imp_path in base_path or include_paths + // looks for alternatives and returns a list from one directory + std::vector Context::find_includes(const Importer& import) { - sources.push_back(contents); - included_files.push_back(abs_path); - queue.push_back(Sass_Queued(load_path, abs_path, contents)); - emitter.add_source_index(sources.size() - 1); - include_links.push_back(resolve_relative_path(abs_path, source_map_file, cwd)); + // first try to resolve the load path relative to the base path + std::vector vec(resolve_includes(import.base_path, import.imp_path)); + // then search in every include path (but only if nothing found yet) + for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i) + { + // call resolve_includes and individual base path and append all results + std::vector resolved(resolve_includes(include_paths[i], import.imp_path)); + if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end()); + } + // return vector + return vec; } - // Add a new import file to the context - std::string Context::add_file(const std::string& file, bool delay) + + // register include with resolved path and its content + // memory of the resources will be freed by us on exit + void Context::register_resource(const Include& inc, const Resources& res) { - using namespace File; - std::string path(make_canonical_path(file)); - std::string resolved(find_file(path, include_paths)); - if (resolved == "") return resolved; - if (char* contents = read_file(resolved)) { - add_source(path, resolved, contents); - style_sheets[path] = 0; - if (delay == false) { - size_t i = queue.size() - 1; - process_queue_entry(queue[i], i); - } - return path; + + // do not parse same resource twice + // maybe raise an error in this case + if (sheets.count(inc.abs_path)) { + free(res.contents); free(res.srcmap); + throw std::runtime_error("duplicate resource registered"); + return; } - return std::string(""); + + // get index for this resource + size_t idx = resources.size(); + + // tell emitter about new resource + emitter.add_source_index(idx); + + // put resources under our control + // the memory will be freed later + resources.push_back(res); + + // add a relative link to the working directory + included_files.push_back(abs2rel(inc.abs_path, CWD)); + // add a relative link to the source map output file + srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD)); + + // create entry only for import stack + Sass_Import_Entry import = sass_make_import( + inc.imp_path.c_str(), + inc.abs_path.c_str(), + res.contents, + res.srcmap + ); + // add the entry to the stack + import_stack.push_back(import); + + // get pointer to the loaded content + const char* contents = resources[idx].contents; + // keep a copy of the path around (for parserstates) + // ToDo: we clean it, but still not very elegant!? + strings.push_back(sass_strdup(inc.abs_path.c_str())); + // create the initial parser state from resource + ParserState pstate(strings.back(), contents, idx); + // create a parser instance from the given c_str buffer + Parser p(Parser::from_c_str(contents, *this, pstate)); + + // then parse the root block + sass_import_take_source(import); + Block* root = p.parse(); + sass_import_take_srcmap(import); + + // delete memory of current stack frame + sass_delete_import(import_stack.back()); + // remove current stack frame + import_stack.pop_back(); + // create key/value pair for ast node + std::pair + ast_pair(inc.abs_path, { res, root }); + // register resulting resource + sheets.insert(ast_pair); + + } - // Add a new import file to the context - // This has some previous directory context - std::string Context::add_file(const std::string& base, const std::string& file, ParserState pstate) + // Add a new import to the context (called from `import_url`) + Include Context::load_import(const Importer& imp, ParserState pstate) { - using namespace File; - std::string path(make_canonical_path(file)); - std::string base_file(join_paths(base, path)); - if (style_sheets.count(base_file)) return base_file; - std::vector resolved(resolve_file(base, path)); + + // search for valid imports (ie. partials) on the filesystem + // this may return more than one valid result (ambiguous imp_path) + const std::vector resolved(find_includes(imp)); + + // error nicely on ambiguous imp_path if (resolved.size() > 1) { std::stringstream msg_stream; msg_stream << "It's not clear which file to import for "; - msg_stream << "'@import \"" << file << "\"'." << "\n"; + msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n"; msg_stream << "Candidates:" << "\n"; for (size_t i = 0, L = resolved.size(); i < L; ++i) - { msg_stream << " " << resolved[i].load_path << "\n"; } + { msg_stream << " " << resolved[i].imp_path << "\n"; } msg_stream << "Please delete or rename all but one of these files." << "\n"; error(msg_stream.str(), pstate); } - if (resolved.size()) { + + // process the resolved entry + else if (resolved.size() == 1) { + // use cache for the resource loading + if (sheets.count(resolved[0].abs_path)) return resolved[0]; + // try to read the content of the resolved file entry + // the memory buffer returned must be freed by us! if (char* contents = read_file(resolved[0].abs_path)) { - add_source(base_file, resolved[0].abs_path, contents); - style_sheets[base_file] = 0; - size_t i = queue.size() - 1; - process_queue_entry(queue[i], i); - return base_file; + // register the newly resolved file resource + register_resource(resolved[0], { contents, 0 }); + // return resolved entry + return resolved[0]; } } - // now go the regular code path - return add_file(path); + + // nothing found + return { imp, "" }; + } + + + + void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) { + + ParserState pstate(imp->pstate()); + std::string imp_path(unquote(load_path)); + std::string protocol("file"); + + using namespace Prelexer; + if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { + + protocol = std::string(imp_path.c_str(), proto - 3); + // std::cerr << "==================== " << protocol << "\n"; + if (protocol.compare("file") && true) { + + } + } + + // add urls (protocol other than file) and urls without procotol to `urls` member + // ToDo: if ctx_path is already a file resource, we should not add it here? + if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") { + imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path)); + } + else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") { + String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path)); + Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc); + Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate); + (*loc_args) << loc_arg; + Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args); + imp->urls().push_back(new_url); + } + else { + const Importer importer(imp_path, ctx_path); + Include include(load_import(importer, pstate)); + if (include.abs_path.empty()) { + error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate); + } + imp->incs().push_back(include); + } + + } + + + // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet + bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector importers, bool only_one) + { + // unique counter + size_t count = 0; + // need one correct import + bool has_import = false; + // process all custom importers (or custom headers) + for (Sass_Importer_Entry& importer : importers) { + // int priority = sass_importer_get_priority(importer); + Sass_Importer_Fn fn = sass_importer_get_function(importer); + // skip importer if it returns NULL + if (Sass_Import_List includes = + fn(load_path.c_str(), importer, c_compiler) + ) { + // get c pointer copy to iterate over + Sass_Import_List it_includes = includes; + while (*it_includes) { ++count; + // create unique path to use as key + std::string uniq_path = load_path; + if (!only_one && count) { + std::stringstream path_strm; + path_strm << uniq_path << ":" << count; + uniq_path = path_strm.str(); + } + // create the importer struct + Importer importer(uniq_path, ctx_path); + // query data from the current include + Sass_Import_Entry include = *it_includes; + char* source = sass_import_take_source(include); + char* srcmap = sass_import_take_source(include); + size_t line = sass_import_get_error_line(include); + size_t column = sass_import_get_error_column(include); + const char *abs_path = sass_import_get_abs_path(include); + // handle error message passed back from custom importer + // it may (or may not) override the line and column info + if (const char* err_message = sass_import_get_error_message(include)) { + if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }); + if (line == std::string::npos && column == std::string::npos) error(err_message, pstate); + else error(err_message, ParserState(ctx_path, source, Position(line, column))); + } + // content for import was set + else if (source) { + // resolved abs_path should be set by custom importer + // use the created uniq_path as fallback (maybe enforce) + std::string path_key(abs_path ? abs_path : uniq_path); + // create the importer struct + Include include(importer, path_key); + // attach information to AST node + imp->incs().push_back(include); + // register the resource buffers + register_resource(include, { source, srcmap }); + } + // only a path was retuned + // try to load it like normal + else if(abs_path) { + // checks some urls to preserve + // `http://`, `https://` and `//` + // or dispatchs to `import_file` + // which will check for a `.css` extension + // or resolves the file on the filesystem + // added and resolved via `add_file` + // finally stores everything on `imp` + import_url(imp, abs_path, ctx_path); + } + // move to next + ++it_includes; + } + // deallocate the returned memory + sass_delete_import_list(includes); + // set success flag + has_import = true; + // break out of loop + if (only_one) break; + } + } + // return result + return has_import; + } + + + + void register_function(Context&, Signature sig, Native_Function f, Env* env); void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env); void register_overload_stub(Context&, std::string name, Env* env); @@ -288,15 +484,15 @@ namespace Sass { void register_c_functions(Context&, Env* env, Sass_Function_List); void register_c_function(Context&, Env* env, Sass_Function_Entry); - char* Context::compile_block(Block* root) + char* Context::render(Block* root) { if (!root) return 0; root->perform(&emitter); emitter.finalize(); OutputBuffer emitted = emitter.get_buffer(); std::string output = emitted.buffer; - if (!omit_source_map_url) { - if (source_map_embed) { + if (!c_options->omit_source_map_url) { + if (c_options->source_map_embed) { output += linefeed + format_embedded_source_map(); } else if (source_map_file != "") { @@ -306,35 +502,96 @@ namespace Sass { return sass_strdup(output.c_str()); } - void Context::process_queue_entry(Sass_Queued& entry, size_t i) + void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate) { - if (style_sheets[queue[i].load_path]) return; - Sass_Import_Entry import = sass_make_import( - queue[i].load_path.c_str(), - queue[i].abs_path.c_str(), - 0, 0 - ); - import_stack.push_back(import); - // keep a copy of the path around (for parser states) - strings.push_back(sass_strdup(queue[i].abs_path.c_str())); - ParserState pstate(strings.back(), queue[i].source, i); - Parser p(Parser::from_c_str(queue[i].source, *this, pstate)); - Block* ast = p.parse(); - sass_delete_import(import_stack.back()); - import_stack.pop_back(); - // ToDo: we store by load_path, which can lead - // to duplicates if importer reports the same path - // Maybe we should add an error for duplicates!? - style_sheets[queue[i].load_path] = ast; + Import* imp = SASS_MEMORY_NEW(mem, Import, pstate); + std::string load_path(entry_path); + call_headers(load_path, ctx_path, pstate, imp); + // increase head count for skip over + head_imports += resources.size() - 1; + // add the statement if we have urls + if (!imp->urls().empty()) (*root) << imp; + // process all other resources (add Import_Stub nodes) + for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { + (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]); + } + } + + Block* File_Context::parse() + { + + // check if entry file is given + if (input_path.empty()) return 0; + + // clear old root + // resources.clear(); + + std::string abs_path(rel2abs(input_path, CWD)); + + // try to load the entry file + char* contents = read_file(abs_path); + + // alternatively also look inside each include path folder + // I think this differs from ruby sass (IMO too late to remove) + for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) { + // build absolute path for this include path entry + abs_path = rel2abs(input_path, include_paths[i]); + // try to load the resulting path + contents = read_file(abs_path); + } + + // abort early if no content could be loaded (various reasons) + if (!contents) throw "File to read not found or unreadable: " + input_path; + + // remember entry path + entry_path = abs_path; + + // create the source entry for file entry + register_resource({{ input_path, "." }, abs_path }, { contents, 0 }); + + // create root ast tree node + return compile(); + } - Block* Context::parse_file() + Block* Data_Context::parse() { - Block* root = 0; - for (size_t i = 0; i < queue.size(); ++i) { - process_queue_entry(queue[i], i); - if (i == 0) root = style_sheets[queue[i].load_path]; + + // check if source string is given + if (!source_c_str) return 0; + + // clear old root + // resources.clear(); + + // convert indented sass syntax + if(c_options->is_indented_syntax_src) { + // call sass2scss to convert the string + char * converted = sass2scss(source_c_str, + // preserve the structure as much as possible + SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); + // replace old source_c_str with converted + free(source_c_str); source_c_str = converted; } + + // remember entry path (defaults to stdin for string) + entry_path = input_path.empty() ? "stdin" : input_path; + + // register a synthetic resource (path does not really exist, skip in includes) + register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str }); + + return compile(); + } + + + + // parse root block from includes + Block* Context::compile() + { + // abort if there is no data + if (resources.size() == 0) return 0; + // get root block from the first style sheet + Block* root = sheets.at(entry_path).root; + // abort on invalid root if (root == 0) return 0; Env global; // create root environment @@ -367,39 +624,11 @@ namespace Sass { // return processed tree return root; } - // EO parse_file - - Block* Context::parse_string() - { - if (!source_c_str) return 0; - queue.clear(); - if(is_indented_syntax_src) { - char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); - add_source(input_path, input_path, contents); - free(source_c_str); - return parse_file(); - } - add_source(input_path, input_path, source_c_str); - size_t idx = queue.size() - 1; - process_queue_entry(queue[idx], idx); - return parse_file(); - } - - char* Context::compile_file() - { - // returns NULL if something fails - return compile_block(parse_file()); - } - - char* Context::compile_string() - { - // returns NULL if something fails - return compile_block(parse_string()); - } + // EO compile std::string Context::format_embedded_source_map() { - std::string map = emitter.generate_source_map(*this); + std::string map = emitter.render_srcmap(*this); std::istringstream is( map ); std::ostringstream buffer; base64::encoder E; @@ -411,15 +640,15 @@ namespace Sass { std::string Context::format_source_mapping_url(const std::string& file) { - std::string url = resolve_relative_path(file, output_path, cwd); + std::string url = abs2rel(file, output_path, CWD); return "/*# sourceMappingURL=" + url + " */"; } - char* Context::generate_source_map() + char* Context::render_srcmap() { if (source_map_file == "") return 0; char* result = 0; - std::string map = emitter.generate_source_map(*this); + std::string map = emitter.render_srcmap(*this); result = sass_strdup(map.c_str()); return result; } @@ -429,7 +658,7 @@ namespace Sass { // we probably always want to skip the header includes? std::vector Context::get_included_files(bool skip, size_t headers) { - // create a copy of the vector for manupulations + // create a copy of the vector for manipulations std::vector includes = included_files; if (includes.size() == 0) return includes; if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); } @@ -439,11 +668,6 @@ namespace Sass { return includes; } - std::string Context::get_cwd() - { - return Sass::File::get_cwd(); - } - void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) { Definition* def = make_native_function(sig, f, ctx); @@ -588,4 +812,8 @@ namespace Sass { (*env)[def->name() + "[f]"] = def; } + + + + } diff --git a/src/context.hpp b/src/context.hpp index 918b1ae1ef..c6e7c48264 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -19,36 +19,56 @@ #include "file.hpp" #include "sass.h" +#include "sass_context.hpp" + struct Sass_Function; namespace Sass { class Context { public: + void import_url (Import* imp, std::string load_path, const std::string& ctx_path); + bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) + { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); }; + bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) + { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); }; + + private: + bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector importers, bool only_one = true); + + public: + const std::string CWD; + std::string entry_path; size_t head_imports; Memory_Manager mem; + Plugins plugins; + Output emitter; + + // resources add under our control + // these are guaranteed to be freed + std::vector strings; + std::vector resources; + std::map sheets; + Subset_Map > subset_map; + std::vector import_stack; - struct Sass_Options* c_options; struct Sass_Compiler* c_compiler; - char* source_c_str; + struct Sass_Options* c_options; - // c-strs containing Sass file contents - // we will overtake ownership of memory - std::vector sources; - // strings get freed with context - std::vector strings; // absolute paths to includes std::vector included_files; - // relative links to includes - std::vector include_links; + // relative includes for sourcemap + std::vector srcmap_links; // vectors above have same size std::vector plugin_paths; // relative paths to load plugins std::vector include_paths; // lookup paths for includes - std::vector queue; // queue of files to be parsed - std::map style_sheets; // map of paths to ASTs - // SourceMap source_map; - Output emitter; + + + + + + void apply_custom_headers(Block* root, const char* path, ParserState pstate); std::vector c_headers; std::vector c_importers; @@ -58,73 +78,29 @@ namespace Sass { void add_c_importer(Sass_Importer_Entry importer); void add_c_function(Sass_Function_Entry function); - std::string indent; // String to be used for indentation - std::string linefeed; // String to be used for line feeds - std::string input_path; // for relative paths in src-map - std::string output_path; // for relative paths to the output - bool source_comments; // for inline debug comments in css output - Output_Style output_style; // output style for the generated css code - std::string source_map_file; // path to source map file (enables feature) - std::string source_map_root; // path for sourceRoot property (pass-through) - bool source_map_embed; // embed in sourceMappingUrl (as data-url) - bool source_map_contents; // insert included contents into source map - bool omit_source_map_url; // disable source map comment in css output - bool is_indented_syntax_src; // treat source string as sass - - // overload import calls - std::vector import_stack; + const std::string indent; // String to be used for indentation + const std::string linefeed; // String to be used for line feeds + const std::string input_path; // for relative paths in src-map + const std::string output_path; // for relative paths to the output + const std::string source_map_file; // path to source map file (enables feature) + const std::string source_map_root; // path for sourceRoot property (pass-through) + + virtual ~Context(); + Context(struct Sass_Context*); + virtual Block* parse() = 0; + virtual Block* compile(); + virtual char* render(Block* root); + virtual char* render_srcmap(); + + void register_resource(const Include&, const Resources&); + std::vector find_includes(const Importer& import); + Include load_import(const Importer&, ParserState pstate); - size_t precision; // precision for outputting fractional numbers - - KWD_ARG_SET(Data) { - KWD_ARG(Data, struct Sass_Options*, c_options) - KWD_ARG(Data, struct Sass_Compiler*, c_compiler) - KWD_ARG(Data, char*, source_c_str) - KWD_ARG(Data, std::string, entry_point) - KWD_ARG(Data, std::string, input_path) - KWD_ARG(Data, std::string, output_path) - KWD_ARG(Data, std::string, indent) - KWD_ARG(Data, std::string, linefeed) - KWD_ARG(Data, const char*, include_paths_c_str) - KWD_ARG(Data, const char*, plugin_paths_c_str) - // KWD_ARG(Data, const char**, include_paths_array) - // KWD_ARG(Data, const char**, plugin_paths_array) - KWD_ARG(Data, std::vector, include_paths) - KWD_ARG(Data, std::vector, plugin_paths) - KWD_ARG(Data, bool, source_comments) - KWD_ARG(Data, Output_Style, output_style) - KWD_ARG(Data, std::string, source_map_file) - KWD_ARG(Data, std::string, source_map_root) - KWD_ARG(Data, bool, omit_source_map_url) - KWD_ARG(Data, bool, is_indented_syntax_src) - KWD_ARG(Data, size_t, precision) - KWD_ARG(Data, bool, source_map_embed) - KWD_ARG(Data, bool, source_map_contents) - }; - - Context(Data); - ~Context(); - static std::string get_cwd(); - - Block* parse_file(); - Block* parse_string(); - void add_source(std::string, std::string, char*); - - std::string add_file(const std::string& imp_path, bool delay = false); - std::string add_file(const std::string& imp_path, const std::string& abs_path, ParserState pstate); - - void process_queue_entry(Sass_Queued& entry, size_t idx); - - // allow to optionally overwrite the input path - // default argument for input_path is std::string("stdin") - // usefull to influence the source-map generating etc. - char* compile_file(); - char* compile_string(); - char* compile_block(Block* root); - char* generate_source_map(); std::vector get_included_files(bool skip = false, size_t headers = 0); + Sass_Output_Style output_style() { return c_options->output_style; }; + private: void collect_plugin_paths(const char* paths_str); void collect_plugin_paths(const char** paths_array); @@ -133,8 +109,6 @@ namespace Sass { std::string format_embedded_source_map(); std::string format_source_mapping_url(const std::string& out_path); - std::string cwd; - Plugins plugins; // void register_built_in_functions(Env* env); // void register_function(Signature sig, Native_Function f, Env* env); @@ -142,7 +116,32 @@ namespace Sass { // void register_overload_stub(std::string name, Env* env); public: - Subset_Map > subset_map; + const std::string& cwd() { return CWD; }; + }; + + class File_Context : public Context { + public: + File_Context(struct Sass_File_Context* ctx) + : Context(ctx) + { } + virtual ~File_Context(); + virtual Block* parse(); + }; + + class Data_Context : public Context { + public: + char* source_c_str; + char* srcmap_c_str; + Data_Context(struct Sass_Data_Context* ctx) + : Context(ctx) + { + source_c_str = ctx->source_string; + srcmap_c_str = ctx->srcmap_string; + ctx->source_string = 0; // passed away + ctx->srcmap_string = 0; // passed away + } + virtual ~Data_Context(); + virtual Block* parse(); }; } diff --git a/src/cssize.hpp b/src/cssize.hpp index 104e80548e..3bd878107d 100644 --- a/src/cssize.hpp +++ b/src/cssize.hpp @@ -38,6 +38,7 @@ namespace Sass { // Statement* operator()(Declaration*); // Statement* operator()(Assignment*); // Statement* operator()(Import*); + // Statement* operator()(Imported*); // Statement* operator()(Import_Stub*); // Statement* operator()(Warning*); // Statement* operator()(Error*); diff --git a/src/debugger.hpp b/src/debugger.hpp index 76e4049c57..e935681d73 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -65,6 +65,11 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl; + } else if (Imported* imp = dynamic_cast(node)) { + std::cerr << ind << "Imported " << imp; + std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << "\n"; + debug_ast(imp->block(), ind + " ", env); } else if (dynamic_cast(node)) { At_Root_Block* root_block = dynamic_cast(node); std::cerr << ind << "At_Root_Block " << root_block; diff --git a/src/emitter.cpp b/src/emitter.cpp index ffe8957432..e18342fe4e 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -27,9 +27,9 @@ namespace Sass { return wbuf.buffer; } - Output_Style Emitter::output_style(void) + Sass_Output_Style Emitter::output_style(void) { - return ctx ? ctx->output_style : COMPRESSED; + return ctx ? ctx->output_style() : SASS_STYLE_COMPRESSED; } // PROXY METHODS FOR SOURCE MAPS @@ -37,8 +37,8 @@ namespace Sass { void Emitter::add_source_index(size_t idx) { wbuf.smap.source_index.push_back(idx); } - std::string Emitter::generate_source_map(Context &ctx) - { return wbuf.smap.generate_source_map(ctx); } + std::string Emitter::render_srcmap(Context &ctx) + { return wbuf.smap.render_srcmap(ctx); } void Emitter::set_filename(const std::string& str) { wbuf.smap.file = str; } @@ -105,7 +105,7 @@ namespace Sass { // write space/lf flush_schedules(); - if (in_comment && output_style() == COMPACT) { + if (in_comment && output_style() == SASS_STYLE_COMPACT) { // unescape comment nodes std::string out = comment_to_string(text); // add to buffer @@ -144,8 +144,8 @@ namespace Sass { void Emitter::append_indentation() { - if (output_style() == COMPRESSED) return; - if (output_style() == COMPACT) return; + if (output_style() == SASS_STYLE_COMPRESSED) return; + if (output_style() == SASS_STYLE_COMPACT) return; if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; @@ -158,13 +158,13 @@ namespace Sass { void Emitter::append_delimiter() { scheduled_delimiter = true; - if (output_style() == COMPACT) { + if (output_style() == SASS_STYLE_COMPACT) { if (indentation == 0) { append_mandatory_linefeed(); } else { append_mandatory_space(); } - } else if (output_style() != COMPRESSED) { + } else if (output_style() != SASS_STYLE_COMPRESSED) { append_optional_linefeed(); } } @@ -190,7 +190,7 @@ namespace Sass { void Emitter::append_optional_space() { - if (output_style() != COMPRESSED && buffer().size()) { + if (output_style() != SASS_STYLE_COMPRESSED && buffer().size()) { char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) { append_mandatory_space(); @@ -200,7 +200,7 @@ namespace Sass { void Emitter::append_special_linefeed() { - if (output_style() == COMPACT) { + if (output_style() == SASS_STYLE_COMPACT) { append_mandatory_linefeed(); for (size_t p = 0; p < indentation; p++) append_string(ctx ? ctx->indent : " "); @@ -210,7 +210,7 @@ namespace Sass { void Emitter::append_optional_linefeed() { if (in_declaration && in_comma_array) return; - if (output_style() == COMPACT) { + if (output_style() == SASS_STYLE_COMPACT) { append_mandatory_space(); } else { append_mandatory_linefeed(); @@ -219,7 +219,7 @@ namespace Sass { void Emitter::append_mandatory_linefeed() { - if (output_style() != COMPRESSED) { + if (output_style() != SASS_STYLE_COMPRESSED) { scheduled_linefeed = 1; scheduled_space = 0; // flush_schedules(); @@ -241,9 +241,9 @@ namespace Sass { { -- indentation; scheduled_linefeed = 0; - if (output_style() == COMPRESSED) + if (output_style() == SASS_STYLE_COMPRESSED) scheduled_delimiter = false; - if (output_style() == EXPANDED) { + if (output_style() == SASS_STYLE_EXPANDED) { append_optional_linefeed(); append_indentation(); } else { @@ -253,7 +253,7 @@ namespace Sass { if (node) add_close_mapping(node); append_optional_linefeed(); if (indentation != 0) return; - if (output_style() != COMPRESSED) + if (output_style() != SASS_STYLE_COMPRESSED) scheduled_linefeed = 2; } diff --git a/src/emitter.hpp b/src/emitter.hpp index f641275a28..1f2574ac0c 100644 --- a/src/emitter.hpp +++ b/src/emitter.hpp @@ -25,7 +25,7 @@ namespace Sass { void set_filename(const std::string& str); void add_open_mapping(AST_Node* node); void add_close_mapping(AST_Node* node); - std::string generate_source_map(Context &ctx); + std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate); public: @@ -52,7 +52,7 @@ namespace Sass { // return buffer as std::string std::string get_buffer(void); // flush scheduled space/linefeed - Output_Style output_style(void); + Sass_Output_Style output_style(void); // add outstanding linefeed void finalize(void); // flush scheduled space/linefeed diff --git a/src/error_handling.cpp b/src/error_handling.cpp index f5f0d32721..5e326c6261 100644 --- a/src/error_handling.cpp +++ b/src/error_handling.cpp @@ -27,7 +27,7 @@ namespace Sass { std::string cwd(Sass::File::get_cwd()); std::cerr << "DEPRECATION WARNING: " << msg << std::endl; std::cerr << "will be an error in future versions of Sass." << std::endl; - std::string rel_path(Sass::File::resolve_relative_path(pstate.path, cwd, cwd)); + std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); std::cerr << " on line " << pstate.line+1 << " of " << rel_path << std::endl; } diff --git a/src/eval.cpp b/src/eval.cpp index e3f45e4714..7c0e542af0 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -24,6 +24,7 @@ #include "parser.hpp" #include "expand.hpp" #include "color_maps.hpp" +#include "sass_context.hpp" namespace Sass { @@ -400,9 +401,9 @@ namespace Sass { } - std::string cwd(ctx.get_cwd()); + std::string cwd(ctx.cwd()); std::string result(unquote(message->perform(&to_string))); - std::string rel_path(Sass::File::resolve_relative_path(d->pstate().path, cwd, cwd)); + std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd)); std::cerr << rel_path << ":" << d->pstate().line+1 << " DEBUG: " << result; std::cerr << std::endl; return 0; @@ -523,8 +524,8 @@ namespace Sass { Expression::Concrete_Type l_type = lhs->concrete_type(); Expression::Concrete_Type r_type = rhs->concrete_type(); - int precision = (int)ctx.precision; - bool compressed = ctx.output_style == COMPRESSED; + int precision = (int)ctx.c_options->precision; + bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED; if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { const Number* l_n = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); @@ -897,7 +898,7 @@ namespace Sass { } else if (List* list = dynamic_cast(s)) { std::string acc = ""; // ToDo: different output styles std::string sep = list->separator() == SASS_COMMA ? "," : " "; - if (ctx.output_style != COMPRESSED && sep == ",") sep += " "; + if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " "; bool initial = false; for(auto item : list->elements()) { if (item->concrete_type() != Expression::NULL_VAL) { diff --git a/src/expand.cpp b/src/expand.cpp index 7673257247..23b1303d1e 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -303,22 +303,40 @@ namespace Sass { Statement* Expand::operator()(Import* imp) { Import* result = SASS_MEMORY_NEW(ctx.mem, Import, imp->pstate()); - if (imp->media_queries()) { + if (imp->media_queries() && imp->media_queries()->size()) { Expression* ex = imp->media_queries()->perform(&eval); result->media_queries(dynamic_cast(ex)); } for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { result->urls().push_back(imp->urls()[i]->perform(&eval)); } + // all resources have been dropped for Input_Stubs + // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {} return result; } Statement* Expand::operator()(Import_Stub* i) { - append_block(ctx.style_sheets[i->file_name()]); + // we don't seem to need that actually afterall + Sass_Import_Entry import = sass_make_import( + i->imp_path().c_str(), + i->abs_path().c_str(), + 0, 0 + ); + ctx.import_stack.push_back(import); + const std::string& abs_path(i->resource().abs_path); + append_block(ctx.sheets.at(abs_path).root); + sass_delete_import(ctx.import_stack.back()); + ctx.import_stack.pop_back(); return 0; } + Statement* Expand::operator()(Imported* i) + { + std::cerr << "not needed anyways??\n"; + return i->block()->perform(this); + } + Statement* Expand::operator()(Warning* w) { // eval handles this too, because warnings may occur in functions diff --git a/src/expand.hpp b/src/expand.hpp index 564c79d765..bfa76876eb 100644 --- a/src/expand.hpp +++ b/src/expand.hpp @@ -30,6 +30,7 @@ namespace Sass { // it's easier to work with vectors std::vector env_stack; std::vector block_stack; + std::vector import_stack; std::vector property_stack; std::vector selector_stack; std::vectorbacktrace_stack; @@ -53,6 +54,7 @@ namespace Sass { Statement* operator()(Declaration*); Statement* operator()(Assignment*); Statement* operator()(Import*); + Statement* operator()(Imported*); Statement* operator()(Import_Stub*); Statement* operator()(Warning*); Statement* operator()(Error*); diff --git a/src/extend.cpp b/src/extend.cpp index 573b9d37d2..26b2aff4b2 100644 --- a/src/extend.cpp +++ b/src/extend.cpp @@ -1751,7 +1751,7 @@ namespace Sass { std::stringstream err; std::string cwd(Sass::File::get_cwd()); ParserState pstate(ext.second->pstate()); - std::string rel_path(Sass::File::resolve_relative_path(pstate.path, cwd, cwd)); + std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); err << "You may not @extend an outer selector from within @media.\n"; err << "You may only @extend selectors within the same directive.\n"; err << "From \"@extend " << ext.second->perform(&to_string) << "\""; diff --git a/src/file.cpp b/src/file.cpp index 4a454c1797..b115010656 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -126,7 +126,7 @@ namespace Sass { else return path.substr(pos+1); } - // do a locigal clean up of the path + // do a logical clean up of the path // no physical check on the filesystem std::string make_canonical_path (std::string path) { @@ -193,58 +193,58 @@ namespace Sass { } // create an absolute path by resolving relative paths with cwd - std::string make_absolute_path(const std::string& path, const std::string& cwd) + std::string rel2abs(const std::string& path, const std::string& base, const std::string& cwd) { - return make_canonical_path((is_absolute_path(path) ? path : join_paths(cwd, path))); + return make_canonical_path(join_paths(join_paths(cwd, base), path)); } // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute - std::string resolve_relative_path(const std::string& uri, const std::string& base, const std::string& cwd) + std::string abs2rel(const std::string& path, const std::string& base, const std::string& cwd) { - std::string absolute_uri = make_absolute_path(uri, cwd); - std::string absolute_base = make_absolute_path(base, cwd); + std::string abs_path = rel2abs(path, cwd); + std::string abs_base = rel2abs(base, cwd); size_t proto = 0; // check if we have a protocol - if (uri[proto] && Prelexer::is_alpha(uri[proto])) { + if (path[proto] && Prelexer::is_alpha(path[proto])) { // skip over all alphanumeric characters - while (uri[proto] && Prelexer::is_alnum(uri[proto++])) {} + while (path[proto] && Prelexer::is_alnum(path[proto++])) {} // then skip over the mandatory colon - if (proto && uri[proto] == ':') ++ proto; + if (proto && path[proto] == ':') ++ proto; } // distinguish between windows absolute paths and valid protocols // we assume that protocols must at least have two chars to be valid - if (proto && uri[proto++] == '/' && proto > 3) return uri; + if (proto && path[proto++] == '/' && proto > 3) return path; #ifdef _WIN32 // absolute link must have a drive letter, and we know that we // can only create relative links if both are on the same drive - if (absolute_base[0] != absolute_uri[0]) return absolute_uri; + if (abs_base[0] != abs_path[0]) return abs_path; #endif std::string stripped_uri = ""; std::string stripped_base = ""; size_t index = 0; - size_t minSize = std::min(absolute_uri.size(), absolute_base.size()); + size_t minSize = std::min(abs_path.size(), abs_base.size()); for (size_t i = 0; i < minSize; ++i) { #ifdef FS_CASE_SENSITIVE - if (absolute_uri[i] != absolute_base[i]) break; + if (abs_path[i] != abs_base[i]) break; #else // compare the charactes in a case insensitive manner // windows fs is only case insensitive in ascii ranges - if (tolower(absolute_uri[i]) != tolower(absolute_base[i])) break; + if (tolower(abs_path[i]) != tolower(abs_base[i])) break; #endif - if (absolute_uri[i] == '/') index = i + 1; + if (abs_path[i] == '/') index = i + 1; } - for (size_t i = index; i < absolute_uri.size(); ++i) { - stripped_uri += absolute_uri[i]; + for (size_t i = index; i < abs_path.size(); ++i) { + stripped_uri += abs_path[i]; } - for (size_t i = index; i < absolute_base.size(); ++i) { - stripped_base += absolute_base[i]; + for (size_t i = index; i < abs_base.size(); ++i) { + stripped_base += abs_base[i]; } size_t left = 0; @@ -278,7 +278,7 @@ namespace Sass { // (2) underscore + given // (3) underscore + given + extension // (4) given + extension - std::vector resolve_file(const std::string& root, const std::string& file) + std::vector resolve_includes(const std::string& root, const std::string& file) { std::string filename = join_paths(root, file); // supported extensions @@ -288,29 +288,29 @@ namespace Sass { // split the filename std::string base(dir_name(file)); std::string name(base_name(file)); - std::vector resolved; + std::vector includes; // create full path (maybe relative) std::string rel_path(join_paths(base, name)); std::string abs_path(join_paths(root, rel_path)); - if (file_exists(abs_path)) resolved.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test variation with underscore rel_path = join_paths(base, "_" + name); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) resolved.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test exts plus underscore for(auto ext : exts) { rel_path = join_paths(base, "_" + name + ext); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) resolved.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // next test plain name with exts for(auto ext : exts) { rel_path = join_paths(base, name + ext); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) resolved.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // nothing found - return resolved; + return includes; } // helper function to resolve a filename @@ -319,7 +319,7 @@ namespace Sass { // search in every include path for a match for (size_t i = 0, S = paths.size(); i < S; ++i) { - std::vector resolved(resolve_file(paths[i], file)); + std::vector resolved(resolve_includes(paths[i], file)); if (resolved.size()) return resolved[0].abs_path; } // nothing found diff --git a/src/file.hpp b/src/file.hpp index 6423d080c0..6316ded990 100644 --- a/src/file.hpp +++ b/src/file.hpp @@ -6,16 +6,9 @@ namespace Sass { + class Block; class Context; - struct Sass_Queued { - std::string abs_path; - std::string load_path; - const char* source; - public: - Sass_Queued(const std::string& load_path, const std::string& abs_path, const char* source); - }; - namespace File { // return the current directory @@ -44,14 +37,11 @@ namespace Sass { std::string join_paths(std::string root, std::string name); // create an absolute path by resolving relative paths with cwd - std::string make_absolute_path(const std::string& path, const std::string& cwd = "."); + std::string rel2abs(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd()); // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute - std::string resolve_relative_path(const std::string& path, const std::string& base, const std::string& cwd = "."); - - // try to find/resolve the filename - std::vector resolve_file(const std::string& root, const std::string& file); + std::string abs2rel(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd()); // helper function to resolve a filename std::string find_file(const std::string& file, const std::vector paths); @@ -64,6 +54,56 @@ namespace Sass { char* read_file(const std::string& file); } + + + class Importer { + public: + std::string imp_path; + std::string ctx_path; + std::string base_path; + public: + Importer(std::string imp_path, std::string ctx_path) + : imp_path(File::make_canonical_path(imp_path)), + ctx_path(File::make_canonical_path(ctx_path)), + base_path(File::dir_name(ctx_path)) + { } + }; + + class Include : public Importer { + public: + std::string abs_path; + public: + Include(const Importer& imp, std::string abs_path) + : Importer(imp), abs_path(abs_path) + { } + }; + + class Resources { + public: + char* contents; + char* srcmap; + public: + Resources(char* contents, char* srcmap) + : contents(contents), srcmap(srcmap) + { } + }; + + class StyleSheet : public Resources { + public: + Block* root; + public: + StyleSheet(const Resources& res, Block* root) + : Resources(res), root(root) + { } + }; + + namespace File { + + // try to find/resolve the filename + std::vector resolve_includes(const std::string& root, const std::string& file); + + } + } #endif diff --git a/src/functions.cpp b/src/functions.cpp index 72bbf5aa65..3dae46f658 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -311,9 +311,9 @@ namespace Sass { return SASS_MEMORY_NEW(ctx.mem, Color, pstate, - std::round(w1*color1->r() + w2*color2->r()), - std::round(w1*color1->g() + w2*color2->g()), - std::round(w1*color1->b() + w2*color2->b()), + Sass::round(w1*color1->r() + w2*color2->r()), + Sass::round(w1*color1->g() + w2*color2->g()), + Sass::round(w1*color1->b() + w2*color2->b()), color1->a()*p + color2->a()*(1-p)); } @@ -331,7 +331,7 @@ namespace Sass { double max = std::max(r, std::max(g, b)); double min = std::min(r, std::min(g, b)); - double del = max - min; + double delta = max - min; double h = 0, s = 0, l = (max + min) / 2.0; @@ -339,12 +339,12 @@ namespace Sass { h = s = 0; // achromatic } else { - if (l < 0.5) s = del / (max + min); - else s = del / (2.0 - max - min); + if (l < 0.5) s = delta / (max + min); + else s = delta / (2.0 - max - min); - if (r == max) h = (g - b) / del + (g < b ? 6 : 0); - else if (g == max) h = (b - r) / del + 2; - else if (b == max) h = (r - g) / del + 4; + if (r == max) h = (g - b) / delta + (g < b ? 6 : 0); + else if (g == max) h = (b - r) / delta + 2; + else if (b == max) h = (r - g) / delta + 4; } HSL hsl_struct; @@ -357,8 +357,8 @@ namespace Sass { // hue to RGB helper function double h_to_rgb(double m1, double m2, double h) { - if (h < 0) h += 1; - if (h > 1) h -= 1; + while (h < 0) h += 1; + while (h > 1) h -= 1; if (h*6.0 < 1) return m1 + (m2 - m1)*h*6; if (h*2.0 < 1) return m2; if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6; @@ -384,9 +384,9 @@ namespace Sass { else m2 = (l+s)-(l*s); double m1 = (l*2.0)-m2; // round the results -- consider moving this into the Color constructor - double r = (h_to_rgb(m1, m2, h+1.0/3.0) * 255.0); + double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0); double g = (h_to_rgb(m1, m2, h) * 255.0); - double b = (h_to_rgb(m1, m2, h-1.0/3.0) * 255.0); + double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0); return SASS_MEMORY_NEW(ctx.mem, Color, pstate, r, g, b, a); } @@ -854,10 +854,10 @@ namespace Sass { std::stringstream ss; ss << '#' << std::setw(2) << std::setfill('0'); - ss << std::hex << std::setw(2) << static_cast(std::floor(a+0.5)); - ss << std::hex << std::setw(2) << static_cast(std::floor(r+0.5)); - ss << std::hex << std::setw(2) << static_cast(std::floor(g+0.5)); - ss << std::hex << std::setw(2) << static_cast(std::floor(b+0.5)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(a)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(r)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(g)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(b)); std::string result(ss.str()); for (size_t i = 0, L = result.length(); i < L; ++i) { @@ -1089,7 +1089,7 @@ namespace Sass { Number* n = ARG("$number", Number); Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n); r->pstate(pstate); - r->value(std::floor(r->value() + 0.5)); + r->value(Sass::round(r->value())); return r; } @@ -1668,13 +1668,13 @@ namespace Sass { } else { bool parentheses = v->concrete_type() == Expression::MAP || v->concrete_type() == Expression::LIST; - Output_Style old_style; - old_style = ctx.output_style; - ctx.output_style = NESTED; + Sass_Output_Style old_style; + old_style = ctx.c_options->output_style; + ctx.c_options->output_style = SASS_STYLE_NESTED; To_String to_string(&ctx, false); std::string inspect = v->perform(&to_string); if (inspect.empty() && parentheses) inspect = "()"; - ctx.output_style = old_style; + ctx.c_options->output_style = old_style; return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, inspect); } // return v; diff --git a/src/inspect.cpp b/src/inspect.cpp index 02864178c6..711b2e2c83 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -11,6 +11,7 @@ #include "listize.hpp" #include "color_maps.hpp" #include "utf8/checked.h" +#include "sass_context.hpp" namespace Sass { @@ -26,11 +27,11 @@ namespace Sass { add_open_mapping(block); append_scope_opener(); } - if (output_style() == NESTED) indentation += block->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation += block->tabs(); for (size_t i = 0, L = block->length(); i < L; ++i) { (*block)[i]->perform(this); } - if (output_style() == NESTED) indentation -= block->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation -= block->tabs(); if (!block->is_root()) { append_scope_closer(); add_close_mapping(block); @@ -123,7 +124,7 @@ namespace Sass { if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true; - if (output_style() == NESTED) + if (output_style() == SASS_STYLE_NESTED) indentation += dec->tabs(); append_indentation(); dec->property()->perform(this); @@ -141,7 +142,7 @@ namespace Sass { append_string("!important"); } append_delimiter(); - if (output_style() == NESTED) + if (output_style() == SASS_STYLE_NESTED) indentation -= dec->tabs(); in_declaration = was_decl; } @@ -198,10 +199,17 @@ namespace Sass { append_indentation(); append_token("@import", import); append_mandatory_space(); - append_string(import->file_name()); + append_string(import->imp_path()); append_delimiter(); } + void Inspect::operator()(Imported* import) + { + // import_stack.push_back(import); + import->block()->perform(this); + // import_stack.pop_back(import); + } + void Inspect::operator()(Warning* warning) { append_indentation(); @@ -363,7 +371,7 @@ namespace Sass { void Inspect::operator()(List* list) { std::string sep(list->separator() == SASS_SPACE ? " " : ","); - if (output_style() != COMPRESSED && sep == ",") sep += " "; + if (output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " "; else if (in_media_block && sep != " ") sep += " "; // verified if (list->empty()) return; bool items_output = false; @@ -459,8 +467,8 @@ namespace Sass { void Inspect::operator()(Number* n) { // use values to_string facility - bool compressed = ctx->output_style == COMPRESSED; - std::string res = n->to_string(compressed, (int)ctx->precision); + bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; + std::string res = n->to_string(compressed, (int)ctx->c_options->precision); // output the final token append_token(res, n); } @@ -468,8 +476,8 @@ namespace Sass { void Inspect::operator()(Color* c) { // use values to_string facility - bool compressed = ctx->output_style == COMPRESSED; - std::string res = c->to_string(compressed, (int)ctx->precision); + bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; + std::string res = c->to_string(compressed, (int)ctx->c_options->precision); // output the final token append_token(res, c); } @@ -477,8 +485,8 @@ namespace Sass { void Inspect::operator()(Boolean* b) { // use values to_string facility - bool compressed = ctx->output_style == COMPRESSED; - std::string res = b->to_string(compressed, (int)ctx->precision); + bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; + std::string res = b->to_string(compressed, (int)ctx->c_options->precision); // output the final token append_token(res, b); } @@ -497,8 +505,8 @@ namespace Sass { void Inspect::operator()(String_Constant* s) { // get options from optional? context - int precision = ctx ? (int)ctx->precision : 5; - bool compressed = ctx ? ctx->output_style == COMPRESSED : false; + int precision = ctx ? (int)ctx->c_options->precision : 5; + bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false; // use values to_string facility std::string res(s->to_string(compressed, precision)); // output the final token @@ -508,8 +516,8 @@ namespace Sass { void Inspect::operator()(String_Quoted* s) { // get options from optional? context - int precision = ctx ? (int)ctx->precision : 5; - bool compressed = ctx ? ctx->output_style == COMPRESSED : false; + int precision = ctx ? (int)ctx->c_options->precision : 5; + bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false; // use values to_string facility std::string res(s->to_string(compressed, precision)); // output the final token @@ -612,8 +620,8 @@ namespace Sass { void Inspect::operator()(Null* n) { // use values to_string facility - bool compressed = ctx->output_style == COMPRESSED; - std::string res = n->to_string(compressed, (int)ctx->precision); + bool compressed = output_style() == SASS_STYLE_COMPRESSED; + std::string res = n->to_string(compressed, (int)ctx->c_options->precision); // output the final token append_token(res, n); } @@ -752,7 +760,7 @@ namespace Sass { (*s)[i]->perform(this); } if (s->has_line_break()) { - if (output_style() != COMPACT) { + if (output_style() != SASS_STYLE_COMPACT) { append_optional_linefeed(); } } @@ -774,7 +782,7 @@ namespace Sass { if (head && head->length() != 0) head->perform(this); bool is_empty = !head || head->length() == 0 || head->is_empty_reference(); bool is_tail = head && !head->is_empty_reference() && tail; - if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0; + if (output_style() == SASS_STYLE_COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0; switch (comb) { case Complex_Selector::ANCESTOR_OF: @@ -810,7 +818,7 @@ namespace Sass { } if (tail) tail->perform(this); if (!tail && c->has_line_break()) { - if (output_style() == COMPACT) { + if (output_style() == SASS_STYLE_COMPACT) { append_mandatory_space(); } } diff --git a/src/inspect.hpp b/src/inspect.hpp index b9386eefda..39c2e686ed 100644 --- a/src/inspect.hpp +++ b/src/inspect.hpp @@ -33,6 +33,7 @@ namespace Sass { virtual void operator()(Declaration*); virtual void operator()(Assignment*); virtual void operator()(Import*); + virtual void operator()(Imported*); virtual void operator()(Import_Stub*); virtual void operator()(Warning*); virtual void operator()(Error*); diff --git a/src/json.cpp b/src/json.cpp index dcb3e4beca..e0ef5968fa 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -802,7 +802,7 @@ static bool parse_object(const char **sp, JsonNode **out) bool parse_string(const char **sp, char **out) { const char *s = *sp; - SB sb = { 0, 0, 0}; + SB sb = { 0, 0, 0 }; char throwaway_buffer[4]; /* enough space for a UTF-8 character */ char *b; diff --git a/src/operation.hpp b/src/operation.hpp index 2cc850ebdf..34133eac42 100644 --- a/src/operation.hpp +++ b/src/operation.hpp @@ -23,6 +23,7 @@ namespace Sass { virtual T operator()(Declaration* x) = 0; virtual T operator()(Assignment* x) = 0; virtual T operator()(Import* x) = 0; + virtual T operator()(Imported* x) = 0; virtual T operator()(Import_Stub* x) = 0; virtual T operator()(Warning* x) = 0; virtual T operator()(Error* x) = 0; @@ -103,6 +104,7 @@ namespace Sass { virtual T operator()(Declaration* x) { return static_cast(this)->fallback(x); } virtual T operator()(Assignment* x) { return static_cast(this)->fallback(x); } virtual T operator()(Import* x) { return static_cast(this)->fallback(x); } + virtual T operator()(Imported* x) { return static_cast(this)->fallback(x); } virtual T operator()(Import_Stub* x) { return static_cast(this)->fallback(x); } virtual T operator()(Warning* x) { return static_cast(this)->fallback(x); } virtual T operator()(Error* x) { return static_cast(this)->fallback(x); } diff --git a/src/output.cpp b/src/output.cpp index 2cb0f256d0..e480ac89fe 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,6 +1,7 @@ #include "ast.hpp" #include "output.hpp" #include "to_string.hpp" +#include "sass_context.hpp" namespace Sass { @@ -74,7 +75,7 @@ namespace Sass { // skip all ascii chars if (chr >= 0) continue; // declare the charset - if (output_style() != COMPRESSED) + if (output_style() != SASS_STYLE_COMPRESSED) charset = "@charset \"UTF-8\";" + ctx->linefeed; else charset = "\xEF\xBB\xBF"; @@ -95,7 +96,7 @@ namespace Sass { std::string txt = c->text()->perform(&to_string); // if (indentation && txt == "/**/") return; bool important = c->is_important(); - if (output_style() != COMPRESSED || important) { + if (output_style() != SASS_STYLE_COMPRESSED || important) { if (buffer().size() == 0) { top_nodes.push_back(c); } else { @@ -131,11 +132,12 @@ namespace Sass { if (b->has_non_hoistable()) { decls = true; - if (output_style() == NESTED) indentation += r->tabs(); - if (ctx && ctx->source_comments) { + if (output_style() == SASS_STYLE_NESTED) indentation += r->tabs(); + if (ctx && ctx->c_options->source_comments) { std::stringstream ss; append_indentation(); - ss << "/* line " << r->pstate().line+1 << ", " << r->pstate().path << " */"; + std::string path = Sass::File::abs2rel(r->pstate().path, ctx->cwd()); + ss << "/* line " << r->pstate().line+1 << ", " << path << " */"; append_string(ss.str()); append_optional_linefeed(); } @@ -171,7 +173,7 @@ namespace Sass { stm->perform(this); } } - if (output_style() == NESTED) indentation -= r->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation -= r->tabs(); append_scope_closer(b); } @@ -238,7 +240,7 @@ namespace Sass { return; } - if (output_style() == NESTED) indentation += f->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation += f->tabs(); append_indentation(); append_token("@supports", f); append_mandatory_space(); @@ -274,7 +276,7 @@ namespace Sass { } } - if (output_style() == NESTED) indentation -= f->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation -= f->tabs(); append_scope_closer(); @@ -297,7 +299,7 @@ namespace Sass { } return; } - if (output_style() == NESTED) indentation += m->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation += m->tabs(); append_indentation(); append_token("@media", m); append_mandatory_space(); @@ -311,7 +313,7 @@ namespace Sass { if (i < L - 1) append_special_linefeed(); } - if (output_style() == NESTED) indentation -= m->tabs(); + if (output_style() == SASS_STYLE_NESTED) indentation -= m->tabs(); append_scope_closer(); } @@ -380,7 +382,7 @@ namespace Sass { void Output::operator()(String_Constant* s) { std::string value(s->value()); - if (s->can_compress_whitespace() && output_style() == COMPRESSED) { + if (s->can_compress_whitespace() && output_style() == SASS_STYLE_COMPRESSED) { value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); } if (!in_comment) { diff --git a/src/parser.cpp b/src/parser.cpp index f798e97d0c..fe2c721173 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -76,18 +76,10 @@ namespace Sass { Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true); read_bom(); - if (ctx.queue.size() == 1) { - is_root = true; - Import* pre = SASS_MEMORY_NEW(ctx.mem, Import, pstate); - std::string load_path(ctx.queue[0].load_path); - do_import(load_path, pre, ctx.c_headers, false); - ctx.head_imports = ctx.queue.size() - 1; - if (!pre->urls().empty()) (*root) << pre; - if (!pre->files().empty()) { - for (size_t i = 0, S = pre->files().size(); i < S; ++i) { - (*root) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, pre->files()[i]); - } - } + // custom headers + if (ctx.resources.size() == 1) { + is_root = true; + ctx.apply_custom_headers(root, path, pstate); } block_stack.push_back(root); @@ -222,11 +214,9 @@ namespace Sass { Import* imp = parse_import(); // if it is a url, we only add the statement if (!imp->urls().empty()) (*block) << imp; - // if it is a file(s), we should process them - if (!imp->files().empty()) { - for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->files()[i]); - } + // process all resources now (add Import_Stub nodes) + for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { + (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->incs()[i]); } } @@ -288,105 +278,7 @@ namespace Sass { } // EO parse_block_nodes - void Parser::add_single_file (Import* imp, std::string import_path) { - - std::string extension; - std::string unquoted(unquote(import_path)); - if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css - // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end - extension = unquoted.substr(unquoted.length() - 4, 4); - } - - if (extension == ".css") { - String_Constant* loc = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, unquote(import_path)); - Argument* loc_arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, loc); - Arguments* loc_args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate); - (*loc_args) << loc_arg; - Function_Call* new_url = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", loc_args); - imp->urls().push_back(new_url); - } - else { - std::string current_dir = File::dir_name(path); - std::string resolved(ctx.add_file(current_dir, unquoted, *this)); - if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir, pstate); - imp->files().push_back(resolved); - } - - } - - void Parser::import_single_file (Import* imp, std::string import_path) { - - if (imp->media_queries() || - !unquote(import_path).substr(0, 7).compare("http://") || - !unquote(import_path).substr(0, 8).compare("https://") || - !unquote(import_path).substr(0, 2).compare("//")) - { - imp->urls().push_back(SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, import_path)); - } - else { - add_single_file(imp, import_path); - } - - } - - bool Parser::do_import(const std::string& import_path, Import* imp, std::vector importers, bool only_one) - { - size_t i = 0; - bool has_import = false; - std::string load_path = unquote(import_path); - // std::cerr << "-- " << load_path << "\n"; - for (Sass_Importer_Entry& importer : importers) { - // int priority = sass_importer_get_priority(importer); - Sass_Importer_Fn fn = sass_importer_get_function(importer); - if (Sass_Import_List includes = - fn(load_path.c_str(), importer, ctx.c_compiler) - ) { - Sass_Import_List list = includes; - while (*includes) { ++i; - std::string uniq_path = load_path; - if (!only_one && i) { - std::stringstream pathstrm; - pathstrm << uniq_path << ":" << i; - uniq_path = pathstrm.str(); - } - Sass_Import_Entry include = *includes; - const char *abs_path = sass_import_get_abs_path(include); - char* source = sass_import_take_source(include); - size_t line = sass_import_get_error_line(include); - size_t column = sass_import_get_error_column(include); - const char* message = sass_import_get_error_message(include); - if (message) { - if (line == std::string::npos && column == std::string::npos) error(message, pstate); - else error(message, ParserState(message, source, Position(line, column))); - } else if (source) { - if (abs_path) { - ctx.add_source(uniq_path, abs_path, source); - imp->files().push_back(uniq_path); - size_t i = ctx.queue.size() - 1; - ctx.process_queue_entry(ctx.queue[i], i); - } else { - ctx.add_source(uniq_path, uniq_path, source); - imp->files().push_back(uniq_path); - size_t i = ctx.queue.size() - 1; - ctx.process_queue_entry(ctx.queue[i], i); - } - } else if(abs_path) { - import_single_file(imp, abs_path); - } - ++includes; - } - // deallocate returned memory - sass_delete_import_list(list); - // set success flag - has_import = true; - // break import chain - if (only_one) return true; - } - } - // return result - return has_import; - } - + // parse imports inside the Import* Parser::parse_import() { Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate); @@ -395,7 +287,7 @@ namespace Sass { do { while (lex< block_comment >()); if (lex< quoted_string >()) { - if (!do_import(lexed, imp, ctx.c_importers, true)) + if (!ctx.call_importers(unquote(std::string(lexed)), path, pstate, imp)) { // push single file import // import_single_file(imp, lexed); @@ -421,8 +313,7 @@ namespace Sass { error("malformed URL", pstate); } if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate); - // imp->urls().push_back(result); - to_import.push_back(std::pair("", result)); + to_import.push_back(std::pair("", result)); } else { if (first) error("@import directive requires a url or quoted path", pstate); @@ -440,7 +331,7 @@ namespace Sass { if (location.second) { imp->urls().push_back(location.second); } else { - import_single_file(imp, location.first); + ctx.import_url(imp, location.first, path); } } diff --git a/src/parser.hpp b/src/parser.hpp index 2e6256e2e6..199934f7dc 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -21,13 +21,9 @@ struct Lookahead { namespace Sass { class Parser : public ParserState { - private: - void add_single_file (Import* imp, std::string import_path); - void import_single_file (Import* imp, std::string import_path); public: enum Syntactic_Context { nothing, mixin_def, function_def }; - bool do_import(const std::string& import_path, Import* imp, std::vector importers, bool only_one = true); Context& ctx; std::vector block_stack; diff --git a/src/prelexer.hpp b/src/prelexer.hpp index cdcce90934..4067ce42bd 100644 --- a/src/prelexer.hpp +++ b/src/prelexer.hpp @@ -337,9 +337,6 @@ namespace Sass { const char* ie_keyword_arg_value(const char* src); const char* ie_keyword_arg_property(const char* src); - // match urls - const char* url(const char* src); - // match url() const char* H(const char* src); const char* W(const char* src); diff --git a/src/sass_context.cpp b/src/sass_context.cpp index d66aa67c92..d36b8f3fb9 100644 --- a/src/sass_context.cpp +++ b/src/sass_context.cpp @@ -9,6 +9,7 @@ #include "json.hpp" #include "util.hpp" #include "context.hpp" +#include "sass_context.hpp" #include "ast_fwd_decl.hpp" #include "error_handling.hpp" @@ -17,161 +18,15 @@ extern "C" { using namespace Sass; - // Input behaviours - enum Sass_Input_Style { - SASS_CONTEXT_NULL, - SASS_CONTEXT_FILE, - SASS_CONTEXT_DATA, - SASS_CONTEXT_FOLDER - }; - - // simple linked list - struct string_list { - string_list* next; - char* string; - }; - - // sass config options structure - struct Sass_Options { - - // Precision for fractional numbers - int precision; - - // Output style for the generated css code - // A value from above SASS_STYLE_* constants - enum Sass_Output_Style output_style; - - // Emit comments in the generated CSS indicating - // the corresponding source line. - bool source_comments; - - // embed sourceMappingUrl as data uri - bool source_map_embed; - - // embed include contents in maps - bool source_map_contents; - - // Disable sourceMappingUrl in css output - bool omit_source_map_url; - - // Treat source_string as sass (as opposed to scss) - bool is_indented_syntax_src; - - // The input path is used for source map - // generation. It can be used to define - // something with string compilation or to - // overload the input file path. It is - // set to "stdin" for data contexts and - // to the input file on file contexts. - char* input_path; - - // The output path is used for source map - // generation. Libsass will not write to - // this file, it is just used to create - // information in source-maps etc. - char* output_path; - - // String to be used for indentation - const char* indent; - // String to be used to for line feeds - const char* linefeed; - - // Colon-separated list of paths - // Semicolon-separated on Windows - // Maybe use array interface instead? - char* include_path; - char* plugin_path; - - // Include paths (linked string list) - struct string_list* include_paths; - // Plugin paths (linked string list) - struct string_list* plugin_paths; - - // Path to source map file - // Enables source map generation - // Used to create sourceMappingUrl - char* source_map_file; - - // Directly inserted in source maps - char* source_map_root; - - // Custom functions that can be called from sccs code - Sass_Function_List c_functions; - - // List of custom importers - Sass_Importer_List c_importers; - - // List of custom headers - Sass_Importer_List c_headers; - - }; - - // base for all contexts - struct Sass_Context : Sass_Options - { - - // store context type info - enum Sass_Input_Style type; - - // generated output data - char* output_string; - - // generated source map json - char* source_map_string; - - // error status - int error_status; - char* error_json; - char* error_text; - char* error_message; - // error position - char* error_file; - size_t error_line; - size_t error_column; - const char* error_src; - - // report imported files - char** included_files; - - }; - - // struct for file compilation - struct Sass_File_Context : Sass_Context { - - // no additional fields required - // input_path is already on options - - }; - - // struct for data compilation - struct Sass_Data_Context : Sass_Context { - - // provided source string - char* source_string; - - }; - - // link c and cpp context - struct Sass_Compiler { - // progress status - Sass_Compiler_State state; - // original c context - Sass_Context* c_ctx; - // Sass::Context - Context* cpp_ctx; - // Sass::Block - Block* root; - }; - static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { *to = *from; } #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; } - #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option) \ - type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ + #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \ + type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \ - { free(options->option); options->option = option ? sass_strdup(option) : 0; } + { free(options->option); options->option = option || def ? sass_strdup(option ? option : def) : 0; } #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; } @@ -186,7 +41,7 @@ extern "C" { catch (Error_Invalid& e) { std::stringstream msg_stream; std::string cwd(Sass::File::get_cwd()); - std::string rel_path(Sass::File::resolve_relative_path(e.pstate.path, cwd, cwd)); + std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); std::string msg_prefix("Error: "); bool got_newline = false; @@ -322,19 +177,10 @@ extern "C" { } // generic compilation function (not exported, use file/data compile instead) - static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context::Data cpp_opt) throw() + static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw() { try { - // get input/output path from options - std::string input_path = safe_str(c_ctx->input_path); - std::string output_path = safe_str(c_ctx->output_path); - // maybe we can extract an output path from input path - if (output_path == "" && input_path != "") { - int lastindex = static_cast(input_path.find_last_of(".")); - output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; - } - // convert include path linked list to static array struct string_list* inc = c_ctx->include_paths; // very poor loop to get the length of the linked list @@ -365,31 +211,6 @@ extern "C" { imp = imp->next; } - // transfer the options to c++ - cpp_opt.c_compiler(0) - .c_options(c_ctx) - .input_path(input_path) - .output_path(output_path) - .output_style((Output_Style) c_ctx->output_style) - .is_indented_syntax_src(c_ctx->is_indented_syntax_src) - .source_comments(c_ctx->source_comments) - .source_map_file(safe_str(c_ctx->source_map_file)) - .source_map_root(safe_str(c_ctx->source_map_root)) - .source_map_embed(c_ctx->source_map_embed) - .source_map_contents(c_ctx->source_map_contents) - .omit_source_map_url(c_ctx->omit_source_map_url) - .include_paths_c_str(c_ctx->include_path) - .plugin_paths_c_str(c_ctx->plugin_path) - // .include_paths_array(include_paths) - // .plugin_paths_array(plugin_paths) - .include_paths(std::vector()) - .plugin_paths(std::vector()) - .precision(c_ctx->precision) - .linefeed(c_ctx->linefeed) - .indent(c_ctx->indent); - - // create new c++ Context - Context* cpp_ctx = new Context(cpp_opt); // free intermediate data free(include_paths); free(plugin_paths); @@ -476,15 +297,12 @@ extern "C" { // maybe skip some entries of included files // we do not include stdin for data contexts - bool skip = false; - - // dispatch to the correct render function - if (c_ctx->type == SASS_CONTEXT_FILE) { - root = cpp_ctx->parse_file(); - } else if (c_ctx->type == SASS_CONTEXT_DATA) { - root = cpp_ctx->parse_string(); - skip = true; // skip first entry of includes - } + bool skip = c_ctx->type == SASS_CONTEXT_DATA; + + // dispatch parse call + root = cpp_ctx->parse(); + // abort on errors + if (!root) return 0; // skip all prefixed files? (ToDo: check srcmap) // IMO source-maps should point to headers already @@ -492,10 +310,9 @@ extern "C" { // remove completely once this is tested size_t headers = cpp_ctx->head_imports; - // copy the included files on to the context (dont forget to free) - if (root) - if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) - throw(std::bad_alloc()); + // copy the included files on to the context (dont forget to free later) + if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) + throw(std::bad_alloc()); // return parsed block return root; @@ -510,11 +327,11 @@ extern "C" { } // generic compilation function (not exported, use file/data compile instead) - static int sass_compile_context (Sass_Context* c_ctx, Context::Data cpp_opt) + static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx) { // prepare sass compiler with context and options - Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_opt); + Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_ctx); try { // call each compiler step @@ -570,60 +387,53 @@ extern "C" { if (source_string == 0) { throw(std::runtime_error("Data context created without a source string")); } if (*source_string == 0) { throw(std::runtime_error("Data context created with empty source string")); } ctx->source_string = source_string; + // ctx->srcmap_string = srcmap_string; } catch (...) { handle_errors(ctx); } return ctx; } - struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* c_ctx) + struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) { - if (c_ctx == 0) return 0; - Context::Data cpp_opt = Context::Data(); - cpp_opt.entry_point(c_ctx->input_path); - return sass_prepare_context(c_ctx, cpp_opt); + if (data_ctx == 0) return 0; + Context* cpp_ctx = new Data_Context(data_ctx); + return sass_prepare_context(data_ctx, cpp_ctx); } - struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* c_ctx) + struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) { - if (c_ctx == 0) return 0; - Context::Data cpp_opt = Context::Data(); - cpp_opt.source_c_str(c_ctx->source_string); - c_ctx->source_string = 0; // passed away - return sass_prepare_context(c_ctx, cpp_opt); + if (file_ctx == 0) return 0; + Context* cpp_ctx = new File_Context(file_ctx); + return sass_prepare_context(file_ctx, cpp_ctx); } int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 1; - Sass_Context* c_ctx = data_ctx; - if (c_ctx->error_status) - return c_ctx->error_status; - Context::Data cpp_opt = Context::Data(); + if (data_ctx->error_status) + return data_ctx->error_status; try { if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); } if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } - cpp_opt.source_c_str(data_ctx->source_string); - data_ctx->source_string = 0; // passed away } - catch (...) { return handle_errors(c_ctx) | 1; } - return sass_compile_context(c_ctx, cpp_opt); + catch (...) { return handle_errors(data_ctx) | 1; } + Context* cpp_ctx = new Data_Context(data_ctx); + return sass_compile_context(data_ctx, cpp_ctx); } int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx) { if (file_ctx == 0) return 1; - Sass_Context* c_ctx = file_ctx; - if (c_ctx->error_status) - return c_ctx->error_status; - Context::Data cpp_opt = Context::Data(); + if (file_ctx->error_status) + return file_ctx->error_status; try { if (file_ctx->input_path == 0) { throw(std::runtime_error("File context has no input path")); } if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); } - cpp_opt.entry_point(file_ctx->input_path); } - catch (...) { return handle_errors(c_ctx) | 1; } - return sass_compile_context(c_ctx, cpp_opt); + catch (...) { return handle_errors(file_ctx) | 1; } + Context* cpp_ctx = new File_Context(file_ctx); + return sass_compile_context(file_ctx, cpp_ctx); } int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler) @@ -655,11 +465,11 @@ extern "C" { Context* cpp_ctx = compiler->cpp_ctx; Block* root = compiler->root; // compile the parsed root block - try { compiler->c_ctx->output_string = cpp_ctx->compile_block(root); } + try { compiler->c_ctx->output_string = cpp_ctx->render(root); } // pass catched errors to generic error handler catch (...) { return handle_errors(compiler->c_ctx) | 1; } // generate source map json and store on context - compiler->c_ctx->source_map_string = cpp_ctx->generate_source_map(); + compiler->c_ctx->source_map_string = cpp_ctx->render_srcmap(); // success return 0; } @@ -788,6 +598,7 @@ extern "C" { // clean the source string if it was not passed // we reset this member once we start parsing if (ctx->source_string) free(ctx->source_string); + if (ctx->srcmap_string) free(ctx->srcmap_string); // clear the context and free it sass_clear_context(ctx); free(ctx); } @@ -829,12 +640,12 @@ extern "C" { IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0); // Create getter and setters for context IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status); diff --git a/src/sass_context.hpp b/src/sass_context.hpp new file mode 100644 index 0000000000..c09e6d8c4a --- /dev/null +++ b/src/sass_context.hpp @@ -0,0 +1,154 @@ +#ifndef SASS_SASS_CONTEXT_H +#define SASS_SASS_CONTEXT_H + +#include "sass.h" + +// Input behaviours +enum Sass_Input_Style { + SASS_CONTEXT_NULL, + SASS_CONTEXT_FILE, + SASS_CONTEXT_DATA, + SASS_CONTEXT_FOLDER +}; + +// simple linked list +struct string_list { + string_list* next; + char* string; +}; + +// sass config options structure +struct Sass_Options { + + // Precision for fractional numbers + int precision; + + // Output style for the generated css code + // A value from above SASS_STYLE_* constants + enum Sass_Output_Style output_style; + + // Emit comments in the generated CSS indicating + // the corresponding source line. + bool source_comments; + + // embed sourceMappingUrl as data uri + bool source_map_embed; + + // embed include contents in maps + bool source_map_contents; + + // Disable sourceMappingUrl in css output + bool omit_source_map_url; + + // Treat source_string as sass (as opposed to scss) + bool is_indented_syntax_src; + + // The input path is used for source map + // generation. It can be used to define + // something with string compilation or to + // overload the input file path. It is + // set to "stdin" for data contexts and + // to the input file on file contexts. + char* input_path; + + // The output path is used for source map + // generation. Libsass will not write to + // this file, it is just used to create + // information in source-maps etc. + char* output_path; + + // String to be used for indentation + const char* indent; + // String to be used to for line feeds + const char* linefeed; + + // Colon-separated list of paths + // Semicolon-separated on Windows + // Maybe use array interface instead? + char* include_path; + char* plugin_path; + + // Include paths (linked string list) + struct string_list* include_paths; + // Plugin paths (linked string list) + struct string_list* plugin_paths; + + // Path to source map file + // Enables source map generation + // Used to create sourceMappingUrl + char* source_map_file; + + // Directly inserted in source maps + char* source_map_root; + + // Custom functions that can be called from sccs code + Sass_Function_List c_functions; + + // List of custom importers + Sass_Importer_List c_importers; + + // List of custom headers + Sass_Importer_List c_headers; + +}; + + +// base for all contexts +struct Sass_Context : Sass_Options +{ + + // store context type info + enum Sass_Input_Style type; + + // generated output data + char* output_string; + + // generated source map json + char* source_map_string; + + // error status + int error_status; + char* error_json; + char* error_text; + char* error_message; + // error position + char* error_file; + size_t error_line; + size_t error_column; + const char* error_src; + + // report imported files + char** included_files; + +}; + +// struct for file compilation +struct Sass_File_Context : Sass_Context { + + // no additional fields required + // input_path is already on options + +}; + +// struct for data compilation +struct Sass_Data_Context : Sass_Context { + + // provided source string + char* source_string; + char* srcmap_string; + +}; + +// link c and cpp context +struct Sass_Compiler { + // progress status + Sass_Compiler_State state; + // original c context + Sass_Context* c_ctx; + // Sass::Context + Sass::Context* cpp_ctx; + // Sass::Block + Sass::Block* root; +}; + +#endif \ No newline at end of file diff --git a/src/sass_functions.cpp b/src/sass_functions.cpp index 8be36f3b64..1b328cb9b7 100644 --- a/src/sass_functions.cpp +++ b/src/sass_functions.cpp @@ -2,17 +2,11 @@ #include "util.hpp" #include "context.hpp" #include "sass/functions.h" +#include "sass_functions.hpp" extern "C" { using namespace Sass; - // Struct to hold custom function callback - struct Sass_Function { - const char* signature; - Sass_Function_Fn function; - void* cookie; - }; - Sass_Function_List ADDCALL sass_make_function_list(size_t length) { return (Sass_Function_List) calloc(length + 1, sizeof(Sass_Function_Entry)); @@ -36,25 +30,6 @@ extern "C" { Sass_Function_Fn ADDCALL sass_function_get_function(Sass_Function_Entry cb) { return cb->function; } void* ADDCALL sass_function_get_cookie(Sass_Function_Entry cb) { return cb->cookie; } - // External import entry - struct Sass_Import { - char* imp_path; // path as found in the import statement - char *abs_path; // path after importer has resolved it - char* source; - char* srcmap; - // error handling - char* error; - size_t line; - size_t column; - }; - - // Struct to hold importer callback - struct Sass_Importer { - Sass_Importer_Fn importer; - double priority; - void* cookie; - }; - Sass_Importer_Entry ADDCALL sass_make_importer(Sass_Importer_Fn importer, double priority, void* cookie) { Sass_Importer_Entry cb = (Sass_Importer_Entry) calloc(1, sizeof(Sass_Importer)); diff --git a/src/sass_functions.hpp b/src/sass_functions.hpp new file mode 100644 index 0000000000..5a7865d775 --- /dev/null +++ b/src/sass_functions.hpp @@ -0,0 +1,32 @@ +#ifndef SASS_SASS_FUNCTIONS_H +#define SASS_SASS_FUNCTIONS_H + +#include "sass.h" + +// Struct to hold custom function callback +struct Sass_Function { + const char* signature; + Sass_Function_Fn function; + void* cookie; +}; + +// External import entry +struct Sass_Import { + char* imp_path; // path as found in the import statement + char *abs_path; // path after importer has resolved it + char* source; + char* srcmap; + // error handling + char* error; + size_t line; + size_t column; +}; + +// Struct to hold importer callback +struct Sass_Importer { + Sass_Importer_Fn importer; + double priority; + void* cookie; +}; + +#endif \ No newline at end of file diff --git a/src/sass_interface.cpp b/src/sass_interface.cpp index 0156bc593d..9284386087 100644 --- a/src/sass_interface.cpp +++ b/src/sass_interface.cpp @@ -69,26 +69,8 @@ extern "C" { else { output_path = c_ctx->output_path; } - Context cpp_ctx( - Context::Data().source_c_str(c_ctx->source_string) - .output_path(output_path) - .output_style((Output_Style) c_ctx->options.output_style) - .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src) - .source_comments(c_ctx->options.source_comments) - .source_map_file(safe_str(c_ctx->options.source_map_file)) - .source_map_root(safe_str(c_ctx->options.source_map_root)) - .source_map_embed(c_ctx->options.source_map_embed) - .source_map_contents(c_ctx->options.source_map_contents) - .omit_source_map_url(c_ctx->options.omit_source_map_url) - .include_paths_c_str(c_ctx->options.include_paths) - .plugin_paths_c_str(c_ctx->options.plugin_paths) - // .include_paths_array(0) - // .plugin_paths_array(0) - .include_paths(std::vector()) - .plugin_paths(std::vector()) - .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) - .indent(c_ctx->options.indent ? c_ctx->options.indent : " ") - .linefeed(c_ctx->options.linefeed ? c_ctx->options.linefeed : LFEED) + Data_Context cpp_ctx( + (Sass_Data_Context*) 0 ); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; @@ -97,8 +79,9 @@ extern "C" { ++this_func_data; } } - c_ctx->output_string = cpp_ctx.compile_string(); - c_ctx->source_map_string = cpp_ctx.generate_source_map(); + Block* root = cpp_ctx.parse(); + c_ctx->output_string = cpp_ctx.render(root); + c_ctx->source_map_string = cpp_ctx.render_srcmap(); c_ctx->error_message = 0; c_ctx->error_status = 0; @@ -162,26 +145,8 @@ extern "C" { else { output_path = c_ctx->output_path; } - Context cpp_ctx( - Context::Data().entry_point(input_path) - .output_path(output_path) - .output_style((Output_Style) c_ctx->options.output_style) - .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src) - .source_comments(c_ctx->options.source_comments) - .source_map_file(safe_str(c_ctx->options.source_map_file)) - .source_map_root(safe_str(c_ctx->options.source_map_root)) - .source_map_embed(c_ctx->options.source_map_embed) - .source_map_contents(c_ctx->options.source_map_contents) - .omit_source_map_url(c_ctx->options.omit_source_map_url) - .include_paths_c_str(c_ctx->options.include_paths) - .plugin_paths_c_str(c_ctx->options.plugin_paths) - // .include_paths_array(0) - // .plugin_paths_array(0) - .include_paths(std::vector()) - .plugin_paths(std::vector()) - .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) - .indent(c_ctx->options.indent ? c_ctx->options.indent : " ") - .linefeed(c_ctx->options.linefeed ? c_ctx->options.linefeed : LFEED) + File_Context cpp_ctx( + (Sass_File_Context*) 0 ); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; @@ -190,8 +155,9 @@ extern "C" { ++this_func_data; } } - c_ctx->output_string = cpp_ctx.compile_file(); - c_ctx->source_map_string = cpp_ctx.generate_source_map(); + Block* root = cpp_ctx.parse(); + c_ctx->output_string = cpp_ctx.render(root); + c_ctx->source_map_string = cpp_ctx.render_srcmap(); c_ctx->error_message = 0; c_ctx->error_status = 0; diff --git a/src/sass_values.cpp b/src/sass_values.cpp index de72a28231..25919fdbcf 100644 --- a/src/sass_values.cpp +++ b/src/sass_values.cpp @@ -4,85 +4,11 @@ #include "eval.hpp" #include "values.hpp" #include "sass/values.h" +#include "sass_values.hpp" extern "C" { using namespace Sass; - struct Sass_Unknown { - enum Sass_Tag tag; - }; - - struct Sass_Boolean { - enum Sass_Tag tag; - bool value; - }; - - struct Sass_Number { - enum Sass_Tag tag; - double value; - char* unit; - }; - - struct Sass_Color { - enum Sass_Tag tag; - double r; - double g; - double b; - double a; - }; - - struct Sass_String { - enum Sass_Tag tag; - bool quoted; - char* value; - }; - - struct Sass_List { - enum Sass_Tag tag; - enum Sass_Separator separator; - size_t length; - // null terminated "array" - union Sass_Value** values; - }; - - struct Sass_Map { - enum Sass_Tag tag; - size_t length; - struct Sass_MapPair* pairs; - }; - - struct Sass_Null { - enum Sass_Tag tag; - }; - - struct Sass_Error { - enum Sass_Tag tag; - char* message; - }; - - struct Sass_Warning { - enum Sass_Tag tag; - char* message; - }; - - union Sass_Value { - struct Sass_Unknown unknown; - struct Sass_Boolean boolean; - struct Sass_Number number; - struct Sass_Color color; - struct Sass_String string; - struct Sass_List list; - struct Sass_Map map; - struct Sass_Null null; - struct Sass_Error error; - struct Sass_Warning warning; - }; - - struct Sass_MapPair { - union Sass_Value* key; - union Sass_Value* value; - }; - // Return the sass tag for a generic sass value enum Sass_Tag ADDCALL sass_value_get_tag(const union Sass_Value* v) { return v->unknown.tag; } @@ -412,7 +338,7 @@ extern "C" { // simply pass the error message back to the caller for now catch (Error_Invalid& e) { return sass_make_error(e.message.c_str()); } - catch (std::bad_alloc& ba) { return sass_make_error("memory exhausted"); } + catch (std::bad_alloc&) { return sass_make_error("memory exhausted"); } catch (std::exception& e) { return sass_make_error(e.what()); } catch (std::string& e) { return sass_make_error(e.c_str()); } catch (const char* e) { return sass_make_error(e); } diff --git a/src/sass_values.hpp b/src/sass_values.hpp new file mode 100644 index 0000000000..b9e9ebfcc9 --- /dev/null +++ b/src/sass_values.hpp @@ -0,0 +1,81 @@ +#ifndef SASS_SASS_VALUES_H +#define SASS_SASS_VALUES_H + +#include "sass.h" + +struct Sass_Unknown { + enum Sass_Tag tag; +}; + +struct Sass_Boolean { + enum Sass_Tag tag; + bool value; +}; + +struct Sass_Number { + enum Sass_Tag tag; + double value; + char* unit; +}; + +struct Sass_Color { + enum Sass_Tag tag; + double r; + double g; + double b; + double a; +}; + +struct Sass_String { + enum Sass_Tag tag; + bool quoted; + char* value; +}; + +struct Sass_List { + enum Sass_Tag tag; + enum Sass_Separator separator; + size_t length; + // null terminated "array" + union Sass_Value** values; +}; + +struct Sass_Map { + enum Sass_Tag tag; + size_t length; + struct Sass_MapPair* pairs; +}; + +struct Sass_Null { + enum Sass_Tag tag; +}; + +struct Sass_Error { + enum Sass_Tag tag; + char* message; +}; + +struct Sass_Warning { + enum Sass_Tag tag; + char* message; +}; + +union Sass_Value { + struct Sass_Unknown unknown; + struct Sass_Boolean boolean; + struct Sass_Number number; + struct Sass_Color color; + struct Sass_String string; + struct Sass_List list; + struct Sass_Map map; + struct Sass_Null null; + struct Sass_Error error; + struct Sass_Warning warning; +}; + +struct Sass_MapPair { + union Sass_Value* key; + union Sass_Value* value; +}; + +#endif \ No newline at end of file diff --git a/src/source_map.cpp b/src/source_map.cpp index 9772b74ed9..4279df4ca0 100644 --- a/src/source_map.cpp +++ b/src/source_map.cpp @@ -9,16 +9,17 @@ #include "context.hpp" #include "position.hpp" #include "source_map.hpp" +#include "sass_context.hpp" namespace Sass { SourceMap::SourceMap() : current_position(0, 0, 0), file("stdin") { } SourceMap::SourceMap(const std::string& file) : current_position(0, 0, 0), file(file) { } - std::string SourceMap::generate_source_map(Context &ctx) { + std::string SourceMap::render_srcmap(Context &ctx) { - const bool include_sources = ctx.source_map_contents; - const std::vector includes = ctx.include_links; - const std::vector sources = ctx.sources; + const bool include_sources = ctx.c_options->source_map_contents; + const std::vector links = ctx.srcmap_links; + const std::vector& sources(ctx.resources); JsonNode* json_srcmap = json_mkobject(); @@ -36,7 +37,7 @@ namespace Sass { JsonNode *json_includes = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { - const char *include = includes[source_index[i]].c_str(); + const char *include = links[source_index[i]].c_str(); JsonNode *json_include = json_mkstring(include); json_append_element(json_includes, json_include); } @@ -45,8 +46,8 @@ namespace Sass { if (include_sources) { JsonNode *json_contents = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { - const char *content = sources[source_index[i]]; - JsonNode *json_content = json_mkstring(content); + const Resources& resource(sources[source_index[i]]); + JsonNode *json_content = json_mkstring(resource.contents); json_append_element(json_contents, json_content); } if (json_contents->children.head) diff --git a/src/source_map.hpp b/src/source_map.hpp index 1df92b78fa..5776e712e5 100644 --- a/src/source_map.hpp +++ b/src/source_map.hpp @@ -24,9 +24,6 @@ namespace Sass { SourceMap(); SourceMap(const std::string& file); - void setFile(const std::string& str) { - file = str; - } void append(const Offset& offset); void prepend(const Offset& offset); void append(const OutputBuffer& out); @@ -34,7 +31,7 @@ namespace Sass { void add_open_mapping(AST_Node* node); void add_close_mapping(AST_Node* node); - std::string generate_source_map(Context &ctx); + std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate); private: diff --git a/src/util.cpp b/src/util.cpp index 5fb9f40516..29c0677921 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -6,15 +6,24 @@ #include "constants.hpp" #include "utf8/checked.h" +#include #include namespace Sass { - #define out_of_memory() do { \ - fprintf(stderr, "Out of memory.\n"); \ - exit(EXIT_FAILURE); \ + #define out_of_memory() do { \ + std::cerr << "Out of memory.\n"; \ + exit(EXIT_FAILURE); \ } while (0) + double round(double val) + { + using namespace std; + // sometimes I saw numbers like 25.5 rounded to 25 + // not sure why converting to a float solves this + return ::round(val); + } + /* Sadly, sass_strdup is not portable. */ char *sass_strdup(const char *str) { @@ -49,8 +58,8 @@ namespace Sass { } // helper for safe access to c_ctx - const char* safe_str (const char* str) { - return str == NULL ? "" : str; + const char* safe_str (const char* str, const char* alt) { + return str == NULL ? alt : str; } void free_string_array(char ** arr) { @@ -571,7 +580,7 @@ namespace Sass { } } - bool isPrintable(Ruleset* r, Output_Style style) { + bool isPrintable(Ruleset* r, Sass_Output_Style style) { if (r == NULL) { return false; } @@ -597,7 +606,7 @@ namespace Sass { } } else if (Comment* c = dynamic_cast(stm)) { // keep for uncompressed - if (style != COMPRESSED) { + if (style != SASS_STYLE_COMPRESSED) { hasDeclarations = true; } // output style compressed @@ -618,17 +627,17 @@ namespace Sass { return false; } - bool isPrintable(String_Constant* s, Output_Style style) + bool isPrintable(String_Constant* s, Sass_Output_Style style) { return ! s->value().empty(); } - bool isPrintable(String_Quoted* s, Output_Style style) + bool isPrintable(String_Quoted* s, Sass_Output_Style style) { return true; } - bool isPrintable(Declaration* d, Output_Style style) + bool isPrintable(Declaration* d, Sass_Output_Style style) { Expression* val = d->value(); if (String_Quoted* sq = dynamic_cast(val)) return isPrintable(sq, style); @@ -636,7 +645,7 @@ namespace Sass { return true; } - bool isPrintable(Supports_Block* f, Output_Style style) { + bool isPrintable(Supports_Block* f, Sass_Output_Style style) { if (f == NULL) { return false; } @@ -673,7 +682,7 @@ namespace Sass { return false; } - bool isPrintable(Media_Block* m, Output_Style style) + bool isPrintable(Media_Block* m, Sass_Output_Style style) { if (m == 0) return false; Block* b = m->block(); @@ -689,7 +698,7 @@ namespace Sass { return false; } - bool isPrintable(Block* b, Output_Style style) { + bool isPrintable(Block* b, Sass_Output_Style style) { if (b == NULL) { return false; } @@ -702,7 +711,7 @@ namespace Sass { else if (typeid(*stm) == typeid(Comment)) { Comment* c = (Comment*) stm; // keep for uncompressed - if (style != COMPRESSED) { + if (style != SASS_STYLE_COMPRESSED) { return true; } // output style compressed diff --git a/src/util.hpp b/src/util.hpp index 58c6e3f895..ecdfe1297d 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -4,15 +4,17 @@ #include #include #include +#include "sass.h" #include "ast_fwd_decl.hpp" #define SASS_ASSERT(cond, msg) assert(cond && msg) namespace Sass { + double round(double val); char* sass_strdup(const char* str); double sass_atof(const char* str); - const char* safe_str(const char *); + const char* safe_str(const char *, const char* = ""); void free_string_array(char **); char **copy_strings(const std::vector&, char ***, int = 0); std::string string_escape(const std::string& str); @@ -45,13 +47,13 @@ namespace Sass { std::string vecJoin(const std::vector& vec, const std::string& sep); bool containsAnyPrintableStatements(Block* b); - bool isPrintable(Ruleset* r, Output_Style style = NESTED); - bool isPrintable(Supports_Block* r, Output_Style style = NESTED); - bool isPrintable(Media_Block* r, Output_Style style = NESTED); - bool isPrintable(Block* b, Output_Style style = NESTED); - bool isPrintable(String_Constant* s, Output_Style style = NESTED); - bool isPrintable(String_Quoted* s, Output_Style style = NESTED); - bool isPrintable(Declaration* d, Output_Style style = NESTED); + bool isPrintable(Ruleset* r, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(Supports_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(Media_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(Block* b, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(String_Constant* s, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(String_Quoted* s, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(Declaration* d, Sass_Output_Style style = SASS_STYLE_NESTED); bool isAscii(const char chr); }