diff --git a/Makefile b/Makefile index 159d75b380..746f76ba5f 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,11 @@ SOURCES = \ prelexer.cpp \ remove_placeholders.cpp \ sass.cpp \ - sass_interface.cpp \ sass_util.cpp \ + sass_values.cpp \ + sass_context.cpp \ + sass_functions.cpp \ + sass_interface.cpp \ sass2scss.cpp \ source_map.cpp \ to_c.cpp \ diff --git a/Makefile.am b/Makefile.am index 8ca73b9c5a..72d7583a92 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,8 +39,11 @@ libsass_la_SOURCES = \ prelexer.cpp \ remove_placeholders.cpp \ sass.cpp \ - sass_interface.cpp \ sass_util.cpp \ + sass_values.cpp \ + sass_context.cpp \ + sass_functions.cpp \ + sass_interface.cpp \ sass2scss.cpp \ source_map.cpp \ to_c.cpp \ @@ -50,13 +53,13 @@ libsass_la_SOURCES = \ util.cpp libsass_la_CXXFLAGS = $(AM_CXXFLAGS) -std=c++0x -libsass_la_LDFLAGS = -no-undefined -version-info 0:0:0 +libsass_la_LDFLAGS = -no-undefined -version-info 0:9:0 if ENABLE_COVERAGE libsass_la_LDFLAGS += -lgcov endif -include_HEADERS = sass2scss.h sass_interface.h sass.h +include_HEADERS = sass2scss.h sass_context.h sass_functions.h sass_values.h sass.h if ENABLE_TESTS diff --git a/ast.hpp b/ast.hpp index 0ae897f30d..08287c44be 100644 --- a/ast.hpp +++ b/ast.hpp @@ -43,9 +43,9 @@ #include "environment.hpp" #endif -#ifndef SASS #include "sass.h" -#endif +#include "sass_values.h" +#include "sass_functions.h" #include "units.hpp" diff --git a/ast_def_macros.hpp b/ast_def_macros.hpp index 663e4c40b3..40c77a7024 100644 --- a/ast_def_macros.hpp +++ b/ast_def_macros.hpp @@ -5,7 +5,7 @@ virtual Statement* perform(Operation* op) { return (*op)(this); }\ virtual Expression* perform(Operation* op) { return (*op)(this); }\ virtual Selector* perform(Operation* op) { return (*op)(this); }\ virtual string perform(Operation* op) { return (*op)(this); }\ -virtual Sass_Value perform(Operation* op) { return (*op)(this); } +virtual Sass_Value* perform(Operation* op) { return (*op)(this); } #define ADD_PROPERTY(type, name)\ protected:\ diff --git a/context.cpp b/context.cpp index b1f9f8aa69..a1fba715b5 100644 --- a/context.cpp +++ b/context.cpp @@ -52,8 +52,9 @@ namespace Sass { queue (vector >()), style_sheets (map()), source_map (resolve_relative_path(initializers.output_path(), initializers.source_map_file(), get_cwd())), - c_functions (vector()), + c_functions (vector()), image_path (initializers.image_path()), + 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()), @@ -62,6 +63,7 @@ namespace Sass { 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()), + importer (initializers.importer()), names_to_colors (map()), colors_to_names (map()), precision (initializers.precision()), @@ -70,6 +72,12 @@ namespace Sass { { cwd = get_cwd(); + // enforce some safe defaults + // used to create relative file links + if (input_path == "") input_path = "stdin"; + if (output_path == "") output_path = "stdout"; + + include_paths.push_back(cwd); collect_include_paths(initializers.include_paths_c_str()); collect_include_paths(initializers.include_paths_array()); @@ -110,7 +118,6 @@ namespace Sass { void Context::collect_include_paths(const char* paths_str) { - include_paths.push_back(cwd); if (paths_str) { const char* beg = paths_str; @@ -134,23 +141,17 @@ namespace Sass { } } - void Context::collect_include_paths(const char* paths_array[]) + void Context::collect_include_paths(const char** paths_array) { - include_paths.push_back(get_cwd()); if (*include_paths.back().rbegin() != '/') include_paths.back() += '/'; - - // if (paths_array) { - // for (size_t i = 0; paths_array[i]; ++i) { - // string path(paths_array[i]); - // if (!path.empty()) { - // if (*path.rbegin() != '/') path += '/'; - // include_paths.push_back(path); - // } - // } - // } + if (paths_array) { + for (size_t i = 0; paths_array[i]; i++) { + collect_include_paths(paths_array[i]); + } + } } - void Context::add_source(const string& load_path, const string& abs_path, const char* contents) + void Context::add_source(string load_path, string abs_path, const char* contents) { sources.push_back(contents); included_files.push_back(abs_path); @@ -209,10 +210,39 @@ namespace Sass { void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env); void register_overload_stub(Context&, string name, Env* env); void register_built_in_functions(Context&, Env* env); - void register_c_functions(Context&, Env* env, Sass_C_Function_Descriptor*); - void register_c_function(Context&, Env* env, Sass_C_Function_Descriptor); + void register_c_functions(Context&, Env* env, Sass_C_Function_List); + void register_c_function(Context&, Env* env, Sass_C_Function_Callback); - char* Context::compile_file() + char* Context::compile_block(Block* root) + { + char* result = 0; + if (!root) return 0; + switch (output_style) { + case COMPRESSED: { + Output_Compressed output_compressed(this); + root->perform(&output_compressed); + string output = output_compressed.get_buffer(); + if (source_map_file != "" && !omit_source_map_url) { + output += format_source_mapping_url(source_map_file); + } + result = copy_c_str(output.c_str()); + } break; + + default: { + Output_Nested output_nested(source_comments, this); + root->perform(&output_nested); + string output = output_nested.get_buffer(); + if (source_map_file != "" && !omit_source_map_url) { + output += "\n" + format_source_mapping_url(source_map_file); + } + result = copy_c_str(output.c_str()); + + } break; + } + return result; + } + + Block* Context::parse_file() { Block* root = 0; for (size_t i = 0; i < queue.size(); ++i) { @@ -225,7 +255,7 @@ namespace Sass { Backtrace backtrace(0, "", Position(), ""); register_built_in_functions(*this, &tge); for (size_t i = 0, S = c_functions.size(); i < S; ++i) { - register_c_function(*this, &tge, c_functions[i]); + register_c_function(*this, &tge, c_functions[i]); } Eval eval(*this, &tge, &backtrace); Contextualize contextualize(*this, &eval, &tge, &backtrace); @@ -242,31 +272,32 @@ namespace Sass { Remove_Placeholders remove_placeholders(*this); root->perform(&remove_placeholders); - char* result = 0; - switch (output_style) { - case COMPRESSED: { - Output_Compressed output_compressed(this); - root->perform(&output_compressed); - string output = output_compressed.get_buffer(); - if (source_map_file != "" && !omit_source_map_url) { - output += format_source_mapping_url(source_map_file); - } - result = copy_c_str(output.c_str()); - } break; - - default: { - Output_Nested output_nested(source_comments, this); - root->perform(&output_nested); - string output = output_nested.get_buffer(); - if (source_map_file != "" && !omit_source_map_url) { - output += "\n" + format_source_mapping_url(source_map_file); - } - result = copy_c_str(output.c_str()); + return root; + } - } break; + 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); + add_source(input_path, input_path, contents); + return parse_file(); } + add_source(input_path, input_path, strdup(source_c_str)); + return parse_file(); + } - return result; + 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()); } string Context::format_source_mapping_url(const string& file) @@ -293,27 +324,15 @@ namespace Sass { return result; } - // allow to optionally overwrite the input path - // default argument for input_path is string("stdin") - // usefull to influence the source-map generating etc. - char* Context::compile_string(const string& input_path) - { - if (!source_c_str) return 0; - queue.clear(); - if(is_indented_syntax_src) { - char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1); - add_source(input_path, input_path, contents); - return compile_file(); - } - add_source(input_path, input_path, strdup(source_c_str)); - return compile_file(); - } - std::vector Context::get_included_files() + std::vector Context::get_included_files(size_t skip) { - std::sort(included_files.begin(), included_files.end()); - included_files.erase( std::unique( included_files.begin(), included_files.end() ), included_files.end()); - return included_files; + vector includes = included_files; + std::sort( includes.begin() + skip, includes.end() ); + includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() ); + // the skip solution seems more robust, as we may have real files named stdin + // includes.erase( std::remove( includes.begin(), includes.end(), "stdin" ), includes.end() ); + return includes; } string Context::get_cwd() @@ -448,16 +467,21 @@ namespace Sass { register_function(ctx, image_url_sig, image_url, env); } - void register_c_functions(Context& ctx, Env* env, Sass_C_Function_Descriptor* descrs) + void register_c_functions(Context& ctx, Env* env, Sass_C_Function_List descrs) { - while (descrs->signature && descrs->function) { + while (descrs && *descrs) { register_c_function(ctx, env, *descrs); ++descrs; } } - void register_c_function(Context& ctx, Env* env, Sass_C_Function_Descriptor descr) + void register_c_function(Context& ctx, Env* env, Sass_C_Function_Callback descr) { - Definition* def = make_c_function(descr.signature, descr.function, descr.cookie, ctx); + Definition* def = make_c_function( + sass_function_get_signature(descr), + sass_function_get_function(descr), + sass_function_get_cookie(descr), + ctx + ); def->environment(env); (*env)[def->name() + "[f]"] = def; } diff --git a/context.hpp b/context.hpp index 1ded5155a5..8bffb87cdb 100644 --- a/context.hpp +++ b/context.hpp @@ -24,6 +24,8 @@ #define BUFFERSIZE 255 #include "b64/encode.h" +#include "sass_functions.h" + struct Sass_C_Function_Descriptor; namespace Sass { @@ -58,9 +60,10 @@ namespace Sass { vector > queue; // queue of files to be parsed map style_sheets; // map of paths to ASTs SourceMap source_map; - vector c_functions; + vector c_functions; string image_path; // for the image-url Sass function + string input_path; // for relative paths in src-map 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 @@ -70,6 +73,9 @@ namespace Sass { 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 + Sass_C_Import_Callback importer; + map names_to_colors; map colors_to_names; @@ -79,6 +85,7 @@ namespace Sass { KWD_ARG_SET(Data) { KWD_ARG(Data, const char*, source_c_str); KWD_ARG(Data, string, entry_point); + KWD_ARG(Data, string, input_path); KWD_ARG(Data, string, output_path); KWD_ARG(Data, string, image_path); KWD_ARG(Data, const char*, include_paths_c_str); @@ -93,26 +100,30 @@ namespace Sass { KWD_ARG(Data, bool, _skip_source_map_update); KWD_ARG(Data, bool, source_map_embed); KWD_ARG(Data, bool, source_map_contents); + KWD_ARG(Data, Sass_C_Import_Callback, importer); }; Context(Data); ~Context(); - void collect_include_paths(const char* paths_str); - void collect_include_paths(const char* paths_array[]); void setup_color_map(); string add_file(string); + Block* parse_file(); string add_file(string, string); + Block* parse_string(); + void add_source(string, string, const char*); // allow to optionally overwrite the input path // default argument for input_path is string("stdin") // usefull to influence the source-map generating etc. - char* compile_string(const string& input_path = "stdin"); char* compile_file(); + char* compile_string(); + char* compile_block(Block* root); char* generate_source_map(); - vector get_included_files(); + vector get_included_files(size_t skip = 0); private: - void add_source(const string &load_path, const string &abs_path, const char* contents); + void collect_include_paths(const char* paths_str); + void collect_include_paths(const char** paths_array); string format_source_mapping_url(const string& file); string get_cwd(); diff --git a/eval.cpp b/eval.cpp index 0a763db631..5bc95481e8 100644 --- a/eval.cpp +++ b/eval.cpp @@ -180,6 +180,25 @@ namespace Sass { { Expression* message = w->message()->perform(this); To_String to_string; + + // try to use generic function + if (env->has("@warn[f]")) { + + Definition* def = static_cast((*env)["@warn[f]"]); + // Block* body = def->block(); + // Native_Function func = def->native_function(); + Sass_C_Function c_func = def->c_function(); + + To_C to_c; + union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); + sass_list_set_value(c_args, 0, message->perform(&to_c)); + Sass_Value* c_val = c_func(c_args, def->cookie()); + sass_delete_value(c_args); + sass_delete_value(c_val); + return 0; + + } + string prefix("WARNING: "); string result(unquote(message->perform(&to_string))); cerr << prefix << result; @@ -313,6 +332,13 @@ namespace Sass { args = static_cast(args->perform(this)); } + // try to use generic function + if (!env->has(full_name)) { + if (env->has("*[f]")) { + full_name = "*[f]"; + } + } + // if it doesn't exist, just pass it through as a literal if (!env->has(full_name)) { Function_Call* lit = new (ctx.mem) Function_Call(c->path(), @@ -382,7 +408,16 @@ namespace Sass { // else if it's a user-defined c function else if (c_func) { - bind("function " + c->name(), params, args, ctx, &new_env, this); + if (full_name != "*[f]") { + bind("function " + c->name(), params, args, ctx, &new_env, this); + } else { + String_Constant *str = new (ctx.mem) String_Constant(c->path(), c->position(), c->name()); + Arguments* new_args = new (ctx.mem) Arguments(c->path(), c->position()); + *new_args << new (ctx.mem) Argument(c->path(), c->position(), str); + *new_args += args; + args = new_args; + } + Env* old_env = env; env = &new_env; @@ -390,15 +425,16 @@ namespace Sass { backtrace = &here; To_C to_c; - union Sass_Value c_args = args->perform(&to_c); - Sass_Value c_val = c_func(c_args, def->cookie()); - if (c_val.unknown.tag == SASS_ERROR) { - error("error in C function " + c->name() + ": " + c_val.error.message, c->path(), c->position(), backtrace); + union Sass_Value* c_args = args->perform(&to_c); + Sass_Value* c_val = c_func(c_args, def->cookie()); + if (sass_value_get_tag(c_val) == SASS_ERROR) { + error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->path(), c->position(), backtrace); } result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->position()); backtrace = here.parent; - free_sass_value(c_val); + sass_delete_value(c_args); + sass_delete_value(c_val); env = old_env; } // else it's an overloaded native function; resolve it @@ -940,37 +976,37 @@ namespace Sass { unquoted ? result : quote(result, q)); } - Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path, Position position) + Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, string path, Position position) { using std::strlen; using std::strcpy; Expression* e = 0; - switch (v.unknown.tag) { + switch (sass_value_get_tag(v)) { case SASS_BOOLEAN: { - e = new (ctx.mem) Boolean(path, position, !!v.boolean.value); + e = new (ctx.mem) Boolean(path, position, !!sass_boolean_get_value(v)); } break; case SASS_NUMBER: { - e = new (ctx.mem) Number(path, position, v.number.value, v.number.unit); + e = new (ctx.mem) Number(path, position, sass_number_get_value(v), sass_number_get_unit(v)); } break; case SASS_COLOR: { - e = new (ctx.mem) Color(path, position, v.color.r, v.color.g, v.color.b, v.color.a); + e = new (ctx.mem) Color(path, position, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); } break; case SASS_STRING: { - e = new (ctx.mem) String_Constant(path, position, v.string.value); + e = new (ctx.mem) String_Constant(path, position, sass_string_get_value(v)); } break; case SASS_LIST: { - List* l = new (ctx.mem) List(path, position, v.list.length, v.list.separator == SASS_COMMA ? List::COMMA : List::SPACE); - for (size_t i = 0, L = v.list.length; i < L; ++i) { - *l << cval_to_astnode(v.list.values[i], ctx, backtrace, path, position); + List* l = new (ctx.mem) List(path, position, sass_list_get_length(v), sass_list_get_separator(v) == SASS_COMMA ? List::COMMA : List::SPACE); + for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) { + *l << cval_to_astnode(sass_list_get_value(v, i), ctx, backtrace, path, position); } e = l; } break; case SASS_MAP: { Map* m = new (ctx.mem) Map(path, position); - for (size_t i = 0, L = v.map.length; i < L; ++i) { + for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) { *m << std::make_pair( - cval_to_astnode(v.map.pairs[i].key, ctx, backtrace, path, position), - cval_to_astnode(v.map.pairs[i].value, ctx, backtrace, path, position)); + cval_to_astnode(sass_map_get_key(v, i), ctx, backtrace, path, position), + cval_to_astnode(sass_map_get_value(v, i), ctx, backtrace, path, position)); } e = m; } break; @@ -978,7 +1014,7 @@ namespace Sass { e = new (ctx.mem) Null(path, position); } break; case SASS_ERROR: { - error("error in C function: " + string(v.error.message), path, position, backtrace); + error("error in C function: " + string(sass_error_get_message(v)), path, position, backtrace); } break; } return e; diff --git a/eval.hpp b/eval.hpp index a0a1a7c214..5eff892dbc 100644 --- a/eval.hpp +++ b/eval.hpp @@ -10,14 +10,12 @@ #include "environment.hpp" #endif -#ifndef SASS -#include "sass.h" -#endif - #ifndef SASS_POSITION #include "position.hpp" #endif +#include "sass_values.h" + namespace Sass { using namespace std; @@ -73,7 +71,7 @@ namespace Sass { Expression* fallback(U x) { return fallback_impl(x); } }; - Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path = "", Position position = Position()); + Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, string path = "", Position position = Position()); bool eq(Expression*, Expression*, Context&); bool lt(Expression*, Expression*, Context&); diff --git a/functions.cpp b/functions.cpp index 3a5bd38194..c7a6aae3e8 100644 --- a/functions.cpp +++ b/functions.cpp @@ -45,7 +45,8 @@ namespace Sass { Definition* make_c_function(Signature sig, Sass_C_Function f, void* cookie, Context& ctx) { Parser sig_parser = Parser::from_c_str(sig, ctx, "[c function]"); - sig_parser.lex(); + // allow to overload generic callback and @warn with custom functions + sig_parser.lex< alternatives < identifier, exactly <'*'>, exactly > >(); string name(Util::normalize_underscores(sig_parser.lexed)); Parameters* params = sig_parser.parse_parameters(); return new (ctx.mem) Definition("[c function]", diff --git a/functions.hpp b/functions.hpp index fc81924a5a..151a48329d 100644 --- a/functions.hpp +++ b/functions.hpp @@ -1,19 +1,17 @@ #define SASS_FUNCTIONS +#include + #ifndef SASS_ENVIRONMENT #include "environment.hpp" #endif -#ifndef SASS -#include "sass.h" -#endif - -#include - #ifndef SASS_POSITION #include "position.hpp" #endif +#include "sass_functions.h" + #define BUILT_IN(name) Expression*\ name(Env& env, Env& d_env, Context& ctx, Signature sig, const string& path, Position position, Backtrace* backtrace) diff --git a/inspect.hpp b/inspect.hpp index 999f4f06de..8e629d503f 100644 --- a/inspect.hpp +++ b/inspect.hpp @@ -95,7 +95,7 @@ namespace Sass { virtual void operator()(Selector_List*); template - void fallback(U x) { fallback_impl(x); } + void fallback(U x) { fallback_impl(reinterpret_cast(x)); } }; string unquote(const string&); diff --git a/parser.cpp b/parser.cpp index cf2f384ba5..d928fe3096 100644 --- a/parser.cpp +++ b/parser.cpp @@ -119,6 +119,32 @@ namespace Sass { return root; } + void Parser::add_single_file (Import* imp, string import_path) { + + string extension; + 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 = new (ctx.mem) String_Constant(path, source_position, import_path, true); + Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc); + Arguments* loc_args = new (ctx.mem) Arguments(path, source_position); + (*loc_args) << loc_arg; + Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args); + imp->urls().push_back(new_url); + } + else { + string current_dir = File::dir_name(path); + string resolved(ctx.add_file(current_dir, unquoted)); + if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir); + imp->files().push_back(resolved); + } + + } + Import* Parser::parse_import() { lex< import >(); @@ -127,25 +153,47 @@ namespace Sass { do { if (lex< string_constant >()) { string import_path(lexed); - string extension; - if (import_path.length() > 6) { // 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 = import_path.substr(import_path.length() - 5, 4); - } - if (extension == ".css") { - String_Constant* loc = new (ctx.mem) String_Constant(path, source_position, import_path, true); - Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc); - Arguments* loc_args = new (ctx.mem) Arguments(path, source_position); - (*loc_args) << loc_arg; - Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args); - imp->urls().push_back(new_url); - } - else { - string current_dir = File::dir_name(path); - string resolved(ctx.add_file(current_dir, unquote(import_path))); - if (resolved.empty()) error("file to import not found or unreadable: " + import_path + "\nCurrent dir: " + current_dir); - imp->files().push_back(resolved); + + // struct Sass_Options opt = sass_context_get_options(ctx) + Sass_C_Import_Callback importer = ctx.importer; + // custom importer + if (importer) { + Sass_C_Import_Fn fn = sass_import_get_function(importer); + void* cookie = sass_import_get_cookie(importer); + // get null delimited "array" of "external" imports + struct Sass_Import** imports = fn(import_path.c_str(), cookie); + struct Sass_Import** includes = imports; + if (includes) { + while (*includes) { + struct Sass_Import* include = *includes; + const char *file = sass_import_get_path(include); + char *source = sass_import_take_source(include); + // char *srcmap = sass_import_take_srcmap(include); + if (source) { + string inc_path = unquote(import_path); + if (file) { + ctx.add_source(file, import_path, source); + imp->files().push_back(file); + } else { + ctx.add_source(import_path, import_path, source); + imp->files().push_back(import_path); + } + } else if(file) { + add_single_file(imp, file); + } + ++includes; + } + // deallocate returned memory + sass_delete_import_list(imports); + // go for next parse loop + continue; + } + // custom importer returned nothing + // means we should use default loader } + + add_single_file(imp, import_path); + } else if (peek< uri_prefix >()) { imp->urls().push_back(parse_value()); diff --git a/parser.hpp b/parser.hpp index c0f0205b07..ea3d682f9f 100644 --- a/parser.hpp +++ b/parser.hpp @@ -37,6 +37,8 @@ namespace Sass { using namespace Prelexer; class Parser { + private: + void add_single_file (Import* imp, string import_path); public: class AST_Node; diff --git a/sass.cpp b/sass.cpp index 84d90190fd..a54914495e 100644 --- a/sass.cpp +++ b/sass.cpp @@ -3,206 +3,26 @@ #include #include -#ifndef SASS #include "sass.h" -#endif - -#include "context.hpp" - -#ifndef SASS_ERROR_HANDLING -#include "error_handling.hpp" -#endif +#include "inspect.hpp" extern "C" { using namespace std; - struct Sass_Context* make_sass_context() - { return (Sass_Context*) calloc(1, sizeof(Sass_Context)); } - - void free_sass_context(struct Sass_Context* ctx) - { - if (ctx->output_string) free(ctx->output_string); - if (ctx->error_message) free(ctx->error_message); - free(ctx); - } - - namespace Sass { - enum Sass_Source { FILE_SOURCE, STRING_SOURCE }; - - static void compile_sass(struct Sass_Context* c_ctx, - Sass::Sass_Source src_option) - { - using namespace Sass; - try { - Context cpp_ctx( - Context::Data().source_c_str (c_ctx->input_string) - - .entry_point (c_ctx->input_path ? - c_ctx->input_path : - "") - - .output_style ((Output_Style) - c_ctx->output_style) - - .source_comments (c_ctx->source_comments) - .source_map_file (c_ctx->source_map_file) - .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) - - .image_path (c_ctx->image_path ? - c_ctx->image_path : - "") - - .output_path (c_ctx->output_path ? - c_ctx->output_path : - "") - - .include_paths_c_str (c_ctx->include_paths_string) - .include_paths_array (/*c_ctx->include_paths_array*/0) - .include_paths (vector()) - .precision (c_ctx->precision ? c_ctx->precision : 5) - ); - if (src_option == FILE_SOURCE) cpp_ctx.compile_file(); - else cpp_ctx.compile_string(); - c_ctx->error_message = 0; - c_ctx->error_status = 0; - } - catch (Error& e) { - stringstream msg_stream; - msg_stream << e.path << ":" << e.position.line << ": error: " << e.message << endl; - string msg(msg_stream.str()); - char* msg_str = (char*) malloc(msg.size() + 1); - strcpy(msg_str, msg.c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->error_message = msg_str; - } - catch (bad_alloc& ba) { - stringstream msg_stream; - msg_stream << "Unable to allocate memory: " << ba.what() << endl; - string msg(msg_stream.str()); - char* msg_str = (char*) malloc(msg.size() + 1); - strcpy(msg_str, msg.c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->error_message = msg_str; - } - } - } - - void compile_sass_file(struct Sass_Context* c_ctx) - { Sass::compile_sass(c_ctx, Sass::FILE_SOURCE); } - - void compile_sass_string(struct Sass_Context* c_ctx) - { Sass::compile_sass(c_ctx, Sass::STRING_SOURCE); } - - - union Sass_Value make_sass_boolean(int val) - { - union Sass_Value v; - v.boolean.tag = SASS_BOOLEAN; - v.boolean.value = val; - return v; + // caller must free the returned memory + char* sass_string_quote (const char *str, const char quotemark) { + string quoted = Sass::quote(str, quotemark); + char *cstr = (char*) malloc(quoted.length() + 1); + std::strcpy(cstr, quoted.c_str()); + return cstr; } - union Sass_Value make_sass_number(double val, const char* unit) - { - union Sass_Value v; - v.number.tag = SASS_NUMBER; - v.number.value = val; - v.number.unit = strdup(unit); - return v; - } - - union Sass_Value make_sass_color(double r, double g, double b, double a) - { - union Sass_Value v; - v.color.tag = SASS_COLOR; - v.color.r = r; - v.color.g = g; - v.color.b = b; - v.color.a = a; - return v; - } - - union Sass_Value make_sass_string(const char* val) - { - union Sass_Value v; - v.string.tag = SASS_STRING; - v.string.value = strdup(val); - return v; - } - - union Sass_Value make_sass_list(size_t len, enum Sass_Separator sep) - { - union Sass_Value v; - v.list.tag = SASS_LIST; - v.list.length = len; - v.list.separator = sep; - v.list.values = (union Sass_Value*) malloc(sizeof(union Sass_Value)*len); - return v; - } - - union Sass_Value make_sass_map(size_t len) - { - union Sass_Value v; - v.map.tag = SASS_MAP; - v.map.length = len; - v.map.pairs = (struct Sass_KeyValuePair*) malloc(sizeof(struct Sass_KeyValuePair)*len); - return v; - } - - union Sass_Value make_sass_null() - { - union Sass_Value v; - v.null.tag = SASS_NULL; - return v; - } - - union Sass_Value make_sass_error(const char* msg) - { - union Sass_Value v; - v.error.tag = SASS_ERROR; - v.error.message = strdup(msg); - return v; - } - - // make_sass_* may allocated additional memory - void free_sass_value(const union Sass_Value val) { - - size_t i; - switch(val.unknown.tag) { - case SASS_NULL: { - } break; - case SASS_BOOLEAN: { - } break; - case SASS_NUMBER: { - free(val.number.unit); - } break; - case SASS_COLOR: { - } break; - case SASS_STRING: { - free(val.string.value); - } break; - case SASS_LIST: { - for (i=0; i #include +#include "sass_values.h" +#include "sass_functions.h" #ifdef __cplusplus extern "C" { #endif -#define SASS_OUTPUT_NESTED 0 -#define SASS_OUTPUT_EXPANDED 1 -#define SASS_OUTPUT_COMPACT 2 -#define SASS_OUTPUT_COMPRESSED 3 -#define SASS_OUTPUT_FORMATTED 4 -struct Sass_Context { - const char* input_path; - const char* input_string; - char* output_string; - - int error_status; - char* error_message; - - int output_style; - bool source_comments; - bool source_map_embed; - bool source_map_contents; - bool omit_source_map_url; - const char* source_map_file; - const char* image_path; - const char* output_path; - const char* include_paths_string; - const char** include_paths_array; - int precision; -}; - -struct Sass_Context* make_sass_context (); -void free_sass_context (struct Sass_Context*); -void compile_sass_file (struct Sass_Context*); -void compile_sass_string (struct Sass_Context*); - -// type tags for Sass values -enum Sass_Tag { - SASS_BOOLEAN, - SASS_NUMBER, - SASS_COLOR, - SASS_STRING, - SASS_LIST, - SASS_MAP, - SASS_NULL, - SASS_ERROR -}; - -// tags for denoting Sass list separators -enum Sass_Separator { - SASS_COMMA, - SASS_SPACE -}; - -// Component structs for the Sass value union type. -// Not meant to be used directly. -struct Sass_Unknown { - enum Sass_Tag tag; -}; - -struct Sass_Boolean { - enum Sass_Tag tag; - int 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; - char* value; +// Different render styles +enum Sass_Output_Style { + SASS_STYLE_NESTED, + SASS_STYLE_EXPANDED, + SASS_STYLE_COMPACT, + SASS_STYLE_COMPRESSED }; -union Sass_Value; +// Some convenient string helper function +char* sass_string_quote (const char *str, const char quotemark); +char* sass_string_unquote (const char *str); -struct Sass_List { - enum Sass_Tag tag; - enum Sass_Separator separator; - size_t length; - union Sass_Value* values; -}; - -struct Sass_KeyValuePair; -struct Sass_Map { - enum Sass_Tag tag; - size_t length; - struct Sass_KeyValuePair* pairs; -}; - -struct Sass_Null { - enum Sass_Tag tag; -}; - -struct Sass_Error { - enum Sass_Tag tag; - char* message; -}; - -// represention of Sass values in C -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_KeyValuePair { - union Sass_Value key; - union Sass_Value value; -}; - -union Sass_Value make_sass_boolean (int val); -union Sass_Value make_sass_number (double val, const char* unit); -union Sass_Value make_sass_color (double r, double g, double b, double a); -union Sass_Value make_sass_string (const char* val); -union Sass_Value make_sass_list (size_t len, enum Sass_Separator sep); -union Sass_Value make_sass_map (size_t len); -union Sass_Value make_sass_null (); -union Sass_Value make_sass_error (const char* msg); - -void free_sass_value (const union Sass_Value val); - -typedef union Sass_Value(*Sass_C_Function)(union Sass_Value, void *cookie); - -struct Sass_C_Function_Descriptor { - const char* signature; - Sass_C_Function function; - void *cookie; -}; #ifdef __cplusplus } #endif + +#endif \ No newline at end of file diff --git a/sass_context.cpp b/sass_context.cpp new file mode 100644 index 0000000000..772e16d5bb --- /dev/null +++ b/sass_context.cpp @@ -0,0 +1,644 @@ +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "json.hpp" +#include "context.hpp" +#include "sass_values.h" +#include "sass_context.h" + +extern "C" { + using namespace std; + 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; + + // For the image-url Sass function + char* image_path; + + // Colon-separated list of paths + // Semicolon-separated on Windows + // Maybe use array interface instead? + char* include_path; + + // Include path (linked string list) + struct string_list* include_paths; + + // Path to source map file + // Enables source map generation + // Used to create sourceMappingUrl + char* source_map_file; + + // Custom functions that can be called from sccs code + Sass_C_Function_List c_functions; + + // Callback to overload imports + Sass_C_Import_Callback importer; + + }; + + // 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_message; + // error position + char* error_file; + size_t error_line; + size_t error_column; + + // 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; + + }; + + // Compiler states + enum Sass_Compiler_State { + SASS_COMPILER_CREATED, + SASS_COMPILER_PARSED, + SASS_COMPILER_EXECUTED + }; + + // link c and cpp context + struct Sass_Compiler { + // progress status + Sass_Compiler_State state; + // original c context + Sass_Context* c_ctx; + // Sass::Context + void* cpp_ctx; + // Sass::Block + void* root; + }; + + + #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \ + type sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ + void sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; } + #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option) \ + type sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ + void sass_option_set_##option (struct Sass_Options* options, type option) { free(options->option); options->option = strdup(option); } + + #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ + type sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; } + #define IMPLEMENT_SASS_FILE_CONTEXT_SETTER(type, option) \ + void sass_file_context_set_##option (struct Sass_File_Context* ctx, type option) { ctx->option = option; } + #define IMPLEMENT_SASS_DATA_CONTEXT_SETTER(type, option) \ + void sass_data_context_set_##option (struct Sass_Data_Context* ctx, type option) { ctx->option = option; } + + // helper for safe access to c_ctx + static const char* safe_str (const char* str) { + return str == NULL ? "" : str; + } + + static void copy_strings(const std::vector& strings, char*** array, int skip = 0) { + int num = static_cast(strings.size()); + char** arr = (char**) malloc(sizeof(char*)* num + 1); + + for(int i = skip; i < num; i++) { + arr[i-skip] = (char*) malloc(sizeof(char) * strings[i].size() + 1); + std::copy(strings[i].begin(), strings[i].end(), arr[i-skip]); + arr[i-skip][strings[i].size()] = '\0'; + } + + arr[num-skip] = 0; + *array = arr; + } + + static void free_string_array(char ** arr) { + if(!arr) + return; + + char **it = arr; + while (it && (*it)) { + free(*it); + ++it; + } + + free(arr); + } + + static int handle_errors(Sass_Context* c_ctx) { + try { + throw; + } + catch (Error& e) { + stringstream msg_stream; + JsonNode* json_err = json_mkobject(); + json_append_member(json_err, "status", json_mknumber(1)); + json_append_member(json_err, "file", json_mkstring(e.path.c_str())); + json_append_member(json_err, "line", json_mknumber(e.position.line)); + json_append_member(json_err, "column", json_mknumber(e.position.column)); + json_append_member(json_err, "message", json_mkstring(e.message.c_str())); + msg_stream << e.path << ":" << e.position.line << ": " << e.message << endl; + c_ctx->error_json = json_stringify(json_err, " ");; + c_ctx->error_message = strdup(msg_stream.str().c_str()); + c_ctx->error_status = 1; + c_ctx->error_file = strdup(e.path.c_str()); + c_ctx->error_line = e.position.line; + c_ctx->error_column = e.position.column; + c_ctx->output_string = 0; + c_ctx->source_map_string = 0; + } + catch(bad_alloc& ba) { + stringstream msg_stream; + JsonNode* json_err = json_mkobject(); + msg_stream << "Unable to allocate memory: " << ba.what() << endl; + json_append_member(json_err, "status", json_mknumber(2)); + json_append_member(json_err, "message", json_mkstring(ba.what())); + c_ctx->error_json = json_stringify(json_err, " ");; + c_ctx->error_message = strdup(msg_stream.str().c_str()); + c_ctx->error_status = 2; + c_ctx->output_string = 0; + c_ctx->source_map_string = 0; + } + catch (std::exception& e) { + stringstream msg_stream; + JsonNode* json_err = json_mkobject(); + msg_stream << "Error: " << e.what() << endl; + json_append_member(json_err, "status", json_mknumber(3)); + json_append_member(json_err, "message", json_mkstring(e.what())); + c_ctx->error_json = json_stringify(json_err, " ");; + c_ctx->error_message = strdup(msg_stream.str().c_str()); + c_ctx->error_status = 3; + c_ctx->output_string = 0; + c_ctx->source_map_string = 0; + } + catch (string& e) { + stringstream msg_stream; + JsonNode* json_err = json_mkobject(); + msg_stream << "Error: " << e << endl; + json_append_member(json_err, "status", json_mknumber(4)); + json_append_member(json_err, "message", json_mkstring(e.c_str())); + c_ctx->error_json = json_stringify(json_err, " ");; + c_ctx->error_message = strdup(msg_stream.str().c_str()); + c_ctx->error_status = 4; + c_ctx->output_string = 0; + c_ctx->source_map_string = 0; + } + catch (...) { + stringstream msg_stream; + JsonNode* json_err = json_mkobject(); + msg_stream << "Unknown error occurred" << endl; + json_append_member(json_err, "status", json_mknumber(5)); + json_append_member(json_err, "message", json_mkstring("unknown")); + c_ctx->error_json = json_stringify(json_err, " ");; + c_ctx->error_message = strdup(msg_stream.str().c_str()); + c_ctx->error_status = 5; + c_ctx->output_string = 0; + c_ctx->source_map_string = 0; + } + return c_ctx->error_status; + } + + // generic compilation function (not exported, use file/data compile instead) + static Context* sass_prepare_context (Sass_Context* c_ctx, Context::Data cpp_opt) + { + try { + + // get input/output path from options + string input_path = safe_str(c_ctx->input_path); + 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* cur = c_ctx->include_paths; + // very poor loop to get the length of the linked list + size_t length = 0; while (cur) { length ++; cur = cur->next; } + // create char* array to hold all paths plus null terminator + const char** include_paths = (const char**) calloc(length + 1, sizeof(char*)); + // reset iterator + cur = c_ctx->include_paths; + // copy over the paths + for (size_t i = 0; cur; i++) { + include_paths[i] = cur->string; + cur = cur->next; + } + // transfer the options to c++ + cpp_opt.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_embed(c_ctx->source_map_embed) + .source_map_contents(c_ctx->source_map_contents) + .omit_source_map_url(c_ctx->omit_source_map_url) + .image_path(safe_str(c_ctx->image_path)) + .include_paths_c_str(c_ctx->include_path) + .importer(c_ctx->importer) + .include_paths_array(include_paths) + .include_paths(vector()) + .precision(c_ctx->precision ? c_ctx->precision : 5); + + // create new c++ Context + Context* cpp_ctx = new Context(cpp_opt); + // free intermediate data + free(include_paths); + + // register our custom functions + if (c_ctx->c_functions) { + Sass_C_Function_List this_func_data = c_ctx->c_functions; + while ((this_func_data) && (*this_func_data)) { + cpp_ctx->c_functions.push_back((*this_func_data)); + ++this_func_data; + } + } + + // reset error status + c_ctx->error_json = 0; + c_ctx->error_message = 0; + c_ctx->error_status = 0; + // reset error position + c_ctx->error_file = 0; + c_ctx->error_line = -1; + c_ctx->error_column = -1; + + // use to parse block + return cpp_ctx; + + } + // pass errors to generic error handler + catch (...) { handle_errors(c_ctx); } + + // error + return 0; + + } + + static Block* sass_parse_block (Sass_Context* c_ctx, Context* cpp_ctx) + { + try { + + // get input/output path from options + string input_path = safe_str(c_ctx->input_path); + string output_path = safe_str(c_ctx->output_path); + + // parsed root block + Block* root = 0; + + // maybe skip some entries of included files + // we do not include stdin for data contexts + size_t skip = 0; + + // 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 = 1; // skip first entry of includes + } + + // copy the included files on to the context (dont forget to free) + copy_strings(cpp_ctx->get_included_files(skip), &c_ctx->included_files, skip); + + // return parsed block + return root; + + } + // pass errors to generic error handler + catch (...) { handle_errors(c_ctx); } + + // error + return 0; + + } + + // generic compilation function (not exported, use file/data compile instead) + static int sass_compile_context (Sass_Context* c_ctx, Context::Data cpp_opt) + { + + Context* cpp_ctx = 0; + + try { + + // first prepare the c++ context + cpp_ctx = sass_prepare_context(c_ctx, cpp_opt); + + // parse given context and return root block + Block* root = sass_parse_block(c_ctx, cpp_ctx); + + // now compile the parsed root block + c_ctx->output_string = cpp_ctx->compile_block(root); + + // generate source map json and store on context + c_ctx->source_map_string = cpp_ctx->generate_source_map(); + + } + // pass errors to generic error handler + catch (...) { handle_errors(c_ctx); } + + delete cpp_ctx; + + return c_ctx->error_status; + } + + Sass_Options* sass_make_options (void) + { + return (struct Sass_Options*) calloc(1, sizeof(struct Sass_Options)); + } + + Sass_File_Context* sass_make_file_context(const char* input_path) + { + struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(1, sizeof(struct Sass_File_Context)); + ctx->type = SASS_CONTEXT_FILE; + sass_option_set_input_path(ctx, input_path); + return ctx; + } + + Sass_Data_Context* sass_make_data_context(char* source_string) + { + struct Sass_Data_Context* ctx = (struct Sass_Data_Context*) calloc(1, sizeof(struct Sass_Data_Context)); + ctx->type = SASS_CONTEXT_DATA; + ctx->source_string = source_string; + return ctx; + } + + struct Sass_Compiler* sass_make_file_compiler (struct Sass_File_Context* c_ctx) + { + struct Sass_Compiler* compiler = (struct Sass_Compiler*) calloc(1, sizeof(struct Sass_Compiler)); + compiler->state = SASS_COMPILER_CREATED; + compiler->c_ctx = c_ctx; + Context::Data cpp_opt = Context::Data(); + cpp_opt.entry_point(c_ctx->input_path); + compiler->cpp_ctx = sass_prepare_context(c_ctx, cpp_opt); + return compiler; + } + + struct Sass_Compiler* sass_make_data_compiler (struct Sass_Data_Context* c_ctx) + { + struct Sass_Compiler* compiler = (struct Sass_Compiler*) calloc(1, sizeof(struct Sass_Compiler)); + compiler->state = SASS_COMPILER_CREATED; + compiler->c_ctx = c_ctx; + Context::Data cpp_opt = Context::Data(); + cpp_opt.source_c_str(c_ctx->source_string); + compiler->cpp_ctx = sass_prepare_context(c_ctx, cpp_opt); + return compiler; + } + + int sass_compile_data_context(Sass_Data_Context* data_ctx) + { + Sass_Context* c_ctx = data_ctx; + Context::Data cpp_opt = Context::Data(); + cpp_opt.source_c_str(data_ctx->source_string); + return sass_compile_context(c_ctx, cpp_opt); + } + + int sass_compile_file_context(Sass_File_Context* file_ctx) + { + Sass_Context* c_ctx = file_ctx; + Context::Data cpp_opt = Context::Data(); + cpp_opt.entry_point(file_ctx->input_path); + return sass_compile_context(c_ctx, cpp_opt); + } + + int sass_compiler_parse(struct Sass_Compiler* compiler) + { + if (compiler->state == SASS_COMPILER_PARSED) return 0; + if (compiler->state != SASS_COMPILER_CREATED) return -1; + if (compiler->c_ctx == NULL) return 1; + if (compiler->cpp_ctx == NULL) return 1; + compiler->state = SASS_COMPILER_PARSED; + Context* cpp_ctx = (Context*) compiler->cpp_ctx; + // parse the context we have set up (file or data) + compiler->root = sass_parse_block(compiler->c_ctx, cpp_ctx); + // success + return 0; + } + + int sass_compiler_execute(struct Sass_Compiler* compiler) + { + if (compiler->state == SASS_COMPILER_EXECUTED) return 0; + if (compiler->state != SASS_COMPILER_PARSED) return -1; + if (compiler->c_ctx == NULL) return 1; + if (compiler->cpp_ctx == NULL) return 1; + if (compiler->root == NULL) return 1; + compiler->state = SASS_COMPILER_EXECUTED; + Context* cpp_ctx = (Context*) compiler->cpp_ctx; + Block* root = (Block*) compiler->root; + // compile the parsed root block + compiler->c_ctx->output_string = cpp_ctx->compile_block(root); + // generate source map json and store on context + compiler->c_ctx->source_map_string = cpp_ctx->generate_source_map(); + // success + return 0; + } + + // helper function, not exported, only accessible locally + static void sass_clear_options (struct Sass_Options* options) + { + // Deallocate custom functions + if (options->c_functions) { + struct Sass_C_Function_Descriptor** this_func_data = options->c_functions; + while ((this_func_data) && (*this_func_data)) { + free((*this_func_data)); + ++this_func_data; + } + } + // Deallocate inc paths + if (options->include_paths) { + struct string_list* cur; + struct string_list* next; + cur = options->include_paths; + while (cur) { + next = cur->next; + free(cur->string); + free(cur); + cur = next; + } + } + // Free the list container + free(options->c_functions); + // Make it null terminated + options->c_functions = 0; + options->include_paths = 0; + } + + // helper function, not exported, only accessible locally + // sass_free_context is also defined in old sass_interface + static void sass_clear_context (struct Sass_Context* ctx) + { + // release the allocated memory (mostly via strdup) + if (ctx->output_string) free(ctx->output_string); + if (ctx->source_map_string) free(ctx->source_map_string); + if (ctx->error_message) free(ctx->error_message); + if (ctx->error_json) free(ctx->error_json); + if (ctx->error_file) free(ctx->error_file); + if (ctx->input_path) free(ctx->input_path); + if (ctx->output_path) free(ctx->output_path); + if (ctx->image_path) free(ctx->image_path); + if (ctx->include_path) free(ctx->include_path); + if (ctx->source_map_file) free(ctx->source_map_file); + free_string_array(ctx->included_files); + // play safe and reset properties + ctx->output_string = 0; + ctx->source_map_string = 0; + ctx->error_message = 0; + ctx->error_json = 0; + ctx->error_file = 0; + ctx->input_path = 0; + ctx->output_path = 0; + ctx->image_path = 0; + ctx->include_path = 0; + ctx->source_map_file = 0; + ctx->included_files = 0; + // now clear the options + sass_clear_options(ctx); + } + + void sass_delete_compiler (struct Sass_Compiler* compiler) + { + Context* cpp_ctx = (Context*) compiler->cpp_ctx; + compiler->cpp_ctx = 0; + delete cpp_ctx; + free(compiler); + } + + // Deallocate all associated memory with contexts + void sass_delete_file_context (struct Sass_File_Context* ctx) { sass_clear_context(ctx); free(ctx); } + void sass_delete_data_context (struct Sass_Data_Context* ctx) { sass_clear_context(ctx); free(ctx); } + + // Getters for sass context from specific implementations + struct Sass_Context* sass_file_context_get_context(struct Sass_File_Context* ctx) { return ctx; } + struct Sass_Context* sass_data_context_get_context(struct Sass_Data_Context* ctx) { return ctx; } + + // Getters for context options from Sass_Context + struct Sass_Options* sass_context_get_options(struct Sass_Context* ctx) { return ctx; } + struct Sass_Options* sass_file_context_get_options(struct Sass_File_Context* ctx) { return ctx; } + struct Sass_Options* sass_data_context_get_options(struct Sass_Data_Context* ctx) { return ctx; } + void sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { (Sass_Options) *ctx = *opt; } + void sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { (Sass_Options) *ctx = *opt; } + + // Create getter and setters for options + IMPLEMENT_SASS_OPTION_ACCESSOR(int, precision); + IMPLEMENT_SASS_OPTION_ACCESSOR(enum Sass_Output_Style, output_style); + IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_comments); + IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed); + IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents); + IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url); + IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src); + IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_C_Function_List, c_functions); + IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_C_Import_Callback, importer); + 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*, image_path); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file); + + // Create getter and setters for context + IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status); + IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_json); + IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_message); + IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_file); + IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_line); + IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_column); + IMPLEMENT_SASS_CONTEXT_GETTER(const char*, output_string); + IMPLEMENT_SASS_CONTEXT_GETTER(const char*, source_map_string); + IMPLEMENT_SASS_CONTEXT_GETTER(char**, included_files); + + // Create getter and setters for specialized contexts + IMPLEMENT_SASS_DATA_CONTEXT_SETTER(char*, source_string); + + // Push function for include paths (no manipulation support for now) + void sass_option_push_include_path(struct Sass_Options* options, const char* path) + { + + struct string_list* include_paths = (struct string_list*) calloc(1, sizeof(struct string_list)); + include_paths->string = strdup(path); + struct string_list* last = options->include_paths; + if (!options->include_paths) { + options->include_paths = include_paths; + } else { + while (last->next) + last = last->next; + last->next = include_paths; + } + + } +} diff --git a/sass_context.h b/sass_context.h new file mode 100644 index 0000000000..1534c35d7d --- /dev/null +++ b/sass_context.h @@ -0,0 +1,118 @@ +#ifndef SASS_C_CONTEXT +#define SASS_C_CONTEXT + +#include +#include +#include "sass.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// Forward declaration +struct Sass_Compiler; + +// Forward declaration +struct Sass_Options; +struct Sass_Context; // : Sass_Options +struct Sass_File_Context; // : Sass_Context +struct Sass_Data_Context; // : Sass_Context + +// Create and initialize an option struct +struct Sass_Options* sass_make_options (void); +// Create and initialize a specific context +struct Sass_File_Context* sass_make_file_context (const char* input_path); +struct Sass_Data_Context* sass_make_data_context (char* source_string); + +// Call the compilation step for the specific context +int sass_compile_file_context (struct Sass_File_Context* ctx); +int sass_compile_data_context (struct Sass_Data_Context* ctx); + +// Create a sass compiler instance for more control +struct Sass_Compiler* sass_make_file_compiler (struct Sass_File_Context* file_ctx); +struct Sass_Compiler* sass_make_data_compiler (struct Sass_Data_Context* data_ctx); + +// Execute the different compilation steps individually +// Usefull if you only want to query the included files +int sass_compiler_parse(struct Sass_Compiler* compiler); +int sass_compiler_execute(struct Sass_Compiler* compiler); + +// Release all memory allocated with the compiler +// This does _not_ include any contexts or options +void sass_delete_compiler(struct Sass_Compiler* compiler); + +// Release all memory allocated and also ourself +void sass_delete_file_context (struct Sass_File_Context* ctx); +void sass_delete_data_context (struct Sass_Data_Context* ctx); + +// Getters for context from specific implementation +struct Sass_Context* sass_file_context_get_context (struct Sass_File_Context* file_ctx); +struct Sass_Context* sass_data_context_get_context (struct Sass_Data_Context* data_ctx); + +// Getters for context options from Sass_Context +struct Sass_Options* sass_context_get_options (struct Sass_Context* ctx); +struct Sass_Options* sass_file_context_get_options (struct Sass_File_Context* file_ctx); +struct Sass_Options* sass_data_context_get_options (struct Sass_Data_Context* data_ctx); +void sass_file_context_set_options (struct Sass_File_Context* file_ctx, struct Sass_Options* opt); +void sass_data_context_set_options (struct Sass_Data_Context* data_ctx, struct Sass_Options* opt); + + +// Getters for options +int sass_option_get_precision (struct Sass_Options* options); +enum Sass_Output_Style sass_option_get_output_style (struct Sass_Options* options); +bool sass_option_get_source_comments (struct Sass_Options* options); +bool sass_option_get_source_map_embed (struct Sass_Options* options); +bool sass_option_get_source_map_contents (struct Sass_Options* options); +bool sass_option_get_omit_source_map_url (struct Sass_Options* options); +bool sass_option_get_is_indented_syntax_src (struct Sass_Options* options); +const char* sass_option_get_input_path (struct Sass_Options* options); +const char* sass_option_get_output_path (struct Sass_Options* options); +const char* sass_option_get_image_path (struct Sass_Options* options); +const char* sass_option_get_include_path (struct Sass_Options* options); +const char* sass_option_get_source_map_file (struct Sass_Options* options); +Sass_C_Function_List sass_option_get_c_functions (struct Sass_Options* options); +Sass_C_Import_Callback sass_option_get_importer (struct Sass_Options* options); + +// Setters for options +void sass_option_set_precision (struct Sass_Options* options, int precision); +void sass_option_set_output_style (struct Sass_Options* options, enum Sass_Output_Style output_style); +void sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); +void sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); +void sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents); +void sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); +void sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); +void sass_option_set_input_path (struct Sass_Options* options, const char* input_path); +void sass_option_set_output_path (struct Sass_Options* options, const char* output_path); +void sass_option_set_image_path (struct Sass_Options* options, const char* image_path); +void sass_option_set_include_path (struct Sass_Options* options, const char* include_path); +void sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file); +void sass_option_set_c_functions (struct Sass_Options* options, Sass_C_Function_List c_functions); +void sass_option_set_importer (struct Sass_Options* options, Sass_C_Import_Callback importer); + + +// Getter for context +const char* sass_context_get_output_string (struct Sass_Context* ctx); +int sass_context_get_error_status (struct Sass_Context* ctx); +const char* sass_context_get_error_json (struct Sass_Context* ctx); +const char* sass_context_get_error_message (struct Sass_Context* ctx); +const char* sass_context_get_error_file (struct Sass_Context* ctx); +size_t sass_context_get_error_line (struct Sass_Context* ctx); +size_t sass_context_get_error_column (struct Sass_Context* ctx); +const char* sass_context_get_source_map_string (struct Sass_Context* ctx); +char** sass_context_get_included_files (struct Sass_Context* ctx); + + +// Setters for specific data context option +// const char* sass_data_context_get_source_string (struct Sass_Data_Context* ctx); +void sass_data_context_set_source_string (struct Sass_Data_Context* ctx, char* source_string); + +// Push function for include paths (no manipulation support for now) +void sass_option_push_include_path (struct Sass_Options* options, const char* path); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/sass_functions.cpp b/sass_functions.cpp new file mode 100644 index 0000000000..f01be315d8 --- /dev/null +++ b/sass_functions.cpp @@ -0,0 +1,107 @@ +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "context.hpp" +#include "sass_functions.h" + +extern "C" { + using namespace std; + + // Struct to hold custom function callback + struct Sass_C_Function_Descriptor { + const char* signature; + Sass_C_Function function; + void* cookie; + }; + + Sass_C_Function_List sass_make_function_list(size_t length) + { + return (Sass_C_Function_List) calloc(length + 1, sizeof(Sass_C_Function_Callback)); + } + + Sass_C_Function_Callback sass_make_function(const char* signature, Sass_C_Function function, void* cookie) + { + Sass_C_Function_Callback cb = (Sass_C_Function_Callback) calloc(1, sizeof(Sass_C_Function_Descriptor)); + cb->signature = signature; + cb->function = function; + cb->cookie = cookie; + return cb; + } + + const char* sass_function_get_signature(Sass_C_Function_Callback fn) { return fn->signature; } + Sass_C_Function sass_function_get_function(Sass_C_Function_Callback fn) { return fn->function; } + void* sass_function_get_cookie(Sass_C_Function_Callback fn) { return fn->cookie; } + + // External import entry + struct Sass_Import { + char* path; + char* source; + char* srcmap; + }; + + // Struct to hold importer callback + struct Sass_C_Import_Descriptor { + Sass_C_Import_Fn function; + void* cookie; + }; + + Sass_C_Import_Callback sass_make_importer(Sass_C_Import_Fn function, void* cookie) + { + Sass_C_Import_Callback cb = (Sass_C_Import_Callback) calloc(1, sizeof(Sass_C_Import_Descriptor)); + cb->function = function; + cb->cookie = cookie; + return cb; + } + + Sass_C_Import_Fn sass_import_get_function(Sass_C_Import_Callback fn) { return fn->function; } + void* sass_import_get_cookie(Sass_C_Import_Callback fn) { return fn->cookie; } + + // Creator for sass custom importer return argument list + struct Sass_Import** sass_make_import_list(size_t length) + { + return (Sass_Import**) calloc(length + 1, sizeof(Sass_Import*)); + } + + // Creator for a single import entry returned by the custom importer inside the list + // We take ownership of the memory for source and srcmap (freed when context is destroyd) + struct Sass_Import* sass_make_import_entry(const char* path, char* source, char* srcmap) + { + Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import)); + v->path = strdup(path); + v->source = source; + v->srcmap = srcmap; + return v; + } + + // Setters and getters for entries on the import list + void sass_import_set_list_entry(struct Sass_Import** list, size_t idx, struct Sass_Import* entry) { list[idx] = entry; } + struct Sass_Import* sass_import_get_list_entry(struct Sass_Import** list, size_t idx) { return list[idx]; } + + // Deallocator for the allocated memory + void sass_delete_import_list(struct Sass_Import** list) + { + struct Sass_Import** it = list; + while(*list) { + free((*list)->path); + free((*list)->source); + free((*list)->srcmap); + free(*list); + ++list; + } + free(it); + } + + // Getter for import entry + const char* sass_import_get_path(struct Sass_Import* entry) { return entry->path; } + const char* sass_import_get_source(struct Sass_Import* entry) { return entry->source; } + const char* sass_import_get_srcmap(struct Sass_Import* entry) { return entry->srcmap; } + + // Explicit functions to take ownership of the memory + // Resets our own property since we do not know if it is still alive + char* sass_import_take_source(struct Sass_Import* entry) { char* ptr = entry->source; entry->source = 0; return ptr; } + char* sass_import_take_srcmap(struct Sass_Import* entry) { char* ptr = entry->srcmap; entry->srcmap = 0; return ptr; } + +} diff --git a/sass_functions.h b/sass_functions.h new file mode 100644 index 0000000000..437f06514d --- /dev/null +++ b/sass_functions.h @@ -0,0 +1,76 @@ +#ifndef SASS_C_FUNCTIONS +#define SASS_C_FUNCTIONS + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Forward declaration +struct Sass_C_Import_Descriptor; + +// Typedef defining the custom importer callback +typedef struct Sass_C_Import_Descriptor (*Sass_C_Import_Callback); +// Typedef defining the importer c function prototype +typedef struct Sass_Import** (*Sass_C_Import_Fn) (const char* url, void* cookie); + +// Creators for custom importer callback (with some additional pointer) +// The pointer is mostly used to store the callback into the actual binding +Sass_C_Import_Callback sass_make_importer (Sass_C_Import_Fn, void* cookie); + +// Getters for import function descriptors +Sass_C_Import_Fn sass_import_get_function (Sass_C_Import_Callback fn); +void* sass_import_get_cookie (Sass_C_Import_Callback fn); + + +// Creator for sass custom importer return argument list +struct Sass_Import** sass_make_import_list (size_t length); +// Creator for a single import entry returned by the custom importer inside the list +struct Sass_Import* sass_make_import_entry (const char* path, char* source, char* srcmap); + +// Setters to insert an entry into the import list (you may also use [] access directly) +// Since we are dealing with pointers they should have a guaranteed and fixed size +void sass_import_set_list_entry (struct Sass_Import** list, size_t idx, struct Sass_Import* entry); +struct Sass_Import* sass_import_get_list_entry (struct Sass_Import** list, size_t idx); + +// Getters for import entry +const char* sass_import_get_path (struct Sass_Import*); +const char* sass_import_get_source (struct Sass_Import*); +const char* sass_import_get_srcmap (struct Sass_Import*); +// Explicit functions to take ownership of these items +// The property on our struct will be reset to NULL +char* sass_import_take_source (struct Sass_Import*); +char* sass_import_take_srcmap (struct Sass_Import*); + +// Deallocator for associated memory (incl. entries) +void sass_delete_import_list (struct Sass_Import**); + + +// Forward declaration +struct Sass_C_Function_Descriptor; + +// Typedef defining null terminated list of custom callbacks +typedef struct Sass_C_Function_Descriptor* (*Sass_C_Function_List); +typedef struct Sass_C_Function_Descriptor (*Sass_C_Function_Callback); +// Typedef defining custom function prototype and its return value type +typedef union Sass_Value*(*Sass_C_Function) (union Sass_Value*, void *cookie); + + +// Creators for sass function list and function descriptors +Sass_C_Function_List sass_make_function_list (size_t length); +Sass_C_Function_Callback sass_make_function (const char* signature, Sass_C_Function fn, void* cookie); + +// Getters for custom function descriptors +const char* sass_function_get_signature (Sass_C_Function_Callback fn); +Sass_C_Function sass_function_get_function (Sass_C_Function_Callback fn); +void* sass_function_get_cookie (Sass_C_Function_Callback fn); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sass_interface.cpp b/sass_interface.cpp index 1347cbde69..5e304d9ac7 100644 --- a/sass_interface.cpp +++ b/sass_interface.cpp @@ -17,7 +17,6 @@ #include #include #include -#include extern "C" { using namespace std; @@ -25,12 +24,33 @@ extern "C" { sass_context* sass_new_context() { return (sass_context*) calloc(1, sizeof(sass_context)); } - void free_string_array(char ** arr, int num) { + // helper for safe access to c_ctx + static const char* safe_str (const char* str) { + return str == NULL ? "" : str; + } + + static void copy_strings(const std::vector& strings, char*** array, int skip = 0) { + int num = static_cast(strings.size()); + char** arr = (char**) malloc(sizeof(char*)* num + 1); + + for(int i = skip; i < num; i++) { + arr[i-skip] = (char*) malloc(sizeof(char) * strings[i].size() + 1); + std::copy(strings[i].begin(), strings[i].end(), arr[i-skip]); + arr[i-skip][strings[i].size()] = '\0'; + } + + arr[num-skip] = 0; + *array = arr; + } + + static void free_string_array(char ** arr) { if(!arr) return; - for(int i = 0; i < num; i++) { - free(arr[i]); + char **it = arr; + while (it && (*it)) { + free(*it); + ++it; } free(arr); @@ -43,7 +63,7 @@ extern "C" { if (ctx->error_message) free(ctx->error_message); if (ctx->c_functions) free(ctx->c_functions); - free_string_array(ctx->included_files, ctx->num_included_files); + free_string_array(ctx->included_files); free(ctx); } @@ -58,7 +78,7 @@ extern "C" { if (ctx->error_message) free(ctx->error_message); if (ctx->c_functions) free(ctx->c_functions); - free_string_array(ctx->included_files, ctx->num_included_files); + free_string_array(ctx->included_files); free(ctx); } @@ -68,29 +88,10 @@ extern "C" { void sass_free_folder_context(sass_folder_context* ctx) { - free_string_array(ctx->included_files, ctx->num_included_files); + free_string_array(ctx->included_files); free(ctx); } - void copy_strings(const std::vector& strings, char*** array, int* n, int skip = 0) { - int num = static_cast(strings.size()); - char** arr = (char**) malloc(sizeof(char*)* num); - - for(int i = skip; i < num; i++) { - arr[i-skip] = (char*) malloc(sizeof(char) * strings[i].size() + 1); - std::copy(strings[i].begin(), strings[i].end(), arr[i-skip]); - arr[i-skip][strings[i].size()] = '\0'; - } - - *array = arr; - *n = num - skip; - } - - // helper for safe access to c_ctx - const char* safe_str (const char* str) { - return str == NULL ? "" : str; - } - int sass_compile(sass_context* c_ctx) { using namespace Sass; @@ -123,20 +124,18 @@ extern "C" { .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) ); if (c_ctx->c_functions) { - struct Sass_C_Function_Descriptor* this_func_data = c_ctx->c_functions; - while (this_func_data->signature && this_func_data->function) { + struct Sass_C_Function_Descriptor** this_func_data = c_ctx->c_functions; + while ((this_func_data) && (*this_func_data)) { cpp_ctx.c_functions.push_back(*this_func_data); ++this_func_data; } } - // by checking c_ctx->input_path, implementors can pass in an empty string - c_ctx->output_string = c_ctx->input_path ? cpp_ctx.compile_string(input_path) : - cpp_ctx.compile_string(); + c_ctx->output_string = cpp_ctx.compile_string(); c_ctx->source_map_string = cpp_ctx.generate_source_map(); c_ctx->error_message = 0; c_ctx->error_status = 0; - copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files, &c_ctx->num_included_files, 1); + copy_strings(cpp_ctx.get_included_files(1), &c_ctx->included_files, 1); } catch (Error& e) { stringstream msg_stream; @@ -212,8 +211,8 @@ extern "C" { .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) ); if (c_ctx->c_functions) { - struct Sass_C_Function_Descriptor* this_func_data = c_ctx->c_functions; - while (this_func_data->signature && this_func_data->function) { + struct Sass_C_Function_Descriptor** this_func_data = c_ctx->c_functions; + while ((this_func_data) && (*this_func_data)) { cpp_ctx.c_functions.push_back(*this_func_data); ++this_func_data; } @@ -223,7 +222,7 @@ extern "C" { c_ctx->error_message = 0; c_ctx->error_status = 0; - copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files, &c_ctx->num_included_files); + copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files); } catch (Error& e) { stringstream msg_stream; @@ -274,20 +273,4 @@ extern "C" { return 1; } - // caller must free the returned memory - char* quote (const char *str, const char quotemark) { - string quoted = Sass::quote(str, quotemark); - char *cstr = (char*) malloc(quoted.length() + 1); - std::strcpy(cstr, quoted.c_str()); - return cstr; - } - - // caller must free the returned memory - char* unquote (const char *str) { - string unquoted = Sass::unquote(str); - char *cstr = (char*) malloc(unquoted.length() + 1); - std::strcpy(cstr, unquoted.c_str()); - return cstr; - } - } diff --git a/sass_interface.h b/sass_interface.h index abda309df7..55d8c4613d 100644 --- a/sass_interface.h +++ b/sass_interface.h @@ -1,17 +1,14 @@ -#define SASS_INTERFACE +#ifndef SASS_C_INTERFACE +#define SASS_C_INTERFACE -#include "sass.h" +#include #include -#include "sass2scss.h" +#include "sass.h" #ifdef __cplusplus extern "C" { #endif -#define SASS_STYLE_NESTED 0 -#define SASS_STYLE_EXPANDED 1 -#define SASS_STYLE_COMPACT 2 -#define SASS_STYLE_COMPRESSED 3 // Please ensure there are no null values. // Thar be dragons. @@ -51,7 +48,7 @@ struct sass_context { struct sass_options options; int error_status; char* error_message; - struct Sass_C_Function_Descriptor* c_functions; + Sass_C_Function_List c_functions; char** included_files; int num_included_files; }; @@ -64,7 +61,7 @@ struct sass_file_context { struct sass_options options; int error_status; char* error_message; - struct Sass_C_Function_Descriptor* c_functions; + Sass_C_Function_List c_functions; char** included_files; int num_included_files; }; @@ -75,26 +72,26 @@ struct sass_folder_context { struct sass_options options; int error_status; char* error_message; - struct Sass_C_Function_Descriptor* c_functions; + Sass_C_Function_List c_functions; char** included_files; int num_included_files; }; -struct sass_context* sass_new_context (void); -struct sass_file_context* sass_new_file_context (void); +struct sass_context* sass_new_context (void); +struct sass_file_context* sass_new_file_context (void); struct sass_folder_context* sass_new_folder_context (void); -void sass_free_context (struct sass_context* ctx); -void sass_free_file_context (struct sass_file_context* ctx); -void sass_free_folder_context (struct sass_folder_context* ctx); +void sass_free_context (struct sass_context* ctx); +void sass_free_file_context (struct sass_file_context* ctx); +void sass_free_folder_context(struct sass_folder_context* ctx); -int sass_compile (struct sass_context* ctx); -int sass_compile_file (struct sass_file_context* ctx); -int sass_compile_folder (struct sass_folder_context* ctx); +int sass_compile (struct sass_context* ctx); +int sass_compile_file (struct sass_file_context* ctx); +int sass_compile_folder (struct sass_folder_context* ctx); -char* quote (const char *str, const char quotemark); -char* unquote (const char *str); #ifdef __cplusplus } #endif + +#endif diff --git a/sass_values.cpp b/sass_values.cpp new file mode 100644 index 0000000000..308910027b --- /dev/null +++ b/sass_values.cpp @@ -0,0 +1,250 @@ +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include +#include "sass_values.h" + +extern "C" { + using namespace std; + + 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; + 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; + }; + + 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_MapPair { + union Sass_Value* key; + union Sass_Value* value; + }; + + // Return the sass tag for a generic sass value + enum Sass_Tag sass_value_get_tag(union Sass_Value* v) { return v->unknown.tag; } + + // Check value for specified type + bool sass_value_is_null(union Sass_Value* v) { return v->unknown.tag == SASS_NULL; } + bool sass_value_is_map(union Sass_Value* v) { return v->unknown.tag == SASS_MAP; } + bool sass_value_is_list(union Sass_Value* v) { return v->unknown.tag == SASS_LIST; } + bool sass_value_is_number(union Sass_Value* v) { return v->unknown.tag == SASS_NUMBER; } + bool sass_value_is_string(union Sass_Value* v) { return v->unknown.tag == SASS_STRING; } + bool sass_value_is_boolean(union Sass_Value* v) { return v->unknown.tag == SASS_BOOLEAN; } + bool sass_value_is_error(union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; } + bool sass_value_is_color(union Sass_Value* v) { return v->unknown.tag == SASS_COLOR; } + + // Getters and setters for Sass_Number + double sass_number_get_value(union Sass_Value* v) { return v->number.value; } + void sass_number_set_value(union Sass_Value* v, double value) { v->number.value = value; } + const char* sass_number_get_unit(union Sass_Value* v) { return v->number.unit; } + void sass_number_set_unit(union Sass_Value* v, char* unit) { v->number.unit = unit; } + + // Getters and setters for Sass_String + const char* sass_string_get_value(union Sass_Value* v) { return v->string.value; } + void sass_string_set_value(union Sass_Value* v, char* value) { v->string.value = value; } + + // Getters and setters for Sass_Boolean + bool sass_boolean_get_value(union Sass_Value* v) { return v->boolean.value; } + void sass_boolean_set_value(union Sass_Value* v, bool value) { v->boolean.value = value; } + + // Getters and setters for Sass_Color + double sass_color_get_r(union Sass_Value* v) { return v->color.r; } + void sass_color_set_r(union Sass_Value* v, double r) { v->color.r = r; } + double sass_color_get_g(union Sass_Value* v) { return v->color.g; } + void sass_color_set_g(union Sass_Value* v, double g) { v->color.g = g; } + double sass_color_get_b(union Sass_Value* v) { return v->color.b; } + void sass_color_set_b(union Sass_Value* v, double b) { v->color.b = b; } + double sass_color_get_a(union Sass_Value* v) { return v->color.a; } + void sass_color_set_a(union Sass_Value* v, double a) { v->color.a = a; } + + // Getters and setters for Sass_List + size_t sass_list_get_length(union Sass_Value* v) { return v->list.length; } + enum Sass_Separator sass_list_get_separator(union Sass_Value* v) { return v->list.separator; } + void sass_list_set_separator(union Sass_Value* v, enum Sass_Separator separator) { v->list.separator = separator; } + // Getters and setters for Sass_List values + union Sass_Value* sass_list_get_value(union Sass_Value* v, size_t i) { return v->list.values[i]; } + void sass_list_set_value(union Sass_Value* v, size_t i, union Sass_Value* value) { v->list.values[i] = value; } + + // Getters and setters for Sass_Map + size_t sass_map_get_length(union Sass_Value* v) { return v->map.length; } + // Getters and setters for Sass_List keys and values + union Sass_Value* sass_map_get_key(union Sass_Value* v, size_t i) { return v->map.pairs[i].key; } + union Sass_Value* sass_map_get_value(union Sass_Value* v, size_t i) { return v->map.pairs[i].value; } + void sass_map_set_key(union Sass_Value* v, size_t i, union Sass_Value* key) { v->map.pairs[i].key = key; } + void sass_map_set_value(union Sass_Value* v, size_t i, union Sass_Value* val) { v->map.pairs[i].value = val; } + + // Getters and setters for Sass_Error + char* sass_error_get_message(union Sass_Value* v) { return v->error.message; }; + void sass_error_set_message(union Sass_Value* v, char* msg) { v->error.message = msg; }; + + // Creator functions for all value types + + union Sass_Value* sass_make_boolean(bool val) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->boolean.tag = SASS_BOOLEAN; + v->boolean.value = val; + return v; + } + + union Sass_Value* sass_make_number(double val, const char* unit) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->number.tag = SASS_NUMBER; + v->number.value = val; + v->number.unit = strdup(unit); + return v; + } + + union Sass_Value* sass_make_color(double r, double g, double b, double a) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->color.tag = SASS_COLOR; + v->color.r = r; + v->color.g = g; + v->color.b = b; + v->color.a = a; + return v; + } + + union Sass_Value* sass_make_string(const char* val) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->string.tag = SASS_STRING; + v->string.value = strdup(val); + return v; + } + + union Sass_Value* sass_make_list(size_t len, enum Sass_Separator sep) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->list.tag = SASS_LIST; + v->list.length = len; + v->list.separator = sep; + v->list.values = (union Sass_Value**) calloc(len, sizeof(union Sass_Value)); + return v; + } + + union Sass_Value* sass_make_map(size_t len) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->map.tag = SASS_MAP; + v->map.length = len; + v->map.pairs = (struct Sass_MapPair*) calloc(len, sizeof(struct Sass_MapPair)); + return v; + } + + union Sass_Value* sass_make_null(void) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->null.tag = SASS_NULL; + return v; + } + + union Sass_Value* sass_make_error(const char* msg) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + v->error.tag = SASS_ERROR; + v->error.message = strdup(msg); + return v; + } + + // will free all associated sass values + void sass_delete_value(union Sass_Value* val) { + + size_t i; + switch(val->unknown.tag) { + case SASS_NULL: { + } break; + case SASS_BOOLEAN: { + } break; + case SASS_NUMBER: { + free(val->number.unit); + } break; + case SASS_COLOR: { + } break; + case SASS_STRING: { + free(val->string.value); + } break; + case SASS_LIST: { + for (i=0; ilist.length; i++) { + sass_delete_value(val->list.values[i]); + } + free(val->list.values); + } break; + case SASS_MAP: { + for (i=0; imap.length; i++) { + sass_delete_value(val->map.pairs[i].key); + sass_delete_value(val->map.pairs[i].value); + } + free(val->map.pairs); + } break; + case SASS_ERROR: { + free(val->error.message); + } break; + } + + free(val); + + } + +} diff --git a/sass_values.h b/sass_values.h new file mode 100644 index 0000000000..73f8c2dd1e --- /dev/null +++ b/sass_values.h @@ -0,0 +1,113 @@ +#ifndef SASS_C_VALUES +#define SASS_C_VALUES + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Forward declaration +union Sass_Value; + +// Type for Sass values +enum Sass_Tag { + SASS_BOOLEAN, + SASS_NUMBER, + SASS_COLOR, + SASS_STRING, + SASS_LIST, + SASS_MAP, + SASS_NULL, + SASS_ERROR +}; + +// Tags for denoting Sass list separators +enum Sass_Separator { + SASS_COMMA, + SASS_SPACE +}; + +// Return the sass tag for a generic sass value +// Check is needed before accessing specific values! +enum Sass_Tag sass_value_get_tag (union Sass_Value* v); + +// Check value to be of a specific type +// Can also be used before accessing properties! +bool sass_value_is_null (union Sass_Value* v); +bool sass_value_is_number (union Sass_Value* v); +bool sass_value_is_string (union Sass_Value* v); +bool sass_value_is_boolean (union Sass_Value* v); +bool sass_value_is_color (union Sass_Value* v); +bool sass_value_is_list (union Sass_Value* v); +bool sass_value_is_map (union Sass_Value* v); +bool sass_value_is_error (union Sass_Value* v); + +// Getters and setters for Sass_Number +double sass_number_get_value (union Sass_Value* v); +void sass_number_set_value (union Sass_Value* v, double value); +const char* sass_number_get_unit (union Sass_Value* v); +void sass_number_set_unit (union Sass_Value* v, char* unit); + +// Getters and setters for Sass_String +const char* sass_string_get_value (union Sass_Value* v); +void sass_string_set_value (union Sass_Value* v, char* value); + +// Getters and setters for Sass_Boolean +bool sass_boolean_get_value (union Sass_Value* v); +void sass_boolean_set_value (union Sass_Value* v, bool value); + +// Getters and setters for Sass_Color +double sass_color_get_r (union Sass_Value* v); +void sass_color_set_r (union Sass_Value* v, double r); +double sass_color_get_g (union Sass_Value* v); +void sass_color_set_g (union Sass_Value* v, double g); +double sass_color_get_b (union Sass_Value* v); +void sass_color_set_b (union Sass_Value* v, double b); +double sass_color_get_a (union Sass_Value* v); +void sass_color_set_a (union Sass_Value* v, double a); + +// Getter for the number of items in list +size_t sass_list_get_length (union Sass_Value* v); +// Getters and setters for Sass_List +enum Sass_Separator sass_list_get_separator (union Sass_Value* v); +void sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value); +// Getters and setters for Sass_List values +union Sass_Value* sass_list_get_value (union Sass_Value* v, size_t i); +void sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value); + +// Getter for the number of items in map +size_t sass_map_get_length (union Sass_Value* v); +// Getters and setters for Sass_List keys and values +union Sass_Value* sass_map_get_key (union Sass_Value* v, size_t i); +void sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*); +union Sass_Value* sass_map_get_value (union Sass_Value* v, size_t i); +void sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*); + +// Getters and setters for Sass_Error +char* sass_error_get_message (union Sass_Value* v); +void sass_error_set_message (union Sass_Value* v, char* msg); + +// Creator functions for all value types +union Sass_Value* sass_make_null (void); +union Sass_Value* sass_make_boolean (bool val); +union Sass_Value* sass_make_string (const char* val); +union Sass_Value* sass_make_number (double val, const char* unit); +union Sass_Value* sass_make_color (double r, double g, double b, double a); +union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep); +union Sass_Value* sass_make_map (size_t len); +union Sass_Value* sass_make_error (const char* msg); + +// Generic destructor function for all types +// Will release memory of all associated Sass_Values +// Means we will delete recursively for lists and maps +void sass_delete_value (union Sass_Value* val); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/to_c.cpp b/to_c.cpp index a3942e715c..b03bb4a3fb 100644 --- a/to_c.cpp +++ b/to_c.cpp @@ -1,59 +1,61 @@ #include "to_c.hpp" #include "ast.hpp" +#include "sass_values.h" + namespace Sass { using namespace std; - Sass_Value To_C::fallback_impl(AST_Node* n) - { return make_sass_null(); } + Sass_Value* To_C::fallback_impl(AST_Node* n) + { return sass_make_null(); } - Sass_Value To_C::operator()(Boolean* b) - { return make_sass_boolean(b->value()); } + Sass_Value* To_C::operator()(Boolean* b) + { return sass_make_boolean(b->value()); } - Sass_Value To_C::operator()(Number* n) - { return make_sass_number(n->value(), n->unit().c_str()); } + Sass_Value* To_C::operator()(Number* n) + { return sass_make_number(n->value(), n->unit().c_str()); } - Sass_Value To_C::operator()(Color* c) - { return make_sass_color(c->r(), c->g(), c->b(), c->a()); } + Sass_Value* To_C::operator()(Color* c) + { return sass_make_color(c->r(), c->g(), c->b(), c->a()); } - Sass_Value To_C::operator()(String_Constant* s) - { return make_sass_string(s->value().c_str()); } + Sass_Value* To_C::operator()(String_Constant* s) + { return sass_make_string(s->value().c_str()); } - Sass_Value To_C::operator()(List* l) + Sass_Value* To_C::operator()(List* l) { - Sass_Value v = make_sass_list(l->length(), l->separator() == List::COMMA ? SASS_COMMA : SASS_SPACE); + Sass_Value* v = sass_make_list(l->length(), l->separator() == List::COMMA ? SASS_COMMA : SASS_SPACE); for (size_t i = 0, L = l->length(); i < L; ++i) { - v.list.values[i] = (*l)[i]->perform(this); + sass_list_set_value(v, i, (*l)[i]->perform(this)); } return v; } - Sass_Value To_C::operator()(Map* m) + Sass_Value* To_C::operator()(Map* m) { - Sass_Value v = make_sass_map(m->length()); + Sass_Value* v = sass_make_map(m->length()); int i = 0; for (auto key : m->keys()) { - v.map.pairs[i].key = key->perform(this); - v.map.pairs[i].value = m->at(key)->perform(this); + sass_map_set_key(v, i, key->perform(this)); + sass_map_set_value(v, i, m->at(key)->perform(this)); i++; } return v; } - Sass_Value To_C::operator()(Arguments* a) + Sass_Value* To_C::operator()(Arguments* a) { - Sass_Value v = make_sass_list(a->length(), SASS_COMMA); + Sass_Value* v = sass_make_list(a->length(), SASS_COMMA); for (size_t i = 0, L = a->length(); i < L; ++i) { - v.list.values[i] = (*a)[i]->perform(this); + sass_list_set_value(v, i, (*a)[i]->perform(this)); } return v; } - Sass_Value To_C::operator()(Argument* a) + Sass_Value* To_C::operator()(Argument* a) { return a->value()->perform(this); } // not strictly necessary because of the fallback - Sass_Value To_C::operator()(Null* n) - { return make_sass_null(); } + Sass_Value* To_C::operator()(Null* n) + { return sass_make_null(); } }; diff --git a/to_c.hpp b/to_c.hpp index a78e63f622..a9a4c3d443 100644 --- a/to_c.hpp +++ b/to_c.hpp @@ -4,9 +4,7 @@ #include "operation.hpp" #endif -#ifndef SASS -#include "sass.h" -#endif +#include "sass_values.h" namespace Sass { using namespace std; @@ -19,28 +17,28 @@ namespace Sass { class Map; class Null; - class To_C : public Operation_CRTP { + class To_C : public Operation_CRTP { - Sass_Value fallback_impl(AST_Node* n); + Sass_Value* fallback_impl(AST_Node* n); public: To_C() { } virtual ~To_C() { } - using Operation::operator(); - - Sass_Value operator()(Boolean*); - Sass_Value operator()(Number*); - Sass_Value operator()(Color*); - Sass_Value operator()(String_Constant*); - Sass_Value operator()(List*); - Sass_Value operator()(Map*); - Sass_Value operator()(Null*); - Sass_Value operator()(Arguments*); - Sass_Value operator()(Argument*); + using Operation::operator(); + + Sass_Value* operator()(Boolean*); + Sass_Value* operator()(Number*); + Sass_Value* operator()(Color*); + Sass_Value* operator()(String_Constant*); + Sass_Value* operator()(List*); + Sass_Value* operator()(Map*); + Sass_Value* operator()(Null*); + Sass_Value* operator()(Arguments*); + Sass_Value* operator()(Argument*); template - Sass_Value fallback(U x) { return fallback_impl(x); } + Sass_Value* fallback(U x) { return fallback_impl(x); } }; }