diff --git a/Makefile.conf b/Makefile.conf index ab498a38c..cb7637528 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -7,6 +7,7 @@ SOURCES = \ ast.cpp \ + ast_values.cpp \ ast_sel_cmp.cpp \ ast_sel_unify.cpp \ ast_selectors.cpp \ diff --git a/src/ast.cpp b/src/ast.cpp index 07a94ca30..17afcf72a 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -31,23 +31,6 @@ namespace Sass { Cast(cond); } - void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v") - { - str.erase( str.find_last_not_of( delimiters ) + 1 ); - } - - void String_Constant::rtrim() - { - str_rtrim(value_); - } - - void String_Schema::rtrim() - { - if (!empty()) { - if (String_Ptr str = Cast(last())) str->rtrim(); - } - } - void Argument::set_delayed(bool delayed) { if (value_) value_->set_delayed(delayed); @@ -169,236 +152,6 @@ namespace Sass { return true; } - Number::Number(ParserState pstate, double val, std::string u, bool zero) - : Value(pstate), - Units(), - value_(val), - zero_(zero), - hash_(0) - { - size_t l = 0; - size_t r; - if (!u.empty()) { - bool nominator = true; - while (true) { - r = u.find_first_of("*/", l); - std::string unit(u.substr(l, r == std::string::npos ? r : r - l)); - if (!unit.empty()) { - if (nominator) numerators.push_back(unit); - else denominators.push_back(unit); - } - if (r == std::string::npos) break; - // ToDo: should error for multiple slashes - // if (!nominator && u[r] == '/') error(...) - if (u[r] == '/') - nominator = false; - // strange math parsing? - // else if (u[r] == '*') - // nominator = true; - l = r + 1; - } - } - concrete_type(NUMBER); - } - - // cancel out unnecessary units - void Number::reduce() - { - // apply conversion factor - value_ *= this->Units::reduce(); - } - - void Number::normalize() - { - // apply conversion factor - value_ *= this->Units::normalize(); - } - - bool Custom_Warning::operator== (const Expression& rhs) const - { - if (Custom_Warning_Ptr_Const r = Cast(&rhs)) { - return message() == r->message(); - } - return false; - } - - bool Custom_Error::operator== (const Expression& rhs) const - { - if (Custom_Error_Ptr_Const r = Cast(&rhs)) { - return message() == r->message(); - } - return false; - } - - bool Number::operator== (const Expression& rhs) const - { - if (auto rhsnr = Cast(&rhs)) { - return *this == *rhsnr; - } - return false; - } - - bool Number::operator== (const Number& rhs) const - { - Number l(*this), r(rhs); l.reduce(); r.reduce(); - size_t lhs_units = l.numerators.size() + l.denominators.size(); - size_t rhs_units = r.numerators.size() + r.denominators.size(); - // unitless and only having one unit seems equivalent (will change in future) - if (!lhs_units || !rhs_units) { - return NEAR_EQUAL(l.value(), r.value()); - } - l.normalize(); r.normalize(); - Units &lhs_unit = l, &rhs_unit = r; - return lhs_unit == rhs_unit && - NEAR_EQUAL(l.value(), r.value()); - } - - bool Number::operator< (const Number& rhs) const - { - Number l(*this), r(rhs); l.reduce(); r.reduce(); - size_t lhs_units = l.numerators.size() + l.denominators.size(); - size_t rhs_units = r.numerators.size() + r.denominators.size(); - // unitless and only having one unit seems equivalent (will change in future) - if (!lhs_units || !rhs_units) { - return l.value() < r.value(); - } - l.normalize(); r.normalize(); - Units &lhs_unit = l, &rhs_unit = r; - if (!(lhs_unit == rhs_unit)) { - /* ToDo: do we always get usefull backtraces? */ - throw Exception::IncompatibleUnits(rhs, *this); - } - return lhs_unit < rhs_unit || - l.value() < r.value(); - } - - bool String_Quoted::operator== (const Expression& rhs) const - { - if (String_Quoted_Ptr_Const qstr = Cast(&rhs)) { - return (value() == qstr->value()); - } else if (String_Constant_Ptr_Const cstr = Cast(&rhs)) { - return (value() == cstr->value()); - } - return false; - } - - bool String_Constant::is_invisible() const { - return value_.empty() && quote_mark_ == 0; - } - - bool String_Constant::operator== (const Expression& rhs) const - { - if (String_Quoted_Ptr_Const qstr = Cast(&rhs)) { - return (value() == qstr->value()); - } else if (String_Constant_Ptr_Const cstr = Cast(&rhs)) { - return (value() == cstr->value()); - } - return false; - } - - bool String_Schema::is_left_interpolant(void) const - { - return length() && first()->is_left_interpolant(); - } - bool String_Schema::is_right_interpolant(void) const - { - return length() && last()->is_right_interpolant(); - } - - bool String_Schema::operator== (const Expression& rhs) const - { - if (String_Schema_Ptr_Const r = Cast(&rhs)) { - if (length() != r->length()) return false; - for (size_t i = 0, L = length(); i < L; ++i) { - Expression_Obj rv = (*r)[i]; - Expression_Obj lv = (*this)[i]; - if (!lv || !rv) return false; - if (!(*lv == *rv)) return false; - } - return true; - } - return false; - } - - bool Boolean::operator== (const Expression& rhs) const - { - if (Boolean_Ptr_Const r = Cast(&rhs)) { - return (value() == r->value()); - } - return false; - } - - bool Color::operator== (const Expression& rhs) const - { - if (Color_Ptr_Const r = Cast(&rhs)) { - return r_ == r->r() && - g_ == r->g() && - b_ == r->b() && - a_ == r->a(); - } - return false; - } - - bool List::operator== (const Expression& rhs) const - { - if (List_Ptr_Const r = Cast(&rhs)) { - if (length() != r->length()) return false; - if (separator() != r->separator()) return false; - if (is_bracketed() != r->is_bracketed()) return false; - for (size_t i = 0, L = length(); i < L; ++i) { - Expression_Obj rv = r->at(i); - Expression_Obj lv = this->at(i); - if (!lv || !rv) return false; - if (!(*lv == *rv)) return false; - } - return true; - } - return false; - } - - bool Map::operator== (const Expression& rhs) const - { - if (Map_Ptr_Const r = Cast(&rhs)) { - if (length() != r->length()) return false; - for (auto key : keys()) { - Expression_Obj lv = at(key); - Expression_Obj rv = r->at(key); - if (!rv || !lv) return false; - if (!(*lv == *rv)) return false; - } - return true; - } - return false; - } - - bool Null::operator== (const Expression& rhs) const - { - return rhs.concrete_type() == NULL_VAL; - } - - bool Function::operator== (const Expression& rhs) const - { - if (Function_Ptr_Const r = Cast(&rhs)) { - Definition_Ptr_Const d1 = Cast(definition()); - Definition_Ptr_Const d2 = Cast(r->definition()); - return d1 && d2 && d1 == d2 && is_css() == r->is_css(); - } - return false; - } - - size_t List::size() const { - if (!is_arglist_) return length(); - // arglist expects a list of arguments - // so we need to break before keywords - for (size_t i = 0, L = length(); i < L; ++i) { - Expression_Obj obj = this->at(i); - if (Argument_Ptr arg = Cast(obj)) { - if (!arg->name().empty()) return i; - } - } - return length(); - } - Expression_Obj Hashed::at(Expression_Obj k) const { if (elements_.count(k)) @@ -431,16 +184,6 @@ namespace Sass { return to_string({ NESTED, 5 }); } - std::string String_Quoted::inspect() const - { - return quote(value_, '*'); - } - - std::string String_Constant::inspect() const - { - return quote(value_, '*'); - } - bool Declaration::is_invisible() const { if (is_custom_property()) return false; @@ -464,81 +207,12 @@ 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_(), 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() const - { - 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. - ////////////////////////////////////////////////////////////////////////////////////////// - List_Obj Map::to_list(ParserState& pstate) { - List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA); - - for (auto key : keys()) { - List_Obj l = SASS_MEMORY_NEW(List, pstate, 2); - l->append(key); - l->append(at(key)); - ret->append(l); - } - - return ret; - } - IMPLEMENT_AST_OPERATORS(Supports_Operator); IMPLEMENT_AST_OPERATORS(Supports_Negation); IMPLEMENT_AST_OPERATORS(Ruleset); IMPLEMENT_AST_OPERATORS(Media_Block); - IMPLEMENT_AST_OPERATORS(Custom_Warning); - IMPLEMENT_AST_OPERATORS(Custom_Error); - IMPLEMENT_AST_OPERATORS(List); - IMPLEMENT_AST_OPERATORS(Map); - IMPLEMENT_AST_OPERATORS(Function); - IMPLEMENT_AST_OPERATORS(Number); - IMPLEMENT_AST_OPERATORS(Binary_Expression); - IMPLEMENT_AST_OPERATORS(String_Schema); - IMPLEMENT_AST_OPERATORS(String_Constant); - IMPLEMENT_AST_OPERATORS(String_Quoted); - IMPLEMENT_AST_OPERATORS(Boolean); - IMPLEMENT_AST_OPERATORS(Color); - IMPLEMENT_AST_OPERATORS(Null); - IMPLEMENT_AST_OPERATORS(Parent_Reference); IMPLEMENT_AST_OPERATORS(Import); IMPLEMENT_AST_OPERATORS(Import_Stub); - IMPLEMENT_AST_OPERATORS(Function_Call); IMPLEMENT_AST_OPERATORS(Directive); IMPLEMENT_AST_OPERATORS(At_Root_Block); IMPLEMENT_AST_OPERATORS(Supports_Block); @@ -556,7 +230,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Assignment); IMPLEMENT_AST_OPERATORS(Return); IMPLEMENT_AST_OPERATORS(At_Root_Query); - IMPLEMENT_AST_OPERATORS(Variable); IMPLEMENT_AST_OPERATORS(Comment); IMPLEMENT_AST_OPERATORS(Supports_Interpolation); IMPLEMENT_AST_OPERATORS(Supports_Declaration); diff --git a/src/ast.hpp b/src/ast.hpp index d080e23c7..d1c2e89a9 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -193,36 +193,6 @@ namespace Sass { size_t hash() const override { return 0; } }; - ////////////////////////////////////////////////////////////////////// - // Still just an expression, but with a to_string method - ////////////////////////////////////////////////////////////////////// - class PreValue : public Expression { - public: - PreValue(ParserState pstate, - bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) - : Expression(pstate, d, e, i, ct) - { } - PreValue(const PreValue* ptr) - : Expression(ptr) - { } - ATTACH_VIRTUAL_AST_OPERATIONS(PreValue); - virtual ~PreValue() { } - }; - - ////////////////////////////////////////////////////////////////////// - // base class for values that support operations - ////////////////////////////////////////////////////////////////////// - class Value : public PreValue { - public: - Value(ParserState pstate, - bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) - : PreValue(pstate, d, e, i, ct) - { } - Value(const Value* ptr) - : PreValue(ptr) - { } - ATTACH_VIRTUAL_AST_OPERATIONS(Value); - }; } ///////////////////////////////////////////////////////////////////////////////////// @@ -1027,101 +997,6 @@ namespace Sass { ATTACH_CRTP_PERFORM_METHODS() }; - /////////////////////////////////////////////////////////////////////// - // Lists of values, both comma- and space-separated (distinguished by a - // type-tag.) Also used to represent variable-length argument lists. - /////////////////////////////////////////////////////////////////////// - class List final : public Value, public Vectorized { - void adjust_after_pushing(Expression_Obj e) override { is_expanded(false); } - private: - ADD_PROPERTY(enum Sass_Separator, separator) - ADD_PROPERTY(bool, is_arglist) - ADD_PROPERTY(bool, is_bracketed) - ADD_PROPERTY(bool, from_selector) - public: - List(ParserState pstate, - size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false) - : Value(pstate), - Vectorized(size), - separator_(sep), - is_arglist_(argl), - is_bracketed_(bracket), - from_selector_(false) - { concrete_type(LIST); } - List(const List* ptr) - : Value(ptr), - Vectorized(*ptr), - separator_(ptr->separator_), - is_arglist_(ptr->is_arglist_), - is_bracketed_(ptr->is_bracketed_), - from_selector_(ptr->from_selector_) - { concrete_type(LIST); } - std::string type() const override { return is_arglist_ ? "arglist" : "list"; } - static std::string type_name() { return "list"; } - const char* sep_string(bool compressed = false) const { - return separator() == SASS_SPACE ? - " " : (compressed ? "," : ", "); - } - bool is_invisible() const override { return empty() && !is_bracketed(); } - Expression_Obj value_at_index(size_t i); - - virtual size_t size() const; - - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(sep_string()); - hash_combine(hash_, std::hash()(is_bracketed())); - for (size_t i = 0, L = length(); i < L; ++i) - hash_combine(hash_, (elements()[i])->hash()); - } - return hash_; - } - - bool operator== (const Expression& rhs) const override; - - ATTACH_AST_OPERATIONS(List) - ATTACH_CRTP_PERFORM_METHODS() - }; - - /////////////////////////////////////////////////////////////////////// - // Key value paris. - /////////////////////////////////////////////////////////////////////// - class Map final : public Value, public Hashed { - void adjust_after_pushing(std::pair p) override { is_expanded(false); } - public: - Map(ParserState pstate, - size_t size = 0) - : Value(pstate), - Hashed(size) - { concrete_type(MAP); } - Map(const Map* ptr) - : Value(ptr), - Hashed(*ptr) - { concrete_type(MAP); } - std::string type() const override { return "map"; } - static std::string type_name() { return "map"; } - bool is_invisible() const override { return empty(); } - List_Obj to_list(ParserState& pstate); - - size_t hash() const override - { - if (hash_ == 0) { - for (auto key : keys()) { - hash_combine(hash_, key->hash()); - hash_combine(hash_, at(key)->hash()); - } - } - - return hash_; - } - - bool operator== (const Expression& rhs) const override; - - ATTACH_AST_OPERATIONS(Map) - ATTACH_CRTP_PERFORM_METHODS() - }; - inline static const std::string sass_op_to_name(enum Sass_OP op) { switch (op) { case AND: return "and"; @@ -1164,78 +1039,6 @@ namespace Sass { } } - ////////////////////////////////////////////////////////////////////////// - // Binary expressions. Represents logical, relational, and arithmetic - // operations. Templatized to avoid large switch statements and repetitive - // subclassing. - ////////////////////////////////////////////////////////////////////////// - class Binary_Expression final : public PreValue { - private: - HASH_PROPERTY(Operand, op) - HASH_PROPERTY(Expression_Obj, left) - HASH_PROPERTY(Expression_Obj, right) - mutable size_t hash_; - public: - Binary_Expression(ParserState pstate, - Operand op, Expression_Obj lhs, Expression_Obj rhs) - : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) - { } - Binary_Expression(const Binary_Expression* ptr) - : PreValue(ptr), - op_(ptr->op_), - left_(ptr->left_), - right_(ptr->right_), - hash_(ptr->hash_) - { } - const std::string type_name() { - return sass_op_to_name(optype()); - } - const std::string separator() { - return sass_op_separator(optype()); - } - bool is_left_interpolant(void) const override; - bool is_right_interpolant(void) const override; - bool has_interpolant() const override - { - return is_left_interpolant() || - is_right_interpolant(); - } - void set_delayed(bool delayed) override - { - right()->set_delayed(delayed); - left()->set_delayed(delayed); - is_delayed(delayed); - } - bool operator==(const Expression& rhs) const override - { - try - { - Binary_Expression_Ptr_Const m = Cast(&rhs); - if (m == 0) return false; - return type() == m->type() && - *left() == *m->left() && - *right() == *m->right(); - } - catch (std::bad_cast&) - { - return false; - } - catch (...) { throw; } - } - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(optype()); - hash_combine(hash_, left()->hash()); - hash_combine(hash_, right()->hash()); - } - return hash_; - } - enum Sass_OP optype() const { return op_.operand; } - ATTACH_AST_OPERATIONS(Binary_Expression) - ATTACH_CRTP_PERFORM_METHODS() - }; - //////////////////////////////////////////////////////////////////////////// // Arithmetic negation (logical negation is just an ordinary function call). //////////////////////////////////////////////////////////////////////////// @@ -1387,432 +1190,6 @@ namespace Sass { ATTACH_CRTP_PERFORM_METHODS() }; - //////////////////////////////////////////////////// - // Function reference. - //////////////////////////////////////////////////// - class Function final : public Value { - public: - ADD_PROPERTY(Definition_Obj, definition) - ADD_PROPERTY(bool, is_css) - public: - Function(ParserState pstate, Definition_Obj def, bool css) - : Value(pstate), definition_(def), is_css_(css) - { concrete_type(FUNCTION_VAL); } - Function(const Function* ptr) - : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_) - { concrete_type(FUNCTION_VAL); } - - std::string type() const override { return "function"; } - static std::string type_name() { return "function"; } - bool is_invisible() const override { return true; } - - std::string name() { - if (definition_) { - return definition_->name(); - } - return ""; - } - - bool operator== (const Expression& rhs) const override; - - ATTACH_AST_OPERATIONS(Function) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////////////// - // Function calls. - ////////////////// - class Function_Call final : public PreValue { - HASH_CONSTREF(String_Obj, sname) - HASH_PROPERTY(Arguments_Obj, arguments) - HASH_PROPERTY(Function_Obj, func) - ADD_PROPERTY(bool, via_call) - ADD_PROPERTY(void*, cookie) - mutable size_t hash_; - public: - 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_(), via_call_(false), cookie_(cookie), hash_(0) - { concrete_type(FUNCTION); } - 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, 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() const { - return sname(); - } - - Function_Call(const Function_Call* ptr) - : PreValue(ptr), - sname_(ptr->sname_), - arguments_(ptr->arguments_), - func_(ptr->func_), - via_call_(ptr->via_call_), - cookie_(ptr->cookie_), - hash_(ptr->hash_) - { concrete_type(FUNCTION); } - - bool is_css() { - if (func_) return func_->is_css(); - return false; - } - - bool operator==(const Expression& rhs) const override; - - size_t hash() const override; - - ATTACH_AST_OPERATIONS(Function_Call) - ATTACH_CRTP_PERFORM_METHODS() - }; - - /////////////////////// - // Variable references. - /////////////////////// - class Variable final : public PreValue { - ADD_CONSTREF(std::string, name) - public: - Variable(ParserState pstate, std::string n) - : PreValue(pstate), name_(n) - { concrete_type(VARIABLE); } - Variable(const Variable* ptr) - : PreValue(ptr), name_(ptr->name_) - { concrete_type(VARIABLE); } - - bool operator==(const Expression& rhs) const override - { - try - { - Variable_Ptr_Const e = Cast(&rhs); - return e && name() == e->name(); - } - catch (std::bad_cast&) - { - return false; - } - catch (...) { throw; } - } - - size_t hash() const override - { - return std::hash()(name()); - } - - ATTACH_AST_OPERATIONS(Variable) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////// - // Numbers, percentages, dimensions, and colors. - //////////////////////////////////////////////// - class Number final : public Value, public Units { - HASH_PROPERTY(double, value) - ADD_PROPERTY(bool, zero) - mutable size_t hash_; - public: - Number(ParserState pstate, double val, std::string u = "", bool zero = true); - - Number(const Number* ptr) - : Value(ptr), - Units(ptr), - value_(ptr->value_), zero_(ptr->zero_), - hash_(ptr->hash_) - { concrete_type(NUMBER); } - - bool zero() { return zero_; } - std::string type() const override { return "number"; } - static std::string type_name() { return "number"; } - - void reduce(); - void normalize(); - - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(value_); - for (const auto numerator : numerators) - hash_combine(hash_, std::hash()(numerator)); - for (const auto denominator : denominators) - hash_combine(hash_, std::hash()(denominator)); - } - return hash_; - } - - bool operator< (const Number& rhs) const; - bool operator== (const Number& rhs) const; - bool operator== (const Expression& rhs) const override; - ATTACH_AST_OPERATIONS(Number) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////// - // Colors. - ////////// - class Color final : public Value { - HASH_PROPERTY(double, r) - HASH_PROPERTY(double, g) - HASH_PROPERTY(double, b) - HASH_PROPERTY(double, a) - ADD_CONSTREF(std::string, disp) - mutable size_t hash_; - public: - Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "") - : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp), - hash_(0) - { concrete_type(COLOR); } - Color(const Color* ptr) - : Value(ptr), - r_(ptr->r_), - g_(ptr->g_), - b_(ptr->b_), - a_(ptr->a_), - disp_(ptr->disp_), - hash_(ptr->hash_) - { concrete_type(COLOR); } - std::string type() const override { return "color"; } - static std::string type_name() { return "color"; } - - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(a_); - hash_combine(hash_, std::hash()(r_)); - hash_combine(hash_, std::hash()(g_)); - hash_combine(hash_, std::hash()(b_)); - } - return hash_; - } - - bool operator== (const Expression& rhs) const override; - - ATTACH_AST_OPERATIONS(Color) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////////////////////////// - // Errors from Sass_Values. - ////////////////////////////// - class Custom_Error final : public Value { - ADD_CONSTREF(std::string, message) - public: - Custom_Error(ParserState pstate, std::string msg) - : Value(pstate), message_(msg) - { concrete_type(C_ERROR); } - Custom_Error(const Custom_Error* ptr) - : Value(ptr), message_(ptr->message_) - { concrete_type(C_ERROR); } - bool operator== (const Expression& rhs) const override; - ATTACH_AST_OPERATIONS(Custom_Error) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////////////////////////// - // Warnings from Sass_Values. - ////////////////////////////// - class Custom_Warning final : public Value { - ADD_CONSTREF(std::string, message) - public: - Custom_Warning(ParserState pstate, std::string msg) - : Value(pstate), message_(msg) - { concrete_type(C_WARNING); } - Custom_Warning(const Custom_Warning* ptr) - : Value(ptr), message_(ptr->message_) - { concrete_type(C_WARNING); } - bool operator== (const Expression& rhs) const override; - ATTACH_AST_OPERATIONS(Custom_Warning) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////// - // Booleans. - //////////// - class Boolean final : public Value { - HASH_PROPERTY(bool, value) - mutable size_t hash_; - public: - Boolean(ParserState pstate, bool val) - : Value(pstate), value_(val), - hash_(0) - { concrete_type(BOOLEAN); } - Boolean(const Boolean* ptr) - : Value(ptr), - value_(ptr->value_), - hash_(ptr->hash_) - { concrete_type(BOOLEAN); } - operator bool() override { return value_; } - std::string type() const override { return "bool"; } - static std::string type_name() { return "bool"; } - bool is_false() override { return !value_; } - - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(value_); - } - return hash_; - } - - bool operator== (const Expression& rhs) const override; - - ATTACH_AST_OPERATIONS(Boolean) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////////////////////////////// - // Abstract base class for Sass string values. Includes interpolated and - // "flat" strings. - //////////////////////////////////////////////////////////////////////// - class String : public Value { - public: - String(ParserState pstate, bool delayed = false) - : Value(pstate, delayed) - { concrete_type(STRING); } - String(const String* ptr) - : Value(ptr) - { concrete_type(STRING); } - static std::string type_name() { return "string"; } - virtual ~String() = 0; - virtual void rtrim() = 0; - virtual bool operator<(const Expression& rhs) const { - return this->to_string() < rhs.to_string(); - }; - ATTACH_VIRTUAL_AST_OPERATIONS(String); - ATTACH_CRTP_PERFORM_METHODS() - }; - inline String::~String() { }; - - /////////////////////////////////////////////////////////////////////// - // Interpolated strings. Meant to be reduced to flat strings during the - // evaluation phase. - /////////////////////////////////////////////////////////////////////// - class String_Schema final : public String, public Vectorized { - ADD_PROPERTY(bool, css) - mutable size_t hash_; - public: - String_Schema(ParserState pstate, size_t size = 0, bool css = true) - : String(pstate), Vectorized(size), css_(css), hash_(0) - { concrete_type(STRING); } - String_Schema(const String_Schema* ptr) - : String(ptr), - Vectorized(*ptr), - css_(ptr->css_), - hash_(ptr->hash_) - { concrete_type(STRING); } - - std::string type() const override { return "string"; } - static std::string type_name() { return "string"; } - - bool is_left_interpolant(void) const override; - bool is_right_interpolant(void) const override; - // void has_interpolants(bool tc) { } - bool has_interpolants() { - for (auto el : elements()) { - if (el->is_interpolant()) return true; - } - return false; - } - void rtrim() override; - - size_t hash() const override - { - if (hash_ == 0) { - for (const auto &str : elements()) - hash_combine(hash_, str->hash()); - } - return hash_; - } - - void set_delayed(bool delayed) override { - is_delayed(delayed); - } - - bool operator==(const Expression& rhs) const override; - ATTACH_AST_OPERATIONS(String_Schema) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////////////// - // Flat strings -- the lowest level of raw textual data. - //////////////////////////////////////////////////////// - class String_Constant : public String { - ADD_PROPERTY(char, quote_mark) - ADD_PROPERTY(bool, can_compress_whitespace) - HASH_CONSTREF(std::string, value) - protected: - mutable size_t hash_; - public: - String_Constant(const String_Constant* ptr) - : String(ptr), - quote_mark_(ptr->quote_mark_), - can_compress_whitespace_(ptr->can_compress_whitespace_), - value_(ptr->value_), - hash_(ptr->hash_) - { } - String_Constant(ParserState pstate, std::string val, bool css = true) - : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0) - { } - String_Constant(ParserState pstate, const char* beg, bool css = true) - : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0) - { } - String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true) - : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0) - { } - String_Constant(ParserState pstate, const Token& tok, bool css = true) - : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0) - { } - std::string type() const override { return "string"; } - static std::string type_name() { return "string"; } - bool is_invisible() const override; - virtual void rtrim() override; - - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(value_); - } - return hash_; - } - - bool operator==(const Expression& rhs) const override; - virtual std::string inspect() const override; // quotes are forced on inspection - - // static char auto_quote() { return '*'; } - static char double_quote() { return '"'; } - static char single_quote() { return '\''; } - - ATTACH_AST_OPERATIONS(String_Constant) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////////////// - // Possibly quoted string (unquote on instantiation) - //////////////////////////////////////////////////////// - class String_Quoted final : public String_Constant { - public: - String_Quoted(ParserState pstate, std::string val, char q = 0, - bool keep_utf8_escapes = false, bool skip_unquoting = false, - bool strict_unquoting = true, bool css = true) - : String_Constant(pstate, val, css) - { - if (skip_unquoting == false) { - value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting); - } - if (q && quote_mark_) quote_mark_ = q; - } - String_Quoted(const String_Quoted* ptr) - : String_Constant(ptr) - { } - bool operator==(const Expression& rhs) const override; - std::string inspect() const override; // quotes are forced on inspection - ATTACH_AST_OPERATIONS(String_Quoted) - ATTACH_CRTP_PERFORM_METHODS() - }; - ///////////////// // Media queries. ///////////////// @@ -2047,29 +1424,6 @@ namespace Sass { ATTACH_CRTP_PERFORM_METHODS() }; - ////////////////// - // The null value. - ////////////////// - class Null final : public Value { - public: - Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); } - Null(const Null* ptr) : Value(ptr) { concrete_type(NULL_VAL); } - std::string type() const override { return "null"; } - static std::string type_name() { return "null"; } - bool is_invisible() const override { return true; } - operator bool() override { return false; } - bool is_false() override { return true; } - - size_t hash() const override - { - return -1; - } - - bool operator== (const Expression& rhs) const override; - - ATTACH_AST_OPERATIONS(Null) - ATTACH_CRTP_PERFORM_METHODS() - }; ///////////////////////////////////////////////////////// // Individual parameter objects for mixins and functions. @@ -2154,26 +1508,9 @@ namespace Sass { ATTACH_CRTP_PERFORM_METHODS() }; - ////////////////////////////////// - // The Parent Reference Expression. - ////////////////////////////////// - class Parent_Reference final : public Value { - public: - Parent_Reference(ParserState pstate) - : Value(pstate) {} - Parent_Reference(const Parent_Reference* ptr) - : Value(ptr) {} - std::string type() const override { return "parent"; } - static std::string type_name() { return "parent"; } - bool operator==(const Expression& rhs) const override { - return true; // can they ever be not equal? - }; - ATTACH_AST_OPERATIONS(Parent_Reference) - ATTACH_CRTP_PERFORM_METHODS() - }; - } +#include "ast_values.hpp" #include "ast_selectors.hpp" #ifdef __clang__ diff --git a/src/ast_values.cpp b/src/ast_values.cpp new file mode 100644 index 000000000..b30673b8f --- /dev/null +++ b/src/ast_values.cpp @@ -0,0 +1,350 @@ +#include "sass.hpp" +#include "ast.hpp" +#include "context.hpp" +#include "node.hpp" +#include "eval.hpp" +#include "extend.hpp" +#include "emitter.hpp" +#include "color_maps.hpp" +#include "ast_fwd_decl.hpp" +#include +#include +#include +#include +#include +#include +#include + +#include "ast_values.hpp" + +namespace Sass { + + void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v") + { + str.erase( str.find_last_not_of( delimiters ) + 1 ); + } + + void String_Constant::rtrim() + { + str_rtrim(value_); + } + + void String_Schema::rtrim() + { + if (!empty()) { + if (String_Ptr str = Cast(last())) str->rtrim(); + } + } + + Number::Number(ParserState pstate, double val, std::string u, bool zero) + : Value(pstate), + Units(), + value_(val), + zero_(zero), + hash_(0) + { + size_t l = 0; + size_t r; + if (!u.empty()) { + bool nominator = true; + while (true) { + r = u.find_first_of("*/", l); + std::string unit(u.substr(l, r == std::string::npos ? r : r - l)); + if (!unit.empty()) { + if (nominator) numerators.push_back(unit); + else denominators.push_back(unit); + } + if (r == std::string::npos) break; + // ToDo: should error for multiple slashes + // if (!nominator && u[r] == '/') error(...) + if (u[r] == '/') + nominator = false; + // strange math parsing? + // else if (u[r] == '*') + // nominator = true; + l = r + 1; + } + } + concrete_type(NUMBER); + } + + // cancel out unnecessary units + void Number::reduce() + { + // apply conversion factor + value_ *= this->Units::reduce(); + } + + void Number::normalize() + { + // apply conversion factor + value_ *= this->Units::normalize(); + } + + bool Custom_Warning::operator== (const Expression& rhs) const + { + if (Custom_Warning_Ptr_Const r = Cast(&rhs)) { + return message() == r->message(); + } + return false; + } + + bool Custom_Error::operator== (const Expression& rhs) const + { + if (Custom_Error_Ptr_Const r = Cast(&rhs)) { + return message() == r->message(); + } + return false; + } + + bool Number::operator== (const Expression& rhs) const + { + if (auto rhsnr = Cast(&rhs)) { + return *this == *rhsnr; + } + return false; + } + + bool Number::operator== (const Number& rhs) const + { + Number l(*this), r(rhs); l.reduce(); r.reduce(); + size_t lhs_units = l.numerators.size() + l.denominators.size(); + size_t rhs_units = r.numerators.size() + r.denominators.size(); + // unitless and only having one unit seems equivalent (will change in future) + if (!lhs_units || !rhs_units) { + return NEAR_EQUAL(l.value(), r.value()); + } + l.normalize(); r.normalize(); + Units &lhs_unit = l, &rhs_unit = r; + return lhs_unit == rhs_unit && + NEAR_EQUAL(l.value(), r.value()); + } + + bool Number::operator< (const Number& rhs) const + { + Number l(*this), r(rhs); l.reduce(); r.reduce(); + size_t lhs_units = l.numerators.size() + l.denominators.size(); + size_t rhs_units = r.numerators.size() + r.denominators.size(); + // unitless and only having one unit seems equivalent (will change in future) + if (!lhs_units || !rhs_units) { + return l.value() < r.value(); + } + l.normalize(); r.normalize(); + Units &lhs_unit = l, &rhs_unit = r; + if (!(lhs_unit == rhs_unit)) { + /* ToDo: do we always get usefull backtraces? */ + throw Exception::IncompatibleUnits(rhs, *this); + } + return lhs_unit < rhs_unit || + l.value() < r.value(); + } + + bool String_Quoted::operator== (const Expression& rhs) const + { + if (String_Quoted_Ptr_Const qstr = Cast(&rhs)) { + return (value() == qstr->value()); + } else if (String_Constant_Ptr_Const cstr = Cast(&rhs)) { + return (value() == cstr->value()); + } + return false; + } + + bool String_Constant::is_invisible() const { + return value_.empty() && quote_mark_ == 0; + } + + bool String_Constant::operator== (const Expression& rhs) const + { + if (String_Quoted_Ptr_Const qstr = Cast(&rhs)) { + return (value() == qstr->value()); + } else if (String_Constant_Ptr_Const cstr = Cast(&rhs)) { + return (value() == cstr->value()); + } + return false; + } + + bool String_Schema::is_left_interpolant(void) const + { + return length() && first()->is_left_interpolant(); + } + bool String_Schema::is_right_interpolant(void) const + { + return length() && last()->is_right_interpolant(); + } + + bool String_Schema::operator== (const Expression& rhs) const + { + if (String_Schema_Ptr_Const r = Cast(&rhs)) { + if (length() != r->length()) return false; + for (size_t i = 0, L = length(); i < L; ++i) { + Expression_Obj rv = (*r)[i]; + Expression_Obj lv = (*this)[i]; + if (!lv || !rv) return false; + if (!(*lv == *rv)) return false; + } + return true; + } + return false; + } + + bool Boolean::operator== (const Expression& rhs) const + { + if (Boolean_Ptr_Const r = Cast(&rhs)) { + return (value() == r->value()); + } + return false; + } + + bool Color::operator== (const Expression& rhs) const + { + if (Color_Ptr_Const r = Cast(&rhs)) { + return r_ == r->r() && + g_ == r->g() && + b_ == r->b() && + a_ == r->a(); + } + return false; + } + + bool List::operator== (const Expression& rhs) const + { + if (List_Ptr_Const r = Cast(&rhs)) { + if (length() != r->length()) return false; + if (separator() != r->separator()) return false; + if (is_bracketed() != r->is_bracketed()) return false; + for (size_t i = 0, L = length(); i < L; ++i) { + Expression_Obj rv = r->at(i); + Expression_Obj lv = this->at(i); + if (!lv || !rv) return false; + if (!(*lv == *rv)) return false; + } + return true; + } + return false; + } + + bool Map::operator== (const Expression& rhs) const + { + if (Map_Ptr_Const r = Cast(&rhs)) { + if (length() != r->length()) return false; + for (auto key : keys()) { + Expression_Obj lv = at(key); + Expression_Obj rv = r->at(key); + if (!rv || !lv) return false; + if (!(*lv == *rv)) return false; + } + return true; + } + return false; + } + + bool Null::operator== (const Expression& rhs) const + { + return rhs.concrete_type() == NULL_VAL; + } + + bool Function::operator== (const Expression& rhs) const + { + if (Function_Ptr_Const r = Cast(&rhs)) { + Definition_Ptr_Const d1 = Cast(definition()); + Definition_Ptr_Const d2 = Cast(r->definition()); + return d1 && d2 && d1 == d2 && is_css() == r->is_css(); + } + return false; + } + + size_t List::size() const { + if (!is_arglist_) return length(); + // arglist expects a list of arguments + // so we need to break before keywords + for (size_t i = 0, L = length(); i < L; ++i) { + Expression_Obj obj = this->at(i); + if (Argument_Ptr arg = Cast(obj)) { + if (!arg->name().empty()) return i; + } + } + return length(); + } + + std::string String_Quoted::inspect() const + { + return quote(value_, '*'); + } + + std::string String_Constant::inspect() const + { + return quote(value_, '*'); + } + + 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_(), 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() const + { + 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. + ////////////////////////////////////////////////////////////////////////////////////////// + List_Obj Map::to_list(ParserState& pstate) { + List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA); + + for (auto key : keys()) { + List_Obj l = SASS_MEMORY_NEW(List, pstate, 2); + l->append(key); + l->append(at(key)); + ret->append(l); + } + + return ret; + } + + IMPLEMENT_AST_OPERATORS(List); + IMPLEMENT_AST_OPERATORS(Map); + IMPLEMENT_AST_OPERATORS(Binary_Expression); + IMPLEMENT_AST_OPERATORS(Function); + IMPLEMENT_AST_OPERATORS(Function_Call); + IMPLEMENT_AST_OPERATORS(Variable); + IMPLEMENT_AST_OPERATORS(Number); + IMPLEMENT_AST_OPERATORS(Color); + IMPLEMENT_AST_OPERATORS(Custom_Error); + IMPLEMENT_AST_OPERATORS(Custom_Warning); + IMPLEMENT_AST_OPERATORS(Boolean); + IMPLEMENT_AST_OPERATORS(String_Schema); + IMPLEMENT_AST_OPERATORS(String_Constant); + IMPLEMENT_AST_OPERATORS(String_Quoted); + IMPLEMENT_AST_OPERATORS(Null); + IMPLEMENT_AST_OPERATORS(Parent_Reference); + +} \ No newline at end of file diff --git a/src/ast_values.hpp b/src/ast_values.hpp new file mode 100644 index 000000000..ddad4b146 --- /dev/null +++ b/src/ast_values.hpp @@ -0,0 +1,712 @@ +#ifndef SASS_AST_VALUES_H +#define SASS_AST_VALUES_H + +#include "sass.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "sass/base.h" +#include "ast_fwd_decl.hpp" + +#include "util.hpp" +#include "units.hpp" +#include "context.hpp" +#include "position.hpp" +#include "constants.hpp" +#include "operation.hpp" +#include "position.hpp" +#include "inspect.hpp" +#include "source_map.hpp" +#include "environment.hpp" +#include "error_handling.hpp" +#include "ast_def_macros.hpp" +#include "ast_fwd_decl.hpp" +#include "source_map.hpp" +#include "fn_utils.hpp" + +#include "sass.h" + +namespace Sass { + + ////////////////////////////////////////////////////////////////////// + // Still just an expression, but with a to_string method + ////////////////////////////////////////////////////////////////////// + class PreValue : public Expression { + public: + PreValue(ParserState pstate, + bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) + : Expression(pstate, d, e, i, ct) + { } + PreValue(const PreValue* ptr) + : Expression(ptr) + { } + ATTACH_VIRTUAL_AST_OPERATIONS(PreValue); + virtual ~PreValue() { } + }; + + ////////////////////////////////////////////////////////////////////// + // base class for values that support operations + ////////////////////////////////////////////////////////////////////// + class Value : public PreValue { + public: + Value(ParserState pstate, + bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) + : PreValue(pstate, d, e, i, ct) + { } + Value(const Value* ptr) + : PreValue(ptr) + { } + ATTACH_VIRTUAL_AST_OPERATIONS(Value); + virtual bool operator== (const Expression& rhs) const = 0; + }; + + /////////////////////////////////////////////////////////////////////// + // Lists of values, both comma- and space-separated (distinguished by a + // type-tag.) Also used to represent variable-length argument lists. + /////////////////////////////////////////////////////////////////////// + class List : public Value, public Vectorized { + void adjust_after_pushing(Expression_Obj e) { is_expanded(false); } + private: + ADD_PROPERTY(enum Sass_Separator, separator) + ADD_PROPERTY(bool, is_arglist) + ADD_PROPERTY(bool, is_bracketed) + ADD_PROPERTY(bool, from_selector) + public: + List(ParserState pstate, + size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false) + : Value(pstate), + Vectorized(size), + separator_(sep), + is_arglist_(argl), + is_bracketed_(bracket), + from_selector_(false) + { concrete_type(LIST); } + List(const List* ptr) + : Value(ptr), + Vectorized(*ptr), + separator_(ptr->separator_), + is_arglist_(ptr->is_arglist_), + is_bracketed_(ptr->is_bracketed_), + from_selector_(ptr->from_selector_) + { concrete_type(LIST); } + std::string type() const { return is_arglist_ ? "arglist" : "list"; } + static std::string type_name() { return "list"; } + const char* sep_string(bool compressed = false) const { + return separator() == SASS_SPACE ? + " " : (compressed ? "," : ", "); + } + bool is_invisible() const { return empty() && !is_bracketed(); } + Expression_Obj value_at_index(size_t i); + + virtual size_t size() const; + + virtual size_t hash() + { + if (hash_ == 0) { + hash_ = std::hash()(sep_string()); + hash_combine(hash_, std::hash()(is_bracketed())); + for (size_t i = 0, L = length(); i < L; ++i) + hash_combine(hash_, (elements()[i])->hash()); + } + return hash_; + } + + virtual void set_delayed(bool delayed) + { + is_delayed(delayed); + // don't set children + } + + virtual bool operator== (const Expression& rhs) const; + + ATTACH_AST_OPERATIONS(List) + ATTACH_CRTP_PERFORM_METHODS() + }; + + /////////////////////////////////////////////////////////////////////// + // Key value paris. + /////////////////////////////////////////////////////////////////////// + class Map : public Value, public Hashed { + void adjust_after_pushing(std::pair p) { is_expanded(false); } + public: + Map(ParserState pstate, + size_t size = 0) + : Value(pstate), + Hashed(size) + { concrete_type(MAP); } + Map(const Map* ptr) + : Value(ptr), + Hashed(*ptr) + { concrete_type(MAP); } + std::string type() const { return "map"; } + static std::string type_name() { return "map"; } + bool is_invisible() const { return empty(); } + List_Obj to_list(ParserState& pstate); + + virtual size_t hash() + { + if (hash_ == 0) { + for (auto key : keys()) { + hash_combine(hash_, key->hash()); + hash_combine(hash_, at(key)->hash()); + } + } + + return hash_; + } + + virtual bool operator== (const Expression& rhs) const; + + ATTACH_AST_OPERATIONS(Map) + ATTACH_CRTP_PERFORM_METHODS() + }; + + + ////////////////////////////////////////////////////////////////////////// + // Binary expressions. Represents logical, relational, and arithmetic + // operations. Templatized to avoid large switch statements and repetitive + // subclassing. + ////////////////////////////////////////////////////////////////////////// + class Binary_Expression : public PreValue { + private: + HASH_PROPERTY(Operand, op) + HASH_PROPERTY(Expression_Obj, left) + HASH_PROPERTY(Expression_Obj, right) + size_t hash_; + public: + Binary_Expression(ParserState pstate, + Operand op, Expression_Obj lhs, Expression_Obj rhs) + : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) + { } + Binary_Expression(const Binary_Expression* ptr) + : PreValue(ptr), + op_(ptr->op_), + left_(ptr->left_), + right_(ptr->right_), + hash_(ptr->hash_) + { } + const std::string type_name() { + return sass_op_to_name(optype()); + } + const std::string separator() { + return sass_op_separator(optype()); + } + bool is_left_interpolant(void) const; + bool is_right_interpolant(void) const; + bool has_interpolant() const + { + return is_left_interpolant() || + is_right_interpolant(); + } + virtual void set_delayed(bool delayed) + { + right()->set_delayed(delayed); + left()->set_delayed(delayed); + is_delayed(delayed); + } + virtual bool operator==(const Expression& rhs) const + { + try + { + Binary_Expression_Ptr_Const m = Cast(&rhs); + if (m == 0) return false; + return type() == m->type() && + *left() == *m->left() && + *right() == *m->right(); + } + catch (std::bad_cast&) + { + return false; + } + catch (...) { throw; } + } + virtual size_t hash() + { + if (hash_ == 0) { + hash_ = std::hash()(optype()); + hash_combine(hash_, left()->hash()); + hash_combine(hash_, right()->hash()); + } + return hash_; + } + enum Sass_OP optype() const { return op_.operand; } + ATTACH_AST_OPERATIONS(Binary_Expression) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////// + // Function reference. + //////////////////////////////////////////////////// + class Function final : public Value { + public: + ADD_PROPERTY(Definition_Obj, definition) + ADD_PROPERTY(bool, is_css) + public: + Function(ParserState pstate, Definition_Obj def, bool css) + : Value(pstate), definition_(def), is_css_(css) + { concrete_type(FUNCTION_VAL); } + Function(const Function* ptr) + : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_) + { concrete_type(FUNCTION_VAL); } + + std::string type() const override { return "function"; } + static std::string type_name() { return "function"; } + bool is_invisible() const override { return true; } + + std::string name() { + if (definition_) { + return definition_->name(); + } + return ""; + } + + bool operator== (const Expression& rhs) const override; + + ATTACH_AST_OPERATIONS(Function) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////// + // Function calls. + ////////////////// + class Function_Call final : public PreValue { + HASH_CONSTREF(String_Obj, sname) + HASH_PROPERTY(Arguments_Obj, arguments) + HASH_PROPERTY(Function_Obj, func) + ADD_PROPERTY(bool, via_call) + ADD_PROPERTY(void*, cookie) + mutable size_t hash_; + public: + 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_(), via_call_(false), cookie_(cookie), hash_(0) + { concrete_type(FUNCTION); } + 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, 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() const { + return sname(); + } + + Function_Call(const Function_Call* ptr) + : PreValue(ptr), + sname_(ptr->sname_), + arguments_(ptr->arguments_), + func_(ptr->func_), + via_call_(ptr->via_call_), + cookie_(ptr->cookie_), + hash_(ptr->hash_) + { concrete_type(FUNCTION); } + + bool is_css() { + if (func_) return func_->is_css(); + return false; + } + + bool operator==(const Expression& rhs) const override; + + size_t hash() const override; + + ATTACH_AST_OPERATIONS(Function_Call) + ATTACH_CRTP_PERFORM_METHODS() + }; + + /////////////////////// + // Variable references. + /////////////////////// + class Variable final : public PreValue { + ADD_CONSTREF(std::string, name) + public: + Variable(ParserState pstate, std::string n) + : PreValue(pstate), name_(n) + { concrete_type(VARIABLE); } + Variable(const Variable* ptr) + : PreValue(ptr), name_(ptr->name_) + { concrete_type(VARIABLE); } + + bool operator==(const Expression& rhs) const override + { + try + { + Variable_Ptr_Const e = Cast(&rhs); + return e && name() == e->name(); + } + catch (std::bad_cast&) + { + return false; + } + catch (...) { throw; } + } + + size_t hash() const override + { + return std::hash()(name()); + } + + ATTACH_AST_OPERATIONS(Variable) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////// + // Numbers, percentages, dimensions, and colors. + //////////////////////////////////////////////// + class Number final : public Value, public Units { + HASH_PROPERTY(double, value) + ADD_PROPERTY(bool, zero) + mutable size_t hash_; + public: + Number(ParserState pstate, double val, std::string u = "", bool zero = true); + + Number(const Number* ptr) + : Value(ptr), + Units(ptr), + value_(ptr->value_), zero_(ptr->zero_), + hash_(ptr->hash_) + { concrete_type(NUMBER); } + + bool zero() { return zero_; } + std::string type() const override { return "number"; } + static std::string type_name() { return "number"; } + + void reduce(); + void normalize(); + + size_t hash() const override + { + if (hash_ == 0) { + hash_ = std::hash()(value_); + for (const auto numerator : numerators) + hash_combine(hash_, std::hash()(numerator)); + for (const auto denominator : denominators) + hash_combine(hash_, std::hash()(denominator)); + } + return hash_; + } + + bool operator< (const Number& rhs) const; + bool operator== (const Number& rhs) const; + bool operator== (const Expression& rhs) const override; + ATTACH_AST_OPERATIONS(Number) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////// + // Colors. + ////////// + class Color final : public Value { + HASH_PROPERTY(double, r) + HASH_PROPERTY(double, g) + HASH_PROPERTY(double, b) + HASH_PROPERTY(double, a) + ADD_CONSTREF(std::string, disp) + mutable size_t hash_; + public: + Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "") + : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp), + hash_(0) + { concrete_type(COLOR); } + Color(const Color* ptr) + : Value(ptr), + r_(ptr->r_), + g_(ptr->g_), + b_(ptr->b_), + a_(ptr->a_), + disp_(ptr->disp_), + hash_(ptr->hash_) + { concrete_type(COLOR); } + std::string type() const override { return "color"; } + static std::string type_name() { return "color"; } + + size_t hash() const override + { + if (hash_ == 0) { + hash_ = std::hash()(a_); + hash_combine(hash_, std::hash()(r_)); + hash_combine(hash_, std::hash()(g_)); + hash_combine(hash_, std::hash()(b_)); + } + return hash_; + } + + bool operator== (const Expression& rhs) const override; + + ATTACH_AST_OPERATIONS(Color) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////// + // Errors from Sass_Values. + ////////////////////////////// + class Custom_Error final : public Value { + ADD_CONSTREF(std::string, message) + public: + Custom_Error(ParserState pstate, std::string msg) + : Value(pstate), message_(msg) + { concrete_type(C_ERROR); } + Custom_Error(const Custom_Error* ptr) + : Value(ptr), message_(ptr->message_) + { concrete_type(C_ERROR); } + bool operator== (const Expression& rhs) const override; + ATTACH_AST_OPERATIONS(Custom_Error) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////// + // Warnings from Sass_Values. + ////////////////////////////// + class Custom_Warning final : public Value { + ADD_CONSTREF(std::string, message) + public: + Custom_Warning(ParserState pstate, std::string msg) + : Value(pstate), message_(msg) + { concrete_type(C_WARNING); } + Custom_Warning(const Custom_Warning* ptr) + : Value(ptr), message_(ptr->message_) + { concrete_type(C_WARNING); } + bool operator== (const Expression& rhs) const override; + ATTACH_AST_OPERATIONS(Custom_Warning) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////// + // Booleans. + //////////// + class Boolean final : public Value { + HASH_PROPERTY(bool, value) + mutable size_t hash_; + public: + Boolean(ParserState pstate, bool val) + : Value(pstate), value_(val), + hash_(0) + { concrete_type(BOOLEAN); } + Boolean(const Boolean* ptr) + : Value(ptr), + value_(ptr->value_), + hash_(ptr->hash_) + { concrete_type(BOOLEAN); } + operator bool() override { return value_; } + std::string type() const override { return "bool"; } + static std::string type_name() { return "bool"; } + bool is_false() override { return !value_; } + + size_t hash() const override + { + if (hash_ == 0) { + hash_ = std::hash()(value_); + } + return hash_; + } + + bool operator== (const Expression& rhs) const override; + + ATTACH_AST_OPERATIONS(Boolean) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////////////////////////// + // Abstract base class for Sass string values. Includes interpolated and + // "flat" strings. + //////////////////////////////////////////////////////////////////////// + class String : public Value { + public: + String(ParserState pstate, bool delayed = false) + : Value(pstate, delayed) + { concrete_type(STRING); } + String(const String* ptr) + : Value(ptr) + { concrete_type(STRING); } + static std::string type_name() { return "string"; } + virtual ~String() = 0; + virtual void rtrim() = 0; + virtual bool operator<(const Expression& rhs) const { + return this->to_string() < rhs.to_string(); + }; + ATTACH_VIRTUAL_AST_OPERATIONS(String); + ATTACH_CRTP_PERFORM_METHODS() + }; + inline String::~String() { }; + + /////////////////////////////////////////////////////////////////////// + // Interpolated strings. Meant to be reduced to flat strings during the + // evaluation phase. + /////////////////////////////////////////////////////////////////////// + class String_Schema final : public String, public Vectorized { + ADD_PROPERTY(bool, css) + mutable size_t hash_; + public: + String_Schema(ParserState pstate, size_t size = 0, bool css = true) + : String(pstate), Vectorized(size), css_(css), hash_(0) + { concrete_type(STRING); } + String_Schema(const String_Schema* ptr) + : String(ptr), + Vectorized(*ptr), + css_(ptr->css_), + hash_(ptr->hash_) + { concrete_type(STRING); } + + std::string type() const override { return "string"; } + static std::string type_name() { return "string"; } + + bool is_left_interpolant(void) const override; + bool is_right_interpolant(void) const override; + // void has_interpolants(bool tc) { } + bool has_interpolants() { + for (auto el : elements()) { + if (el->is_interpolant()) return true; + } + return false; + } + void rtrim() override; + + size_t hash() const override + { + if (hash_ == 0) { + for (const auto &str : elements()) + hash_combine(hash_, str->hash()); + } + return hash_; + } + + void set_delayed(bool delayed) override { + is_delayed(delayed); + } + + bool operator==(const Expression& rhs) const override; + ATTACH_AST_OPERATIONS(String_Schema) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////////// + // Flat strings -- the lowest level of raw textual data. + //////////////////////////////////////////////////////// + class String_Constant : public String { + ADD_PROPERTY(char, quote_mark) + ADD_PROPERTY(bool, can_compress_whitespace) + HASH_CONSTREF(std::string, value) + protected: + mutable size_t hash_; + public: + String_Constant(const String_Constant* ptr) + : String(ptr), + quote_mark_(ptr->quote_mark_), + can_compress_whitespace_(ptr->can_compress_whitespace_), + value_(ptr->value_), + hash_(ptr->hash_) + { } + String_Constant(ParserState pstate, std::string val, bool css = true) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0) + { } + String_Constant(ParserState pstate, const char* beg, bool css = true) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0) + { } + String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0) + { } + String_Constant(ParserState pstate, const Token& tok, bool css = true) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0) + { } + std::string type() const override { return "string"; } + static std::string type_name() { return "string"; } + bool is_invisible() const override; + virtual void rtrim() override; + + size_t hash() const override + { + if (hash_ == 0) { + hash_ = std::hash()(value_); + } + return hash_; + } + + bool operator==(const Expression& rhs) const override; + virtual std::string inspect() const override; // quotes are forced on inspection + + // static char auto_quote() { return '*'; } + static char double_quote() { return '"'; } + static char single_quote() { return '\''; } + + ATTACH_AST_OPERATIONS(String_Constant) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////////// + // Possibly quoted string (unquote on instantiation) + //////////////////////////////////////////////////////// + class String_Quoted final : public String_Constant { + public: + String_Quoted(ParserState pstate, std::string val, char q = 0, + bool keep_utf8_escapes = false, bool skip_unquoting = false, + bool strict_unquoting = true, bool css = true) + : String_Constant(pstate, val, css) + { + if (skip_unquoting == false) { + value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting); + } + if (q && quote_mark_) quote_mark_ = q; + } + String_Quoted(const String_Quoted* ptr) + : String_Constant(ptr) + { } + bool operator==(const Expression& rhs) const override; + std::string inspect() const override; // quotes are forced on inspection + ATTACH_AST_OPERATIONS(String_Quoted) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////// + // The null value. + ////////////////// + class Null final : public Value { + public: + Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); } + Null(const Null* ptr) : Value(ptr) { concrete_type(NULL_VAL); } + std::string type() const override { return "null"; } + static std::string type_name() { return "null"; } + bool is_invisible() const override { return true; } + operator bool() override { return false; } + bool is_false() override { return true; } + + size_t hash() const override + { + return -1; + } + + bool operator== (const Expression& rhs) const override; + + ATTACH_AST_OPERATIONS(Null) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////////// + // The Parent Reference Expression. + ////////////////////////////////// + class Parent_Reference final : public Value { + public: + Parent_Reference(ParserState pstate) + : Value(pstate) {} + Parent_Reference(const Parent_Reference* ptr) + : Value(ptr) {} + std::string type() const override { return "parent"; } + static std::string type_name() { return "parent"; } + bool operator==(const Expression& rhs) const override { + return true; // can they ever be not equal? + }; + ATTACH_AST_OPERATIONS(Parent_Reference) + ATTACH_CRTP_PERFORM_METHODS() + }; + +} + +#endif \ No newline at end of file diff --git a/win/libsass.targets b/win/libsass.targets index 7c17408cf..5c7dda247 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -11,6 +11,7 @@ + @@ -78,6 +79,7 @@ + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index 9ac1d14ab..855ad11c2 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -48,6 +48,9 @@ Headers + + Headers + Headers @@ -245,6 +248,9 @@ Sources + + Sources + Sources