diff --git a/Makefile.conf b/Makefile.conf index cb7637528..c692e6cd6 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -8,6 +8,7 @@ SOURCES = \ ast.cpp \ ast_values.cpp \ + ast_supports.cpp \ ast_sel_cmp.cpp \ ast_sel_unify.cpp \ ast_selectors.cpp \ diff --git a/src/ast.cpp b/src/ast.cpp index 17afcf72a..943168e73 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -19,64 +19,617 @@ namespace Sass { static Null sass_null(ParserState("null")); - bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const { - if (Supports_Operator_Obj op = Cast(cond)) { - return op->operand() != operand(); + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + void AST_Node::update_pstate(const ParserState& pstate) + { + pstate_.offset += pstate - pstate_ + pstate.offset; + } + + const std::string AST_Node::to_string(Sass_Inspect_Options opt) const + { + Sass_Output_Options out(opt); + Emitter emitter(out); + Inspect i(emitter); + i.in_declaration = true; + // ToDo: inspect should be const + const_cast(this)->perform(&i); + return i.get_buffer(); + } + + const std::string AST_Node::to_string() const + { + return to_string({ NESTED, 5 }); + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Expression_Obj Hashed::at(Expression_Obj k) const + { + if (elements_.count(k)) + { return elements_.at(k); } + else { return {}; } + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Statement::Statement(ParserState pstate, Type st, size_t t) + : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false) + { } + Statement::Statement(const Statement* ptr) + : AST_Node(ptr), + statement_type_(ptr->statement_type_), + tabs_(ptr->tabs_), + group_end_(ptr->group_end_) + { } + + bool Statement::bubbles() + { + return false; + } + + bool Statement::has_content() + { + return statement_type_ == CONTENT; + } + + bool Statement::is_invisible() const + { + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Block::Block(ParserState pstate, size_t s, bool r) + : Statement(pstate), + Vectorized(s), + is_root_(r) + { } + Block::Block(const Block* ptr) + : Statement(ptr), + Vectorized(*ptr), + is_root_(ptr->is_root_) + { } + + bool Block::has_content() + { + for (size_t i = 0, L = elements().size(); i < L; ++i) { + if (elements()[i]->has_content()) return true; } - return Cast(cond) != NULL; + return Statement::has_content(); } - bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const { - return Cast(cond) || - Cast(cond); + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Has_Block::Has_Block(ParserState pstate, Block_Obj b) + : Statement(pstate), block_(b) + { } + Has_Block::Has_Block(const Has_Block* ptr) + : Statement(ptr), block_(ptr->block_) + { } + + bool Has_Block::has_content() + { + return (block_ && block_->has_content()) || Statement::has_content(); } - void Argument::set_delayed(bool delayed) + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Ruleset::Ruleset(ParserState pstate, Selector_List_Obj s, Block_Obj b) + : Has_Block(pstate, b), selector_(s), is_root_(false) + { statement_type(RULESET); } + Ruleset::Ruleset(const Ruleset* ptr) + : Has_Block(ptr), + selector_(ptr->selector_), + is_root_(ptr->is_root_) + { statement_type(RULESET); } + + bool Ruleset::is_invisible() const { + if (Selector_List_Ptr sl = Cast(selector())) { + for (size_t i = 0, L = sl->length(); i < L; ++i) + if (!(*sl)[i]->has_placeholder()) return false; + } + return true; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Bubble::Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g, size_t t) + : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0) + { } + Bubble::Bubble(const Bubble* ptr) + : Statement(ptr), + node_(ptr->node_), + group_end_(ptr->group_end_) + { } + + bool Bubble::bubbles() { - if (value_) value_->set_delayed(delayed); - is_delayed(delayed); + return true; } - void Arguments::set_delayed(bool delayed) + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Trace::Trace(ParserState pstate, std::string n, Block_Obj b, char type) + : Has_Block(pstate, b), type_(type), name_(n) + { } + Trace::Trace(const Trace* ptr) + : Has_Block(ptr), + type_(ptr->type_), + name_(ptr->name_) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Media_Block::Media_Block(ParserState pstate, List_Obj mqs, Block_Obj b) + : Has_Block(pstate, b), media_queries_(mqs) + { statement_type(MEDIA); } + Media_Block::Media_Block(const Media_Block* ptr) + : Has_Block(ptr), media_queries_(ptr->media_queries_) + { statement_type(MEDIA); } + + bool Media_Block::is_invisible() const { + for (size_t i = 0, L = block()->length(); i < L; ++i) { + Statement_Obj stm = block()->at(i); + if (!stm->is_invisible()) return false; + } + return true; + } + + bool Media_Block::bubbles() { - for (Argument_Obj arg : elements()) { - if (arg) arg->set_delayed(delayed); + return true; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Directive::Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel, Block_Obj b, Expression_Obj val) + : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed + { statement_type(DIRECTIVE); } + Directive::Directive(const Directive* ptr) + : Has_Block(ptr), + keyword_(ptr->keyword_), + selector_(ptr->selector_), + value_(ptr->value_) // set value manually if needed + { statement_type(DIRECTIVE); } + + bool Directive::bubbles() { return is_keyframes() || is_media(); } + + bool Directive::is_media() { + return keyword_.compare("@-webkit-media") == 0 || + keyword_.compare("@-moz-media") == 0 || + keyword_.compare("@-o-media") == 0 || + keyword_.compare("@media") == 0; + } + bool Directive::is_keyframes() { + return keyword_.compare("@-webkit-keyframes") == 0 || + keyword_.compare("@-moz-keyframes") == 0 || + keyword_.compare("@-o-keyframes") == 0 || + keyword_.compare("@keyframes") == 0; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Keyframe_Rule::Keyframe_Rule(ParserState pstate, Block_Obj b) + : Has_Block(pstate, b), name_() + { statement_type(KEYFRAMERULE); } + Keyframe_Rule::Keyframe_Rule(const Keyframe_Rule* ptr) + : Has_Block(ptr), name_(ptr->name_) + { statement_type(KEYFRAMERULE); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Declaration::Declaration(ParserState pstate, String_Obj prop, Expression_Obj val, bool i, bool c, Block_Obj b) + : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false) + { statement_type(DECLARATION); } + Declaration::Declaration(const Declaration* ptr) + : Has_Block(ptr), + property_(ptr->property_), + value_(ptr->value_), + is_important_(ptr->is_important_), + is_custom_property_(ptr->is_custom_property_), + is_indented_(ptr->is_indented_) + { statement_type(DECLARATION); } + + bool Declaration::is_invisible() const + { + if (is_custom_property()) return false; + return !(value_ && !Cast(value_)); + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Assignment::Assignment(ParserState pstate, std::string var, Expression_Obj val, bool is_default, bool is_global) + : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global) + { statement_type(ASSIGNMENT); } + Assignment::Assignment(const Assignment* ptr) + : Statement(ptr), + variable_(ptr->variable_), + value_(ptr->value_), + is_default_(ptr->is_default_), + is_global_(ptr->is_global_) + { statement_type(ASSIGNMENT); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Import::Import(ParserState pstate) + : Statement(pstate), + urls_(std::vector()), + incs_(std::vector()), + import_queries_() + { statement_type(IMPORT); } + Import::Import(const Import* ptr) + : Statement(ptr), + urls_(ptr->urls_), + incs_(ptr->incs_), + import_queries_(ptr->import_queries_) + { statement_type(IMPORT); } + + std::vector& Import::incs() { return incs_; } + std::vector& Import::urls() { return urls_; } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Import_Stub::Import_Stub(ParserState pstate, Include res) + : Statement(pstate), resource_(res) + { statement_type(IMPORT_STUB); } + Import_Stub::Import_Stub(const Import_Stub* ptr) + : Statement(ptr), resource_(ptr->resource_) + { statement_type(IMPORT_STUB); } + Include Import_Stub::resource() { return resource_; }; + std::string Import_Stub::imp_path() { return resource_.imp_path; }; + std::string Import_Stub::abs_path() { return resource_.abs_path; }; + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Warning::Warning(ParserState pstate, Expression_Obj msg) + : Statement(pstate), message_(msg) + { statement_type(WARNING); } + Warning::Warning(const Warning* ptr) + : Statement(ptr), message_(ptr->message_) + { statement_type(WARNING); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Error::Error(ParserState pstate, Expression_Obj msg) + : Statement(pstate), message_(msg) + { statement_type(ERROR); } + Error::Error(const Error* ptr) + : Statement(ptr), message_(ptr->message_) + { statement_type(ERROR); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Debug::Debug(ParserState pstate, Expression_Obj val) + : Statement(pstate), value_(val) + { statement_type(DEBUGSTMT); } + Debug::Debug(const Debug* ptr) + : Statement(ptr), value_(ptr->value_) + { statement_type(DEBUGSTMT); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Comment::Comment(ParserState pstate, String_Obj txt, bool is_important) + : Statement(pstate), text_(txt), is_important_(is_important) + { statement_type(COMMENT); } + Comment::Comment(const Comment* ptr) + : Statement(ptr), + text_(ptr->text_), + is_important_(ptr->is_important_) + { statement_type(COMMENT); } + + bool Comment::is_invisible() const + { + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + If::If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt) + : Has_Block(pstate, con), predicate_(pred), alternative_(alt) + { statement_type(IF); } + If::If(const If* ptr) + : Has_Block(ptr), + predicate_(ptr->predicate_), + alternative_(ptr->alternative_) + { statement_type(IF); } + + bool If::has_content() + { + return Has_Block::has_content() || (alternative_ && alternative_->has_content()); + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + For::For(ParserState pstate, + std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc) + : Has_Block(pstate, b), + variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc) + { statement_type(FOR); } + For::For(const For* ptr) + : Has_Block(ptr), + variable_(ptr->variable_), + lower_bound_(ptr->lower_bound_), + upper_bound_(ptr->upper_bound_), + is_inclusive_(ptr->is_inclusive_) + { statement_type(FOR); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Each::Each(ParserState pstate, std::vector vars, Expression_Obj lst, Block_Obj b) + : Has_Block(pstate, b), variables_(vars), list_(lst) + { statement_type(EACH); } + Each::Each(const Each* ptr) + : Has_Block(ptr), variables_(ptr->variables_), list_(ptr->list_) + { statement_type(EACH); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + While::While(ParserState pstate, Expression_Obj pred, Block_Obj b) + : Has_Block(pstate, b), predicate_(pred) + { statement_type(WHILE); } + While::While(const While* ptr) + : Has_Block(ptr), predicate_(ptr->predicate_) + { statement_type(WHILE); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Return::Return(ParserState pstate, Expression_Obj val) + : Statement(pstate), value_(val) + { statement_type(RETURN); } + Return::Return(const Return* ptr) + : Statement(ptr), value_(ptr->value_) + { statement_type(RETURN); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Extension::Extension(ParserState pstate, Selector_List_Obj s) + : Statement(pstate), selector_(s) + { statement_type(EXTEND); } + Extension::Extension(const Extension* ptr) + : Statement(ptr), selector_(ptr->selector_) + { statement_type(EXTEND); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Definition::Definition(const Definition* ptr) + : Has_Block(ptr), + name_(ptr->name_), + parameters_(ptr->parameters_), + environment_(ptr->environment_), + type_(ptr->type_), + native_function_(ptr->native_function_), + c_function_(ptr->c_function_), + cookie_(ptr->cookie_), + is_overload_stub_(ptr->is_overload_stub_), + signature_(ptr->signature_) + { } + + Definition::Definition(ParserState pstate, + std::string n, + Parameters_Obj params, + Block_Obj b, + Type t) + : Has_Block(pstate, b), + name_(n), + parameters_(params), + environment_(0), + type_(t), + native_function_(0), + c_function_(0), + cookie_(0), + is_overload_stub_(false), + signature_(0) + { } + + Definition::Definition(ParserState pstate, + Signature sig, + std::string n, + Parameters_Obj params, + Native_Function func_ptr, + bool overload_stub) + : Has_Block(pstate, {}), + name_(n), + parameters_(params), + environment_(0), + type_(FUNCTION), + native_function_(func_ptr), + c_function_(0), + cookie_(0), + is_overload_stub_(overload_stub), + signature_(sig) + { } + + Definition::Definition(ParserState pstate, + Signature sig, + std::string n, + Parameters_Obj params, + Sass_Function_Entry c_func) + : Has_Block(pstate, {}), + name_(n), + parameters_(params), + environment_(0), + type_(FUNCTION), + native_function_(0), + c_function_(c_func), + cookie_(sass_function_get_cookie(c_func)), + is_overload_stub_(false), + signature_(sig) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Mixin_Call::Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Parameters_Obj b_params, Block_Obj b) + : Has_Block(pstate, b), name_(n), arguments_(args), block_parameters_(b_params) + { } + Mixin_Call::Mixin_Call(const Mixin_Call* ptr) + : Has_Block(ptr), + name_(ptr->name_), + arguments_(ptr->arguments_), + block_parameters_(ptr->block_parameters_) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Content::Content(ParserState pstate, Arguments_Obj args) + : Statement(pstate), + arguments_(args) + { statement_type(CONTENT); } + Content::Content(const Content* ptr) + : Statement(ptr), + arguments_(ptr->arguments_) + { statement_type(CONTENT); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Unary_Expression::Unary_Expression(ParserState pstate, Type t, Expression_Obj o) + : Expression(pstate), optype_(t), operand_(o), hash_(0) + { } + Unary_Expression::Unary_Expression(const Unary_Expression* ptr) + : Expression(ptr), + optype_(ptr->optype_), + operand_(ptr->operand_), + hash_(ptr->hash_) + { } + const std::string Unary_Expression::type_name() { + switch (optype_) { + case PLUS: return "plus"; + case MINUS: return "minus"; + case SLASH: return "slash"; + case NOT: return "not"; + default: return "invalid"; } - is_delayed(delayed); + } + bool Unary_Expression::operator==(const Expression& rhs) const + { + try + { + Unary_Expression_Ptr_Const m = Cast(&rhs); + if (m == 0) return false; + return type() == m->type() && + *operand() == *m->operand(); + } + catch (std::bad_cast&) + { + return false; + } + catch (...) { throw; } + } + size_t Unary_Expression::hash() const + { + if (hash_ == 0) { + hash_ = std::hash()(optype_); + hash_combine(hash_, operand()->hash()); + }; + return hash_; } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// - bool At_Root_Query::exclude(std::string str) + Argument::Argument(ParserState pstate, Expression_Obj val, std::string n, bool rest, bool keyword) + : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0) { - bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; - List_Ptr l = static_cast(value().ptr()); - std::string v; + if (!name_.empty() && is_rest_argument_) { + coreError("variable-length argument may not be passed by name", pstate_); + } + } + Argument::Argument(const Argument* ptr) + : Expression(ptr), + value_(ptr->value_), + name_(ptr->name_), + is_rest_argument_(ptr->is_rest_argument_), + is_keyword_argument_(ptr->is_keyword_argument_), + hash_(ptr->hash_) + { + if (!name_.empty() && is_rest_argument_) { + coreError("variable-length argument may not be passed by name", pstate_); + } + } - if (with) + void Argument::set_delayed(bool delayed) + { + if (value_) value_->set_delayed(delayed); + is_delayed(delayed); + } + + bool Argument::operator==(const Expression& rhs) const + { + try { - if (!l || l->length() == 0) return str.compare("rule") != 0; - for (size_t i = 0, L = l->length(); i < L; ++i) - { - v = unquote((*l)[i]->to_string()); - if (v.compare("all") == 0 || v == str) return false; - } - return true; + Argument_Ptr_Const m = Cast(&rhs); + if (!(m && name() == m->name())) return false; + return *value() == *m->value(); } - else + catch (std::bad_cast&) { - if (!l || !l->length()) return str.compare("rule") == 0; - for (size_t i = 0, L = l->length(); i < L; ++i) - { - v = unquote((*l)[i]->to_string()); - if (v.compare("all") == 0 || v == str) return true; - } return false; } + catch (...) { throw; } } - void AST_Node::update_pstate(const ParserState& pstate) + size_t Argument::hash() const { - pstate_.offset += pstate - pstate_ + pstate.offset; + if (hash_ == 0) { + hash_ = std::hash()(name()); + hash_combine(hash_, value()->hash()); + } + return hash_; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Arguments::Arguments(ParserState pstate) + : Expression(pstate), + Vectorized(), + has_named_arguments_(false), + has_rest_argument_(false), + has_keyword_argument_(false) + { } + Arguments::Arguments(const Arguments* ptr) + : Expression(ptr), + Vectorized(*ptr), + has_named_arguments_(ptr->has_named_arguments_), + has_rest_argument_(ptr->has_rest_argument_), + has_keyword_argument_(ptr->has_keyword_argument_) + { } + + void Arguments::set_delayed(bool delayed) + { + for (Argument_Obj arg : elements()) { + if (arg) arg->set_delayed(delayed); + } + is_delayed(delayed); } Argument_Obj Arguments::get_rest_argument() @@ -136,86 +689,185 @@ namespace Sass { } } - bool Ruleset::is_invisible() const { - if (Selector_List_Ptr sl = Cast(selector())) { - for (size_t i = 0, L = sl->length(); i < L; ++i) - if (!(*sl)[i]->has_placeholder()) return false; + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Media_Query::Media_Query(ParserState pstate, String_Obj t, size_t s, bool n, bool r) + : Expression(pstate), Vectorized(s), + media_type_(t), is_negated_(n), is_restricted_(r) + { } + Media_Query::Media_Query(const Media_Query* ptr) + : Expression(ptr), + Vectorized(*ptr), + media_type_(ptr->media_type_), + is_negated_(ptr->is_negated_), + is_restricted_(ptr->is_restricted_) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Media_Query_Expression::Media_Query_Expression(ParserState pstate, + Expression_Obj f, Expression_Obj v, bool i) + : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) + { } + Media_Query_Expression::Media_Query_Expression(const Media_Query_Expression* ptr) + : Expression(ptr), + feature_(ptr->feature_), + value_(ptr->value_), + is_interpolated_(ptr->is_interpolated_) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + At_Root_Query::At_Root_Query(ParserState pstate, Expression_Obj f, Expression_Obj v, bool i) + : Expression(pstate), feature_(f), value_(v) + { } + At_Root_Query::At_Root_Query(const At_Root_Query* ptr) + : Expression(ptr), + feature_(ptr->feature_), + value_(ptr->value_) + { } + + bool At_Root_Query::exclude(std::string str) + { + bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; + List_Ptr l = static_cast(value().ptr()); + std::string v; + + if (with) + { + if (!l || l->length() == 0) return str.compare("rule") != 0; + for (size_t i = 0, L = l->length(); i < L; ++i) + { + v = unquote((*l)[i]->to_string()); + if (v.compare("all") == 0 || v == str) return false; + } + return true; + } + else + { + if (!l || !l->length()) return str.compare("rule") == 0; + for (size_t i = 0, L = l->length(); i < L; ++i) + { + v = unquote((*l)[i]->to_string()); + if (v.compare("all") == 0 || v == str) return true; + } + return false; } - return true; } - bool Media_Block::is_invisible() const { - for (size_t i = 0, L = block()->length(); i < L; ++i) { - Statement_Obj stm = block()->at(i); - if (!stm->is_invisible()) return false; - } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + At_Root_Block::At_Root_Block(ParserState pstate, Block_Obj b, At_Root_Query_Obj e) + : Has_Block(pstate, b), expression_(e) + { statement_type(ATROOT); } + At_Root_Block::At_Root_Block(const At_Root_Block* ptr) + : Has_Block(ptr), expression_(ptr->expression_) + { statement_type(ATROOT); } + + bool At_Root_Block::bubbles() { return true; } - Expression_Obj Hashed::at(Expression_Obj k) const - { - if (elements_.count(k)) - { return elements_.at(k); } - else { return {}; } - } + bool At_Root_Block::exclude_node(Statement_Obj s) { + if (expression() == 0) + { + return s->statement_type() == Statement::RULESET; + } - bool Binary_Expression::is_left_interpolant(void) const - { - return is_interpolant() || (left() && left()->is_left_interpolant()); - } - bool Binary_Expression::is_right_interpolant(void) const - { - return is_interpolant() || (right() && right()->is_right_interpolant()); + if (s->statement_type() == Statement::DIRECTIVE) + { + if (Directive_Obj dir = Cast(s)) + { + std::string keyword(dir->keyword()); + if (keyword.length() > 0) keyword.erase(0, 1); + return expression()->exclude(keyword); + } + } + if (s->statement_type() == Statement::MEDIA) + { + return expression()->exclude("media"); + } + if (s->statement_type() == Statement::RULESET) + { + return expression()->exclude("rule"); + } + if (s->statement_type() == Statement::SUPPORTS) + { + return expression()->exclude("supports"); + } + if (Directive_Obj dir = Cast(s)) + { + if (dir->is_keyframes()) return expression()->exclude("keyframes"); + } + return false; } - const std::string AST_Node::to_string(Sass_Inspect_Options opt) const - { - Sass_Output_Options out(opt); - Emitter emitter(out); - Inspect i(emitter); - i.in_declaration = true; - // ToDo: inspect should be const - const_cast(this)->perform(&i); - return i.get_buffer(); - } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// - const std::string AST_Node::to_string() const - { - return to_string({ NESTED, 5 }); - } + Parameter::Parameter(ParserState pstate, std::string n, Expression_Obj def, bool rest) + : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest) + { } + Parameter::Parameter(const Parameter* ptr) + : AST_Node(ptr), + name_(ptr->name_), + default_value_(ptr->default_value_), + is_rest_parameter_(ptr->is_rest_parameter_) + { } - bool Declaration::is_invisible() const - { - if (is_custom_property()) return false; + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// - return !(value_ && value_->concrete_type() != Expression::NULL_VAL); - } + Parameters::Parameters(ParserState pstate) + : AST_Node(pstate), + Vectorized(), + has_optional_parameters_(false), + has_rest_parameter_(false) + { } + Parameters::Parameters(const Parameters* ptr) + : AST_Node(ptr), + Vectorized(*ptr), + has_optional_parameters_(ptr->has_optional_parameters_), + has_rest_parameter_(ptr->has_rest_parameter_) + { } - ////////////////////////////////////////////////////////////////////////////////////////// - // Additional method on Lists to retrieve values directly or from an encompassed Argument. - ////////////////////////////////////////////////////////////////////////////////////////// - Expression_Obj List::value_at_index(size_t i) { - Expression_Obj obj = this->at(i); - if (is_arglist_) { - if (Argument_Ptr arg = Cast(obj)) { - return arg->value(); - } else { - return obj; + void Parameters::adjust_after_pushing(Parameter_Obj p) + { + if (p->default_value()) { + if (has_rest_parameter()) { + coreError("optional parameters may not be combined with variable-length parameters", p->pstate()); + } + has_optional_parameters(true); + } + else if (p->is_rest_parameter()) { + if (has_rest_parameter()) { + coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate()); + } + has_rest_parameter(true); + } + else { + if (has_rest_parameter()) { + coreError("required parameters must precede variable-length parameters", p->pstate()); + } + if (has_optional_parameters()) { + coreError("required parameters must precede optional parameters", p->pstate()); } - } else { - return obj; } } - IMPLEMENT_AST_OPERATORS(Supports_Operator); - IMPLEMENT_AST_OPERATORS(Supports_Negation); + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + IMPLEMENT_AST_OPERATORS(Ruleset); IMPLEMENT_AST_OPERATORS(Media_Block); IMPLEMENT_AST_OPERATORS(Import); IMPLEMENT_AST_OPERATORS(Import_Stub); IMPLEMENT_AST_OPERATORS(Directive); IMPLEMENT_AST_OPERATORS(At_Root_Block); - IMPLEMENT_AST_OPERATORS(Supports_Block); IMPLEMENT_AST_OPERATORS(While); IMPLEMENT_AST_OPERATORS(Each); IMPLEMENT_AST_OPERATORS(For); @@ -231,9 +883,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Return); IMPLEMENT_AST_OPERATORS(At_Root_Query); IMPLEMENT_AST_OPERATORS(Comment); - IMPLEMENT_AST_OPERATORS(Supports_Interpolation); - IMPLEMENT_AST_OPERATORS(Supports_Declaration); - IMPLEMENT_AST_OPERATORS(Supports_Condition); IMPLEMENT_AST_OPERATORS(Parameters); IMPLEMENT_AST_OPERATORS(Parameter); IMPLEMENT_AST_OPERATORS(Arguments); @@ -246,4 +895,8 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Bubble); IMPLEMENT_AST_OPERATORS(Definition); IMPLEMENT_AST_OPERATORS(Declaration); + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + } diff --git a/src/ast.hpp b/src/ast.hpp index 4cf99d502..abdb34a95 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -69,6 +69,48 @@ namespace Sass { } ////////////////////////////////////////////////////////// + inline static const std::string sass_op_to_name(enum Sass_OP op) { + switch (op) { + case AND: return "and"; + case OR: return "or"; + case EQ: return "eq"; + case NEQ: return "neq"; + case GT: return "gt"; + case GTE: return "gte"; + case LT: return "lt"; + case LTE: return "lte"; + case ADD: return "plus"; + case SUB: return "sub"; + case MUL: return "times"; + case DIV: return "div"; + case MOD: return "mod"; + // this is only used internally! + case NUM_OPS: return "[OPS]"; + default: return "invalid"; + } + } + + inline static const std::string sass_op_separator(enum Sass_OP op) { + switch (op) { + case AND: return "&&"; + case OR: return "||"; + case EQ: return "=="; + case NEQ: return "!="; + case GT: return ">"; + case GTE: return ">="; + case LT: return "<"; + case LTE: return "<="; + case ADD: return "+"; + case SUB: return "-"; + case MUL: return "*"; + case DIV: return "/"; + case MOD: return "%"; + // this is only used internally! + case NUM_OPS: return "[OPS]"; + default: return "invalid"; + } + } + ////////////////////////////////////////////////////////// // Abstract base class for all abstract syntax tree nodes. ////////////////////////////////////////////////////////// @@ -146,6 +188,7 @@ namespace Sass { C_ERROR, FUNCTION, VARIABLE, + PARENT, NUM_TYPES }; private: @@ -172,9 +215,11 @@ namespace Sass { { } virtual operator bool() { return true; } virtual ~Expression() { } - virtual std::string type() const { return ""; /* TODO: raise an error? */ } virtual bool is_invisible() const { return false; } + + virtual std::string type() const { return ""; } static std::string type_name() { return ""; } + virtual bool is_false() { return false; } // virtual bool is_true() { return !is_false(); } virtual bool operator< (const Expression& rhs) const { return false; } @@ -389,23 +434,14 @@ namespace Sass { ADD_PROPERTY(size_t, tabs) ADD_PROPERTY(bool, group_end) public: - Statement(ParserState pstate, Type st = NONE, size_t t = 0) - : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false) - { } - Statement(const Statement* ptr) - : AST_Node(ptr), - statement_type_(ptr->statement_type_), - tabs_(ptr->tabs_), - group_end_(ptr->group_end_) - { } - virtual ~Statement() = 0; + Statement(ParserState pstate, Type st = NONE, size_t t = 0); + Statement(const Statement* ptr); // copy constructor + virtual ~Statement() = 0; // virtual destructor // needed for rearranging nested rulesets during CSS emission - virtual bool is_invisible() const { return false; } - virtual bool bubbles() { return false; } - virtual bool has_content() - { - return statement_type_ == CONTENT; - } + virtual bool bubbles(); + virtual bool has_content(); + virtual bool is_invisible() const; + ATTACH_VIRTUAL_AST_OPERATIONS(Statement) }; inline Statement::~Statement() { } @@ -416,27 +452,10 @@ namespace Sass { ADD_PROPERTY(bool, is_root) // needed for properly formatted CSS emission protected: - void adjust_after_pushing(Statement_Obj s) override - { - } + void adjust_after_pushing(Statement_Obj s) override {} public: - Block(ParserState pstate, size_t s = 0, bool r = false) - : Statement(pstate), - Vectorized(s), - is_root_(r) - { } - Block(const Block* ptr) - : Statement(ptr), - Vectorized(*ptr), - is_root_(ptr->is_root_) - { } - bool has_content() override - { - for (size_t i = 0, L = elements().size(); i < L; ++i) { - if (elements()[i]->has_content()) return true; - } - return Statement::has_content(); - } + Block(ParserState pstate, size_t s = 0, bool r = false); + bool has_content() override; ATTACH_AST_OPERATIONS(Block) ATTACH_CRTP_PERFORM_METHODS() }; @@ -447,17 +466,10 @@ namespace Sass { class Has_Block : public Statement { ADD_PROPERTY(Block_Obj, block) public: - Has_Block(ParserState pstate, Block_Obj b) - : Statement(pstate), block_(b) - { } - Has_Block(const Has_Block* ptr) - : Statement(ptr), block_(ptr->block_) - { } - virtual bool has_content() override - { - return (block_ && block_->has_content()) || Statement::has_content(); - } - virtual ~Has_Block() = 0; + Has_Block(ParserState pstate, Block_Obj b); + Has_Block(const Has_Block* ptr); // copy constructor + virtual ~Has_Block() = 0; // virtual destructor + virtual bool has_content() override; }; inline Has_Block::~Has_Block() { } @@ -469,14 +481,7 @@ namespace Sass { ADD_PROPERTY(Selector_List_Obj, selector) ADD_PROPERTY(bool, is_root); public: - Ruleset(ParserState pstate, Selector_List_Obj s = {}, Block_Obj b = {}) - : Has_Block(pstate, b), selector_(s), is_root_(false) - { statement_type(RULESET); } - Ruleset(const Ruleset* ptr) - : Has_Block(ptr), - selector_(ptr->selector_), - is_root_(ptr->is_root_) - { statement_type(RULESET); } + Ruleset(ParserState pstate, Selector_List_Obj s = {}, Block_Obj b = {}); bool is_invisible() const override; ATTACH_AST_OPERATIONS(Ruleset) ATTACH_CRTP_PERFORM_METHODS() @@ -489,15 +494,8 @@ namespace Sass { ADD_PROPERTY(Statement_Obj, node) ADD_PROPERTY(bool, group_end) public: - Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = {}, size_t t = 0) - : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0) - { } - Bubble(const Bubble* ptr) - : Statement(ptr), - node_(ptr->node_), - group_end_(ptr->group_end_) - { } - bool bubbles() override { return true; } + Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = {}, size_t t = 0); + bool bubbles() override; ATTACH_AST_OPERATIONS(Bubble) ATTACH_CRTP_PERFORM_METHODS() }; @@ -509,14 +507,7 @@ namespace Sass { ADD_CONSTREF(char, type) ADD_CONSTREF(std::string, name) public: - Trace(ParserState pstate, std::string n, Block_Obj b = {}, char type = 'm') - : Has_Block(pstate, b), type_(type), name_(n) - { } - Trace(const Trace* ptr) - : Has_Block(ptr), - type_(ptr->type_), - name_(ptr->name_) - { } + Trace(ParserState pstate, std::string n, Block_Obj b = {}, char type = 'm'); ATTACH_AST_OPERATIONS(Trace) ATTACH_CRTP_PERFORM_METHODS() }; @@ -527,13 +518,8 @@ namespace Sass { class Media_Block final : public Has_Block { ADD_PROPERTY(List_Obj, media_queries) public: - Media_Block(ParserState pstate, List_Obj mqs, Block_Obj b) - : Has_Block(pstate, b), media_queries_(mqs) - { statement_type(MEDIA); } - Media_Block(const Media_Block* ptr) - : Has_Block(ptr), media_queries_(ptr->media_queries_) - { statement_type(MEDIA); } - bool bubbles() override { return true; } + Media_Block(ParserState pstate, List_Obj mqs, Block_Obj b); + bool bubbles() override; bool is_invisible() const override; ATTACH_AST_OPERATIONS(Media_Block) ATTACH_CRTP_PERFORM_METHODS() @@ -548,28 +534,10 @@ namespace Sass { ADD_PROPERTY(Selector_List_Obj, selector) ADD_PROPERTY(Expression_Obj, value) public: - Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel = {}, Block_Obj b = {}, Expression_Obj val = {}) - : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed - { statement_type(DIRECTIVE); } - Directive(const Directive* ptr) - : Has_Block(ptr), - keyword_(ptr->keyword_), - selector_(ptr->selector_), - value_(ptr->value_) // set value manually if needed - { statement_type(DIRECTIVE); } - bool bubbles() override { return is_keyframes() || is_media(); } - bool is_media() { - return keyword_.compare("@-webkit-media") == 0 || - keyword_.compare("@-moz-media") == 0 || - keyword_.compare("@-o-media") == 0 || - keyword_.compare("@media") == 0; - } - bool is_keyframes() { - return keyword_.compare("@-webkit-keyframes") == 0 || - keyword_.compare("@-moz-keyframes") == 0 || - keyword_.compare("@-o-keyframes") == 0 || - keyword_.compare("@keyframes") == 0; - } + Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel = {}, Block_Obj b = {}, Expression_Obj val = {}); + bool bubbles() override; + bool is_media(); + bool is_keyframes(); ATTACH_AST_OPERATIONS(Directive) ATTACH_CRTP_PERFORM_METHODS() }; @@ -582,12 +550,7 @@ namespace Sass { // = | ADD_PROPERTY(Selector_List_Obj, name) public: - Keyframe_Rule(ParserState pstate, Block_Obj b) - : Has_Block(pstate, b), name_() - { statement_type(KEYFRAMERULE); } - Keyframe_Rule(const Keyframe_Rule* ptr) - : Has_Block(ptr), name_(ptr->name_) - { statement_type(KEYFRAMERULE); } + Keyframe_Rule(ParserState pstate, Block_Obj b); ATTACH_AST_OPERATIONS(Keyframe_Rule) ATTACH_CRTP_PERFORM_METHODS() }; @@ -602,18 +565,7 @@ namespace Sass { ADD_PROPERTY(bool, is_custom_property) ADD_PROPERTY(bool, is_indented) public: - Declaration(ParserState pstate, - String_Obj prop, Expression_Obj val, bool i = false, bool c = false, Block_Obj b = {}) - : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false) - { statement_type(DECLARATION); } - Declaration(const Declaration* ptr) - : Has_Block(ptr), - property_(ptr->property_), - value_(ptr->value_), - is_important_(ptr->is_important_), - is_custom_property_(ptr->is_custom_property_), - is_indented_(ptr->is_indented_) - { statement_type(DECLARATION); } + Declaration(ParserState pstate, String_Obj prop, Expression_Obj val, bool i = false, bool c = false, Block_Obj b = {}); bool is_invisible() const override; ATTACH_AST_OPERATIONS(Declaration) ATTACH_CRTP_PERFORM_METHODS() @@ -628,19 +580,7 @@ namespace Sass { ADD_PROPERTY(bool, is_default) ADD_PROPERTY(bool, is_global) public: - Assignment(ParserState pstate, - std::string var, Expression_Obj val, - bool is_default = false, - bool is_global = false) - : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global) - { statement_type(ASSIGNMENT); } - Assignment(const Assignment* ptr) - : Statement(ptr), - variable_(ptr->variable_), - value_(ptr->value_), - is_default_(ptr->is_default_), - is_global_(ptr->is_global_) - { statement_type(ASSIGNMENT); } + Assignment(ParserState pstate, std::string var, Expression_Obj val, bool is_default = false, bool is_global = false); ATTACH_AST_OPERATIONS(Assignment) ATTACH_CRTP_PERFORM_METHODS() }; @@ -651,23 +591,12 @@ namespace Sass { //////////////////////////////////////////////////////////////////////////// class Import final : public Statement { std::vector urls_; - std::vector incs_; + std::vector incs_; ADD_PROPERTY(List_Obj, import_queries); public: - Import(ParserState pstate) - : Statement(pstate), - urls_(std::vector()), - incs_(std::vector()), - import_queries_() - { statement_type(IMPORT); } - Import(const Import* ptr) - : Statement(ptr), - urls_(ptr->urls_), - incs_(ptr->incs_), - import_queries_(ptr->import_queries_) - { statement_type(IMPORT); } - std::vector& urls() { return urls_; } - std::vector& incs() { return incs_; } + Import(ParserState pstate); + std::vector& incs(); + std::vector& urls(); ATTACH_AST_OPERATIONS(Import) ATTACH_CRTP_PERFORM_METHODS() }; @@ -677,16 +606,10 @@ namespace Sass { class Import_Stub final : public Statement { Include resource_; public: - std::string abs_path() { return resource_.abs_path; }; - std::string imp_path() { return resource_.imp_path; }; - Include resource() { return resource_; }; - - Import_Stub(ParserState pstate, Include res) - : Statement(pstate), resource_(res) - { statement_type(IMPORT_STUB); } - Import_Stub(const Import_Stub* ptr) - : Statement(ptr), resource_(ptr->resource_) - { statement_type(IMPORT_STUB); } + Import_Stub(ParserState pstate, Include res); + Include resource(); + std::string imp_path(); + std::string abs_path(); ATTACH_AST_OPERATIONS(Import_Stub) ATTACH_CRTP_PERFORM_METHODS() }; @@ -697,12 +620,7 @@ namespace Sass { class Warning final : public Statement { ADD_PROPERTY(Expression_Obj, message) public: - Warning(ParserState pstate, Expression_Obj msg) - : Statement(pstate), message_(msg) - { statement_type(WARNING); } - Warning(const Warning* ptr) - : Statement(ptr), message_(ptr->message_) - { statement_type(WARNING); } + Warning(ParserState pstate, Expression_Obj msg); ATTACH_AST_OPERATIONS(Warning) ATTACH_CRTP_PERFORM_METHODS() }; @@ -713,12 +631,7 @@ namespace Sass { class Error final : public Statement { ADD_PROPERTY(Expression_Obj, message) public: - Error(ParserState pstate, Expression_Obj msg) - : Statement(pstate), message_(msg) - { statement_type(ERROR); } - Error(const Error* ptr) - : Statement(ptr), message_(ptr->message_) - { statement_type(ERROR); } + Error(ParserState pstate, Expression_Obj msg); ATTACH_AST_OPERATIONS(Error) ATTACH_CRTP_PERFORM_METHODS() }; @@ -729,12 +642,7 @@ namespace Sass { class Debug final : public Statement { ADD_PROPERTY(Expression_Obj, value) public: - Debug(ParserState pstate, Expression_Obj val) - : Statement(pstate), value_(val) - { statement_type(DEBUGSTMT); } - Debug(const Debug* ptr) - : Statement(ptr), value_(ptr->value_) - { statement_type(DEBUGSTMT); } + Debug(ParserState pstate, Expression_Obj val); ATTACH_AST_OPERATIONS(Debug) ATTACH_CRTP_PERFORM_METHODS() }; @@ -746,16 +654,8 @@ namespace Sass { ADD_PROPERTY(String_Obj, text) ADD_PROPERTY(bool, is_important) public: - Comment(ParserState pstate, String_Obj txt, bool is_important) - : Statement(pstate), text_(txt), is_important_(is_important) - { statement_type(COMMENT); } - Comment(const Comment* ptr) - : Statement(ptr), - text_(ptr->text_), - is_important_(ptr->is_important_) - { statement_type(COMMENT); } - bool is_invisible() const override - { return /* is_important() == */ false; } + Comment(ParserState pstate, String_Obj txt, bool is_important); + virtual bool is_invisible() const override; ATTACH_AST_OPERATIONS(Comment) ATTACH_CRTP_PERFORM_METHODS() }; @@ -767,18 +667,8 @@ namespace Sass { ADD_PROPERTY(Expression_Obj, predicate) ADD_PROPERTY(Block_Obj, alternative) public: - If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt = {}) - : Has_Block(pstate, con), predicate_(pred), alternative_(alt) - { statement_type(IF); } - If(const If* ptr) - : Has_Block(ptr), - predicate_(ptr->predicate_), - alternative_(ptr->alternative_) - { statement_type(IF); } - bool has_content() override - { - return Has_Block::has_content() || (alternative_ && alternative_->has_content()); - } + If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt = {}); + virtual bool has_content() override; ATTACH_AST_OPERATIONS(If) ATTACH_CRTP_PERFORM_METHODS() }; @@ -792,18 +682,7 @@ namespace Sass { ADD_PROPERTY(Expression_Obj, upper_bound) ADD_PROPERTY(bool, is_inclusive) public: - For(ParserState pstate, - std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc) - : Has_Block(pstate, b), - variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc) - { statement_type(FOR); } - For(const For* ptr) - : Has_Block(ptr), - variable_(ptr->variable_), - lower_bound_(ptr->lower_bound_), - upper_bound_(ptr->upper_bound_), - is_inclusive_(ptr->is_inclusive_) - { statement_type(FOR); } + For(ParserState pstate, std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc); ATTACH_AST_OPERATIONS(For) ATTACH_CRTP_PERFORM_METHODS() }; @@ -815,12 +694,7 @@ namespace Sass { ADD_PROPERTY(std::vector, variables) ADD_PROPERTY(Expression_Obj, list) public: - Each(ParserState pstate, std::vector vars, Expression_Obj lst, Block_Obj b) - : Has_Block(pstate, b), variables_(vars), list_(lst) - { statement_type(EACH); } - Each(const Each* ptr) - : Has_Block(ptr), variables_(ptr->variables_), list_(ptr->list_) - { statement_type(EACH); } + Each(ParserState pstate, std::vector vars, Expression_Obj lst, Block_Obj b); ATTACH_AST_OPERATIONS(Each) ATTACH_CRTP_PERFORM_METHODS() }; @@ -831,12 +705,7 @@ namespace Sass { class While final : public Has_Block { ADD_PROPERTY(Expression_Obj, predicate) public: - While(ParserState pstate, Expression_Obj pred, Block_Obj b) - : Has_Block(pstate, b), predicate_(pred) - { statement_type(WHILE); } - While(const While* ptr) - : Has_Block(ptr), predicate_(ptr->predicate_) - { statement_type(WHILE); } + While(ParserState pstate, Expression_Obj pred, Block_Obj b); ATTACH_AST_OPERATIONS(While) ATTACH_CRTP_PERFORM_METHODS() }; @@ -847,12 +716,7 @@ namespace Sass { class Return final : public Statement { ADD_PROPERTY(Expression_Obj, value) public: - Return(ParserState pstate, Expression_Obj val) - : Statement(pstate), value_(val) - { statement_type(RETURN); } - Return(const Return* ptr) - : Statement(ptr), value_(ptr->value_) - { statement_type(RETURN); } + Return(ParserState pstate, Expression_Obj val); ATTACH_AST_OPERATIONS(Return) ATTACH_CRTP_PERFORM_METHODS() }; @@ -863,12 +727,7 @@ namespace Sass { class Extension final : public Statement { ADD_PROPERTY(Selector_List_Obj, selector) public: - Extension(ParserState pstate, Selector_List_Obj s) - : Statement(pstate), selector_(s) - { statement_type(EXTEND); } - Extension(const Extension* ptr) - : Statement(ptr), selector_(ptr->selector_) - { statement_type(EXTEND); } + Extension(ParserState pstate, Selector_List_Obj s); ATTACH_AST_OPERATIONS(Extension) ATTACH_CRTP_PERFORM_METHODS() }; @@ -890,68 +749,22 @@ namespace Sass { ADD_PROPERTY(bool, is_overload_stub) ADD_PROPERTY(Signature, signature) public: - Definition(const Definition* ptr) - : Has_Block(ptr), - name_(ptr->name_), - parameters_(ptr->parameters_), - environment_(ptr->environment_), - type_(ptr->type_), - native_function_(ptr->native_function_), - c_function_(ptr->c_function_), - cookie_(ptr->cookie_), - is_overload_stub_(ptr->is_overload_stub_), - signature_(ptr->signature_) - { } - Definition(ParserState pstate, std::string n, Parameters_Obj params, Block_Obj b, - Type t) - : Has_Block(pstate, b), - name_(n), - parameters_(params), - environment_(0), - type_(t), - native_function_(0), - c_function_(0), - cookie_(0), - is_overload_stub_(false), - signature_(0) - { } + Type t); Definition(ParserState pstate, Signature sig, std::string n, Parameters_Obj params, Native_Function func_ptr, - bool overload_stub = false) - : Has_Block(pstate, {}), - name_(n), - parameters_(params), - environment_(0), - type_(FUNCTION), - native_function_(func_ptr), - c_function_(0), - cookie_(0), - is_overload_stub_(overload_stub), - signature_(sig) - { } + bool overload_stub = false); Definition(ParserState pstate, Signature sig, std::string n, Parameters_Obj params, - Sass_Function_Entry c_func) - : Has_Block(pstate, {}), - name_(n), - parameters_(params), - environment_(0), - type_(FUNCTION), - native_function_(0), - c_function_(c_func), - cookie_(sass_function_get_cookie(c_func)), - is_overload_stub_(false), - signature_(sig) - { } + Sass_Function_Entry c_func); ATTACH_AST_OPERATIONS(Definition) ATTACH_CRTP_PERFORM_METHODS() }; @@ -964,15 +777,7 @@ namespace Sass { ADD_PROPERTY(Arguments_Obj, arguments) ADD_PROPERTY(Parameters_Obj, block_parameters) public: - Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Parameters_Obj b_params = {}, Block_Obj b = {}) - : Has_Block(pstate, b), name_(n), arguments_(args), block_parameters_(b_params) - { } - Mixin_Call(const Mixin_Call* ptr) - : Has_Block(ptr), - name_(ptr->name_), - arguments_(ptr->arguments_), - block_parameters_(ptr->block_parameters_) - { } + Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Parameters_Obj b_params = {}, Block_Obj b = {}); ATTACH_AST_OPERATIONS(Mixin_Call) ATTACH_CRTP_PERFORM_METHODS() }; @@ -983,60 +788,11 @@ namespace Sass { class Content final : public Statement { ADD_PROPERTY(Arguments_Obj, arguments) public: - Content(ParserState pstate, Arguments_Obj args) - : Statement(pstate), - arguments_(args) - { statement_type(CONTENT); } - Content(const Content* ptr) - : Statement(ptr), - arguments_(ptr->arguments_) - { statement_type(CONTENT); } + Content(ParserState pstate, Arguments_Obj args); ATTACH_AST_OPERATIONS(Content) ATTACH_CRTP_PERFORM_METHODS() }; - inline static const std::string sass_op_to_name(enum Sass_OP op) { - switch (op) { - case AND: return "and"; - case OR: return "or"; - case EQ: return "eq"; - case NEQ: return "neq"; - case GT: return "gt"; - case GTE: return "gte"; - case LT: return "lt"; - case LTE: return "lte"; - case ADD: return "plus"; - case SUB: return "sub"; - case MUL: return "times"; - case DIV: return "div"; - case MOD: return "mod"; - // this is only used internally! - case NUM_OPS: return "[OPS]"; - default: return "invalid"; - } - } - - inline static const std::string sass_op_separator(enum Sass_OP op) { - switch (op) { - case AND: return "&&"; - case OR: return "||"; - case EQ: return "=="; - case NEQ: return "!="; - case GT: return ">"; - case GTE: return ">="; - case LT: return "<"; - case LTE: return "<="; - case ADD: return "+"; - case SUB: return "-"; - case MUL: return "*"; - case DIV: return "/"; - case MOD: return "%"; - // this is only used internally! - case NUM_OPS: return "[OPS]"; - default: return "invalid"; - } - } - //////////////////////////////////////////////////////////////////////////// // Arithmetic negation (logical negation is just an ordinary function call). //////////////////////////////////////////////////////////////////////////// @@ -1048,47 +804,10 @@ namespace Sass { HASH_PROPERTY(Expression_Obj, operand) mutable size_t hash_; public: - Unary_Expression(ParserState pstate, Type t, Expression_Obj o) - : Expression(pstate), optype_(t), operand_(o), hash_(0) - { } - Unary_Expression(const Unary_Expression* ptr) - : Expression(ptr), - optype_(ptr->optype_), - operand_(ptr->operand_), - hash_(ptr->hash_) - { } - const std::string type_name() { - switch (optype_) { - case PLUS: return "plus"; - case MINUS: return "minus"; - case SLASH: return "slash"; - case NOT: return "not"; - default: return "invalid"; - } - } - bool operator==(const Expression& rhs) const override - { - try - { - Unary_Expression_Ptr_Const m = Cast(&rhs); - if (m == 0) return false; - return type() == m->type() && - *operand() == *m->operand(); - } - catch (std::bad_cast&) - { - return false; - } - catch (...) { throw; } - } - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(optype_); - hash_combine(hash_, operand()->hash()); - }; - return hash_; - } + Unary_Expression(ParserState pstate, Type t, Expression_Obj o); + const std::string type_name(); + virtual bool operator==(const Expression& rhs) const override; + size_t hash() const override; ATTACH_AST_OPERATIONS(Unary_Expression) ATTACH_CRTP_PERFORM_METHODS() }; @@ -1103,51 +822,10 @@ namespace Sass { ADD_PROPERTY(bool, is_keyword_argument) mutable size_t hash_; public: - Argument(ParserState pstate, Expression_Obj val, std::string n = "", bool rest = false, bool keyword = false) - : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0) - { - if (!name_.empty() && is_rest_argument_) { - coreError("variable-length argument may not be passed by name", pstate_); - } - } - Argument(const Argument* ptr) - : Expression(ptr), - value_(ptr->value_), - name_(ptr->name_), - is_rest_argument_(ptr->is_rest_argument_), - is_keyword_argument_(ptr->is_keyword_argument_), - hash_(ptr->hash_) - { - if (!name_.empty() && is_rest_argument_) { - coreError("variable-length argument may not be passed by name", pstate_); - } - } - + Argument(ParserState pstate, Expression_Obj val, std::string n = "", bool rest = false, bool keyword = false); void set_delayed(bool delayed) override; - bool operator==(const Expression& rhs) const override - { - try - { - Argument_Ptr_Const m = Cast(&rhs); - if (!(m && name() == m->name())) return false; - return *value() == *m->value(); - } - catch (std::bad_cast&) - { - return false; - } - catch (...) { throw; } - } - - size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(name()); - hash_combine(hash_, value()->hash()); - } - return hash_; - } - + bool operator==(const Expression& rhs) const override; + size_t hash() const override; ATTACH_AST_OPERATIONS(Argument) ATTACH_CRTP_PERFORM_METHODS() }; @@ -1164,26 +842,10 @@ namespace Sass { protected: void adjust_after_pushing(Argument_Obj a) override; public: - Arguments(ParserState pstate) - : Expression(pstate), - Vectorized(), - has_named_arguments_(false), - has_rest_argument_(false), - has_keyword_argument_(false) - { } - Arguments(const Arguments* ptr) - : Expression(ptr), - Vectorized(*ptr), - has_named_arguments_(ptr->has_named_arguments_), - has_rest_argument_(ptr->has_rest_argument_), - has_keyword_argument_(ptr->has_keyword_argument_) - { } - + Arguments(ParserState pstate); void set_delayed(bool delayed) override; - Argument_Obj get_rest_argument(); Argument_Obj get_keyword_argument(); - ATTACH_AST_OPERATIONS(Arguments) ATTACH_CRTP_PERFORM_METHODS() }; @@ -1197,18 +859,7 @@ namespace Sass { ADD_PROPERTY(bool, is_negated) ADD_PROPERTY(bool, is_restricted) public: - Media_Query(ParserState pstate, - String_Obj t = {}, size_t s = 0, bool n = false, bool r = false) - : Expression(pstate), Vectorized(s), - media_type_(t), is_negated_(n), is_restricted_(r) - { } - Media_Query(const Media_Query* ptr) - : Expression(ptr), - Vectorized(*ptr), - media_type_(ptr->media_type_), - is_negated_(ptr->is_negated_), - is_restricted_(ptr->is_restricted_) - { } + Media_Query(ParserState pstate, String_Obj t = {}, size_t s = 0, bool n = false, bool r = false); ATTACH_AST_OPERATIONS(Media_Query) ATTACH_CRTP_PERFORM_METHODS() }; @@ -1221,136 +872,11 @@ namespace Sass { ADD_PROPERTY(Expression_Obj, value) ADD_PROPERTY(bool, is_interpolated) public: - Media_Query_Expression(ParserState pstate, - Expression_Obj f, Expression_Obj v, bool i = false) - : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) - { } - Media_Query_Expression(const Media_Query_Expression* ptr) - : Expression(ptr), - feature_(ptr->feature_), - value_(ptr->value_), - is_interpolated_(ptr->is_interpolated_) - { } + Media_Query_Expression(ParserState pstate, Expression_Obj f, Expression_Obj v, bool i = false); ATTACH_AST_OPERATIONS(Media_Query_Expression) ATTACH_CRTP_PERFORM_METHODS() }; - //////////////////// - // `@supports` rule. - //////////////////// - class Supports_Block final : public Has_Block { - ADD_PROPERTY(Supports_Condition_Obj, condition) - public: - Supports_Block(ParserState pstate, Supports_Condition_Obj condition, Block_Obj block = {}) - : Has_Block(pstate, block), condition_(condition) - { statement_type(SUPPORTS); } - Supports_Block(const Supports_Block* ptr) - : Has_Block(ptr), condition_(ptr->condition_) - { statement_type(SUPPORTS); } - bool bubbles() override { return true; } - ATTACH_AST_OPERATIONS(Supports_Block) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////////////////////////////////////////////////// - // The abstract superclass of all Supports conditions. - ////////////////////////////////////////////////////// - class Supports_Condition : public Expression { - public: - Supports_Condition(ParserState pstate) - : Expression(pstate) - { } - Supports_Condition(const Supports_Condition* ptr) - : Expression(ptr) - { } - virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; } - ATTACH_AST_OPERATIONS(Supports_Condition) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////////////////// - // An operator condition (e.g. `CONDITION1 and CONDITION2`). - //////////////////////////////////////////////////////////// - class Supports_Operator final : public Supports_Condition { - public: - enum Operand { AND, OR }; - private: - ADD_PROPERTY(Supports_Condition_Obj, left); - ADD_PROPERTY(Supports_Condition_Obj, right); - ADD_PROPERTY(Operand, operand); - public: - Supports_Operator(ParserState pstate, Supports_Condition_Obj l, Supports_Condition_Obj r, Operand o) - : Supports_Condition(pstate), left_(l), right_(r), operand_(o) - { } - Supports_Operator(const Supports_Operator* ptr) - : Supports_Condition(ptr), - left_(ptr->left_), - right_(ptr->right_), - operand_(ptr->operand_) - { } - bool needs_parens(Supports_Condition_Obj cond) const override; - ATTACH_AST_OPERATIONS(Supports_Operator) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////////////////////////////////////// - // A negation condition (`not CONDITION`). - ////////////////////////////////////////// - class Supports_Negation final : public Supports_Condition { - private: - ADD_PROPERTY(Supports_Condition_Obj, condition); - public: - Supports_Negation(ParserState pstate, Supports_Condition_Obj c) - : Supports_Condition(pstate), condition_(c) - { } - Supports_Negation(const Supports_Negation* ptr) - : Supports_Condition(ptr), condition_(ptr->condition_) - { } - bool needs_parens(Supports_Condition_Obj cond) const override; - ATTACH_AST_OPERATIONS(Supports_Negation) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ///////////////////////////////////////////////////// - // A declaration condition (e.g. `(feature: value)`). - ///////////////////////////////////////////////////// - class Supports_Declaration final : public Supports_Condition { - private: - ADD_PROPERTY(Expression_Obj, feature); - ADD_PROPERTY(Expression_Obj, value); - public: - Supports_Declaration(ParserState pstate, Expression_Obj f, Expression_Obj v) - : Supports_Condition(pstate), feature_(f), value_(v) - { } - Supports_Declaration(const Supports_Declaration* ptr) - : Supports_Condition(ptr), - feature_(ptr->feature_), - value_(ptr->value_) - { } - bool needs_parens(Supports_Condition_Obj cond) const override { return false; } - ATTACH_AST_OPERATIONS(Supports_Declaration) - ATTACH_CRTP_PERFORM_METHODS() - }; - - /////////////////////////////////////////////// - // An interpolation condition (e.g. `#{$var}`). - /////////////////////////////////////////////// - class Supports_Interpolation final : public Supports_Condition { - private: - ADD_PROPERTY(Expression_Obj, value); - public: - Supports_Interpolation(ParserState pstate, Expression_Obj v) - : Supports_Condition(pstate), value_(v) - { } - Supports_Interpolation(const Supports_Interpolation* ptr) - : Supports_Condition(ptr), - value_(ptr->value_) - { } - bool needs_parens(Supports_Condition_Obj cond) const override { return false; } - ATTACH_AST_OPERATIONS(Supports_Interpolation) - ATTACH_CRTP_PERFORM_METHODS() - }; - ///////////////////////////////////////////////// // At root expressions (for use inside @at-root). ///////////////////////////////////////////////// @@ -1359,14 +885,7 @@ namespace Sass { ADD_PROPERTY(Expression_Obj, feature) ADD_PROPERTY(Expression_Obj, value) public: - At_Root_Query(ParserState pstate, Expression_Obj f = {}, Expression_Obj v = {}, bool i = false) - : Expression(pstate), feature_(f), value_(v) - { } - At_Root_Query(const At_Root_Query* ptr) - : Expression(ptr), - feature_(ptr->feature_), - value_(ptr->value_) - { } + At_Root_Query(ParserState pstate, Expression_Obj f = {}, Expression_Obj v = {}, bool i = false); bool exclude(std::string str); ATTACH_AST_OPERATIONS(At_Root_Query) ATTACH_CRTP_PERFORM_METHODS() @@ -1378,51 +897,13 @@ namespace Sass { class At_Root_Block final : public Has_Block { ADD_PROPERTY(At_Root_Query_Obj, expression) public: - At_Root_Block(ParserState pstate, Block_Obj b = {}, At_Root_Query_Obj e = {}) - : Has_Block(pstate, b), expression_(e) - { statement_type(ATROOT); } - At_Root_Block(const At_Root_Block* ptr) - : Has_Block(ptr), expression_(ptr->expression_) - { statement_type(ATROOT); } - bool bubbles() override { return true; } - bool exclude_node(Statement_Obj s) { - if (expression() == 0) - { - return s->statement_type() == Statement::RULESET; - } - - if (s->statement_type() == Statement::DIRECTIVE) - { - if (Directive_Obj dir = Cast(s)) - { - std::string keyword(dir->keyword()); - if (keyword.length() > 0) keyword.erase(0, 1); - return expression()->exclude(keyword); - } - } - if (s->statement_type() == Statement::MEDIA) - { - return expression()->exclude("media"); - } - if (s->statement_type() == Statement::RULESET) - { - return expression()->exclude("rule"); - } - if (s->statement_type() == Statement::SUPPORTS) - { - return expression()->exclude("supports"); - } - if (Directive_Obj dir = Cast(s)) - { - if (dir->is_keyframes()) return expression()->exclude("keyframes"); - } - return false; - } + At_Root_Block(ParserState pstate, Block_Obj b = {}, At_Root_Query_Obj e = {}); + bool bubbles() override; + bool exclude_node(Statement_Obj s); ATTACH_AST_OPERATIONS(At_Root_Block) ATTACH_CRTP_PERFORM_METHODS() }; - ///////////////////////////////////////////////////////// // Individual parameter objects for mixins and functions. ///////////////////////////////////////////////////////// @@ -1431,28 +912,7 @@ namespace Sass { ADD_PROPERTY(Expression_Obj, default_value) ADD_PROPERTY(bool, is_rest_parameter) public: - Parameter(ParserState pstate, - std::string n, Expression_Obj def = {}, bool rest = false) - : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest) - { - // tried to come up with a spec test for this, but it does no longer - // get past the parser (it error out earlier). A spec test was added! - // if (default_value_ && is_rest_parameter_) { - // error("variable-length parameter may not have a default value", pstate_); - // } - } - Parameter(const Parameter* ptr) - : AST_Node(ptr), - name_(ptr->name_), - default_value_(ptr->default_value_), - is_rest_parameter_(ptr->is_rest_parameter_) - { - // tried to come up with a spec test for this, but it does no longer - // get past the parser (it error out earlier). A spec test was added! - // if (default_value_ && is_rest_parameter_) { - // error("variable-length parameter may not have a default value", pstate_); - // } - } + Parameter(ParserState pstate, std::string n, Expression_Obj def = {}, bool rest = false); ATTACH_AST_OPERATIONS(Parameter) ATTACH_CRTP_PERFORM_METHODS() }; @@ -1466,42 +926,9 @@ namespace Sass { ADD_PROPERTY(bool, has_optional_parameters) ADD_PROPERTY(bool, has_rest_parameter) protected: - void adjust_after_pushing(Parameter_Obj p) override - { - if (p->default_value()) { - if (has_rest_parameter()) { - coreError("optional parameters may not be combined with variable-length parameters", p->pstate()); - } - has_optional_parameters(true); - } - else if (p->is_rest_parameter()) { - if (has_rest_parameter()) { - coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate()); - } - has_rest_parameter(true); - } - else { - if (has_rest_parameter()) { - coreError("required parameters must precede variable-length parameters", p->pstate()); - } - if (has_optional_parameters()) { - coreError("required parameters must precede optional parameters", p->pstate()); - } - } - } + void adjust_after_pushing(Parameter_Obj p) override; public: - Parameters(ParserState pstate) - : AST_Node(pstate), - Vectorized(), - has_optional_parameters_(false), - has_rest_parameter_(false) - { } - Parameters(const Parameters* ptr) - : AST_Node(ptr), - Vectorized(*ptr), - has_optional_parameters_(ptr->has_optional_parameters_), - has_rest_parameter_(ptr->has_rest_parameter_) - { } + Parameters(ParserState pstate); ATTACH_AST_OPERATIONS(Parameters) ATTACH_CRTP_PERFORM_METHODS() }; @@ -1509,6 +936,7 @@ namespace Sass { } #include "ast_values.hpp" +#include "ast_supports.hpp" #include "ast_selectors.hpp" #ifdef __clang__ diff --git a/src/ast_def_macros.hpp b/src/ast_def_macros.hpp index d354f3185..75bb6fcde 100644 --- a/src/ast_def_macros.hpp +++ b/src/ast_def_macros.hpp @@ -78,6 +78,7 @@ public: \ virtual klass##_Ptr clone(std::string, size_t) const override = 0; \ #define ATTACH_AST_OPERATIONS(klass) \ + klass(const klass* ptr); \ virtual klass##_Ptr copy(std::string, size_t) const override; \ virtual klass##_Ptr clone(std::string, size_t) const override; \ @@ -92,6 +93,7 @@ public: \ virtual klass##_Ptr clone() const override = 0; \ #define ATTACH_AST_OPERATIONS(klass) \ + klass(const klass* ptr); \ virtual klass##_Ptr copy() const override; \ virtual klass##_Ptr clone() const override; \ diff --git a/src/ast_sel_cmp.cpp b/src/ast_sel_cmp.cpp index 658dcc696..72279da69 100644 --- a/src/ast_sel_cmp.cpp +++ b/src/ast_sel_cmp.cpp @@ -147,34 +147,17 @@ namespace Sass { bool Selector_List::operator== (const Selector_List& rhs) const { - // for array access - size_t i = 0, n = 0; - size_t iL = length(); - size_t nL = rhs.length(); - // create temporary vectors to sort them for compare - std::vector l_lst = this->elements(); - std::vector r_lst = rhs.elements(); - std::sort(l_lst.begin(), l_lst.end(), OrderNodes()); - std::sort(r_lst.begin(), r_lst.end(), OrderNodes()); - // process loop - while (true) - { - // first check for valid index - if (i == iL) return iL == nL; - else if (n == nL) return iL == nL; - // access the vector items - Complex_Selector_Ptr l = l_lst[i]; - Complex_Selector_Ptr r = r_lst[n]; - // skip nulls - if (!l) ++i; - else if (!r) ++n; - // do the check - else if (*l != *r) break; - // advance - ++i; ++n; + if (&rhs == this) return true; + if (rhs.length() != length()) return false; + std::unordered_set lhs_set; + lhs_set.reserve(length()); + for (const Complex_Selector_Obj &element : elements()) { + lhs_set.insert(element.ptr()); } - // not equal - return false; + for (const Complex_Selector_Obj &element : rhs.elements()) { + if (lhs_set.find(element.ptr()) == lhs_set.end()) return false; + } + return true; } bool Compound_Selector::operator< (const Compound_Selector& rhs) const diff --git a/src/ast_selectors.cpp b/src/ast_selectors.cpp index dc030593c..00a1b4fb3 100644 --- a/src/ast_selectors.cpp +++ b/src/ast_selectors.cpp @@ -18,45 +18,134 @@ namespace Sass { - bool Wrapped_Selector::find ( bool (*f)(AST_Node_Obj) ) + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Selector::Selector(ParserState pstate) + : Expression(pstate), + has_line_feed_(false), + has_line_break_(false), + is_optional_(false), + media_block_(0), + hash_(0) + { concrete_type(SELECTOR); } + + Selector::Selector(const Selector* ptr) + : Expression(ptr), + has_line_feed_(ptr->has_line_feed_), + has_line_break_(ptr->has_line_break_), + is_optional_(ptr->is_optional_), + media_block_(ptr->media_block_), + hash_(ptr->hash_) + { concrete_type(SELECTOR); } + + void Selector::set_media_block(Media_Block_Ptr mb) { - // check children first - if (selector_) { - if (selector_->find(f)) return true; + media_block(mb); + } + + bool Selector::has_parent_ref() const + { + return false; + } + + bool Selector::has_real_parent_ref() const + { + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Selector_Schema::Selector_Schema(ParserState pstate, String_Obj c) + : AST_Node(pstate), + contents_(c), + connect_parent_(true), + media_block_(NULL), + hash_(0) + { } + Selector_Schema::Selector_Schema(const Selector_Schema* ptr) + : AST_Node(ptr), + contents_(ptr->contents_), + connect_parent_(ptr->connect_parent_), + media_block_(ptr->media_block_), + hash_(ptr->hash_) + { } + + unsigned long Selector_Schema::specificity() const + { + return 0; + } + + size_t Selector_Schema::hash() const { + if (hash_ == 0) { + hash_combine(hash_, contents_->hash()); } - // execute last - return f(this); + return hash_; } - bool Selector_List::find ( bool (*f)(AST_Node_Obj) ) + bool Selector_Schema::has_parent_ref() const { - // check children first - for (Complex_Selector_Obj sel : elements()) { - if (sel->find(f)) return true; + if (String_Schema_Obj schema = Cast(contents())) { + return schema->length() > 0 && Cast(schema->at(0)) != NULL; } - // execute last - return f(this); + return false; } - bool Compound_Selector::find ( bool (*f)(AST_Node_Obj) ) + bool Selector_Schema::has_real_parent_ref() const { - // check children first - for (Simple_Selector_Obj sel : elements()) { - if (sel->find(f)) return true; + if (String_Schema_Obj schema = Cast(contents())) { + if (schema->length() == 0) return false; + return Cast(schema->at(0)); } - // execute last - return f(this); + return false; } - bool Complex_Selector::find ( bool (*f)(AST_Node_Obj) ) + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Simple_Selector::Simple_Selector(ParserState pstate, std::string n) + : Selector(pstate), ns_(""), name_(n), has_ns_(false) { - // check children first - if (head_ && head_->find(f)) return true; - if (tail_ && tail_->find(f)) return true; - // execute last - return f(this); + size_t pos = n.find('|'); + // found some namespace + if (pos != std::string::npos) { + has_ns_ = true; + ns_ = n.substr(0, pos); + name_ = n.substr(pos + 1); + } + } + Simple_Selector::Simple_Selector(const Simple_Selector* ptr) + : Selector(ptr), + ns_(ptr->ns_), + name_(ptr->name_), + has_ns_(ptr->has_ns_) + { } + + std::string Simple_Selector::ns_name() const + { + std::string name(""); + if (has_ns_) + name += ns_ + "|"; + return name + name_; } + size_t Simple_Selector::hash() const + { + if (hash_ == 0) { + hash_combine(hash_, std::hash()(SELECTOR)); + hash_combine(hash_, std::hash()(simple_type())); + hash_combine(hash_, std::hash()(ns())); + hash_combine(hash_, std::hash()(name())); + } + return hash_; + } + + bool Simple_Selector::empty() const { + return ns().empty() && name().empty(); + } + + // namespace compare functions bool Simple_Selector::is_ns_eq(const Simple_Selector& r) const { // https://github.com/sass/sass/issues/2229 @@ -71,34 +160,229 @@ namespace Sass { return false; } - bool Compound_Selector::has_parent_ref() const + // namespace query functions + bool Simple_Selector::is_universal_ns() const + { + return has_ns_ && ns_ == "*"; + } + + bool Simple_Selector::has_universal_ns() const + { + return !has_ns_ || ns_ == "*"; + } + + bool Simple_Selector::is_empty_ns() const + { + return !has_ns_ || ns_ == ""; + } + + bool Simple_Selector::has_empty_ns() const + { + return has_ns_ && ns_ == ""; + } + + bool Simple_Selector::has_qualified_ns() const + { + return has_ns_ && ns_ != "" && ns_ != "*"; + } + + // name query functions + bool Simple_Selector::is_universal() const + { + return name_ == "*"; + } + + bool Simple_Selector::has_placeholder() { - for (Simple_Selector_Obj s : *this) { - if (s && s->has_parent_ref()) return true; - } return false; } - bool Compound_Selector::has_real_parent_ref() const + bool Simple_Selector::has_parent_ref() const + { + return false; + }; + + bool Simple_Selector::has_real_parent_ref() const + { + return false; + }; + + bool Simple_Selector::is_pseudo_element() const { - for (Simple_Selector_Obj s : *this) { - if (s && s->has_real_parent_ref()) return true; - } return false; } - bool Complex_Selector::has_parent_ref() const + bool Simple_Selector::is_superselector_of(Compound_Selector_Ptr_Const sub) const { - return (head() && head()->has_parent_ref()) || - (tail() && tail()->has_parent_ref()); + return false; } - bool Complex_Selector::has_real_parent_ref() const + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Parent_Selector::Parent_Selector(ParserState pstate, bool r) + : Simple_Selector(pstate, "&"), real_(r) + { simple_type(PARENT_SEL); } + Parent_Selector::Parent_Selector(const Parent_Selector* ptr) + : Simple_Selector(ptr), real_(ptr->real_) + { simple_type(PARENT_SEL); } + + bool Parent_Selector::has_parent_ref() const { - return (head() && head()->has_real_parent_ref()) || - (tail() && tail()->has_real_parent_ref()); + return true; + }; + + bool Parent_Selector::has_real_parent_ref() const + { + return real(); + }; + + unsigned long Parent_Selector::specificity() const + { + return 0; } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Placeholder_Selector::Placeholder_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { simple_type(PLACEHOLDER_SEL); } + Placeholder_Selector::Placeholder_Selector(const Placeholder_Selector* ptr) + : Simple_Selector(ptr) + { simple_type(PLACEHOLDER_SEL); } + unsigned long Placeholder_Selector::specificity() const + { + return Constants::Specificity_Base; + } + bool Placeholder_Selector::has_placeholder() { + return true; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Type_Selector::Type_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { simple_type(TYPE_SEL); } + Type_Selector::Type_Selector(const Type_Selector* ptr) + : Simple_Selector(ptr) + { simple_type(TYPE_SEL); } + + unsigned long Type_Selector::specificity() const + { + if (name() == "*") return 0; + else return Constants::Specificity_Element; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Class_Selector::Class_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { simple_type(CLASS_SEL); } + Class_Selector::Class_Selector(const Class_Selector* ptr) + : Simple_Selector(ptr) + { simple_type(CLASS_SEL); } + + unsigned long Class_Selector::specificity() const + { + return Constants::Specificity_Class; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Id_Selector::Id_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { simple_type(ID_SEL); } + Id_Selector::Id_Selector(const Id_Selector* ptr) + : Simple_Selector(ptr) + { simple_type(ID_SEL); } + + unsigned long Id_Selector::specificity() const + { + return Constants::Specificity_ID; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Attribute_Selector::Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o) + : Simple_Selector(pstate, n), matcher_(m), value_(v), modifier_(o) + { simple_type(ATTRIBUTE_SEL); } + Attribute_Selector::Attribute_Selector(const Attribute_Selector* ptr) + : Simple_Selector(ptr), + matcher_(ptr->matcher_), + value_(ptr->value_), + modifier_(ptr->modifier_) + { simple_type(ATTRIBUTE_SEL); } + + size_t Attribute_Selector::hash() const + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + hash_combine(hash_, std::hash()(matcher())); + if (value_) hash_combine(hash_, value_->hash()); + } + return hash_; + } + + unsigned long Attribute_Selector::specificity() const + { + return Constants::Specificity_Attr; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Pseudo_Selector::Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr) + : Simple_Selector(pstate, n), expression_(expr) + { simple_type(PSEUDO_SEL); } + Pseudo_Selector::Pseudo_Selector(const Pseudo_Selector* ptr) + : Simple_Selector(ptr), expression_(ptr->expression_) + { simple_type(PSEUDO_SEL); } + + // A pseudo-element is made of two colons (::) followed by the name. + // The `::` notation is introduced by the current document in order to + // establish a discrimination between pseudo-classes and pseudo-elements. + // For compatibility with existing style sheets, user agents must also + // accept the previous one-colon notation for pseudo-elements introduced + // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and + // :after). This compatibility is not allowed for the new pseudo-elements + // introduced in this specification. + bool Pseudo_Selector::is_pseudo_element() const + { + return (name_[0] == ':' && name_[1] == ':') + || is_pseudo_class_element(name_); + } + + size_t Pseudo_Selector::hash() const + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + if (expression_) hash_combine(hash_, expression_->hash()); + } + return hash_; + } + + unsigned long Pseudo_Selector::specificity() const + { + if (is_pseudo_element()) + return Constants::Specificity_Element; + return Constants::Specificity_Pseudo; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Wrapped_Selector::Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel) + : Simple_Selector(pstate, n), selector_(sel) + { simple_type(WRAPPED_SEL); } + Wrapped_Selector::Wrapped_Selector(const Wrapped_Selector* ptr) + : Simple_Selector(ptr), selector_(ptr->selector_) + { simple_type(WRAPPED_SEL); } + bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Ptr_Const sub) const { if (this->name() != sub->name()) return false; @@ -112,6 +396,105 @@ namespace Sass { return false; } + // Selectors inside the negation pseudo-class are counted like any + // other, but the negation itself does not count as a pseudo-class. + + void Wrapped_Selector::cloneChildren() + { + selector(SASS_MEMORY_CLONE(selector())); + } + + size_t Wrapped_Selector::hash() const + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + if (selector_) hash_combine(hash_, selector_->hash()); + } + return hash_; + } + + bool Wrapped_Selector::has_parent_ref() const { + if (!selector()) return false; + return selector()->has_parent_ref(); + } + + bool Wrapped_Selector::has_real_parent_ref() const { + if (!selector()) return false; + return selector()->has_real_parent_ref(); + } + + unsigned long Wrapped_Selector::specificity() const + { + return selector_ ? selector_->specificity() : 0; + } + + bool Wrapped_Selector::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + if (selector_) { + if (selector_->find(f)) return true; + } + // execute last + return f(this); + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Compound_Selector::Compound_Selector(ParserState pstate, size_t s) + : Selector(pstate), + Vectorized(s), + extended_(false), + has_parent_reference_(false) + { } + + Compound_Selector::Compound_Selector(const Compound_Selector* ptr) + : Selector(ptr), + Vectorized(*ptr), + extended_(ptr->extended_), + has_parent_reference_(ptr->has_parent_reference_) + { } + + bool Compound_Selector::contains_placeholder() { + for (size_t i = 0, L = length(); i < L; ++i) { + if ((*this)[i]->has_placeholder()) return true; + } + return false; + }; + + void Compound_Selector::cloneChildren() + { + for (size_t i = 0, l = length(); i < l; i++) { + at(i) = SASS_MEMORY_CLONE(at(i)); + } + } + + bool Compound_Selector::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + for (Simple_Selector_Obj sel : elements()) { + if (sel->find(f)) return true; + } + // execute last + return f(this); + } + + bool Compound_Selector::has_parent_ref() const + { + for (Simple_Selector_Obj s : *this) { + if (s && s->has_parent_ref()) return true; + } + return false; + } + + bool Compound_Selector::has_real_parent_ref() const + { + for (Simple_Selector_Obj s : *this) { + if (s && s->has_real_parent_ref()) return true; + } + return false; + } + bool Compound_Selector::is_superselector_of(Selector_List_Ptr_Const rhs, std::string wrapped) const { for (Complex_Selector_Obj item : rhs->elements()) { @@ -211,7 +594,7 @@ namespace Sass { if (Wrapped_Selector_Obj wrapped = Cast(r)) { if (wrapped->name() == ":not") { if (Selector_List_Obj ls = Cast(wrapped->selector())) { - ls->remove_parent_selectors(); + ls->remove_parent_selectors(); // unverified if (is_superselector_of(ls, wrapped->name())) return false; } } @@ -220,7 +603,7 @@ namespace Sass { if (wrapping != wrapped->name()) return false; } if (Selector_List_Obj ls = Cast(wrapped->selector())) { - ls->remove_parent_selectors(); + ls->remove_parent_selectors(); // unverified return (is_superselector_of(ls, wrapped->name())); } } @@ -238,6 +621,11 @@ namespace Sass { } + bool Compound_Selector::is_universal() const + { + return length() == 1 && (*this)[0]->is_universal(); + } + // create complex selector (ancestor of) from compound selector Complex_Selector_Obj Compound_Selector::to_complex() { @@ -249,6 +637,232 @@ namespace Sass { {}); } + Simple_Selector_Ptr Compound_Selector::base() const { + if (length() == 0) return 0; + // ToDo: why is this needed? + if (Cast((*this)[0])) + return (*this)[0]; + return 0; + } + + size_t Compound_Selector::hash() const + { + if (Selector::hash_ == 0) { + hash_combine(Selector::hash_, std::hash()(SELECTOR)); + if (length()) hash_combine(Selector::hash_, Vectorized::hash()); + } + return Selector::hash_; + } + + unsigned long Compound_Selector::specificity() const + { + int sum = 0; + for (size_t i = 0, L = length(); i < L; ++i) + { sum += (*this)[i]->specificity(); } + return sum; + } + + bool Compound_Selector::has_placeholder() + { + if (length() == 0) return false; + if (Simple_Selector_Obj ss = elements().front()) { + if (ss->has_placeholder()) return true; + } + return false; + } + + bool Compound_Selector::is_empty_reference() + { + return length() == 1 && + Cast((*this)[0]); + } + + void Compound_Selector::append(Simple_Selector_Obj element) + { + Vectorized::append(element); + pstate_.offset += element->pstate().offset; + } + + Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs) + { + Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate()); + // result->has_parent_reference(has_parent_reference()); + + // not very efficient because it needs to preserve order + for (size_t i = 0, L = length(); i < L; ++i) + { + bool found = false; + for (size_t j = 0, M = rhs->length(); j < M; ++j) + { + if (*get(i) == *rhs->get(j)) + { + found = true; + break; + } + } + if (!found) result->append(get(i)); + } + + return result; + } + + void Compound_Selector::mergeSources(ComplexSelectorSet& sources) + { + for (ComplexSelectorSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) { + this->sources_.insert(SASS_MEMORY_CLONE(*iterator)); + } + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Complex_Selector::Complex_Selector(ParserState pstate, + Combinator c, + Compound_Selector_Obj h, + Complex_Selector_Obj t, + String_Obj r) + : Selector(pstate), + combinator_(c), + head_(h), tail_(t), + reference_(r) + {} + Complex_Selector::Complex_Selector(const Complex_Selector* ptr) + : Selector(ptr), + combinator_(ptr->combinator_), + head_(ptr->head_), tail_(ptr->tail_), + reference_(ptr->reference_) + {} + + bool Complex_Selector::empty() const { + return (!tail() || tail()->empty()) + && (!head() || head()->empty()) + && combinator_ == ANCESTOR_OF; + } + + Complex_Selector_Obj Complex_Selector::skip_empty_reference() + { + if ((!head_ || !head_->length() || head_->is_empty_reference()) && + combinator() == Combinator::ANCESTOR_OF) + { + if (!tail_) return {}; + tail_->has_line_feed_ = this->has_line_feed_; + // tail_->has_line_break_ = this->has_line_break_; + return tail_->skip_empty_reference(); + } + return this; + } + + bool Complex_Selector::is_empty_ancestor() const + { + return (!head() || head()->length() == 0) && + combinator() == Combinator::ANCESTOR_OF; + } + + size_t Complex_Selector::hash() const + { + if (hash_ == 0) { + hash_combine(hash_, std::hash()(SELECTOR)); + hash_combine(hash_, std::hash()(combinator_)); + if (head_) hash_combine(hash_, head_->hash()); + if (tail_) hash_combine(hash_, tail_->hash()); + } + return hash_; + } + + unsigned long Complex_Selector::specificity() const + { + int sum = 0; + if (head()) sum += head()->specificity(); + if (tail()) sum += tail()->specificity(); + return sum; + } + + void Complex_Selector::set_media_block(Media_Block_Ptr mb) { + media_block(mb); + if (tail_) tail_->set_media_block(mb); + if (head_) head_->set_media_block(mb); + } + + bool Complex_Selector::has_placeholder() { + if (head_ && head_->has_placeholder()) return true; + if (tail_ && tail_->has_placeholder()) return true; + return false; + } + + const ComplexSelectorSet Complex_Selector::sources() + { + //s = Set.new + //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} + //s + + ComplexSelectorSet srcs; + + Compound_Selector_Obj pHead = head(); + Complex_Selector_Obj pTail = tail(); + + if (pHead) { + const ComplexSelectorSet& headSources = pHead->sources(); + srcs.insert(headSources.begin(), headSources.end()); + } + + if (pTail) { + const ComplexSelectorSet& tailSources = pTail->sources(); + srcs.insert(tailSources.begin(), tailSources.end()); + } + + return srcs; + } + + void Complex_Selector::addSources(ComplexSelectorSet& sources) + { + // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} + Complex_Selector_Ptr pIter = this; + while (pIter) { + Compound_Selector_Ptr pHead = pIter->head(); + + if (pHead) { + pHead->mergeSources(sources); + } + + pIter = pIter->tail(); + } + } + + void Complex_Selector::clearSources() + { + Complex_Selector_Ptr pIter = this; + while (pIter) { + Compound_Selector_Ptr pHead = pIter->head(); + + if (pHead) { + pHead->clearSources(); + } + + pIter = pIter->tail(); + } + } + + bool Complex_Selector::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + if (head_ && head_->find(f)) return true; + if (tail_ && tail_->find(f)) return true; + // execute last + return f(this); + } + + bool Complex_Selector::has_parent_ref() const + { + return (head() && head()->has_parent_ref()) || + (tail() && tail()->has_parent_ref()); + } + + bool Complex_Selector::has_real_parent_ref() const + { + return (head() && head()->has_real_parent_ref()) || + (tail() && tail()->has_real_parent_ref()); + } + bool Complex_Selector::is_superselector_of(Compound_Selector_Ptr_Const rhs, std::string wrapping) const { return last()->head() && last()->head()->is_superselector_of(rhs, wrapping); @@ -421,25 +1035,6 @@ namespace Sass { } - Selector_List_Obj Selector_List::eval(Eval& eval) - { - Selector_List_Obj list = schema() ? - eval(schema()) : eval(this); - list->schema(schema()); - return list; - } - - 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()); - for (size_t si = 0, sL = this->length(); si < sL; ++si) { - Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent); - ss->concat(rv); - } - return ss; - } - Selector_List_Ptr Complex_Selector::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) { Complex_Selector_Obj tail = this->tail(); @@ -672,23 +1267,67 @@ namespace Sass { if (tail()) tail(SASS_MEMORY_CLONE(tail())); } - void Compound_Selector::cloneChildren() + // it's a superselector if every selector of the right side + // list is a superselector of the given left side selector + bool Complex_Selector::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const { - for (size_t i = 0, l = length(); i < l; i++) { - at(i) = SASS_MEMORY_CLONE(at(i)); + // Check every rhs selector against left hand list + for(size_t i = 0, L = sub->length(); i < L; ++i) { + if (!is_superselector_of((*sub)[i], wrapping)) return false; } + return true; } - void Selector_List::cloneChildren() + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Selector_List::Selector_List(ParserState pstate, size_t s) + : Selector(pstate), + Vectorized(s), + schema_({}), + wspace_(0) + { } + Selector_List::Selector_List(const Selector_List* ptr) + : Selector(ptr), + Vectorized(*ptr), + schema_(ptr->schema_), + wspace_(ptr->wspace_) + { } + + bool Selector_List::find ( bool (*f)(AST_Node_Obj) ) { - for (size_t i = 0, l = length(); i < l; i++) { - at(i) = SASS_MEMORY_CLONE(at(i)); + // check children first + for (Complex_Selector_Obj sel : elements()) { + if (sel->find(f)) return true; } + // execute last + return f(this); } - void Wrapped_Selector::cloneChildren() + Selector_List_Obj Selector_List::eval(Eval& eval) { - selector(SASS_MEMORY_CLONE(selector())); + Selector_List_Obj list = schema() ? + eval(schema()) : eval(this); + list->schema(schema()); + return list; + } + + 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()); + for (size_t si = 0, sL = this->length(); si < sL; ++si) { + Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent); + ss->concat(rv); + } + return ss; + } + + void Selector_List::cloneChildren() + { + for (size_t i = 0, l = length(); i < l; i++) { + at(i) = SASS_MEMORY_CLONE(at(i)); + } } // remove parent selector references @@ -716,30 +1355,6 @@ namespace Sass { } } - size_t Wrapped_Selector::hash() const - { - if (hash_ == 0) { - hash_combine(hash_, Simple_Selector::hash()); - if (selector_) hash_combine(hash_, selector_->hash()); - } - return hash_; - } - bool Wrapped_Selector::has_parent_ref() const { - // if (has_reference()) return true; - if (!selector()) return false; - return selector()->has_parent_ref(); - } - bool Wrapped_Selector::has_real_parent_ref() const { - // if (has_reference()) return true; - if (!selector()) return false; - return selector()->has_real_parent_ref(); - } - unsigned long Wrapped_Selector::specificity() const - { - return selector_ ? selector_->specificity() : 0; - } - - bool Selector_List::has_parent_ref() const { for (Complex_Selector_Obj s : elements()) { @@ -756,39 +1371,11 @@ namespace Sass { return false; } - bool Selector_Schema::has_parent_ref() const - { - if (String_Schema_Obj schema = Cast(contents())) { - return schema->length() > 0 && Cast(schema->at(0)) != NULL; - } - return false; - } - - bool Selector_Schema::has_real_parent_ref() const - { - if (String_Schema_Obj schema = Cast(contents())) { - if (schema->length() == 0) return false; - return Cast(schema->at(0)) != nullptr; - } - return false; - } - void Selector_List::adjust_after_pushing(Complex_Selector_Obj c) { // if (c->has_reference()) has_reference(true); } - // it's a superselector if every selector of the right side - // list is a superselector of the given left side selector - bool Complex_Selector::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const - { - // Check every rhs selector against left hand list - for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i], wrapping)) return false; - } - return true; - } - // it's a superselector if every selector of the right side // list is a superselector of the given left side selector bool Selector_List::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const @@ -855,43 +1442,46 @@ namespace Sass { } }; - void Compound_Selector::append(Simple_Selector_Obj element) + size_t Selector_List::hash() const { - Vectorized::append(element); - pstate_.offset += element->pstate().offset; + if (Selector::hash_ == 0) { + hash_combine(Selector::hash_, std::hash()(SELECTOR)); + hash_combine(Selector::hash_, Vectorized::hash()); + } + return Selector::hash_; } - Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs) + unsigned long Selector_List::specificity() const { - Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate()); - // result->has_parent_reference(has_parent_reference()); - - // not very efficient because it needs to preserve order + unsigned long sum = 0; + unsigned long specificity; for (size_t i = 0, L = length(); i < L; ++i) { - bool found = false; - std::string thisSelector((*this)[i]->to_string()); - for (size_t j = 0, M = rhs->length(); j < M; ++j) - { - if (thisSelector == (*rhs)[j]->to_string()) - { - found = true; - break; - } - } - if (!found) result->append((*this)[i]); + specificity = (*this)[i]->specificity(); + if (sum < specificity) sum = specificity; } + return sum; + } - return result; + void Selector_List::set_media_block(Media_Block_Ptr mb) + { + media_block(mb); + for (Complex_Selector_Obj cs : elements()) { + cs->set_media_block(mb); + } } - void Compound_Selector::mergeSources(ComplexSelectorSet& sources) + bool Selector_List::has_placeholder() { - for (ComplexSelectorSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) { - this->sources_.insert(SASS_MEMORY_CLONE(*iterator)); + for (Complex_Selector_Obj cs : elements()) { + if (cs->has_placeholder()) return true; } + return false; } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + IMPLEMENT_AST_OPERATORS(Selector_Schema); IMPLEMENT_AST_OPERATORS(Placeholder_Selector); IMPLEMENT_AST_OPERATORS(Parent_Selector); diff --git a/src/ast_selectors.hpp b/src/ast_selectors.hpp index a81a697a7..9f5270799 100644 --- a/src/ast_selectors.hpp +++ b/src/ast_selectors.hpp @@ -37,51 +37,27 @@ namespace Sass { // Abstract base class for CSS selectors. ///////////////////////////////////////// class Selector : public Expression { - // ADD_PROPERTY(bool, has_reference) // line break before list separator ADD_PROPERTY(bool, has_line_feed) // line break after list separator ADD_PROPERTY(bool, has_line_break) // maybe we have optional flag ADD_PROPERTY(bool, is_optional) - // parent block pointers - // must not be a reference counted object // otherwise we create circular references ADD_PROPERTY(Media_Block_Ptr, media_block) protected: mutable size_t hash_; public: - Selector(ParserState pstate) - : Expression(pstate), - has_line_feed_(false), - has_line_break_(false), - is_optional_(false), - media_block_(0), - hash_(0) - { concrete_type(SELECTOR); } - Selector(const Selector* ptr) - : Expression(ptr), - // has_reference_(ptr->has_reference_), - has_line_feed_(ptr->has_line_feed_), - has_line_break_(ptr->has_line_break_), - is_optional_(ptr->is_optional_), - media_block_(ptr->media_block_), - hash_(ptr->hash_) - { concrete_type(SELECTOR); } + Selector(ParserState pstate); + Selector(const Selector* ptr); virtual ~Selector() = 0; size_t hash() const override = 0; virtual unsigned long specificity() const = 0; virtual int unification_order() const = 0; - virtual void set_media_block(Media_Block_Ptr mb) { - media_block(mb); - } - virtual bool has_parent_ref() const { - return false; - } - virtual bool has_real_parent_ref() const { - return false; - } + virtual void set_media_block(Media_Block_Ptr mb); + virtual bool has_parent_ref() const; + virtual bool has_real_parent_ref() const; // dispatch to correct handlers virtual bool operator<(const Selector& rhs) const = 0; virtual bool operator==(const Selector& rhs) const = 0; @@ -104,33 +80,15 @@ namespace Sass { // store computed hash mutable size_t hash_; public: - Selector_Schema(ParserState pstate, String_Obj c) - : AST_Node(pstate), - contents_(c), - connect_parent_(true), - media_block_(NULL), - hash_(0) - { } - Selector_Schema(const Selector_Schema* ptr) - : AST_Node(ptr), - contents_(ptr->contents_), - connect_parent_(ptr->connect_parent_), - media_block_(ptr->media_block_), - hash_(ptr->hash_) - { } + Selector_Schema(ParserState pstate, String_Obj c); bool has_parent_ref() const; bool has_real_parent_ref() const; bool operator<(const Selector& rhs) const; bool operator==(const Selector& rhs) const; // selector schema is not yet a final selector, so we do not // have a specificity for it yet. We need to - unsigned long specificity() const { return 0; } - size_t hash() const override { - if (hash_ == 0) { - hash_combine(hash_, contents_->hash()); - } - return hash_; - } + virtual unsigned long specificity() const; + size_t hash() const override; ATTACH_AST_OPERATIONS(Selector_Schema) ATTACH_CRTP_PERFORM_METHODS() }; @@ -156,81 +114,30 @@ namespace Sass { ADD_PROPERTY(Simple_Type, simple_type) ADD_PROPERTY(bool, has_ns) public: - Simple_Selector(ParserState pstate, std::string n = "") - : Selector(pstate), ns_(""), name_(n), has_ns_(false) - { - size_t pos = n.find('|'); - // found some namespace - if (pos != std::string::npos) { - has_ns_ = true; - ns_ = n.substr(0, pos); - name_ = n.substr(pos + 1); - } - } - Simple_Selector(const Simple_Selector* ptr) - : Selector(ptr), - ns_(ptr->ns_), - name_(ptr->name_), - has_ns_(ptr->has_ns_) - { } - std::string ns_name() const - { - std::string name(""); - if (has_ns_) - name += ns_ + "|"; - return name + name_; - } - virtual size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, std::hash()(SELECTOR)); - hash_combine(hash_, std::hash()(simple_type())); - hash_combine(hash_, std::hash()(ns())); - hash_combine(hash_, std::hash()(name())); - } - return hash_; - } - bool empty() const { - return ns().empty() && name().empty(); - } + Simple_Selector(ParserState pstate, std::string n = ""); + Simple_Selector(const Simple_Selector* ptr); + virtual std::string ns_name() const; + size_t hash() const override; + bool empty() const; // namespace compare functions bool is_ns_eq(const Simple_Selector& r) const; // namespace query functions - bool is_universal_ns() const - { - return has_ns_ && ns_ == "*"; - } - bool has_universal_ns() const - { - return !has_ns_ || ns_ == "*"; - } - bool is_empty_ns() const - { - return !has_ns_ || ns_ == ""; - } - bool has_empty_ns() const - { - return has_ns_ && ns_ == ""; - } - bool has_qualified_ns() const - { - return has_ns_ && ns_ != "" && ns_ != "*"; - } + bool is_universal_ns() const; + bool has_universal_ns() const; + bool is_empty_ns() const; + bool has_empty_ns() const; + bool has_qualified_ns() const; // name query functions - virtual bool is_universal() const - { - return name_ == "*"; - } - - virtual bool has_placeholder() { - return false; - } + bool is_universal() const; + virtual bool has_placeholder(); virtual ~Simple_Selector() = 0; virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); - virtual bool is_pseudo_element() const { return false; } - virtual bool is_superselector_of(Compound_Selector_Ptr_Const sub) const { return false; } + virtual bool has_parent_ref() const override; + virtual bool has_real_parent_ref() const override; + virtual bool is_pseudo_element() const; + virtual bool is_superselector_of(Compound_Selector_Ptr_Const sub) const; bool operator<(const Selector& rhs) const final override; bool operator==(const Selector& rhs) const final override; @@ -249,29 +156,23 @@ namespace Sass { }; inline Simple_Selector::~Simple_Selector() { } - ////////////////////////////////// // The Parent Selector Expression. ////////////////////////////////// - // parent selectors can occur in selectors but also - // inside strings in declarations (Compound_Selector). - // only one simple parent selector means the first case. class Parent_Selector final : public Simple_Selector { + // a real parent selector is given by the user + // others are added implicitly to connect the + // selector scopes automatically when rendered + // a Parent_Reference is never seen in selectors + // and is only used in values (e.g. `prop: #{&};`) ADD_PROPERTY(bool, real) public: - Parent_Selector(ParserState pstate, bool r = true) - : Simple_Selector(pstate, "&"), real_(r) - { simple_type(PARENT_SEL); } - Parent_Selector(const Parent_Selector* ptr) - : Simple_Selector(ptr), real_(ptr->real_) - { simple_type(PARENT_SEL); } - bool is_real_parent_ref() const { return real(); }; - bool has_parent_ref() const override { return true; }; - bool has_real_parent_ref() const override { return is_real_parent_ref(); }; - unsigned long specificity() const override - { - return 0; - } + Parent_Selector(ParserState pstate, bool r = true); + + virtual bool has_parent_ref() const override; + virtual bool has_real_parent_ref() const override; + + virtual unsigned long specificity() const override; int unification_order() const override { throw std::runtime_error("unification_order for Parent_Selector is undefined"); @@ -292,26 +193,17 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// class Placeholder_Selector final : public Simple_Selector { public: - Placeholder_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { simple_type(PLACEHOLDER_SEL); } - Placeholder_Selector(const Placeholder_Selector* ptr) - : Simple_Selector(ptr) - { simple_type(PLACEHOLDER_SEL); } - unsigned long specificity() const override - { - return Constants::Specificity_Base; - } + Placeholder_Selector(ParserState pstate, std::string n); + int unification_order() const override { return Constants::UnificationOrder_Placeholder; } - bool has_placeholder() override { - return true; - } virtual ~Placeholder_Selector() {}; - bool operator<(const Simple_Selector& rhs) const final override; - bool operator==(const Simple_Selector& rhs) const final override; + virtual unsigned long specificity() const override; + virtual bool has_placeholder() override; + bool operator<(const Simple_Selector& rhs) const override; + bool operator==(const Simple_Selector& rhs) const override; bool operator<(const Placeholder_Selector& rhs) const; bool operator==(const Placeholder_Selector& rhs) const; ATTACH_AST_OPERATIONS(Placeholder_Selector) @@ -323,17 +215,8 @@ namespace Sass { ///////////////////////////////////////////////////////////////////// class Type_Selector final : public Simple_Selector { public: - Type_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { simple_type(TYPE_SEL); } - Type_Selector(const Type_Selector* ptr) - : Simple_Selector(ptr) - { simple_type(TYPE_SEL); } - unsigned long specificity() const override - { - if (name() == "*") return 0; - else return Constants::Specificity_Element; - } + Type_Selector(ParserState pstate, std::string n); + virtual unsigned long specificity() const override; int unification_order() const override { return Constants::UnificationOrder_Element; @@ -353,16 +236,8 @@ namespace Sass { //////////////////////////////////////////////// class Class_Selector final : public Simple_Selector { public: - Class_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { simple_type(CLASS_SEL); } - Class_Selector(const Class_Selector* ptr) - : Simple_Selector(ptr) - { simple_type(CLASS_SEL); } - unsigned long specificity() const override - { - return Constants::Specificity_Class; - } + Class_Selector(ParserState pstate, std::string n); + virtual unsigned long specificity() const override; int unification_order() const override { return Constants::UnificationOrder_Class; @@ -381,16 +256,8 @@ namespace Sass { //////////////////////////////////////////////// class Id_Selector final : public Simple_Selector { public: - Id_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { simple_type(ID_SEL); } - Id_Selector(const Id_Selector* ptr) - : Simple_Selector(ptr) - { simple_type(ID_SEL); } - unsigned long specificity() const override - { - return Constants::Specificity_ID; - } + Id_Selector(ParserState pstate, std::string n); + virtual unsigned long specificity() const override; int unification_order() const override { return Constants::UnificationOrder_Id; @@ -413,28 +280,9 @@ namespace Sass { ADD_PROPERTY(String_Obj, value) // might be interpolated ADD_PROPERTY(char, modifier); public: - Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o = 0) - : Simple_Selector(pstate, n), matcher_(m), value_(v), modifier_(o) - { simple_type(ATTRIBUTE_SEL); } - Attribute_Selector(const Attribute_Selector* ptr) - : Simple_Selector(ptr), - matcher_(ptr->matcher_), - value_(ptr->value_), - modifier_(ptr->modifier_) - { simple_type(ATTRIBUTE_SEL); } - size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, Simple_Selector::hash()); - hash_combine(hash_, std::hash()(matcher())); - if (value_) hash_combine(hash_, value_->hash()); - } - return hash_; - } - unsigned long specificity() const override - { - return Constants::Specificity_Attr; - } + Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o = 0); + size_t hash() const override; + virtual unsigned long specificity() const override; int unification_order() const override { return Constants::UnificationOrder_Attribute; @@ -466,40 +314,10 @@ namespace Sass { class Pseudo_Selector final : public Simple_Selector { ADD_PROPERTY(String_Obj, expression) public: - Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = {}) - : Simple_Selector(pstate, n), expression_(expr) - { simple_type(PSEUDO_SEL); } - Pseudo_Selector(const Pseudo_Selector* ptr) - : Simple_Selector(ptr), expression_(ptr->expression_) - { simple_type(PSEUDO_SEL); } - - // A pseudo-element is made of two colons (::) followed by the name. - // The `::` notation is introduced by the current document in order to - // establish a discrimination between pseudo-classes and pseudo-elements. - // For compatibility with existing style sheets, user agents must also - // accept the previous one-colon notation for pseudo-elements introduced - // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and - // :after). This compatibility is not allowed for the new pseudo-elements - // introduced in this specification. - bool is_pseudo_element() const override - { - return (name_[0] == ':' && name_[1] == ':') - || is_pseudo_class_element(name_); - } - size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, Simple_Selector::hash()); - if (expression_) hash_combine(hash_, expression_->hash()); - } - return hash_; - } - unsigned long specificity() const override - { - if (is_pseudo_element()) - return Constants::Specificity_Element; - return Constants::Specificity_Pseudo; - } + Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = {}); + virtual bool is_pseudo_element() const override; + size_t hash() const override; + virtual unsigned long specificity() const override; int unification_order() const override { if (is_pseudo_element()) @@ -521,12 +339,7 @@ namespace Sass { class Wrapped_Selector final : public Simple_Selector { ADD_PROPERTY(Selector_List_Obj, selector) public: - Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel) - : Simple_Selector(pstate, n), selector_(sel) - { simple_type(WRAPPED_SEL); } - Wrapped_Selector(const Wrapped_Selector* ptr) - : Simple_Selector(ptr), selector_(ptr->selector_) - { simple_type(WRAPPED_SEL); } + Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel); using Simple_Selector::is_superselector_of; bool is_superselector_of(Wrapped_Selector_Ptr_Const sub) const; // Selectors inside the negation pseudo-class are counted like any @@ -565,82 +378,27 @@ namespace Sass { // if (s->has_placeholder()) has_placeholder(true); } public: - Compound_Selector(ParserState pstate, size_t s = 0) - : Selector(pstate), - Vectorized(s), - extended_(false), - has_parent_reference_(false) - { } - Compound_Selector(const Compound_Selector* ptr) - : Selector(ptr), - Vectorized(*ptr), - extended_(ptr->extended_), - has_parent_reference_(ptr->has_parent_reference_) - { } - bool contains_placeholder() { - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return true; - } - return false; - }; - + Compound_Selector(ParserState pstate, size_t s = 0); + bool contains_placeholder(); void append(Simple_Selector_Obj element) override; - - bool is_universal() const - { - return length() == 1 && (*this)[0]->is_universal(); - } - + bool is_universal() const; Complex_Selector_Obj to_complex(); Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs); // virtual Placeholder_Selector_Ptr find_placeholder(); bool has_parent_ref() const override; bool has_real_parent_ref() const override; - Simple_Selector_Ptr base() const { - if (length() == 0) return 0; - // ToDo: why is this needed? - if (Cast((*this)[0])) - return (*this)[0]; - return 0; - } + Simple_Selector_Ptr base() const; bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapped = "") const; bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapped = "") const; bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapped = "") const; - size_t hash() const override - { - if (Selector::hash_ == 0) { - hash_combine(Selector::hash_, std::hash()(SELECTOR)); - if (length()) hash_combine(Selector::hash_, Vectorized::hash()); - } - return Selector::hash_; - } - unsigned long specificity() const override - { - int sum = 0; - for (size_t i = 0, L = length(); i < L; ++i) - { sum += (*this)[i]->specificity(); } - return sum; - } + size_t hash() const override; + virtual unsigned long specificity() const override; + virtual bool has_placeholder(); + bool is_empty_reference(); int unification_order() const override { throw std::runtime_error("unification_order for Compound_Selector is undefined"); } - - bool has_placeholder() - { - if (length() == 0) return false; - if (Simple_Selector_Obj ss = elements().front()) { - if (ss->has_placeholder()) return true; - } - return false; - } - - bool is_empty_reference() - { - return length() == 1 && - Cast((*this)[0]); - } - bool find ( bool (*f)(AST_Node_Obj) ) override; bool operator<(const Selector& rhs) const override; @@ -687,45 +445,16 @@ namespace Sass { Combinator c = ANCESTOR_OF, Compound_Selector_Obj h = {}, Complex_Selector_Obj t = {}, - String_Obj r = {}) - : Selector(pstate), - combinator_(c), - head_(h), tail_(t), - reference_(r) - {} - Complex_Selector(const Complex_Selector* ptr) - : Selector(ptr), - combinator_(ptr->combinator_), - head_(ptr->head_), tail_(ptr->tail_), - reference_(ptr->reference_) - {}; - bool empty() const { - return (!tail() || tail()->empty()) - && (!head() || head()->empty()) - && combinator_ == ANCESTOR_OF; - } + String_Obj r = {}); + + bool empty() const; + bool has_parent_ref() const override; bool has_real_parent_ref() const override; - - Complex_Selector_Obj skip_empty_reference() - { - if ((!head_ || !head_->length() || head_->is_empty_reference()) && - combinator() == Combinator::ANCESTOR_OF) - { - if (!tail_) return {}; - tail_->has_line_feed_ = this->has_line_feed_; - // tail_->has_line_break_ = this->has_line_break_; - return tail_->skip_empty_reference(); - } - return this; - } + Complex_Selector_Obj skip_empty_reference(); // can still have a tail - bool is_empty_ancestor() const - { - return (!head() || head()->length() == 0) && - combinator() == Combinator::ANCESTOR_OF; - } + bool is_empty_ancestor() const; Selector_List_Ptr tails(Selector_List_Ptr tails); @@ -747,37 +476,15 @@ namespace Sass { Combinator clear_innermost(); void append(Complex_Selector_Obj, Backtraces& traces); void set_innermost(Complex_Selector_Obj, Combinator); - size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, std::hash()(SELECTOR)); - hash_combine(hash_, std::hash()(combinator_)); - if (head_) hash_combine(hash_, head_->hash()); - if (tail_) hash_combine(hash_, tail_->hash()); - } - return hash_; - } - unsigned long specificity() const override - { - int sum = 0; - if (head()) sum += head()->specificity(); - if (tail()) sum += tail()->specificity(); - return sum; - } + + size_t hash() const override; + virtual unsigned long specificity() const override; + virtual void set_media_block(Media_Block_Ptr mb) override; + virtual bool has_placeholder(); int unification_order() const override { throw std::runtime_error("unification_order for Complex_Selector is undefined"); } - void set_media_block(Media_Block_Ptr mb) override { - media_block(mb); - if (tail_) tail_->set_media_block(mb); - if (head_) head_->set_media_block(mb); - } - bool has_placeholder() { - if (head_ && head_->has_placeholder()) return true; - if (tail_ && tail_->has_placeholder()) return true; - return false; - } bool find ( bool (*f)(AST_Node_Obj) ) override; bool operator<(const Selector& rhs) const override; @@ -791,54 +498,9 @@ namespace Sass { bool operator<(const Simple_Selector& rhs) const; bool operator==(const Simple_Selector& rhs) const; - const ComplexSelectorSet sources() - { - //s = Set.new - //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} - //s - - ComplexSelectorSet srcs; - - Compound_Selector_Obj pHead = head(); - Complex_Selector_Obj pTail = tail(); - - if (pHead) { - const ComplexSelectorSet& headSources = pHead->sources(); - srcs.insert(headSources.begin(), headSources.end()); - } - - if (pTail) { - const ComplexSelectorSet& tailSources = pTail->sources(); - srcs.insert(tailSources.begin(), tailSources.end()); - } - - return srcs; - } - void addSources(ComplexSelectorSet& sources) { - // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} - Complex_Selector_Ptr pIter = this; - while (pIter) { - Compound_Selector_Ptr pHead = pIter->head(); - - if (pHead) { - pHead->mergeSources(sources); - } - - pIter = pIter->tail(); - } - } - void clearSources() { - Complex_Selector_Ptr pIter = this; - while (pIter) { - Compound_Selector_Ptr pHead = pIter->head(); - - if (pHead) { - pHead->clearSources(); - } - - pIter = pIter->tail(); - } - } + const ComplexSelectorSet sources(); + void addSources(ComplexSelectorSet& sources); + void clearSources(); void cloneChildren() override; ATTACH_AST_OPERATIONS(Complex_Selector) @@ -854,18 +516,7 @@ namespace Sass { protected: void adjust_after_pushing(Complex_Selector_Obj c) override; public: - Selector_List(ParserState pstate, size_t s = 0) - : Selector(pstate), - Vectorized(s), - schema_({}), - wspace_(0) - { } - Selector_List(const Selector_List* ptr) - : Selector(ptr), - Vectorized(*ptr), - schema_(ptr->schema_), - wspace_(ptr->wspace_) - { } + Selector_List(ParserState pstate, size_t s = 0); std::string type() const override { return "list"; } // remove parent selector references // basically unwraps parsed selectors @@ -879,41 +530,15 @@ namespace Sass { Selector_List_Ptr unify_with(Selector_List_Ptr); void populate_extends(Selector_List_Obj, Subset_Map&); Selector_List_Obj eval(Eval& eval); - size_t hash() const override - { - if (Selector::hash_ == 0) { - hash_combine(Selector::hash_, std::hash()(SELECTOR)); - hash_combine(Selector::hash_, Vectorized::hash()); - } - return Selector::hash_; - } - unsigned long specificity() const override - { - unsigned long sum = 0; - unsigned long specificity; - for (size_t i = 0, L = length(); i < L; ++i) - { - specificity = (*this)[i]->specificity(); - if (sum < specificity) sum = specificity; - } - return sum; - } + + size_t hash() const override; + virtual unsigned long specificity() const override; + virtual void set_media_block(Media_Block_Ptr mb) override; + virtual bool has_placeholder(); int unification_order() const override { throw std::runtime_error("unification_order for Selector_List is undefined"); } - void set_media_block(Media_Block_Ptr mb) override { - media_block(mb); - for (Complex_Selector_Obj cs : elements()) { - cs->set_media_block(mb); - } - } - bool has_placeholder() { - for (Complex_Selector_Obj cs : elements()) { - if (cs->has_placeholder()) return true; - } - return false; - } bool find ( bool (*f)(AST_Node_Obj) ) override; bool operator<(const Selector& rhs) const override; bool operator==(const Selector& rhs) const override; diff --git a/src/ast_supports.cpp b/src/ast_supports.cpp new file mode 100644 index 000000000..087f14f0d --- /dev/null +++ b/src/ast_supports.cpp @@ -0,0 +1,127 @@ +#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 { + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Supports_Block::Supports_Block(ParserState pstate, Supports_Condition_Obj condition, Block_Obj block) + : Has_Block(pstate, block), condition_(condition) + { statement_type(SUPPORTS); } + Supports_Block::Supports_Block(const Supports_Block* ptr) + : Has_Block(ptr), condition_(ptr->condition_) + { statement_type(SUPPORTS); } + bool Supports_Block::bubbles() { return true; } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Supports_Condition::Supports_Condition(ParserState pstate) + : Expression(pstate) + { } + + Supports_Condition::Supports_Condition(const Supports_Condition* ptr) + : Expression(ptr) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Supports_Operator::Supports_Operator(ParserState pstate, Supports_Condition_Obj l, Supports_Condition_Obj r, Operand o) + : Supports_Condition(pstate), left_(l), right_(r), operand_(o) + { } + Supports_Operator::Supports_Operator(const Supports_Operator* ptr) + : Supports_Condition(ptr), + left_(ptr->left_), + right_(ptr->right_), + operand_(ptr->operand_) + { } + + bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const + { + if (Supports_Operator_Obj op = Cast(cond)) { + return op->operand() != operand(); + } + return Cast(cond) != NULL; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Supports_Negation::Supports_Negation(ParserState pstate, Supports_Condition_Obj c) + : Supports_Condition(pstate), condition_(c) + { } + Supports_Negation::Supports_Negation(const Supports_Negation* ptr) + : Supports_Condition(ptr), condition_(ptr->condition_) + { } + + bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const + { + return Cast(cond) || + Cast(cond); + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Supports_Declaration::Supports_Declaration(ParserState pstate, Expression_Obj f, Expression_Obj v) + : Supports_Condition(pstate), feature_(f), value_(v) + { } + Supports_Declaration::Supports_Declaration(const Supports_Declaration* ptr) + : Supports_Condition(ptr), + feature_(ptr->feature_), + value_(ptr->value_) + { } + + bool Supports_Declaration::needs_parens(Supports_Condition_Obj cond) const + { + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Supports_Interpolation::Supports_Interpolation(ParserState pstate, Expression_Obj v) + : Supports_Condition(pstate), value_(v) + { } + Supports_Interpolation::Supports_Interpolation(const Supports_Interpolation* ptr) + : Supports_Condition(ptr), + value_(ptr->value_) + { } + + bool Supports_Interpolation::needs_parens(Supports_Condition_Obj cond) const + { + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + IMPLEMENT_AST_OPERATORS(Supports_Block); + IMPLEMENT_AST_OPERATORS(Supports_Condition); + IMPLEMENT_AST_OPERATORS(Supports_Operator); + IMPLEMENT_AST_OPERATORS(Supports_Negation); + IMPLEMENT_AST_OPERATORS(Supports_Declaration); + IMPLEMENT_AST_OPERATORS(Supports_Interpolation); + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + +} \ No newline at end of file diff --git a/src/ast_supports.hpp b/src/ast_supports.hpp new file mode 100644 index 000000000..2caf82b5c --- /dev/null +++ b/src/ast_supports.hpp @@ -0,0 +1,118 @@ +#ifndef SASS_AST_SUPPORTS_H +#define SASS_AST_SUPPORTS_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 { + + //////////////////// + // `@supports` rule. + //////////////////// + class Supports_Block : public Has_Block { + ADD_PROPERTY(Supports_Condition_Obj, condition) + public: + Supports_Block(ParserState pstate, Supports_Condition_Obj condition, Block_Obj block = {}); + bool bubbles() override; + ATTACH_AST_OPERATIONS(Supports_Block) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////////////////////////////// + // The abstract superclass of all Supports conditions. + ////////////////////////////////////////////////////// + class Supports_Condition : public Expression { + public: + Supports_Condition(ParserState pstate); + virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; } + ATTACH_AST_OPERATIONS(Supports_Condition) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////////////// + // An operator condition (e.g. `CONDITION1 and CONDITION2`). + //////////////////////////////////////////////////////////// + class Supports_Operator : public Supports_Condition { + public: + enum Operand { AND, OR }; + private: + ADD_PROPERTY(Supports_Condition_Obj, left); + ADD_PROPERTY(Supports_Condition_Obj, right); + ADD_PROPERTY(Operand, operand); + public: + Supports_Operator(ParserState pstate, Supports_Condition_Obj l, Supports_Condition_Obj r, Operand o); + virtual bool needs_parens(Supports_Condition_Obj cond) const override; + ATTACH_AST_OPERATIONS(Supports_Operator) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////////////////// + // A negation condition (`not CONDITION`). + ////////////////////////////////////////// + class Supports_Negation : public Supports_Condition { + private: + ADD_PROPERTY(Supports_Condition_Obj, condition); + public: + Supports_Negation(ParserState pstate, Supports_Condition_Obj c); + virtual bool needs_parens(Supports_Condition_Obj cond) const override; + ATTACH_AST_OPERATIONS(Supports_Negation) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ///////////////////////////////////////////////////// + // A declaration condition (e.g. `(feature: value)`). + ///////////////////////////////////////////////////// + class Supports_Declaration : public Supports_Condition { + private: + ADD_PROPERTY(Expression_Obj, feature); + ADD_PROPERTY(Expression_Obj, value); + public: + Supports_Declaration(ParserState pstate, Expression_Obj f, Expression_Obj v); + virtual bool needs_parens(Supports_Condition_Obj cond) const override; + ATTACH_AST_OPERATIONS(Supports_Declaration) + ATTACH_CRTP_PERFORM_METHODS() + }; + + /////////////////////////////////////////////// + // An interpolation condition (e.g. `#{$var}`). + /////////////////////////////////////////////// + class Supports_Interpolation : public Supports_Condition { + private: + ADD_PROPERTY(Expression_Obj, value); + public: + Supports_Interpolation(ParserState pstate, Expression_Obj v); + virtual bool needs_parens(Supports_Condition_Obj cond) const override; + ATTACH_AST_OPERATIONS(Supports_Interpolation) + ATTACH_CRTP_PERFORM_METHODS() + }; + +} + +#endif diff --git a/src/ast_values.cpp b/src/ast_values.cpp index b30673b8f..633ec5ae4 100644 --- a/src/ast_values.cpp +++ b/src/ast_values.cpp @@ -24,18 +24,351 @@ namespace Sass { str.erase( str.find_last_not_of( delimiters ) + 1 ); } - void String_Constant::rtrim() + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + PreValue::PreValue(ParserState pstate, bool d, bool e, bool i, Type ct) + : Expression(pstate, d, e, i, ct) + { } + PreValue::PreValue(const PreValue* ptr) + : Expression(ptr) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Value::Value(ParserState pstate, bool d, bool e, bool i, Type ct) + : PreValue(pstate, d, e, i, ct) + { } + Value::Value(const Value* ptr) + : PreValue(ptr) + { } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + List::List(ParserState pstate, size_t size, enum Sass_Separator sep, bool argl, bool bracket) + : Value(pstate), + Vectorized(size), + separator_(sep), + is_arglist_(argl), + is_bracketed_(bracket), + from_selector_(false) + { concrete_type(LIST); } + + 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); } + + size_t List::hash() const { - str_rtrim(value_); + 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_; } - void String_Schema::rtrim() + void List::set_delayed(bool delayed) { - if (!empty()) { - if (String_Ptr str = Cast(last())) str->rtrim(); + is_delayed(delayed); + // don't set children + } + + bool List::operator== (const Expression& rhs) const + { + if (auto 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) { + auto rv = r->at(i); + auto lv = this->at(i); + if (!lv && rv) return false; + else if (!rv && lv) return false; + else if (*lv != *rv) return false; + } + return true; + } + 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 List::value_at_index(size_t i) { + Expression_Obj obj = this->at(i); + if (is_arglist_) { + if (Argument_Ptr arg = Cast(obj)) { + return arg->value(); + } else { + return obj; + } + } else { + return obj; + } + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Map::Map(ParserState pstate, size_t size) + : Value(pstate), + Hashed(size) + { concrete_type(MAP); } + + Map::Map(const Map* ptr) + : Value(ptr), + Hashed(*ptr) + { concrete_type(MAP); } + + bool Map::operator== (const Expression& rhs) const + { + if (auto r = Cast(&rhs)) { + if (length() != r->length()) return false; + for (auto key : keys()) { + auto rv = r->at(key); + auto lv = this->at(key); + if (!lv && rv) return false; + else if (!rv && lv) return false; + else if (*lv != *rv) return false; + } + return true; + } + return false; + } + + 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; + } + + size_t Map::hash() const + { + if (hash_ == 0) { + for (auto key : keys()) { + hash_combine(hash_, key->hash()); + hash_combine(hash_, at(key)->hash()); + } + } + + return hash_; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Binary_Expression::Binary_Expression(ParserState pstate, + Operand op, Expression_Obj lhs, Expression_Obj rhs) + : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) + { } + + Binary_Expression::Binary_Expression(const Binary_Expression* ptr) + : PreValue(ptr), + op_(ptr->op_), + left_(ptr->left_), + right_(ptr->right_), + hash_(ptr->hash_) + { } + + bool Binary_Expression::is_left_interpolant(void) const + { + return is_interpolant() || (left() && left()->is_left_interpolant()); + } + bool Binary_Expression::is_right_interpolant(void) const + { + return is_interpolant() || (right() && right()->is_right_interpolant()); + } + + const std::string Binary_Expression::type_name() + { + return sass_op_to_name(optype()); + } + + const std::string Binary_Expression::separator() + { + return sass_op_separator(optype()); + } + + bool Binary_Expression::has_interpolant() const + { + return is_left_interpolant() || + is_right_interpolant(); + } + + void Binary_Expression::set_delayed(bool delayed) + { + right()->set_delayed(delayed); + left()->set_delayed(delayed); + is_delayed(delayed); + } + + bool Binary_Expression::operator==(const Expression& rhs) const + { + if (auto m = Cast(&rhs)) { + return type() == m->type() && + *left() == *m->left() && + *right() == *m->right(); } + return false; } + size_t Binary_Expression::hash() const + { + if (hash_ == 0) { + hash_ = std::hash()(optype()); + hash_combine(hash_, left()->hash()); + hash_combine(hash_, right()->hash()); + } + return hash_; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Function::Function(ParserState pstate, Definition_Obj def, bool css) + : Value(pstate), definition_(def), is_css_(css) + { concrete_type(FUNCTION_VAL); } + + Function::Function(const Function* ptr) + : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_) + { concrete_type(FUNCTION_VAL); } + + bool Function::operator== (const Expression& rhs) const + { + if (auto r = Cast(&rhs)) { + auto d1 = Cast(definition()); + auto d2 = Cast(r->definition()); + return d1 && d2 && d1 == d2 && is_css() == r->is_css(); + } + return false; + } + + std::string Function::name() { + if (definition_) { + return definition_->name(); + } + return ""; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Function_Call::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::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::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); } + + 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); } + + Function_Call::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 Function_Call::operator==(const Expression& rhs) const + { + if (auto m = Cast(&rhs)) { + if (*sname() != *m->sname()) return false; + if (arguments()->length() != m->arguments()->length()) return false; + for (size_t i = 0, L = arguments()->length(); i < L; ++i) + if (*arguments()->get(i) != *m->arguments()->get(i)) return false; + return true; + } + return false; + } + + 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_; + } + + std::string Function_Call::name() const + { + return sname(); + } + + bool Function_Call::is_css() { + if (func_) return func_->is_css(); + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Variable::Variable(ParserState pstate, std::string n) + : PreValue(pstate), name_(n) + { concrete_type(VARIABLE); } + + Variable::Variable(const Variable* ptr) + : PreValue(ptr), name_(ptr->name_) + { concrete_type(VARIABLE); } + + bool Variable::operator==(const Expression& rhs) const + { + if (auto e = Cast(&rhs)) { + return name() == e->name(); + } + return false; + } + + size_t Variable::hash() const + { + return std::hash()(name()); + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + Number::Number(ParserState pstate, double val, std::string u, bool zero) : Value(pstate), Units(), @@ -68,6 +401,13 @@ namespace Sass { concrete_type(NUMBER); } + Number::Number(const Number* ptr) + : Value(ptr), + Units(ptr), + value_(ptr->value_), zero_(ptr->zero_), + hash_(ptr->hash_) + { concrete_type(NUMBER); } + // cancel out unnecessary units void Number::reduce() { @@ -81,39 +421,37 @@ namespace Sass { value_ *= this->Units::normalize(); } - bool Custom_Warning::operator== (const Expression& rhs) const + size_t Number::hash() 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(); + 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 false; + return hash_; } bool Number::operator== (const Expression& rhs) const { - if (auto rhsnr = Cast(&rhs)) { - return *this == *rhsnr; + if (auto n = Cast(&rhs)) { + return *this == *n; } return false; } bool Number::operator== (const Number& rhs) const { + // unitless or only having one unit are equivalent (3.4) + // therefore we need to reduce the units beforehand 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()); } + // ensure both have same units l.normalize(); r.normalize(); Units &lhs_unit = l, &rhs_unit = r; return lhs_unit == rhs_unit && @@ -122,47 +460,167 @@ namespace Sass { bool Number::operator< (const Number& rhs) const { + // unitless or only having one unit are equivalent (3.4) + // therefore we need to reduce the units beforehand 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(); } + // ensure both have same units 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(); + if (lhs_unit == rhs_unit) { + return l.value() < r.value(); + } else { + return lhs_unit < rhs_unit; + } } - bool String_Quoted::operator== (const Expression& rhs) const + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Color::Color(ParserState pstate, double r, double g, double b, double a, const std::string disp) + : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp), + hash_(0) + { concrete_type(COLOR); } + + 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); } + + bool Color::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()); + if (auto r = Cast(&rhs)) { + return r_ == r->r() && + g_ == r->g() && + b_ == r->b() && + a_ == r->a(); } return false; } - bool String_Constant::is_invisible() const { - return value_.empty() && quote_mark_ == 0; + size_t Color::hash() const + { + 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 String_Constant::operator== (const Expression& rhs) const + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Custom_Error::Custom_Error(ParserState pstate, std::string msg) + : Value(pstate), message_(msg) + { concrete_type(C_ERROR); } + + Custom_Error::Custom_Error(const Custom_Error* ptr) + : Value(ptr), message_(ptr->message_) + { concrete_type(C_ERROR); } + + bool Custom_Error::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()); + if (auto r = Cast(&rhs)) { + return message() == r->message(); } return false; } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Custom_Warning::Custom_Warning(ParserState pstate, std::string msg) + : Value(pstate), message_(msg) + { concrete_type(C_WARNING); } + + Custom_Warning::Custom_Warning(const Custom_Warning* ptr) + : Value(ptr), message_(ptr->message_) + { concrete_type(C_WARNING); } + + bool Custom_Warning::operator== (const Expression& rhs) const + { + if (auto r = Cast(&rhs)) { + return message() == r->message(); + } + return false; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + Boolean::Boolean(ParserState pstate, bool val) + : Value(pstate), value_(val), + hash_(0) + { concrete_type(BOOLEAN); } + + Boolean::Boolean(const Boolean* ptr) + : Value(ptr), + value_(ptr->value_), + hash_(ptr->hash_) + { concrete_type(BOOLEAN); } + + bool Boolean::operator== (const Expression& rhs) const + { + if (auto r = Cast(&rhs)) { + return (value() == r->value()); + } + return false; + } + + size_t Boolean::hash() const + { + if (hash_ == 0) { + hash_ = std::hash()(value_); + } + return hash_; + } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + String::String(ParserState pstate, bool delayed) + : Value(pstate, delayed) + { concrete_type(STRING); } + String::String(const String* ptr) + : Value(ptr) + { concrete_type(STRING); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + String_Schema::String_Schema(ParserState pstate, size_t size, bool css) + : String(pstate), Vectorized(size), css_(css), hash_(0) + { concrete_type(STRING); } + + String_Schema::String_Schema(const String_Schema* ptr) + : String(ptr), + Vectorized(*ptr), + css_(ptr->css_), + hash_(ptr->hash_) + { concrete_type(STRING); } + + void String_Schema::rtrim() + { + if (!empty()) { + if (String_Ptr str = Cast(last())) str->rtrim(); + } + } + bool String_Schema::is_left_interpolant(void) const { return length() && first()->is_left_interpolant(); @@ -174,161 +632,162 @@ namespace Sass { bool String_Schema::operator== (const Expression& rhs) const { - if (String_Schema_Ptr_Const r = Cast(&rhs)) { + if (auto 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; + auto rv = (*r)[i]; + auto lv = (*this)[i]; + if (*lv != *rv) return false; } return true; } return false; } - bool Boolean::operator== (const Expression& rhs) const + bool String_Schema::has_interpolants() { - if (Boolean_Ptr_Const r = Cast(&rhs)) { - return (value() == r->value()); + for (auto el : elements()) { + if (el->is_interpolant()) return true; } return false; } - bool Color::operator== (const Expression& rhs) const + size_t String_Schema::hash() const { - if (Color_Ptr_Const r = Cast(&rhs)) { - return r_ == r->r() && - g_ == r->g() && - b_ == r->b() && - a_ == r->a(); + if (hash_ == 0) { + for (auto string : elements()) + hash_combine(hash_, string->hash()); } - return false; + return hash_; } - bool List::operator== (const Expression& rhs) const + void String_Schema::set_delayed(bool delayed) { - 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; + is_delayed(delayed); } - bool Map::operator== (const Expression& rhs) const + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + String_Constant::String_Constant(ParserState pstate, std::string val, bool css) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0) + { } + String_Constant::String_Constant(ParserState pstate, const char* beg, bool css) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0) + { } + String_Constant::String_Constant(ParserState pstate, const char* beg, const char* end, bool css) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0) + { } + String_Constant::String_Constant(ParserState pstate, const Token& tok, bool css) + : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0) + { } + + String_Constant::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_) + { } + + bool String_Constant::is_invisible() const { + return value_.empty() && quote_mark_ == 0; + } + + bool String_Constant::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; + if (auto qstr = Cast(&rhs)) { + return value() == qstr->value(); + } else if (auto cstr = Cast(&rhs)) { + return value() == cstr->value(); } return false; } - bool Null::operator== (const Expression& rhs) const + std::string String_Constant::inspect() const { - return rhs.concrete_type() == NULL_VAL; + return quote(value_, '*'); } - bool Function::operator== (const Expression& rhs) const + void String_Constant::rtrim() { - 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(); + str_rtrim(value_); + } + + size_t String_Constant::hash() const + { + if (hash_ == 0) { + hash_ = std::hash()(value_); } - return false; + return hash_; } - 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; - } + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + String_Quoted::String_Quoted(ParserState pstate, std::string val, char q, + bool keep_utf8_escapes, bool skip_unquoting, + bool strict_unquoting, bool css) + : String_Constant(pstate, val, css) + { + if (skip_unquoting == false) { + value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting); } - return length(); + if (q && quote_mark_) quote_mark_ = q; } - std::string String_Quoted::inspect() const + String_Quoted::String_Quoted(const String_Quoted* ptr) + : String_Constant(ptr) + { } + + bool String_Quoted::operator== (const Expression& rhs) const { - return quote(value_, '*'); + if (auto qstr = Cast(&rhs)) { + return value() == qstr->value(); + } else if (auto cstr = Cast(&rhs)) { + return value() == cstr->value(); + } + return false; } - std::string String_Constant::inspect() const + std::string String_Quoted::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 + Null::Null(ParserState pstate) + : Value(pstate) + { concrete_type(NULL_VAL); } + + Null::Null(const Null* ptr) : Value(ptr) + { concrete_type(NULL_VAL); } + + bool Null::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; } + return Cast(&rhs) != NULL; } - size_t Function_Call::hash() const + size_t Null::hash() const { - if (hash_ == 0) { - hash_ = std::hash()(name()); - for (auto argument : arguments()->elements()) - hash_combine(hash_, argument->hash()); - } - return hash_; + return -1; } - ////////////////////////////////////////////////////////////////////////////////////////// - // 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); - } + Parent_Reference::Parent_Reference(ParserState pstate) + : Value(pstate) + { concrete_type(PARENT); } - return ret; - } + Parent_Reference::Parent_Reference(const Parent_Reference* ptr) + : Value(ptr) + { concrete_type(PARENT); } + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// IMPLEMENT_AST_OPERATORS(List); IMPLEMENT_AST_OPERATORS(Map); @@ -347,4 +806,7 @@ namespace Sass { 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 index 2c029a268..ee49962e5 100644 --- a/src/ast_values.hpp +++ b/src/ast_values.hpp @@ -38,13 +38,8 @@ namespace Sass { ////////////////////////////////////////////////////////////////////// class PreValue : public Expression { public: - PreValue(ParserState pstate, - bool d = false, bool e = false, bool i = false, Type ct = NONE) - : Expression(pstate, d, e, i, ct) - { } - PreValue(const PreValue* ptr) - : Expression(ptr) - { } + PreValue(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE); + PreValue(const PreValue* ptr); ATTACH_VIRTUAL_AST_OPERATIONS(PreValue); virtual ~PreValue() { } }; @@ -54,13 +49,8 @@ namespace Sass { ////////////////////////////////////////////////////////////////////// class Value : public PreValue { public: - Value(ParserState pstate, - bool d = false, bool e = false, bool i = false, Type ct = NONE) - : PreValue(pstate, d, e, i, ct) - { } - Value(const Value* ptr) - : PreValue(ptr) - { } + Value(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE); + Value(const Value* ptr); ATTACH_VIRTUAL_AST_OPERATIONS(Value); virtual bool operator== (const Expression& rhs) const override = 0; }; @@ -77,23 +67,7 @@ namespace Sass { 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); } + List(ParserState pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false); 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 { @@ -103,25 +77,9 @@ namespace Sass { bool is_invisible() const override { return empty() && !is_bracketed(); } Expression_Obj value_at_index(size_t i); + virtual size_t hash() const override; virtual size_t size() const; - - virtual 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_; - } - - virtual void set_delayed(bool delayed) override - { - is_delayed(delayed); - // don't set children - } - + virtual void set_delayed(bool delayed) override; virtual bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(List) @@ -134,32 +92,13 @@ namespace Sass { class Map : 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); } + Map(ParserState pstate, size_t size = 0); 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); - virtual 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_; - } - + virtual size_t hash() const override; virtual bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Map) @@ -180,60 +119,19 @@ namespace Sass { 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()); - } + Operand op, Expression_Obj lhs, Expression_Obj rhs); + + const std::string type_name(); + const std::string separator(); 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(); - } - virtual void set_delayed(bool delayed) override - { - right()->set_delayed(delayed); - left()->set_delayed(delayed); - is_delayed(delayed); - } - virtual 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; } - } - virtual size_t hash() const override - { - if (hash_ == 0) { - hash_ = std::hash()(optype()); - hash_combine(hash_, left()->hash()); - hash_combine(hash_, right()->hash()); - } - return hash_; - } + bool has_interpolant() const override; + + virtual void set_delayed(bool delayed) override; + + virtual bool operator==(const Expression& rhs) const override; + + virtual size_t hash() const override; enum Sass_OP optype() const { return op_.operand; } ATTACH_AST_OPERATIONS(Binary_Expression) ATTACH_CRTP_PERFORM_METHODS() @@ -247,23 +145,13 @@ namespace Sass { 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); } + Function(ParserState pstate, Definition_Obj def, bool css); 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 ""; - } + std::string name(); bool operator== (const Expression& rhs) const override; @@ -286,34 +174,12 @@ namespace Sass { 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(ParserState pstate, String_Obj n, Arguments_Obj args, void* cookie); + Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, Function_Obj func); + Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args); - 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; - } + std::string name() const; + bool is_css(); bool operator==(const Expression& rhs) const override; @@ -329,32 +195,9 @@ namespace Sass { 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()); - } - + Variable(ParserState pstate, std::string n); + virtual bool operator==(const Expression& rhs) const override; + virtual size_t hash() const override; ATTACH_AST_OPERATIONS(Variable) ATTACH_CRTP_PERFORM_METHODS() }; @@ -369,31 +212,20 @@ namespace Sass { 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"; } + // cancel out unnecessary units + // result will be in input units void reduce(); + + // normalize units to defaults + // needed to compare two numbers 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_; - } + size_t hash() const override; bool operator< (const Number& rhs) const; bool operator== (const Number& rhs) const; @@ -413,32 +245,12 @@ namespace Sass { 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); } + Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = ""); + 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_; - } + size_t hash() const override; bool operator== (const Expression& rhs) const override; @@ -452,12 +264,7 @@ namespace Sass { 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); } + Custom_Error(ParserState pstate, std::string msg); bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Custom_Error) ATTACH_CRTP_PERFORM_METHODS() @@ -469,12 +276,7 @@ namespace Sass { 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); } + Custom_Warning(ParserState pstate, std::string msg); bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Custom_Warning) ATTACH_CRTP_PERFORM_METHODS() @@ -487,27 +289,15 @@ namespace Sass { 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); } + Boolean(ParserState pstate, bool val); 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_; - } + size_t hash() const override; + + bool is_false() override { return !value_; } bool operator== (const Expression& rhs) const override; @@ -521,12 +311,8 @@ namespace Sass { //////////////////////////////////////////////////////////////////////// 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); } + String(ParserState pstate, bool delayed = false); + String(const String* ptr); static std::string type_name() { return "string"; } virtual ~String() = 0; virtual void rtrim() = 0; @@ -546,42 +332,18 @@ namespace Sass { 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); } + String_Schema(ParserState pstate, size_t size = 0, bool css = true); 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 has_interpolants(); + void rtrim() override; + size_t hash() const override; + virtual void set_delayed(bool delayed) override; bool operator==(const Expression& rhs) const override; ATTACH_AST_OPERATIONS(String_Schema) @@ -598,45 +360,18 @@ namespace Sass { 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) - { } + String_Constant(ParserState pstate, std::string val, bool css = true); + String_Constant(ParserState pstate, const char* beg, bool css = true); + String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true); + String_Constant(ParserState pstate, const Token& tok, bool css = true); 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_; - } - + size_t hash() const override; 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 '\''; } - + // quotes are forced on inspection + virtual std::string inspect() const override; ATTACH_AST_OPERATIONS(String_Constant) ATTACH_CRTP_PERFORM_METHODS() }; @@ -648,19 +383,10 @@ namespace Sass { 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 strict_unquoting = true, bool css = true); bool operator==(const Expression& rhs) const override; - std::string inspect() const override; // quotes are forced on inspection + // quotes are forced on inspection + std::string inspect() const override; ATTACH_AST_OPERATIONS(String_Quoted) ATTACH_CRTP_PERFORM_METHODS() }; @@ -670,18 +396,14 @@ namespace Sass { ////////////////// 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); } + Null(ParserState pstate); 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; - } + size_t hash() const override; bool operator== (const Expression& rhs) const override; @@ -694,10 +416,7 @@ namespace Sass { ////////////////////////////////// class Parent_Reference final : public Value { public: - Parent_Reference(ParserState pstate) - : Value(pstate) {} - Parent_Reference(const Parent_Reference* ptr) - : Value(ptr) {} + Parent_Reference(ParserState pstate); std::string type() const override { return "parent"; } static std::string type_name() { return "parent"; } bool operator==(const Expression& rhs) const override { diff --git a/src/debugger.hpp b/src/debugger.hpp index ec774bcda..dfad6817a 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -126,7 +126,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) // if (selector->not_selector()) cerr << " [in_declaration]"; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; - std::cerr << " [" << (selector->is_real_parent_ref() ? "REAL" : "FAKE") << "]"; + std::cerr << " [" << (selector->real() ? "REAL" : "FAKE") << "]"; std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; // debug_ast(selector->selector(), ind + "->", env); diff --git a/src/fn_strings.cpp b/src/fn_strings.cpp index 16ed74b34..780e7d8e6 100644 --- a/src/fn_strings.cpp +++ b/src/fn_strings.cpp @@ -69,7 +69,7 @@ namespace Sass { 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())); + std::string str(quote(arg->to_string(ctx.c_options), '"')); String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str); result->quote_mark('*'); return result; diff --git a/src/inspect.cpp b/src/inspect.cpp index 2fb04a78c..1cfeba2e3 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -913,7 +913,7 @@ namespace Sass { void Inspect::operator()(Parent_Selector_Ptr p) { - if (p->is_real_parent_ref()) append_string("&"); + if (p->real()) append_string("&"); } void Inspect::operator()(Placeholder_Selector_Ptr s) diff --git a/src/units.cpp b/src/units.cpp index 779f1d2b4..ed99f5b2c 100644 --- a/src/units.cpp +++ b/src/units.cpp @@ -266,6 +266,10 @@ namespace Sass { return (numerators == rhs.numerators) && (denominators == rhs.denominators); } + bool Units::operator!= (const Units& rhs) const + { + return ! (*this == rhs); + } double Units::normalize() { diff --git a/src/units.hpp b/src/units.hpp index 306f5349b..3eb7369bc 100644 --- a/src/units.hpp +++ b/src/units.hpp @@ -83,6 +83,7 @@ namespace Sass { // compare operations bool operator< (const Units& rhs) const; bool operator== (const Units& rhs) const; + bool operator!= (const Units& rhs) const; // factor to convert into given units double convert_factor(const Units&) const; }; diff --git a/win/libsass.targets b/win/libsass.targets index 5c7dda247..085577637 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -13,6 +13,7 @@ + @@ -80,6 +81,7 @@ + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index 855ad11c2..a53aaeb8e 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -54,6 +54,9 @@ Headers + + Headers + Headers @@ -254,6 +257,9 @@ Sources + + Sources + Sources