diff --git a/Makefile.conf b/Makefile.conf index 5ba968b68..2725c6bf0 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -10,6 +10,14 @@ SOURCES = \ node.cpp \ context.cpp \ constants.cpp \ + fn_utils.cpp \ + fn_miscs.cpp \ + fn_maps.cpp \ + fn_lists.cpp \ + fn_colors.cpp \ + fn_numbers.cpp \ + fn_strings.cpp \ + fn_selectors.cpp \ functions.cpp \ color_maps.cpp \ environment.cpp \ @@ -43,7 +51,8 @@ SOURCES = \ sass2scss.cpp \ backtrace.cpp \ operators.cpp \ - to_c.cpp \ + ast2c.cpp \ + c2ast.cpp \ to_value.cpp \ source_map.cpp \ subset_map.cpp \ diff --git a/src/ast.cpp b/src/ast.cpp index c3b38efb9..357bd778d 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -1259,7 +1259,7 @@ namespace Sass { return list; } - Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector& pstack, Backtraces& traces, bool implicit_parent) + Selector_List_Ptr Selector_List::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) { if (!this->has_parent_ref()) return this; Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate()); @@ -1273,7 +1273,7 @@ namespace Sass { return ss; } - Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector& pstack, Backtraces& traces, bool implicit_parent) + Selector_List_Ptr Complex_Selector::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) { Complex_Selector_Obj tail = this->tail(); Compound_Selector_Obj head = this->head(); @@ -1590,8 +1590,8 @@ namespace Sass { bool Selector_Schema::has_real_parent_ref() const { if (String_Schema_Obj schema = Cast(contents())) { - Parent_Selector_Obj p = Cast(schema->at(0)); - return schema->length() > 0 && p && p->is_real_parent_ref(); + if (schema->length() == 0) return false; + return Cast(schema->at(0)); } return false; } @@ -2108,6 +2108,44 @@ namespace Sass { } } + Function_Call::Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie) + : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0) + { concrete_type(FUNCTION); } + Function_Call::Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func) + : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) + { concrete_type(FUNCTION); } + Function_Call::Function_Call(ParserState pstate, std::string n, Arguments_Obj args) + : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), via_call_(false), cookie_(0), hash_(0) + { concrete_type(FUNCTION); } + + bool Function_Call::operator==(const Expression& rhs) const + { + try + { + Function_Call_Ptr_Const m = Cast(&rhs); + if (!(m && *sname() == *m->sname())) return false; + if (!(m && arguments()->length() == m->arguments()->length())) return false; + for (size_t i =0, L = arguments()->length(); i < L; ++i) + if (!(*(*arguments())[i] == *(*m->arguments())[i])) return false; + return true; + } + catch (std::bad_cast&) + { + return false; + } + catch (...) { throw; } + } + + size_t Function_Call::hash() + { + if (hash_ == 0) { + hash_ = std::hash()(name()); + for (auto argument : arguments()->elements()) + hash_combine(hash_, argument->hash()); + } + return hash_; + } + ////////////////////////////////////////////////////////////////////////////////////////// // Convert map to (key, value) list. ////////////////////////////////////////////////////////////////////////////////////////// @@ -2124,38 +2162,6 @@ namespace Sass { return ret; } - ////////////////////////////////////////////////////////////////////////////////////////// - // Copy implementations - ////////////////////////////////////////////////////////////////////////////////////////// - - #ifdef DEBUG_SHARED_PTR - - #define IMPLEMENT_AST_OPERATORS(klass) \ - klass##_Ptr klass::copy(std::string file, size_t line) const { \ - klass##_Ptr cpy = new klass(this); \ - cpy->trace(file, line); \ - return cpy; \ - } \ - klass##_Ptr klass::clone(std::string file, size_t line) const { \ - klass##_Ptr cpy = copy(file, line); \ - cpy->cloneChildren(); \ - return cpy; \ - } \ - - #else - - #define IMPLEMENT_AST_OPERATORS(klass) \ - klass##_Ptr klass::copy() const { \ - return new klass(this); \ - } \ - klass##_Ptr klass::clone() const { \ - klass##_Ptr cpy = copy(); \ - cpy->cloneChildren(); \ - return cpy; \ - } \ - - #endif - IMPLEMENT_AST_OPERATORS(Supports_Operator); IMPLEMENT_AST_OPERATORS(Supports_Negation); IMPLEMENT_AST_OPERATORS(Compound_Selector); @@ -2182,6 +2188,7 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Color); IMPLEMENT_AST_OPERATORS(Null); IMPLEMENT_AST_OPERATORS(Parent_Selector); + IMPLEMENT_AST_OPERATORS(Parent_Reference); IMPLEMENT_AST_OPERATORS(Import); IMPLEMENT_AST_OPERATORS(Import_Stub); IMPLEMENT_AST_OPERATORS(Function_Call); @@ -2213,7 +2220,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Arguments); IMPLEMENT_AST_OPERATORS(Argument); IMPLEMENT_AST_OPERATORS(Unary_Expression); - IMPLEMENT_AST_OPERATORS(Function_Call_Schema); IMPLEMENT_AST_OPERATORS(Block); IMPLEMENT_AST_OPERATORS(Content); IMPLEMENT_AST_OPERATORS(Trace); diff --git a/src/ast.hpp b/src/ast.hpp index a2be8685c..8f247f8cc 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -13,42 +13,6 @@ #include "sass/base.h" #include "ast_fwd_decl.hpp" -#ifdef DEBUG_SHARED_PTR - -#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \ - virtual klass##_Ptr copy(std::string, size_t) const = 0; \ - virtual klass##_Ptr clone(std::string, size_t) const = 0; \ - -#define ATTACH_AST_OPERATIONS(klass) \ - virtual klass##_Ptr copy(std::string, size_t) const; \ - virtual klass##_Ptr clone(std::string, size_t) const; \ - -#else - -#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \ - virtual klass##_Ptr copy() const = 0; \ - virtual klass##_Ptr clone() const = 0; \ - -#define ATTACH_AST_OPERATIONS(klass) \ - virtual klass##_Ptr copy() const; \ - virtual klass##_Ptr clone() const; \ - -#endif - -#ifdef __clang__ - -/* - * There are some overloads used here that trigger the clang overload - * hiding warning. Specifically: - * - * Type type() which hides string type() from Expression - * - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Woverloaded-virtual" - -#endif - #include "util.hpp" #include "units.hpp" #include "context.hpp" @@ -63,6 +27,7 @@ #include "ast_def_macros.hpp" #include "ast_fwd_decl.hpp" #include "source_map.hpp" +#include "fn_utils.hpp" #include "sass.h" @@ -117,6 +82,12 @@ namespace Sass { : pstate_(ptr->pstate_) { } + // allow implicit conversion to string + // needed for by SharedPtr implementation + operator std::string() { + return to_string(); + } + // AST_Node(AST_Node& ptr) = delete; virtual ~AST_Node() = 0; @@ -135,7 +106,7 @@ namespace Sass { public: Offset off() { return pstate(); } Position pos() { return pstate(); } - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; inline AST_Node::~AST_Node() { } @@ -245,14 +216,14 @@ namespace Sass { ////////////////////////////////////////////////////////////////////// // base class for values that support operations ////////////////////////////////////////////////////////////////////// - class Value : public Expression { + class Value : public PreValue { public: Value(ParserState pstate, bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) - : Expression(pstate, d, e, i, ct) + : PreValue(pstate, d, e, i, ct) { } Value(const Value* ptr) - : Expression(ptr) + : PreValue(ptr) { } ATTACH_VIRTUAL_AST_OPERATIONS(Value); virtual bool operator== (const Expression& rhs) const = 0; @@ -504,7 +475,7 @@ namespace Sass { return Statement::has_content(); } ATTACH_AST_OPERATIONS(Block) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// @@ -545,7 +516,7 @@ namespace Sass { { statement_type(RULESET); } bool is_invisible() const; ATTACH_AST_OPERATIONS(Ruleset) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////// @@ -565,7 +536,7 @@ namespace Sass { { } bool bubbles() { return true; } ATTACH_AST_OPERATIONS(Bubble) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////// @@ -584,7 +555,7 @@ namespace Sass { name_(ptr->name_) { } ATTACH_AST_OPERATIONS(Trace) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////// @@ -602,7 +573,7 @@ namespace Sass { bool bubbles() { return true; } bool is_invisible() const; ATTACH_AST_OPERATIONS(Media_Block) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// @@ -637,7 +608,7 @@ namespace Sass { keyword_.compare("@keyframes") == 0; } ATTACH_AST_OPERATIONS(Directive) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// @@ -655,7 +626,7 @@ namespace Sass { : Has_Block(ptr), name_(ptr->name_) { statement_type(KEYFRAMERULE); } ATTACH_AST_OPERATIONS(Keyframe_Rule) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// @@ -682,7 +653,7 @@ namespace Sass { { statement_type(DECLARATION); } virtual bool is_invisible() const; ATTACH_AST_OPERATIONS(Declaration) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////// @@ -708,7 +679,7 @@ namespace Sass { is_global_(ptr->is_global_) { statement_type(ASSIGNMENT); } ATTACH_AST_OPERATIONS(Assignment) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// @@ -735,7 +706,7 @@ namespace Sass { std::vector& urls() { return urls_; } std::vector& incs() { return incs_; } ATTACH_AST_OPERATIONS(Import) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; // not yet resolved single import @@ -754,7 +725,7 @@ namespace Sass { : Statement(ptr), resource_(ptr->resource_) { statement_type(IMPORT_STUB); } ATTACH_AST_OPERATIONS(Import_Stub) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////// @@ -770,7 +741,7 @@ namespace Sass { : Statement(ptr), message_(ptr->message_) { statement_type(WARNING); } ATTACH_AST_OPERATIONS(Warning) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////// @@ -786,7 +757,7 @@ namespace Sass { : Statement(ptr), message_(ptr->message_) { statement_type(ERROR); } ATTACH_AST_OPERATIONS(Error) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////// @@ -802,7 +773,7 @@ namespace Sass { : Statement(ptr), value_(ptr->value_) { statement_type(DEBUGSTMT); } ATTACH_AST_OPERATIONS(Debug) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////// @@ -823,7 +794,7 @@ namespace Sass { virtual bool is_invisible() const { return /* is_important() == */ false; } ATTACH_AST_OPERATIONS(Comment) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////// @@ -846,7 +817,7 @@ namespace Sass { return Has_Block::has_content() || (alternative_ && alternative_->has_content()); } ATTACH_AST_OPERATIONS(If) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////// @@ -871,7 +842,7 @@ namespace Sass { is_inclusive_(ptr->is_inclusive_) { statement_type(FOR); } ATTACH_AST_OPERATIONS(For) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////// @@ -888,7 +859,7 @@ namespace Sass { : Has_Block(ptr), variables_(ptr->variables_), list_(ptr->list_) { statement_type(EACH); } ATTACH_AST_OPERATIONS(Each) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////// @@ -904,7 +875,7 @@ namespace Sass { : Has_Block(ptr), predicate_(ptr->predicate_) { statement_type(WHILE); } ATTACH_AST_OPERATIONS(While) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////// @@ -920,7 +891,7 @@ namespace Sass { : Statement(ptr), value_(ptr->value_) { statement_type(RETURN); } ATTACH_AST_OPERATIONS(Return) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////// @@ -936,16 +907,13 @@ namespace Sass { : Statement(ptr), selector_(ptr->selector_) { statement_type(EXTEND); } ATTACH_AST_OPERATIONS(Extension) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////////////// // Definitions for both mixins and functions. The two cases are distinguished // by a type tag. ///////////////////////////////////////////////////////////////////////////// - struct Backtrace; - typedef const char* Signature; - typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector); class Definition : public Has_Block { public: enum Type { MIXIN, FUNCTION }; @@ -1009,9 +977,7 @@ namespace Sass { Signature sig, std::string n, Parameters_Obj params, - Sass_Function_Entry c_func, - bool whatever, - bool whatever2) + Sass_Function_Entry c_func) : Has_Block(pstate, 0), name_(n), parameters_(params), @@ -1024,7 +990,7 @@ namespace Sass { signature_(sig) { } ATTACH_AST_OPERATIONS(Definition) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////// @@ -1043,7 +1009,7 @@ namespace Sass { arguments_(ptr->arguments_) { } ATTACH_AST_OPERATIONS(Mixin_Call) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////// @@ -1061,7 +1027,7 @@ namespace Sass { media_block_(ptr->media_block_) { statement_type(CONTENT); } ATTACH_AST_OPERATIONS(Content) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// @@ -1124,7 +1090,7 @@ namespace Sass { virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(List) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// @@ -1162,7 +1128,7 @@ namespace Sass { virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Map) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; inline static const std::string sass_op_to_name(enum Sass_OP op) { @@ -1276,7 +1242,7 @@ namespace Sass { } enum Sass_OP optype() const { return op_.operand; } ATTACH_AST_OPERATIONS(Binary_Expression) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// @@ -1332,7 +1298,7 @@ namespace Sass { return hash_; } ATTACH_AST_OPERATIONS(Unary_Expression) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////// @@ -1391,7 +1357,7 @@ namespace Sass { } ATTACH_AST_OPERATIONS(Argument) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// @@ -1427,7 +1393,7 @@ namespace Sass { Argument_Obj get_keyword_argument(); ATTACH_AST_OPERATIONS(Arguments) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////// @@ -1459,32 +1425,41 @@ namespace Sass { virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Function) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////// // Function calls. ////////////////// class Function_Call : public PreValue { - HASH_CONSTREF(std::string, name) + HASH_CONSTREF(String_Obj, sname) HASH_PROPERTY(Arguments_Obj, arguments) HASH_PROPERTY(Function_Obj, func) ADD_PROPERTY(bool, via_call) ADD_PROPERTY(void*, cookie) size_t hash_; public: - Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie) - : PreValue(pstate), name_(n), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0) + Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie); + Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func); + Function_Call(ParserState pstate, std::string n, Arguments_Obj args); + + Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, void* cookie) + : PreValue(pstate), sname_(n), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0) { concrete_type(FUNCTION); } - Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func) - : PreValue(pstate), name_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) + Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, Function_Obj func) + : PreValue(pstate), sname_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); } - Function_Call(ParserState pstate, std::string n, Arguments_Obj args) - : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0) + Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args) + : PreValue(pstate), sname_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); } + + std::string name() { + return sname(); + } + Function_Call(const Function_Call* ptr) : PreValue(ptr), - name_(ptr->name_), + sname_(ptr->sname_), arguments_(ptr->arguments_), func_(ptr->func_), via_call_(ptr->via_call_), @@ -1497,54 +1472,12 @@ namespace Sass { return false; } - virtual bool operator==(const Expression& rhs) const - { - try - { - Function_Call_Ptr_Const m = Cast(&rhs); - if (!(m && name() == m->name())) return false; - if (!(m && arguments()->length() == m->arguments()->length())) return false; - for (size_t i =0, L = arguments()->length(); i < L; ++i) - if (!(*(*arguments())[i] == *(*m->arguments())[i])) return false; - return true; - } - catch (std::bad_cast&) - { - return false; - } - catch (...) { throw; } - } + virtual bool operator==(const Expression& rhs) const; - virtual size_t hash() - { - if (hash_ == 0) { - hash_ = std::hash()(name()); - for (auto argument : arguments()->elements()) - hash_combine(hash_, argument->hash()); - } - return hash_; - } - ATTACH_AST_OPERATIONS(Function_Call) - ATTACH_OPERATIONS() - }; + virtual size_t hash(); - ///////////////////////// - // Function call schemas. - ///////////////////////// - class Function_Call_Schema : public Expression { - ADD_PROPERTY(String_Obj, name) - ADD_PROPERTY(Arguments_Obj, arguments) - public: - Function_Call_Schema(ParserState pstate, String_Obj n, Arguments_Obj args) - : Expression(pstate), name_(n), arguments_(args) - { concrete_type(STRING); } - Function_Call_Schema(const Function_Call_Schema* ptr) - : Expression(ptr), - name_(ptr->name_), - arguments_(ptr->arguments_) - { concrete_type(STRING); } - ATTACH_AST_OPERATIONS(Function_Call_Schema) - ATTACH_OPERATIONS() + ATTACH_AST_OPERATIONS(Function_Call) + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////// @@ -1580,7 +1513,7 @@ namespace Sass { } ATTACH_AST_OPERATIONS(Variable) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////// @@ -1623,7 +1556,7 @@ namespace Sass { virtual bool operator== (const Number& rhs) const; virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Number) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////// @@ -1667,7 +1600,7 @@ namespace Sass { virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Color) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////// @@ -1684,7 +1617,7 @@ namespace Sass { { concrete_type(C_ERROR); } virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Custom_Error) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////// @@ -1701,7 +1634,7 @@ namespace Sass { { concrete_type(C_WARNING); } virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Custom_Warning) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////// @@ -1736,7 +1669,7 @@ namespace Sass { virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Boolean) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// @@ -1759,7 +1692,7 @@ namespace Sass { return this->to_string() < rhs.to_string(); }; ATTACH_VIRTUAL_AST_OPERATIONS(String); - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; inline String::~String() { }; @@ -1767,16 +1700,16 @@ namespace Sass { // Interpolated strings. Meant to be reduced to flat strings during the // evaluation phase. /////////////////////////////////////////////////////////////////////// - class String_Schema : public String, public Vectorized { + class String_Schema : public String, public Vectorized { ADD_PROPERTY(bool, css) size_t hash_; public: String_Schema(ParserState pstate, size_t size = 0, bool css = true) - : String(pstate), Vectorized(size), css_(css), hash_(0) + : String(pstate), Vectorized(size), css_(css), hash_(0) { concrete_type(STRING); } String_Schema(const String_Schema* ptr) : String(ptr), - Vectorized(*ptr), + Vectorized(*ptr), css_(ptr->css_), hash_(ptr->hash_) { concrete_type(STRING); } @@ -1810,7 +1743,7 @@ namespace Sass { virtual bool operator==(const Expression& rhs) const; ATTACH_AST_OPERATIONS(String_Schema) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////// @@ -1863,7 +1796,7 @@ namespace Sass { static char single_quote() { return '\''; } ATTACH_AST_OPERATIONS(String_Constant) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////// @@ -1887,7 +1820,7 @@ namespace Sass { virtual bool operator==(const Expression& rhs) const; virtual std::string inspect() const; // quotes are forced on inspection ATTACH_AST_OPERATIONS(String_Quoted) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////// @@ -1912,7 +1845,7 @@ namespace Sass { is_restricted_(ptr->is_restricted_) { } ATTACH_AST_OPERATIONS(Media_Query) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////// @@ -1934,7 +1867,7 @@ namespace Sass { is_interpolated_(ptr->is_interpolated_) { } ATTACH_AST_OPERATIONS(Media_Query_Expression) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////// @@ -1951,7 +1884,7 @@ namespace Sass { { statement_type(SUPPORTS); } bool bubbles() { return true; } ATTACH_AST_OPERATIONS(Supports_Block) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////////////////// @@ -1967,7 +1900,7 @@ namespace Sass { { } virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; } ATTACH_AST_OPERATIONS(Supports_Condition) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////// @@ -1992,7 +1925,7 @@ namespace Sass { { } virtual bool needs_parens(Supports_Condition_Obj cond) const; ATTACH_AST_OPERATIONS(Supports_Operator) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////// @@ -2010,7 +1943,7 @@ namespace Sass { { } virtual bool needs_parens(Supports_Condition_Obj cond) const; ATTACH_AST_OPERATIONS(Supports_Negation) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////// @@ -2031,7 +1964,7 @@ namespace Sass { { } virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; } ATTACH_AST_OPERATIONS(Supports_Declaration) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////// @@ -2050,7 +1983,7 @@ namespace Sass { { } virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; } ATTACH_AST_OPERATIONS(Supports_Interpolation) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////// @@ -2071,7 +2004,7 @@ namespace Sass { { } bool exclude(std::string str); ATTACH_AST_OPERATIONS(At_Root_Query) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////// @@ -2121,7 +2054,7 @@ namespace Sass { return false; } ATTACH_AST_OPERATIONS(At_Root_Block) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////// @@ -2145,7 +2078,7 @@ namespace Sass { virtual bool operator== (const Expression& rhs) const; ATTACH_AST_OPERATIONS(Null) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////// @@ -2191,7 +2124,7 @@ namespace Sass { // } } ATTACH_AST_OPERATIONS(Parameter) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////////// @@ -2240,7 +2173,7 @@ namespace Sass { has_rest_parameter_(ptr->has_rest_parameter_) { } ATTACH_AST_OPERATIONS(Parameters) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////// @@ -2339,7 +2272,7 @@ namespace Sass { return hash_; } ATTACH_AST_OPERATIONS(Selector_Schema) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////// @@ -2434,7 +2367,7 @@ namespace Sass { bool operator<(const Simple_Selector& rhs) const; // default implementation should work for most of the simple selectors (otherwise overload) ATTACH_VIRTUAL_AST_OPERATIONS(Simple_Selector); - ATTACH_OPERATIONS(); + ATTACH_CRTP_PERFORM_METHODS(); }; inline Simple_Selector::~Simple_Selector() { } @@ -2464,7 +2397,25 @@ namespace Sass { std::string type() const { return "selector"; } static std::string type_name() { return "selector"; } ATTACH_AST_OPERATIONS(Parent_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////////// + // The Parent Reference Expression. + ////////////////////////////////// + class Parent_Reference : public Value { + public: + Parent_Reference(ParserState pstate) + : Value(pstate) {} + Parent_Reference(const Parent_Reference* ptr) + : Value(ptr) {} + std::string type() const { return "parent"; } + static std::string type_name() { return "parent"; } + virtual bool operator==(const Expression& rhs) const { + return true; // can they ever be not equal? + }; + ATTACH_AST_OPERATIONS(Parent_Reference) + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////////// @@ -2487,7 +2438,7 @@ namespace Sass { } virtual ~Placeholder_Selector() {}; ATTACH_AST_OPERATIONS(Placeholder_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////// @@ -2513,7 +2464,7 @@ namespace Sass { virtual bool operator<(const Simple_Selector& rhs) const; virtual bool operator<(const Element_Selector& rhs) const; ATTACH_AST_OPERATIONS(Element_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////// @@ -2533,7 +2484,7 @@ namespace Sass { } virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); ATTACH_AST_OPERATIONS(Class_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////// @@ -2553,7 +2504,7 @@ namespace Sass { } virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); ATTACH_AST_OPERATIONS(Id_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////// @@ -2592,7 +2543,7 @@ namespace Sass { virtual bool operator<(const Simple_Selector& rhs) const; virtual bool operator<(const Attribute_Selector& rhs) const; ATTACH_AST_OPERATIONS(Attribute_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////////////////////////////// @@ -2654,7 +2605,7 @@ namespace Sass { virtual bool operator<(const Pseudo_Selector& rhs) const; virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); ATTACH_AST_OPERATIONS(Pseudo_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////// @@ -2683,7 +2634,7 @@ namespace Sass { virtual bool operator<(const Wrapped_Selector& rhs) const; virtual void cloneChildren(); ATTACH_AST_OPERATIONS(Wrapped_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// @@ -2788,7 +2739,7 @@ namespace Sass { Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs); virtual void cloneChildren(); ATTACH_AST_OPERATIONS(Compound_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// @@ -2861,7 +2812,7 @@ namespace Sass { Complex_Selector_Obj innermost() { return last(); }; size_t length() const; - Selector_List_Ptr resolve_parent_refs(std::vector& pstack, Backtraces& traces, bool implicit_parent = true); + Selector_List_Ptr resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent = true); virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = ""); @@ -2953,7 +2904,7 @@ namespace Sass { virtual void cloneChildren(); ATTACH_AST_OPERATIONS(Complex_Selector) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////// @@ -2983,7 +2934,7 @@ namespace Sass { virtual bool has_parent_ref() const; virtual bool has_real_parent_ref() const; void remove_parent_selectors(); - Selector_List_Ptr resolve_parent_refs(std::vector& pstack, Backtraces& traces, bool implicit_parent = true); + Selector_List_Ptr resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent = true); virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = ""); @@ -3030,7 +2981,7 @@ namespace Sass { virtual bool operator==(const Expression& rhs) const; virtual void cloneChildren(); ATTACH_AST_OPERATIONS(Selector_List) - ATTACH_OPERATIONS() + ATTACH_CRTP_PERFORM_METHODS() }; // compare function for sorting and probably other other uses diff --git a/src/to_c.cpp b/src/ast2c.cpp similarity index 63% rename from src/to_c.cpp rename to src/ast2c.cpp index 8a6ea8d51..be9ea800f 100644 --- a/src/to_c.cpp +++ b/src/ast2c.cpp @@ -1,28 +1,25 @@ #include "sass.hpp" -#include "to_c.hpp" +#include "ast2c.hpp" #include "ast.hpp" namespace Sass { - union Sass_Value* To_C::fallback_impl(AST_Node_Ptr n) - { return sass_make_error("unknown type for C-API"); } - - union Sass_Value* To_C::operator()(Boolean_Ptr b) + union Sass_Value* AST2C::operator()(Boolean_Ptr b) { return sass_make_boolean(b->value()); } - union Sass_Value* To_C::operator()(Number_Ptr n) + union Sass_Value* AST2C::operator()(Number_Ptr n) { return sass_make_number(n->value(), n->unit().c_str()); } - union Sass_Value* To_C::operator()(Custom_Warning_Ptr w) + union Sass_Value* AST2C::operator()(Custom_Warning_Ptr w) { return sass_make_warning(w->message().c_str()); } - union Sass_Value* To_C::operator()(Custom_Error_Ptr e) + union Sass_Value* AST2C::operator()(Custom_Error_Ptr e) { return sass_make_error(e->message().c_str()); } - union Sass_Value* To_C::operator()(Color_Ptr c) + union Sass_Value* AST2C::operator()(Color_Ptr c) { return sass_make_color(c->r(), c->g(), c->b(), c->a()); } - union Sass_Value* To_C::operator()(String_Constant_Ptr s) + union Sass_Value* AST2C::operator()(String_Constant_Ptr s) { if (s->quote_mark()) { return sass_make_qstring(s->value().c_str()); @@ -31,10 +28,10 @@ namespace Sass { } } - union Sass_Value* To_C::operator()(String_Quoted_Ptr s) + union Sass_Value* AST2C::operator()(String_Quoted_Ptr s) { return sass_make_qstring(s->value().c_str()); } - union Sass_Value* To_C::operator()(List_Ptr l) + union Sass_Value* AST2C::operator()(List_Ptr l) { union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { @@ -43,7 +40,7 @@ namespace Sass { return v; } - union Sass_Value* To_C::operator()(Map_Ptr m) + union Sass_Value* AST2C::operator()(Map_Ptr m) { union Sass_Value* v = sass_make_map(m->length()); int i = 0; @@ -55,7 +52,7 @@ namespace Sass { return v; } - union Sass_Value* To_C::operator()(Arguments_Ptr a) + union Sass_Value* AST2C::operator()(Arguments_Ptr a) { union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false); for (size_t i = 0, L = a->length(); i < L; ++i) { @@ -64,11 +61,11 @@ namespace Sass { return v; } - union Sass_Value* To_C::operator()(Argument_Ptr a) + union Sass_Value* AST2C::operator()(Argument_Ptr a) { return a->value()->perform(this); } // not strictly necessary because of the fallback - union Sass_Value* To_C::operator()(Null_Ptr n) + union Sass_Value* AST2C::operator()(Null_Ptr n) { return sass_make_null(); } }; diff --git a/src/to_c.hpp b/src/ast2c.hpp similarity index 71% rename from src/to_c.hpp rename to src/ast2c.hpp index a5331e3bf..428791619 100644 --- a/src/to_c.hpp +++ b/src/ast2c.hpp @@ -1,5 +1,5 @@ -#ifndef SASS_TO_C_H -#define SASS_TO_C_H +#ifndef SASS_AST2C_H +#define SASS_AST2C_H #include "ast_fwd_decl.hpp" #include "operation.hpp" @@ -7,14 +7,12 @@ namespace Sass { - class To_C : public Operation_CRTP { - // override this to define a catch-all - union Sass_Value* fallback_impl(AST_Node_Ptr n); + class AST2C : public Operation_CRTP { public: - To_C() { } - ~To_C() { } + AST2C() { } + ~AST2C() { } union Sass_Value* operator()(Boolean_Ptr); union Sass_Value* operator()(Number_Ptr); @@ -29,9 +27,10 @@ namespace Sass { union Sass_Value* operator()(Arguments_Ptr); union Sass_Value* operator()(Argument_Ptr); - // dispatch to fallback implementation + // return sass error if type is not supported union Sass_Value* fallback(AST_Node_Ptr x) - { return fallback_impl(x); } + { return sass_make_error("unknown type for C-API"); } + }; } diff --git a/src/ast_def_macros.hpp b/src/ast_def_macros.hpp index b3a7f8d16..eadb1b1ac 100644 --- a/src/ast_def_macros.hpp +++ b/src/ast_def_macros.hpp @@ -35,16 +35,6 @@ class LocalOption { LocalOption cnt_##name(name, name + 1); \ if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \ -#define ATTACH_OPERATIONS()\ -virtual void perform(Operation* op) { (*op)(this); }\ -virtual AST_Node_Ptr perform(Operation* op) { return (*op)(this); }\ -virtual Statement_Ptr perform(Operation* op) { return (*op)(this); }\ -virtual Expression_Ptr perform(Operation* op) { return (*op)(this); }\ -virtual Selector_Ptr perform(Operation* op) { return (*op)(this); }\ -virtual std::string perform(Operation* op) { return (*op)(this); }\ -virtual union Sass_Value* perform(Operation* op) { return (*op)(this); }\ -virtual Value_Ptr perform(Operation* op) { return (*op)(this); } - #define ADD_PROPERTY(type, name)\ protected:\ type name##_;\ @@ -77,4 +67,54 @@ public: \ void name(type name##__) { hash_ = 0; name##_ = name##__; } \ private: +#ifdef DEBUG_SHARED_PTR + +#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \ + virtual klass##_Ptr copy(std::string, size_t) const = 0; \ + virtual klass##_Ptr clone(std::string, size_t) const = 0; \ + +#define ATTACH_AST_OPERATIONS(klass) \ + virtual klass##_Ptr copy(std::string, size_t) const; \ + virtual klass##_Ptr clone(std::string, size_t) const; \ + +#else + +#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \ + virtual klass##_Ptr copy() const = 0; \ + virtual klass##_Ptr clone() const = 0; \ + +#define ATTACH_AST_OPERATIONS(klass) \ + virtual klass##_Ptr copy() const; \ + virtual klass##_Ptr clone() const; \ + +#endif + +#ifdef DEBUG_SHARED_PTR + + #define IMPLEMENT_AST_OPERATORS(klass) \ + klass##_Ptr klass::copy(std::string file, size_t line) const { \ + klass##_Ptr cpy = new klass(this); \ + cpy->trace(file, line); \ + return cpy; \ + } \ + klass##_Ptr klass::clone(std::string file, size_t line) const { \ + klass##_Ptr cpy = copy(file, line); \ + cpy->cloneChildren(); \ + return cpy; \ + } \ + +#else + + #define IMPLEMENT_AST_OPERATORS(klass) \ + klass##_Ptr klass::copy() const { \ + return new klass(this); \ + } \ + klass##_Ptr klass::clone() const { \ + klass##_Ptr cpy = copy(); \ + cpy->cloneChildren(); \ + return cpy; \ + } \ + +#endif + #endif diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index 5145a092b..a084f3069 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -30,6 +30,10 @@ namespace Sass { typedef Simple_Selector* Simple_Selector_Ptr; typedef Simple_Selector const* Simple_Selector_Ptr_Const; + class Parent_Reference; + typedef Parent_Reference* Parent_Reference_Ptr; + typedef Parent_Reference const* Parent_Reference_Ptr_Const; + class PreValue; typedef PreValue* PreValue_Ptr; typedef PreValue const* PreValue_Ptr_Const; @@ -149,9 +153,6 @@ namespace Sass { class Function_Call; typedef Function_Call* Function_Call_Ptr; typedef Function_Call const* Function_Call_Ptr_Const; - class Function_Call_Schema; - typedef Function_Call_Schema* Function_Call_Schema_Ptr; - typedef Function_Call_Schema const* Function_Call_Schema_Ptr_Const; class Custom_Warning; typedef Custom_Warning* Custom_Warning_Ptr; typedef Custom_Warning const* Custom_Warning_Ptr_Const; @@ -319,7 +320,6 @@ namespace Sass { IMPL_MEM_OBJ(Binary_Expression); IMPL_MEM_OBJ(Unary_Expression); IMPL_MEM_OBJ(Function_Call); - IMPL_MEM_OBJ(Function_Call_Schema); IMPL_MEM_OBJ(Custom_Warning); IMPL_MEM_OBJ(Custom_Error); IMPL_MEM_OBJ(Variable); @@ -340,6 +340,7 @@ namespace Sass { IMPL_MEM_OBJ(At_Root_Query); IMPL_MEM_OBJ(Null); IMPL_MEM_OBJ(Parent_Selector); + IMPL_MEM_OBJ(Parent_Reference); IMPL_MEM_OBJ(Parameter); IMPL_MEM_OBJ(Parameters); IMPL_MEM_OBJ(Argument); @@ -419,7 +420,12 @@ namespace Sass { typedef std::set CompoundSelectorSet; typedef std::unordered_set SimpleSelectorDict; - typedef std::vector* ImporterStack; + typedef std::vector BlockStack; + typedef std::vector CalleeStack; + typedef std::vector CallStack; + typedef std::vector MediaStack; + typedef std::vector SelectorStack; + typedef std::vector ImporterStack; // only to switch implementations for testing #define environment_map std::map diff --git a/src/backtrace.cpp b/src/backtrace.cpp index 8da963a72..b9ee1dff1 100644 --- a/src/backtrace.cpp +++ b/src/backtrace.cpp @@ -24,6 +24,8 @@ namespace Sass { ss << indent; ss << "on line "; ss << trace.pstate.line + 1; + ss << ":"; + ss << trace.pstate.column + 1; ss << " of " << rel_path; // ss << trace.caller; first = false; @@ -33,6 +35,8 @@ namespace Sass { ss << indent; ss << "from line "; ss << trace.pstate.line + 1; + ss << ":"; + ss << trace.pstate.column + 1; ss << " of " << rel_path; } diff --git a/src/bind.cpp b/src/bind.cpp index ec20ac838..1874667a8 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -1,6 +1,7 @@ #include "sass.hpp" #include "bind.hpp" #include "ast.hpp" +#include "backtrace.hpp" #include "context.hpp" #include "expand.hpp" #include "eval.hpp" @@ -10,7 +11,7 @@ namespace Sass { - void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval) + void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces) { std::string callee(type + " " + name); @@ -54,7 +55,7 @@ namespace Sass { std::stringstream msg; msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << " for `" << name << "'"; - return error(msg.str(), as->pstate(), eval->exp.traces); + return error(msg.str(), as->pstate(), traces); } Parameter_Obj p = ps->at(ip); @@ -107,8 +108,8 @@ namespace Sass { false, false)); } else { - eval->exp.traces.push_back(Backtrace(key->pstate())); - throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a); + traces.push_back(Backtrace(key->pstate())); + throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); } } @@ -222,15 +223,15 @@ namespace Sass { for (auto key : argmap->keys()) { String_Constant_Ptr val = Cast(key); if (val == NULL) { - eval->exp.traces.push_back(Backtrace(key->pstate())); - throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a); + traces.push_back(Backtrace(key->pstate())); + throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); } std::string param = "$" + unquote(val->value()); if (!param_map.count(param)) { std::stringstream msg; msg << callee << " has no parameter named " << param; - error(msg.str(), a->pstate(), eval->exp.traces); + error(msg.str(), a->pstate(), traces); } env->local_frame()[param] = argmap->at(key); } @@ -245,7 +246,7 @@ namespace Sass { std::stringstream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee; - error(msg.str(), a->pstate(), eval->exp.traces); + error(msg.str(), a->pstate(), traces); } // ordinal arg -- bind it to the next param env->local_frame()[p->name()] = a->value(); @@ -259,7 +260,7 @@ namespace Sass { } else { std::stringstream msg; msg << callee << " has no parameter named " << a->name(); - error(msg.str(), a->pstate(), eval->exp.traces); + error(msg.str(), a->pstate(), traces); } } if (param_map[a->name()]) { @@ -267,14 +268,14 @@ namespace Sass { std::stringstream msg; msg << "argument " << a->name() << " of " << callee << "cannot be used as named argument"; - error(msg.str(), a->pstate(), eval->exp.traces); + error(msg.str(), a->pstate(), traces); } } if (env->has_local(a->name())) { std::stringstream msg; msg << "parameter " << p->name() << "provided more than once in call to " << callee; - error(msg.str(), a->pstate(), eval->exp.traces); + error(msg.str(), a->pstate(), traces); } env->local_frame()[a->name()] = a->value(); } @@ -299,7 +300,7 @@ namespace Sass { } else { // param is unbound and has no default value -- error - throw Exception::MissingArgument(as->pstate(), eval->exp.traces, name, leftover->name(), type); + throw Exception::MissingArgument(as->pstate(), traces, name, leftover->name(), type); } } } diff --git a/src/bind.hpp b/src/bind.hpp index 93a503aa6..57bcd01f9 100644 --- a/src/bind.hpp +++ b/src/bind.hpp @@ -2,12 +2,14 @@ #define SASS_BIND_H #include +#include "backtrace.hpp" #include "environment.hpp" #include "ast_fwd_decl.hpp" namespace Sass { - void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Context*, Env*, Eval*); + void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Env*, Eval*, Backtraces& traces); + } #endif diff --git a/src/c2ast.cpp b/src/c2ast.cpp new file mode 100644 index 000000000..c766fa184 --- /dev/null +++ b/src/c2ast.cpp @@ -0,0 +1,64 @@ +#include "ast.hpp" +#include "units.hpp" +#include "position.hpp" +#include "backtrace.hpp" +#include "sass/values.h" +#include "ast_fwd_decl.hpp" +#include "error_handling.hpp" + +namespace Sass { + + Value_Ptr c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate) + { + using std::strlen; + using std::strcpy; + Value_Ptr e = NULL; + switch (sass_value_get_tag(v)) { + case SASS_BOOLEAN: { + e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v)); + } break; + case SASS_NUMBER: { + e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v)); + } break; + case SASS_COLOR: { + e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); + } break; + case SASS_STRING: { + if (sass_string_is_quoted(v)) + e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v)); + else { + e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v)); + } + } break; + case SASS_LIST: { + List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v)); + for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) { + l->append(c2ast(sass_list_get_value(v, i), traces, pstate)); + } + l->is_bracketed(sass_list_get_is_bracketed(v)); + e = l; + } break; + case SASS_MAP: { + Map_Ptr m = SASS_MEMORY_NEW(Map, pstate); + for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) { + *m << std::make_pair( + c2ast(sass_map_get_key(v, i), traces, pstate), + c2ast(sass_map_get_value(v, i), traces, pstate)); + } + e = m; + } break; + case SASS_NULL: { + e = SASS_MEMORY_NEW(Null, pstate); + } break; + case SASS_ERROR: { + error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces); + } break; + case SASS_WARNING: { + error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces); + } break; + default: break; + } + return e; + } + +} diff --git a/src/c2ast.hpp b/src/c2ast.hpp new file mode 100644 index 000000000..d6c0a97c3 --- /dev/null +++ b/src/c2ast.hpp @@ -0,0 +1,14 @@ +#ifndef SASS_C2AST_H +#define SASS_C2AST_H + +#include "position.hpp" +#include "backtrace.hpp" +#include "ast_fwd_decl.hpp" + +namespace Sass { + + Value_Ptr c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate); + +} + +#endif diff --git a/src/check_nesting.cpp b/src/check_nesting.cpp index 880bcca37..b43b03197 100644 --- a/src/check_nesting.cpp +++ b/src/check_nesting.cpp @@ -132,13 +132,6 @@ namespace Sass { return i; } - Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s) - { - Block_Ptr b1 = Cast(s); - Has_Block_Ptr b2 = Cast(s); - return b1 || b2 ? visit_children(s) : s; - } - bool CheckNesting::should_visit(Statement_Ptr node) { if (!this->parent) return true; diff --git a/src/check_nesting.hpp b/src/check_nesting.hpp index 62c38d9dc..687f4afdd 100644 --- a/src/check_nesting.hpp +++ b/src/check_nesting.hpp @@ -13,7 +13,6 @@ namespace Sass { Statement_Ptr parent; Definition_Ptr current_mixin_definition; - Statement_Ptr fallback_impl(Statement_Ptr); Statement_Ptr before(Statement_Ptr); Statement_Ptr visit_children(Statement_Ptr); @@ -27,11 +26,13 @@ namespace Sass { template Statement_Ptr fallback(U x) { - Statement_Ptr n = Cast(x); - if (this->should_visit(n)) { - return fallback_impl(n); + Statement_Ptr s = Cast(x); + if (s && this->should_visit(s)) { + Block_Ptr b1 = Cast(s); + Has_Block_Ptr b2 = Cast(s); + if (b1 || b2) return visit_children(s); } - return NULL; + return s; } private: diff --git a/src/context.cpp b/src/context.cpp index dae2cbd75..e2cffd1db 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -29,6 +29,14 @@ #include "sass2scss.h" #include "prelexer.hpp" #include "emitter.hpp" +#include "fn_utils.hpp" +#include "fn_miscs.hpp" +#include "fn_maps.hpp" +#include "fn_lists.hpp" +#include "fn_colors.hpp" +#include "fn_numbers.hpp" +#include "fn_strings.hpp" +#include "fn_selectors.hpp" namespace Sass { using namespace Constants; diff --git a/src/context.hpp b/src/context.hpp index d3caba13e..027ffca22 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -46,14 +46,14 @@ namespace Sass { // generic ast node garbage container // used to avoid possible circular refs - std::vector ast_gc; + CallStack ast_gc; // 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; + ImporterStack import_stack; std::vector callee_stack; std::vector traces; diff --git a/src/cssize.cpp b/src/cssize.cpp index 4c062a628..cb4ab2c99 100644 --- a/src/cssize.cpp +++ b/src/cssize.cpp @@ -11,7 +11,7 @@ namespace Sass { Cssize::Cssize(Context& ctx) : ctx(ctx), traces(ctx.traces), - block_stack(std::vector()), + block_stack(BlockStack()), p_stack(std::vector()) { } @@ -509,11 +509,6 @@ namespace Sass { return flatten(result); } - Statement_Ptr Cssize::fallback_impl(AST_Node_Ptr n) - { - return static_cast(n); - } - void Cssize::append_block(Block_Ptr b, Block_Ptr cur) { for (size_t i = 0, L = b->length(); i < L; ++i) { diff --git a/src/cssize.hpp b/src/cssize.hpp index 5a6c704b0..d6962fbf3 100644 --- a/src/cssize.hpp +++ b/src/cssize.hpp @@ -14,11 +14,9 @@ namespace Sass { Context& ctx; Backtraces& traces; - std::vector block_stack; + BlockStack block_stack; std::vector p_stack; - Statement_Ptr fallback_impl(AST_Node_Ptr n); - public: Cssize(Context&); ~Cssize() { } @@ -66,8 +64,10 @@ namespace Sass { List_Ptr merge_media_queries(Media_Block_Ptr, Media_Block_Ptr); Media_Query_Ptr merge_media_query(Media_Query_Ptr, Media_Query_Ptr); + // generic fallback template - Statement_Ptr fallback(U x) { return fallback_impl(x); } + Statement_Ptr fallback(U x) + { return Cast(x); } void append_block(Block_Ptr, Block_Ptr); }; diff --git a/src/debugger.hpp b/src/debugger.hpp index ee0d6eba7..b256c0da9 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -111,6 +111,15 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) // Expression_Ptr expression = Cast(node); // std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl; + } else if (Cast(node)) { + Parent_Reference_Ptr selector = Cast(node); + std::cerr << ind << "Parent_Reference " << selector; +// if (selector->not_selector()) cerr << " [in_declaration]"; + std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " <" << selector->hash() << ">"; + std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; +// debug_ast(selector->selector(), ind + "->", env); + } else if (Cast(node)) { Parent_Selector_Ptr selector = Cast(node); std::cerr << ind << "Parent_Selector " << selector; @@ -497,14 +506,6 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) std::cerr << " [" << expression->name() << "]" << std::endl; std::string name(expression->name()); if (env && env->has(name)) debug_ast(Cast((*env)[name]), ind + " -> ", env); - } else if (Cast(node)) { - Function_Call_Schema_Ptr expression = Cast(node); - std::cerr << ind << "Function_Call_Schema " << expression; - std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; - std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << "" << std::endl; - debug_ast(expression->name(), ind + "name: ", env); - debug_ast(expression->arguments(), ind + " args: ", env); } else if (Cast(node)) { Function_Call_Ptr expression = Cast(node); std::cerr << ind << "Function_Call " << expression; @@ -620,6 +621,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) Color_Ptr expression = Cast(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " [name: " << expression->disp() << "] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl; diff --git a/src/environment.cpp b/src/environment.cpp index e382e7e05..dccda1de8 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -206,6 +206,20 @@ namespace Sass { } }; + // use array access for getter and setter functions + template + T& Environment::get(const std::string& key) + { + auto cur = this; + while (cur) { + if (cur->has_local(key)) { + return cur->get_local(key); + } + cur = cur->parent_; + } + return get_local(key); + } + // use array access for getter and setter functions template T& Environment::operator[](const std::string& key) diff --git a/src/environment.hpp b/src/environment.hpp index a6939be23..08452ea32 100644 --- a/src/environment.hpp +++ b/src/environment.hpp @@ -7,6 +7,7 @@ namespace Sass { + // this defeats the whole purpose of environment being templatable!! typedef environment_map::iterator EnvIter; class EnvResult { @@ -92,6 +93,10 @@ namespace Sass { // include all scopes available bool has(const std::string& key) const; + // look on the full stack for key + // include all scopes available + T& get(const std::string& key); + // look on the full stack for key // include all scopes available EnvResult find(const std::string& key); @@ -107,6 +112,7 @@ namespace Sass { // define typedef for our use case typedef Environment Env; + typedef std::vector EnvStack; } diff --git a/src/error_handling.hpp b/src/error_handling.hpp index f863792ea..099f26dcc 100644 --- a/src/error_handling.hpp +++ b/src/error_handling.hpp @@ -4,6 +4,7 @@ #include #include #include +#include "units.hpp" #include "position.hpp" #include "backtrace.hpp" #include "ast_fwd_decl.hpp" diff --git a/src/eval.cpp b/src/eval.cpp index 2ddfa93ea..9c5cf4c63 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -17,7 +17,8 @@ #include "position.hpp" #include "sass/values.h" #include "to_value.hpp" -#include "to_c.hpp" +#include "ast2c.hpp" +#include "c2ast.hpp" #include "context.hpp" #include "backtrace.hpp" #include "lexer.hpp" @@ -47,11 +48,48 @@ namespace Sass { return exp.environment(); } + const std::string Eval::cwd() + { + return ctx.cwd(); + } + + struct Sass_Inspect_Options& Eval::options() + { + return ctx.c_options; + } + + struct Sass_Compiler* Eval::compiler() + { + return ctx.c_compiler; + } + + EnvStack& Eval::env_stack() + { + return exp.env_stack; + } + Selector_List_Obj Eval::selector() { return exp.selector(); } + std::vector& Eval::callee_stack() + { + return ctx.callee_stack; + } + + + SelectorStack& Eval::selector_stack() + { + return exp.selector_stack; + } + + bool& Eval::old_at_root_without_rule() + { + return exp.old_at_root_without_rule; + } + + Expression_Ptr Eval::operator()(Block_Ptr b) { Expression_Ptr val = 0; @@ -64,7 +102,7 @@ namespace Sass { Expression_Ptr Eval::operator()(Assignment_Ptr a) { - Env* env = exp.environment(); + Env* env = environment(); std::string var(a->variable()); if (a->is_global()) { if (a->is_default()) { @@ -126,8 +164,8 @@ namespace Sass { Expression_Ptr Eval::operator()(If_Ptr i) { Expression_Obj rv = 0; - Env env(exp.environment()); - exp.env_stack.push_back(&env); + Env env(environment()); + env_stack().push_back(&env); Expression_Obj cond = i->predicate()->perform(this); if (!cond->is_false()) { rv = i->block()->perform(this); @@ -136,7 +174,7 @@ namespace Sass { Block_Obj alt = i->alternative(); if (alt) rv = alt->perform(this); } - exp.env_stack.pop_back(); + env_stack().pop_back(); return rv.detach(); } @@ -168,7 +206,7 @@ namespace Sass { double end = sass_end->value(); // only create iterator once in this environment Env env(environment(), true); - exp.env_stack.push_back(&env); + env_stack().push_back(&env); Block_Obj body = f->block(); Expression_Ptr val = 0; if (start < end) { @@ -192,7 +230,7 @@ namespace Sass { if (val) break; } } - exp.env_stack.pop_back(); + env_stack().pop_back(); return val; } @@ -203,7 +241,7 @@ namespace Sass { std::vector variables(e->variables()); Expression_Obj expr = e->list()->perform(this); Env env(environment(), true); - exp.env_stack.push_back(&env); + env_stack().push_back(&env); List_Obj list = 0; Map_Ptr map = 0; if (expr->concrete_type() == Expression::MAP) { @@ -279,7 +317,7 @@ namespace Sass { if (val) break; } } - exp.env_stack.pop_back(); + env_stack().pop_back(); return val.detach(); } @@ -288,17 +326,17 @@ namespace Sass { Expression_Obj pred = w->predicate(); Block_Obj body = w->block(); Env env(environment(), true); - exp.env_stack.push_back(&env); + env_stack().push_back(&env); Expression_Obj cond = pred->perform(this); while (!cond->is_false()) { Expression_Obj val = body->perform(this); if (val) { - exp.env_stack.pop_back(); + env_stack().pop_back(); return val.detach(); } cond = pred->perform(this); } - exp.env_stack.pop_back(); + env_stack().pop_back(); return 0; } @@ -309,16 +347,16 @@ namespace Sass { Expression_Ptr Eval::operator()(Warning_Ptr w) { - Sass_Output_Style outstyle = ctx.c_options.output_style; - ctx.c_options.output_style = NESTED; + Sass_Output_Style outstyle = options().output_style; + options().output_style = NESTED; Expression_Obj message = w->message()->perform(this); - Env* env = exp.environment(); + Env* env = environment(); // try to use generic function if (env->has("@warn[f]")) { // add call stack entry - ctx.callee_stack.push_back({ + callee_stack().push_back({ "@warn", w->pstate().path, w->pstate().line + 1, @@ -333,12 +371,12 @@ namespace Sass { Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); - To_C to_c; + AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false); - sass_list_set_value(c_args, 0, message->perform(&to_c)); - union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); - ctx.c_options.output_style = outstyle; - ctx.callee_stack.pop_back(); + sass_list_set_value(c_args, 0, message->perform(&ast2c)); + union Sass_Value* c_val = c_func(c_args, c_function, compiler()); + options().output_style = outstyle; + callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; @@ -350,23 +388,23 @@ namespace Sass { traces.push_back(Backtrace(w->pstate())); std::cerr << traces_to_string(traces, " "); std::cerr << std::endl; - ctx.c_options.output_style = outstyle; + options().output_style = outstyle; traces.pop_back(); return 0; } Expression_Ptr Eval::operator()(Error_Ptr e) { - Sass_Output_Style outstyle = ctx.c_options.output_style; - ctx.c_options.output_style = NESTED; + Sass_Output_Style outstyle = options().output_style; + options().output_style = NESTED; Expression_Obj message = e->message()->perform(this); - Env* env = exp.environment(); + Env* env = environment(); // try to use generic function if (env->has("@error[f]")) { // add call stack entry - ctx.callee_stack.push_back({ + callee_stack().push_back({ "@error", e->pstate().path, e->pstate().line + 1, @@ -381,12 +419,12 @@ namespace Sass { Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); - To_C to_c; + AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false); - sass_list_set_value(c_args, 0, message->perform(&to_c)); - union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); - ctx.c_options.output_style = outstyle; - ctx.callee_stack.pop_back(); + sass_list_set_value(c_args, 0, message->perform(&ast2c)); + union Sass_Value* c_val = c_func(c_args, c_function, compiler()); + options().output_style = outstyle; + callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; @@ -394,23 +432,23 @@ namespace Sass { } std::string result(unquote(message->to_sass())); - ctx.c_options.output_style = outstyle; + options().output_style = outstyle; error(result, e->pstate(), traces); return 0; } Expression_Ptr Eval::operator()(Debug_Ptr d) { - Sass_Output_Style outstyle = ctx.c_options.output_style; - ctx.c_options.output_style = NESTED; + Sass_Output_Style outstyle = options().output_style; + options().output_style = NESTED; Expression_Obj message = d->value()->perform(this); - Env* env = exp.environment(); + Env* env = environment(); // try to use generic function if (env->has("@debug[f]")) { // add call stack entry - ctx.callee_stack.push_back({ + callee_stack().push_back({ "@debug", d->pstate().path, d->pstate().line + 1, @@ -425,24 +463,23 @@ namespace Sass { Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); - To_C to_c; + AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false); - sass_list_set_value(c_args, 0, message->perform(&to_c)); - union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); - ctx.c_options.output_style = outstyle; - ctx.callee_stack.pop_back(); + sass_list_set_value(c_args, 0, message->perform(&ast2c)); + union Sass_Value* c_val = c_func(c_args, c_function, compiler()); + options().output_style = outstyle; + callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; } - std::string cwd(ctx.cwd()); std::string result(unquote(message->to_sass())); - std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd)); - std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd)); + std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd(), cwd())); + std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd(), cwd())); std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path)); - ctx.c_options.output_style = outstyle; + options().output_style = outstyle; std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result; std::cerr << std::endl; @@ -583,7 +620,7 @@ namespace Sass { case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false; case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: - return Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, b_in->pstate()); + return Operators::op_numbers(op_type, *l_n, *r_n, options(), b_in->pstate()); default: break; } } @@ -599,12 +636,8 @@ namespace Sass { switch (op_type) { case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false; case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true; - case Sass_OP::LT: return *l_n < *r_c ? bool_true : bool_false; - case Sass_OP::GTE: return *l_n < *r_c ? bool_false : bool_true; - case Sass_OP::LTE: return *l_n < *r_c || *l_n == *r_c ? bool_true : bool_false; - case Sass_OP::GT: return *l_n < *r_c || *l_n == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: - return Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate()); + return Operators::op_number_color(op_type, *l_n, *r_c, options(), b_in->pstate()); default: break; } } @@ -627,7 +660,7 @@ namespace Sass { case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false; case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: - return Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, b_in->pstate()); + return Operators::op_colors(op_type, *l_c, *r_c, options(), b_in->pstate()); default: break; } } @@ -643,12 +676,8 @@ namespace Sass { switch (op_type) { case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false; case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true; - case Sass_OP::LT: return *l_c < *r_n ? bool_true : bool_false; - case Sass_OP::GTE: return *l_c < *r_n ? bool_false : bool_true; - case Sass_OP::LTE: return *l_c < *r_n || *l_c == *r_n ? bool_true : bool_false; - case Sass_OP::GT: return *l_c < *r_n || *l_c == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: - return Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate()); + return Operators::op_color_number(op_type, *l_c, *r_n, options(), b_in->pstate()); default: break; } } @@ -670,9 +699,9 @@ namespace Sass { b->op(), s_l->last(), b->right()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // unverified for (size_t i = 0; i < s_l->length() - 1; ++i) { - ret_schema->append(s_l->at(i)->perform(this)); + ret_schema->append(Cast(s_l->at(i)->perform(this))); } - ret_schema->append(bin_ex->perform(this)); + ret_schema->append(Cast(bin_ex->perform(this))); return ret_schema->perform(this); } } @@ -683,9 +712,9 @@ namespace Sass { Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(), b->op(), b->left(), s_r->first()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // verified - ret_schema->append(bin_ex->perform(this)); + ret_schema->append(Cast(bin_ex->perform(this))); for (size_t i = 1; i < s_r->length(); ++i) { - ret_schema->append(s_r->at(i)->perform(this)); + ret_schema->append(Cast(s_r->at(i)->perform(this))); } return ret_schema->perform(this); } @@ -760,11 +789,11 @@ namespace Sass { if (force_delay) { std::string str(""); - str += v_l->to_string(ctx.c_options); + str += v_l->to_string(options()); if (b->op().ws_before) str += " "; str += b->separator(); if (b->op().ws_after) str += " "; - str += v_r->to_string(ctx.c_options); + str += v_r->to_string(options()); String_Constant_Ptr val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str); val->is_interpolant(b->left()->has_interpolant()); return val; @@ -785,7 +814,6 @@ namespace Sass { } catch (Exception::OperationError& err) { - // throw Exception::Base(b->pstate(), err.what()); traces.push_back(Backtrace(b->pstate())); throw Exception::SassValueError(traces, b->pstate(), err); } @@ -802,22 +830,22 @@ namespace Sass { Number_Ptr l_n = Cast(lhs); Number_Ptr r_n = Cast(rhs); l_n->reduce(); r_n->reduce(); - rv = Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, pstate); + rv = Operators::op_numbers(op_type, *l_n, *r_n, options(), pstate); } else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { Number_Ptr l_n = Cast(lhs); Color_Ptr r_c = Cast(rhs); - rv = Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, pstate); + rv = Operators::op_number_color(op_type, *l_n, *r_c, options(), pstate); } else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { Color_Ptr l_c = Cast(lhs); Number_Ptr r_n = Cast(rhs); - rv = Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, pstate); + rv = Operators::op_color_number(op_type, *l_c, *r_n, options(), pstate); } else if (l_type == Expression::COLOR && r_type == Expression::COLOR) { Color_Ptr l_c = Cast(lhs); Color_Ptr r_c = Cast(rhs); - rv = Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, pstate); + rv = Operators::op_colors(op_type, *l_c, *r_c, options(), pstate); } else { To_Value to_value(ctx); @@ -838,7 +866,7 @@ namespace Sass { traces.push_back(Backtrace(v_r->pstate())); throw Exception::InvalidValue(traces, *v_r); } - Value_Ptr ex = Operators::op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress + Value_Ptr ex = Operators::op_strings(b->op(), *v_l, *v_r, options(), pstate, !interpolant); // pass true to compress if (String_Constant_Ptr str = Cast(ex)) { if (str->concrete_type() == Expression::STRING) @@ -890,7 +918,7 @@ namespace Sass { return cpy.detach(); // return the copy } else if (u->optype() == Unary_Expression::SLASH) { - std::string str = '/' + nr->to_string(ctx.c_options); + std::string str = '/' + nr->to_string(options()); return SASS_MEMORY_NEW(String_Constant, u->pstate(), str); } // nothing for positive @@ -930,8 +958,18 @@ namespace Sass { stm << "Stack depth exceeded max of " << Constants::MaxCallStack; error(stm.str(), c->pstate(), traces); } + + if (Cast(c->sname())) { + Expression_Ptr evaluated_name = c->sname()->perform(this); + Expression_Ptr evaluated_args = c->arguments()->perform(this); + std::string str(evaluated_name->to_string()); + str += evaluated_args->to_string(); + return SASS_MEMORY_NEW(String_Constant, c->pstate(), str); + } + std::string name(Util::normalize_underscores(c->name())); std::string full_name(name + "[f]"); + // we make a clone here, need to implement that further Arguments_Obj args = c->arguments(); @@ -953,7 +991,7 @@ namespace Sass { } String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), - lit->to_string(ctx.c_options)); + lit->to_string(options())); str->is_interpolant(c->is_interpolant()); return str; } else { @@ -999,13 +1037,13 @@ namespace Sass { Parameters_Obj params = def->parameters(); Env fn_env(def->environment()); - exp.env_stack.push_back(&fn_env); + env_stack().push_back(&fn_env); if (func || body) { - bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this); + bind(std::string("Function"), c->name(), params, args, &fn_env, this, traces); std::string msg(", in function `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg)); - ctx.callee_stack.push_back({ + callee_stack().push_back({ c->name().c_str(), c->pstate().path, c->pstate().line + 1, @@ -1024,7 +1062,7 @@ namespace Sass { if (!result) { error(std::string("Function ") + c->name() + " finished without @return", c->pstate(), traces); } - ctx.callee_stack.pop_back(); + callee_stack().pop_back(); traces.pop_back(); } @@ -1042,10 +1080,10 @@ namespace Sass { // populates env with default values for params std::string ff(c->name()); - bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this); + bind(std::string("Function"), c->name(), params, args, &fn_env, this, traces); std::string msg(", in function `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg)); - ctx.callee_stack.push_back({ + callee_stack().push_back({ c->name().c_str(), c->pstate().path, c->pstate().line + 1, @@ -1054,24 +1092,24 @@ namespace Sass { { env } }); - To_C to_c; + AST2C ast2c; union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA, false); for(size_t i = 0; i < params->length(); i++) { Parameter_Obj param = params->at(i); std::string key = param->name(); AST_Node_Obj node = fn_env.get_local(key); Expression_Obj arg = Cast(node); - sass_list_set_value(c_args, i, arg->perform(&to_c)); + sass_list_set_value(c_args, i, arg->perform(&ast2c)); } - union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); + union Sass_Value* c_val = c_func(c_args, c_function, compiler()); if (sass_value_get_tag(c_val) == SASS_ERROR) { error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), traces); } else if (sass_value_get_tag(c_val) == SASS_WARNING) { error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), traces); } - result = cval_to_astnode(c_val, traces, c->pstate()); + result = c2ast(c_val, traces, c->pstate()); - ctx.callee_stack.pop_back(); + callee_stack().pop_back(); traces.pop_back(); sass_delete_value(c_args); if (c_val != c_args) @@ -1085,20 +1123,10 @@ namespace Sass { result = result->perform(this); result->is_interpolant(c->is_interpolant()); - exp.env_stack.pop_back(); + env_stack().pop_back(); return result.detach(); } - Expression_Ptr Eval::operator()(Function_Call_Schema_Ptr s) - { - Expression_Ptr evaluated_name = s->name()->perform(this); - Expression_Ptr evaluated_args = s->arguments()->perform(this); - String_Schema_Obj ss = SASS_MEMORY_NEW(String_Schema, s->pstate(), 2); - ss->append(evaluated_name); - ss->append(evaluated_args); - return ss->perform(this); - } - Expression_Ptr Eval::operator()(Variable_Ptr v) { Expression_Obj value = 0; @@ -1172,6 +1200,11 @@ namespace Sass { // XXX: this is never hit via spec tests ex = ex->perform(this); } + // parent selector needs another go + if (Cast(ex)) { + // XXX: this is never hit via spec tests + ex = ex->perform(this); + } if (List_Ptr l = Cast(ex)) { List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator()); @@ -1187,12 +1220,12 @@ namespace Sass { // here. Normally single list items are already unwrapped. if (l->size() > 1) { // string_to_output would fail "#{'_\a' '_\a'}"; - std::string str(ll->to_string(ctx.c_options)); + std::string str(ll->to_string(options())); str = read_hex_escapes(str); // read escapes newline_to_space(str); // replace directly res += str; // append to result string } else { - res += (ll->to_string(ctx.c_options)); + res += (ll->to_string(options())); } ll->is_interpolant(l->is_interpolant()); } @@ -1207,9 +1240,9 @@ namespace Sass { else { // ex = ex->perform(this); if (into_quotes && ex->is_interpolant()) { - res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : ""); + res += evacuate_escapes(ex ? ex->to_string(options()) : ""); } else { - std::string str(ex ? ex->to_string(ctx.c_options) : ""); + std::string str(ex ? ex->to_string(options()) : ""); if (into_quotes) str = read_hex_escapes(str); res += str; // append to result string } @@ -1265,13 +1298,6 @@ namespace Sass { Expression_Ptr Eval::operator()(String_Constant_Ptr s) { - if (!s->is_delayed() && name_to_color(s->value())) { - Color_Ptr c = SASS_MEMORY_COPY(name_to_color(s->value())); // copy - c->pstate(s->pstate()); - c->disp(s->value()); - c->is_delayed(true); - return c; - } return s; } @@ -1467,69 +1493,9 @@ namespace Sass { return 0; } - inline Expression_Ptr Eval::fallback_impl(AST_Node_Ptr n) - { - return static_cast(n); - } - - // All the binary helpers. - - Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate) - { - using std::strlen; - using std::strcpy; - Expression_Ptr e = NULL; - switch (sass_value_get_tag(v)) { - case SASS_BOOLEAN: { - e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v)); - } break; - case SASS_NUMBER: { - e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v)); - } break; - case SASS_COLOR: { - e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); - } break; - case SASS_STRING: { - if (sass_string_is_quoted(v)) - e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v)); - else { - e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v)); - } - } break; - case SASS_LIST: { - List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v)); - for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) { - l->append(cval_to_astnode(sass_list_get_value(v, i), traces, pstate)); - } - l->is_bracketed(sass_list_get_is_bracketed(v)); - e = l; - } break; - case SASS_MAP: { - Map_Ptr m = SASS_MEMORY_NEW(Map, pstate); - for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) { - *m << std::make_pair( - cval_to_astnode(sass_map_get_key(v, i), traces, pstate), - cval_to_astnode(sass_map_get_value(v, i), traces, pstate)); - } - e = m; - } break; - case SASS_NULL: { - e = SASS_MEMORY_NEW(Null, pstate); - } break; - case SASS_ERROR: { - error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces); - } break; - case SASS_WARNING: { - error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces); - } break; - default: break; - } - return e; - } - Selector_List_Ptr Eval::operator()(Selector_List_Ptr s) { - std::vector rv; + SelectorStack rv; Selector_List_Obj sl = SASS_MEMORY_NEW(Selector_List, s->pstate()); sl->is_optional(s->is_optional()); sl->media_block(s->media_block()); @@ -1593,10 +1559,8 @@ namespace Sass { { LOCAL_FLAG(is_in_selector_schema, true); // the parser will look for a brace to end the selector - ctx.c_options.in_selector = true; // do not compress colors Expression_Obj sel = s->contents()->perform(this); - std::string result_str(sel->to_string(ctx.c_options)); - ctx.c_options.in_selector = false; // flag temporary only + std::string result_str(sel->to_string(options())); result_str = unquote(Util::rtrim(result_str)); char* temp_cstr = sass_copy_c_string(result_str.c_str()); ctx.strings.push_back(temp_cstr); // attach to context @@ -1636,6 +1600,18 @@ namespace Sass { } } + Expression_Ptr Eval::operator()(Parent_Reference_Ptr p) + { + if (Selector_List_Obj pr = selector()) { + exp.selector_stack.pop_back(); + Selector_List_Obj rv = operator()(pr); + exp.selector_stack.push_back(rv); + return rv.detach(); + } else { + return SASS_MEMORY_NEW(Null, p->pstate()); + } + } + Simple_Selector_Ptr Eval::operator()(Simple_Selector_Ptr s) { return s; diff --git a/src/eval.hpp b/src/eval.hpp index aeaada87e..741472e81 100644 --- a/src/eval.hpp +++ b/src/eval.hpp @@ -14,9 +14,6 @@ namespace Sass { class Eval : public Operation_CRTP { - private: - Expression_Ptr fallback_impl(AST_Node_Ptr n); - public: Expand& exp; Context& ctx; @@ -32,7 +29,15 @@ namespace Sass { Boolean_Obj bool_false; Env* environment(); + EnvStack& env_stack(); + const std::string cwd(); Selector_List_Obj selector(); + CalleeStack& callee_stack(); + SelectorStack& selector_stack(); + bool& old_at_root_without_rule(); + struct Sass_Inspect_Options& options(); + struct Sass_Inspect_Options options2(); + struct Sass_Compiler* compiler(); // for evaluating function bodies Expression_Ptr operator()(Block_Ptr); @@ -51,7 +56,6 @@ namespace Sass { Expression_Ptr operator()(Binary_Expression_Ptr); Expression_Ptr operator()(Unary_Expression_Ptr); Expression_Ptr operator()(Function_Call_Ptr); - Expression_Ptr operator()(Function_Call_Schema_Ptr); Expression_Ptr operator()(Variable_Ptr); Expression_Ptr operator()(Number_Ptr); Expression_Ptr operator()(Color_Ptr); @@ -78,26 +82,30 @@ namespace Sass { Compound_Selector_Ptr operator()(Compound_Selector_Ptr); Simple_Selector_Ptr operator()(Simple_Selector_Ptr s); Wrapped_Selector_Ptr operator()(Wrapped_Selector_Ptr s); + // they don't have any specific implementation (yet) - // Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; }; - // Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; }; - // Class_Selector_Ptr operator()(Class_Selector_Ptr s) { return s; }; - // Id_Selector_Ptr operator()(Id_Selector_Ptr s) { return s; }; - // Placeholder_Selector_Ptr operator()(Placeholder_Selector_Ptr s) { return s; }; + Id_Selector_Ptr operator()(Id_Selector_Ptr s) { return s; }; + Class_Selector_Ptr operator()(Class_Selector_Ptr s) { return s; }; + Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; }; + Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; }; + Attribute_Selector_Ptr operator()(Attribute_Selector_Ptr s) { return s; }; + Placeholder_Selector_Ptr operator()(Placeholder_Selector_Ptr s) { return s; }; + // actual evaluated selectors Selector_List_Ptr operator()(Selector_Schema_Ptr); Expression_Ptr operator()(Parent_Selector_Ptr); + Expression_Ptr operator()(Parent_Reference_Ptr); + // generic fallback template - Expression_Ptr fallback(U x) { return fallback_impl(x); } + Expression_Ptr fallback(U x) + { return Cast(x); } private: void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false); }; - Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate = ParserState("[AST]")); - } #endif diff --git a/src/expand.cpp b/src/expand.cpp index ccd2822df..9731d80fa 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -16,7 +16,7 @@ namespace Sass { // simple endless recursion protection const size_t maxRecursion = 500; - Expand::Expand(Context& ctx, Env* env, std::vector* stack) + Expand::Expand(Context& ctx, Env* env, SelectorStack* stack) : ctx(ctx), traces(ctx.traces), eval(Eval(*this)), @@ -24,11 +24,11 @@ namespace Sass { in_keyframes(false), at_root_without_rule(false), old_at_root_without_rule(false), - env_stack(std::vector()), - block_stack(std::vector()), - call_stack(std::vector()), - selector_stack(std::vector()), - media_block_stack(std::vector()) + env_stack(EnvStack()), + block_stack(BlockStack()), + call_stack(CallStack()), + selector_stack(SelectorStack()), + media_stack(MediaStack()) { env_stack.push_back(0); env_stack.push_back(env); @@ -36,7 +36,7 @@ namespace Sass { call_stack.push_back(0); if (stack == NULL) { selector_stack.push_back(0); } else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); } - media_block_stack.push_back(0); + media_stack.push_back(0); } Env* Expand::environment() @@ -139,7 +139,7 @@ namespace Sass { if (block_stack.back()->is_root()) { env_stack.push_back(&env); } - sel->set_media_block(media_block_stack.back()); + sel->set_media_block(media_stack.back()); Block_Obj blk = 0; if (r->block()) blk = operator()(r->block()); Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset, @@ -183,13 +183,13 @@ namespace Sass { Parser p(Parser::from_c_str(str, ctx, traces, mq->pstate())); mq = p.parse_media_queries(); // re-assign now cpy->media_queries(mq); - media_block_stack.push_back(cpy); + media_stack.push_back(cpy); Block_Obj blk = operator()(m->block()); Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block, m->pstate(), mq, blk); - media_block_stack.pop_back(); + media_stack.pop_back(); mm->tabs(m->tabs()); return mm; } @@ -658,7 +658,7 @@ namespace Sass { } for (Complex_Selector_Obj cs : sl->elements()) { if (!cs.isNull() && !cs->head().isNull()) { - cs->head()->media_block(media_block_stack.back()); + cs->head()->media_block(media_stack.back()); } } selector_stack.push_back(0); @@ -740,7 +740,7 @@ namespace Sass { new_env.local_frame()["@content[m]"] = thunk; } - bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval); + bind(std::string("Mixin"), c->name(), params, args, &new_env, &eval, traces); Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate()); Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block); @@ -792,15 +792,6 @@ namespace Sass { return trace.detach(); } - // produce an error if something is not implemented - inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n) - { - std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name(); - String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err); - error("unknown internal error; please contact the LibSass maintainers", n->pstate(), traces); - return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg); - } - // process and add to last block on stack inline void Expand::append_block(Block_Ptr b) { diff --git a/src/expand.hpp b/src/expand.hpp index 3464c98f6..1c494194e 100644 --- a/src/expand.hpp +++ b/src/expand.hpp @@ -30,21 +30,19 @@ namespace Sass { bool old_at_root_without_rule; // it's easier to work with vectors - std::vector env_stack; - std::vector block_stack; - std::vector call_stack; - std::vector selector_stack; - std::vector media_block_stack; + EnvStack env_stack; + BlockStack block_stack; + CallStack call_stack; + SelectorStack selector_stack; + MediaStack media_stack; Boolean_Obj bool_true; - Statement_Ptr fallback_impl(AST_Node_Ptr n); - private: void expand_selector_list(Selector_Obj, Selector_List_Obj extender); public: - Expand(Context&, Env*, std::vector* stack = NULL); + Expand(Context&, Env*, SelectorStack* stack = NULL); ~Expand() { } Block_Ptr operator()(Block_Ptr); @@ -71,10 +69,8 @@ namespace Sass { Statement_Ptr operator()(Mixin_Call_Ptr); Statement_Ptr operator()(Content_Ptr); - template - Statement_Ptr fallback(U x) { return fallback_impl(x); } - void append_block(Block_Ptr); + }; } diff --git a/src/extend.hpp b/src/extend.hpp index 03042f3e2..e222359e3 100644 --- a/src/extend.hpp +++ b/src/extend.hpp @@ -20,8 +20,6 @@ namespace Sass { Subset_Map& subset_map; Eval* eval; - void fallback_impl(AST_Node_Ptr n) { } - private: std::unordered_map< @@ -77,8 +75,10 @@ namespace Sass { void operator()(Media_Block_Ptr); void operator()(Directive_Ptr); + // ignore missed types template - void fallback(U x) { return fallback_impl(x); } + void fallback(U x) {} + }; } diff --git a/src/file.cpp b/src/file.cpp index 32d4a7c63..44c2e3221 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -21,6 +21,7 @@ #include "prelexer.hpp" #include "utf8_string.hpp" #include "sass_functions.hpp" +#include "error_handling.hpp" #include "sass2scss.h" #ifdef _WIN32 diff --git a/src/fn_colors.cpp b/src/fn_colors.cpp new file mode 100644 index 000000000..642e9d4f4 --- /dev/null +++ b/src/fn_colors.cpp @@ -0,0 +1,742 @@ +#include +#include +#include "ast.hpp" +#include "fn_utils.hpp" +#include "fn_colors.hpp" + +namespace Sass { + + namespace Functions { + + bool special_number(String_Constant_Ptr s) { + if (s) { + std::string calc("calc("); + std::string var("var("); + std::string ss(s->value()); + return std::equal(calc.begin(), calc.end(), ss.begin()) || + std::equal(var.begin(), var.end(), ss.begin()); + } + return false; + } + + Signature rgb_sig = "rgb($red, $green, $blue)"; + BUILT_IN(rgb) + { + if ( + special_number(Cast(env["$red"])) || + special_number(Cast(env["$green"])) || + special_number(Cast(env["$blue"])) + ) { + return SASS_MEMORY_NEW(String_Constant, pstate, "rgb(" + + env["$red"]->to_string() + + ", " + + env["$green"]->to_string() + + ", " + + env["$blue"]->to_string() + + ")" + ); + } + + return SASS_MEMORY_NEW(Color, + pstate, + COLOR_NUM("$red"), + COLOR_NUM("$green"), + COLOR_NUM("$blue")); + } + + Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; + BUILT_IN(rgba_4) + { + if ( + special_number(Cast(env["$red"])) || + special_number(Cast(env["$green"])) || + special_number(Cast(env["$blue"])) || + special_number(Cast(env["$alpha"])) + ) { + return SASS_MEMORY_NEW(String_Constant, pstate, "rgba(" + + env["$red"]->to_string() + + ", " + + env["$green"]->to_string() + + ", " + + env["$blue"]->to_string() + + ", " + + env["$alpha"]->to_string() + + ")" + ); + } + + return SASS_MEMORY_NEW(Color, + pstate, + COLOR_NUM("$red"), + COLOR_NUM("$green"), + COLOR_NUM("$blue"), + ALPHA_NUM("$alpha")); + } + + Signature rgba_2_sig = "rgba($color, $alpha)"; + BUILT_IN(rgba_2) + { + if ( + special_number(Cast(env["$color"])) + ) { + return SASS_MEMORY_NEW(String_Constant, pstate, "rgba(" + + env["$color"]->to_string() + + ", " + + env["$alpha"]->to_string() + + ")" + ); + } + + Color_Ptr c_arg = ARG("$color", Color); + + if ( + special_number(Cast(env["$alpha"])) + ) { + std::stringstream strm; + strm << "rgba(" + << (int)c_arg->r() << ", " + << (int)c_arg->g() << ", " + << (int)c_arg->b() << ", " + << env["$alpha"]->to_string() + << ")"; + return SASS_MEMORY_NEW(String_Constant, pstate, strm.str()); + } + + Color_Ptr new_c = SASS_MEMORY_COPY(c_arg); + new_c->a(ALPHA_NUM("$alpha")); + new_c->disp(""); + return new_c; + } + + //////////////// + // RGB FUNCTIONS + //////////////// + + Signature red_sig = "red($color)"; + BUILT_IN(red) + { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->r()); } + + Signature green_sig = "green($color)"; + BUILT_IN(green) + { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->g()); } + + Signature blue_sig = "blue($color)"; + BUILT_IN(blue) + { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); } + + Color* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) { + double p = weight/100; + double w = 2*p - 1; + double a = color1->a() - color2->a(); + + double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0; + double w2 = 1 - w1; + + return SASS_MEMORY_NEW(Color, + pstate, + Sass::round(w1*color1->r() + w2*color2->r(), ctx.c_options.precision), + Sass::round(w1*color1->g() + w2*color2->g(), ctx.c_options.precision), + Sass::round(w1*color1->b() + w2*color2->b(), ctx.c_options.precision), + color1->a()*p + color2->a()*(1-p)); + } + + Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)"; + BUILT_IN(mix) + { + Color_Obj color1 = ARG("$color-1", Color); + Color_Obj color2 = ARG("$color-2", Color); + double weight = DARG_U_PRCT("$weight"); + return colormix(ctx, pstate, color1, color2, weight); + + } + + //////////////// + // HSL FUNCTIONS + //////////////// + + // RGB to HSL helper function + struct HSL { double h; double s; double l; }; + HSL rgb_to_hsl(double r, double g, double b) + { + + // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV + r /= 255.0; g /= 255.0; b /= 255.0; + + double max = std::max(r, std::max(g, b)); + double min = std::min(r, std::min(g, b)); + double delta = max - min; + + double h = 0; + double s; + double l = (max + min) / 2.0; + + if (max == min) { + h = s = 0; // achromatic + } + else { + if (l < 0.5) s = delta / (max + min); + else s = delta / (2.0 - max - min); + + 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; + hsl_struct.h = h / 6 * 360; + hsl_struct.s = s * 100; + hsl_struct.l = l * 100; + + return hsl_struct; + } + + // hue to RGB helper function + double h_to_rgb(double m1, double m2, double h) { + 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; + return m1; + } + + Color_Ptr hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate) + { + h /= 360.0; + s /= 100.0; + l /= 100.0; + + if (l < 0) l = 0; + if (s < 0) s = 0; + if (l > 1) l = 1; + if (s > 1) s = 1; + while (h < 0) h += 1; + while (h > 1) h -= 1; + + // if saturation is exacly zero, we loose + // information for hue, since it will evaluate + // to zero if converted back from rgb. Setting + // saturation to a very tiny number solves this. + if (s == 0) s = 1e-10; + + // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color. + double m2; + if (l <= 0.5) m2 = l*(s+1.0); + 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 g = (h_to_rgb(m1, m2, h) * 255.0); + double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0); + + return SASS_MEMORY_NEW(Color, pstate, r, g, b, a); + } + + Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; + BUILT_IN(hsl) + { + if ( + special_number(Cast(env["$hue"])) || + special_number(Cast(env["$saturation"])) || + special_number(Cast(env["$lightness"])) + ) { + return SASS_MEMORY_NEW(String_Constant, pstate, "hsl(" + + env["$hue"]->to_string() + + ", " + + env["$saturation"]->to_string() + + ", " + + env["$lightness"]->to_string() + + ")" + ); + } + + return hsla_impl(ARGVAL("$hue"), + ARGVAL("$saturation"), + ARGVAL("$lightness"), + 1.0, + ctx, + pstate); + } + + Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; + BUILT_IN(hsla) + { + if ( + special_number(Cast(env["$hue"])) || + special_number(Cast(env["$saturation"])) || + special_number(Cast(env["$lightness"])) || + special_number(Cast(env["$alpha"])) + ) { + return SASS_MEMORY_NEW(String_Constant, pstate, "hsla(" + + env["$hue"]->to_string() + + ", " + + env["$saturation"]->to_string() + + ", " + + env["$lightness"]->to_string() + + ", " + + env["$alpha"]->to_string() + + ")" + ); + } + + return hsla_impl(ARGVAL("$hue"), + ARGVAL("$saturation"), + ARGVAL("$lightness"), + ARGVAL("$alpha"), + ctx, + pstate); + } + + Signature hue_sig = "hue($color)"; + BUILT_IN(hue) + { + Color_Ptr rgb_color = ARG("$color", Color); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + return SASS_MEMORY_NEW(Number, pstate, hsl_color.h, "deg"); + } + + Signature saturation_sig = "saturation($color)"; + BUILT_IN(saturation) + { + Color_Ptr rgb_color = ARG("$color", Color); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + return SASS_MEMORY_NEW(Number, pstate, hsl_color.s, "%"); + } + + Signature lightness_sig = "lightness($color)"; + BUILT_IN(lightness) + { + Color_Ptr rgb_color = ARG("$color", Color); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + return SASS_MEMORY_NEW(Number, pstate, hsl_color.l, "%"); + } + + Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; + BUILT_IN(adjust_hue) + { + Color_Ptr rgb_color = ARG("$color", Color); + double degrees = ARGVAL("$degrees"); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + return hsla_impl(hsl_color.h + degrees, + hsl_color.s, + hsl_color.l, + rgb_color->a(), + ctx, + pstate); + } + + Signature lighten_sig = "lighten($color, $amount)"; + BUILT_IN(lighten) + { + Color_Ptr rgb_color = ARG("$color", Color); + double amount = DARG_U_PRCT("$amount"); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + //Check lightness is not negative before lighten it + double hslcolorL = hsl_color.l; + if (hslcolorL < 0) { + hslcolorL = 0; + } + + return hsla_impl(hsl_color.h, + hsl_color.s, + hslcolorL + amount, + rgb_color->a(), + ctx, + pstate); + } + + Signature darken_sig = "darken($color, $amount)"; + BUILT_IN(darken) + { + Color_Ptr rgb_color = ARG("$color", Color); + double amount = DARG_U_PRCT("$amount"); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + + //Check lightness if not over 100, before darken it + double hslcolorL = hsl_color.l; + if (hslcolorL > 100) { + hslcolorL = 100; + } + + return hsla_impl(hsl_color.h, + hsl_color.s, + hslcolorL - amount, + rgb_color->a(), + ctx, + pstate); + } + + Signature saturate_sig = "saturate($color, $amount: false)"; + BUILT_IN(saturate) + { + // CSS3 filter function overload: pass literal through directly + if (!Cast(env["$amount"])) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); + } + + double amount = DARG_U_PRCT("$amount"); + Color_Ptr rgb_color = ARG("$color", Color); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + + double hslcolorS = hsl_color.s + amount; + + // Saturation cannot be below 0 or above 100 + if (hslcolorS < 0) { + hslcolorS = 0; + } + if (hslcolorS > 100) { + hslcolorS = 100; + } + + return hsla_impl(hsl_color.h, + hslcolorS, + hsl_color.l, + rgb_color->a(), + ctx, + pstate); + } + + Signature desaturate_sig = "desaturate($color, $amount)"; + BUILT_IN(desaturate) + { + Color_Ptr rgb_color = ARG("$color", Color); + double amount = DARG_U_PRCT("$amount"); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + + double hslcolorS = hsl_color.s - amount; + + // Saturation cannot be below 0 or above 100 + if (hslcolorS <= 0) { + hslcolorS = 0; + } + if (hslcolorS > 100) { + hslcolorS = 100; + } + + return hsla_impl(hsl_color.h, + hslcolorS, + hsl_color.l, + rgb_color->a(), + ctx, + pstate); + } + + Signature grayscale_sig = "grayscale($color)"; + BUILT_IN(grayscale) + { + // CSS3 filter function overload: pass literal through directly + Number_Ptr amount = Cast(env["$color"]); + if (amount) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); + } + + Color_Ptr rgb_color = ARG("$color", Color); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + return hsla_impl(hsl_color.h, + 0.0, + hsl_color.l, + rgb_color->a(), + ctx, + pstate); + } + + Signature complement_sig = "complement($color)"; + BUILT_IN(complement) + { + Color_Ptr rgb_color = ARG("$color", Color); + HSL hsl_color = rgb_to_hsl(rgb_color->r(), + rgb_color->g(), + rgb_color->b()); + return hsla_impl(hsl_color.h - 180.0, + hsl_color.s, + hsl_color.l, + rgb_color->a(), + ctx, + pstate); + } + + Signature invert_sig = "invert($color, $weight: 100%)"; + BUILT_IN(invert) + { + // CSS3 filter function overload: pass literal through directly + Number_Ptr amount = Cast(env["$color"]); + if (amount) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); + } + + double weight = DARG_U_PRCT("$weight"); + Color_Ptr rgb_color = ARG("$color", Color); + Color_Obj inv = SASS_MEMORY_NEW(Color, + pstate, + 255 - rgb_color->r(), + 255 - rgb_color->g(), + 255 - rgb_color->b(), + rgb_color->a()); + return colormix(ctx, pstate, inv, rgb_color, weight); + } + + //////////////////// + // OPACITY FUNCTIONS + //////////////////// + Signature alpha_sig = "alpha($color)"; + Signature opacity_sig = "opacity($color)"; + BUILT_IN(alpha) + { + String_Constant_Ptr ie_kwd = Cast(env["$color"]); + if (ie_kwd) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")"); + } + + // CSS3 filter function overload: pass literal through directly + Number_Ptr amount = Cast(env["$color"]); + if (amount) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); + } + + return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a()); + } + + Signature opacify_sig = "opacify($color, $amount)"; + Signature fade_in_sig = "fade-in($color, $amount)"; + BUILT_IN(opacify) + { + Color_Ptr color = ARG("$color", Color); + double amount = DARG_U_FACT("$amount"); + double alpha = std::min(color->a() + amount, 1.0); + return SASS_MEMORY_NEW(Color, + pstate, + color->r(), + color->g(), + color->b(), + alpha); + } + + Signature transparentize_sig = "transparentize($color, $amount)"; + Signature fade_out_sig = "fade-out($color, $amount)"; + BUILT_IN(transparentize) + { + Color_Ptr color = ARG("$color", Color); + double amount = DARG_U_FACT("$amount"); + double alpha = std::max(color->a() - amount, 0.0); + return SASS_MEMORY_NEW(Color, + pstate, + color->r(), + color->g(), + color->b(), + alpha); + } + + //////////////////////// + // OTHER COLOR FUNCTIONS + //////////////////////// + + Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; + BUILT_IN(adjust_color) + { + Color_Ptr color = ARG("$color", Color); + Number_Ptr r = Cast(env["$red"]); + Number_Ptr g = Cast(env["$green"]); + Number_Ptr b = Cast(env["$blue"]); + Number_Ptr h = Cast(env["$hue"]); + Number_Ptr s = Cast(env["$saturation"]); + Number_Ptr l = Cast(env["$lightness"]); + Number_Ptr a = Cast(env["$alpha"]); + + bool rgb = r || g || b; + bool hsl = h || s || l; + + if (rgb && hsl) { + error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces); + } + if (rgb) { + double rr = r ? DARG_R_BYTE("$red") : 0; + double gg = g ? DARG_R_BYTE("$green") : 0; + double bb = b ? DARG_R_BYTE("$blue") : 0; + double aa = a ? DARG_R_FACT("$alpha") : 0; + return SASS_MEMORY_NEW(Color, + pstate, + color->r() + rr, + color->g() + gg, + color->b() + bb, + color->a() + aa); + } + if (hsl) { + HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); + double ss = s ? DARG_R_PRCT("$saturation") : 0; + double ll = l ? DARG_R_PRCT("$lightness") : 0; + double aa = a ? DARG_R_FACT("$alpha") : 0; + return hsla_impl(hsl_struct.h + (h ? h->value() : 0), + hsl_struct.s + ss, + hsl_struct.l + ll, + color->a() + aa, + ctx, + pstate); + } + if (a) { + return SASS_MEMORY_NEW(Color, + pstate, + color->r(), + color->g(), + color->b(), + color->a() + (a ? a->value() : 0)); + } + error("not enough arguments for `adjust-color'", pstate, traces); + // unreachable + return color; + } + + Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; + BUILT_IN(scale_color) + { + Color_Ptr color = ARG("$color", Color); + Number_Ptr r = Cast(env["$red"]); + Number_Ptr g = Cast(env["$green"]); + Number_Ptr b = Cast(env["$blue"]); + Number_Ptr h = Cast(env["$hue"]); + Number_Ptr s = Cast(env["$saturation"]); + Number_Ptr l = Cast(env["$lightness"]); + Number_Ptr a = Cast(env["$alpha"]); + + bool rgb = r || g || b; + bool hsl = h || s || l; + + if (rgb && hsl) { + error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces); + } + if (rgb) { + double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0; + double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0; + double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0; + double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; + return SASS_MEMORY_NEW(Color, + pstate, + color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()), + color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()), + color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()), + color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); + } + if (hsl) { + double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0; + double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0; + double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0; + double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; + HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); + hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h); + hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s); + hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l); + double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()); + return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); + } + if (a) { + double ascale = (DARG_R_PRCT("$alpha")) / 100.0; + return SASS_MEMORY_NEW(Color, + pstate, + color->r(), + color->g(), + color->b(), + color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); + } + error("not enough arguments for `scale-color'", pstate, traces); + // unreachable + return color; + } + + Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; + BUILT_IN(change_color) + { + Color_Ptr color = ARG("$color", Color); + Number_Ptr r = Cast(env["$red"]); + Number_Ptr g = Cast(env["$green"]); + Number_Ptr b = Cast(env["$blue"]); + Number_Ptr h = Cast(env["$hue"]); + Number_Ptr s = Cast(env["$saturation"]); + Number_Ptr l = Cast(env["$lightness"]); + Number_Ptr a = Cast(env["$alpha"]); + + bool rgb = r || g || b; + bool hsl = h || s || l; + + if (rgb && hsl) { + error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces); + } + if (rgb) { + return SASS_MEMORY_NEW(Color, + pstate, + r ? DARG_U_BYTE("$red") : color->r(), + g ? DARG_U_BYTE("$green") : color->g(), + b ? DARG_U_BYTE("$blue") : color->b(), + a ? DARG_U_BYTE("$alpha") : color->a()); + } + if (hsl) { + HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); + if (h) hsl_struct.h = std::fmod(h->value(), 360.0); + if (s) hsl_struct.s = DARG_U_PRCT("$saturation"); + if (l) hsl_struct.l = DARG_U_PRCT("$lightness"); + double alpha = a ? DARG_U_FACT("$alpha") : color->a(); + return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); + } + if (a) { + double alpha = DARG_U_FACT("$alpha"); + return SASS_MEMORY_NEW(Color, + pstate, + color->r(), + color->g(), + color->b(), + alpha); + } + error("not enough arguments for `change-color'", pstate, traces); + // unreachable + return color; + } + + template + static double cap_channel(double c) { + if (c > range) return range; + else if (c < 0) return 0; + else return c; + } + + Signature ie_hex_str_sig = "ie-hex-str($color)"; + BUILT_IN(ie_hex_str) + { + Color_Ptr c = ARG("$color", Color); + double r = cap_channel<0xff>(c->r()); + double g = cap_channel<0xff>(c->g()); + double b = cap_channel<0xff>(c->b()); + double a = cap_channel<1> (c->a()) * 255; + + std::stringstream ss; + ss << '#' << std::setw(2) << std::setfill('0'); + ss << std::hex << std::setw(2) << static_cast(Sass::round(a, ctx.c_options.precision)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(r, ctx.c_options.precision)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(g, ctx.c_options.precision)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(b, ctx.c_options.precision)); + + std::string result(ss.str()); + for (size_t i = 0, L = result.length(); i < L; ++i) { + result[i] = std::toupper(result[i]); + } + return SASS_MEMORY_NEW(String_Quoted, pstate, result); + } + + } + +} diff --git a/src/fn_colors.hpp b/src/fn_colors.hpp new file mode 100644 index 000000000..a474c64b5 --- /dev/null +++ b/src/fn_colors.hpp @@ -0,0 +1,85 @@ +#ifndef SASS_FN_COLORS_H +#define SASS_FN_COLORS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + // macros for common ranges (u mean unsigned or upper, r for full range) + #define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double + #define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double + #define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double + #define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double + #define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double + #define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double + + // macros for color related inputs (rbg and alpha/opacity values) + #define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double + #define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double + + extern Signature rgb_sig; + extern Signature rgba_4_sig; + extern Signature rgba_2_sig; + extern Signature red_sig; + extern Signature green_sig; + extern Signature blue_sig; + extern Signature mix_sig; + extern Signature hsl_sig; + extern Signature hsla_sig; + extern Signature hue_sig; + extern Signature saturation_sig; + extern Signature lightness_sig; + extern Signature adjust_hue_sig; + extern Signature lighten_sig; + extern Signature darken_sig; + extern Signature saturate_sig; + extern Signature desaturate_sig; + extern Signature grayscale_sig; + extern Signature complement_sig; + extern Signature invert_sig; + extern Signature alpha_sig; + extern Signature opacity_sig; + extern Signature opacify_sig; + extern Signature fade_in_sig; + extern Signature transparentize_sig; + extern Signature fade_out_sig; + extern Signature adjust_color_sig; + extern Signature scale_color_sig; + extern Signature change_color_sig; + extern Signature ie_hex_str_sig; + + BUILT_IN(rgb); + BUILT_IN(rgba_4); + BUILT_IN(rgba_2); + BUILT_IN(red); + BUILT_IN(green); + BUILT_IN(blue); + BUILT_IN(mix); + BUILT_IN(hsl); + BUILT_IN(hsla); + BUILT_IN(hue); + BUILT_IN(saturation); + BUILT_IN(lightness); + BUILT_IN(adjust_hue); + BUILT_IN(lighten); + BUILT_IN(darken); + BUILT_IN(saturate); + BUILT_IN(desaturate); + BUILT_IN(grayscale); + BUILT_IN(complement); + BUILT_IN(invert); + BUILT_IN(alpha); + BUILT_IN(opacify); + BUILT_IN(transparentize); + BUILT_IN(adjust_color); + BUILT_IN(scale_color); + BUILT_IN(change_color); + BUILT_IN(ie_hex_str); + + } + +} + +#endif diff --git a/src/fn_lists.cpp b/src/fn_lists.cpp new file mode 100644 index 000000000..b17da6398 --- /dev/null +++ b/src/fn_lists.cpp @@ -0,0 +1,284 @@ +#include "listize.hpp" +#include "operators.hpp" +#include "fn_utils.hpp" +#include "fn_lists.hpp" + +namespace Sass { + + namespace Functions { + + ///////////////// + // LIST FUNCTIONS + ///////////////// + + Signature keywords_sig = "keywords($args)"; + BUILT_IN(keywords) + { + List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy + Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1); + for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { + Expression_Obj obj = arglist->at(i); + Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX + std::string name = std::string(arg->name()); + name = name.erase(0, 1); // sanitize name (remove dollar sign) + *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted, + pstate, name), + arg->value()); + } + return result.detach(); + } + + Signature length_sig = "length($list)"; + BUILT_IN(length) + { + if (Selector_List_Ptr sl = Cast(env["$list"])) { + return SASS_MEMORY_NEW(Number, pstate, (double)sl->length()); + } + Expression_Ptr v = ARG("$list", Expression); + if (v->concrete_type() == Expression::MAP) { + Map_Ptr map = Cast(env["$list"]); + return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1)); + } + if (v->concrete_type() == Expression::SELECTOR) { + if (Compound_Selector_Ptr h = Cast(v)) { + return SASS_MEMORY_NEW(Number, pstate, (double)h->length()); + } else if (Selector_List_Ptr ls = Cast(v)) { + return SASS_MEMORY_NEW(Number, pstate, (double)ls->length()); + } else { + return SASS_MEMORY_NEW(Number, pstate, 1); + } + } + + List_Ptr list = Cast(env["$list"]); + return SASS_MEMORY_NEW(Number, + pstate, + (double)(list ? list->size() : 1)); + } + + Signature nth_sig = "nth($list, $n)"; + BUILT_IN(nth) + { + double nr = ARGVAL("$n"); + Map_Ptr m = Cast(env["$list"]); + if (Selector_List_Ptr sl = Cast(env["$list"])) { + size_t len = m ? m->length() : sl->length(); + bool empty = m ? m->empty() : sl->empty(); + if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); + double index = std::floor(nr < 0 ? len + nr : nr - 1); + if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); + // return (*sl)[static_cast(index)]; + Listize listize; + return Cast((*sl)[static_cast(index)]->perform(&listize)); + } + List_Obj l = Cast(env["$list"]); + if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces); + // if the argument isn't a list, then wrap it in a singleton list + if (!m && !l) { + l = SASS_MEMORY_NEW(List, pstate, 1); + l->append(ARG("$list", Expression)); + } + size_t len = m ? m->length() : l->length(); + bool empty = m ? m->empty() : l->empty(); + if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); + double index = std::floor(nr < 0 ? len + nr : nr - 1); + if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); + + if (m) { + l = SASS_MEMORY_NEW(List, pstate, 1); + l->append(m->keys()[static_cast(index)]); + l->append(m->at(m->keys()[static_cast(index)])); + return l.detach(); + } + else { + Value_Obj rv = l->value_at_index(static_cast(index)); + rv->set_delayed(false); + return rv.detach(); + } + } + + Signature set_nth_sig = "set-nth($list, $n, $value)"; + BUILT_IN(set_nth) + { + Map_Obj m = Cast(env["$list"]); + List_Obj l = Cast(env["$list"]); + Number_Obj n = ARG("$n", Number); + Expression_Obj v = ARG("$value", Expression); + if (!l) { + l = SASS_MEMORY_NEW(List, pstate, 1); + l->append(ARG("$list", Expression)); + } + if (m) { + l = m->to_list(pstate); + } + if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); + double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); + if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); + List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed()); + for (size_t i = 0, L = l->length(); i < L; ++i) { + result->append(((i == index) ? v : (*l)[i])); + } + return result; + } + + Signature index_sig = "index($list, $value)"; + BUILT_IN(index) + { + Map_Obj m = Cast(env["$list"]); + List_Obj l = Cast(env["$list"]); + Expression_Obj v = ARG("$value", Expression); + if (!l) { + l = SASS_MEMORY_NEW(List, pstate, 1); + l->append(ARG("$list", Expression)); + } + if (m) { + l = m->to_list(pstate); + } + for (size_t i = 0, L = l->length(); i < L; ++i) { + if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1)); + } + return SASS_MEMORY_NEW(Null, pstate); + } + + Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)"; + BUILT_IN(join) + { + Map_Obj m1 = Cast(env["$list1"]); + Map_Obj m2 = Cast(env["$list2"]); + List_Obj l1 = Cast(env["$list1"]); + List_Obj l2 = Cast(env["$list2"]); + String_Constant_Obj sep = ARG("$separator", String_Constant); + enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); + Value* bracketed = ARG("$bracketed", Value); + bool is_bracketed = (l1 ? l1->is_bracketed() : false); + if (!l1) { + l1 = SASS_MEMORY_NEW(List, pstate, 1); + l1->append(ARG("$list1", Expression)); + sep_val = (l2 ? l2->separator() : SASS_SPACE); + is_bracketed = (l2 ? l2->is_bracketed() : false); + } + if (!l2) { + l2 = SASS_MEMORY_NEW(List, pstate, 1); + l2->append(ARG("$list2", Expression)); + } + if (m1) { + l1 = m1->to_list(pstate); + sep_val = SASS_COMMA; + } + if (m2) { + l2 = m2->to_list(pstate); + } + size_t len = l1->length() + l2->length(); + std::string sep_str = unquote(sep->value()); + if (sep_str == "space") sep_val = SASS_SPACE; + else if (sep_str == "comma") sep_val = SASS_COMMA; + else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); + String_Constant_Obj bracketed_as_str = Cast(bracketed); + bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto"; + if (!bracketed_is_auto) { + is_bracketed = !bracketed->is_false(); + } + List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed); + result->concat(l1); + result->concat(l2); + return result.detach(); + } + + Signature append_sig = "append($list, $val, $separator: auto)"; + BUILT_IN(append) + { + Map_Obj m = Cast(env["$list"]); + List_Obj l = Cast(env["$list"]); + Expression_Obj v = ARG("$val", Expression); + if (Selector_List_Ptr sl = Cast(env["$list"])) { + Listize listize; + l = Cast(sl->perform(&listize)); + } + String_Constant_Obj sep = ARG("$separator", String_Constant); + if (!l) { + l = SASS_MEMORY_NEW(List, pstate, 1); + l->append(ARG("$list", Expression)); + } + if (m) { + l = m->to_list(pstate); + } + List_Ptr result = SASS_MEMORY_COPY(l); + std::string sep_str(unquote(sep->value())); + if (sep_str != "auto") { // check default first + if (sep_str == "space") result->separator(SASS_SPACE); + else if (sep_str == "comma") result->separator(SASS_COMMA); + else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); + } + if (l->is_arglist()) { + result->append(SASS_MEMORY_NEW(Argument, + v->pstate(), + v, + "", + false, + false)); + + } else { + result->append(v); + } + return result; + } + + Signature zip_sig = "zip($lists...)"; + BUILT_IN(zip) + { + List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List)); + size_t shortest = 0; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + List_Obj ith = Cast(arglist->value_at_index(i)); + Map_Obj mith = Cast(arglist->value_at_index(i)); + if (!ith) { + if (mith) { + ith = mith->to_list(pstate); + } else { + ith = SASS_MEMORY_NEW(List, pstate, 1); + ith->append(arglist->value_at_index(i)); + } + if (arglist->is_arglist()) { + Argument_Obj arg = (Argument_Ptr)(arglist->at(i).ptr()); // XXX + arg->value(ith); + } else { + (*arglist)[i] = ith; + } + } + shortest = (i ? std::min(shortest, ith->length()) : ith->length()); + } + List_Ptr zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA); + size_t L = arglist->length(); + for (size_t i = 0; i < shortest; ++i) { + List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L); + for (size_t j = 0; j < L; ++j) { + zipper->append(Cast(arglist->value_at_index(j))->at(i)); + } + zippers->append(zipper); + } + return zippers; + } + + Signature list_separator_sig = "list_separator($list)"; + BUILT_IN(list_separator) + { + List_Obj l = Cast(env["$list"]); + if (!l) { + l = SASS_MEMORY_NEW(List, pstate, 1); + l->append(ARG("$list", Expression)); + } + return SASS_MEMORY_NEW(String_Quoted, + pstate, + l->separator() == SASS_COMMA ? "comma" : "space"); + } + + Signature is_bracketed_sig = "is-bracketed($list)"; + BUILT_IN(is_bracketed) + { + Value_Obj value = ARG("$list", Value); + List_Obj list = Cast(value); + return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed()); + } + + } + +} \ No newline at end of file diff --git a/src/fn_lists.hpp b/src/fn_lists.hpp new file mode 100644 index 000000000..260023aeb --- /dev/null +++ b/src/fn_lists.hpp @@ -0,0 +1,34 @@ +#ifndef SASS_FN_LISTS_H +#define SASS_FN_LISTS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + extern Signature length_sig; + extern Signature nth_sig; + extern Signature index_sig; + extern Signature join_sig; + extern Signature append_sig; + extern Signature zip_sig; + extern Signature list_separator_sig; + extern Signature is_bracketed_sig; + extern Signature keywords_sig; + + BUILT_IN(length); + BUILT_IN(nth); + BUILT_IN(index); + BUILT_IN(join); + BUILT_IN(append); + BUILT_IN(zip); + BUILT_IN(list_separator); + BUILT_IN(is_bracketed); + BUILT_IN(keywords); + + } + +} + +#endif diff --git a/src/fn_maps.cpp b/src/fn_maps.cpp new file mode 100644 index 000000000..54a659433 --- /dev/null +++ b/src/fn_maps.cpp @@ -0,0 +1,94 @@ +#include "operators.hpp" +#include "fn_utils.hpp" +#include "fn_maps.hpp" + +namespace Sass { + + namespace Functions { + + ///////////////// + // MAP FUNCTIONS + ///////////////// + + Signature map_get_sig = "map-get($map, $key)"; + BUILT_IN(map_get) + { + // leaks for "map-get((), foo)" if not Obj + // investigate why this is (unexpected) + Map_Obj m = ARGM("$map", Map); + Expression_Obj v = ARG("$key", Expression); + try { + Value_Obj val = m->at(v); + if (!val) return SASS_MEMORY_NEW(Null, pstate); + val->set_delayed(false); + return val.detach(); + } catch (const std::out_of_range&) { + return SASS_MEMORY_NEW(Null, pstate); + } + catch (...) { throw; } + } + + Signature map_has_key_sig = "map-has-key($map, $key)"; + BUILT_IN(map_has_key) + { + Map_Obj m = ARGM("$map", Map); + Expression_Obj v = ARG("$key", Expression); + return SASS_MEMORY_NEW(Boolean, pstate, m->has(v)); + } + + Signature map_keys_sig = "map-keys($map)"; + BUILT_IN(map_keys) + { + Map_Obj m = ARGM("$map", Map); + List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); + for ( auto key : m->keys()) { + result->append(key); + } + return result; + } + + Signature map_values_sig = "map-values($map)"; + BUILT_IN(map_values) + { + Map_Obj m = ARGM("$map", Map); + List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); + for ( auto key : m->keys()) { + result->append(m->at(key)); + } + return result; + } + + Signature map_merge_sig = "map-merge($map1, $map2)"; + BUILT_IN(map_merge) + { + Map_Obj m1 = ARGM("$map1", Map); + Map_Obj m2 = ARGM("$map2", Map); + + size_t len = m1->length() + m2->length(); + Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len); + // concat not implemented for maps + *result += m1; + *result += m2; + return result; + } + + Signature map_remove_sig = "map-remove($map, $keys...)"; + BUILT_IN(map_remove) + { + bool remove; + Map_Obj m = ARGM("$map", Map); + List_Obj arglist = ARG("$keys", List); + Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, 1); + for (auto key : m->keys()) { + remove = false; + for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) { + remove = Operators::eq(key, arglist->value_at_index(j)); + } + if (!remove) *result << std::make_pair(key, m->at(key)); + } + return result; + } + + } + +} \ No newline at end of file diff --git a/src/fn_maps.hpp b/src/fn_maps.hpp new file mode 100644 index 000000000..9551ec33e --- /dev/null +++ b/src/fn_maps.hpp @@ -0,0 +1,30 @@ +#ifndef SASS_FN_MAPS_H +#define SASS_FN_MAPS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + #define ARGM(argname, argtype) get_arg_m(argname, env, sig, pstate, traces) + + extern Signature map_get_sig; + extern Signature map_merge_sig; + extern Signature map_remove_sig; + extern Signature map_keys_sig; + extern Signature map_values_sig; + extern Signature map_has_key_sig; + + BUILT_IN(map_get); + BUILT_IN(map_merge); + BUILT_IN(map_remove); + BUILT_IN(map_keys); + BUILT_IN(map_values); + BUILT_IN(map_has_key); + + } + +} + +#endif diff --git a/src/fn_miscs.cpp b/src/fn_miscs.cpp new file mode 100644 index 000000000..938e3f153 --- /dev/null +++ b/src/fn_miscs.cpp @@ -0,0 +1,252 @@ +#include "ast.hpp" +#include "expand.hpp" +#include "fn_utils.hpp" +#include "fn_miscs.hpp" +#include "debugger.hpp" + +namespace Sass { + + namespace Functions { + + // features + static std::set features { + "global-variable-shadowing", + "extend-selector-pseudoclass", + "at-error", + "units-level-3", + "custom-property" + }; + + ////////////////////////// + // INTROSPECTION FUNCTIONS + ////////////////////////// + + Signature type_of_sig = "type-of($value)"; + BUILT_IN(type_of) + { + Expression_Ptr v = ARG("$value", Expression); + return SASS_MEMORY_NEW(String_Quoted, pstate, v->type()); + } + + Signature variable_exists_sig = "variable-exists($name)"; + BUILT_IN(variable_exists) + { + std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); + + if(d_env.has("$"+s)) { + return SASS_MEMORY_NEW(Boolean, pstate, true); + } + else { + return SASS_MEMORY_NEW(Boolean, pstate, false); + } + } + + Signature global_variable_exists_sig = "global-variable-exists($name)"; + BUILT_IN(global_variable_exists) + { + std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); + + if(d_env.has_global("$"+s)) { + return SASS_MEMORY_NEW(Boolean, pstate, true); + } + else { + return SASS_MEMORY_NEW(Boolean, pstate, false); + } + } + + Signature function_exists_sig = "function-exists($name)"; + BUILT_IN(function_exists) + { + String_Constant_Ptr ss = Cast(env["$name"]); + if (!ss) { + error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces); + } + + std::string name = Util::normalize_underscores(unquote(ss->value())); + + if(d_env.has_global(name+"[f]")) { + return SASS_MEMORY_NEW(Boolean, pstate, true); + } + else { + return SASS_MEMORY_NEW(Boolean, pstate, false); + } + } + + Signature mixin_exists_sig = "mixin-exists($name)"; + BUILT_IN(mixin_exists) + { + std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); + + if(d_env.has_global(s+"[m]")) { + return SASS_MEMORY_NEW(Boolean, pstate, true); + } + else { + return SASS_MEMORY_NEW(Boolean, pstate, false); + } + } + + Signature feature_exists_sig = "feature-exists($name)"; + BUILT_IN(feature_exists) + { + std::string s = unquote(ARG("$name", String_Constant)->value()); + + if(features.find(s) == features.end()) { + return SASS_MEMORY_NEW(Boolean, pstate, false); + } + else { + return SASS_MEMORY_NEW(Boolean, pstate, true); + } + } + + Signature call_sig = "call($name, $args...)"; + BUILT_IN(call) + { + std::string name; + Function_Ptr ff = Cast(env["$name"]); + String_Constant_Ptr ss = Cast(env["$name"]); + + if (ss) { + name = Util::normalize_underscores(unquote(ss->value())); + std::cerr << "DEPRECATION WARNING: "; + std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl; + std::cerr << "in Sass 4.0. Use call(get-function(" + quote(name) + ")) instead." << std::endl; + std::cerr << std::endl; + } else if (ff) { + name = ff->name(); + } + + List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); + + Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); + // std::string full_name(name + "[f]"); + // Definition_Ptr def = d_env.has(full_name) ? Cast((d_env)[full_name]) : 0; + // Parameters_Ptr params = def ? def->parameters() : 0; + // size_t param_size = params ? params->length() : 0; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj expr = arglist->value_at_index(i); + // if (params && params->has_rest_parameter()) { + // Parameter_Obj p = param_size > i ? (*params)[i] : 0; + // List_Ptr list = Cast(expr); + // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; + // } + if (arglist->is_arglist()) { + Expression_Obj obj = arglist->at(i); + Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX + args->append(SASS_MEMORY_NEW(Argument, + pstate, + expr, + arg ? arg->name() : "", + arg ? arg->is_rest_argument() : false, + arg ? arg->is_keyword_argument() : false)); + } else { + args->append(SASS_MEMORY_NEW(Argument, pstate, expr)); + } + } + Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args); + Expand expand(ctx, &d_env, &selector_stack); + func->via_call(true); // calc invoke is allowed + if (ff) func->func(ff); + return Cast(func->perform(&expand.eval)); + } + + //////////////////// + // BOOLEAN FUNCTIONS + //////////////////// + + Signature not_sig = "not($value)"; + BUILT_IN(sass_not) + { + return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false()); + } + + Signature if_sig = "if($condition, $if-true, $if-false)"; + // BUILT_IN(sass_if) + // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } + BUILT_IN(sass_if) + { + Expand expand(ctx, &d_env, &selector_stack); + Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval); + bool is_true = !cond->is_false(); + Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression); + Value_Obj qwe = Cast(res->perform(&expand.eval)); + // if (qwe == 0) debug_ast(res); + // res = res->perform(&expand.eval.val_eval); + qwe->set_delayed(false); // clone? + return qwe.detach(); + } + + ////////////////////////// + // MISCELLANEOUS FUNCTIONS + ////////////////////////// + + // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String) + // unquoted_string(value.to_sass) + + Signature inspect_sig = "inspect($value)"; + BUILT_IN(inspect) + { + Expression_Ptr v = ARG("$value", Expression); + if (v->concrete_type() == Expression::NULL_VAL) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "null"); + } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) { + return SASS_MEMORY_NEW(String_Quoted, pstate, "false"); + } else if (v->concrete_type() == Expression::STRING) { + return Cast(v); + } else { + // ToDo: fix to_sass for nested parentheses + Sass_Output_Style old_style; + old_style = ctx.c_options.output_style; + ctx.c_options.output_style = TO_SASS; + Emitter emitter(ctx.c_options); + Inspect i(emitter); + i.in_declaration = false; + v->perform(&i); + ctx.c_options.output_style = old_style; + return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer()); + } + // return v; + } + + Signature content_exists_sig = "content-exists()"; + BUILT_IN(content_exists) + { + if (!d_env.has_global("is_in_mixin")) { + error("Cannot call content-exists() except within a mixin.", pstate, traces); + } + return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]")); + } + + Signature get_function_sig = "get-function($name, $css: false)"; + BUILT_IN(get_function) + { + String_Constant_Ptr ss = Cast(env["$name"]); + if (!ss) { + error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces); + } + + std::string name = Util::normalize_underscores(unquote(ss->value())); + std::string full_name = name + "[f]"; + + Boolean_Obj css = ARG("$css", Boolean); + if (!css->is_false()) { + Definition_Ptr def = SASS_MEMORY_NEW(Definition, + pstate, + name, + SASS_MEMORY_NEW(Parameters, pstate), + SASS_MEMORY_NEW(Block, pstate, 0, false), + Definition::FUNCTION); + return SASS_MEMORY_NEW(Function, pstate, def, true); + } + + + if (!d_env.has_global(full_name)) { + error("Function not found: " + name, pstate, traces); + } + + Definition_Ptr def = Cast(d_env[full_name]); + return SASS_MEMORY_NEW(Function, pstate, def, false); + } + + } + +} \ No newline at end of file diff --git a/src/fn_miscs.hpp b/src/fn_miscs.hpp new file mode 100644 index 000000000..aec693e92 --- /dev/null +++ b/src/fn_miscs.hpp @@ -0,0 +1,40 @@ +#ifndef SASS_FN_MISCS_H +#define SASS_FN_MISCS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + extern Signature type_of_sig; + extern Signature variable_exists_sig; + extern Signature global_variable_exists_sig; + extern Signature function_exists_sig; + extern Signature mixin_exists_sig; + extern Signature feature_exists_sig; + extern Signature call_sig; + extern Signature not_sig; + extern Signature if_sig; + extern Signature set_nth_sig; + extern Signature content_exists_sig; + extern Signature get_function_sig; + + BUILT_IN(type_of); + BUILT_IN(variable_exists); + BUILT_IN(global_variable_exists); + BUILT_IN(function_exists); + BUILT_IN(mixin_exists); + BUILT_IN(feature_exists); + BUILT_IN(call); + BUILT_IN(sass_not); + BUILT_IN(sass_if); + BUILT_IN(set_nth); + BUILT_IN(content_exists); + BUILT_IN(get_function); + + } + +} + +#endif diff --git a/src/fn_numbers.cpp b/src/fn_numbers.cpp new file mode 100644 index 000000000..249d69856 --- /dev/null +++ b/src/fn_numbers.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ast.hpp" +#include "sass.hpp" +#include "units.hpp" +#include "fn_utils.hpp" +#include "fn_numbers.hpp" + +#ifdef __MINGW32__ +#include "windows.h" +#include "wincrypt.h" +#endif + +namespace Sass { + + namespace Functions { + + #ifdef __MINGW32__ + uint64_t GetSeed() + { + HCRYPTPROV hp = 0; + BYTE rb[8]; + CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + CryptGenRandom(hp, sizeof(rb), rb); + CryptReleaseContext(hp, 0); + + uint64_t seed; + memcpy(&seed, &rb[0], sizeof(seed)); + + return seed; + } + #else + uint64_t GetSeed() + { + std::random_device rd; + return rd(); + } + #endif + + // note: the performance of many implementations of + // random_device degrades sharply once the entropy pool + // is exhausted. For practical use, random_device is + // generally only used to seed a PRNG such as mt19937. + static std::mt19937 rand(static_cast(GetSeed())); + + /////////////////// + // NUMBER FUNCTIONS + /////////////////// + + Signature percentage_sig = "percentage($number)"; + BUILT_IN(percentage) + { + Number_Obj n = ARGN("$number"); + if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces); + return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%"); + } + + Signature round_sig = "round($number)"; + BUILT_IN(round) + { + Number_Obj r = ARGN("$number"); + r->value(Sass::round(r->value(), ctx.c_options.precision)); + r->pstate(pstate); + return r.detach(); + } + + Signature ceil_sig = "ceil($number)"; + BUILT_IN(ceil) + { + Number_Obj r = ARGN("$number"); + r->value(std::ceil(r->value())); + r->pstate(pstate); + return r.detach(); + } + + Signature floor_sig = "floor($number)"; + BUILT_IN(floor) + { + Number_Obj r = ARGN("$number"); + r->value(std::floor(r->value())); + r->pstate(pstate); + return r.detach(); + } + + Signature abs_sig = "abs($number)"; + BUILT_IN(abs) + { + Number_Obj r = ARGN("$number"); + r->value(std::abs(r->value())); + r->pstate(pstate); + return r.detach(); + } + + Signature min_sig = "min($numbers...)"; + BUILT_IN(min) + { + List_Ptr arglist = ARG("$numbers", List); + Number_Obj least = NULL; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj val = arglist->value_at_index(i); + Number_Obj xi = Cast(val); + if (!xi) { + error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces); + } + if (least) { + if (*xi < *least) least = xi; + } else least = xi; + } + return least.detach(); + } + + Signature max_sig = "max($numbers...)"; + BUILT_IN(max) + { + List_Ptr arglist = ARG("$numbers", List); + Number_Obj greatest = NULL; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj val = arglist->value_at_index(i); + Number_Obj xi = Cast(val); + if (!xi) { + error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces); + } + if (greatest) { + if (*greatest < *xi) greatest = xi; + } else greatest = xi; + } + return greatest.detach(); + } + + Signature random_sig = "random($limit:false)"; + BUILT_IN(random) + { + AST_Node_Obj arg = env["$limit"]; + Value_Ptr v = Cast(arg); + Number_Ptr l = Cast(arg); + Boolean_Ptr b = Cast(arg); + if (l) { + double lv = l->value(); + if (lv < 1) { + std::stringstream err; + err << "$limit " << lv << " must be greater than or equal to 1 for `random'"; + error(err.str(), pstate, traces); + } + bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON; + if (!eq_int) { + std::stringstream err; + err << "Expected $limit to be an integer but got " << lv << " for `random'"; + error(err.str(), pstate, traces); + } + std::uniform_real_distribution<> distributor(1, lv + 1); + uint_fast32_t distributed = static_cast(distributor(rand)); + return SASS_MEMORY_NEW(Number, pstate, (double)distributed); + } + else if (b) { + std::uniform_real_distribution<> distributor(0, 1); + double distributed = static_cast(distributor(rand)); + return SASS_MEMORY_NEW(Number, pstate, distributed); + } else if (v) { + traces.push_back(Backtrace(pstate)); + throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v); + } else { + traces.push_back(Backtrace(pstate)); + throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number"); + } + } + + Signature unique_id_sig = "unique-id()"; + BUILT_IN(unique_id) + { + std::stringstream ss; + std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8 + uint_fast32_t distributed = static_cast(distributor(rand)); + ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed; + return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str()); + } + + Signature unit_sig = "unit($number)"; + BUILT_IN(unit) + { + Number_Obj arg = ARGN("$number"); + std::string str(quote(arg->unit(), '"')); + return SASS_MEMORY_NEW(String_Quoted, pstate, str); + } + + Signature unitless_sig = "unitless($number)"; + BUILT_IN(unitless) + { + Number_Obj arg = ARGN("$number"); + bool unitless = arg->is_unitless(); + return SASS_MEMORY_NEW(Boolean, pstate, unitless); + } + + Signature comparable_sig = "comparable($number-1, $number-2)"; + BUILT_IN(comparable) + { + Number_Obj n1 = ARGN("$number-1"); + Number_Obj n2 = ARGN("$number-2"); + if (n1->is_unitless() || n2->is_unitless()) { + return SASS_MEMORY_NEW(Boolean, pstate, true); + } + // normalize into main units + n1->normalize(); n2->normalize(); + Units &lhs_unit = *n1, &rhs_unit = *n2; + bool is_comparable = (lhs_unit == rhs_unit); + return SASS_MEMORY_NEW(Boolean, pstate, is_comparable); + } + + } + +} \ No newline at end of file diff --git a/src/fn_numbers.hpp b/src/fn_numbers.hpp new file mode 100644 index 000000000..dba96be0b --- /dev/null +++ b/src/fn_numbers.hpp @@ -0,0 +1,45 @@ +#ifndef SASS_FN_NUMBERS_H +#define SASS_FN_NUMBERS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + // return a number object (copied since we want to have reduced units) + #define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy + + extern Signature percentage_sig; + extern Signature round_sig; + extern Signature ceil_sig; + extern Signature floor_sig; + extern Signature abs_sig; + extern Signature min_sig; + extern Signature max_sig; + extern Signature inspect_sig; + extern Signature random_sig; + extern Signature unique_id_sig; + extern Signature unit_sig; + extern Signature unitless_sig; + extern Signature comparable_sig; + + BUILT_IN(percentage); + BUILT_IN(round); + BUILT_IN(ceil); + BUILT_IN(floor); + BUILT_IN(abs); + BUILT_IN(min); + BUILT_IN(max); + BUILT_IN(inspect); + BUILT_IN(random); + BUILT_IN(unique_id); + BUILT_IN(unit); + BUILT_IN(unitless); + BUILT_IN(comparable); + + } + +} + +#endif \ No newline at end of file diff --git a/src/fn_selectors.cpp b/src/fn_selectors.cpp new file mode 100644 index 000000000..eff7b0dad --- /dev/null +++ b/src/fn_selectors.cpp @@ -0,0 +1,238 @@ +#include "parser.hpp" +#include "extend.hpp" +#include "fn_utils.hpp" +#include "fn_selectors.hpp" + +namespace Sass { + + namespace Functions { + + Signature selector_nest_sig = "selector-nest($selectors...)"; + BUILT_IN(selector_nest) + { + List_Ptr arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed for `selector-nest'", pstate, traces); + + // Parse args into vector of selectors + SelectorStack parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj exp = Cast(arglist->value_at_index(i)); + if (exp->concrete_type() == Expression::NULL_VAL) { + std::stringstream msg; + msg << "$selectors: null is not a valid selector: it must be a string,\n"; + msg << "a list of strings, or a list of lists of strings for 'selector-nest'"; + error(msg.str(), pstate, traces); + } + if (String_Constant_Obj str = Cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options); + Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces); + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return SASS_MEMORY_NEW(Null, pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + SelectorStack::iterator itr = parsedSelectors.begin(); + Selector_List_Obj result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List_Obj child = *itr; + std::vector exploded; + selector_stack.push_back(result); + Selector_List_Obj rv = child->resolve_parent_refs(selector_stack, traces); + selector_stack.pop_back(); + for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) { + exploded.push_back((*rv)[m]); + } + result->elements(exploded); + } + + Listize listize; + return Cast(result->perform(&listize)); + } + + Signature selector_append_sig = "selector-append($selectors...)"; + BUILT_IN(selector_append) + { + List_Ptr arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed for `selector-append'", pstate, traces); + + // Parse args into vector of selectors + SelectorStack parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj exp = Cast(arglist->value_at_index(i)); + if (exp->concrete_type() == Expression::NULL_VAL) { + std::stringstream msg; + msg << "$selectors: null is not a valid selector: it must be a string,\n"; + msg << "a list of strings, or a list of lists of strings for 'selector-append'"; + error(msg.str(), pstate, traces); + } + if (String_Constant_Ptr str = Cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(); + Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces); + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return SASS_MEMORY_NEW(Null, pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + SelectorStack::iterator itr = parsedSelectors.begin(); + Selector_List_Obj result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List_Obj child = *itr; + std::vector newElements; + + // For every COMPLEX_SELECTOR in `result` + // For every COMPLEX_SELECTOR in `child` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Append all of childSeq head elements into parentSeqClone + // Set the innermost tail of parentSeqClone, to childSeq's tail + // Replace result->elements with newElements + for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { + for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { + Complex_Selector_Obj parentSeqClone = SASS_MEMORY_CLONE((*result)[i]); + Complex_Selector_Obj childSeq = (*child)[j]; + Complex_Selector_Obj base = childSeq->tail(); + + // Must be a simple sequence + if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { + std::string msg("Can't append \""); + msg += childSeq->to_string(); + msg += "\" to \""; + msg += parentSeqClone->to_string(); + msg += "\" for `selector-append'"; + error(msg, pstate, traces); + } + + // Cannot be a Universal selector + Element_Selector_Obj pType = Cast(childSeq->head()->first()); + if(pType && pType->name() == "*") { + std::string msg("Can't append \""); + msg += childSeq->to_string(); + msg += "\" to \""; + msg += parentSeqClone->to_string(); + msg += "\" for `selector-append'"; + error(msg, pstate, traces); + } + + // TODO: Add check for namespace stuff + + // append any selectors in childSeq's head + parentSeqClone->innermost()->head()->concat(base->head()); + + // Set parentSeqClone new tail + parentSeqClone->innermost()->tail( base->tail() ); + + newElements.push_back(parentSeqClone); + } + } + + result->elements(newElements); + } + + Listize listize; + return Cast(result->perform(&listize)); + } + + Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; + BUILT_IN(selector_unify) + { + Selector_List_Obj selector1 = ARGSELS("$selector1"); + Selector_List_Obj selector2 = ARGSELS("$selector2"); + + Selector_List_Obj result = selector1->unify_with(selector2); + Listize listize; + return Cast(result->perform(&listize)); + } + + Signature simple_selectors_sig = "simple-selectors($selector)"; + BUILT_IN(simple_selectors) + { + Compound_Selector_Obj sel = ARGSEL("$selector"); + + List_Ptr l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); + + for (size_t i = 0, L = sel->length(); i < L; ++i) { + Simple_Selector_Obj ss = (*sel)[i]; + std::string ss_string = ss->to_string() ; + + l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string)); + } + + return l; + } + + Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; + BUILT_IN(selector_extend) + { + Selector_List_Obj selector = ARGSELS("$selector"); + Selector_List_Obj extendee = ARGSELS("$extendee"); + Selector_List_Obj extender = ARGSELS("$extender"); + + Subset_Map subset_map; + extender->populate_extends(extendee, subset_map); + Extend extend(subset_map); + + Selector_List_Obj result = extend.extendSelectorList(selector, false); + + Listize listize; + return Cast(result->perform(&listize)); + } + + Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; + BUILT_IN(selector_replace) + { + Selector_List_Obj selector = ARGSELS("$selector"); + Selector_List_Obj original = ARGSELS("$original"); + Selector_List_Obj replacement = ARGSELS("$replacement"); + Subset_Map subset_map; + replacement->populate_extends(original, subset_map); + Extend extend(subset_map); + + Selector_List_Obj result = extend.extendSelectorList(selector, true); + + Listize listize; + return Cast(result->perform(&listize)); + } + + Signature selector_parse_sig = "selector-parse($selector)"; + BUILT_IN(selector_parse) + { + Selector_List_Obj sel = ARGSELS("$selector"); + + Listize listize; + return Cast(sel->perform(&listize)); + } + + Signature is_superselector_sig = "is-superselector($super, $sub)"; + BUILT_IN(is_superselector) + { + Selector_List_Obj sel_sup = ARGSELS("$super"); + Selector_List_Obj sel_sub = ARGSELS("$sub"); + bool result = sel_sup->is_superselector_of(sel_sub); + return SASS_MEMORY_NEW(Boolean, pstate, result); + } + + } + +} \ No newline at end of file diff --git a/src/fn_selectors.hpp b/src/fn_selectors.hpp new file mode 100644 index 000000000..d5c106cd2 --- /dev/null +++ b/src/fn_selectors.hpp @@ -0,0 +1,35 @@ +#ifndef SASS_FN_SELECTORS_H +#define SASS_FN_SELECTORS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + #define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, traces, ctx) + #define ARGSELS(argname) get_arg_sels(argname, env, sig, pstate, traces, ctx) + + BUILT_IN(selector_nest); + BUILT_IN(selector_append); + BUILT_IN(selector_extend); + BUILT_IN(selector_replace); + BUILT_IN(selector_unify); + BUILT_IN(is_superselector); + BUILT_IN(simple_selectors); + BUILT_IN(selector_parse); + + extern Signature selector_nest_sig; + extern Signature selector_append_sig; + extern Signature selector_extend_sig; + extern Signature selector_replace_sig; + extern Signature selector_unify_sig; + extern Signature is_superselector_sig; + extern Signature simple_selectors_sig; + extern Signature selector_parse_sig; + + } + +} + +#endif diff --git a/src/fn_strings.cpp b/src/fn_strings.cpp new file mode 100644 index 000000000..16ed74b34 --- /dev/null +++ b/src/fn_strings.cpp @@ -0,0 +1,261 @@ +#include +#include "utf8.h" +#include "ast.hpp" +#include "sass.hpp" +#include "fn_utils.hpp" +#include "fn_strings.hpp" + +namespace Sass { + + namespace Functions { + + void handle_utf8_error (const ParserState& pstate, Backtraces traces) + { + try { + throw; + } + catch (utf8::invalid_code_point) { + std::string msg("utf8::invalid_code_point"); + error(msg, pstate, traces); + } + catch (utf8::not_enough_room) { + std::string msg("utf8::not_enough_room"); + error(msg, pstate, traces); + } + catch (utf8::invalid_utf8) { + std::string msg("utf8::invalid_utf8"); + error(msg, pstate, traces); + } + catch (...) { throw; } + } + + /////////////////// + // STRING FUNCTIONS + /////////////////// + + Signature unquote_sig = "unquote($string)"; + BUILT_IN(sass_unquote) + { + AST_Node_Obj arg = env["$string"]; + if (String_Quoted_Ptr string_quoted = Cast(arg)) { + String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value()); + // remember if the string was quoted (color tokens) + result->is_delayed(true); // delay colors + return result; + } + else if (String_Constant_Ptr str = Cast(arg)) { + return str; + } + else if (Value_Ptr ex = Cast(arg)) { + Sass_Output_Style oldstyle = ctx.c_options.output_style; + ctx.c_options.output_style = SASS_STYLE_NESTED; + std::string val(arg->to_string(ctx.c_options)); + val = Cast(arg) ? "null" : val; + ctx.c_options.output_style = oldstyle; + + deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate); + return ex; + } + throw std::runtime_error("Invalid Data Type for unquote"); + } + + Signature quote_sig = "quote($string)"; + BUILT_IN(sass_quote) + { + AST_Node_Obj arg = env["$string"]; + // only set quote mark to true if already a string + if (String_Quoted_Ptr qstr = Cast(arg)) { + qstr->quote_mark('*'); + return qstr; + } + // all other nodes must be converted to a string node + std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); + String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str); + result->quote_mark('*'); + return result; + } + + + Signature str_length_sig = "str-length($string)"; + BUILT_IN(str_length) + { + size_t len = std::string::npos; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + len = UTF_8::code_point_count(s->value(), 0, s->value().size()); + + } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, traces); } + // return something even if we had an error (-1) + return SASS_MEMORY_NEW(Number, pstate, (double)len); + } + + Signature str_insert_sig = "str-insert($string, $insert, $index)"; + BUILT_IN(str_insert) + { + std::string str; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + str = s->value(); + str = unquote(str); + String_Constant_Ptr i = ARG("$insert", String_Constant); + std::string ins = i->value(); + ins = unquote(ins); + double index = ARGVAL("$index"); + size_t len = UTF_8::code_point_count(str, 0, str.size()); + + if (index > 0 && index <= len) { + // positive and within string length + str.insert(UTF_8::offset_at_position(str, static_cast(index) - 1), ins); + } + else if (index > len) { + // positive and past string length + str += ins; + } + else if (index == 0) { + str = ins + str; + } + else if (std::abs(index) <= len) { + // negative and within string length + index += len + 1; + str.insert(UTF_8::offset_at_position(str, static_cast(index)), ins); + } + else { + // negative and past string length + str = ins + str; + } + + if (String_Quoted_Ptr ss = Cast(s)) { + if (ss->quote_mark()) str = quote(str); + } + } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, traces); } + return SASS_MEMORY_NEW(String_Quoted, pstate, str); + } + + Signature str_index_sig = "str-index($string, $substring)"; + BUILT_IN(str_index) + { + size_t index = std::string::npos; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + String_Constant_Ptr t = ARG("$substring", String_Constant); + std::string str = s->value(); + str = unquote(str); + std::string substr = t->value(); + substr = unquote(substr); + + size_t c_index = str.find(substr); + if(c_index == std::string::npos) { + return SASS_MEMORY_NEW(Null, pstate); + } + index = UTF_8::code_point_count(str, 0, c_index) + 1; + } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, traces); } + // return something even if we had an error (-1) + return SASS_MEMORY_NEW(Number, pstate, (double)index); + } + + Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)"; + BUILT_IN(str_slice) + { + std::string newstr; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + double start_at = ARGVAL("$start-at"); + double end_at = ARGVAL("$end-at"); + String_Quoted_Ptr ss = Cast(s); + + std::string str = unquote(s->value()); + + size_t size = utf8::distance(str.begin(), str.end()); + + if (!Cast(env["$end-at"])) { + end_at = -1; + } + + if (end_at == 0 || (end_at + size) < 0) { + if (ss && ss->quote_mark()) newstr = quote(""); + return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); + } + + if (end_at < 0) { + end_at += size + 1; + if (end_at == 0) end_at = 1; + } + if (end_at > size) { end_at = (double)size; } + if (start_at < 0) { + start_at += size + 1; + if (start_at < 0) start_at = 0; + } + else if (start_at == 0) { ++ start_at; } + + if (start_at <= end_at) + { + std::string::iterator start = str.begin(); + utf8::advance(start, start_at - 1, str.end()); + std::string::iterator end = start; + utf8::advance(end, end_at - start_at + 1, str.end()); + newstr = std::string(start, end); + } + if (ss) { + if(ss->quote_mark()) newstr = quote(newstr); + } + } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, traces); } + return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); + } + + Signature to_upper_case_sig = "to-upper-case($string)"; + BUILT_IN(to_upper_case) + { + String_Constant_Ptr s = ARG("$string", String_Constant); + std::string str = s->value(); + + for (size_t i = 0, L = str.length(); i < L; ++i) { + if (Sass::Util::isAscii(str[i])) { + str[i] = std::toupper(str[i]); + } + } + + if (String_Quoted_Ptr ss = Cast(s)) { + String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); + cpy->value(str); + return cpy; + } else { + return SASS_MEMORY_NEW(String_Quoted, pstate, str); + } + } + + Signature to_lower_case_sig = "to-lower-case($string)"; + BUILT_IN(to_lower_case) + { + String_Constant_Ptr s = ARG("$string", String_Constant); + std::string str = s->value(); + + for (size_t i = 0, L = str.length(); i < L; ++i) { + if (Sass::Util::isAscii(str[i])) { + str[i] = std::tolower(str[i]); + } + } + + if (String_Quoted_Ptr ss = Cast(s)) { + String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); + cpy->value(str); + return cpy; + } else { + return SASS_MEMORY_NEW(String_Quoted, pstate, str); + } + } + + } + +} diff --git a/src/fn_strings.hpp b/src/fn_strings.hpp new file mode 100644 index 000000000..4a1ed1900 --- /dev/null +++ b/src/fn_strings.hpp @@ -0,0 +1,34 @@ +#ifndef SASS_FN_STRINGS_H +#define SASS_FN_STRINGS_H + +#include "fn_utils.hpp" + +namespace Sass { + + namespace Functions { + + extern Signature unquote_sig; + extern Signature quote_sig; + extern Signature str_length_sig; + extern Signature str_insert_sig; + extern Signature str_index_sig; + extern Signature str_slice_sig; + extern Signature to_upper_case_sig; + extern Signature to_lower_case_sig; + extern Signature length_sig; + + BUILT_IN(sass_unquote); + BUILT_IN(sass_quote); + BUILT_IN(str_length); + BUILT_IN(str_insert); + BUILT_IN(str_index); + BUILT_IN(str_slice); + BUILT_IN(to_upper_case); + BUILT_IN(to_lower_case); + BUILT_IN(length); + + } + +} + +#endif diff --git a/src/fn_utils.cpp b/src/fn_utils.cpp new file mode 100644 index 000000000..2c86852b0 --- /dev/null +++ b/src/fn_utils.cpp @@ -0,0 +1,153 @@ +#include "sass.hpp" +#include "parser.hpp" +#include "fn_utils.hpp" +#include "functions.hpp" + +namespace Sass { + + Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx) + { + Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]")); + sig_parser.lex(); + std::string name(Util::normalize_underscores(sig_parser.lexed)); + Parameters_Obj params = sig_parser.parse_parameters(); + return SASS_MEMORY_NEW(Definition, + ParserState("[built-in function]"), + sig, + name, + params, + func, + false); + } + + Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx) + { + using namespace Prelexer; + + const char* sig = sass_function_get_signature(c_func); + Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]")); + // allow to overload generic callback plus @warn, @error and @debug with custom functions + sig_parser.lex < alternatives < identifier, exactly <'*'>, + exactly < Constants::warn_kwd >, + exactly < Constants::error_kwd >, + exactly < Constants::debug_kwd > + > >(); + std::string name(Util::normalize_underscores(sig_parser.lexed)); + Parameters_Obj params = sig_parser.parse_parameters(); + return SASS_MEMORY_NEW(Definition, + ParserState("[c function]"), + sig, + name, + params, + c_func); + } + + namespace Functions { + + std::string function_name(Signature sig) + { + std::string str(sig); + return str.substr(0, str.find('(')); + } + + Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) + { + AST_Node_Ptr value = env[argname]; + if (Map_Ptr map = Cast(value)) return map; + List_Ptr list = Cast(value); + if (list && list->length() == 0) { + return SASS_MEMORY_NEW(Map, pstate, 0); + } + return get_arg(argname, env, sig, pstate, traces); + } + + double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi) + { + Number_Ptr val = get_arg(argname, env, sig, pstate, traces); + Number tmpnr(val); + tmpnr.reduce(); + double v = tmpnr.value(); + if (!(lo <= v && v <= hi)) { + std::stringstream msg; + msg << "argument `" << argname << "` of `" << sig << "` must be between "; + msg << lo << " and " << hi; + error(msg.str(), pstate, traces); + } + return v; + } + + Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) + { + Number_Ptr val = get_arg(argname, env, sig, pstate, traces); + val = SASS_MEMORY_COPY(val); + val->reduce(); + return val; + } + + double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) + { + Number_Ptr val = get_arg(argname, env, sig, pstate, traces); + Number tmpnr(val); + tmpnr.reduce(); + return tmpnr.value(); + } + + double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) + { + Number_Ptr val = get_arg(argname, env, sig, pstate, traces); + Number tmpnr(val); + tmpnr.reduce(); + if (tmpnr.unit() == "%") { + return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0); + } else { + return std::min(std::max(tmpnr.value(), 0.0), 255.0); + } + } + + double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) { + Number_Ptr val = get_arg(argname, env, sig, pstate, traces); + Number tmpnr(val); + tmpnr.reduce(); + if (tmpnr.unit() == "%") { + return std::min(std::max(tmpnr.value(), 0.0), 100.0); + } else { + return std::min(std::max(tmpnr.value(), 0.0), 1.0); + } + } + + Selector_List_Obj get_arg_sels(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) { + Expression_Obj exp = ARG(argname, Expression); + if (exp->concrete_type() == Expression::NULL_VAL) { + std::stringstream msg; + msg << argname << ": null is not a valid selector: it must be a string,\n"; + msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; + error(msg.str(), pstate, traces); + } + if (String_Constant_Ptr str = Cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options); + return Parser::parse_selector(exp_src.c_str(), ctx, traces); + } + + Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) { + Expression_Obj exp = ARG(argname, Expression); + if (exp->concrete_type() == Expression::NULL_VAL) { + std::stringstream msg; + msg << argname << ": null is not a string for `" << function_name(sig) << "'"; + error(msg.str(), pstate, traces); + } + if (String_Constant_Ptr str = Cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options); + Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces); + if (sel_list->length() == 0) return NULL; + Complex_Selector_Obj first = sel_list->first(); + if (!first->tail()) return first->head(); + return first->tail()->head(); + } + + } + +} diff --git a/src/fn_utils.hpp b/src/fn_utils.hpp new file mode 100644 index 000000000..0ff39d8cf --- /dev/null +++ b/src/fn_utils.hpp @@ -0,0 +1,62 @@ +#ifndef SASS_FN_UTILS_H +#define SASS_FN_UTILS_H +#include "units.hpp" +#include "backtrace.hpp" +#include "environment.hpp" +#include "ast_fwd_decl.hpp" +#include "error_handling.hpp" + +namespace Sass { + + #define FN_PROTOTYPE \ + Env& env, \ + Env& d_env, \ + Context& ctx, \ + Signature sig, \ + ParserState pstate, \ + Backtraces& traces, \ + SelectorStack& selector_stack + + typedef const char* Signature; + typedef PreValue_Ptr (*Native_Function)(FN_PROTOTYPE); + #define BUILT_IN(name) PreValue_Ptr name(FN_PROTOTYPE) + + #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, traces) + // special function for weird hsla percent (10px == 10% == 10 != 0.1) + #define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double + + Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx); + Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx); + + namespace Functions { + + template + T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) + { + T* val = Cast(env[argname]); + if (!val) { + std::string msg("argument `"); + msg += argname; + msg += "` of `"; + msg += sig; + msg += "` must be a "; + msg += T::type_name(); + error(msg, pstate, traces); + } + return val; + } + + Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // maps only + Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // numbers only + double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // colors only + double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // colors only + double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi); // colors only + double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // shared + Selector_List_Obj get_arg_sels(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); // selectors only + Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); // selectors only + + } + +} + +#endif diff --git a/src/functions.cpp b/src/functions.cpp index 9b118d53a..15ca7632e 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -23,2212 +23,20 @@ #include #include #include -#include #include -#ifdef __MINGW32__ -#include "windows.h" -#include "wincrypt.h" -#endif - -#define ARG(argname, argtype) get_arg(argname, env, sig, pstate, traces) -#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, traces, ctx) - -// return a number object (copied since we want to have reduced units) -#define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy - -// special function for weird hsla percent (10px == 10% == 10 != 0.1) -#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double - -// macros for common ranges (u mean unsigned or upper, r for full range) -#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double -#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double -#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double -#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double -#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double -#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double - -// macros for color related inputs (rbg and alpha/opacity values) -#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double -#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double +#include "fn_utils.hpp" +#include "fn_miscs.hpp" +#include "fn_maps.hpp" +#include "fn_lists.hpp" +#include "fn_colors.hpp" +#include "fn_numbers.hpp" +#include "fn_strings.hpp" +#include "fn_selectors.hpp" namespace Sass { - using std::stringstream; - using std::endl; - - Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx) - { - Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]")); - sig_parser.lex(); - std::string name(Util::normalize_underscores(sig_parser.lexed)); - Parameters_Obj params = sig_parser.parse_parameters(); - return SASS_MEMORY_NEW(Definition, - ParserState("[built-in function]"), - sig, - name, - params, - func, - false); - } - - Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx) - { - using namespace Prelexer; - - const char* sig = sass_function_get_signature(c_func); - Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]")); - // allow to overload generic callback plus @warn, @error and @debug with custom functions - sig_parser.lex < alternatives < identifier, exactly <'*'>, - exactly < Constants::warn_kwd >, - exactly < Constants::error_kwd >, - exactly < Constants::debug_kwd > - > >(); - std::string name(Util::normalize_underscores(sig_parser.lexed)); - Parameters_Obj params = sig_parser.parse_parameters(); - return SASS_MEMORY_NEW(Definition, - ParserState("[c function]"), - sig, - name, - params, - c_func, - false, true); - } - - std::string function_name(Signature sig) - { - std::string str(sig); - return str.substr(0, str.find('(')); - } namespace Functions { - inline void handle_utf8_error (const ParserState& pstate, Backtraces traces) - { - try { - throw; - } - catch (utf8::invalid_code_point) { - std::string msg("utf8::invalid_code_point"); - error(msg, pstate, traces); - } - catch (utf8::not_enough_room) { - std::string msg("utf8::not_enough_room"); - error(msg, pstate, traces); - } - catch (utf8::invalid_utf8) { - std::string msg("utf8::invalid_utf8"); - error(msg, pstate, traces); - } - catch (...) { throw; } - } - - template - T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - T* val = Cast(env[argname]); - if (!val) { - std::string msg("argument `"); - msg += argname; - msg += "` of `"; - msg += sig; - msg += "` must be a "; - msg += T::type_name(); - error(msg, pstate, traces); - } - return val; - } - - Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Map_Ptr val = Cast(env[argname]); - if (val) return val; - - List_Ptr lval = Cast(env[argname]); - if (lval && lval->length() == 0) return SASS_MEMORY_NEW(Map, pstate, 0); - - // fallback on get_arg for error handling - val = get_arg(argname, env, sig, pstate, traces); - return val; - } - - double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Number_Ptr val = get_arg(argname, env, sig, pstate, traces); - Number tmpnr(val); - tmpnr.reduce(); - double v = tmpnr.value(); - if (!(lo <= v && v <= hi)) { - std::stringstream msg; - msg << "argument `" << argname << "` of `" << sig << "` must be between "; - msg << lo << " and " << hi; - error(msg.str(), pstate, traces); - } - return v; - } - - Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Number_Ptr val = get_arg(argname, env, sig, pstate, traces); - val = SASS_MEMORY_COPY(val); - val->reduce(); - return val; - } - - double get_arg_v(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Number_Ptr val = get_arg(argname, env, sig, pstate, traces); - Number tmpnr(val); - tmpnr.reduce(); - /* - if (tmpnr.unit() == "%") { - tmpnr.value(tmpnr.value() / 100); - tmpnr.numerators.clear(); - } else { - if (!tmpnr.is_unitless()) error("argument " + argname + " of `" + std::string(sig) + "` must be unitless", pstate); - } - */ - return tmpnr.value(); - } - - double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Number_Ptr val = get_arg(argname, env, sig, pstate, traces); - Number tmpnr(val); - tmpnr.reduce(); - return tmpnr.value(); - } - - double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) - { - Number_Ptr val = get_arg(argname, env, sig, pstate, traces); - Number tmpnr(val); - tmpnr.reduce(); - if (tmpnr.unit() == "%") { - return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0); - } else { - return std::min(std::max(tmpnr.value(), 0.0), 255.0); - } - } - - - inline double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) { - Number_Ptr val = get_arg(argname, env, sig, pstate, traces); - Number tmpnr(val); - tmpnr.reduce(); - if (tmpnr.unit() == "%") { - return std::min(std::max(tmpnr.value(), 0.0), 100.0); - } else { - return std::min(std::max(tmpnr.value(), 0.0), 1.0); - } - } - - #define ARGSEL(argname, seltype, contextualize) get_arg_sel(argname, env, sig, pstate, traces, ctx) - - template - T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); - - template <> - Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) { - Expression_Obj exp = ARG(argname, Expression); - if (exp->concrete_type() == Expression::NULL_VAL) { - std::stringstream msg; - msg << argname << ": null is not a valid selector: it must be a string,\n"; - msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; - error(msg.str(), pstate, traces); - } - if (String_Constant_Ptr str = Cast(exp)) { - str->quote_mark(0); - } - std::string exp_src = exp->to_string(ctx.c_options); - return Parser::parse_selector(exp_src.c_str(), ctx, traces); - } - - template <> - Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) { - Expression_Obj exp = ARG(argname, Expression); - if (exp->concrete_type() == Expression::NULL_VAL) { - std::stringstream msg; - msg << argname << ": null is not a string for `" << function_name(sig) << "'"; - error(msg.str(), pstate, traces); - } - if (String_Constant_Ptr str = Cast(exp)) { - str->quote_mark(0); - } - std::string exp_src = exp->to_string(ctx.c_options); - Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces); - if (sel_list->length() == 0) return NULL; - Complex_Selector_Obj first = sel_list->first(); - if (!first->tail()) return first->head(); - return first->tail()->head(); - } - - #ifdef __MINGW32__ - uint64_t GetSeed() - { - HCRYPTPROV hp = 0; - BYTE rb[8]; - CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - CryptGenRandom(hp, sizeof(rb), rb); - CryptReleaseContext(hp, 0); - - uint64_t seed; - memcpy(&seed, &rb[0], sizeof(seed)); - - return seed; - } - #else - uint64_t GetSeed() - { - std::random_device rd; - return rd(); - } - #endif - - // note: the performance of many implementations of - // random_device degrades sharply once the entropy pool - // is exhausted. For practical use, random_device is - // generally only used to seed a PRNG such as mt19937. - static std::mt19937 rand(static_cast(GetSeed())); - - // features - static std::set features { - "global-variable-shadowing", - "extend-selector-pseudoclass", - "at-error", - "units-level-3", - "custom-property" - }; - - //////////////// - // RGB FUNCTIONS - //////////////// - - inline bool special_number(String_Constant_Ptr s) { - if (s) { - std::string calc("calc("); - std::string var("var("); - std::string ss(s->value()); - return std::equal(calc.begin(), calc.end(), ss.begin()) || - std::equal(var.begin(), var.end(), ss.begin()); - } - return false; - } - - Signature rgb_sig = "rgb($red, $green, $blue)"; - BUILT_IN(rgb) - { - if ( - special_number(Cast(env["$red"])) || - special_number(Cast(env["$green"])) || - special_number(Cast(env["$blue"])) - ) { - return SASS_MEMORY_NEW(String_Constant, pstate, "rgb(" - + env["$red"]->to_string() - + ", " - + env["$green"]->to_string() - + ", " - + env["$blue"]->to_string() - + ")" - ); - } - - return SASS_MEMORY_NEW(Color, - pstate, - COLOR_NUM("$red"), - COLOR_NUM("$green"), - COLOR_NUM("$blue")); - } - - Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; - BUILT_IN(rgba_4) - { - if ( - special_number(Cast(env["$red"])) || - special_number(Cast(env["$green"])) || - special_number(Cast(env["$blue"])) || - special_number(Cast(env["$alpha"])) - ) { - return SASS_MEMORY_NEW(String_Constant, pstate, "rgba(" - + env["$red"]->to_string() - + ", " - + env["$green"]->to_string() - + ", " - + env["$blue"]->to_string() - + ", " - + env["$alpha"]->to_string() - + ")" - ); - } - - return SASS_MEMORY_NEW(Color, - pstate, - COLOR_NUM("$red"), - COLOR_NUM("$green"), - COLOR_NUM("$blue"), - ALPHA_NUM("$alpha")); - } - - Signature rgba_2_sig = "rgba($color, $alpha)"; - BUILT_IN(rgba_2) - { - if ( - special_number(Cast(env["$color"])) - ) { - return SASS_MEMORY_NEW(String_Constant, pstate, "rgba(" - + env["$color"]->to_string() - + ", " - + env["$alpha"]->to_string() - + ")" - ); - } - - Color_Ptr c_arg = ARG("$color", Color); - - if ( - special_number(Cast(env["$alpha"])) - ) { - std::stringstream strm; - strm << "rgba(" - << (int)c_arg->r() << ", " - << (int)c_arg->g() << ", " - << (int)c_arg->b() << ", " - << env["$alpha"]->to_string() - << ")"; - return SASS_MEMORY_NEW(String_Constant, pstate, strm.str()); - } - - Color_Ptr new_c = SASS_MEMORY_COPY(c_arg); - new_c->a(ALPHA_NUM("$alpha")); - new_c->disp(""); - return new_c; - } - - Signature red_sig = "red($color)"; - BUILT_IN(red) - { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->r()); } - - Signature green_sig = "green($color)"; - BUILT_IN(green) - { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->g()); } - - Signature blue_sig = "blue($color)"; - BUILT_IN(blue) - { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); } - - Color* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) { - double p = weight/100; - double w = 2*p - 1; - double a = color1->a() - color2->a(); - - double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0; - double w2 = 1 - w1; - - return SASS_MEMORY_NEW(Color, - pstate, - Sass::round(w1*color1->r() + w2*color2->r(), ctx.c_options.precision), - Sass::round(w1*color1->g() + w2*color2->g(), ctx.c_options.precision), - Sass::round(w1*color1->b() + w2*color2->b(), ctx.c_options.precision), - color1->a()*p + color2->a()*(1-p)); - } - - Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)"; - BUILT_IN(mix) - { - Color_Obj color1 = ARG("$color-1", Color); - Color_Obj color2 = ARG("$color-2", Color); - double weight = DARG_U_PRCT("$weight"); - return colormix(ctx, pstate, color1, color2, weight); - - } - - //////////////// - // HSL FUNCTIONS - //////////////// - - // RGB to HSL helper function - struct HSL { double h; double s; double l; }; - HSL rgb_to_hsl(double r, double g, double b) - { - - // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV - r /= 255.0; g /= 255.0; b /= 255.0; - - double max = std::max(r, std::max(g, b)); - double min = std::min(r, std::min(g, b)); - double delta = max - min; - - double h = 0; - double s; - double l = (max + min) / 2.0; - - if (max == min) { - h = s = 0; // achromatic - } - else { - if (l < 0.5) s = delta / (max + min); - else s = delta / (2.0 - max - min); - - 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; - hsl_struct.h = h / 6 * 360; - hsl_struct.s = s * 100; - hsl_struct.l = l * 100; - - return hsl_struct; - } - - // hue to RGB helper function - double h_to_rgb(double m1, double m2, double h) { - 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; - return m1; - } - - Color_Ptr hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate) - { - h /= 360.0; - s /= 100.0; - l /= 100.0; - - if (l < 0) l = 0; - if (s < 0) s = 0; - if (l > 1) l = 1; - if (s > 1) s = 1; - while (h < 0) h += 1; - while (h > 1) h -= 1; - - // if saturation is exacly zero, we loose - // information for hue, since it will evaluate - // to zero if converted back from rgb. Setting - // saturation to a very tiny number solves this. - if (s == 0) s = 1e-10; - - // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color. - double m2; - if (l <= 0.5) m2 = l*(s+1.0); - 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 g = (h_to_rgb(m1, m2, h) * 255.0); - double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0); - - return SASS_MEMORY_NEW(Color, pstate, r, g, b, a); - } - - Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; - BUILT_IN(hsl) - { - if ( - special_number(Cast(env["$hue"])) || - special_number(Cast(env["$saturation"])) || - special_number(Cast(env["$lightness"])) - ) { - return SASS_MEMORY_NEW(String_Constant, pstate, "hsl(" - + env["$hue"]->to_string() - + ", " - + env["$saturation"]->to_string() - + ", " - + env["$lightness"]->to_string() - + ")" - ); - } - - return hsla_impl(ARGVAL("$hue"), - ARGVAL("$saturation"), - ARGVAL("$lightness"), - 1.0, - ctx, - pstate); - } - - Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; - BUILT_IN(hsla) - { - if ( - special_number(Cast(env["$hue"])) || - special_number(Cast(env["$saturation"])) || - special_number(Cast(env["$lightness"])) || - special_number(Cast(env["$alpha"])) - ) { - return SASS_MEMORY_NEW(String_Constant, pstate, "hsla(" - + env["$hue"]->to_string() - + ", " - + env["$saturation"]->to_string() - + ", " - + env["$lightness"]->to_string() - + ", " - + env["$alpha"]->to_string() - + ")" - ); - } - - return hsla_impl(ARGVAL("$hue"), - ARGVAL("$saturation"), - ARGVAL("$lightness"), - ARGVAL("$alpha"), - ctx, - pstate); - } - - Signature hue_sig = "hue($color)"; - BUILT_IN(hue) - { - Color_Ptr rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return SASS_MEMORY_NEW(Number, pstate, hsl_color.h, "deg"); - } - - Signature saturation_sig = "saturation($color)"; - BUILT_IN(saturation) - { - Color_Ptr rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return SASS_MEMORY_NEW(Number, pstate, hsl_color.s, "%"); - } - - Signature lightness_sig = "lightness($color)"; - BUILT_IN(lightness) - { - Color_Ptr rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return SASS_MEMORY_NEW(Number, pstate, hsl_color.l, "%"); - } - - Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; - BUILT_IN(adjust_hue) - { - Color_Ptr rgb_color = ARG("$color", Color); - double degrees = ARGVAL("$degrees"); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return hsla_impl(hsl_color.h + degrees, - hsl_color.s, - hsl_color.l, - rgb_color->a(), - ctx, - pstate); - } - - Signature lighten_sig = "lighten($color, $amount)"; - BUILT_IN(lighten) - { - Color_Ptr rgb_color = ARG("$color", Color); - double amount = DARG_U_PRCT("$amount"); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - //Check lightness is not negative before lighten it - double hslcolorL = hsl_color.l; - if (hslcolorL < 0) { - hslcolorL = 0; - } - - return hsla_impl(hsl_color.h, - hsl_color.s, - hslcolorL + amount, - rgb_color->a(), - ctx, - pstate); - } - - Signature darken_sig = "darken($color, $amount)"; - BUILT_IN(darken) - { - Color_Ptr rgb_color = ARG("$color", Color); - double amount = DARG_U_PRCT("$amount"); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - - //Check lightness if not over 100, before darken it - double hslcolorL = hsl_color.l; - if (hslcolorL > 100) { - hslcolorL = 100; - } - - return hsla_impl(hsl_color.h, - hsl_color.s, - hslcolorL - amount, - rgb_color->a(), - ctx, - pstate); - } - - Signature saturate_sig = "saturate($color, $amount: false)"; - BUILT_IN(saturate) - { - // CSS3 filter function overload: pass literal through directly - if (!Cast(env["$amount"])) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); - } - - double amount = DARG_U_PRCT("$amount"); - Color_Ptr rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - - double hslcolorS = hsl_color.s + amount; - - // Saturation cannot be below 0 or above 100 - if (hslcolorS < 0) { - hslcolorS = 0; - } - if (hslcolorS > 100) { - hslcolorS = 100; - } - - return hsla_impl(hsl_color.h, - hslcolorS, - hsl_color.l, - rgb_color->a(), - ctx, - pstate); - } - - Signature desaturate_sig = "desaturate($color, $amount)"; - BUILT_IN(desaturate) - { - Color_Ptr rgb_color = ARG("$color", Color); - double amount = DARG_U_PRCT("$amount"); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - - double hslcolorS = hsl_color.s - amount; - - // Saturation cannot be below 0 or above 100 - if (hslcolorS <= 0) { - hslcolorS = 0; - } - if (hslcolorS > 100) { - hslcolorS = 100; - } - - return hsla_impl(hsl_color.h, - hslcolorS, - hsl_color.l, - rgb_color->a(), - ctx, - pstate); - } - - Signature grayscale_sig = "grayscale($color)"; - BUILT_IN(grayscale) - { - // CSS3 filter function overload: pass literal through directly - Number_Ptr amount = Cast(env["$color"]); - if (amount) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); - } - - Color_Ptr rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return hsla_impl(hsl_color.h, - 0.0, - hsl_color.l, - rgb_color->a(), - ctx, - pstate); - } - - Signature complement_sig = "complement($color)"; - BUILT_IN(complement) - { - Color_Ptr rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return hsla_impl(hsl_color.h - 180.0, - hsl_color.s, - hsl_color.l, - rgb_color->a(), - ctx, - pstate); - } - - Signature invert_sig = "invert($color, $weight: 100%)"; - BUILT_IN(invert) - { - // CSS3 filter function overload: pass literal through directly - Number_Ptr amount = Cast(env["$color"]); - if (amount) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); - } - - double weight = DARG_U_PRCT("$weight"); - Color_Ptr rgb_color = ARG("$color", Color); - Color_Obj inv = SASS_MEMORY_NEW(Color, - pstate, - 255 - rgb_color->r(), - 255 - rgb_color->g(), - 255 - rgb_color->b(), - rgb_color->a()); - return colormix(ctx, pstate, inv, rgb_color, weight); - } - - //////////////////// - // OPACITY FUNCTIONS - //////////////////// - Signature alpha_sig = "alpha($color)"; - Signature opacity_sig = "opacity($color)"; - BUILT_IN(alpha) - { - String_Constant_Ptr ie_kwd = Cast(env["$color"]); - if (ie_kwd) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")"); - } - - // CSS3 filter function overload: pass literal through directly - Number_Ptr amount = Cast(env["$color"]); - if (amount) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); - } - - return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a()); - } - - Signature opacify_sig = "opacify($color, $amount)"; - Signature fade_in_sig = "fade-in($color, $amount)"; - BUILT_IN(opacify) - { - Color_Ptr color = ARG("$color", Color); - double amount = DARG_U_FACT("$amount"); - double alpha = std::min(color->a() + amount, 1.0); - return SASS_MEMORY_NEW(Color, - pstate, - color->r(), - color->g(), - color->b(), - alpha); - } - - Signature transparentize_sig = "transparentize($color, $amount)"; - Signature fade_out_sig = "fade-out($color, $amount)"; - BUILT_IN(transparentize) - { - Color_Ptr color = ARG("$color", Color); - double amount = DARG_U_FACT("$amount"); - double alpha = std::max(color->a() - amount, 0.0); - return SASS_MEMORY_NEW(Color, - pstate, - color->r(), - color->g(), - color->b(), - alpha); - } - - //////////////////////// - // OTHER COLOR FUNCTIONS - //////////////////////// - - Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; - BUILT_IN(adjust_color) - { - Color_Ptr color = ARG("$color", Color); - Number_Ptr r = Cast(env["$red"]); - Number_Ptr g = Cast(env["$green"]); - Number_Ptr b = Cast(env["$blue"]); - Number_Ptr h = Cast(env["$hue"]); - Number_Ptr s = Cast(env["$saturation"]); - Number_Ptr l = Cast(env["$lightness"]); - Number_Ptr a = Cast(env["$alpha"]); - - bool rgb = r || g || b; - bool hsl = h || s || l; - - if (rgb && hsl) { - error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces); - } - if (rgb) { - double rr = r ? DARG_R_BYTE("$red") : 0; - double gg = g ? DARG_R_BYTE("$green") : 0; - double bb = b ? DARG_R_BYTE("$blue") : 0; - double aa = a ? DARG_R_FACT("$alpha") : 0; - return SASS_MEMORY_NEW(Color, - pstate, - color->r() + rr, - color->g() + gg, - color->b() + bb, - color->a() + aa); - } - if (hsl) { - HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); - double ss = s ? DARG_R_PRCT("$saturation") : 0; - double ll = l ? DARG_R_PRCT("$lightness") : 0; - double aa = a ? DARG_R_FACT("$alpha") : 0; - return hsla_impl(hsl_struct.h + (h ? h->value() : 0), - hsl_struct.s + ss, - hsl_struct.l + ll, - color->a() + aa, - ctx, - pstate); - } - if (a) { - return SASS_MEMORY_NEW(Color, - pstate, - color->r(), - color->g(), - color->b(), - color->a() + (a ? a->value() : 0)); - } - error("not enough arguments for `adjust-color'", pstate, traces); - // unreachable - return color; - } - - Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; - BUILT_IN(scale_color) - { - Color_Ptr color = ARG("$color", Color); - Number_Ptr r = Cast(env["$red"]); - Number_Ptr g = Cast(env["$green"]); - Number_Ptr b = Cast(env["$blue"]); - Number_Ptr h = Cast(env["$hue"]); - Number_Ptr s = Cast(env["$saturation"]); - Number_Ptr l = Cast(env["$lightness"]); - Number_Ptr a = Cast(env["$alpha"]); - - bool rgb = r || g || b; - bool hsl = h || s || l; - - if (rgb && hsl) { - error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces); - } - if (rgb) { - double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0; - double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0; - double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0; - double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; - return SASS_MEMORY_NEW(Color, - pstate, - color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()), - color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()), - color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()), - color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); - } - if (hsl) { - double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0; - double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0; - double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0; - double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; - HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); - hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h); - hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s); - hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l); - double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()); - return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); - } - if (a) { - double ascale = (DARG_R_PRCT("$alpha")) / 100.0; - return SASS_MEMORY_NEW(Color, - pstate, - color->r(), - color->g(), - color->b(), - color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); - } - error("not enough arguments for `scale-color'", pstate, traces); - // unreachable - return color; - } - - Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; - BUILT_IN(change_color) - { - Color_Ptr color = ARG("$color", Color); - Number_Ptr r = Cast(env["$red"]); - Number_Ptr g = Cast(env["$green"]); - Number_Ptr b = Cast(env["$blue"]); - Number_Ptr h = Cast(env["$hue"]); - Number_Ptr s = Cast(env["$saturation"]); - Number_Ptr l = Cast(env["$lightness"]); - Number_Ptr a = Cast(env["$alpha"]); - - bool rgb = r || g || b; - bool hsl = h || s || l; - - if (rgb && hsl) { - error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces); - } - if (rgb) { - return SASS_MEMORY_NEW(Color, - pstate, - r ? DARG_U_BYTE("$red") : color->r(), - g ? DARG_U_BYTE("$green") : color->g(), - b ? DARG_U_BYTE("$blue") : color->b(), - a ? DARG_U_BYTE("$alpha") : color->a()); - } - if (hsl) { - HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); - if (h) hsl_struct.h = std::fmod(h->value(), 360.0); - if (s) hsl_struct.s = DARG_U_PRCT("$saturation"); - if (l) hsl_struct.l = DARG_U_PRCT("$lightness"); - double alpha = a ? DARG_U_FACT("$alpha") : color->a(); - return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); - } - if (a) { - double alpha = DARG_U_FACT("$alpha"); - return SASS_MEMORY_NEW(Color, - pstate, - color->r(), - color->g(), - color->b(), - alpha); - } - error("not enough arguments for `change-color'", pstate, traces); - // unreachable - return color; - } - - template - static double cap_channel(double c) { - if (c > range) return range; - else if (c < 0) return 0; - else return c; - } - - Signature ie_hex_str_sig = "ie-hex-str($color)"; - BUILT_IN(ie_hex_str) - { - Color_Ptr c = ARG("$color", Color); - double r = cap_channel<0xff>(c->r()); - double g = cap_channel<0xff>(c->g()); - double b = cap_channel<0xff>(c->b()); - double a = cap_channel<1> (c->a()) * 255; - - std::stringstream ss; - ss << '#' << std::setw(2) << std::setfill('0'); - ss << std::hex << std::setw(2) << static_cast(Sass::round(a, ctx.c_options.precision)); - ss << std::hex << std::setw(2) << static_cast(Sass::round(r, ctx.c_options.precision)); - ss << std::hex << std::setw(2) << static_cast(Sass::round(g, ctx.c_options.precision)); - ss << std::hex << std::setw(2) << static_cast(Sass::round(b, ctx.c_options.precision)); - - std::string result(ss.str()); - for (size_t i = 0, L = result.length(); i < L; ++i) { - result[i] = std::toupper(result[i]); - } - return SASS_MEMORY_NEW(String_Quoted, pstate, result); - } - - /////////////////// - // STRING FUNCTIONS - /////////////////// - - Signature unquote_sig = "unquote($string)"; - BUILT_IN(sass_unquote) - { - AST_Node_Obj arg = env["$string"]; - if (String_Quoted_Ptr string_quoted = Cast(arg)) { - String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value()); - // remember if the string was quoted (color tokens) - result->is_delayed(true); // delay colors - return result; - } - else if (String_Constant_Ptr str = Cast(arg)) { - return str; - } - else if (Expression_Ptr ex = Cast(arg)) { - Sass_Output_Style oldstyle = ctx.c_options.output_style; - ctx.c_options.output_style = SASS_STYLE_NESTED; - std::string val(arg->to_string(ctx.c_options)); - val = Cast(arg) ? "null" : val; - ctx.c_options.output_style = oldstyle; - - deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate); - return ex; - } - throw std::runtime_error("Invalid Data Type for unquote"); - } - - Signature quote_sig = "quote($string)"; - BUILT_IN(sass_quote) - { - AST_Node_Obj arg = env["$string"]; - // only set quote mark to true if already a string - if (String_Quoted_Ptr qstr = Cast(arg)) { - qstr->quote_mark('*'); - return qstr; - } - // all other nodes must be converted to a string node - std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); - String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str); - result->quote_mark('*'); - return result; - } - - - Signature str_length_sig = "str-length($string)"; - BUILT_IN(str_length) - { - size_t len = std::string::npos; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - len = UTF_8::code_point_count(s->value(), 0, s->value().size()); - - } - // handle any invalid utf8 errors - // other errors will be re-thrown - catch (...) { handle_utf8_error(pstate, traces); } - // return something even if we had an error (-1) - return SASS_MEMORY_NEW(Number, pstate, (double)len); - } - - Signature str_insert_sig = "str-insert($string, $insert, $index)"; - BUILT_IN(str_insert) - { - std::string str; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - str = s->value(); - str = unquote(str); - String_Constant_Ptr i = ARG("$insert", String_Constant); - std::string ins = i->value(); - ins = unquote(ins); - double index = ARGVAL("$index"); - size_t len = UTF_8::code_point_count(str, 0, str.size()); - - if (index > 0 && index <= len) { - // positive and within string length - str.insert(UTF_8::offset_at_position(str, static_cast(index) - 1), ins); - } - else if (index > len) { - // positive and past string length - str += ins; - } - else if (index == 0) { - str = ins + str; - } - else if (std::abs(index) <= len) { - // negative and within string length - index += len + 1; - str.insert(UTF_8::offset_at_position(str, static_cast(index)), ins); - } - else { - // negative and past string length - str = ins + str; - } - - if (String_Quoted_Ptr ss = Cast(s)) { - if (ss->quote_mark()) str = quote(str); - } - } - // handle any invalid utf8 errors - // other errors will be re-thrown - catch (...) { handle_utf8_error(pstate, traces); } - return SASS_MEMORY_NEW(String_Quoted, pstate, str); - } - - Signature str_index_sig = "str-index($string, $substring)"; - BUILT_IN(str_index) - { - size_t index = std::string::npos; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - String_Constant_Ptr t = ARG("$substring", String_Constant); - std::string str = s->value(); - str = unquote(str); - std::string substr = t->value(); - substr = unquote(substr); - - size_t c_index = str.find(substr); - if(c_index == std::string::npos) { - return SASS_MEMORY_NEW(Null, pstate); - } - index = UTF_8::code_point_count(str, 0, c_index) + 1; - } - // handle any invalid utf8 errors - // other errors will be re-thrown - catch (...) { handle_utf8_error(pstate, traces); } - // return something even if we had an error (-1) - return SASS_MEMORY_NEW(Number, pstate, (double)index); - } - - Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)"; - BUILT_IN(str_slice) - { - std::string newstr; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - double start_at = ARGVAL("$start-at"); - double end_at = ARGVAL("$end-at"); - String_Quoted_Ptr ss = Cast(s); - - std::string str = unquote(s->value()); - - size_t size = utf8::distance(str.begin(), str.end()); - - if (!Cast(env["$end-at"])) { - end_at = -1; - } - - if (end_at == 0 || (end_at + size) < 0) { - if (ss && ss->quote_mark()) newstr = quote(""); - return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); - } - - if (end_at < 0) { - end_at += size + 1; - if (end_at == 0) end_at = 1; - } - if (end_at > size) { end_at = (double)size; } - if (start_at < 0) { - start_at += size + 1; - if (start_at < 0) start_at = 0; - } - else if (start_at == 0) { ++ start_at; } - - if (start_at <= end_at) - { - std::string::iterator start = str.begin(); - utf8::advance(start, start_at - 1, str.end()); - std::string::iterator end = start; - utf8::advance(end, end_at - start_at + 1, str.end()); - newstr = std::string(start, end); - } - if (ss) { - if(ss->quote_mark()) newstr = quote(newstr); - } - } - // handle any invalid utf8 errors - // other errors will be re-thrown - catch (...) { handle_utf8_error(pstate, traces); } - return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); - } - - Signature to_upper_case_sig = "to-upper-case($string)"; - BUILT_IN(to_upper_case) - { - String_Constant_Ptr s = ARG("$string", String_Constant); - std::string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (Sass::Util::isAscii(str[i])) { - str[i] = std::toupper(str[i]); - } - } - - if (String_Quoted_Ptr ss = Cast(s)) { - String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); - cpy->value(str); - return cpy; - } else { - return SASS_MEMORY_NEW(String_Quoted, pstate, str); - } - } - - Signature to_lower_case_sig = "to-lower-case($string)"; - BUILT_IN(to_lower_case) - { - String_Constant_Ptr s = ARG("$string", String_Constant); - std::string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (Sass::Util::isAscii(str[i])) { - str[i] = std::tolower(str[i]); - } - } - - if (String_Quoted_Ptr ss = Cast(s)) { - String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); - cpy->value(str); - return cpy; - } else { - return SASS_MEMORY_NEW(String_Quoted, pstate, str); - } - } - - /////////////////// - // NUMBER FUNCTIONS - /////////////////// - - Signature percentage_sig = "percentage($number)"; - BUILT_IN(percentage) - { - Number_Obj n = ARGN("$number"); - if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces); - return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%"); - } - - Signature round_sig = "round($number)"; - BUILT_IN(round) - { - Number_Obj r = ARGN("$number"); - r->value(Sass::round(r->value(), ctx.c_options.precision)); - r->pstate(pstate); - return r.detach(); - } - - Signature ceil_sig = "ceil($number)"; - BUILT_IN(ceil) - { - Number_Obj r = ARGN("$number"); - r->value(std::ceil(r->value())); - r->pstate(pstate); - return r.detach(); - } - - Signature floor_sig = "floor($number)"; - BUILT_IN(floor) - { - Number_Obj r = ARGN("$number"); - r->value(std::floor(r->value())); - r->pstate(pstate); - return r.detach(); - } - - Signature abs_sig = "abs($number)"; - BUILT_IN(abs) - { - Number_Obj r = ARGN("$number"); - r->value(std::abs(r->value())); - r->pstate(pstate); - return r.detach(); - } - - Signature min_sig = "min($numbers...)"; - BUILT_IN(min) - { - List_Ptr arglist = ARG("$numbers", List); - Number_Obj least = NULL; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Expression_Obj val = arglist->value_at_index(i); - Number_Obj xi = Cast(val); - if (!xi) { - error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces); - } - if (least) { - if (*xi < *least) least = xi; - } else least = xi; - } - return least.detach(); - } - - Signature max_sig = "max($numbers...)"; - BUILT_IN(max) - { - List_Ptr arglist = ARG("$numbers", List); - Number_Obj greatest = NULL; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Expression_Obj val = arglist->value_at_index(i); - Number_Obj xi = Cast(val); - if (!xi) { - error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces); - } - if (greatest) { - if (*greatest < *xi) greatest = xi; - } else greatest = xi; - } - return greatest.detach(); - } - - Signature random_sig = "random($limit:false)"; - BUILT_IN(random) - { - AST_Node_Obj arg = env["$limit"]; - Value_Ptr v = Cast(arg); - Number_Ptr l = Cast(arg); - Boolean_Ptr b = Cast(arg); - if (l) { - double lv = l->value(); - if (lv < 1) { - stringstream err; - err << "$limit " << lv << " must be greater than or equal to 1 for `random'"; - error(err.str(), pstate, traces); - } - bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON; - if (!eq_int) { - stringstream err; - err << "Expected $limit to be an integer but got " << lv << " for `random'"; - error(err.str(), pstate, traces); - } - std::uniform_real_distribution<> distributor(1, lv + 1); - uint_fast32_t distributed = static_cast(distributor(rand)); - return SASS_MEMORY_NEW(Number, pstate, (double)distributed); - } - else if (b) { - std::uniform_real_distribution<> distributor(0, 1); - double distributed = static_cast(distributor(rand)); - return SASS_MEMORY_NEW(Number, pstate, distributed); - } else if (v) { - traces.push_back(Backtrace(pstate)); - throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v); - } else { - traces.push_back(Backtrace(pstate)); - throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number"); - } - } - - ///////////////// - // LIST FUNCTIONS - ///////////////// - - Signature length_sig = "length($list)"; - BUILT_IN(length) - { - if (Selector_List_Ptr sl = Cast(env["$list"])) { - return SASS_MEMORY_NEW(Number, pstate, (double)sl->length()); - } - Expression_Ptr v = ARG("$list", Expression); - if (v->concrete_type() == Expression::MAP) { - Map_Ptr map = Cast(env["$list"]); - return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1)); - } - if (v->concrete_type() == Expression::SELECTOR) { - if (Compound_Selector_Ptr h = Cast(v)) { - return SASS_MEMORY_NEW(Number, pstate, (double)h->length()); - } else if (Selector_List_Ptr ls = Cast(v)) { - return SASS_MEMORY_NEW(Number, pstate, (double)ls->length()); - } else { - return SASS_MEMORY_NEW(Number, pstate, 1); - } - } - - List_Ptr list = Cast(env["$list"]); - return SASS_MEMORY_NEW(Number, - pstate, - (double)(list ? list->size() : 1)); - } - - Signature nth_sig = "nth($list, $n)"; - BUILT_IN(nth) - { - double nr = ARGVAL("$n"); - Map_Ptr m = Cast(env["$list"]); - if (Selector_List_Ptr sl = Cast(env["$list"])) { - size_t len = m ? m->length() : sl->length(); - bool empty = m ? m->empty() : sl->empty(); - if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); - double index = std::floor(nr < 0 ? len + nr : nr - 1); - if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); - // return (*sl)[static_cast(index)]; - Listize listize; - return (*sl)[static_cast(index)]->perform(&listize); - } - List_Obj l = Cast(env["$list"]); - if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces); - // if the argument isn't a list, then wrap it in a singleton list - if (!m && !l) { - l = SASS_MEMORY_NEW(List, pstate, 1); - l->append(ARG("$list", Expression)); - } - size_t len = m ? m->length() : l->length(); - bool empty = m ? m->empty() : l->empty(); - if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); - double index = std::floor(nr < 0 ? len + nr : nr - 1); - if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); - - if (m) { - l = SASS_MEMORY_NEW(List, pstate, 1); - l->append(m->keys()[static_cast(index)]); - l->append(m->at(m->keys()[static_cast(index)])); - return l.detach(); - } - else { - Expression_Obj rv = l->value_at_index(static_cast(index)); - rv->set_delayed(false); - return rv.detach(); - } - } - - Signature set_nth_sig = "set-nth($list, $n, $value)"; - BUILT_IN(set_nth) - { - Map_Obj m = Cast(env["$list"]); - List_Obj l = Cast(env["$list"]); - Number_Obj n = ARG("$n", Number); - Expression_Obj v = ARG("$value", Expression); - if (!l) { - l = SASS_MEMORY_NEW(List, pstate, 1); - l->append(ARG("$list", Expression)); - } - if (m) { - l = m->to_list(pstate); - } - if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); - double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); - if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); - List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed()); - for (size_t i = 0, L = l->length(); i < L; ++i) { - result->append(((i == index) ? v : (*l)[i])); - } - return result; - } - - Signature index_sig = "index($list, $value)"; - BUILT_IN(index) - { - Map_Obj m = Cast(env["$list"]); - List_Obj l = Cast(env["$list"]); - Expression_Obj v = ARG("$value", Expression); - if (!l) { - l = SASS_MEMORY_NEW(List, pstate, 1); - l->append(ARG("$list", Expression)); - } - if (m) { - l = m->to_list(pstate); - } - for (size_t i = 0, L = l->length(); i < L; ++i) { - if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1)); - } - return SASS_MEMORY_NEW(Null, pstate); - } - - Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)"; - BUILT_IN(join) - { - Map_Obj m1 = Cast(env["$list1"]); - Map_Obj m2 = Cast(env["$list2"]); - List_Obj l1 = Cast(env["$list1"]); - List_Obj l2 = Cast(env["$list2"]); - String_Constant_Obj sep = ARG("$separator", String_Constant); - enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); - Value* bracketed = ARG("$bracketed", Value); - bool is_bracketed = (l1 ? l1->is_bracketed() : false); - if (!l1) { - l1 = SASS_MEMORY_NEW(List, pstate, 1); - l1->append(ARG("$list1", Expression)); - sep_val = (l2 ? l2->separator() : SASS_SPACE); - is_bracketed = (l2 ? l2->is_bracketed() : false); - } - if (!l2) { - l2 = SASS_MEMORY_NEW(List, pstate, 1); - l2->append(ARG("$list2", Expression)); - } - if (m1) { - l1 = m1->to_list(pstate); - sep_val = SASS_COMMA; - } - if (m2) { - l2 = m2->to_list(pstate); - } - size_t len = l1->length() + l2->length(); - std::string sep_str = unquote(sep->value()); - if (sep_str == "space") sep_val = SASS_SPACE; - else if (sep_str == "comma") sep_val = SASS_COMMA; - else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); - String_Constant_Obj bracketed_as_str = Cast(bracketed); - bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto"; - if (!bracketed_is_auto) { - is_bracketed = !bracketed->is_false(); - } - List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed); - result->concat(l1); - result->concat(l2); - return result.detach(); - } - - Signature append_sig = "append($list, $val, $separator: auto)"; - BUILT_IN(append) - { - Map_Obj m = Cast(env["$list"]); - List_Obj l = Cast(env["$list"]); - Expression_Obj v = ARG("$val", Expression); - if (Selector_List_Ptr sl = Cast(env["$list"])) { - Listize listize; - l = Cast(sl->perform(&listize)); - } - String_Constant_Obj sep = ARG("$separator", String_Constant); - if (!l) { - l = SASS_MEMORY_NEW(List, pstate, 1); - l->append(ARG("$list", Expression)); - } - if (m) { - l = m->to_list(pstate); - } - List_Ptr result = SASS_MEMORY_COPY(l); - std::string sep_str(unquote(sep->value())); - if (sep_str != "auto") { // check default first - if (sep_str == "space") result->separator(SASS_SPACE); - else if (sep_str == "comma") result->separator(SASS_COMMA); - else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); - } - if (l->is_arglist()) { - result->append(SASS_MEMORY_NEW(Argument, - v->pstate(), - v, - "", - false, - false)); - - } else { - result->append(v); - } - return result; - } - - Signature zip_sig = "zip($lists...)"; - BUILT_IN(zip) - { - List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List)); - size_t shortest = 0; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - List_Obj ith = Cast(arglist->value_at_index(i)); - Map_Obj mith = Cast(arglist->value_at_index(i)); - if (!ith) { - if (mith) { - ith = mith->to_list(pstate); - } else { - ith = SASS_MEMORY_NEW(List, pstate, 1); - ith->append(arglist->value_at_index(i)); - } - if (arglist->is_arglist()) { - Argument_Obj arg = (Argument_Ptr)(arglist->at(i).ptr()); // XXX - arg->value(ith); - } else { - (*arglist)[i] = ith; - } - } - shortest = (i ? std::min(shortest, ith->length()) : ith->length()); - } - List_Ptr zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA); - size_t L = arglist->length(); - for (size_t i = 0; i < shortest; ++i) { - List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L); - for (size_t j = 0; j < L; ++j) { - zipper->append(Cast(arglist->value_at_index(j))->at(i)); - } - zippers->append(zipper); - } - return zippers; - } - - Signature list_separator_sig = "list_separator($list)"; - BUILT_IN(list_separator) - { - List_Obj l = Cast(env["$list"]); - if (!l) { - l = SASS_MEMORY_NEW(List, pstate, 1); - l->append(ARG("$list", Expression)); - } - return SASS_MEMORY_NEW(String_Quoted, - pstate, - l->separator() == SASS_COMMA ? "comma" : "space"); - } - - ///////////////// - // MAP FUNCTIONS - ///////////////// - - Signature map_get_sig = "map-get($map, $key)"; - BUILT_IN(map_get) - { - // leaks for "map-get((), foo)" if not Obj - // investigate why this is (unexpected) - Map_Obj m = ARGM("$map", Map, ctx); - Expression_Obj v = ARG("$key", Expression); - try { - Expression_Obj val = m->at(v); - if (!val) return SASS_MEMORY_NEW(Null, pstate); - val->set_delayed(false); - return val.detach(); - } catch (const std::out_of_range&) { - return SASS_MEMORY_NEW(Null, pstate); - } - catch (...) { throw; } - } - - Signature map_has_key_sig = "map-has-key($map, $key)"; - BUILT_IN(map_has_key) - { - Map_Obj m = ARGM("$map", Map, ctx); - Expression_Obj v = ARG("$key", Expression); - return SASS_MEMORY_NEW(Boolean, pstate, m->has(v)); - } - - Signature map_keys_sig = "map-keys($map)"; - BUILT_IN(map_keys) - { - Map_Obj m = ARGM("$map", Map, ctx); - List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); - for ( auto key : m->keys()) { - result->append(key); - } - return result; - } - - Signature map_values_sig = "map-values($map)"; - BUILT_IN(map_values) - { - Map_Obj m = ARGM("$map", Map, ctx); - List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); - for ( auto key : m->keys()) { - result->append(m->at(key)); - } - return result; - } - - Signature map_merge_sig = "map-merge($map1, $map2)"; - BUILT_IN(map_merge) - { - Map_Obj m1 = ARGM("$map1", Map, ctx); - Map_Obj m2 = ARGM("$map2", Map, ctx); - - size_t len = m1->length() + m2->length(); - Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len); - // concat not implemented for maps - *result += m1; - *result += m2; - return result; - } - - Signature map_remove_sig = "map-remove($map, $keys...)"; - BUILT_IN(map_remove) - { - bool remove; - Map_Obj m = ARGM("$map", Map, ctx); - List_Obj arglist = ARG("$keys", List); - Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, 1); - for (auto key : m->keys()) { - remove = false; - for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) { - remove = Operators::eq(key, arglist->value_at_index(j)); - } - if (!remove) *result << std::make_pair(key, m->at(key)); - } - return result; - } - - Signature keywords_sig = "keywords($args)"; - BUILT_IN(keywords) - { - List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy - Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1); - for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { - Expression_Obj obj = arglist->at(i); - Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX - std::string name = std::string(arg->name()); - name = name.erase(0, 1); // sanitize name (remove dollar sign) - *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted, - pstate, name), - arg->value()); - } - return result.detach(); - } - - ////////////////////////// - // INTROSPECTION FUNCTIONS - ////////////////////////// - - Signature type_of_sig = "type-of($value)"; - BUILT_IN(type_of) - { - Expression_Ptr v = ARG("$value", Expression); - return SASS_MEMORY_NEW(String_Quoted, pstate, v->type()); - } - - Signature unit_sig = "unit($number)"; - BUILT_IN(unit) - { - Number_Obj arg = ARGN("$number"); - std::string str(quote(arg->unit(), '"')); - return SASS_MEMORY_NEW(String_Quoted, pstate, str); - } - - Signature unitless_sig = "unitless($number)"; - BUILT_IN(unitless) - { - Number_Obj arg = ARGN("$number"); - bool unitless = arg->is_unitless(); - return SASS_MEMORY_NEW(Boolean, pstate, unitless); - } - - Signature comparable_sig = "comparable($number-1, $number-2)"; - BUILT_IN(comparable) - { - Number_Obj n1 = ARGN("$number-1"); - Number_Obj n2 = ARGN("$number-2"); - if (n1->is_unitless() || n2->is_unitless()) { - return SASS_MEMORY_NEW(Boolean, pstate, true); - } - // normalize into main units - n1->normalize(); n2->normalize(); - Units &lhs_unit = *n1, &rhs_unit = *n2; - bool is_comparable = (lhs_unit == rhs_unit); - return SASS_MEMORY_NEW(Boolean, pstate, is_comparable); - } - - Signature variable_exists_sig = "variable-exists($name)"; - BUILT_IN(variable_exists) - { - std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); - - if(d_env.has("$"+s)) { - return SASS_MEMORY_NEW(Boolean, pstate, true); - } - else { - return SASS_MEMORY_NEW(Boolean, pstate, false); - } - } - - Signature global_variable_exists_sig = "global-variable-exists($name)"; - BUILT_IN(global_variable_exists) - { - std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); - - if(d_env.has_global("$"+s)) { - return SASS_MEMORY_NEW(Boolean, pstate, true); - } - else { - return SASS_MEMORY_NEW(Boolean, pstate, false); - } - } - - Signature function_exists_sig = "function-exists($name)"; - BUILT_IN(function_exists) - { - String_Constant_Ptr ss = Cast(env["$name"]); - if (!ss) { - error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces); - } - - std::string name = Util::normalize_underscores(unquote(ss->value())); - - if(d_env.has_global(name+"[f]")) { - return SASS_MEMORY_NEW(Boolean, pstate, true); - } - else { - return SASS_MEMORY_NEW(Boolean, pstate, false); - } - } - - Signature mixin_exists_sig = "mixin-exists($name)"; - BUILT_IN(mixin_exists) - { - std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); - - if(d_env.has_global(s+"[m]")) { - return SASS_MEMORY_NEW(Boolean, pstate, true); - } - else { - return SASS_MEMORY_NEW(Boolean, pstate, false); - } - } - - Signature feature_exists_sig = "feature-exists($name)"; - BUILT_IN(feature_exists) - { - std::string s = unquote(ARG("$name", String_Constant)->value()); - - if(features.find(s) == features.end()) { - return SASS_MEMORY_NEW(Boolean, pstate, false); - } - else { - return SASS_MEMORY_NEW(Boolean, pstate, true); - } - } - - Signature call_sig = "call($name, $args...)"; - BUILT_IN(call) - { - std::string name; - Function_Ptr ff = Cast(env["$name"]); - String_Constant_Ptr ss = Cast(env["$name"]); - - if (ss) { - name = Util::normalize_underscores(unquote(ss->value())); - std::cerr << "DEPRECATION WARNING: "; - std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl; - std::cerr << "in Sass 4.0. Use call(get-function(" + quote(name) + ")) instead." << std::endl; - std::cerr << std::endl; - } else if (ff) { - name = ff->name(); - } - - List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); - - Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); - // std::string full_name(name + "[f]"); - // Definition_Ptr def = d_env.has(full_name) ? Cast((d_env)[full_name]) : 0; - // Parameters_Ptr params = def ? def->parameters() : 0; - // size_t param_size = params ? params->length() : 0; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Expression_Obj expr = arglist->value_at_index(i); - // if (params && params->has_rest_parameter()) { - // Parameter_Obj p = param_size > i ? (*params)[i] : 0; - // List_Ptr list = Cast(expr); - // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; - // } - if (arglist->is_arglist()) { - Expression_Obj obj = arglist->at(i); - Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX - args->append(SASS_MEMORY_NEW(Argument, - pstate, - expr, - arg ? arg->name() : "", - arg ? arg->is_rest_argument() : false, - arg ? arg->is_keyword_argument() : false)); - } else { - args->append(SASS_MEMORY_NEW(Argument, pstate, expr)); - } - } - Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args); - Expand expand(ctx, &d_env, &selector_stack); - func->via_call(true); // calc invoke is allowed - if (ff) func->func(ff); - return func->perform(&expand.eval); - } - - //////////////////// - // BOOLEAN FUNCTIONS - //////////////////// - - Signature not_sig = "not($value)"; - BUILT_IN(sass_not) - { - return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false()); - } - - Signature if_sig = "if($condition, $if-true, $if-false)"; - // BUILT_IN(sass_if) - // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } - BUILT_IN(sass_if) - { - Expand expand(ctx, &d_env, &selector_stack); - Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval); - bool is_true = !cond->is_false(); - Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression); - res = res->perform(&expand.eval); - res->set_delayed(false); // clone? - return res.detach(); - } - - ////////////////////////// - // MISCELLANEOUS FUNCTIONS - ////////////////////////// - - // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String) - // unquoted_string(value.to_sass) - - Signature inspect_sig = "inspect($value)"; - BUILT_IN(inspect) - { - Expression_Ptr v = ARG("$value", Expression); - if (v->concrete_type() == Expression::NULL_VAL) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "null"); - } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) { - return SASS_MEMORY_NEW(String_Quoted, pstate, "false"); - } else if (v->concrete_type() == Expression::STRING) { - return v; - } else { - // ToDo: fix to_sass for nested parentheses - Sass_Output_Style old_style; - old_style = ctx.c_options.output_style; - ctx.c_options.output_style = TO_SASS; - Emitter emitter(ctx.c_options); - Inspect i(emitter); - i.in_declaration = false; - v->perform(&i); - ctx.c_options.output_style = old_style; - return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer()); - } - // return v; - } - Signature selector_nest_sig = "selector-nest($selectors...)"; - BUILT_IN(selector_nest) - { - List_Ptr arglist = ARG("$selectors", List); - - // Not enough parameters - if( arglist->length() == 0 ) - error("$selectors: At least one selector must be passed for `selector-nest'", pstate, traces); - - // Parse args into vector of selectors - std::vector parsedSelectors; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Expression_Obj exp = Cast(arglist->value_at_index(i)); - if (exp->concrete_type() == Expression::NULL_VAL) { - std::stringstream msg; - msg << "$selectors: null is not a valid selector: it must be a string,\n"; - msg << "a list of strings, or a list of lists of strings for 'selector-nest'"; - error(msg.str(), pstate, traces); - } - if (String_Constant_Obj str = Cast(exp)) { - str->quote_mark(0); - } - std::string exp_src = exp->to_string(ctx.c_options); - Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces); - parsedSelectors.push_back(sel); - } - - // Nothing to do - if( parsedSelectors.empty() ) { - return SASS_MEMORY_NEW(Null, pstate); - } - - // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. - std::vector::iterator itr = parsedSelectors.begin(); - Selector_List_Obj result = *itr; - ++itr; - - for(;itr != parsedSelectors.end(); ++itr) { - Selector_List_Obj child = *itr; - std::vector exploded; - selector_stack.push_back(result); - Selector_List_Obj rv = child->resolve_parent_refs(selector_stack, traces); - selector_stack.pop_back(); - for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) { - exploded.push_back((*rv)[m]); - } - result->elements(exploded); - } - - Listize listize; - return result->perform(&listize); - } - - Signature selector_append_sig = "selector-append($selectors...)"; - BUILT_IN(selector_append) - { - List_Ptr arglist = ARG("$selectors", List); - - // Not enough parameters - if( arglist->length() == 0 ) - error("$selectors: At least one selector must be passed for `selector-append'", pstate, traces); - - // Parse args into vector of selectors - std::vector parsedSelectors; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Expression_Obj exp = Cast(arglist->value_at_index(i)); - if (exp->concrete_type() == Expression::NULL_VAL) { - std::stringstream msg; - msg << "$selectors: null is not a valid selector: it must be a string,\n"; - msg << "a list of strings, or a list of lists of strings for 'selector-append'"; - error(msg.str(), pstate, traces); - } - if (String_Constant_Ptr str = Cast(exp)) { - str->quote_mark(0); - } - std::string exp_src = exp->to_string(); - Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces); - parsedSelectors.push_back(sel); - } - - // Nothing to do - if( parsedSelectors.empty() ) { - return SASS_MEMORY_NEW(Null, pstate); - } - - // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. - std::vector::iterator itr = parsedSelectors.begin(); - Selector_List_Obj result = *itr; - ++itr; - - for(;itr != parsedSelectors.end(); ++itr) { - Selector_List_Obj child = *itr; - std::vector newElements; - - // For every COMPLEX_SELECTOR in `result` - // For every COMPLEX_SELECTOR in `child` - // let parentSeqClone equal a copy of result->elements[i] - // let childSeq equal child->elements[j] - // Append all of childSeq head elements into parentSeqClone - // Set the innermost tail of parentSeqClone, to childSeq's tail - // Replace result->elements with newElements - for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { - for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { - Complex_Selector_Obj parentSeqClone = SASS_MEMORY_CLONE((*result)[i]); - Complex_Selector_Obj childSeq = (*child)[j]; - Complex_Selector_Obj base = childSeq->tail(); - - // Must be a simple sequence - if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { - std::string msg("Can't append \""); - msg += childSeq->to_string(); - msg += "\" to \""; - msg += parentSeqClone->to_string(); - msg += "\" for `selector-append'"; - error(msg, pstate, traces); - } - - // Cannot be a Universal selector - Element_Selector_Obj pType = Cast(childSeq->head()->first()); - if(pType && pType->name() == "*") { - std::string msg("Can't append \""); - msg += childSeq->to_string(); - msg += "\" to \""; - msg += parentSeqClone->to_string(); - msg += "\" for `selector-append'"; - error(msg, pstate, traces); - } - - // TODO: Add check for namespace stuff - - // append any selectors in childSeq's head - parentSeqClone->innermost()->head()->concat(base->head()); - - // Set parentSeqClone new tail - parentSeqClone->innermost()->tail( base->tail() ); - - newElements.push_back(parentSeqClone); - } - } - - result->elements(newElements); - } - - Listize listize; - return result->perform(&listize); - } - - Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; - BUILT_IN(selector_unify) - { - Selector_List_Obj selector1 = ARGSEL("$selector1", Selector_List_Obj, p_contextualize); - Selector_List_Obj selector2 = ARGSEL("$selector2", Selector_List_Obj, p_contextualize); - - Selector_List_Obj result = selector1->unify_with(selector2); - Listize listize; - return result->perform(&listize); - } - - Signature simple_selectors_sig = "simple-selectors($selector)"; - BUILT_IN(simple_selectors) - { - Compound_Selector_Obj sel = ARGSEL("$selector", Compound_Selector_Obj, p_contextualize); - - List_Ptr l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); - - for (size_t i = 0, L = sel->length(); i < L; ++i) { - Simple_Selector_Obj ss = (*sel)[i]; - std::string ss_string = ss->to_string() ; - - l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string)); - } - - return l; - } - - Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; - BUILT_IN(selector_extend) - { - Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize); - Selector_List_Obj extendee = ARGSEL("$extendee", Selector_List_Obj, p_contextualize); - Selector_List_Obj extender = ARGSEL("$extender", Selector_List_Obj, p_contextualize); - - Subset_Map subset_map; - extender->populate_extends(extendee, subset_map); - Extend extend(subset_map); - - Selector_List_Obj result = extend.extendSelectorList(selector, false); - - Listize listize; - return result->perform(&listize); - } - - Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; - BUILT_IN(selector_replace) - { - Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize); - Selector_List_Obj original = ARGSEL("$original", Selector_List_Obj, p_contextualize); - Selector_List_Obj replacement = ARGSEL("$replacement", Selector_List_Obj, p_contextualize); - Subset_Map subset_map; - replacement->populate_extends(original, subset_map); - Extend extend(subset_map); - - Selector_List_Obj result = extend.extendSelectorList(selector, true); - - Listize listize; - return result->perform(&listize); - } - - Signature selector_parse_sig = "selector-parse($selector)"; - BUILT_IN(selector_parse) - { - Selector_List_Obj sel = ARGSEL("$selector", Selector_List_Obj, p_contextualize); - - Listize listize; - return sel->perform(&listize); - } - - Signature is_superselector_sig = "is-superselector($super, $sub)"; - BUILT_IN(is_superselector) - { - Selector_List_Obj sel_sup = ARGSEL("$super", Selector_List_Obj, p_contextualize); - Selector_List_Obj sel_sub = ARGSEL("$sub", Selector_List_Obj, p_contextualize); - bool result = sel_sup->is_superselector_of(sel_sub); - return SASS_MEMORY_NEW(Boolean, pstate, result); - } - - Signature unique_id_sig = "unique-id()"; - BUILT_IN(unique_id) - { - std::stringstream ss; - std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8 - uint_fast32_t distributed = static_cast(distributor(rand)); - ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed; - return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str()); - } - - Signature is_bracketed_sig = "is-bracketed($list)"; - BUILT_IN(is_bracketed) - { - Value_Obj value = ARG("$list", Value); - List_Obj list = Cast(value); - return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed()); - } - - Signature content_exists_sig = "content-exists()"; - BUILT_IN(content_exists) - { - if (!d_env.has_global("is_in_mixin")) { - error("Cannot call content-exists() except within a mixin.", pstate, traces); - } - return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]")); - } - - Signature get_function_sig = "get-function($name, $css: false)"; - BUILT_IN(get_function) - { - String_Constant_Ptr ss = Cast(env["$name"]); - if (!ss) { - error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces); - } - - std::string name = Util::normalize_underscores(unquote(ss->value())); - std::string full_name = name + "[f]"; - - Boolean_Obj css = ARG("$css", Boolean); - if (!css->is_false()) { - Definition_Ptr def = SASS_MEMORY_NEW(Definition, - pstate, - name, - SASS_MEMORY_NEW(Parameters, pstate), - SASS_MEMORY_NEW(Block, pstate, 0, false), - Definition::FUNCTION); - return SASS_MEMORY_NEW(Function, pstate, def, true); - } - - - if (!d_env.has_global(full_name)) { - error("Function not found: " + name, pstate, traces); - } - - Definition_Ptr def = Cast(d_env[full_name]); - return SASS_MEMORY_NEW(Function, pstate, def, false); - } } } diff --git a/src/functions.hpp b/src/functions.hpp index 7019be934..c91000f66 100644 --- a/src/functions.hpp +++ b/src/functions.hpp @@ -1,198 +1,12 @@ #ifndef SASS_FUNCTIONS_H #define SASS_FUNCTIONS_H -#include "listize.hpp" -#include "position.hpp" -#include "environment.hpp" -#include "ast_fwd_decl.hpp" -#include "sass/functions.h" - -#define BUILT_IN(name) Expression_Ptr \ -name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtraces traces, std::vector selector_stack) - namespace Sass { - struct Backtrace; - typedef const char* Signature; - typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector); - - Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx); - Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx); - - std::string function_name(Signature); namespace Functions { - extern Signature rgb_sig; - extern Signature rgba_4_sig; - extern Signature rgba_2_sig; - extern Signature red_sig; - extern Signature green_sig; - extern Signature blue_sig; - extern Signature mix_sig; - extern Signature hsl_sig; - extern Signature hsla_sig; - extern Signature hue_sig; - extern Signature saturation_sig; - extern Signature lightness_sig; - extern Signature adjust_hue_sig; - extern Signature lighten_sig; - extern Signature darken_sig; - extern Signature saturate_sig; - extern Signature desaturate_sig; - extern Signature grayscale_sig; - extern Signature complement_sig; - extern Signature invert_sig; - extern Signature alpha_sig; - extern Signature opacity_sig; - extern Signature opacify_sig; - extern Signature fade_in_sig; - extern Signature transparentize_sig; - extern Signature fade_out_sig; - extern Signature adjust_color_sig; - extern Signature scale_color_sig; - extern Signature change_color_sig; - extern Signature ie_hex_str_sig; - extern Signature unquote_sig; - extern Signature quote_sig; - extern Signature str_length_sig; - extern Signature str_insert_sig; - extern Signature str_index_sig; - extern Signature str_slice_sig; - extern Signature to_upper_case_sig; - extern Signature to_lower_case_sig; - extern Signature percentage_sig; - extern Signature round_sig; - extern Signature ceil_sig; - extern Signature floor_sig; - extern Signature abs_sig; - extern Signature min_sig; - extern Signature max_sig; - extern Signature inspect_sig; - extern Signature random_sig; - extern Signature length_sig; - extern Signature nth_sig; - extern Signature index_sig; - extern Signature join_sig; - extern Signature append_sig; - extern Signature zip_sig; - extern Signature list_separator_sig; - extern Signature type_of_sig; - extern Signature unit_sig; - extern Signature unitless_sig; - extern Signature comparable_sig; - extern Signature variable_exists_sig; - extern Signature global_variable_exists_sig; - extern Signature function_exists_sig; - extern Signature mixin_exists_sig; - extern Signature feature_exists_sig; - extern Signature call_sig; - extern Signature not_sig; - extern Signature if_sig; - extern Signature map_get_sig; - extern Signature map_merge_sig; - extern Signature map_remove_sig; - extern Signature map_keys_sig; - extern Signature map_values_sig; - extern Signature map_has_key_sig; - extern Signature keywords_sig; - extern Signature set_nth_sig; - extern Signature unique_id_sig; - extern Signature selector_nest_sig; - extern Signature selector_append_sig; - extern Signature selector_extend_sig; - extern Signature selector_replace_sig; - extern Signature selector_unify_sig; - extern Signature is_superselector_sig; - extern Signature simple_selectors_sig; - extern Signature selector_parse_sig; - extern Signature is_bracketed_sig; - extern Signature content_exists_sig; - extern Signature get_function_sig; - - BUILT_IN(rgb); - BUILT_IN(rgba_4); - BUILT_IN(rgba_2); - BUILT_IN(red); - BUILT_IN(green); - BUILT_IN(blue); - BUILT_IN(mix); - BUILT_IN(hsl); - BUILT_IN(hsla); - BUILT_IN(hue); - BUILT_IN(saturation); - BUILT_IN(lightness); - BUILT_IN(adjust_hue); - BUILT_IN(lighten); - BUILT_IN(darken); - BUILT_IN(saturate); - BUILT_IN(desaturate); - BUILT_IN(grayscale); - BUILT_IN(complement); - BUILT_IN(invert); - BUILT_IN(alpha); - BUILT_IN(opacify); - BUILT_IN(transparentize); - BUILT_IN(adjust_color); - BUILT_IN(scale_color); - BUILT_IN(change_color); - BUILT_IN(ie_hex_str); - BUILT_IN(sass_unquote); - BUILT_IN(sass_quote); - BUILT_IN(str_length); - BUILT_IN(str_insert); - BUILT_IN(str_index); - BUILT_IN(str_slice); - BUILT_IN(to_upper_case); - BUILT_IN(to_lower_case); - BUILT_IN(percentage); - BUILT_IN(round); - BUILT_IN(ceil); - BUILT_IN(floor); - BUILT_IN(abs); - BUILT_IN(min); - BUILT_IN(max); - BUILT_IN(inspect); - BUILT_IN(random); - BUILT_IN(length); - BUILT_IN(nth); - BUILT_IN(index); - BUILT_IN(join); - BUILT_IN(append); - BUILT_IN(zip); - BUILT_IN(list_separator); - BUILT_IN(type_of); - BUILT_IN(unit); - BUILT_IN(unitless); - BUILT_IN(comparable); - BUILT_IN(variable_exists); - BUILT_IN(global_variable_exists); - BUILT_IN(function_exists); - BUILT_IN(mixin_exists); - BUILT_IN(feature_exists); - BUILT_IN(call); - BUILT_IN(sass_not); - BUILT_IN(sass_if); - BUILT_IN(map_get); - BUILT_IN(map_merge); - BUILT_IN(map_remove); - BUILT_IN(map_keys); - BUILT_IN(map_values); - BUILT_IN(map_has_key); - BUILT_IN(keywords); - BUILT_IN(set_nth); - BUILT_IN(unique_id); - BUILT_IN(selector_nest); - BUILT_IN(selector_append); - BUILT_IN(selector_extend); - BUILT_IN(selector_replace); - BUILT_IN(selector_unify); - BUILT_IN(is_superselector); - BUILT_IN(simple_selectors); - BUILT_IN(selector_parse); - BUILT_IN(is_bracketed); - BUILT_IN(content_exists); - BUILT_IN(get_function); } + } #endif diff --git a/src/inspect.cpp b/src/inspect.cpp index 273ff8fb1..4aeb82436 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -42,9 +42,7 @@ namespace Sass { void Inspect::operator()(Ruleset_Ptr ruleset) { if (ruleset->selector()) { - opt.in_selector = true; ruleset->selector()->perform(this); - opt.in_selector = false; } if (ruleset->block()) { ruleset->block()->perform(this); @@ -513,12 +511,6 @@ namespace Sass { call->arguments()->perform(this); } - void Inspect::operator()(Function_Call_Schema_Ptr call) - { - call->name()->perform(this); - call->arguments()->perform(this); - } - void Inspect::operator()(Variable_Ptr var) { append_token(var->name(), var); @@ -629,11 +621,6 @@ namespace Sass { // maybe an unknown token std::string name = c->disp(); - if (opt.in_selector && name != "") { - append_token(name, c); - return; - } - // resolved color std::string res_name = name; @@ -658,6 +645,9 @@ namespace Sass { } std::stringstream hexlet; + // dart sass compressed all colors in regular css always + // ruby sass and libsass does it only when not delayed + // since color math is going to be removed, this can go too bool compressed = opt.output_style == COMPRESSED; hexlet << '#' << std::setw(1) << std::setfill('0'); // create a short color hexlet if there is any need for it @@ -921,9 +911,7 @@ namespace Sass { void Inspect::operator()(Selector_Schema_Ptr s) { - opt.in_selector = true; s->contents()->perform(this); - opt.in_selector = false; } void Inspect::operator()(Parent_Selector_Ptr p) @@ -1131,8 +1119,4 @@ namespace Sass { } - void Inspect::fallback_impl(AST_Node_Ptr n) - { - } - } diff --git a/src/inspect.hpp b/src/inspect.hpp index c36790b80..774290d3f 100644 --- a/src/inspect.hpp +++ b/src/inspect.hpp @@ -13,8 +13,6 @@ namespace Sass { // import all the class-specific methods and override as desired using Operation_CRTP::operator(); - void fallback_impl(AST_Node_Ptr n); - public: Inspect(const Emitter& emi); @@ -53,7 +51,6 @@ namespace Sass { virtual void operator()(Binary_Expression_Ptr); virtual void operator()(Unary_Expression_Ptr); virtual void operator()(Function_Call_Ptr); - virtual void operator()(Function_Call_Schema_Ptr); // virtual void operator()(Custom_Warning_Ptr); // virtual void operator()(Custom_Error_Ptr); virtual void operator()(Variable_Ptr); @@ -95,8 +92,6 @@ namespace Sass { virtual std::string lbracket(List_Ptr); virtual std::string rbracket(List_Ptr); - // template - // void fallback(U x) { fallback_impl(reinterpret_cast(x)); } }; } diff --git a/src/listize.cpp b/src/listize.cpp index cb921ae67..a18724e45 100644 --- a/src/listize.cpp +++ b/src/listize.cpp @@ -78,9 +78,4 @@ namespace Sass { return l.detach(); } - Expression_Ptr Listize::fallback_impl(AST_Node_Ptr n) - { - return Cast(n); - } - } diff --git a/src/listize.hpp b/src/listize.hpp index 9716ebefc..8fa148816 100644 --- a/src/listize.hpp +++ b/src/listize.hpp @@ -15,8 +15,6 @@ namespace Sass { class Listize : public Operation_CRTP { - Expression_Ptr fallback_impl(AST_Node_Ptr n); - public: Listize(); ~Listize() { } @@ -25,8 +23,10 @@ namespace Sass { Expression_Ptr operator()(Complex_Selector_Ptr); Expression_Ptr operator()(Compound_Selector_Ptr); + // generic fallback template - Expression_Ptr fallback(U x) { return fallback_impl(x); } + Expression_Ptr fallback(U x) + { return Cast(x); } }; } diff --git a/src/memory/SharedPtr.hpp b/src/memory/SharedPtr.hpp index f20dfa39b..7e13bf06c 100644 --- a/src/memory/SharedPtr.hpp +++ b/src/memory/SharedPtr.hpp @@ -78,6 +78,9 @@ namespace Sass { static void setTaint(bool val) { taint = val; } + + virtual const std::string to_string() const = 0; + virtual ~SharedObj(); long getRefCount() { return refcounter; @@ -169,6 +172,14 @@ namespace Sass { } return *this; } + + // allow implicit conversion to string + // relies on base class implementation + operator const std::string() const { + if (node) return node->to_string(); + else return std::string("[NULLPTR]"); + } + ~SharedImpl() {}; public: operator T*() const { diff --git a/src/operation.hpp b/src/operation.hpp index 2d4fbec9d..415e52b3a 100644 --- a/src/operation.hpp +++ b/src/operation.hpp @@ -1,15 +1,33 @@ #ifndef SASS_OPERATION_H #define SASS_OPERATION_H +// base classes to implement curiously recurring template pattern (CRTP) +// https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern + #include "ast_fwd_decl.hpp" +#include "ast_def_macros.hpp" namespace Sass { + // you must add operators to every class + // ensures `this` of actual instance type + // we therefore call the specific operator + // they are virtual so most specific is used + #define ATTACH_CRTP_PERFORM_METHODS()\ + virtual void perform(Operation* op) { return (*op)(this); } \ + virtual Value_Ptr perform(Operation* op) { return (*op)(this); } \ + virtual std::string perform(Operation* op) { return (*op)(this); } \ + virtual AST_Node_Ptr perform(Operation* op) { return (*op)(this); } \ + virtual Selector_Ptr perform(Operation* op) { return (*op)(this); } \ + virtual Statement_Ptr perform(Operation* op) { return (*op)(this); } \ + virtual Expression_Ptr perform(Operation* op) { return (*op)(this); } \ + virtual union Sass_Value* perform(Operation* op) { return (*op)(this); } \ + virtual Supports_Condition_Ptr perform(Operation* op) { return (*op)(this); } \ + template class Operation { public: virtual T operator()(AST_Node_Ptr x) = 0; - virtual ~Operation() { } // statements virtual T operator()(Block_Ptr x) = 0; virtual T operator()(Ruleset_Ptr x) = 0; @@ -38,13 +56,13 @@ namespace Sass { virtual T operator()(Definition_Ptr x) = 0; virtual T operator()(Mixin_Call_Ptr x) = 0; // expressions + virtual T operator()(Null_Ptr x) = 0; virtual T operator()(List_Ptr x) = 0; virtual T operator()(Map_Ptr x) = 0; virtual T operator()(Function_Ptr x) = 0; virtual T operator()(Binary_Expression_Ptr x) = 0; virtual T operator()(Unary_Expression_Ptr x) = 0; virtual T operator()(Function_Call_Ptr x) = 0; - virtual T operator()(Function_Call_Schema_Ptr x) = 0; virtual T operator()(Custom_Warning_Ptr x) = 0; virtual T operator()(Custom_Error_Ptr x) = 0; virtual T operator()(Variable_Ptr x) = 0; @@ -62,8 +80,8 @@ namespace Sass { virtual T operator()(Media_Query_Ptr x) = 0; virtual T operator()(Media_Query_Expression_Ptr x) = 0; virtual T operator()(At_Root_Query_Ptr x) = 0; - virtual T operator()(Null_Ptr x) = 0; virtual T operator()(Parent_Selector_Ptr x) = 0; + virtual T operator()(Parent_Reference_Ptr x) = 0; // parameters and arguments virtual T operator()(Parameter_Ptr x) = 0; virtual T operator()(Parameters_Ptr x) = 0; @@ -81,15 +99,14 @@ namespace Sass { virtual T operator()(Compound_Selector_Ptr x)= 0; virtual T operator()(Complex_Selector_Ptr x) = 0; virtual T operator()(Selector_List_Ptr x) = 0; - - template - T fallback(U x) { return T(); } }; + // example: Operation_CRTP + // T is the base return type of all visitors + // D is the class derived visitor class + // normaly you want to implement all operators template class Operation_CRTP : public Operation { - public: - D& impl() { return static_cast(*this); } public: T operator()(AST_Node_Ptr x) { return static_cast(this)->fallback(x); } // statements @@ -120,13 +137,13 @@ namespace Sass { T operator()(Definition_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Mixin_Call_Ptr x) { return static_cast(this)->fallback(x); } // expressions + T operator()(Null_Ptr x) { return static_cast(this)->fallback(x); } T operator()(List_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Map_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Function_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Binary_Expression_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Unary_Expression_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Function_Call_Ptr x) { return static_cast(this)->fallback(x); } - T operator()(Function_Call_Schema_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Custom_Warning_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Custom_Error_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Variable_Ptr x) { return static_cast(this)->fallback(x); } @@ -144,8 +161,8 @@ namespace Sass { T operator()(Media_Query_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Media_Query_Expression_Ptr x) { return static_cast(this)->fallback(x); } T operator()(At_Root_Query_Ptr x) { return static_cast(this)->fallback(x); } - T operator()(Null_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Parent_Selector_Ptr x) { return static_cast(this)->fallback(x); } + T operator()(Parent_Reference_Ptr x) { return static_cast(this)->fallback(x); } // parameters and arguments T operator()(Parameter_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Parameters_Ptr x) { return static_cast(this)->fallback(x); } @@ -164,8 +181,15 @@ namespace Sass { T operator()(Complex_Selector_Ptr x) { return static_cast(this)->fallback(x); } T operator()(Selector_List_Ptr x) { return static_cast(this)->fallback(x); } - template - T fallback(U x) { return T(); } + // fallback with specific type U + // will be called if not overloaded + template T fallback(U x) + { + std::string msg(typeid(*this).name()); + msg += ": CRTP not implemented for "; + throw std::runtime_error(msg + typeid(*x).name()); + } + }; } diff --git a/src/parser.cpp b/src/parser.cpp index 525f199d1..1b7dac6ac 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1578,7 +1578,7 @@ namespace Sass { return nr; } - Expression_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed) + Value_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed) { Color_Ptr color = NULL; if (parsed[0] != '#') { @@ -1628,6 +1628,19 @@ namespace Sass { return color; } + Value_Ptr Parser::color_or_string(const std::string& lexed) const + { + if (auto color = name_to_color(lexed)) { + auto c = SASS_MEMORY_NEW(Color, color); + c->is_delayed(true); + c->pstate(pstate); + c->disp(lexed); + return c; + } else { + return SASS_MEMORY_NEW(String_Constant, pstate, lexed); + } + } + // parse one value for a list Expression_Obj Parser::parse_value() { @@ -1637,7 +1650,7 @@ namespace Sass { if (match< ampersand >()) { warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate); } - return SASS_MEMORY_NEW(Parent_Selector, pstate); } + return SASS_MEMORY_NEW(Parent_Reference, pstate); } if (lex< kwd_important >()) { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); } @@ -1670,7 +1683,7 @@ namespace Sass { { return SASS_MEMORY_NEW(Null, pstate); } if (lex< identifier >()) { - return SASS_MEMORY_NEW(String_Constant, pstate, lexed); + return color_or_string(lexed); } if (lex< percentage >()) @@ -1841,7 +1854,7 @@ namespace Sass { return schema->length() > 0 ? schema.detach() : NULL; } - String_Constant_Obj Parser::parse_static_value() + Value_Obj Parser::parse_static_value() { lex< static_value >(); Token str(lexed); @@ -1852,8 +1865,7 @@ namespace Sass { --str.end; --position; - String_Constant_Ptr str_node = SASS_MEMORY_NEW(String_Constant, pstate, str.time_wspace()); - return str_node; + return color_or_string(str.time_wspace());; } String_Obj Parser::parse_string() @@ -1986,7 +1998,7 @@ namespace Sass { } if (peek < exactly < '-' > >()) break; } - else if (lex< sequence < identifier > >()) { + else if (lex< identifier >()) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { // need_space = true; @@ -2179,13 +2191,13 @@ namespace Sass { return SASS_MEMORY_NEW(Function_Call, call_pos, name, args); } - Function_Call_Schema_Obj Parser::parse_function_call_schema() + Function_Call_Obj Parser::parse_function_call_schema() { String_Obj name = parse_identifier_schema(); ParserState source_position_of_call = pstate; Arguments_Obj args = parse_arguments(); - return SASS_MEMORY_NEW(Function_Call_Schema, source_position_of_call, name, args); + return SASS_MEMORY_NEW(Function_Call, source_position_of_call, name, args); } Content_Obj Parser::parse_content_directive() diff --git a/src/parser.hpp b/src/parser.hpp index 83c7f34ba..e8f331f69 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -285,12 +285,12 @@ namespace Sass { Expression_Obj parse_value(); Function_Call_Obj parse_calc_function(); Function_Call_Obj parse_function_call(); - Function_Call_Schema_Obj parse_function_call_schema(); + Function_Call_Obj parse_function_call_schema(); String_Obj parse_url_function_string(); String_Obj parse_url_function_argument(); String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true); String_Obj parse_string(); - String_Constant_Obj parse_static_value(); + Value_Obj parse_static_value(); String_Schema_Obj parse_css_variable_value(bool top_level = true); String_Schema_Obj parse_css_variable_value_token(bool top_level = true); String_Obj parse_ie_property(); @@ -325,6 +325,8 @@ namespace Sass { Error_Obj parse_error(); Debug_Obj parse_debug(); + Value_Ptr color_or_string(const std::string& lexed) const; + // be more like ruby sass Expression_Obj lex_almost_any_value_token(); Expression_Obj lex_almost_any_value_chars(); @@ -380,12 +382,12 @@ namespace Sass { static Number_Ptr lexed_number(const ParserState& pstate, const std::string& parsed); static Number_Ptr lexed_dimension(const ParserState& pstate, const std::string& parsed); static Number_Ptr lexed_percentage(const ParserState& pstate, const std::string& parsed); - static Expression_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed); + static Value_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed); private: Number_Ptr lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); }; Number_Ptr lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); }; Number_Ptr lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); }; - Expression_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); }; + Value_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); }; static const char* re_attr_sensitive_close(const char* src); static const char* re_attr_insensitive_close(const char* src); diff --git a/src/remove_placeholders.hpp b/src/remove_placeholders.hpp index c13b63134..690b0f3eb 100644 --- a/src/remove_placeholders.hpp +++ b/src/remove_placeholders.hpp @@ -11,8 +11,6 @@ namespace Sass { class Remove_Placeholders : public Operation_CRTP { - void fallback_impl(AST_Node_Ptr n) {} - public: Selector_List_Ptr remove_placeholders(Selector_List_Ptr); @@ -26,8 +24,9 @@ namespace Sass { void operator()(Supports_Block_Ptr); void operator()(Directive_Ptr); + // ignore missed types template - void fallback(U x) { return fallback_impl(x); } + void fallback(U x) {} }; } diff --git a/src/sass.cpp b/src/sass.cpp index 98e349f48..06468f92d 100644 --- a/src/sass.cpp +++ b/src/sass.cpp @@ -7,6 +7,7 @@ #include "sass.h" #include "file.hpp" #include "util.hpp" +#include "context.hpp" #include "sass_context.hpp" #include "sass_functions.hpp" diff --git a/src/sass.hpp b/src/sass.hpp index f0550490c..fdeface14 100644 --- a/src/sass.hpp +++ b/src/sass.hpp @@ -14,11 +14,17 @@ // aplies to MSVC and MinGW #ifdef _WIN32 // we do not want the ERROR macro -# define NOGDI +# ifndef NOGDI +# define NOGDI +# endif // we do not want the min/max macro -# define NOMINMAX +# ifndef NOMINMAX +# define NOMINMAX +# endif // we do not want the IN/OUT macro -# define _NO_W32_PSEUDO_MODIFIERS +# ifndef _NO_W32_PSEUDO_MODIFIERS +# define _NO_W32_PSEUDO_MODIFIERS +# endif #endif @@ -90,13 +96,10 @@ struct Sass_Inspect_Options { // Precision for fractional numbers int precision; - // Do not compress colors in selectors - bool in_selector; - // initialization list (constructor with defaults) Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED, - int precision = 5, bool in_selector = false) - : output_style(style), precision(precision), in_selector(in_selector) + int precision = 5) + : output_style(style), precision(precision) { } }; diff --git a/src/sass_functions.hpp b/src/sass_functions.hpp index 3b646d67e..482ed641b 100644 --- a/src/sass_functions.hpp +++ b/src/sass_functions.hpp @@ -3,7 +3,7 @@ #include "sass.h" #include "environment.hpp" -#include "functions.hpp" +#include "fn_utils.hpp" // Struct to hold custom function callback struct Sass_Function { diff --git a/src/to_value.cpp b/src/to_value.cpp index 3912c5510..f94c09a69 100644 --- a/src/to_value.cpp +++ b/src/to_value.cpp @@ -4,13 +4,6 @@ namespace Sass { - Value_Ptr To_Value::fallback_impl(AST_Node_Ptr n) - { - // throw a runtime error if this happens - // we want a well defined set of possible nodes - throw std::runtime_error("invalid node for to_value"); - } - // Custom_Error is a valid value Value_Ptr To_Value::operator()(Custom_Error_Ptr e) { diff --git a/src/to_value.hpp b/src/to_value.hpp index 8f64128c4..ba422b3d4 100644 --- a/src/to_value.hpp +++ b/src/to_value.hpp @@ -9,8 +9,6 @@ namespace Sass { class To_Value : public Operation_CRTP { - Value_Ptr fallback_impl(AST_Node_Ptr n); - private: Context& ctx; @@ -40,9 +38,6 @@ namespace Sass { Value_Ptr operator()(Selector_List_Ptr); Value_Ptr operator()(Binary_Expression_Ptr); - // fallback throws error - template - Value_Ptr fallback(U x) { return fallback_impl(x); } }; } diff --git a/win/libsass.targets b/win/libsass.targets index c1c7d45f3..62780aeac 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -32,7 +32,15 @@ + + + + + + + + @@ -55,7 +63,8 @@ - + + @@ -86,6 +95,14 @@ + + + + + + + + @@ -108,7 +125,8 @@ - + + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index 980f00f3f..407fac783 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -108,9 +108,33 @@ Headers + + Headers + Headers + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + Headers @@ -174,7 +198,10 @@ Headers - + + Headers + + Headers @@ -269,6 +296,30 @@ Sources + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + Sources @@ -335,7 +386,10 @@ Sources - + + Sources + + Sources