#ifndef SASS_AST_SEL_H
#define SASS_AST_SEL_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
namespace Sass {
/////////////////////////////////////////////////////////////////////////
// Some helper functions
/////////////////////////////////////////////////////////////////////////
bool compoundIsSuperselector(
const CompoundSelectorObj& compound1,
const CompoundSelectorObj& compound2,
const sass::vector<SelectorComponentObj>& parents);
bool complexIsParentSuperselector(
const sass::vector<SelectorComponentObj>& complex1,
const sass::vector<SelectorComponentObj>& complex2);
sass::vector<sass::vector<SelectorComponentObj>> weave(
const sass::vector<sass::vector<SelectorComponentObj>>& complexes);
sass::vector<sass::vector<SelectorComponentObj>> weaveParents(
sass::vector<SelectorComponentObj> parents1,
sass::vector<SelectorComponentObj> parents2);
sass::vector<SimpleSelectorObj> unifyCompound(
const sass::vector<SimpleSelectorObj>& compound1,
const sass::vector<SimpleSelectorObj>& compound2);
sass::vector<sass::vector<SelectorComponentObj>> unifyComplex(
const sass::vector<sass::vector<SelectorComponentObj>>& complexes);
/////////////////////////////////////////
// Abstract base class for CSS selectors.
/////////////////////////////////////////
class Selector : public Expression {
protected:
mutable size_t hash_;
public:
Selector(SourceSpan pstate);
virtual ~Selector() = 0;
size_t hash() const override = 0;
virtual bool has_real_parent_ref() const;
// you should reset this to null on containers
virtual unsigned long specificity() const = 0;
// by default we return the regular specificity
// you must override this for all containers
virtual size_t maxSpecificity() const { return specificity(); }
virtual size_t minSpecificity() const { return specificity(); }
// dispatch to correct handlers
ATTACH_VIRTUAL_CMP_OPERATIONS(Selector)
ATTACH_VIRTUAL_AST_OPERATIONS(Selector)
};
inline Selector::~Selector() { }
/////////////////////////////////////////////////////////////////////////
// Interpolated selectors -- the interpolated String will be expanded and
// re-parsed into a normal selector class.
/////////////////////////////////////////////////////////////////////////
class Selector_Schema final : public AST_Node {
ADD_PROPERTY(String_Schema_Obj, contents)
ADD_PROPERTY(bool, connect_parent);
// store computed hash
mutable size_t hash_;
public:
Selector_Schema(SourceSpan pstate, String_Obj c);
bool has_real_parent_ref() const;
// selector schema is not yet a final selector, so we do not
// have a specificity for it yet. We need to
virtual unsigned long specificity() const;
size_t hash() const override;
ATTACH_AST_OPERATIONS(Selector_Schema)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////
// Abstract base class for simple selectors.
////////////////////////////////////////////
class SimpleSelector : public Selector {
public:
enum Simple_Type {
ID_SEL,
TYPE_SEL,
CLASS_SEL,
PSEUDO_SEL,
ATTRIBUTE_SEL,
PLACEHOLDER_SEL,
};
public:
HASH_CONSTREF(sass::string, ns)
HASH_CONSTREF(sass::string, name)
ADD_PROPERTY(Simple_Type, simple_type)
HASH_PROPERTY(bool, has_ns)
public:
SimpleSelector(SourceSpan pstate, sass::string n = "");
// ordering within parent (peudos go last)
virtual int getSortOrder() const = 0;
virtual sass::string ns_name() const;
size_t hash() const override;
virtual bool empty() const;
// namespace compare functions
bool is_ns_eq(const SimpleSelector& r) const;
// namespace query functions
bool is_universal_ns() const;
bool is_empty_ns() const;
bool has_empty_ns() const;
bool has_qualified_ns() const;
// name query functions
bool is_universal() const;
virtual bool has_placeholder();
virtual ~SimpleSelector() = 0;
virtual CompoundSelector* unifyWith(CompoundSelector*);
/* helper function for syntax sugar */
virtual IDSelector* getIdSelector() { return NULL; }
virtual TypeSelector* getTypeSelector() { return NULL; }
virtual PseudoSelector* getPseudoSelector() { return NULL; }
ComplexSelectorObj wrapInComplex();
CompoundSelectorObj wrapInCompound();
virtual bool isInvisible() const { return false; }
virtual bool is_pseudo_element() const;
virtual bool has_real_parent_ref() const override;
bool operator==(const Selector& rhs) const final override;
virtual bool operator==(const SelectorList& rhs) const;
virtual bool operator==(const ComplexSelector& rhs) const;
virtual bool operator==(const CompoundSelector& rhs) const;
ATTACH_VIRTUAL_CMP_OPERATIONS(SimpleSelector);
ATTACH_VIRTUAL_AST_OPERATIONS(SimpleSelector);
ATTACH_CRTP_PERFORM_METHODS();
};
inline SimpleSelector::~SimpleSelector() { }
/////////////////////////////////////////////////////////////////////////
// Placeholder selectors (e.g., "%foo") for use in extend-only selectors.
/////////////////////////////////////////////////////////////////////////
class PlaceholderSelector final : public SimpleSelector {
public:
PlaceholderSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 0; }
bool isInvisible() const override { return true; }
virtual unsigned long specificity() const override;
virtual bool has_placeholder() override;
bool operator==(const SimpleSelector& rhs) const override;
ATTACH_CMP_OPERATIONS(PlaceholderSelector)
ATTACH_AST_OPERATIONS(PlaceholderSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
/////////////////////////////////////////////////////////////////////
// Type selectors (and the universal selector) -- e.g., div, span, *.
/////////////////////////////////////////////////////////////////////
class TypeSelector final : public SimpleSelector {
public:
TypeSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 1; }
virtual unsigned long specificity() const override;
SimpleSelector* unifyWith(const SimpleSelector*);
CompoundSelector* unifyWith(CompoundSelector*) override;
TypeSelector* getTypeSelector() override { return this; }
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(TypeSelector)
ATTACH_AST_OPERATIONS(TypeSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////
// Class selectors -- i.e., .foo.
////////////////////////////////////////////////
class ClassSelector final : public SimpleSelector {
public:
ClassSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 2; }
virtual unsigned long specificity() const override;
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(ClassSelector)
ATTACH_AST_OPERATIONS(ClassSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////
// ID selectors -- i.e., #foo.
////////////////////////////////////////////////
class IDSelector final : public SimpleSelector {
public:
IDSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 2; }
virtual unsigned long specificity() const override;
CompoundSelector* unifyWith(CompoundSelector*) override;
IDSelector* getIdSelector() final override { return this; }
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(IDSelector)
ATTACH_AST_OPERATIONS(IDSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////////////////////////////////
// Attribute selectors -- e.g., [src*=".jpg"], etc.
///////////////////////////////////////////////////
class AttributeSelector final : public SimpleSelector {
ADD_CONSTREF(sass::string, matcher)
// this cannot be changed to obj atm!!!!!!????!!!!!!!
ADD_PROPERTY(String_Obj, value) // might be interpolated
ADD_PROPERTY(char, modifier);
public:
AttributeSelector(SourceSpan pstate, sass::string n, sass::string m, String_Obj v, char o = 0);
int getSortOrder() const override final { return 2; }
size_t hash() const override;
virtual unsigned long specificity() const override;
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(AttributeSelector)
ATTACH_AST_OPERATIONS(AttributeSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////////////////////////////////////////
// Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.
//////////////////////////////////////////////////////////////////
// Pseudo Selector cannot have any namespace?
class PseudoSelector final : public SimpleSelector {
ADD_PROPERTY(sass::string, normalized)
ADD_PROPERTY(String_Obj, argument)
ADD_PROPERTY(SelectorListObj, selector)
ADD_PROPERTY(bool, isSyntacticClass)
ADD_PROPERTY(bool, isClass)
public:
PseudoSelector(SourceSpan pstate, sass::string n, bool element = false);
int getSortOrder() const override final { return 3; }
virtual bool is_pseudo_element() const override;
size_t hash() const override;
bool empty() const override;
bool has_real_parent_ref() const override;
// Whether this is a pseudo-element selector.
// This is `true` if and only if [isClass] is `false`.
bool isElement() const { return !isClass(); }
// Whether this is syntactically a pseudo-element selector.
// This is `true` if and only if [isSyntacticClass] is `false`.
bool isSyntacticElement() const { return !isSyntacticClass(); }
virtual unsigned long specificity() const override;
PseudoSelectorObj withSelector(SelectorListObj selector);
CompoundSelector* unifyWith(CompoundSelector*) override;
PseudoSelector* getPseudoSelector() final override { return this; }
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(PseudoSelector)
ATTACH_AST_OPERATIONS(PseudoSelector)
void cloneChildren() override;
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////////
// Complex Selectors are the most important class of selectors.
// A Selector List consists of Complex Selectors (separated by comma)
// Complex Selectors are itself a list of Compounds and Combinators
// Between each item there is an implicit ancestor of combinator
////////////////////////////////////////////////////////////////////////////
class ComplexSelector final : public Selector, public Vectorized<SelectorComponentObj> {
ADD_PROPERTY(bool, chroots);
// line break before list separator
ADD_PROPERTY(bool, hasPreLineFeed);
public:
ComplexSelector(SourceSpan pstate);
// Returns true if the first components
// is a compound selector and fulfills
// a few other criteria.
bool isInvisible() const;
bool isInvalidCss() const;
size_t hash() const override;
void cloneChildren() override;
bool has_placeholder() const;
bool has_real_parent_ref() const override;
SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);
virtual unsigned long specificity() const override;
SelectorList* unifyWith(ComplexSelector* rhs);
bool isSuperselectorOf(const ComplexSelector* sub) const;
SelectorListObj wrapInList();
size_t maxSpecificity() const override;
size_t minSpecificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const SelectorList& rhs) const;
bool operator==(const CompoundSelector& rhs) const;
bool operator==(const SimpleSelector& rhs) const;
ATTACH_CMP_OPERATIONS(ComplexSelector)
ATTACH_AST_OPERATIONS(ComplexSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////////
// Base class for complex selector components
////////////////////////////////////////////////////////////////////////////
class SelectorComponent : public Selector {
// line break after list separator
ADD_PROPERTY(bool, hasPostLineBreak)
public:
SelectorComponent(SourceSpan pstate, bool postLineBreak = false);
size_t hash() const override = 0;
void cloneChildren() override;
// By default we consider instances not empty
virtual bool empty() const { return false; }
virtual bool has_placeholder() const = 0;
bool has_real_parent_ref() const override = 0;
ComplexSelector* wrapInComplex();
size_t maxSpecificity() const override { return 0; }
size_t minSpecificity() const override { return 0; }
virtual bool isCompound() const { return false; };
virtual bool isCombinator() const { return false; };
/* helper function for syntax sugar */
virtual CompoundSelector* getCompound() { return NULL; }
virtual SelectorCombinator* getCombinator() { return NULL; }
virtual const CompoundSelector* getCompound() const { return NULL; }
virtual const SelectorCombinator* getCombinator() const { return NULL; }
virtual unsigned long specificity() const override;
bool operator==(const Selector& rhs) const override = 0;
ATTACH_VIRTUAL_CMP_OPERATIONS(SelectorComponent);
ATTACH_VIRTUAL_AST_OPERATIONS(SelectorComponent);
};
////////////////////////////////////////////////////////////////////////////
// A specific combinator between compound selectors
////////////////////////////////////////////////////////////////////////////
class SelectorCombinator final : public SelectorComponent {
public:
// Enumerate all possible selector combinators. There is some
// discrepancy with dart-sass. Opted to name them as in CSS33
enum Combinator { CHILD /* > */, GENERAL /* ~ */, ADJACENT /* + */};
private:
// Store the type of this combinator
HASH_CONSTREF(Combinator, combinator)
public:
SelectorCombinator(SourceSpan pstate, Combinator combinator, bool postLineBreak = false);
bool has_real_parent_ref() const override { return false; }
bool has_placeholder() const override { return false; }
/* helper function for syntax sugar */
SelectorCombinator* getCombinator() final override { return this; }
const SelectorCombinator* getCombinator() const final override { return this; }
// Query type of combinator
bool isCombinator() const override { return true; };
// Matches the right-hand selector if it's a direct child of the left-
// hand selector in the DOM tree. Dart-sass also calls this `child`
// https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator
bool isChildCombinator() const { return combinator_ == CHILD; } // >
// Matches the right-hand selector if it comes after the left-hand
// selector in the DOM tree. Dart-sass class this `followingSibling`
// https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator
bool isGeneralCombinator() const { return combinator_ == GENERAL; } // ~
// Matches the right-hand selector if it's immediately adjacent to the
// left-hand selector in the DOM tree. Dart-sass calls this `nextSibling`
// https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator
bool isAdjacentCombinator() const { return combinator_ == ADJACENT; } // +
size_t maxSpecificity() const override { return 0; }
size_t minSpecificity() const override { return 0; }
size_t hash() const override {
return std::hash<int>()(combinator_);
}
void cloneChildren() override;
virtual unsigned long specificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const SelectorComponent& rhs) const override;
ATTACH_CMP_OPERATIONS(SelectorCombinator)
ATTACH_AST_OPERATIONS(SelectorCombinator)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////////
// A compound selector consists of multiple simple selectors
////////////////////////////////////////////////////////////////////////////
class CompoundSelector final : public SelectorComponent, public Vectorized<SimpleSelectorObj> {
ADD_PROPERTY(bool, hasRealParent)
public:
CompoundSelector(SourceSpan pstate, bool postLineBreak = false);
// Returns true if this compound selector
// fulfills various criteria.
bool isInvisible() const;
bool empty() const override {
return Vectorized::empty();
}
size_t hash() const override;
CompoundSelector* unifyWith(CompoundSelector* rhs);
/* helper function for syntax sugar */
CompoundSelector* getCompound() final override { return this; }
const CompoundSelector* getCompound() const final override { return this; }
bool isSuperselectorOf(const CompoundSelector* sub, sass::string wrapped = "") const;
void cloneChildren() override;
bool has_real_parent_ref() const override;
bool has_placeholder() const override;
sass::vector<ComplexSelectorObj> resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);
virtual bool isCompound() const override { return true; };
virtual unsigned long specificity() const override;
size_t maxSpecificity() const override;
size_t minSpecificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const SelectorComponent& rhs) const override;
bool operator==(const SelectorList& rhs) const;
bool operator==(const ComplexSelector& rhs) const;
bool operator==(const SimpleSelector& rhs) const;
void sortChildren();
bool isInvalidCss() const;
ATTACH_CMP_OPERATIONS(CompoundSelector)
ATTACH_AST_OPERATIONS(CompoundSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////////////////
// Comma-separated selector groups.
///////////////////////////////////
class SelectorList final : public Selector, public Vectorized<ComplexSelectorObj> {
private:
// maybe we have optional flag
// ToDo: should be at ExtendRule?
ADD_PROPERTY(bool, is_optional)
public:
SelectorList(SourceSpan pstate, size_t s = 0);
sass::string type() const override { return "list"; }
size_t hash() const override;
SelectorList* unifyWith(SelectorList*);
// Returns true if all complex selectors
// can have real parents, meaning every
// first component does allow for it
bool isInvisible() const;
void cloneChildren() override;
bool has_real_parent_ref() const override;
SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);
virtual unsigned long specificity() const override;
bool isSuperselectorOf(const SelectorList* sub) const;
size_t maxSpecificity() const override;
size_t minSpecificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const ComplexSelector& rhs) const;
bool operator==(const CompoundSelector& rhs) const;
bool operator==(const SimpleSelector& rhs) const;
// Selector Lists can be compared to comma lists
bool operator==(const Expression& rhs) const override;
ATTACH_CMP_OPERATIONS(SelectorList)
ATTACH_AST_OPERATIONS(SelectorList)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////
// The Sass `@extend` directive.
////////////////////////////////
class ExtendRule final : public Statement {
ADD_PROPERTY(bool, isOptional)
// This should be a simple selector only!
ADD_PROPERTY(SelectorListObj, selector)
ADD_PROPERTY(Selector_Schema_Obj, schema)
public:
ExtendRule(SourceSpan pstate, SelectorListObj s);
ExtendRule(SourceSpan pstate, Selector_Schema_Obj s);
ATTACH_AST_OPERATIONS(ExtendRule)
ATTACH_CRTP_PERFORM_METHODS()
};
}
#endif