packages feed

hlibsass 0.1.8.1 → 0.1.9.0

raw patch · 144 files changed

+17034/−15310 lines, 144 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Bindings.Libsass.Context: sass_context_take_error_src :: Ptr SassContext -> IO CString
+ Bindings.Libsass.Types: SassStyleToCss :: SassOutputStyle

Files

Bindings/Libsass/Context.hs view
@@ -327,6 +327,10 @@     :: Ptr SassContext     -> IO CString +foreign import ccall unsafe "sass/context.h" sass_context_take_error_src+    :: Ptr SassContext+    -> IO CString+ foreign import ccall unsafe "sass/context.h" sass_context_take_output_string     :: Ptr SassContext     -> IO CString
Bindings/Libsass/Types.hs view
@@ -13,6 +13,7 @@                      | SassStyleCompressed                      | SassStyleInspect -- ^ Marked as internal                      | SassStyleToSass -- ^ Marked as internal+                     | SassStyleToCss -- ^ Marked as internal                      deriving (Eq, Show)  instance Enum SassOutputStyle where@@ -22,6 +23,7 @@     fromEnum SassStyleCompressed = 3     fromEnum SassStyleInspect    = 4     fromEnum SassStyleToSass     = 5+    fromEnum SassStyleToCss      = 6      toEnum 0 = SassStyleNested     toEnum 1 = SassStyleExpanded@@ -29,6 +31,7 @@     toEnum 3 = SassStyleCompressed     toEnum 4 = SassStyleInspect     toEnum 5 = SassStyleToSass+    toEnum 6 = SassStyleToCss     toEnum u = error ("SassOutputStyle.toEnum: Cannot match " ++ show u)  -- From sass/context.h
CHANGELOG.md view
@@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.1.9.0] - 2020-03-26+### Changed+- Update to LibSass 3.6.3+ ## [0.1.8.1] - 2020-02-21 ### Changed - Support Cabal 3@@ -87,6 +91,7 @@ - Bindings to Libsass C API - Basic tests +[0.1.9.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.8.1...v0.1.9.0 [0.1.8.1]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.8.0...v0.1.8.1 [0.1.8.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.7.0...v0.1.8.0 [0.1.7.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.6.1...v0.1.7.0
hlibsass.cabal view
@@ -1,5 +1,5 @@ name:                hlibsass-version:             0.1.8.1+version:             0.1.9.0 license:             MIT license-file:        LICENSE author:              Jakub Fijałkowski <kuba@codinginfinity.me>
libsass/Makefile view
@@ -1,6 +1,6 @@ OS       ?= $(shell uname -s)-CC       ?= gcc-CXX      ?= g+++CC       ?= cc+CXX      ?= c++ RM       ?= rm -f CP       ?= cp -a MKDIR    ?= mkdir@@ -15,7 +15,7 @@ CFLAGS   ?= -Wall CXXFLAGS ?= -Wall LDFLAGS  ?= -Wall-ifeq "x$(COVERAGE)" "x"+ifndef COVERAGE   CFLAGS   += -O2   CXXFLAGS += -O2   LDFLAGS  += -O2@@ -24,66 +24,58 @@   CXXFLAGS += -O1 -fno-omit-frame-pointer   LDFLAGS  += -O1 -fno-omit-frame-pointer endif-LDFLAGS  += -Wl,-undefined,error-CAT      ?= $(if $(filter $(OS),Windows_NT),type,cat)+CAT ?= $(if $(filter $(OS),Windows_NT),type,cat)  ifneq (,$(findstring /cygdrive/,$(PATH))) 	UNAME := Cygwin else-	ifneq (,$(findstring Windows_NT,$(OS)))-		UNAME := Windows-	else-		ifneq (,$(findstring mingw32,$(MAKE)))-			UNAME := Windows-		else-			ifneq (,$(findstring MINGW32,$(shell uname -s)))-				UNAME = Windows-			else-				UNAME := $(shell uname -s)-			endif-		endif-	endif+ifneq (,$(findstring Windows_NT,$(OS)))+	UNAME := Windows+else+ifneq (,$(findstring mingw32,$(MAKE)))+	UNAME := Windows+else+ifneq (,$(findstring MINGW32,$(shell uname -s)))+	UNAME := Windows+else+	UNAME := $(shell uname -s) endif--ifeq ($(SASS_LIBSASS_PATH),)-	SASS_LIBSASS_PATH = $(abspath $(CURDIR)) endif+endif+endif -ifeq ($(LIBSASS_VERSION),)+ifndef LIBSASS_VERSION 	ifneq ($(wildcard ./.git/ ),) 		LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags) 	endif-endif--ifeq ($(LIBSASS_VERSION),) 	ifneq ($(wildcard VERSION),) 		LIBSASS_VERSION ?= $(shell $(CAT) VERSION) 	endif endif--ifneq ($(LIBSASS_VERSION),)+ifdef LIBSASS_VERSION 	CFLAGS   += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" 	CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" endif -# enable mandatory flag+CXXFLAGS += -std=c++11+LDFLAGS  += -std=c++11+ ifeq (Windows,$(UNAME)) 	ifneq ($(BUILD),shared) 		STATIC_ALL     ?= 1 	endif 	STATIC_LIBGCC    ?= 1 	STATIC_LIBSTDCPP ?= 1-	CXXFLAGS += -std=gnu++0x-	LDFLAGS  += -std=gnu++0x else 	STATIC_ALL       ?= 0 	STATIC_LIBGCC    ?= 0 	STATIC_LIBSTDCPP ?= 0-	CXXFLAGS += -std=c++0x-	LDFLAGS  += -std=c++0x endif -ifneq ($(SASS_LIBSASS_PATH),)+ifndef SASS_LIBSASS_PATH+	SASS_LIBSASS_PATH = $(CURDIR)+endif+ifdef SASS_LIBSASS_PATH 	CFLAGS   += -I $(SASS_LIBSASS_PATH)/include 	CXXFLAGS += -I $(SASS_LIBSASS_PATH)/include else@@ -92,20 +84,15 @@ 	CXXFLAGS += -I include endif -ifneq ($(EXTRA_CFLAGS),)-	CFLAGS   += $(EXTRA_CFLAGS)-endif-ifneq ($(EXTRA_CXXFLAGS),)-	CXXFLAGS += $(EXTRA_CXXFLAGS)-endif-ifneq ($(EXTRA_LDFLAGS),)-	LDFLAGS  += $(EXTRA_LDFLAGS)-endif+CFLAGS   += $(EXTRA_CFLAGS)+CXXFLAGS += $(EXTRA_CXXFLAGS)+LDFLAGS  += $(EXTRA_LDFLAGS)  LDLIBS = -lm- ifneq ($(BUILD),shared)-	LDLIBS += -lstdc+++	ifneq ($(STATIC_LIBSTDCPP),1)+		LDLIBS += -lstdc+++	endif endif  # link statically into lib@@ -143,7 +130,7 @@ 	BUILD := debug-$(BUILD) endif -ifeq (,$(TRAVIS_BUILD_DIR))+ifndef TRAVIS_BUILD_DIR 	ifeq ($(OS),SunOS) 		PREFIX ?= /opt/local 	else@@ -153,54 +140,39 @@ 	PREFIX ?= $(TRAVIS_BUILD_DIR) endif - SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc RUBY_BIN = ruby +RESOURCES =+STATICLIB = lib/libsass.a+SHAREDLIB = lib/libsass.so LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so--ifeq (Windows,$(UNAME))-	ifeq (shared,$(BUILD))-		CFLAGS     += -D ADD_EXPORTS-		CXXFLAGS   += -D ADD_EXPORTS-		LIB_SHARED  = $(SASS_LIBSASS_PATH)/lib/libsass.dll-	endif-else-	ifneq (Cygwin,$(UNAME))-		CFLAGS   += -fPIC-		CXXFLAGS += -fPIC-		LDFLAGS  += -fPIC-	endif+ifeq ($(UNAME),Darwin)+	SHAREDLIB = lib/libsass.dylib+	LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dylib endif- ifeq (Windows,$(UNAME)) 	SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe-endif--include Makefile.conf--RESOURCES =-STATICLIB = lib/libsass.a-SHAREDLIB = lib/libsass.so-ifeq (Windows,$(UNAME)) 	RESOURCES += res/resource.rc 	SHAREDLIB  = lib/libsass.dll 	ifeq (shared,$(BUILD)) 		CFLAGS    += -D ADD_EXPORTS 		CXXFLAGS  += -D ADD_EXPORTS+		LIB_SHARED  = $(SASS_LIBSASS_PATH)/lib/libsass.dll 	endif else-	ifneq (Cygwin,$(UNAME))-		CFLAGS   += -fPIC-		CXXFLAGS += -fPIC-		LDFLAGS  += -fPIC-	endif+ifneq (Cygwin,$(UNAME))+	CFLAGS   += -fPIC+	CXXFLAGS += -fPIC+	LDFLAGS  += -fPIC endif+endif +include Makefile.conf OBJECTS = $(addprefix src/,$(SOURCES:.cpp=.o)) COBJECTS = $(addprefix src/,$(CSOURCES:.c=.o)) RCOBJECTS = $(RESOURCES:.rc=.o)@@ -230,15 +202,20 @@ lib: 	$(MKDIR) lib -lib/libsass.a: lib $(COBJECTS) $(OBJECTS)+lib/libsass.a: $(COBJECTS) $(OBJECTS) | lib 	$(AR) rcvs $@ $(COBJECTS) $(OBJECTS) -lib/libsass.so: lib $(COBJECTS) $(OBJECTS)+lib/libsass.so: $(COBJECTS) $(OBJECTS) | lib 	$(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS) -lib/libsass.dll: lib $(COBJECTS) $(OBJECTS) $(RCOBJECTS)-	$(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -Wl,--subsystem,windows,--out-implib,lib/libsass.a+lib/libsass.dylib: $(COBJECTS) $(OBJECTS) | lib+	$(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS) \+	-install_name @rpath/libsass.dylib +lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) | lib+	$(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) \+	-s -Wl,--subsystem,windows,--out-implib,lib/libsass.a+ %.o: %.c 	$(CC) $(CFLAGS) -c -o $@ $< @@ -259,18 +236,17 @@ $(DESTDIR)$(PREFIX): 	$(MKDIR) $(DESTDIR)$(PREFIX) -$(DESTDIR)$(PREFIX)/lib: $(DESTDIR)$(PREFIX)+$(DESTDIR)$(PREFIX)/lib: | $(DESTDIR)$(PREFIX) 	$(MKDIR) $(DESTDIR)$(PREFIX)/lib -$(DESTDIR)$(PREFIX)/include: $(DESTDIR)$(PREFIX)+$(DESTDIR)$(PREFIX)/include: | $(DESTDIR)$(PREFIX) 	$(MKDIR) $(DESTDIR)$(PREFIX)/include -$(DESTDIR)$(PREFIX)/include/sass: $(DESTDIR)$(PREFIX)/include+$(DESTDIR)$(PREFIX)/include/sass: | $(DESTDIR)$(PREFIX)/include 	$(MKDIR) $(DESTDIR)$(PREFIX)/include/sass  $(DESTDIR)$(PREFIX)/include/%.h: include/%.h \-                                 $(DESTDIR)$(PREFIX)/include \-                                 $(DESTDIR)$(PREFIX)/include/sass+                                 | $(DESTDIR)$(PREFIX)/include/sass 	$(INSTALL) -v -m0644 "$<" "$@"  install-headers: $(DESTDIR)$(PREFIX)/include/sass.h \@@ -282,46 +258,59 @@                  $(DESTDIR)$(PREFIX)/include/sass/functions.h  $(DESTDIR)$(PREFIX)/lib/%.a: lib/%.a \-                             $(DESTDIR)$(PREFIX)/lib+                             | $(DESTDIR)$(PREFIX)/lib 	@$(INSTALL) -v -m0755 "$<" "$@"  $(DESTDIR)$(PREFIX)/lib/%.so: lib/%.so \-                             $(DESTDIR)$(PREFIX)/lib+                             | $(DESTDIR)$(PREFIX)/lib 	@$(INSTALL) -v -m0755 "$<" "$@"  $(DESTDIR)$(PREFIX)/lib/%.dll: lib/%.dll \-                               $(DESTDIR)$(PREFIX)/lib+                               | $(DESTDIR)$(PREFIX)/lib 	@$(INSTALL) -v -m0755 "$<" "$@" +$(DESTDIR)$(PREFIX)/lib/%.dylib: lib/%.dylib \+                               | $(DESTDIR)$(PREFIX)/lib+	@$(INSTALL) -v -m0755 "$<" "$@"+ install-static: $(DESTDIR)$(PREFIX)/lib/libsass.a -install-shared: $(DESTDIR)$(PREFIX)/lib/libsass.so \+install-shared: $(DESTDIR)$(PREFIX)/$(SHAREDLIB) \                 install-headers  $(SASSC_BIN): $(BUILD)-	$(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD)-dev+	$(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD)  sassc: $(SASSC_BIN) 	$(SASSC_BIN) -v  version: $(SASSC_BIN)-	$(SASSC_BIN) -h 	$(SASSC_BIN) -v -test: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+test: test_build  test_build: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+	--cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+	$(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)  test_full: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+	--cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+	--run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)  test_probe: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+	--cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+	--probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) -clean-objects: lib-	-$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la+test_interactive: $(SASSC_BIN)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+	--cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+	--interactive $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)++clean-objects: | lib+	-$(RM) lib/*.a lib/*.so lib/*.dll lib/*.dylib lib/*.la 	-$(RMDIR) lib clean: clean-objects 	$(RM) $(CLEANUPS)@@ -347,5 +336,6 @@         debug debug-static debug-shared \         install install-static install-shared \         lib-opts lib-opts-shared lib-opts-static \-        lib-file lib-file-shared lib-file-static+        lib-file lib-file-shared lib-file-static \+        test test_build test_full test_probe .DELETE_ON_ERROR:
libsass/Makefile.conf view
@@ -7,16 +7,30 @@  SOURCES = \ 	ast.cpp \-	node.cpp \+	ast_values.cpp \+	ast_supports.cpp \+	ast_sel_cmp.cpp \+	ast_sel_unify.cpp \+	ast_sel_super.cpp \+	ast_sel_weave.cpp \+	ast_selectors.cpp \ 	context.cpp \ 	constants.cpp \-	functions.cpp \+	fn_utils.cpp \+	fn_miscs.cpp \+	fn_maps.cpp \+	fn_lists.cpp \+	fn_colors.cpp \+	fn_numbers.cpp \+	fn_strings.cpp \+	fn_selectors.cpp \ 	color_maps.cpp \ 	environment.cpp \ 	ast_fwd_decl.cpp \ 	bind.cpp \ 	file.cpp \ 	util.cpp \+	util_string.cpp \ 	json.cpp \ 	units.cpp \ 	values.cpp \@@ -24,29 +38,32 @@ 	position.cpp \ 	lexer.cpp \ 	parser.cpp \+	parser_selectors.cpp \ 	prelexer.cpp \ 	eval.cpp \+	eval_selectors.cpp \ 	expand.cpp \ 	listize.cpp \ 	cssize.cpp \-	extend.cpp \+	extender.cpp \+	extension.cpp \+	stylesheet.cpp \ 	output.cpp \ 	inspect.cpp \ 	emitter.cpp \ 	check_nesting.cpp \ 	remove_placeholders.cpp \ 	sass.cpp \-	sass_util.cpp \ 	sass_values.cpp \ 	sass_context.cpp \ 	sass_functions.cpp \ 	sass2scss.cpp \ 	backtrace.cpp \ 	operators.cpp \-	to_c.cpp \+	ast2c.cpp \+	c2ast.cpp \ 	to_value.cpp \ 	source_map.cpp \-	subset_map.cpp \ 	error_handling.cpp \ 	memory/SharedPtr.cpp \ 	utf8_string.cpp \
libsass/include/sass/base.h view
@@ -1,6 +1,7 @@ #ifndef SASS_BASE_H #define SASS_BASE_H +// #define DEBUG // #define DEBUG_SHARED_PTR  #ifdef _MSC_VER@@ -16,6 +17,12 @@   #endif #endif +// Work around lack of `noexcept` keyword support in VS2013+#if defined(_MSC_VER) && (_MSC_VER <= 1800) && !defined(_ALLOW_KEYWORD_MACROS)+#define _ALLOW_KEYWORD_MACROS 1+#define noexcept throw( )+#endif+ #include <stddef.h> #include <stdbool.h> @@ -61,7 +68,8 @@   SASS_STYLE_COMPRESSED,   // only used internaly   SASS_STYLE_INSPECT,-  SASS_STYLE_TO_SASS+  SASS_STYLE_TO_SASS,+  SASS_STYLE_TO_CSS };  // to allocate buffer to be filled
libsass/include/sass/context.h view
@@ -43,7 +43,7 @@ ADDAPI struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx);  // Execute the different compilation steps individually-// Usefull if you only want to query the included files+// Useful if you only want to query the included files ADDAPI int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler); ADDAPI int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler); @@ -134,6 +134,7 @@ ADDAPI char* ADDCALL sass_context_take_error_text (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_message (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_file (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_error_src (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_output_string (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_source_map_string (struct Sass_Context* ctx); ADDAPI char** ADDCALL sass_context_take_included_files (struct Sass_Context* ctx);
libsass/src/GNUmakefile.am view
@@ -13,11 +13,7 @@ AM_CXXFLAGS = $(AM_COPT) AM_LDFLAGS  = $(AM_COPT) $(AM_COVLDFLAGS) -if COMPILER_IS_MINGW32-  AM_CXXFLAGS += -std=gnu++0x-else-  AM_CXXFLAGS += -std=c++0x-endif+AM_CXXFLAGS += -std=c++11  EXTRA_DIST = \ 	COPYING \
libsass/src/ast.cpp view
@@ -1,2226 +1,953 @@-#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 <set>-#include <iomanip>-#include <iostream>-#include <algorithm>-#include <functional>-#include <cctype>-#include <locale>--namespace Sass {--  static Null sass_null(ParserState("null"));--  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);-  }--  bool Selector_List::find ( bool (*f)(AST_Node_Obj) )-  {-    // check children first-    for (Complex_Selector_Obj sel : elements()) {-      if (sel->find(f)) return true;-    }-    // execute last-    return f(this);-  }--  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 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 Supports_Operator::needs_parens(Supports_Condition_Obj cond) const {-    if (Supports_Operator_Obj op = Cast<Supports_Operator>(cond)) {-      return op->operand() != operand();-    }-    return Cast<Supports_Negation>(cond) != NULL;-  }--  bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const {-    return Cast<Supports_Negation>(cond) ||-           Cast<Supports_Operator>(cond);-  }--  void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v")-  {-    str.erase( str.find_last_not_of( delimiters ) + 1 );-  }--  void String_Constant::rtrim()-  {-    str_rtrim(value_);-  }--  void String_Schema::rtrim()-  {-    if (!empty()) {-      if (String_Ptr str = Cast<String>(last())) str->rtrim();-    }-  }--  void Argument::set_delayed(bool delayed)-  {-    if (value_) value_->set_delayed(delayed);-    is_delayed(delayed);-  }--  void Arguments::set_delayed(bool delayed)-  {-    for (Argument_Obj arg : elements()) {-      if (arg) arg->set_delayed(delayed);-    }-    is_delayed(delayed);-  }---  bool At_Root_Query::exclude(std::string str)-  {-    bool with = feature() && unquote(feature()->to_string()).compare("with") == 0;-    List_Ptr l = static_cast<List_Ptr>(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;-    }-  }--  void AST_Node::update_pstate(const ParserState& pstate)-  {-    pstate_.offset += pstate - pstate_ + pstate.offset;-  }--  bool Simple_Selector::is_ns_eq(const Simple_Selector& r) const-  {-    // https://github.com/sass/sass/issues/2229-    if ((has_ns_ == r.has_ns_) ||-        (has_ns_ && ns_.empty()) ||-        (r.has_ns_ && r.ns_.empty())-    ) {-      if (ns_.empty() && r.ns() == "*") return false;-      else if (r.ns().empty() && ns() == "*") return false;-      else return ns() == r.ns();-    }-    return false;-  }--  bool Compound_Selector::operator< (const Compound_Selector& rhs) const-  {-    size_t L = std::min(length(), rhs.length());-    for (size_t i = 0; i < L; ++i)-    {-      Simple_Selector_Obj l = (*this)[i];-      Simple_Selector_Obj r = rhs[i];-      if (!l && !r) return false;-      else if (!r) return false;-      else if (!l) return true;-      else if (*l != *r)-      { return *l < *r; }-    }-    // just compare the length now-    return length() < rhs.length();-  }--  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 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::operator< (const Complex_Selector& rhs) const-  {-    // const iterators for tails-    Complex_Selector_Ptr_Const l = this;-    Complex_Selector_Ptr_Const r = &rhs;-    Compound_Selector_Ptr l_h = NULL;-    Compound_Selector_Ptr r_h = NULL;-    if (l) l_h = l->head();-    if (r) r_h = r->head();-    // process all tails-    while (true)-    {-      #ifdef DEBUG-      // skip empty ancestor first-      if (l && l->is_empty_ancestor())-      {-        l_h = NULL;-        l = l->tail();-        if(l) l_h = l->head();-        continue;-      }-      // skip empty ancestor first-      if (r && r->is_empty_ancestor())-      {-        r_h = NULL;-        r = r->tail();-        if (r) r_h = r->head();-        continue;-      }-      #endif-      // check for valid selectors-      if (!l) return !!r;-      if (!r) return false;-      // both are null-      else if (!l_h && !r_h)-      {-        // check combinator after heads-        if (l->combinator() != r->combinator())-        { return l->combinator() < r->combinator(); }-        // advance to next tails-        l = l->tail();-        r = r->tail();-        // fetch the next headers-        l_h = NULL; r_h = NULL;-        if (l) l_h = l->head();-        if (r) r_h = r->head();-      }-      // one side is null-      else if (!r_h) return true;-      else if (!l_h) return false;-      // heads ok and equal-      else if (*l_h == *r_h)-      {-        // check combinator after heads-        if (l->combinator() != r->combinator())-        { return l->combinator() < r->combinator(); }-        // advance to next tails-        l = l->tail();-        r = r->tail();-        // fetch the next headers-        l_h = NULL; r_h = NULL;-        if (l) l_h = l->head();-        if (r) r_h = r->head();-      }-      // heads are not equal-      else return *l_h < *r_h;-    }-  }--  bool Complex_Selector::operator== (const Complex_Selector& rhs) const-  {-    // const iterators for tails-    Complex_Selector_Ptr_Const l = this;-    Complex_Selector_Ptr_Const r = &rhs;-    Compound_Selector_Ptr l_h = NULL;-    Compound_Selector_Ptr r_h = NULL;-    if (l) l_h = l->head();-    if (r) r_h = r->head();-    // process all tails-    while (true)-    {-      #ifdef DEBUG-      // skip empty ancestor first-      if (l && l->is_empty_ancestor())-      {-        l_h = NULL;-        l = l->tail();-        if (l) l_h = l->head();-        continue;-      }-      // skip empty ancestor first-      if (r && r->is_empty_ancestor())-      {-        r_h = NULL;-        r = r->tail();-        if (r) r_h = r->head();-        continue;-      }-      #endif-      // check the pointers-      if (!r) return !l;-      if (!l) return !r;-      // both are null-      if (!l_h && !r_h)-      {-        // check combinator after heads-        if (l->combinator() != r->combinator())-        { return l->combinator() < r->combinator(); }-        // advance to next tails-        l = l->tail();-        r = r->tail();-        // fetch the next heads-        l_h = NULL; r_h = NULL;-        if (l) l_h = l->head();-        if (r) r_h = r->head();-      }-      // equals if other head is empty-      else if ((!l_h && !r_h) ||-               (!l_h && r_h->empty()) ||-               (!r_h && l_h->empty()) ||-               (l_h && r_h && *l_h == *r_h))-      {-        // check combinator after heads-        if (l->combinator() != r->combinator())-        { return l->combinator() == r->combinator(); }-        // advance to next tails-        l = l->tail();-        r = r->tail();-        // fetch the next heads-        l_h = NULL; r_h = NULL;-        if (l) l_h = l->head();-        if (r) r_h = r->head();-      }-      // abort-      else break;-    }-    // unreachable-    return false;-  }--  Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs)-  {-    if (empty()) return rhs;-    Compound_Selector_Obj unified = SASS_MEMORY_COPY(rhs);-    for (size_t i = 0, L = length(); i < L; ++i)-    {-      if (unified.isNull()) break;-      unified = at(i)->unify_with(unified);-    }-    return unified.detach();-  }--  bool Complex_Selector::operator== (const Selector& rhs) const-  {-    if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;-    if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;-    if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;-    if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;-    throw std::runtime_error("invalid selector base classes to compare");-  }---  bool Complex_Selector::operator< (const Selector& rhs) const-  {-    if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;-    if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;-    if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;-    if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;-    throw std::runtime_error("invalid selector base classes to compare");-  }--  bool Compound_Selector::operator== (const Selector& rhs) const-  {-    if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;-    if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;-    if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;-    if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;-    throw std::runtime_error("invalid selector base classes to compare");-  }--  bool Compound_Selector::operator< (const Selector& rhs) const-  {-    if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;-    if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;-    if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;-    if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;-    throw std::runtime_error("invalid selector base classes to compare");-  }--  bool Selector_Schema::operator== (const Selector& rhs) const-  {-    if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;-    if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;-    if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;-    if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;-    throw std::runtime_error("invalid selector base classes to compare");-  }--  bool Selector_Schema::operator< (const Selector& rhs) const-  {-    if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;-    if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;-    if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;-    if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;-    throw std::runtime_error("invalid selector base classes to compare");-  }--  bool Simple_Selector::operator== (const Selector& rhs) const-  {-    if (Simple_Selector_Ptr_Const sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;-    return false;-  }--  bool Simple_Selector::operator< (const Selector& rhs) const-  {-    if (Simple_Selector_Ptr_Const sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;-    return false;-  }--  bool Simple_Selector::operator== (const Simple_Selector& rhs) const-  {-    // solve the double dispatch problem by using RTTI information via dynamic cast-    if (const Pseudo_Selector* lhs = Cast<Pseudo_Selector>(this)) {return *lhs == rhs; }-    else if (const Wrapped_Selector* lhs = Cast<Wrapped_Selector>(this)) {return *lhs == rhs; }-    else if (const Element_Selector* lhs = Cast<Element_Selector>(this)) {return *lhs == rhs; }-    else if (const Attribute_Selector* lhs = Cast<Attribute_Selector>(this)) {return *lhs == rhs; }-    else if (name_ == rhs.name_)-    { return is_ns_eq(rhs); }-    else return false;-  }--  bool Simple_Selector::operator< (const Simple_Selector& rhs) const-  {-    // solve the double dispatch problem by using RTTI information via dynamic cast-    if (const Pseudo_Selector* lhs = Cast<Pseudo_Selector>(this)) {return *lhs < rhs; }-    else if (const Wrapped_Selector* lhs = Cast<Wrapped_Selector>(this)) {return *lhs < rhs; }-    else if (const Element_Selector* lhs = Cast<Element_Selector>(this)) {return *lhs < rhs; }-    else if (const Attribute_Selector* lhs = Cast<Attribute_Selector>(this)) {return *lhs < rhs; }-    if (is_ns_eq(rhs))-    { return name_ < rhs.name_; }-    return ns_ < rhs.ns_;-  }--  bool Selector_List::operator== (const Selector& rhs) const-  {-    // solve the double dispatch problem by using RTTI information via dynamic cast-    if (Selector_List_Ptr_Const sl = Cast<Selector_List>(&rhs)) { return *this == *sl; }-    else if (Complex_Selector_Ptr_Const cpx = Cast<Complex_Selector>(&rhs)) { return *this == *cpx; }-    else if (Compound_Selector_Ptr_Const cpd = Cast<Compound_Selector>(&rhs)) { return *this == *cpd; }-    // no compare method-    return this == &rhs;-  }--  // Selector lists can be compared to comma lists-  bool Selector_List::operator== (const Expression& rhs) const-  {-    // solve the double dispatch problem by using RTTI information via dynamic cast-    if (List_Ptr_Const ls = Cast<List>(&rhs)) { return *ls == *this; }-    if (Selector_Ptr_Const ls = Cast<Selector>(&rhs)) { return *this == *ls; }-    // compare invalid (maybe we should error?)-    return false;-  }--  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 and sort them-    std::vector<Complex_Selector_Obj> l_lst = this->elements();-    std::vector<Complex_Selector_Obj> 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;-      // the access the vector items-      Complex_Selector_Obj l = l_lst[i];-      Complex_Selector_Obj r = r_lst[n];-      // skip nulls-      if (!l) ++i;-      else if (!r) ++n;-      // do the check-      else if (*l != *r)-      { return false; }-      // advance-      ++i; ++n;-    }-    // there is no break?!-  }--  bool Selector_List::operator< (const Selector& rhs) const-  {-    if (Selector_List_Ptr_Const sp = Cast<Selector_List>(&rhs)) return *this < *sp;-    return false;-  }--  bool Selector_List::operator< (const Selector_List& rhs) const-  {-    size_t l = rhs.length();-    if (length() < l) l = length();-    for (size_t i = 0; i < l; i ++) {-      if (*at(i) < *rhs.at(i)) return true;-    }-    return false;-  }--  Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs)-  {-    for (size_t i = 0, L = rhs->length(); i < L; ++i)-    { if (to_string() == rhs->at(i)->to_string()) return rhs; }--    // check for pseudo elements because they are always last-    size_t i, L;-    bool found = false;-    if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector) || typeid(*this) == typeid(Attribute_Selector))-    {-      for (i = 0, L = rhs->length(); i < L; ++i)-      {-        if ((Cast<Pseudo_Selector>((*rhs)[i]) || Cast<Wrapped_Selector>((*rhs)[i]) || Cast<Attribute_Selector>((*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element())-        { found = true; break; }-      }-    }-    else-    {-      for (i = 0, L = rhs->length(); i < L; ++i)-      {-        if (Cast<Pseudo_Selector>((*rhs)[i]) || Cast<Wrapped_Selector>((*rhs)[i]) || Cast<Attribute_Selector>((*rhs)[i]))-        { found = true; break; }-      }-    }-    if (!found)-    {-      rhs->append(this);-    } else {-      rhs->elements().insert(rhs->elements().begin() + i, this);-    }-    return rhs;-  }--  Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs)-  {-    // check if ns can be extended-    // true for no ns or universal-    if (has_universal_ns())-    {-      // but dont extend with universal-      // true for valid ns and universal-      if (!rhs->is_universal_ns())-      {-        // overwrite the name if star is given as name-        if (this->name() == "*") { this->name(rhs->name()); }-        // now overwrite the namespace name and flag-        this->ns(rhs->ns()); this->has_ns(rhs->has_ns());-        // return copy-        return this;-      }-    }-    // namespace may changed, check the name now-    // overwrite star (but not with another star)-    if (name() == "*" && rhs->name() != "*")-    {-      // simply set the new name-      this->name(rhs->name());-      // return copy-      return this;-    }-    // return original-    return this;-  }--  Compound_Selector_Ptr Element_Selector::unify_with(Compound_Selector_Ptr rhs)-  {-    // TODO: handle namespaces--    // if the rhs is empty, just return a copy of this-    if (rhs->length() == 0) {-      rhs->append(this);-      return rhs;-    }--    Simple_Selector_Ptr rhs_0 = rhs->at(0);-    // otherwise, this is a tag name-    if (name() == "*")-    {-      if (typeid(*rhs_0) == typeid(Element_Selector))-      {-        // if rhs is universal, just return this tagname + rhs's qualifiers-        Element_Selector_Ptr ts = Cast<Element_Selector>(rhs_0);-        rhs->at(0) = this->unify_with(ts);-        return rhs;-      }-      else if (Cast<Class_Selector>(rhs_0) || Cast<Id_Selector>(rhs_0)) {-        // qualifier is `.class`, so we can prefix with `ns|*.class`-        if (has_ns() && !rhs_0->has_ns()) {-          if (ns() != "*") rhs->elements().insert(rhs->begin(), this);-        }-        return rhs;-      }---      return rhs;-    }--    if (typeid(*rhs_0) == typeid(Element_Selector))-    {-      // if rhs is universal, just return this tagname + rhs's qualifiers-      if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0;-      // otherwise create new compound and unify first simple selector-      rhs->at(0) = this->unify_with(rhs_0);-      return rhs;--    }-    // else it's a tag name and a bunch of qualifiers -- just append them-    if (name() != "*") rhs->elements().insert(rhs->begin(), this);-    return rhs;-  }--  Compound_Selector_Ptr Class_Selector::unify_with(Compound_Selector_Ptr rhs)-  {-    rhs->has_line_break(has_line_break());-    return Simple_Selector::unify_with(rhs);-  }--  Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs)-  {-    for (size_t i = 0, L = rhs->length(); i < L; ++i)-    {-      if (Id_Selector_Ptr sel = Cast<Id_Selector>(rhs->at(i))) {-        if (sel->name() != name()) return 0;-      }-    }-    rhs->has_line_break(has_line_break());-    return Simple_Selector::unify_with(rhs);-  }--  Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs)-  {-    if (is_pseudo_element())-    {-      for (size_t i = 0, L = rhs->length(); i < L; ++i)-      {-        if (Pseudo_Selector_Ptr sel = Cast<Pseudo_Selector>(rhs->at(i))) {-          if (sel->is_pseudo_element() && sel->name() != name()) return 0;-        }-      }-    }-    return Simple_Selector::unify_with(rhs);-  }--  bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const-  {-    if (is_ns_eq(rhs)) {-      if (name() == rhs.name()) {-        if (matcher() == rhs.matcher()) {-          bool no_lhs_val = value().isNull();-          bool no_rhs_val = rhs.value().isNull();-          if (no_lhs_val && no_rhs_val) return false; // equal-          else if (no_lhs_val) return true; // lhs is null-          else if (no_rhs_val) return false; // rhs is null-          return *value() < *rhs.value(); // both are given-        } else { return matcher() < rhs.matcher(); }-      } else { return name() < rhs.name(); }-    } else { return ns() < rhs.ns(); }-  }--  bool Attribute_Selector::operator< (const Simple_Selector& rhs) const-  {-    if (Attribute_Selector_Ptr_Const w = Cast<Attribute_Selector>(&rhs))-    {-      return *this < *w;-    }-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const-  {-    // get optional value state-    bool no_lhs_val = value().isNull();-    bool no_rhs_val = rhs.value().isNull();-    // both are null, therefore equal-    if (no_lhs_val && no_rhs_val) {-      return (name() == rhs.name())-        && (matcher() == rhs.matcher())-        && (is_ns_eq(rhs));-    }-    // both are defined, evaluate-    if (no_lhs_val == no_rhs_val) {-      return (name() == rhs.name())-        && (matcher() == rhs.matcher())-        && (is_ns_eq(rhs))-        && (*value() == *rhs.value());-    }-    // not equal-    return false;--  }--  bool Attribute_Selector::operator== (const Simple_Selector& rhs) const-  {-    if (Attribute_Selector_Ptr_Const w = Cast<Attribute_Selector>(&rhs))-    {-      return is_ns_eq(rhs) &&-             name() == rhs.name() &&-             *this == *w;-    }-    return false;-  }--  bool Element_Selector::operator< (const Element_Selector& rhs) const-  {-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Element_Selector::operator< (const Simple_Selector& rhs) const-  {-    if (Element_Selector_Ptr_Const w = Cast<Element_Selector>(&rhs))-    {-      return *this < *w;-    }-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Element_Selector::operator== (const Element_Selector& rhs) const-  {-    return is_ns_eq(rhs) &&-           name() == rhs.name();-  }--  bool Element_Selector::operator== (const Simple_Selector& rhs) const-  {-    if (Element_Selector_Ptr_Const w = Cast<Element_Selector>(&rhs))-    {-      return is_ns_eq(rhs) &&-             name() == rhs.name() &&-             *this == *w;-    }-    return false;-  }--  bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const-  {-    if (is_ns_eq(rhs) && name() == rhs.name())-    {-      String_Obj lhs_ex = expression();-      String_Obj rhs_ex = rhs.expression();-      if (rhs_ex && lhs_ex) return *lhs_ex == *rhs_ex;-      else return lhs_ex.ptr() == rhs_ex.ptr();-    }-    else return false;-  }--  bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const-  {-    if (Pseudo_Selector_Ptr_Const w = Cast<Pseudo_Selector>(&rhs))-    {-      return *this == *w;-    }-    return is_ns_eq(rhs) &&-           name() == rhs.name();-  }--  bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const-  {-    if (is_ns_eq(rhs) && name() == rhs.name())-    {-      String_Obj lhs_ex = expression();-      String_Obj rhs_ex = rhs.expression();-      if (rhs_ex && lhs_ex) return *lhs_ex < *rhs_ex;-      else return lhs_ex.ptr() < rhs_ex.ptr();-    }-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const-  {-    if (Pseudo_Selector_Ptr_Const w = Cast<Pseudo_Selector>(&rhs))-    {-      return *this < *w;-    }-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const-  {-    if (is_ns_eq(rhs) && name() == rhs.name())-    { return *(selector()) == *(rhs.selector()); }-    else return false;-  }--  bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const-  {-    if (Wrapped_Selector_Ptr_Const w = Cast<Wrapped_Selector>(&rhs))-    {-      return *this == *w;-    }-    return is_ns_eq(rhs) &&-           name() == rhs.name();-  }--  bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const-  {-    if (is_ns_eq(rhs) && name() == rhs.name())-    { return *(selector()) < *(rhs.selector()); }-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const-  {-    if (Wrapped_Selector_Ptr_Const w = Cast<Wrapped_Selector>(&rhs))-    {-      return *this < *w;-    }-    if (is_ns_eq(rhs))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Obj sub)-  {-    if (this->name() != sub->name()) return false;-    if (this->name() == ":current") return false;-    if (Selector_List_Obj rhs_list = Cast<Selector_List>(sub->selector())) {-      if (Selector_List_Obj lhs_list = Cast<Selector_List>(selector())) {-        return lhs_list->is_superselector_of(rhs_list);-      }-    }-    coreError("is_superselector expected a Selector_List", sub->pstate());-    return false;-  }--  bool Compound_Selector::is_superselector_of(Selector_List_Obj rhs, std::string wrapped)-  {-    for (Complex_Selector_Obj item : rhs->elements()) {-      if (is_superselector_of(item, wrapped)) return true;-    }-    return false;-  }--  bool Compound_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapped)-  {-    if (rhs->head()) return is_superselector_of(rhs->head(), wrapped);-    return false;-  }--  bool Compound_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)-  {-    Compound_Selector_Ptr lhs = this;-    Simple_Selector_Ptr lbase = lhs->base();-    Simple_Selector_Ptr rbase = rhs->base();--    // Check if pseudo-elements are the same between the selectors--    std::set<std::string> lpsuedoset, rpsuedoset;-    for (size_t i = 0, L = length(); i < L; ++i)-    {-      if ((*this)[i]->is_pseudo_element()) {-        std::string pseudo((*this)[i]->to_string());-        pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving-        lpsuedoset.insert(pseudo);-      }-    }-    for (size_t i = 0, L = rhs->length(); i < L; ++i)-    {-      if ((*rhs)[i]->is_pseudo_element()) {-        std::string pseudo((*rhs)[i]->to_string());-        pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving-        rpsuedoset.insert(pseudo);-      }-    }-    if (lpsuedoset != rpsuedoset) {-      return false;-    }--    // would like to replace this without stringification-    // https://github.com/sass/sass/issues/2229-    // SimpleSelectorSet lset, rset;-    std::set<std::string> lset, rset;--    if (lbase && rbase)-    {-      if (lbase->to_string() == rbase->to_string()) {-        for (size_t i = 1, L = length(); i < L; ++i)-        { lset.insert((*this)[i]->to_string()); }-        for (size_t i = 1, L = rhs->length(); i < L; ++i)-        { rset.insert((*rhs)[i]->to_string()); }-        return includes(rset.begin(), rset.end(), lset.begin(), lset.end());-      }-      return false;-    }--    for (size_t i = 0, iL = length(); i < iL; ++i)-    {-      Selector_Obj wlhs = (*this)[i];-      // very special case for wrapped matches selector-      if (Wrapped_Selector_Obj wrapped = Cast<Wrapped_Selector>(wlhs)) {-        if (wrapped->name() == ":not") {-          if (Selector_List_Obj not_list = Cast<Selector_List>(wrapped->selector())) {-            if (not_list->is_superselector_of(rhs, wrapped->name())) return false;-          } else {-            throw std::runtime_error("wrapped not selector is not a list");-          }-        }-        if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {-          wlhs = wrapped->selector();-          if (Selector_List_Obj list = Cast<Selector_List>(wrapped->selector())) {-            if (Compound_Selector_Obj comp = Cast<Compound_Selector>(rhs)) {-              if (!wrapping.empty() && wrapping != wrapped->name()) return false;-              if (wrapping.empty() || wrapping != wrapped->name()) {;-                if (list->is_superselector_of(comp, wrapped->name())) return true;-              }-            }-          }-        }-        Simple_Selector_Ptr rhs_sel = NULL;-        if (rhs->elements().size() > i) rhs_sel = (*rhs)[i];-        if (Wrapped_Selector_Ptr wrapped_r = Cast<Wrapped_Selector>(rhs_sel)) {-          if (wrapped->name() == wrapped_r->name()) {-          if (wrapped->is_superselector_of(wrapped_r)) {-             continue;-          }}-        }-      }-      // match from here on as strings-      lset.insert(wlhs->to_string());-    }--    for (size_t n = 0, nL = rhs->length(); n < nL; ++n)-    {-      Selector_Obj r = (*rhs)[n];-      if (Wrapped_Selector_Obj wrapped = Cast<Wrapped_Selector>(r)) {-        if (wrapped->name() == ":not") {-          if (Selector_List_Obj ls = Cast<Selector_List>(wrapped->selector())) {-            ls->remove_parent_selectors();-            if (is_superselector_of(ls, wrapped->name())) return false;-          }-        }-        if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {-          if (!wrapping.empty()) {-            if (wrapping != wrapped->name()) return false;-          }-          if (Selector_List_Obj ls = Cast<Selector_List>(wrapped->selector())) {-            ls->remove_parent_selectors();-            return (is_superselector_of(ls, wrapped->name()));-          }-        }-      }-      rset.insert(r->to_string());-    }--    //for (auto l : lset) { cerr << "l: " << l << endl; }-    //for (auto r : rset) { cerr << "r: " << r << endl; }--    if (lset.empty()) return true;-    // return true if rset contains all the elements of lset-    return includes(rset.begin(), rset.end(), lset.begin(), lset.end());--  }--  // create complex selector (ancestor of) from compound selector-  Complex_Selector_Obj Compound_Selector::to_complex()-  {-    // create an intermediate complex selector-    return SASS_MEMORY_NEW(Complex_Selector,-                           pstate(),-                           Complex_Selector::ANCESTOR_OF,-                           this,-                           0);-  }--  Selector_List_Ptr Complex_Selector::unify_with(Complex_Selector_Ptr other)-  {--    // get last tails (on the right side)-    Complex_Selector_Obj l_last = this->last();-    Complex_Selector_Obj r_last = other->last();--    // check valid pointers (assertion)-    SASS_ASSERT(l_last, "lhs is null");-    SASS_ASSERT(r_last, "rhs is null");--    // Not sure about this check, but closest way I could check-    // was to see if this is a ruby 'SimpleSequence' equivalent.-    // It seems to do the job correctly as some specs react to this-    if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0;-    if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0;--    // get the headers for the last tails-    Compound_Selector_Obj l_last_head = l_last->head();-    Compound_Selector_Obj r_last_head = r_last->head();--    // check valid head pointers (assertion)-    SASS_ASSERT(l_last_head, "lhs head is null");-    SASS_ASSERT(r_last_head, "rhs head is null");--    // get the unification of the last compound selectors-    Compound_Selector_Obj unified = r_last_head->unify_with(l_last_head);--    // abort if we could not unify heads-    if (unified == 0) return 0;--    // check for universal (star: `*`) selector-    bool is_universal = l_last_head->is_universal() ||-                        r_last_head->is_universal();--    if (is_universal)-    {-      // move the head-      l_last->head(0);-      r_last->head(unified);-    }--    // create nodes from both selectors-    Node lhsNode = complexSelectorToNode(this);-    Node rhsNode = complexSelectorToNode(other);--    // overwrite universal base-    if (!is_universal)-    {-      // create some temporaries to convert to node-      Complex_Selector_Obj fake = unified->to_complex();-      Node unified_node = complexSelectorToNode(fake);-      // add to permutate the list?-      rhsNode.plus(unified_node);-    }--    // do some magic we inherit from node and extend-    Node node = subweave(lhsNode, rhsNode);-    Selector_List_Obj result = SASS_MEMORY_NEW(Selector_List, pstate());-    NodeDequePtr col = node.collection(); // move from collection to list-    for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++)-    { result->append(nodeToComplexSelector(Node::naiveTrim(*it))); }--    // only return if list has some entries-    return result->length() ? result.detach() : 0;--  }--  bool Compound_Selector::operator== (const Compound_Selector& rhs) const-  {-    // for array access-    size_t i = 0, n = 0;-    size_t iL = length();-    size_t nL = rhs.length();-    // create temporary vectors and sort them-    std::vector<Simple_Selector_Obj> l_lst = this->elements();-    std::vector<Simple_Selector_Obj> 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;-      // the access the vector items-      Simple_Selector_Obj l = l_lst[i];-      Simple_Selector_Obj r = r_lst[n];-      // skip nulls-      if (!l) ++i;-      if (!r) ++n;-      // do the check now-      else if (*l != *r)-      { return false; }-      // advance now-      ++i; ++n;-    }-    // there is no break?!-  }--  bool Complex_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)-  {-    return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);-  }--  bool Complex_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapping)-  {-    Complex_Selector_Ptr lhs = this;-    // check for selectors with leading or trailing combinators-    if (!lhs->head() || !rhs->head())-    { return false; }-    Complex_Selector_Obj l_innermost = lhs->innermost();-    if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)-    { return false; }-    Complex_Selector_Obj r_innermost = rhs->innermost();-    if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF)-    { return false; }-    // more complex (i.e., longer) selectors are always more specific-    size_t l_len = lhs->length(), r_len = rhs->length();-    if (l_len > r_len)-    { return false; }--    if (l_len == 1)-    { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); }--    // we have to look one tail deeper, since we cary the-    // combinator around for it (which is important here)-    if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {-      Complex_Selector_Obj lhs_tail = lhs->tail();-      Complex_Selector_Obj rhs_tail = rhs->tail();-      if (lhs_tail->combinator() != rhs_tail->combinator()) return false;-      if (lhs_tail->head() && !rhs_tail->head()) return false;-      if (!lhs_tail->head() && rhs_tail->head()) return false;-      if (lhs_tail->head() && rhs_tail->head()) {-        if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;-      }-    }--    bool found = false;-    Complex_Selector_Obj marker = rhs;-    for (size_t i = 0, L = rhs->length(); i < L; ++i) {-      if (i == L-1)-      { return false; }-      if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping))-      { found = true; break; }-      marker = marker->tail();-    }-    if (!found)-    { return false; }--    /*-      Hmm, I hope I have the logic right:--      if lhs has a combinator:-        if !(marker has a combinator) return false-        if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false-        return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)-      else if marker has a combinator:-        if !(marker.combinator == ">") return false-        return lhs.tail.is_superselector_of(marker.tail)-      else-        return lhs.tail.is_superselector_of(marker.tail)-    */-    if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)-    {-      if (marker->combinator() == Complex_Selector::ANCESTOR_OF)-      { return false; }-      if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))-      { return false; }-      return lhs->tail()->is_superselector_of(marker->tail());-    }-    else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)-    {-      if (marker->combinator() != Complex_Selector::PARENT_OF)-      { return false; }-      return lhs->tail()->is_superselector_of(marker->tail());-    }-    return lhs->tail()->is_superselector_of(marker->tail());-  }--  size_t Complex_Selector::length() const-  {-    // TODO: make this iterative-    if (!tail()) return 1;-    return 1 + tail()->length();-  }--  // append another complex selector at the end-  // check if we need to append some headers-  // then we need to check for the combinator-  // only then we can safely set the new tail-  void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces)-  {--    Complex_Selector_Obj t = ss->tail();-    Combinator c = ss->combinator();-    String_Obj r = ss->reference();-    Compound_Selector_Obj h = ss->head();--    if (ss->has_line_feed()) has_line_feed(true);-    if (ss->has_line_break()) has_line_break(true);--    // append old headers-    if (h && h->length()) {-      if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {-        traces.push_back(Backtrace(pstate()));-        throw Exception::InvalidParent(this, traces, ss);-      } else if (last()->head_ && last()->head_->length()) {-        Compound_Selector_Obj rh = last()->head();-        size_t i;-        size_t L = h->length();-        if (Cast<Element_Selector>(h->first())) {-          if (Class_Selector_Ptr cs = Cast<Class_Selector>(rh->last())) {-            Class_Selector_Ptr sqs = SASS_MEMORY_COPY(cs);-            sqs->name(sqs->name() + (*h)[0]->name());-            sqs->pstate((*h)[0]->pstate());-            (*rh)[rh->length()-1] = sqs;-            rh->pstate(h->pstate());-            for (i = 1; i < L; ++i) rh->append((*h)[i]);-          } else if (Id_Selector_Ptr is = Cast<Id_Selector>(rh->last())) {-            Id_Selector_Ptr sqs = SASS_MEMORY_COPY(is);-            sqs->name(sqs->name() + (*h)[0]->name());-            sqs->pstate((*h)[0]->pstate());-            (*rh)[rh->length()-1] = sqs;-            rh->pstate(h->pstate());-            for (i = 1; i < L; ++i) rh->append((*h)[i]);-          } else if (Element_Selector_Ptr ts = Cast<Element_Selector>(rh->last())) {-            Element_Selector_Ptr tss = SASS_MEMORY_COPY(ts);-            tss->name(tss->name() + (*h)[0]->name());-            tss->pstate((*h)[0]->pstate());-            (*rh)[rh->length()-1] = tss;-            rh->pstate(h->pstate());-            for (i = 1; i < L; ++i) rh->append((*h)[i]);-          } else if (Placeholder_Selector_Ptr ps = Cast<Placeholder_Selector>(rh->last())) {-            Placeholder_Selector_Ptr pss = SASS_MEMORY_COPY(ps);-            pss->name(pss->name() + (*h)[0]->name());-            pss->pstate((*h)[0]->pstate());-            (*rh)[rh->length()-1] = pss;-            rh->pstate(h->pstate());-            for (i = 1; i < L; ++i) rh->append((*h)[i]);-          } else {-            last()->head_->concat(h);-          }-        } else {-          last()->head_->concat(h);-        }-      } else if (last()->head_) {-        last()->head_->concat(h);-      }-    } else {-      // std::cerr << "has no or empty head\n";-    }--    if (last()) {-      if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {-        Complex_Selector_Ptr inter = SASS_MEMORY_NEW(Complex_Selector, pstate());-        inter->reference(r);-        inter->combinator(c);-        inter->tail(t);-        last()->tail(inter);-      } else {-        if (last()->combinator() == ANCESTOR_OF) {-          last()->combinator(c);-          last()->reference(r);-        }-        last()->tail(t);-      }-    }--  }--  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(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)-  {-    if (!this->has_parent_ref()) return this;-    Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());-    Selector_List_Ptr ps = pstack.back();-    for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {-      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(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)-  {-    Complex_Selector_Obj tail = this->tail();-    Compound_Selector_Obj head = this->head();-    Selector_List_Ptr parents = pstack.back();--    if (!this->has_real_parent_ref() && !implicit_parent) {-      Selector_List_Ptr retval = SASS_MEMORY_NEW(Selector_List, pstate());-      retval->append(this);-      return retval;-    }--    // first resolve_parent_refs the tail (which may return an expanded list)-    Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0;--    if (head && head->length() > 0) {--      Selector_List_Obj retval;-      // we have a parent selector in a simple compound list-      // mix parent complex selector into the compound list-      if (Cast<Parent_Selector>((*head)[0])) {-        retval = SASS_MEMORY_NEW(Selector_List, pstate());--        // it turns out that real parent references reach-        // across @at-root rules, which comes unexpected-        if (parents == NULL && head->has_real_parent_ref()) {-          int i = pstack.size() - 1;-          while (!parents && i > -1) {-            parents = pstack.at(i--);-          }-        }--        if (parents && parents->length()) {-          if (tails && tails->length() > 0) {-            for (size_t n = 0, nL = tails->length(); n < nL; ++n) {-              for (size_t i = 0, iL = parents->length(); i < iL; ++i) {-                Complex_Selector_Obj t = (*tails)[n];-                Complex_Selector_Obj parent = (*parents)[i];-                Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);-                Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);-                ss->tail(t ? SASS_MEMORY_CLONE(t) : NULL);-                Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);-                // remove parent selector from sequence-                if (h->length()) {-                  h->erase(h->begin());-                  ss->head(h);-                } else {-                  ss->head(NULL);-                }-                // adjust for parent selector (1 char)-                // if (h->length()) {-                //   ParserState state(h->at(0)->pstate());-                //   state.offset.column += 1;-                //   state.column -= 1;-                //   (*h)[0]->pstate(state);-                // }-                // keep old parser state-                s->pstate(pstate());-                // append new tail-                s->append(ss, traces);-                retval->append(s);-              }-            }-          }-          // have no tails but parents-          // loop above is inside out-          else {-            for (size_t i = 0, iL = parents->length(); i < iL; ++i) {-              Complex_Selector_Obj parent = (*parents)[i];-              Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);-              Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);-              // this is only if valid if the parent has no trailing op-              // otherwise we cannot append more simple selectors to head-              if (parent->last()->combinator() != ANCESTOR_OF) {-                traces.push_back(Backtrace(pstate()));-                throw Exception::InvalidParent(parent, traces, ss);-              }-              ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL);-              Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);-              // remove parent selector from sequence-              if (h->length()) {-                h->erase(h->begin());-                ss->head(h);-              } else {-                ss->head(NULL);-              }-              // \/ IMO ruby sass bug \/-              ss->has_line_feed(false);-              // adjust for parent selector (1 char)-              // if (h->length()) {-              //   ParserState state(h->at(0)->pstate());-              //   state.offset.column += 1;-              //   state.column -= 1;-              //   (*h)[0]->pstate(state);-              // }-              // keep old parser state-              s->pstate(pstate());-              // append new tail-              s->append(ss, traces);-              retval->append(s);-            }-          }-        }-        // have no parent but some tails-        else {-          if (tails && tails->length() > 0) {-            for (size_t n = 0, nL = tails->length(); n < nL; ++n) {-              Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);-              cpy->tail(SASS_MEMORY_CLONE(tails->at(n)));-              cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));-              for (size_t i = 1, L = this->head()->length(); i < L; ++i)-                cpy->head()->append((*this->head())[i]);-              if (!cpy->head()->length()) cpy->head(0);-              retval->append(cpy->skip_empty_reference());-            }-          }-          // have no parent nor tails-          else {-            Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);-            cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));-            for (size_t i = 1, L = this->head()->length(); i < L; ++i)-              cpy->head()->append((*this->head())[i]);-            if (!cpy->head()->length()) cpy->head(0);-            retval->append(cpy->skip_empty_reference());-          }-        }-      }-      // no parent selector in head-      else {-        retval = this->tails(tails);-      }--      for (Simple_Selector_Obj ss : head->elements()) {-        if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {-          if (Selector_List_Ptr sl = Cast<Selector_List>(ws->selector())) {-            if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent));-          }-        }-      }--      return retval.detach();--    }-    // has no head-    return this->tails(tails);-  }--  Selector_List_Ptr Complex_Selector::tails(Selector_List_Ptr tails)-  {-    Selector_List_Ptr rv = SASS_MEMORY_NEW(Selector_List, pstate_);-    if (tails && tails->length()) {-      for (size_t i = 0, iL = tails->length(); i < iL; ++i) {-        Complex_Selector_Obj pr = SASS_MEMORY_CLONE(this);-        pr->tail(tails->at(i));-        rv->append(pr);-      }-    }-    else {-      rv->append(this);-    }-    return rv;-  }--  // return the last tail that is defined-  Complex_Selector_Obj Complex_Selector::first()-  {-    // declare variables used in loop-    Complex_Selector_Obj cur = this;-    Compound_Selector_Obj head;-    // processing loop-    while (cur)-    {-      // get the head-      head = cur->head_;-      // abort (and return) if it is not a parent selector-      if (!head || head->length() != 1 || !Cast<Parent_Selector>((*head)[0])) {-        break;-      }-      // advance to next-      cur = cur->tail_;-    }-    // result-    return cur;-  }--  // return the last tail that is defined-  Complex_Selector_Obj Complex_Selector::last()-  {-    Complex_Selector_Ptr cur = this;-    Complex_Selector_Ptr nxt = cur;-    // loop until last-    while (nxt) {-      cur = nxt;-      nxt = cur->tail();-    }-    return cur;-  }--  Complex_Selector::Combinator Complex_Selector::clear_innermost()-  {-    Combinator c;-    if (!tail() || tail()->tail() == 0)-    { c = combinator(); combinator(ANCESTOR_OF); tail(0); }-    else-    { c = tail()->clear_innermost(); }-    return c;-  }--  void Complex_Selector::set_innermost(Complex_Selector_Obj val, Combinator c)-  {-    if (!tail())-    { tail(val); combinator(c); }-    else-    { tail()->set_innermost(val, c); }-  }--  void Complex_Selector::cloneChildren()-  {-    if (head()) head(SASS_MEMORY_CLONE(head()));-    if (tail()) tail(SASS_MEMORY_CLONE(tail()));-  }--  void Compound_Selector::cloneChildren()-  {-    for (size_t i = 0, l = length(); i < l; i++) {-      at(i) = SASS_MEMORY_CLONE(at(i));-    }-  }--  void Selector_List::cloneChildren()-  {-    for (size_t i = 0, l = length(); i < l; i++) {-      at(i) = SASS_MEMORY_CLONE(at(i));-    }-  }--  void Wrapped_Selector::cloneChildren()-  {-    selector(SASS_MEMORY_CLONE(selector()));-  }--  // remove parent selector references-  // basically unwraps parsed selectors-  void Selector_List::remove_parent_selectors()-  {-    // Check every rhs selector against left hand list-    for(size_t i = 0, L = length(); i < L; ++i) {-      if (!(*this)[i]->head()) continue;-      if ((*this)[i]->head()->is_empty_reference()) {-        // simply move to the next tail if we have "no" combinator-        if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) {-          if ((*this)[i]->tail()) {-            if ((*this)[i]->has_line_feed()) {-              (*this)[i]->tail()->has_line_feed(true);-            }-            (*this)[i] = (*this)[i]->tail();-          }-        }-        // otherwise remove the first item from head-        else {-          (*this)[i]->head()->erase((*this)[i]->head()->begin());-        }-      }-    }-  }--  size_t Wrapped_Selector::hash()-  {-    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()) {-      if (s && s->has_parent_ref()) return true;-    }-    return false;-  }--  bool Selector_List::has_real_parent_ref() const-  {-    for (Complex_Selector_Obj s : elements()) {-      if (s && s->has_real_parent_ref()) return true;-    }-    return false;-  }--  bool Selector_Schema::has_parent_ref() const-  {-    if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {-      return schema->length() > 0 && Cast<Parent_Selector>(schema->at(0)) != NULL;-    }-    return false;-  }--  bool Selector_Schema::has_real_parent_ref() const-  {-    if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {-      Parent_Selector_Obj p = Cast<Parent_Selector>(schema->at(0));-      return schema->length() > 0 && p && p->is_real_parent_ref();-    }-    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_Obj sub, std::string wrapping)-  {-    // 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_Obj sub, std::string wrapping)-  {-    // 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 on the right side-  // is a superselector of any one of the left side selectors-  bool Selector_List::is_superselector_of(Compound_Selector_Obj sub, std::string wrapping)-  {-    // Check every lhs selector against right hand-    for(size_t i = 0, L = length(); i < L; ++i) {-      if ((*this)[i]->is_superselector_of(sub, wrapping)) return true;-    }-    return false;-  }--  // it's a superselector if every selector on the right side-  // is a superselector of any one of the left side selectors-  bool Selector_List::is_superselector_of(Complex_Selector_Obj sub, std::string wrapping)-  {-    // Check every lhs selector against right hand-    for(size_t i = 0, L = length(); i < L; ++i) {-      if ((*this)[i]->is_superselector_of(sub)) return true;-    }-    return false;-  }--  Selector_List_Ptr Selector_List::unify_with(Selector_List_Ptr rhs) {-    std::vector<Complex_Selector_Obj> unified_complex_selectors;-    // Unify all of children with RHS's children, storing the results in `unified_complex_selectors`-    for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) {-      Complex_Selector_Obj seq1 = (*this)[lhs_i];-      for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {-        Complex_Selector_Ptr seq2 = rhs->at(rhs_i);--        Selector_List_Obj result = seq1->unify_with(seq2);-        if( result ) {-          for(size_t i = 0, L = result->length(); i < L; ++i) {-            unified_complex_selectors.push_back( (*result)[i] );-          }-        }-      }-    }--    // Creates the final Selector_List by combining all the complex selectors-    Selector_List_Ptr final_result = SASS_MEMORY_NEW(Selector_List, pstate());-    for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {-      final_result->append(*itr);-    }-    return final_result;-  }--  void Selector_List::populate_extends(Selector_List_Obj extendee, Subset_Map& extends)-  {--    Selector_List_Ptr extender = this;-    for (auto complex_sel : extendee->elements()) {-      Complex_Selector_Obj c = complex_sel;---      // Ignore any parent selectors, until we find the first non Selectorerence head-      Compound_Selector_Obj compound_sel = c->head();-      Complex_Selector_Obj pIter = complex_sel;-      while (pIter) {-        Compound_Selector_Obj pHead = pIter->head();-        if (pHead && Cast<Parent_Selector>(pHead->elements()[0]) == NULL) {-          compound_sel = pHead;-          break;-        }--        pIter = pIter->tail();-      }--      if (!pIter->head() || pIter->tail()) {-        coreError("nested selectors may not be extended", c->pstate());-      }--      compound_sel->is_optional(extendee->is_optional());--      for (size_t i = 0, L = extender->length(); i < L; ++i) {-        extends.put(compound_sel, std::make_pair((*extender)[i], compound_sel));-      }-    }-  };--  void Compound_Selector::append(Simple_Selector_Ptr element)-  {-    Vectorized<Simple_Selector_Obj>::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;-      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]);-    }--    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));-    }-  }--  Argument_Obj Arguments::get_rest_argument()-  {-    if (this->has_rest_argument()) {-      for (Argument_Obj arg : this->elements()) {-        if (arg->is_rest_argument()) {-          return arg;-        }-      }-    }-    return NULL;-  }--  Argument_Obj Arguments::get_keyword_argument()-  {-    if (this->has_keyword_argument()) {-      for (Argument_Obj arg : this->elements()) {-        if (arg->is_keyword_argument()) {-          return arg;-        }-      }-    }-    return NULL;-  }--  void Arguments::adjust_after_pushing(Argument_Obj a)-  {-    if (!a->name().empty()) {-      if (has_keyword_argument()) {-        coreError("named arguments must precede variable-length argument", a->pstate());-      }-      has_named_arguments(true);-    }-    else if (a->is_rest_argument()) {-      if (has_rest_argument()) {-        coreError("functions and mixins may only be called with one variable-length argument", a->pstate());-      }-      if (has_keyword_argument_) {-        coreError("only keyword arguments may follow variable arguments", a->pstate());-      }-      has_rest_argument(true);-    }-    else if (a->is_keyword_argument()) {-      if (has_keyword_argument()) {-        coreError("functions and mixins may only be called with one keyword argument", a->pstate());-      }-      has_keyword_argument(true);-    }-    else {-      if (has_rest_argument()) {-        coreError("ordinal arguments must precede variable-length arguments", a->pstate());-      }-      if (has_named_arguments()) {-        coreError("ordinal arguments must precede named arguments", a->pstate());-      }-    }-  }--  bool Ruleset::is_invisible() const {-    if (Selector_List_Ptr sl = Cast<Selector_List>(selector())) {-      for (size_t i = 0, L = sl->length(); i < L; ++i)-        if (!(*sl)[i]->has_placeholder()) 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;-    }-    return true;-  }--  Number::Number(ParserState pstate, double val, std::string u, bool zero)-  : Value(pstate),-    Units(),-    value_(val),-    zero_(zero),-    hash_(0)-  {-    size_t l = 0;-    size_t r;-    if (!u.empty()) {-      bool nominator = true;-      while (true) {-        r = u.find_first_of("*/", l);-        std::string unit(u.substr(l, r == std::string::npos ? r : r - l));-        if (!unit.empty()) {-          if (nominator) numerators.push_back(unit);-          else denominators.push_back(unit);-        }-        if (r == std::string::npos) break;-        // ToDo: should error for multiple slashes-        // if (!nominator && u[r] == '/') error(...)-        if (u[r] == '/')-          nominator = false;-        // strange math parsing?-        // else if (u[r] == '*')-        //  nominator = true;-        l = r + 1;-      }-    }-    concrete_type(NUMBER);-  }--  // cancel out unnecessary units-  void Number::reduce()-  {-    // apply conversion factor-    value_ *= this->Units::reduce();-  }--  void Number::normalize()-  {-    // apply conversion factor-    value_ *= this->Units::normalize();-  }--  bool Custom_Warning::operator== (const Expression& rhs) const-  {-    if (Custom_Warning_Ptr_Const r = Cast<Custom_Warning>(&rhs)) {-      return message() == r->message();-    }-    return false;-  }--  bool Custom_Error::operator== (const Expression& rhs) const-  {-    if (Custom_Error_Ptr_Const r = Cast<Custom_Error>(&rhs)) {-      return message() == r->message();-    }-    return false;-  }--  bool Number::operator== (const Expression& rhs) const-  {-    if (auto rhsnr = Cast<Number>(&rhs)) {-      return *this == *rhsnr;-    }-    return false;-  }--  bool Number::operator== (const Number& rhs) const-  {-    Number l(*this), r(rhs); l.reduce(); r.reduce();-    size_t lhs_units = l.numerators.size() + l.denominators.size();-    size_t rhs_units = r.numerators.size() + r.denominators.size();-    // unitless and only having one unit seems equivalent (will change in future)-    if (!lhs_units || !rhs_units) {-      return NEAR_EQUAL(l.value(), r.value());-    }-    l.normalize(); r.normalize();-    Units &lhs_unit = l, &rhs_unit = r;-    return lhs_unit == rhs_unit &&-      NEAR_EQUAL(l.value(), r.value());-  }--  bool Number::operator< (const Number& rhs) const-  {-    Number l(*this), r(rhs); l.reduce(); r.reduce();-    size_t lhs_units = l.numerators.size() + l.denominators.size();-    size_t rhs_units = r.numerators.size() + r.denominators.size();-    // unitless and only having one unit seems equivalent (will change in future)-    if (!lhs_units || !rhs_units) {-      return l.value() < r.value();-    }-    l.normalize(); r.normalize();-    Units &lhs_unit = l, &rhs_unit = r;-    if (!(lhs_unit == rhs_unit)) {-      /* ToDo: do we always get usefull backtraces? */-      throw Exception::IncompatibleUnits(rhs, *this);-    }-    return lhs_unit < rhs_unit ||-           l.value() < r.value();-  }--  bool String_Quoted::operator== (const Expression& rhs) const-  {-    if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(&rhs)) {-      return (value() == qstr->value());-    } else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(&rhs)) {-      return (value() == cstr->value());-    }-    return false;-  }--  bool String_Constant::is_invisible() const {-    return value_.empty() && quote_mark_ == 0;-  }--  bool String_Constant::operator== (const Expression& rhs) const-  {-    if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(&rhs)) {-      return (value() == qstr->value());-    } else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(&rhs)) {-      return (value() == cstr->value());-    }-    return false;-  }--  bool String_Schema::is_left_interpolant(void) const-  {-    return length() && first()->is_left_interpolant();-  }-  bool String_Schema::is_right_interpolant(void) const-  {-    return length() && last()->is_right_interpolant();-  }--  bool String_Schema::operator== (const Expression& rhs) const-  {-    if (String_Schema_Ptr_Const r = Cast<String_Schema>(&rhs)) {-      if (length() != r->length()) return false;-      for (size_t i = 0, L = length(); i < L; ++i) {-        Expression_Obj rv = (*r)[i];-        Expression_Obj lv = (*this)[i];-        if (!lv || !rv) return false;-        if (!(*lv == *rv)) return false;-      }-      return true;-    }-    return false;-  }--  bool Boolean::operator== (const Expression& rhs) const-  {-    if (Boolean_Ptr_Const r = Cast<Boolean>(&rhs)) {-      return (value() == r->value());-    }-    return false;-  }--  bool Color::operator== (const Expression& rhs) const-  {-    if (Color_Ptr_Const r = Cast<Color>(&rhs)) {-      return r_ == r->r() &&-             g_ == r->g() &&-             b_ == r->b() &&-             a_ == r->a();-    }-    return false;-  }--  bool List::operator== (const Expression& rhs) const-  {-    if (List_Ptr_Const r = Cast<List>(&rhs)) {-      if (length() != r->length()) return false;-      if (separator() != r->separator()) return false;-      if (is_bracketed() != r->is_bracketed()) return false;-      for (size_t i = 0, L = length(); i < L; ++i) {-        Expression_Obj rv = r->at(i);-        Expression_Obj lv = this->at(i);-        if (!lv || !rv) return false;-        if (!(*lv == *rv)) return false;-      }-      return true;-    }-    return false;-  }--  bool Map::operator== (const Expression& rhs) const-  {-    if (Map_Ptr_Const r = Cast<Map>(&rhs)) {-      if (length() != r->length()) return false;-      for (auto key : keys()) {-        Expression_Obj lv = at(key);-        Expression_Obj rv = r->at(key);-        if (!rv || !lv) return false;-        if (!(*lv == *rv)) return false;-      }-      return true;-    }-    return false;-  }--  bool Null::operator== (const Expression& rhs) const-  {-    return rhs.concrete_type() == NULL_VAL;-  }--  bool Function::operator== (const Expression& rhs) const-  {-    if (Function_Ptr_Const r = Cast<Function>(&rhs)) {-      Definition_Ptr_Const d1 = Cast<Definition>(definition());-      Definition_Ptr_Const d2 = Cast<Definition>(r->definition());-      return d1 && d2 && d1 == d2 && is_css() == r->is_css();-    }-    return false;-  }--  size_t List::size() const {-    if (!is_arglist_) return length();-    // arglist expects a list of arguments-    // so we need to break before keywords-    for (size_t i = 0, L = length(); i < L; ++i) {-      Expression_Obj obj = this->at(i);-      if (Argument_Ptr arg = Cast<Argument>(obj)) {-        if (!arg->name().empty()) return i;-      }-    }-    return length();-  }--  Expression_Obj Hashed::at(Expression_Obj k) const-  {-    if (elements_.count(k))-    { return elements_.at(k); }-    else { return NULL; }-  }--  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 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<AST_Node_Ptr>(this)->perform(&i);-    return i.get_buffer();-  }--  const std::string AST_Node::to_string() const-  {-    return to_string({ NESTED, 5 });-  }--  std::string String_Quoted::inspect() const-  {-    return quote(value_, '*');-  }--  std::string String_Constant::inspect() const-  {-    return quote(value_, '*');-  }--  bool Declaration::is_invisible() const-  {-    if (is_custom_property()) return false;--    return !(value_ && value_->concrete_type() != Expression::NULL_VAL);-  }--  //////////////////////////////////////////////////////////////////////////////////////////-  // 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<Argument>(obj)) {-        return arg->value();-      } else {-        return obj;-      }-    } else {-      return obj;-    }-  }--  //////////////////////////////////////////////////////////////////////////////////////////-  // Convert map to (key, value) list.-  //////////////////////////////////////////////////////////////////////////////////////////-  List_Obj Map::to_list(ParserState& pstate) {-    List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA);--    for (auto key : keys()) {-      List_Obj l = SASS_MEMORY_NEW(List, pstate, 2);-      l->append(key);-      l->append(at(key));-      ret->append(l);-    }--    return ret;-  }--  //////////////////////////////////////////////////////////////////////////////////////////-  // Copy implementations-  //////////////////////////////////////////////////////////////////////////////////////////--  #ifdef DEBUG_SHARED_PTR--  #define IMPLEMENT_AST_OPERATORS(klass) \-    klass##_Ptr klass::copy(std::string file, size_t line) const { \-      klass##_Ptr cpy = new klass(this); \-      cpy->trace(file, line); \-      return cpy; \-    } \-    klass##_Ptr klass::clone(std::string file, size_t line) const { \-      klass##_Ptr cpy = copy(file, line); \-      cpy->cloneChildren(); \-      return cpy; \-    } \--  #else--  #define IMPLEMENT_AST_OPERATORS(klass) \-    klass##_Ptr klass::copy() const { \-      return new klass(this); \-    } \-    klass##_Ptr klass::clone() const { \-      klass##_Ptr cpy = copy(); \-      cpy->cloneChildren(); \-      return cpy; \-    } \--  #endif--  IMPLEMENT_AST_OPERATORS(Supports_Operator);-  IMPLEMENT_AST_OPERATORS(Supports_Negation);-  IMPLEMENT_AST_OPERATORS(Compound_Selector);-  IMPLEMENT_AST_OPERATORS(Complex_Selector);-  IMPLEMENT_AST_OPERATORS(Element_Selector);-  IMPLEMENT_AST_OPERATORS(Class_Selector);-  IMPLEMENT_AST_OPERATORS(Id_Selector);-  IMPLEMENT_AST_OPERATORS(Pseudo_Selector);-  IMPLEMENT_AST_OPERATORS(Wrapped_Selector);-  IMPLEMENT_AST_OPERATORS(Selector_List);-  IMPLEMENT_AST_OPERATORS(Ruleset);-  IMPLEMENT_AST_OPERATORS(Media_Block);-  IMPLEMENT_AST_OPERATORS(Custom_Warning);-  IMPLEMENT_AST_OPERATORS(Custom_Error);-  IMPLEMENT_AST_OPERATORS(List);-  IMPLEMENT_AST_OPERATORS(Map);-  IMPLEMENT_AST_OPERATORS(Function);-  IMPLEMENT_AST_OPERATORS(Number);-  IMPLEMENT_AST_OPERATORS(Binary_Expression);-  IMPLEMENT_AST_OPERATORS(String_Schema);-  IMPLEMENT_AST_OPERATORS(String_Constant);-  IMPLEMENT_AST_OPERATORS(String_Quoted);-  IMPLEMENT_AST_OPERATORS(Boolean);-  IMPLEMENT_AST_OPERATORS(Color);-  IMPLEMENT_AST_OPERATORS(Null);-  IMPLEMENT_AST_OPERATORS(Parent_Selector);-  IMPLEMENT_AST_OPERATORS(Import);-  IMPLEMENT_AST_OPERATORS(Import_Stub);-  IMPLEMENT_AST_OPERATORS(Function_Call);-  IMPLEMENT_AST_OPERATORS(Directive);-  IMPLEMENT_AST_OPERATORS(At_Root_Block);-  IMPLEMENT_AST_OPERATORS(Supports_Block);-  IMPLEMENT_AST_OPERATORS(While);-  IMPLEMENT_AST_OPERATORS(Each);-  IMPLEMENT_AST_OPERATORS(For);-  IMPLEMENT_AST_OPERATORS(If);-  IMPLEMENT_AST_OPERATORS(Mixin_Call);-  IMPLEMENT_AST_OPERATORS(Extension);-  IMPLEMENT_AST_OPERATORS(Media_Query);-  IMPLEMENT_AST_OPERATORS(Media_Query_Expression);-  IMPLEMENT_AST_OPERATORS(Debug);-  IMPLEMENT_AST_OPERATORS(Error);-  IMPLEMENT_AST_OPERATORS(Warning);-  IMPLEMENT_AST_OPERATORS(Assignment);-  IMPLEMENT_AST_OPERATORS(Return);-  IMPLEMENT_AST_OPERATORS(At_Root_Query);-  IMPLEMENT_AST_OPERATORS(Variable);-  IMPLEMENT_AST_OPERATORS(Comment);-  IMPLEMENT_AST_OPERATORS(Attribute_Selector);-  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);-  IMPLEMENT_AST_OPERATORS(Argument);-  IMPLEMENT_AST_OPERATORS(Unary_Expression);-  IMPLEMENT_AST_OPERATORS(Function_Call_Schema);-  IMPLEMENT_AST_OPERATORS(Block);-  IMPLEMENT_AST_OPERATORS(Content);-  IMPLEMENT_AST_OPERATORS(Trace);-  IMPLEMENT_AST_OPERATORS(Keyframe_Rule);-  IMPLEMENT_AST_OPERATORS(Bubble);-  IMPLEMENT_AST_OPERATORS(Selector_Schema);-  IMPLEMENT_AST_OPERATORS(Placeholder_Selector);-  IMPLEMENT_AST_OPERATORS(Definition);-  IMPLEMENT_AST_OPERATORS(Declaration);+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"++namespace Sass {++  static Null sass_null(ParserState("null"));++  const char* 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 "minus";+      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";+    }+  }++  const char* 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";+    }+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  void AST_Node::update_pstate(const ParserState& pstate)+  {+    pstate_.offset += pstate - pstate_ + pstate.offset;+  }++  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<AST_Node*>(this)->perform(&i);+    return i.get_buffer();+  }++  std::string AST_Node::to_css(Sass_Inspect_Options opt) const+  {+    opt.output_style = TO_CSS;+    Sass_Output_Options out(opt);+    Emitter emitter(out);+    Inspect i(emitter);+    i.in_declaration = true;+    // ToDo: inspect should be const+    const_cast<AST_Node*>(this)->perform(&i);+    return i.get_buffer();+  }++  std::string AST_Node::to_string() const+  {+    return to_string({ NESTED, 5 });+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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<Statement_Obj>(s),+    is_root_(r)+  { }+  Block::Block(const Block* ptr)+  : Statement(ptr),+    Vectorized<Statement_Obj>(*ptr),+    is_root_(ptr->is_root_)+  { }++  bool Block::isInvisible() const+  {+    for (auto& item : elements()) {+      if (!item->is_invisible()) return false;+    }+    return true;+  }++  bool Block::has_content()+  {+    for (size_t i = 0, L = elements().size(); i < L; ++i) {+      if (elements()[i]->has_content()) return true;+    }+    return Statement::has_content();+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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();+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Ruleset::Ruleset(ParserState pstate, SelectorListObj s, Block_Obj b)+  : Has_Block(pstate, b), selector_(s), schema_(), is_root_(false)+  { statement_type(RULESET); }+  Ruleset::Ruleset(const Ruleset* ptr)+  : Has_Block(ptr),+    selector_(ptr->selector_),+    schema_(ptr->schema_),+    is_root_(ptr->is_root_)+  { statement_type(RULESET); }++  bool Ruleset::is_invisible() const {+    if (const SelectorList * sl = Cast<SelectorList>(selector())) {+      for (size_t i = 0, L = sl->length(); i < L; i += 1)+        if (!(*sl)[i]->isInvisible()) 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 == nullptr)+  { }+  Bubble::Bubble(const Bubble* ptr)+  : Statement(ptr),+    node_(ptr->node_),+    group_end_(ptr->group_end_)+  { }++  bool Bubble::bubbles()+  {+    return true;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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_)+  { }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Directive::Directive(ParserState pstate, std::string kwd, SelectorListObj 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<Null>(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<Expression_Obj>()),+    incs_(std::vector<Include>()),+    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<Include>& Import::incs() { return incs_; }+  std::vector<Expression_Obj>& 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<std::string> 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); }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++    ExtendRule::ExtendRule(ParserState pstate, SelectorListObj s)+  : Statement(pstate), isOptional_(false), selector_(s), schema_()+  { statement_type(EXTEND); }+  ExtendRule::ExtendRule(ParserState pstate, Selector_Schema_Obj s)+    : Statement(pstate), isOptional_(false), selector_(), schema_(s)+  {+    statement_type(EXTEND);+  }+  ExtendRule::ExtendRule(const ExtendRule* ptr)+  : Statement(ptr),+    isOptional_(ptr->isOptional_),+    selector_(ptr->selector_),+    schema_(ptr->schema_)+  { 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); }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Expression::Expression(ParserState pstate, bool d, bool e, bool i, Type ct)+  : AST_Node(pstate),+    is_delayed_(d),+    is_expanded_(e),+    is_interpolant_(i),+    concrete_type_(ct)+  { }++  Expression::Expression(const Expression* ptr)+  : AST_Node(ptr),+    is_delayed_(ptr->is_delayed_),+    is_expanded_(ptr->is_expanded_),+    is_interpolant_(ptr->is_interpolant_),+    concrete_type_(ptr->concrete_type_)+  { }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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";+    }+  }+  bool Unary_Expression::operator==(const Expression& rhs) const+  {+    try+    {+      const Unary_Expression* m = Cast<Unary_Expression>(&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<size_t>()(optype_);+      hash_combine(hash_, operand()->hash());+    };+    return hash_;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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)+  {+    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_);+    }+  }++  void Argument::set_delayed(bool delayed)+  {+    if (value_) value_->set_delayed(delayed);+    is_delayed(delayed);+  }++  bool Argument::operator==(const Expression& rhs) const+  {+    try+    {+      const Argument* m = Cast<Argument>(&rhs);+      if (!(m && name() == m->name())) return false;+      return *value() == *m->value();+    }+    catch (std::bad_cast&)+    {+      return false;+    }+    catch (...) { throw; }+  }++  size_t Argument::hash() const+  {+    if (hash_ == 0) {+      hash_ = std::hash<std::string>()(name());+      hash_combine(hash_, value()->hash());+    }+    return hash_;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Arguments::Arguments(ParserState pstate)+  : Expression(pstate),+    Vectorized<Argument_Obj>(),+    has_named_arguments_(false),+    has_rest_argument_(false),+    has_keyword_argument_(false)+  { }+  Arguments::Arguments(const Arguments* ptr)+  : Expression(ptr),+    Vectorized<Argument_Obj>(*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()+  {+    if (this->has_rest_argument()) {+      for (Argument_Obj arg : this->elements()) {+        if (arg->is_rest_argument()) {+          return arg;+        }+      }+    }+    return {};+  }++  Argument_Obj Arguments::get_keyword_argument()+  {+    if (this->has_keyword_argument()) {+      for (Argument_Obj arg : this->elements()) {+        if (arg->is_keyword_argument()) {+          return arg;+        }+      }+    }+    return {};+  }++  void Arguments::adjust_after_pushing(Argument_Obj a)+  {+    if (!a->name().empty()) {+      if (has_keyword_argument()) {+        coreError("named arguments must precede variable-length argument", a->pstate());+      }+      has_named_arguments(true);+    }+    else if (a->is_rest_argument()) {+      if (has_rest_argument()) {+        coreError("functions and mixins may only be called with one variable-length argument", a->pstate());+      }+      if (has_keyword_argument_) {+        coreError("only keyword arguments may follow variable arguments", a->pstate());+      }+      has_rest_argument(true);+    }+    else if (a->is_keyword_argument()) {+      if (has_keyword_argument()) {+        coreError("functions and mixins may only be called with one keyword argument", a->pstate());+      }+      has_keyword_argument(true);+    }+    else {+      if (has_rest_argument()) {+        coreError("ordinal arguments must precede variable-length arguments", a->pstate());+      }+      if (has_named_arguments()) {+        coreError("ordinal arguments must precede named arguments", a->pstate());+      }+    }+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Media_Query::Media_Query(ParserState pstate, String_Obj t, size_t s, bool n, bool r)+  : Expression(pstate), Vectorized<Media_Query_Expression_Obj>(s),+    media_type_(t), is_negated_(n), is_restricted_(r)+  { }+  Media_Query::Media_Query(const Media_Query* ptr)+  : Expression(ptr),+    Vectorized<Media_Query_Expression_Obj>(*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* l = static_cast<List*>(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;+    }+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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;+  }++  bool At_Root_Block::exclude_node(Statement_Obj s) {+    if (expression() == nullptr)+    {+      return s->statement_type() == Statement::RULESET;+    }++    if (s->statement_type() == Statement::DIRECTIVE)+    {+      if (Directive_Obj dir = Cast<Directive>(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<Directive>(s))+    {+      if (dir->is_keyframes()) return expression()->exclude("keyframes");+    }+    return false;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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_)+  { }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Parameters::Parameters(ParserState pstate)+  : AST_Node(pstate),+    Vectorized<Parameter_Obj>(),+    has_optional_parameters_(false),+    has_rest_parameter_(false)+  { }+  Parameters::Parameters(const Parameters* ptr)+  : AST_Node(ptr),+    Vectorized<Parameter_Obj>(*ptr),+    has_optional_parameters_(ptr->has_optional_parameters_),+    has_rest_parameter_(ptr->has_rest_parameter_)+  { }++  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());+      }+    }+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  // If you forget to add a class here you will get+  // undefined reference to `vtable for Sass::Class'++  IMPLEMENT_AST_OPERATORS(Ruleset);+  IMPLEMENT_AST_OPERATORS(MediaRule);+  IMPLEMENT_AST_OPERATORS(CssMediaRule);+  IMPLEMENT_AST_OPERATORS(CssMediaQuery);+  IMPLEMENT_AST_OPERATORS(Import);+  IMPLEMENT_AST_OPERATORS(Import_Stub);+  IMPLEMENT_AST_OPERATORS(Directive);+  IMPLEMENT_AST_OPERATORS(At_Root_Block);+  IMPLEMENT_AST_OPERATORS(While);+  IMPLEMENT_AST_OPERATORS(Each);+  IMPLEMENT_AST_OPERATORS(For);+  IMPLEMENT_AST_OPERATORS(If);+  IMPLEMENT_AST_OPERATORS(Mixin_Call);+  IMPLEMENT_AST_OPERATORS(ExtendRule);+  IMPLEMENT_AST_OPERATORS(Media_Query);+  IMPLEMENT_AST_OPERATORS(Media_Query_Expression);+  IMPLEMENT_AST_OPERATORS(Debug);+  IMPLEMENT_AST_OPERATORS(Error);+  IMPLEMENT_AST_OPERATORS(Warning);+  IMPLEMENT_AST_OPERATORS(Assignment);+  IMPLEMENT_AST_OPERATORS(Return);+  IMPLEMENT_AST_OPERATORS(At_Root_Query);+  IMPLEMENT_AST_OPERATORS(Comment);+  IMPLEMENT_AST_OPERATORS(Parameters);+  IMPLEMENT_AST_OPERATORS(Parameter);+  IMPLEMENT_AST_OPERATORS(Arguments);+  IMPLEMENT_AST_OPERATORS(Argument);+  IMPLEMENT_AST_OPERATORS(Unary_Expression);+  IMPLEMENT_AST_OPERATORS(Block);+  IMPLEMENT_AST_OPERATORS(Content);+  IMPLEMENT_AST_OPERATORS(Trace);+  IMPLEMENT_AST_OPERATORS(Keyframe_Rule);+  IMPLEMENT_AST_OPERATORS(Bubble);+  IMPLEMENT_AST_OPERATORS(Definition);+  IMPLEMENT_AST_OPERATORS(Declaration);++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////+ }
libsass/src/ast.hpp view
@@ -1,3048 +1,1065 @@ #ifndef SASS_AST_H #define SASS_AST_H -#include "sass.hpp"-#include <set>-#include <deque>-#include <vector>-#include <string>-#include <sstream>-#include <iostream>-#include <typeinfo>-#include <algorithm>-#include "sass/base.h"-#include "ast_fwd_decl.hpp"--#ifdef DEBUG_SHARED_PTR--#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \-  virtual klass##_Ptr copy(std::string, size_t) const = 0; \-  virtual klass##_Ptr clone(std::string, size_t) const = 0; \--#define ATTACH_AST_OPERATIONS(klass) \-  virtual klass##_Ptr copy(std::string, size_t) const; \-  virtual klass##_Ptr clone(std::string, size_t) const; \--#else--#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \-  virtual klass##_Ptr copy() const = 0; \-  virtual klass##_Ptr clone() const = 0; \--#define ATTACH_AST_OPERATIONS(klass) \-  virtual klass##_Ptr copy() const; \-  virtual klass##_Ptr clone() const; \--#endif--#ifdef __clang__--/*- * There are some overloads used here that trigger the clang overload- * hiding warning. Specifically:- *- * Type type() which hides string type() from Expression- *- */-#pragma clang diagnostic push-#pragma clang diagnostic ignored "-Woverloaded-virtual"--#endif--#include "util.hpp"-#include "units.hpp"-#include "context.hpp"-#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 "sass.h"--namespace Sass {--  // easier to search with name-  const bool DELAYED = true;--  // ToDo: should this really be hardcoded-  // Note: most methods follow precision option-  const double NUMBER_EPSILON = 0.00000000000001;--  // macro to test if numbers are equal within a small error margin-  #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON--  // ToDo: where does this fit best?-  // We don't share this with C-API?-  class Operand {-    public:-      Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false)-      : operand(operand), ws_before(ws_before), ws_after(ws_after)-      { }-    public:-      enum Sass_OP operand;-      bool ws_before;-      bool ws_after;-  };--  //////////////////////////////////////////////////////////-  // `hash_combine` comes from boost (functional/hash):-  // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html-  // Boost Software License - Version 1.0-  // http://www.boost.org/users/license.html-  template <typename T>-  void hash_combine (std::size_t& seed, const T& val)-  {-    seed ^= std::hash<T>()(val) + 0x9e3779b9-             + (seed<<6) + (seed>>2);-  }-  //////////////////////////////////////////////////////////--  //////////////////////////////////////////////////////////-  // Abstract base class for all abstract syntax tree nodes.-  //////////////////////////////////////////////////////////-  class AST_Node : public SharedObj {-    ADD_PROPERTY(ParserState, pstate)-  public:-    AST_Node(ParserState pstate)-    : pstate_(pstate)-    { }-    AST_Node(const AST_Node* ptr)-    : pstate_(ptr->pstate_)-    { }--    // AST_Node(AST_Node& ptr) = delete;--    virtual ~AST_Node() = 0;-    virtual size_t hash() { return 0; }-    ATTACH_VIRTUAL_AST_OPERATIONS(AST_Node);-    virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }-    virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }-    virtual const std::string to_string(Sass_Inspect_Options opt) const;-    virtual const std::string to_string() const;-    virtual void cloneChildren() {};-    // generic find function (not fully implemented yet)-    // ToDo: add specific implementions to all children-    virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); };-  public:-    void update_pstate(const ParserState& pstate);-  public:-    Offset off() { return pstate(); }-    Position pos() { return pstate(); }-    ATTACH_OPERATIONS()-  };-  inline AST_Node::~AST_Node() { }--  //////////////////////////////////////////////////////////////////////-  // define cast template now (need complete type)-  //////////////////////////////////////////////////////////////////////--  template<class T>-  T* Cast(AST_Node* ptr) {-    return ptr && typeid(T) == typeid(*ptr) ?-           static_cast<T*>(ptr) : NULL;-  };--  template<class T>-  const T* Cast(const AST_Node* ptr) {-    return ptr && typeid(T) == typeid(*ptr) ?-           static_cast<const T*>(ptr) : NULL;-  };--  //////////////////////////////////////////////////////////////////////-  // Abstract base class for expressions. This side of the AST hierarchy-  // represents elements in value contexts, which exist primarily to be-  // evaluated and returned.-  //////////////////////////////////////////////////////////////////////-  class Expression : public AST_Node {-  public:-    enum Concrete_Type {-      NONE,-      BOOLEAN,-      NUMBER,-      COLOR,-      STRING,-      LIST,-      MAP,-      SELECTOR,-      NULL_VAL,-      FUNCTION_VAL,-      C_WARNING,-      C_ERROR,-      FUNCTION,-      VARIABLE,-      NUM_TYPES-    };-    enum Simple_Type {-      SIMPLE,-      ATTR_SEL,-      PSEUDO_SEL,-      WRAPPED_SEL,-    };-  private:-    // expressions in some contexts shouldn't be evaluated-    ADD_PROPERTY(bool, is_delayed)-    ADD_PROPERTY(bool, is_expanded)-    ADD_PROPERTY(bool, is_interpolant)-    ADD_PROPERTY(Concrete_Type, concrete_type)-  public:-    Expression(ParserState pstate,-               bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)-    : AST_Node(pstate),-      is_delayed_(d),-      is_expanded_(e),-      is_interpolant_(i),-      concrete_type_(ct)-    { }-    Expression(const Expression* ptr)-    : AST_Node(ptr),-      is_delayed_(ptr->is_delayed_),-      is_expanded_(ptr->is_expanded_),-      is_interpolant_(ptr->is_interpolant_),-      concrete_type_(ptr->concrete_type_)-    { }-    virtual operator bool() { return true; }-    virtual ~Expression() { }-    virtual std::string type() const { return ""; /* TODO: raise an error? */ }-    virtual bool is_invisible() const { return false; }-    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; }-    virtual bool eq(const Expression& rhs) const { return *this == rhs; };-    virtual void set_delayed(bool delayed) { is_delayed(delayed); }-    virtual bool has_interpolant() const { return is_interpolant(); }-    virtual bool is_left_interpolant() const { return is_interpolant(); }-    virtual bool is_right_interpolant() const { return is_interpolant(); }-    virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }-    virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }-    ATTACH_VIRTUAL_AST_OPERATIONS(Expression);-    virtual size_t hash() { return 0; }-  };--  //////////////////////////////////////////////////////////////////////-  // Still just an expression, but with a to_string method-  //////////////////////////////////////////////////////////////////////-  class PreValue : public Expression {-  public:-    PreValue(ParserState pstate,-               bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)-    : Expression(pstate, d, e, i, ct)-    { }-    PreValue(const PreValue* ptr)-    : Expression(ptr)-    { }-    ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);-    virtual ~PreValue() { }-  };--  //////////////////////////////////////////////////////////////////////-  // base class for values that support operations-  //////////////////////////////////////////////////////////////////////-  class Value : public Expression {-  public:-    Value(ParserState pstate,-          bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)-    : Expression(pstate, d, e, i, ct)-    { }-    Value(const Value* ptr)-    : Expression(ptr)-    { }-    ATTACH_VIRTUAL_AST_OPERATIONS(Value);-    virtual bool operator== (const Expression& rhs) const = 0;-  };-}--/////////////////////////////////////////////////////////////////////////////////////-// Hash method specializations for std::unordered_map to work with Sass::Expression-/////////////////////////////////////////////////////////////////////////////////////--namespace std {-  template<>-  struct hash<Sass::Expression_Obj>-  {-    size_t operator()(Sass::Expression_Obj s) const-    {-      return s->hash();-    }-  };-  template<>-  struct equal_to<Sass::Expression_Obj>-  {-    bool operator()( Sass::Expression_Obj lhs,  Sass::Expression_Obj rhs) const-    {-      return lhs->hash() == rhs->hash();-    }-  };-}--namespace Sass {--  /////////////////////////////////////////////////////////////////////////////-  // Mixin class for AST nodes that should behave like vectors. Uses the-  // "Template Method" design pattern to allow subclasses to adjust their flags-  // when certain objects are pushed.-  /////////////////////////////////////////////////////////////////////////////-  template <typename T>-  class Vectorized {-    std::vector<T> elements_;-  protected:-    size_t hash_;-    void reset_hash() { hash_ = 0; }-    virtual void adjust_after_pushing(T element) { }-  public:-    Vectorized(size_t s = 0) : elements_(std::vector<T>()), hash_(0)-    { elements_.reserve(s); }-    virtual ~Vectorized() = 0;-    size_t length() const   { return elements_.size(); }-    bool empty() const      { return elements_.empty(); }-    void clear()            { return elements_.clear(); }-    T last() const          { return elements_.back(); }-    T first() const         { return elements_.front(); }-    T& operator[](size_t i) { return elements_[i]; }-    virtual const T& at(size_t i) const { return elements_.at(i); }-    virtual T& at(size_t i) { return elements_.at(i); }-    const T& operator[](size_t i) const { return elements_[i]; }-    virtual void append(T element)-    {-      if (element) {-        reset_hash();-        elements_.push_back(element);-        adjust_after_pushing(element);-      }-    }-    virtual void concat(Vectorized* v)-    {-      for (size_t i = 0, L = v->length(); i < L; ++i) this->append((*v)[i]);-    }-    Vectorized& unshift(T element)-    {-      elements_.insert(elements_.begin(), element);-      return *this;-    }-    std::vector<T>& elements() { return elements_; }-    const std::vector<T>& elements() const { return elements_; }-    std::vector<T>& elements(std::vector<T>& e) { elements_ = e; return elements_; }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        for (T& el : elements_) {-          hash_combine(hash_, el->hash());-        }-      }-      return hash_;-    }--    typename std::vector<T>::iterator end() { return elements_.end(); }-    typename std::vector<T>::iterator begin() { return elements_.begin(); }-    typename std::vector<T>::const_iterator end() const { return elements_.end(); }-    typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }-    typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { return elements_.erase(el); }-    typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { return elements_.erase(el); }--  };-  template <typename T>-  inline Vectorized<T>::~Vectorized() { }--  /////////////////////////////////////////////////////////////////////////////-  // Mixin class for AST nodes that should behave like a hash table. Uses an-  // extra <std::vector> internally to maintain insertion order for interation.-  /////////////////////////////////////////////////////////////////////////////-  class Hashed {-  private:-    ExpressionMap elements_;-    std::vector<Expression_Obj> list_;-  protected:-    size_t hash_;-    Expression_Obj duplicate_key_;-    void reset_hash() { hash_ = 0; }-    void reset_duplicate_key() { duplicate_key_ = 0; }-    virtual void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) { }-  public:-    Hashed(size_t s = 0)-    : elements_(ExpressionMap(s)),-      list_(std::vector<Expression_Obj>()),-      hash_(0), duplicate_key_(NULL)-    { elements_.reserve(s); list_.reserve(s); }-    virtual ~Hashed();-    size_t length() const                  { return list_.size(); }-    bool empty() const                     { return list_.empty(); }-    bool has(Expression_Obj k) const          { return elements_.count(k) == 1; }-    Expression_Obj at(Expression_Obj k) const;-    bool has_duplicate_key() const         { return duplicate_key_ != 0; }-    Expression_Obj get_duplicate_key() const  { return duplicate_key_; }-    const ExpressionMap elements() { return elements_; }-    Hashed& operator<<(std::pair<Expression_Obj, Expression_Obj> p)-    {-      reset_hash();--      if (!has(p.first)) list_.push_back(p.first);-      else if (!duplicate_key_) duplicate_key_ = p.first;--      elements_[p.first] = p.second;--      adjust_after_pushing(p);-      return *this;-    }-    Hashed& operator+=(Hashed* h)-    {-      if (length() == 0) {-        this->elements_ = h->elements_;-        this->list_ = h->list_;-        return *this;-      }--      for (auto key : h->keys()) {-        *this << std::make_pair(key, h->at(key));-      }--      reset_duplicate_key();-      return *this;-    }-    const ExpressionMap& pairs() const { return elements_; }-    const std::vector<Expression_Obj>& keys() const { return list_; }--//    std::unordered_map<Expression_Obj, Expression_Obj>::iterator end() { return elements_.end(); }-//    std::unordered_map<Expression_Obj, Expression_Obj>::iterator begin() { return elements_.begin(); }-//    std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator end() const { return elements_.end(); }-//    std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator begin() const { return elements_.begin(); }--  };-  inline Hashed::~Hashed() { }---  /////////////////////////////////////////////////////////////////////////-  // Abstract base class for statements. This side of the AST hierarchy-  // represents elements in expansion contexts, which exist primarily to be-  // rewritten and macro-expanded.-  /////////////////////////////////////////////////////////////////////////-  class Statement : public AST_Node {-  public:-    enum Statement_Type {-      NONE,-      RULESET,-      MEDIA,-      DIRECTIVE,-      SUPPORTS,-      ATROOT,-      BUBBLE,-      CONTENT,-      KEYFRAMERULE,-      DECLARATION,-      ASSIGNMENT,-      IMPORT_STUB,-      IMPORT,-      COMMENT,-      WARNING,-      RETURN,-      EXTEND,-      ERROR,-      DEBUGSTMT,-      WHILE,-      EACH,-      FOR,-      IF-    };-  private:-    ADD_PROPERTY(Statement_Type, statement_type)-    ADD_PROPERTY(size_t, tabs)-    ADD_PROPERTY(bool, group_end)-  public:-    Statement(ParserState pstate, Statement_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;-    // 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;-    }-  };-  inline Statement::~Statement() { }--  ////////////////////////-  // Blocks of statements.-  ////////////////////////-  class Block : public Statement, public Vectorized<Statement_Obj> {-    ADD_PROPERTY(bool, is_root)-    // needed for properly formatted CSS emission-  protected:-    void adjust_after_pushing(Statement_Obj s)-    {-    }-  public:-    Block(ParserState pstate, size_t s = 0, bool r = false)-    : Statement(pstate),-      Vectorized<Statement_Obj>(s),-      is_root_(r)-    { }-    Block(const Block* ptr)-    : Statement(ptr),-      Vectorized<Statement_Obj>(*ptr),-      is_root_(ptr->is_root_)-    { }-    virtual bool has_content()-    {-      for (size_t i = 0, L = elements().size(); i < L; ++i) {-        if (elements()[i]->has_content()) return true;-      }-      return Statement::has_content();-    }-    ATTACH_AST_OPERATIONS(Block)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Abstract base class for statements that contain blocks of statements.-  ////////////////////////////////////////////////////////////////////////-  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()-    {-      return (block_ && block_->has_content()) || Statement::has_content();-    }-    virtual ~Has_Block() = 0;-  };-  inline Has_Block::~Has_Block() { }--  /////////////////////////////////////////////////////////////////////////////-  // Rulesets (i.e., sets of styles headed by a selector and containing a block-  // of style declarations.-  /////////////////////////////////////////////////////////////////////////////-  class Ruleset : public Has_Block {-    ADD_PROPERTY(Selector_List_Obj, selector)-    ADD_PROPERTY(bool, is_root);-  public:-    Ruleset(ParserState pstate, Selector_List_Obj s = 0, Block_Obj b = 0)-    : 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); }-    bool is_invisible() const;-    ATTACH_AST_OPERATIONS(Ruleset)-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Bubble.-  /////////////////-  class Bubble : public Statement {-    ADD_PROPERTY(Statement_Obj, node)-    ADD_PROPERTY(bool, group_end)-  public:-    Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = 0, 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() { return true; }-    ATTACH_AST_OPERATIONS(Bubble)-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Trace.-  /////////////////-  class Trace : public Has_Block {-    ADD_CONSTREF(char, type)-    ADD_CONSTREF(std::string, name)-  public:-    Trace(ParserState pstate, std::string n, Block_Obj b = 0, char type = 'm')-    : Has_Block(pstate, b), type_(type), name_(n)-    { }-    Trace(const Trace* ptr)-    : Has_Block(ptr),-      type_(ptr->type_),-      name_(ptr->name_)-    { }-    ATTACH_AST_OPERATIONS(Trace)-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Media queries.-  /////////////////-  class Media_Block : 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() { return true; }-    bool is_invisible() const;-    ATTACH_AST_OPERATIONS(Media_Block)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // At-rules -- arbitrary directives beginning with "@" that may have an-  // optional statement block.-  ///////////////////////////////////////////////////////////////////////-  class Directive : public Has_Block {-    ADD_CONSTREF(std::string, keyword)-    ADD_PROPERTY(Selector_List_Obj, selector)-    ADD_PROPERTY(Expression_Obj, value)-  public:-    Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel = 0, Block_Obj b = 0, Expression_Obj val = 0)-    : 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() { 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;-    }-    ATTACH_AST_OPERATIONS(Directive)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // Keyframe-rules -- the child blocks of "@keyframes" nodes.-  ///////////////////////////////////////////////////////////////////////-  class Keyframe_Rule : public Has_Block {-    // according to css spec, this should be <keyframes-name>-    // <keyframes-name> = <custom-ident> | <string>-    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); }-    ATTACH_AST_OPERATIONS(Keyframe_Rule)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Declarations -- style rules consisting of a property name and values.-  ////////////////////////////////////////////////////////////////////////-  class Declaration : public Has_Block {-    ADD_PROPERTY(String_Obj, property)-    ADD_PROPERTY(Expression_Obj, value)-    ADD_PROPERTY(bool, is_important)-    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 = 0)-    : 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); }-    virtual bool is_invisible() const;-    ATTACH_AST_OPERATIONS(Declaration)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////-  // Assignments -- variable and value.-  /////////////////////////////////////-  class Assignment : public Statement {-    ADD_CONSTREF(std::string, variable)-    ADD_PROPERTY(Expression_Obj, value)-    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); }-    ATTACH_AST_OPERATIONS(Assignment)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////////-  // Import directives. CSS and Sass import lists can be intermingled, so it's-  // necessary to store a list of each in an Import node.-  ////////////////////////////////////////////////////////////////////////////-  class Import : public Statement {-    std::vector<Expression_Obj> urls_;-    std::vector<Include>     incs_;-    ADD_PROPERTY(List_Obj,      import_queries);-  public:-    Import(ParserState pstate)-    : Statement(pstate),-      urls_(std::vector<Expression_Obj>()),-      incs_(std::vector<Include>()),-      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<Expression_Obj>& urls() { return urls_; }-    std::vector<Include>& incs() { return incs_; }-    ATTACH_AST_OPERATIONS(Import)-    ATTACH_OPERATIONS()-  };--  // not yet resolved single import-  // so far we only know requested name-  class Import_Stub : 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); }-    ATTACH_AST_OPERATIONS(Import_Stub)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////-  // The Sass `@warn` directive.-  //////////////////////////////-  class Warning : 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); }-    ATTACH_AST_OPERATIONS(Warning)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////-  // The Sass `@error` directive.-  ///////////////////////////////-  class Error : 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); }-    ATTACH_AST_OPERATIONS(Error)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////-  // The Sass `@debug` directive.-  ///////////////////////////////-  class Debug : 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); }-    ATTACH_AST_OPERATIONS(Debug)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////-  // CSS comments. These may be interpolated.-  ///////////////////////////////////////////-  class Comment : public Statement {-    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); }-    virtual bool is_invisible() const-    { return /* is_important() == */ false; }-    ATTACH_AST_OPERATIONS(Comment)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////-  // The Sass `@if` control directive.-  ////////////////////////////////////-  class If : public Has_Block {-    ADD_PROPERTY(Expression_Obj, predicate)-    ADD_PROPERTY(Block_Obj, alternative)-  public:-    If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt = 0)-    : 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); }-    virtual bool has_content()-    {-      return Has_Block::has_content() || (alternative_ && alternative_->has_content());-    }-    ATTACH_AST_OPERATIONS(If)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////-  // The Sass `@for` control directive.-  /////////////////////////////////////-  class For : public Has_Block {-    ADD_CONSTREF(std::string, variable)-    ADD_PROPERTY(Expression_Obj, lower_bound)-    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); }-    ATTACH_AST_OPERATIONS(For)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////-  // The Sass `@each` control directive.-  //////////////////////////////////////-  class Each : public Has_Block {-    ADD_PROPERTY(std::vector<std::string>, variables)-    ADD_PROPERTY(Expression_Obj, list)-  public:-    Each(ParserState pstate, std::vector<std::string> 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); }-    ATTACH_AST_OPERATIONS(Each)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////-  // The Sass `@while` control directive.-  ///////////////////////////////////////-  class While : 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); }-    ATTACH_AST_OPERATIONS(While)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////-  // The @return directive for use inside SassScript functions.-  /////////////////////////////////////////////////////////////-  class Return : 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); }-    ATTACH_AST_OPERATIONS(Return)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////-  // The Sass `@extend` directive.-  ////////////////////////////////-  class Extension : 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); }-    ATTACH_AST_OPERATIONS(Extension)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////////////-  // Definitions for both mixins and functions. The two cases are distinguished-  // by a type tag.-  /////////////////////////////////////////////////////////////////////////////-  struct Backtrace;-  typedef const char* Signature;-  typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>);-  class Definition : public Has_Block {-  public:-    enum Type { MIXIN, FUNCTION };-    ADD_CONSTREF(std::string, name)-    ADD_PROPERTY(Parameters_Obj, parameters)-    ADD_PROPERTY(Env*, environment)-    ADD_PROPERTY(Type, type)-    ADD_PROPERTY(Native_Function, native_function)-    ADD_PROPERTY(Sass_Function_Entry, c_function)-    ADD_PROPERTY(void*, cookie)-    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)-    { }-    Definition(ParserState pstate,-               Signature sig,-               std::string n,-               Parameters_Obj params,-               Native_Function func_ptr,-               bool overload_stub = false)-    : Has_Block(pstate, 0),-      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(ParserState pstate,-               Signature sig,-               std::string n,-               Parameters_Obj params,-               Sass_Function_Entry c_func,-               bool whatever,-               bool whatever2)-    : Has_Block(pstate, 0),-      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)-    { }-    ATTACH_AST_OPERATIONS(Definition)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////-  // Mixin calls (i.e., `@include ...`).-  //////////////////////////////////////-  class Mixin_Call : public Has_Block {-    ADD_CONSTREF(std::string, name)-    ADD_PROPERTY(Arguments_Obj, arguments)-  public:-    Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Block_Obj b = 0)-    : Has_Block(pstate, b), name_(n), arguments_(args)-    { }-    Mixin_Call(const Mixin_Call* ptr)-    : Has_Block(ptr),-      name_(ptr->name_),-      arguments_(ptr->arguments_)-    { }-    ATTACH_AST_OPERATIONS(Mixin_Call)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////-  // The @content directive for mixin content blocks.-  ///////////////////////////////////////////////////-  class Content : public Statement {-    ADD_PROPERTY(Media_Block_Ptr, media_block)-  public:-    Content(ParserState pstate)-    : Statement(pstate),-      media_block_(NULL)-    { statement_type(CONTENT); }-    Content(const Content* ptr)-    : Statement(ptr),-      media_block_(ptr->media_block_)-    { statement_type(CONTENT); }-    ATTACH_AST_OPERATIONS(Content)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // Lists of values, both comma- and space-separated (distinguished by a-  // type-tag.) Also used to represent variable-length argument lists.-  ///////////////////////////////////////////////////////////////////////-  class List : public Value, public Vectorized<Expression_Obj> {-    void adjust_after_pushing(Expression_Obj e) { is_expanded(false); }-  private:-    ADD_PROPERTY(enum Sass_Separator, separator)-    ADD_PROPERTY(bool, is_arglist)-    ADD_PROPERTY(bool, is_bracketed)-    ADD_PROPERTY(bool, from_selector)-  public:-    List(ParserState pstate,-         size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false)-    : Value(pstate),-      Vectorized<Expression_Obj>(size),-      separator_(sep),-      is_arglist_(argl),-      is_bracketed_(bracket),-      from_selector_(false)-    { concrete_type(LIST); }-    List(const List* ptr)-    : Value(ptr),-      Vectorized<Expression_Obj>(*ptr),-      separator_(ptr->separator_),-      is_arglist_(ptr->is_arglist_),-      is_bracketed_(ptr->is_bracketed_),-      from_selector_(ptr->from_selector_)-    { concrete_type(LIST); }-    std::string type() const { return is_arglist_ ? "arglist" : "list"; }-    static std::string type_name() { return "list"; }-    const char* sep_string(bool compressed = false) const {-      return separator() == SASS_SPACE ?-        " " : (compressed ? "," : ", ");-    }-    bool is_invisible() const { return empty() && !is_bracketed(); }-    Expression_Obj value_at_index(size_t i);--    virtual size_t size() const;--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<std::string>()(sep_string());-        hash_combine(hash_, std::hash<bool>()(is_bracketed()));-        for (size_t i = 0, L = length(); i < L; ++i)-          hash_combine(hash_, (elements()[i])->hash());-      }-      return hash_;-    }--    virtual void set_delayed(bool delayed)-    {-      is_delayed(delayed);-      // don't set children-    }--    virtual bool operator== (const Expression& rhs) const;--    ATTACH_AST_OPERATIONS(List)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // Key value paris.-  ///////////////////////////////////////////////////////////////////////-  class Map : public Value, public Hashed {-    void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) { is_expanded(false); }-  public:-    Map(ParserState pstate,-         size_t size = 0)-    : Value(pstate),-      Hashed(size)-    { concrete_type(MAP); }-    Map(const Map* ptr)-    : Value(ptr),-      Hashed(*ptr)-    { concrete_type(MAP); }-    std::string type() const { return "map"; }-    static std::string type_name() { return "map"; }-    bool is_invisible() const { return empty(); }-    List_Obj to_list(ParserState& pstate);--    virtual size_t hash()-    {-      if (hash_ == 0) {-        for (auto key : keys()) {-          hash_combine(hash_, key->hash());-          hash_combine(hash_, at(key)->hash());-        }-      }--      return hash_;-    }--    virtual bool operator== (const Expression& rhs) const;--    ATTACH_AST_OPERATIONS(Map)-    ATTACH_OPERATIONS()-  };--  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";-    }-  }--  //////////////////////////////////////////////////////////////////////////-  // Binary expressions. Represents logical, relational, and arithmetic-  // operations. Templatized to avoid large switch statements and repetitive-  // subclassing.-  //////////////////////////////////////////////////////////////////////////-  class Binary_Expression : public PreValue {-  private:-    HASH_PROPERTY(Operand, op)-    HASH_PROPERTY(Expression_Obj, left)-    HASH_PROPERTY(Expression_Obj, right)-    size_t hash_;-  public:-    Binary_Expression(ParserState pstate,-                      Operand op, Expression_Obj lhs, Expression_Obj rhs)-    : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0)-    { }-    Binary_Expression(const Binary_Expression* ptr)-    : PreValue(ptr),-      op_(ptr->op_),-      left_(ptr->left_),-      right_(ptr->right_),-      hash_(ptr->hash_)-    { }-    const std::string type_name() {-      return sass_op_to_name(optype());-    }-    const std::string separator() {-      return sass_op_separator(optype());-    }-    bool is_left_interpolant(void) const;-    bool is_right_interpolant(void) const;-    bool has_interpolant() const-    {-      return is_left_interpolant() ||-             is_right_interpolant();-    }-    virtual void set_delayed(bool delayed)-    {-      right()->set_delayed(delayed);-      left()->set_delayed(delayed);-      is_delayed(delayed);-    }-    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        Binary_Expression_Ptr_Const m = Cast<Binary_Expression>(&rhs);-        if (m == 0) return false;-        return type() == m->type() &&-               *left() == *m->left() &&-               *right() == *m->right();-      }-      catch (std::bad_cast&)-      {-        return false;-      }-      catch (...) { throw; }-    }-    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<size_t>()(optype());-        hash_combine(hash_, left()->hash());-        hash_combine(hash_, right()->hash());-      }-      return hash_;-    }-    enum Sass_OP optype() const { return op_.operand; }-    ATTACH_AST_OPERATIONS(Binary_Expression)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////////-  // Arithmetic negation (logical negation is just an ordinary function call).-  ////////////////////////////////////////////////////////////////////////////-  class Unary_Expression : public Expression {-  public:-    enum Type { PLUS, MINUS, NOT, SLASH };-  private:-    HASH_PROPERTY(Type, optype)-    HASH_PROPERTY(Expression_Obj, operand)-    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";-      }-    }-    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        Unary_Expression_Ptr_Const m = Cast<Unary_Expression>(&rhs);-        if (m == 0) return false;-        return type() == m->type() &&-               *operand() == *m->operand();-      }-      catch (std::bad_cast&)-      {-        return false;-      }-      catch (...) { throw; }-    }-    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<size_t>()(optype_);-        hash_combine(hash_, operand()->hash());-      };-      return hash_;-    }-    ATTACH_AST_OPERATIONS(Unary_Expression)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////-  // Individual argument objects for mixin and function calls.-  ////////////////////////////////////////////////////////////-  class Argument : public Expression {-    HASH_PROPERTY(Expression_Obj, value)-    HASH_CONSTREF(std::string, name)-    ADD_PROPERTY(bool, is_rest_argument)-    ADD_PROPERTY(bool, is_keyword_argument)-    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_);-      }-    }--    virtual void set_delayed(bool delayed);-    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        Argument_Ptr_Const m = Cast<Argument>(&rhs);-        if (!(m && name() == m->name())) return false;-        return *value() == *m->value();-      }-      catch (std::bad_cast&)-      {-        return false;-      }-      catch (...) { throw; }-    }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<std::string>()(name());-        hash_combine(hash_, value()->hash());-      }-      return hash_;-    }--    ATTACH_AST_OPERATIONS(Argument)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Argument lists -- in their own class to facilitate context-sensitive-  // error checking (e.g., ensuring that all ordinal arguments precede all-  // named arguments).-  ////////////////////////////////////////////////////////////////////////-  class Arguments : public Expression, public Vectorized<Argument_Obj> {-    ADD_PROPERTY(bool, has_named_arguments)-    ADD_PROPERTY(bool, has_rest_argument)-    ADD_PROPERTY(bool, has_keyword_argument)-  protected:-    void adjust_after_pushing(Argument_Obj a);-  public:-    Arguments(ParserState pstate)-    : Expression(pstate),-      Vectorized<Argument_Obj>(),-      has_named_arguments_(false),-      has_rest_argument_(false),-      has_keyword_argument_(false)-    { }-    Arguments(const Arguments* ptr)-    : Expression(ptr),-      Vectorized<Argument_Obj>(*ptr),-      has_named_arguments_(ptr->has_named_arguments_),-      has_rest_argument_(ptr->has_rest_argument_),-      has_keyword_argument_(ptr->has_keyword_argument_)-    { }--    virtual void set_delayed(bool delayed);--    Argument_Obj get_rest_argument();-    Argument_Obj get_keyword_argument();--    ATTACH_AST_OPERATIONS(Arguments)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////-  // Function reference.-  ////////////////////////////////////////////////////-  class Function : public Value {-  public:-    ADD_PROPERTY(Definition_Obj, definition)-    ADD_PROPERTY(bool, is_css)-  public:-    Function(ParserState pstate, Definition_Obj def, bool css)-    : Value(pstate), definition_(def), is_css_(css)-    { concrete_type(FUNCTION_VAL); }-    Function(const Function* ptr)-    : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_)-    { concrete_type(FUNCTION_VAL); }--    std::string type() const { return "function"; }-    static std::string type_name() { return "function"; }-    bool is_invisible() const { return true; }--    std::string name() {-      if (definition_) {-        return definition_->name();-      }-      return "";-    }--    virtual bool operator== (const Expression& rhs) const;--    ATTACH_AST_OPERATIONS(Function)-    ATTACH_OPERATIONS()-  };--  //////////////////-  // Function calls.-  //////////////////-  class Function_Call : public PreValue {-    HASH_CONSTREF(std::string, name)-    HASH_PROPERTY(Arguments_Obj, arguments)-    HASH_PROPERTY(Function_Obj, func)-    ADD_PROPERTY(bool, via_call)-    ADD_PROPERTY(void*, cookie)-    size_t hash_;-  public:-    Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie)-    : PreValue(pstate), name_(n), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0)-    { concrete_type(FUNCTION); }-    Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func)-    : PreValue(pstate), name_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0)-    { concrete_type(FUNCTION); }-    Function_Call(ParserState pstate, std::string n, Arguments_Obj args)-    : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0)-    { concrete_type(FUNCTION); }-    Function_Call(const Function_Call* ptr)-    : PreValue(ptr),-      name_(ptr->name_),-      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;-    }--    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        Function_Call_Ptr_Const m = Cast<Function_Call>(&rhs);-        if (!(m && name() == m->name())) return false;-        if (!(m && arguments()->length() == m->arguments()->length())) return false;-        for (size_t i =0, L = arguments()->length(); i < L; ++i)-          if (!(*(*arguments())[i] == *(*m->arguments())[i])) return false;-        return true;-      }-      catch (std::bad_cast&)-      {-        return false;-      }-      catch (...) { throw; }-    }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<std::string>()(name());-        for (auto argument : arguments()->elements())-          hash_combine(hash_, argument->hash());-      }-      return hash_;-    }-    ATTACH_AST_OPERATIONS(Function_Call)-    ATTACH_OPERATIONS()-  };--  /////////////////////////-  // Function call schemas.-  /////////////////////////-  class Function_Call_Schema : public Expression {-    ADD_PROPERTY(String_Obj, name)-    ADD_PROPERTY(Arguments_Obj, arguments)-  public:-    Function_Call_Schema(ParserState pstate, String_Obj n, Arguments_Obj args)-    : Expression(pstate), name_(n), arguments_(args)-    { concrete_type(STRING); }-    Function_Call_Schema(const Function_Call_Schema* ptr)-    : Expression(ptr),-      name_(ptr->name_),-      arguments_(ptr->arguments_)-    { concrete_type(STRING); }-    ATTACH_AST_OPERATIONS(Function_Call_Schema)-    ATTACH_OPERATIONS()-  };--  ///////////////////////-  // Variable references.-  ///////////////////////-  class Variable : 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); }--    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        Variable_Ptr_Const e = Cast<Variable>(&rhs);-        return e && name() == e->name();-      }-      catch (std::bad_cast&)-      {-        return false;-      }-      catch (...) { throw; }-    }--    virtual size_t hash()-    {-      return std::hash<std::string>()(name());-    }--    ATTACH_AST_OPERATIONS(Variable)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////-  // Numbers, percentages, dimensions, and colors.-  ////////////////////////////////////////////////-  class Number : public Value, public Units {-    HASH_PROPERTY(double, value)-    ADD_PROPERTY(bool, zero)-    size_t hash_;-  public:-    Number(ParserState pstate, double val, std::string u = "", bool zero = true);--    Number(const Number* ptr)-    : Value(ptr),-      Units(ptr),-      value_(ptr->value_), zero_(ptr->zero_),-      hash_(ptr->hash_)-    { concrete_type(NUMBER); }--    bool zero() { return zero_; }-    std::string type() const { return "number"; }-    static std::string type_name() { return "number"; }--    void reduce();-    void normalize();--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<double>()(value_);-        for (const auto numerator : numerators)-          hash_combine(hash_, std::hash<std::string>()(numerator));-        for (const auto denominator : denominators)-          hash_combine(hash_, std::hash<std::string>()(denominator));-      }-      return hash_;-    }--    virtual bool operator< (const Number& rhs) const;-    virtual bool operator== (const Number& rhs) const;-    virtual bool operator== (const Expression& rhs) const;-    ATTACH_AST_OPERATIONS(Number)-    ATTACH_OPERATIONS()-  };--  //////////-  // Colors.-  //////////-  class Color : public Value {-    HASH_PROPERTY(double, r)-    HASH_PROPERTY(double, g)-    HASH_PROPERTY(double, b)-    HASH_PROPERTY(double, a)-    ADD_CONSTREF(std::string, disp)-    size_t hash_;-  public:-    Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "")-    : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp),-      hash_(0)-    { concrete_type(COLOR); }-    Color(const Color* ptr)-    : Value(ptr),-      r_(ptr->r_),-      g_(ptr->g_),-      b_(ptr->b_),-      a_(ptr->a_),-      disp_(ptr->disp_),-      hash_(ptr->hash_)-    { concrete_type(COLOR); }-    std::string type() const { return "color"; }-    static std::string type_name() { return "color"; }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<double>()(a_);-        hash_combine(hash_, std::hash<double>()(r_));-        hash_combine(hash_, std::hash<double>()(g_));-        hash_combine(hash_, std::hash<double>()(b_));-      }-      return hash_;-    }--    virtual bool operator== (const Expression& rhs) const;--    ATTACH_AST_OPERATIONS(Color)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////-  // Errors from Sass_Values.-  //////////////////////////////-  class Custom_Error : 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); }-    virtual bool operator== (const Expression& rhs) const;-    ATTACH_AST_OPERATIONS(Custom_Error)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////-  // Warnings from Sass_Values.-  //////////////////////////////-  class Custom_Warning : 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); }-    virtual bool operator== (const Expression& rhs) const;-    ATTACH_AST_OPERATIONS(Custom_Warning)-    ATTACH_OPERATIONS()-  };--  ////////////-  // Booleans.-  ////////////-  class Boolean : public Value {-    HASH_PROPERTY(bool, value)-    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); }-    virtual operator bool() { return value_; }-    std::string type() const { return "bool"; }-    static std::string type_name() { return "bool"; }-    virtual bool is_false() { return !value_; }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<bool>()(value_);-      }-      return hash_;-    }--    virtual bool operator== (const Expression& rhs) const;--    ATTACH_AST_OPERATIONS(Boolean)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Abstract base class for Sass string values. Includes interpolated and-  // "flat" strings.-  ////////////////////////////////////////////////////////////////////////-  class String : public Value {-  public:-    String(ParserState pstate, bool delayed = false)-    : Value(pstate, delayed)-    { concrete_type(STRING); }-    String(const String* ptr)-    : Value(ptr)-    { concrete_type(STRING); }-    static std::string type_name() { return "string"; }-    virtual ~String() = 0;-    virtual void rtrim() = 0;-    virtual bool operator==(const Expression& rhs) const = 0;-    virtual bool operator<(const Expression& rhs) const {-      return this->to_string() < rhs.to_string();-    };-    ATTACH_VIRTUAL_AST_OPERATIONS(String);-    ATTACH_OPERATIONS()-  };-  inline String::~String() { };--  ///////////////////////////////////////////////////////////////////////-  // Interpolated strings. Meant to be reduced to flat strings during the-  // evaluation phase.-  ///////////////////////////////////////////////////////////////////////-  class String_Schema : public String, public Vectorized<Expression_Obj> {-    ADD_PROPERTY(bool, css)-    size_t hash_;-  public:-    String_Schema(ParserState pstate, size_t size = 0, bool css = true)-    : String(pstate), Vectorized<Expression_Obj>(size), css_(css), hash_(0)-    { concrete_type(STRING); }-    String_Schema(const String_Schema* ptr)-    : String(ptr),-      Vectorized<Expression_Obj>(*ptr),-      css_(ptr->css_),-      hash_(ptr->hash_)-    { concrete_type(STRING); }--    std::string type() const { return "string"; }-    static std::string type_name() { return "string"; }--    bool is_left_interpolant(void) const;-    bool is_right_interpolant(void) const;-    // void has_interpolants(bool tc) { }-    bool has_interpolants() {-      for (auto el : elements()) {-        if (el->is_interpolant()) return true;-      }-      return false;-    }-    virtual void rtrim();--    virtual size_t hash()-    {-      if (hash_ == 0) {-        for (auto string : elements())-          hash_combine(hash_, string->hash());-      }-      return hash_;-    }--    virtual void set_delayed(bool delayed) {-      is_delayed(delayed);-    }--    virtual bool operator==(const Expression& rhs) const;-    ATTACH_AST_OPERATIONS(String_Schema)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////-  // Flat strings -- the lowest level of raw textual data.-  ////////////////////////////////////////////////////////-  class String_Constant : public String {-    ADD_PROPERTY(char, quote_mark)-    ADD_PROPERTY(bool, can_compress_whitespace)-    HASH_CONSTREF(std::string, value)-  protected:-    size_t hash_;-  public:-    String_Constant(const String_Constant* ptr)-    : String(ptr),-      quote_mark_(ptr->quote_mark_),-      can_compress_whitespace_(ptr->can_compress_whitespace_),-      value_(ptr->value_),-      hash_(ptr->hash_)-    { }-    String_Constant(ParserState pstate, std::string val, bool css = true)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0)-    { }-    String_Constant(ParserState pstate, const char* beg, bool css = true)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0)-    { }-    String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0)-    { }-    String_Constant(ParserState pstate, const Token& tok, bool css = true)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0)-    { }-    std::string type() const { return "string"; }-    static std::string type_name() { return "string"; }-    virtual bool is_invisible() const;-    virtual void rtrim();--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<std::string>()(value_);-      }-      return hash_;-    }--    virtual bool operator==(const Expression& rhs) const;-    virtual std::string inspect() const; // quotes are forced on inspection--    // static char auto_quote() { return '*'; }-    static char double_quote() { return '"'; }-    static char single_quote() { return '\''; }--    ATTACH_AST_OPERATIONS(String_Constant)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////-  // Possibly quoted string (unquote on instantiation)-  ////////////////////////////////////////////////////////-  class String_Quoted : public String_Constant {-  public:-    String_Quoted(ParserState pstate, std::string val, char q = 0,-      bool keep_utf8_escapes = false, bool skip_unquoting = false,-      bool strict_unquoting = true, bool css = true)-    : String_Constant(pstate, val, css)-    {-      if (skip_unquoting == false) {-        value_ = unquote(value_, &quote_mark_, keep_utf8_escapes, strict_unquoting);-      }-      if (q && quote_mark_) quote_mark_ = q;-    }-    String_Quoted(const String_Quoted* ptr)-    : String_Constant(ptr)-    { }-    virtual bool operator==(const Expression& rhs) const;-    virtual std::string inspect() const; // quotes are forced on inspection-    ATTACH_AST_OPERATIONS(String_Quoted)-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Media queries.-  /////////////////-  class Media_Query : public Expression,-                      public Vectorized<Media_Query_Expression_Obj> {-    ADD_PROPERTY(String_Obj, media_type)-    ADD_PROPERTY(bool, is_negated)-    ADD_PROPERTY(bool, is_restricted)-  public:-    Media_Query(ParserState pstate,-                String_Obj t = 0, size_t s = 0, bool n = false, bool r = false)-    : Expression(pstate), Vectorized<Media_Query_Expression_Obj>(s),-      media_type_(t), is_negated_(n), is_restricted_(r)-    { }-    Media_Query(const Media_Query* ptr)-    : Expression(ptr),-      Vectorized<Media_Query_Expression_Obj>(*ptr),-      media_type_(ptr->media_type_),-      is_negated_(ptr->is_negated_),-      is_restricted_(ptr->is_restricted_)-    { }-    ATTACH_AST_OPERATIONS(Media_Query)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////-  // Media expressions (for use inside media queries).-  ////////////////////////////////////////////////////-  class Media_Query_Expression : public Expression {-    ADD_PROPERTY(Expression_Obj, feature)-    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_)-    { }-    ATTACH_AST_OPERATIONS(Media_Query_Expression)-    ATTACH_OPERATIONS()-  };--  ////////////////////-  // `@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 = 0)-    : 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() { return true; }-    ATTACH_AST_OPERATIONS(Supports_Block)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////////////////////-  // 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_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////-  // 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)-    : 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_)-    { }-    virtual bool needs_parens(Supports_Condition_Obj cond) const;-    ATTACH_AST_OPERATIONS(Supports_Operator)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////////-  // 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)-    : Supports_Condition(pstate), condition_(c)-    { }-    Supports_Negation(const Supports_Negation* ptr)-    : Supports_Condition(ptr), condition_(ptr->condition_)-    { }-    virtual bool needs_parens(Supports_Condition_Obj cond) const;-    ATTACH_AST_OPERATIONS(Supports_Negation)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////-  // 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)-    : Supports_Condition(pstate), feature_(f), value_(v)-    { }-    Supports_Declaration(const Supports_Declaration* ptr)-    : Supports_Condition(ptr),-      feature_(ptr->feature_),-      value_(ptr->value_)-    { }-    virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; }-    ATTACH_AST_OPERATIONS(Supports_Declaration)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////-  // 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)-    : Supports_Condition(pstate), value_(v)-    { }-    Supports_Interpolation(const Supports_Interpolation* ptr)-    : Supports_Condition(ptr),-      value_(ptr->value_)-    { }-    virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; }-    ATTACH_AST_OPERATIONS(Supports_Interpolation)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////-  // At root expressions (for use inside @at-root).-  /////////////////////////////////////////////////-  class At_Root_Query : public Expression {-  private:-    ADD_PROPERTY(Expression_Obj, feature)-    ADD_PROPERTY(Expression_Obj, value)-  public:-    At_Root_Query(ParserState pstate, Expression_Obj f = 0, Expression_Obj v = 0, 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_)-    { }-    bool exclude(std::string str);-    ATTACH_AST_OPERATIONS(At_Root_Query)-    ATTACH_OPERATIONS()-  };--  ///////////-  // At-root.-  ///////////-  class At_Root_Block : public Has_Block {-    ADD_PROPERTY(At_Root_Query_Obj, expression)-  public:-    At_Root_Block(ParserState pstate, Block_Obj b = 0, At_Root_Query_Obj e = 0)-    : 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() { 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<Directive>(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<Directive>(s))-      {-        if (dir->is_keyframes()) return expression()->exclude("keyframes");-      }-      return false;-    }-    ATTACH_AST_OPERATIONS(At_Root_Block)-    ATTACH_OPERATIONS()-  };--  //////////////////-  // The null value.-  //////////////////-  class Null : public Value {-  public:-    Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); }-    Null(const Null* ptr) : Value(ptr) { concrete_type(NULL_VAL); }-    std::string type() const { return "null"; }-    static std::string type_name() { return "null"; }-    bool is_invisible() const { return true; }-    operator bool() { return false; }-    bool is_false() { return true; }--    virtual size_t hash()-    {-      return -1;-    }--    virtual bool operator== (const Expression& rhs) const;--    ATTACH_AST_OPERATIONS(Null)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////-  // Thunks for delayed evaluation.-  /////////////////////////////////-  class Thunk : public Expression {-    ADD_PROPERTY(Expression_Obj, expression)-    ADD_PROPERTY(Env*, environment)-  public:-    Thunk(ParserState pstate, Expression_Obj exp, Env* env = 0)-    : Expression(pstate), expression_(exp), environment_(env)-    { }-  };--  /////////////////////////////////////////////////////////-  // Individual parameter objects for mixins and functions.-  /////////////////////////////////////////////////////////-  class Parameter : public AST_Node {-    ADD_CONSTREF(std::string, name)-    ADD_PROPERTY(Expression_Obj, default_value)-    ADD_PROPERTY(bool, is_rest_parameter)-  public:-    Parameter(ParserState pstate,-              std::string n, Expression_Obj def = 0, 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_);-      // }-    }-    ATTACH_AST_OPERATIONS(Parameter)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////////-  // Parameter lists -- in their own class to facilitate context-sensitive-  // error checking (e.g., ensuring that all optional parameters follow all-  // required parameters).-  /////////////////////////////////////////////////////////////////////////-  class Parameters : public AST_Node, public Vectorized<Parameter_Obj> {-    ADD_PROPERTY(bool, has_optional_parameters)-    ADD_PROPERTY(bool, has_rest_parameter)-  protected:-    void 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());-        }-      }-    }-  public:-    Parameters(ParserState pstate)-    : AST_Node(pstate),-      Vectorized<Parameter_Obj>(),-      has_optional_parameters_(false),-      has_rest_parameter_(false)-    { }-    Parameters(const Parameters* ptr)-    : AST_Node(ptr),-      Vectorized<Parameter_Obj>(*ptr),-      has_optional_parameters_(ptr->has_optional_parameters_),-      has_rest_parameter_(ptr->has_rest_parameter_)-    { }-    ATTACH_AST_OPERATIONS(Parameters)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////-  // 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:-    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); }-    virtual ~Selector() = 0;-    virtual size_t hash() = 0;-    virtual unsigned long specificity() 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;-    }-    // dispatch to correct handlers-    virtual bool operator<(const Selector& rhs) const = 0;-    virtual bool operator==(const Selector& rhs) const = 0;-    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 : public AST_Node {-    ADD_PROPERTY(String_Obj, contents)-    ADD_PROPERTY(bool, connect_parent);-    // must not be a reference counted object-    // otherwise we create circular references-    ADD_PROPERTY(Media_Block_Ptr, media_block)-    // store computed hash-    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_)-    { }-    virtual bool has_parent_ref() const;-    virtual bool has_real_parent_ref() const;-    virtual bool operator<(const Selector& rhs) const;-    virtual 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-    virtual unsigned long specificity() const { return 0; }-    virtual size_t hash() {-      if (hash_ == 0) {-        hash_combine(hash_, contents_->hash());-      }-      return hash_;-    }-    ATTACH_AST_OPERATIONS(Selector_Schema)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////-  // Abstract base class for simple selectors.-  ////////////////////////////////////////////-  class Simple_Selector : public Selector {-    ADD_CONSTREF(std::string, ns)-    ADD_CONSTREF(std::string, name)-    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)-    {-      simple_type(SIMPLE);-      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_)-    { simple_type(SIMPLE); }-    virtual std::string ns_name() const-    {-      std::string name("");-      if (has_ns_)-        name += ns_ + "|";-      return name + name_;-    }-    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_combine(hash_, std::hash<int>()(SELECTOR));-        hash_combine(hash_, std::hash<std::string>()(ns()));-        hash_combine(hash_, std::hash<std::string>()(name()));-      }-      return hash_;-    }-    // 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_ != "*";-    }-    // name query functions-    bool is_universal() const-    {-      return name_ == "*";-    }--    virtual bool has_placeholder() {-      return false;-    }--    virtual ~Simple_Selector() = 0;-    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);-    virtual bool has_parent_ref() const { return false; };-    virtual bool has_real_parent_ref() const  { return false; };-    virtual bool is_pseudo_element() const { return false; }--    virtual bool is_superselector_of(Compound_Selector_Obj sub) { return false; }--    virtual bool operator==(const Selector& rhs) const;-    virtual bool operator==(const Simple_Selector& rhs) const;-    inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); }--    bool operator<(const Selector& rhs) const;-    bool operator<(const Simple_Selector& rhs) const;-    // default implementation should work for most of the simple selectors (otherwise overload)-    ATTACH_VIRTUAL_AST_OPERATIONS(Simple_Selector);-    ATTACH_OPERATIONS();-  };-  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 : public Simple_Selector {-    ADD_PROPERTY(bool, real)-  public:-    Parent_Selector(ParserState pstate, bool r = true)-    : Simple_Selector(pstate, "&"), real_(r)-    { /* has_reference(true); */ }-    Parent_Selector(const Parent_Selector* ptr)-    : Simple_Selector(ptr), real_(ptr->real_)-    { /* has_reference(true); */ }-    bool is_real_parent_ref() const { return real(); };-    virtual bool has_parent_ref() const { return true; };-    virtual bool has_real_parent_ref() const { return is_real_parent_ref(); };-    virtual unsigned long specificity() const-    {-      return 0;-    }-    std::string type() const { return "selector"; }-    static std::string type_name() { return "selector"; }-    ATTACH_AST_OPERATIONS(Parent_Selector)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////////-  // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.-  /////////////////////////////////////////////////////////////////////////-  class Placeholder_Selector : public Simple_Selector {-  public:-    Placeholder_Selector(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { }-    Placeholder_Selector(const Placeholder_Selector* ptr)-    : Simple_Selector(ptr)-    { }-    virtual unsigned long specificity() const-    {-      return Constants::Specificity_Base;-    }-    virtual bool has_placeholder() {-      return true;-    }-    virtual ~Placeholder_Selector() {};-    ATTACH_AST_OPERATIONS(Placeholder_Selector)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////-  // Element selectors (and the universal selector) -- e.g., div, span, *.-  /////////////////////////////////////////////////////////////////////-  class Element_Selector : public Simple_Selector {-  public:-    Element_Selector(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { }-    Element_Selector(const Element_Selector* ptr)-    : Simple_Selector(ptr)-    { }-    virtual unsigned long specificity() const-    {-      if (name() == "*") return 0;-      else               return Constants::Specificity_Element;-    }-    virtual Simple_Selector_Ptr unify_with(Simple_Selector_Ptr);-    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);-    virtual bool operator==(const Simple_Selector& rhs) const;-    virtual bool operator==(const Element_Selector& rhs) const;-    virtual bool operator<(const Simple_Selector& rhs) const;-    virtual bool operator<(const Element_Selector& rhs) const;-    ATTACH_AST_OPERATIONS(Element_Selector)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////-  // Class selectors  -- i.e., .foo.-  ////////////////////////////////////////////////-  class Class_Selector : public Simple_Selector {-  public:-    Class_Selector(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { }-    Class_Selector(const Class_Selector* ptr)-    : Simple_Selector(ptr)-    { }-    virtual unsigned long specificity() const-    {-      return Constants::Specificity_Class;-    }-    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);-    ATTACH_AST_OPERATIONS(Class_Selector)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////-  // ID selectors -- i.e., #foo.-  ////////////////////////////////////////////////-  class Id_Selector : public Simple_Selector {-  public:-    Id_Selector(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { }-    Id_Selector(const Id_Selector* ptr)-    : Simple_Selector(ptr)-    { }-    virtual unsigned long specificity() const-    {-      return Constants::Specificity_ID;-    }-    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);-    ATTACH_AST_OPERATIONS(Id_Selector)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////-  // Attribute selectors -- e.g., [src*=".jpg"], etc.-  ///////////////////////////////////////////////////-  class Attribute_Selector : public Simple_Selector {-    ADD_CONSTREF(std::string, matcher)-    // this cannot be changed to obj atm!!!!!!????!!!!!!!-    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(ATTR_SEL); }-    Attribute_Selector(const Attribute_Selector* ptr)-    : Simple_Selector(ptr),-      matcher_(ptr->matcher_),-      value_(ptr->value_),-      modifier_(ptr->modifier_)-    { simple_type(ATTR_SEL); }-    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_combine(hash_, Simple_Selector::hash());-        hash_combine(hash_, std::hash<std::string>()(matcher()));-        if (value_) hash_combine(hash_, value_->hash());-      }-      return hash_;-    }-    virtual unsigned long specificity() const-    {-      return Constants::Specificity_Attr;-    }-    virtual bool operator==(const Simple_Selector& rhs) const;-    virtual bool operator==(const Attribute_Selector& rhs) const;-    virtual bool operator<(const Simple_Selector& rhs) const;-    virtual bool operator<(const Attribute_Selector& rhs) const;-    ATTACH_AST_OPERATIONS(Attribute_Selector)-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////////////////////////////////-  // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.-  //////////////////////////////////////////////////////////////////-  /* '::' starts a pseudo-element, ':' a pseudo-class */-  /* Except :first-line, :first-letter, :before and :after */-  /* Note that pseudo-elements are restricted to one per selector */-  /* and occur only in the last simple_selector_sequence. */-  inline bool is_pseudo_class_element(const std::string& name)-  {-    return name == ":before"       ||-           name == ":after"        ||-           name == ":first-line"   ||-           name == ":first-letter";-  }--  // Pseudo Selector cannot have any namespace?-  class Pseudo_Selector : public Simple_Selector {-    ADD_PROPERTY(String_Obj, expression)-  public:-    Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = 0)-    : 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.-    virtual bool is_pseudo_element() const-    {-      return (name_[0] == ':' && name_[1] == ':')-             || is_pseudo_class_element(name_);-    }-    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_combine(hash_, Simple_Selector::hash());-        if (expression_) hash_combine(hash_, expression_->hash());-      }-      return hash_;-    }-    virtual unsigned long specificity() const-    {-      if (is_pseudo_element())-        return Constants::Specificity_Element;-      return Constants::Specificity_Pseudo;-    }-    virtual bool operator==(const Simple_Selector& rhs) const;-    virtual bool operator==(const Pseudo_Selector& rhs) const;-    virtual bool operator<(const Simple_Selector& rhs) const;-    virtual bool operator<(const Pseudo_Selector& rhs) const;-    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);-    ATTACH_AST_OPERATIONS(Pseudo_Selector)-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////-  // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir)-  /////////////////////////////////////////////////-  class Wrapped_Selector : 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); }-    virtual bool is_superselector_of(Wrapped_Selector_Obj sub);-    // Selectors inside the negation pseudo-class are counted like any-    // other, but the negation itself does not count as a pseudo-class.-    virtual size_t hash();-    virtual bool has_parent_ref() const;-    virtual bool has_real_parent_ref() const;-    virtual unsigned long specificity() const;-    virtual bool find ( bool (*f)(AST_Node_Obj) );-    virtual bool operator==(const Simple_Selector& rhs) const;-    virtual bool operator==(const Wrapped_Selector& rhs) const;-    virtual bool operator<(const Simple_Selector& rhs) const;-    virtual bool operator<(const Wrapped_Selector& rhs) const;-    virtual void cloneChildren();-    ATTACH_AST_OPERATIONS(Wrapped_Selector)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////////-  // Simple selector sequences. Maintains flags indicating whether it contains-  // any parent references or placeholders, to simplify expansion.-  ////////////////////////////////////////////////////////////////////////////-  class Compound_Selector : public Selector, public Vectorized<Simple_Selector_Obj> {-  private:-    ComplexSelectorSet sources_;-    ADD_PROPERTY(bool, extended);-    ADD_PROPERTY(bool, has_parent_reference);-  protected:-    void adjust_after_pushing(Simple_Selector_Obj s)-    {-      // if (s->has_reference())   has_reference(true);-      // if (s->has_placeholder()) has_placeholder(true);-    }-  public:-    Compound_Selector(ParserState pstate, size_t s = 0)-    : Selector(pstate),-      Vectorized<Simple_Selector_Obj>(s),-      extended_(false),-      has_parent_reference_(false)-    { }-    Compound_Selector(const Compound_Selector* ptr)-    : Selector(ptr),-      Vectorized<Simple_Selector_Obj>(*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;-    };--    void append(Simple_Selector_Ptr element);--    bool is_universal() const-    {-      return length() == 1 && (*this)[0]->is_universal();-    }--    Complex_Selector_Obj to_complex();-    Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs);-    // virtual Placeholder_Selector_Ptr find_placeholder();-    virtual bool has_parent_ref() const;-    virtual bool has_real_parent_ref() const;-    Simple_Selector_Ptr base() const {-      if (length() == 0) return 0;-      // ToDo: why is this needed?-      if (Cast<Element_Selector>((*this)[0]))-        return (*this)[0];-      return 0;-    }-    virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapped = "");-    virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapped = "");-    virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapped = "");-    virtual size_t hash()-    {-      if (Selector::hash_ == 0) {-        hash_combine(Selector::hash_, std::hash<int>()(SELECTOR));-        if (length()) hash_combine(Selector::hash_, Vectorized::hash());-      }-      return Selector::hash_;-    }-    virtual unsigned long specificity() const-    {-      int sum = 0;-      for (size_t i = 0, L = length(); i < L; ++i)-      { sum += (*this)[i]->specificity(); }-      return sum;-    }--    virtual 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<Parent_Selector>((*this)[0]);-    }--    virtual bool find ( bool (*f)(AST_Node_Obj) );-    virtual bool operator<(const Selector& rhs) const;-    virtual bool operator==(const Selector& rhs) const;-    virtual bool operator<(const Compound_Selector& rhs) const;-    virtual bool operator==(const Compound_Selector& rhs) const;-    inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); }--    ComplexSelectorSet& sources() { return sources_; }-    void clearSources() { sources_.clear(); }-    void mergeSources(ComplexSelectorSet& sources);--    Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs);-    virtual void cloneChildren();-    ATTACH_AST_OPERATIONS(Compound_Selector)-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////////-  // General selectors -- i.e., simple sequences combined with one of the four-  // CSS selector combinators (">", "+", "~", and whitespace). Essentially a-  // linked list.-  ////////////////////////////////////////////////////////////////////////////-  class Complex_Selector : public Selector {-  public:-    enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE };-  private:-    HASH_CONSTREF(Combinator, combinator)-    HASH_PROPERTY(Compound_Selector_Obj, head)-    HASH_PROPERTY(Complex_Selector_Obj, tail)-    HASH_PROPERTY(String_Obj, reference);-  public:-    bool contains_placeholder() {-      if (head() && head()->contains_placeholder()) return true;-      if (tail() && tail()->contains_placeholder()) return true;-      return false;-    };-    Complex_Selector(ParserState pstate,-                     Combinator c = ANCESTOR_OF,-                     Compound_Selector_Obj h = 0,-                     Complex_Selector_Obj t = 0,-                     String_Obj r = 0)-    : 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_)-    {};-    virtual bool has_parent_ref() const;-    virtual bool has_real_parent_ref() const;--    Complex_Selector_Obj skip_empty_reference()-    {-      if ((!head_ || !head_->length() || head_->is_empty_reference()) &&-          combinator() == Combinator::ANCESTOR_OF)-      {-        if (!tail_) return 0;-        tail_->has_line_feed_ = this->has_line_feed_;-        // tail_->has_line_break_ = this->has_line_break_;-        return tail_->skip_empty_reference();-      }-      return this;-    }--    // can still have a tail-    bool is_empty_ancestor() const-    {-      return (!head() || head()->length() == 0) &&-             combinator() == Combinator::ANCESTOR_OF;-    }--    Selector_List_Ptr tails(Selector_List_Ptr tails);--    // front returns the first real tail-    // skips over parent and empty ones-    Complex_Selector_Obj first();-    // last returns the last real tail-    Complex_Selector_Obj last();--    // some shortcuts that should be removed-    Complex_Selector_Obj innermost() { return last(); };--    size_t length() const;-    Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);-    virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");-    virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");-    virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");-    Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs);-    Combinator clear_innermost();-    void append(Complex_Selector_Obj, Backtraces& traces);-    void set_innermost(Complex_Selector_Obj, Combinator);-    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_combine(hash_, std::hash<int>()(SELECTOR));-        hash_combine(hash_, std::hash<int>()(combinator_));-        if (head_) hash_combine(hash_, head_->hash());-        if (tail_) hash_combine(hash_, tail_->hash());-      }-      return hash_;-    }-    virtual unsigned long specificity() const-    {-      int sum = 0;-      if (head()) sum += head()->specificity();-      if (tail()) sum += tail()->specificity();-      return sum;-    }-    virtual void set_media_block(Media_Block_Ptr mb) {-      media_block(mb);-      if (tail_) tail_->set_media_block(mb);-      if (head_) head_->set_media_block(mb);-    }-    virtual bool has_placeholder() {-      if (head_ && head_->has_placeholder()) return true;-      if (tail_ && tail_->has_placeholder()) return true;-      return false;-    }-    virtual bool find ( bool (*f)(AST_Node_Obj) );-    virtual bool operator<(const Selector& rhs) const;-    virtual bool operator==(const Selector& rhs) const;-    virtual bool operator<(const Complex_Selector& rhs) const;-    virtual bool operator==(const Complex_Selector& rhs) const;-    inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }-    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();-      }-    }--    virtual void cloneChildren();-    ATTACH_AST_OPERATIONS(Complex_Selector)-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////-  // Comma-separated selector groups.-  ///////////////////////////////////-  class Selector_List : public Selector, public Vectorized<Complex_Selector_Obj> {-    ADD_PROPERTY(Selector_Schema_Obj, schema)-    ADD_CONSTREF(std::vector<std::string>, wspace)-  protected:-    void adjust_after_pushing(Complex_Selector_Obj c);-  public:-    Selector_List(ParserState pstate, size_t s = 0)-    : Selector(pstate),-      Vectorized<Complex_Selector_Obj>(s),-      schema_(NULL),-      wspace_(0)-    { }-    Selector_List(const Selector_List* ptr)-    : Selector(ptr),-      Vectorized<Complex_Selector_Obj>(*ptr),-      schema_(ptr->schema_),-      wspace_(ptr->wspace_)-    { }-    std::string type() const { return "list"; }-    // remove parent selector references-    // basically unwraps parsed selectors-    virtual bool has_parent_ref() const;-    virtual bool has_real_parent_ref() const;-    void remove_parent_selectors();-    Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);-    virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");-    virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");-    virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");-    Selector_List_Ptr unify_with(Selector_List_Ptr);-    void populate_extends(Selector_List_Obj, Subset_Map&);-    Selector_List_Obj eval(Eval& eval);-    virtual size_t hash()-    {-      if (Selector::hash_ == 0) {-        hash_combine(Selector::hash_, std::hash<int>()(SELECTOR));-        hash_combine(Selector::hash_, Vectorized::hash());-      }-      return Selector::hash_;-    }-    virtual unsigned long specificity() const-    {-      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;-    }-    virtual void set_media_block(Media_Block_Ptr mb) {-      media_block(mb);-      for (Complex_Selector_Obj cs : elements()) {-        cs->set_media_block(mb);-      }-    }-    virtual bool has_placeholder() {-      for (Complex_Selector_Obj cs : elements()) {-        if (cs->has_placeholder()) return true;-      }-      return false;-    }-    virtual bool find ( bool (*f)(AST_Node_Obj) );-    virtual bool operator<(const Selector& rhs) const;-    virtual bool operator==(const Selector& rhs) const;-    virtual bool operator<(const Selector_List& rhs) const;-    virtual bool operator==(const Selector_List& rhs) const;-    // Selector Lists can be compared to comma lists-    virtual bool operator==(const Expression& rhs) const;-    virtual void cloneChildren();-    ATTACH_AST_OPERATIONS(Selector_List)-    ATTACH_OPERATIONS()-  };--  // compare function for sorting and probably other other uses-  struct cmp_complex_selector { inline bool operator() (const Complex_Selector_Obj l, const Complex_Selector_Obj r) { return (*l < *r); } };-  struct cmp_compound_selector { inline bool operator() (const Compound_Selector_Obj l, const Compound_Selector_Obj r) { return (*l < *r); } };-  struct cmp_simple_selector { inline bool operator() (const Simple_Selector_Obj l, const Simple_Selector_Obj r) { return (*l < *r); } };--}--#ifdef __clang__--#pragma clang diagnostic pop+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <typeinfo>+#include <unordered_map>++#include "sass/base.h"+#include "ast_helpers.hpp"+#include "ast_fwd_decl.hpp"+#include "ast_def_macros.hpp"++#include "file.hpp"+#include "position.hpp"+#include "operation.hpp"+#include "environment.hpp"+#include "fn_utils.hpp"++namespace Sass {++  // ToDo: where does this fit best?+  // We don't share this with C-API?+  class Operand {+    public:+      Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false)+      : operand(operand), ws_before(ws_before), ws_after(ws_after)+      { }+    public:+      enum Sass_OP operand;+      bool ws_before;+      bool ws_after;+  };++  //////////////////////////////////////////////////////////+  // `hash_combine` comes from boost (functional/hash):+  // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html+  // Boost Software License - Version 1.0+  // http://www.boost.org/users/license.html+  template <typename T>+  void hash_combine (std::size_t& seed, const T& val)+  {+    seed ^= std::hash<T>()(val) + 0x9e3779b9+             + (seed<<6) + (seed>>2);+  }+  //////////////////////////////////////////////////////////++  const char* sass_op_to_name(enum Sass_OP op);++  const char* sass_op_separator(enum Sass_OP op);++  //////////////////////////////////////////////////////////+  // Abstract base class for all abstract syntax tree nodes.+  //////////////////////////////////////////////////////////+  class AST_Node : public SharedObj {+    ADD_PROPERTY(ParserState, pstate)+  public:+    AST_Node(ParserState pstate)+    : pstate_(pstate)+    { }+    AST_Node(const AST_Node* ptr)+    : pstate_(ptr->pstate_)+    { }++    // allow implicit conversion to string+    // needed for by SharedPtr implementation+    operator std::string() {+      return to_string();+    }++    // AST_Node(AST_Node& ptr) = delete;++    virtual ~AST_Node() = 0;+    virtual size_t hash() const { return 0; }+    virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }+    virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }+    virtual std::string to_string(Sass_Inspect_Options opt) const;+    virtual std::string to_css(Sass_Inspect_Options opt) const;+    virtual std::string to_string() const;+    virtual void cloneChildren() {};+    // generic find function (not fully implemented yet)+    // ToDo: add specific implementions to all children+    virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); };+    void update_pstate(const ParserState& pstate);+    Offset off() { return pstate(); }+    Position pos() { return pstate(); }++    // Some obects are not meant to be compared+    // ToDo: maybe fallback to pointer comparison?+    virtual bool operator== (const AST_Node& rhs) const {+      throw std::runtime_error("operator== not implemented");+    }++    // We can give some reasonable implementations by using+    // inverst operators on the specialized implementations+    virtual bool operator!= (const AST_Node& rhs) const {+      // Unequal if not equal+      return !(*this == rhs);+    }++    ATTACH_ABSTRACT_AST_OPERATIONS(AST_Node);+    ATTACH_ABSTRACT_CRTP_PERFORM_METHODS()+  };+  inline AST_Node::~AST_Node() { }++  //////////////////////////////////////////////////////////////////////+  // define cast template now (need complete type)+  //////////////////////////////////////////////////////////////////////++  template<class T>+  T* Cast(AST_Node* ptr) {+    return ptr && typeid(T) == typeid(*ptr) ?+           static_cast<T*>(ptr) : NULL;+  };++  template<class T>+  const T* Cast(const AST_Node* ptr) {+    return ptr && typeid(T) == typeid(*ptr) ?+           static_cast<const T*>(ptr) : NULL;+  };++  //////////////////////////////////////////////////////////////////////+  // Abstract base class for expressions. This side of the AST hierarchy+  // represents elements in value contexts, which exist primarily to be+  // evaluated and returned.+  //////////////////////////////////////////////////////////////////////+  class Expression : public AST_Node {+  public:+    enum Type {+      NONE,+      BOOLEAN,+      NUMBER,+      COLOR,+      STRING,+      LIST,+      MAP,+      SELECTOR,+      NULL_VAL,+      FUNCTION_VAL,+      C_WARNING,+      C_ERROR,+      FUNCTION,+      VARIABLE,+      PARENT,+      NUM_TYPES+    };+  private:+    // expressions in some contexts shouldn't be evaluated+    ADD_PROPERTY(bool, is_delayed)+    ADD_PROPERTY(bool, is_expanded)+    ADD_PROPERTY(bool, is_interpolant)+    ADD_PROPERTY(Type, concrete_type)+  public:+    Expression(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);+    virtual operator bool() { return true; }+    virtual ~Expression() { }+    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; }+    virtual bool operator== (const Expression& rhs) const { return false; }+    inline bool operator>(const Expression& rhs) const { return rhs < *this; }+    inline bool operator!=(const Expression& rhs) const { return !(rhs == *this); }+    virtual bool eq(const Expression& rhs) const { return *this == rhs; };+    virtual void set_delayed(bool delayed) { is_delayed(delayed); }+    virtual bool has_interpolant() const { return is_interpolant(); }+    virtual bool is_left_interpolant() const { return is_interpolant(); }+    virtual bool is_right_interpolant() const { return is_interpolant(); }+    ATTACH_VIRTUAL_AST_OPERATIONS(Expression);+    size_t hash() const override { return 0; }+  };++}++/////////////////////////////////////////////////////////////////////////////////////+// Hash method specializations for std::unordered_map to work with Sass::Expression+/////////////////////////////////////////////////////////////////////////////////////++namespace std {+  template<>+  struct hash<Sass::Expression_Obj>+  {+    size_t operator()(Sass::Expression_Obj s) const+    {+      return s->hash();+    }+  };+  template<>+  struct equal_to<Sass::Expression_Obj>+  {+    bool operator()( Sass::Expression_Obj lhs,  Sass::Expression_Obj rhs) const+    {+      return lhs->hash() == rhs->hash();+    }+  };+}++namespace Sass {++  /////////////////////////////////////////////////////////////////////////////+  // Mixin class for AST nodes that should behave like vectors. Uses the+  // "Template Method" design pattern to allow subclasses to adjust their flags+  // when certain objects are pushed.+  /////////////////////////////////////////////////////////////////////////////+  template <typename T>+  class Vectorized {+    std::vector<T> elements_;+  protected:+    mutable size_t hash_;+    void reset_hash() { hash_ = 0; }+    virtual void adjust_after_pushing(T element) { }+  public:+    Vectorized(size_t s = 0) : hash_(0)+    { elements_.reserve(s); }+    Vectorized(std::vector<T> vec) :+      elements_(std::move(vec)),+      hash_(0)+    {}+    virtual ~Vectorized() = 0;+    size_t length() const   { return elements_.size(); }+    bool empty() const      { return elements_.empty(); }+    void clear()            { return elements_.clear(); }+    T& last()               { return elements_.back(); }+    T& first()              { return elements_.front(); }+    const T& last() const   { return elements_.back(); }+    const T& first() const  { return elements_.front(); }++    bool operator== (const Vectorized<T>& rhs) const {+      // Abort early if sizes do not match+      if (length() != rhs.length()) return false;+      // Otherwise test each node for object equalicy in order+      return std::equal(begin(), end(), rhs.begin(), ObjEqualityFn<T>);+    }++    bool operator!= (const Vectorized<T>& rhs) const {+      return !(*this == rhs);+    }++    T& operator[](size_t i) { return elements_[i]; }+    virtual const T& at(size_t i) const { return elements_.at(i); }+    virtual T& at(size_t i) { return elements_.at(i); }+    const T& get(size_t i) const { return elements_[i]; }+    const T& operator[](size_t i) const { return elements_[i]; }++    // Implicitly get the std::vector from our object+    // Makes the Vector directly assignable to std::vector+    // You are responsible to make a copy if needed+    // Note: since this returns the real object, we can't+    // Note: guarantee that the hash will not get out of sync+    operator std::vector<T>&() { return elements_; }+    operator const std::vector<T>&() const { return elements_; }++    // Explicitly request all elements as a real std::vector+    // You are responsible to make a copy if needed+    // Note: since this returns the real object, we can't+    // Note: guarantee that the hash will not get out of sync+    std::vector<T>& elements() { return elements_; }+    const std::vector<T>& elements() const { return elements_; }++    // Insert all items from compatible vector+    void concat(const std::vector<T>& v)+    {+      if (!v.empty()) reset_hash();+      elements().insert(end(), v.begin(), v.end());+    }++    // Syntatic sugar for pointers+    void concat(const Vectorized<T>* v)+    {+      if (v != nullptr) {+        return concat(*v);+      }+    }++    // Insert one item on the front+    void unshift(T element)+    {+      reset_hash();+      elements_.insert(begin(), element);+    }++    // Remove and return item on the front+    // ToDo: handle empty vectors+    T shift() {+      reset_hash();+      T first = get(0);+      elements_.erase(begin());+      return first;+    }++    // Insert one item on the back+    // ToDo: rename this to push+    void append(T element)+    {+      reset_hash();+      elements_.insert(end(), element);+      // ToDo: Mostly used by parameters and arguments+      // ToDo: Find a more elegant way to support this+      adjust_after_pushing(element);+    }++    // Check if an item already exists+    // Uses underlying object `operator==`+    // E.g. compares the actual objects+    bool contains(const T& el) const {+      for (const T& rhs : elements_) {+        // Test the underlying objects for equality+        // A std::find checks for pointer equality+        if (ObjEqualityFn(el, rhs)) {+          return true;+        }+      }+      return false;+    }++    // This might be better implemented as `operator=`?+    void elements(std::vector<T> e) {+      reset_hash();+      elements_ = std::move(e);+    }++    virtual size_t hash() const+    {+      if (hash_ == 0) {+        for (const T& el : elements_) {+          hash_combine(hash_, el->hash());+        }+      }+      return hash_;+    }++    template <typename P, typename V>+    typename std::vector<T>::iterator insert(P position, const V& val) {+      reset_hash();+      return elements_.insert(position, val);+    }++    typename std::vector<T>::iterator end() { return elements_.end(); }+    typename std::vector<T>::iterator begin() { return elements_.begin(); }+    typename std::vector<T>::const_iterator end() const { return elements_.end(); }+    typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }+    typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { reset_hash(); return elements_.erase(el); }+    typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { reset_hash(); return elements_.erase(el); }++  };+  template <typename T>+  inline Vectorized<T>::~Vectorized() { }++  /////////////////////////////////////////////////////////////////////////////+  // Mixin class for AST nodes that should behave like a hash table. Uses an+  // extra <std::vector> internally to maintain insertion order for interation.+  /////////////////////////////////////////////////////////////////////////////+  template <typename K, typename T, typename U>+  class Hashed {+  private:+    std::unordered_map<+      K, T, ObjHash, ObjEquality+    > elements_;++    std::vector<K> _keys;+    std::vector<T> _values;+  protected:+    mutable size_t hash_;+    K duplicate_key_;+    void reset_hash() { hash_ = 0; }+    void reset_duplicate_key() { duplicate_key_ = {}; }+    virtual void adjust_after_pushing(std::pair<K, T> p) { }+  public:+    Hashed(size_t s = 0)+    : elements_(),+      _keys(),+      _values(),+      hash_(0), duplicate_key_({})+    {+      _keys.reserve(s);+      _values.reserve(s);+      elements_.reserve(s);+    }+    virtual ~Hashed();+    size_t length() const                  { return _keys.size(); }+    bool empty() const                     { return _keys.empty(); }+    bool has(K k) const          {+      return elements_.find(k) != elements_.end();+    }+    T at(K k) const {+      if (elements_.count(k))+      {+        return elements_.at(k);+      }+      else { return {}; }+    }+    bool has_duplicate_key() const         { return duplicate_key_ != nullptr; }+    K get_duplicate_key() const  { return duplicate_key_; }+    const std::unordered_map<+      K, T, ObjHash, ObjEquality+    >& elements() { return elements_; }+    Hashed& operator<<(std::pair<K, T> p)+    {+      reset_hash();++      if (!has(p.first)) {+        _keys.push_back(p.first);+        _values.push_back(p.second);+      }+      else if (!duplicate_key_) {+        duplicate_key_ = p.first;+      }++      elements_[p.first] = p.second;++      adjust_after_pushing(p);+      return *this;+    }+    Hashed& operator+=(Hashed* h)+    {+      if (length() == 0) {+        this->elements_ = h->elements_;+        this->_values = h->_values;+        this->_keys = h->_keys;+        return *this;+      }++      for (auto key : h->keys()) {+        *this << std::make_pair(key, h->at(key));+      }++      reset_duplicate_key();+      return *this;+    }+    const std::unordered_map<+      K, T, ObjHash, ObjEquality+    >& pairs() const { return elements_; }++    const std::vector<K>& keys() const { return _keys; }+    const std::vector<T>& values() const { return _values; }++//    std::unordered_map<Expression_Obj, Expression_Obj>::iterator end() { return elements_.end(); }+//    std::unordered_map<Expression_Obj, Expression_Obj>::iterator begin() { return elements_.begin(); }+//    std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator end() const { return elements_.end(); }+//    std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator begin() const { return elements_.begin(); }++  };+  template <typename K, typename T, typename U>+  inline Hashed<K, T, U>::~Hashed() { }++  /////////////////////////////////////////////////////////////////////////+  // Abstract base class for statements. This side of the AST hierarchy+  // represents elements in expansion contexts, which exist primarily to be+  // rewritten and macro-expanded.+  /////////////////////////////////////////////////////////////////////////+  class Statement : public AST_Node {+  public:+    enum Type {+      NONE,+      RULESET,+      MEDIA,+      DIRECTIVE,+      SUPPORTS,+      ATROOT,+      BUBBLE,+      CONTENT,+      KEYFRAMERULE,+      DECLARATION,+      ASSIGNMENT,+      IMPORT_STUB,+      IMPORT,+      COMMENT,+      WARNING,+      RETURN,+      EXTEND,+      ERROR,+      DEBUGSTMT,+      WHILE,+      EACH,+      FOR,+      IF+    };+  private:+    ADD_PROPERTY(Type, statement_type)+    ADD_PROPERTY(size_t, tabs)+    ADD_PROPERTY(bool, group_end)+  public:+    Statement(ParserState pstate, Type st = NONE, size_t t = 0);+    virtual ~Statement() = 0; // virtual destructor+    // needed for rearranging nested rulesets during CSS emission+    virtual bool bubbles();+    virtual bool has_content();+    virtual bool is_invisible() const;+    ATTACH_VIRTUAL_AST_OPERATIONS(Statement)+  };+  inline Statement::~Statement() { }++  ////////////////////////+  // Blocks of statements.+  ////////////////////////+  class Block final : public Statement, public Vectorized<Statement_Obj> {+    ADD_PROPERTY(bool, is_root)+    // needed for properly formatted CSS emission+  protected:+    void adjust_after_pushing(Statement_Obj s) override {}+  public:+    Block(ParserState pstate, size_t s = 0, bool r = false);+    bool isInvisible() const;+    bool has_content() override;+    ATTACH_AST_OPERATIONS(Block)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////////////////////+  // Abstract base class for statements that contain blocks of statements.+  ////////////////////////////////////////////////////////////////////////+  class Has_Block : public Statement {+    ADD_PROPERTY(Block_Obj, block)+  public:+    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() { }++  /////////////////////////////////////////////////////////////////////////////+  // Rulesets (i.e., sets of styles headed by a selector and containing a block+  // of style declarations.+  /////////////////////////////////////////////////////////////////////////////+  class Ruleset final : public Has_Block {+    ADD_PROPERTY(SelectorListObj, selector)+    ADD_PROPERTY(Selector_Schema_Obj, schema)+    ADD_PROPERTY(bool, is_root);+  public:+    Ruleset(ParserState pstate, SelectorListObj s = {}, Block_Obj b = {});+    bool is_invisible() const override;+    ATTACH_AST_OPERATIONS(Ruleset)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////+  // Bubble.+  /////////////////+  class Bubble final : public Statement {+    ADD_PROPERTY(Statement_Obj, node)+    ADD_PROPERTY(bool, group_end)+  public:+    Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = {}, size_t t = 0);+    bool bubbles() override;+    ATTACH_AST_OPERATIONS(Bubble)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////+  // Trace.+  /////////////////+  class Trace final : public Has_Block {+    ADD_CONSTREF(char, type)+    ADD_CONSTREF(std::string, name)+  public:+    Trace(ParserState pstate, std::string n, Block_Obj b = {}, char type = 'm');+    ATTACH_AST_OPERATIONS(Trace)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////////////////////////////////////+  // At-rules -- arbitrary directives beginning with "@" that may have an+  // optional statement block.+  ///////////////////////////////////////////////////////////////////////+  class Directive final : public Has_Block {+    ADD_CONSTREF(std::string, keyword)+    ADD_PROPERTY(SelectorListObj, selector)+    ADD_PROPERTY(Expression_Obj, value)+  public:+    Directive(ParserState pstate, std::string kwd, SelectorListObj sel = {}, Block_Obj b = {}, Expression_Obj val = {});+    bool bubbles() override;+    bool is_media();+    bool is_keyframes();+    ATTACH_AST_OPERATIONS(Directive)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////////////////////////////////////+  // Keyframe-rules -- the child blocks of "@keyframes" nodes.+  ///////////////////////////////////////////////////////////////////////+  class Keyframe_Rule final : public Has_Block {+    // according to css spec, this should be <keyframes-name>+    // <keyframes-name> = <custom-ident> | <string>+    ADD_PROPERTY(SelectorListObj, name)+  public:+    Keyframe_Rule(ParserState pstate, Block_Obj b);+    ATTACH_AST_OPERATIONS(Keyframe_Rule)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////////////////////+  // Declarations -- style rules consisting of a property name and values.+  ////////////////////////////////////////////////////////////////////////+  class Declaration final : public Has_Block {+    ADD_PROPERTY(String_Obj, property)+    ADD_PROPERTY(Expression_Obj, value)+    ADD_PROPERTY(bool, is_important)+    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 = {});+    bool is_invisible() const override;+    ATTACH_AST_OPERATIONS(Declaration)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////////////////////////+  // Assignments -- variable and value.+  /////////////////////////////////////+  class Assignment final : public Statement {+    ADD_CONSTREF(std::string, variable)+    ADD_PROPERTY(Expression_Obj, value)+    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);+    ATTACH_AST_OPERATIONS(Assignment)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////////////////////////+  // Import directives. CSS and Sass import lists can be intermingled, so it's+  // necessary to store a list of each in an Import node.+  ////////////////////////////////////////////////////////////////////////////+  class Import final : public Statement {+    std::vector<Expression_Obj> urls_;+    std::vector<Include>        incs_;+    ADD_PROPERTY(List_Obj,      import_queries);+  public:+    Import(ParserState pstate);+    std::vector<Include>& incs();+    std::vector<Expression_Obj>& urls();+    ATTACH_AST_OPERATIONS(Import)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  // not yet resolved single import+  // so far we only know requested name+  class Import_Stub final : public Statement {+    Include resource_;+  public:+    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()+  };++  //////////////////////////////+  // The Sass `@warn` directive.+  //////////////////////////////+  class Warning final : public Statement {+    ADD_PROPERTY(Expression_Obj, message)+  public:+    Warning(ParserState pstate, Expression_Obj msg);+    ATTACH_AST_OPERATIONS(Warning)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////+  // The Sass `@error` directive.+  ///////////////////////////////+  class Error final : public Statement {+    ADD_PROPERTY(Expression_Obj, message)+  public:+    Error(ParserState pstate, Expression_Obj msg);+    ATTACH_AST_OPERATIONS(Error)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////+  // The Sass `@debug` directive.+  ///////////////////////////////+  class Debug final : public Statement {+    ADD_PROPERTY(Expression_Obj, value)+  public:+    Debug(ParserState pstate, Expression_Obj val);+    ATTACH_AST_OPERATIONS(Debug)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////////+  // CSS comments. These may be interpolated.+  ///////////////////////////////////////////+  class Comment final : public Statement {+    ADD_PROPERTY(String_Obj, text)+    ADD_PROPERTY(bool, is_important)+  public:+    Comment(ParserState pstate, String_Obj txt, bool is_important);+    virtual bool is_invisible() const override;+    ATTACH_AST_OPERATIONS(Comment)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////+  // The Sass `@if` control directive.+  ////////////////////////////////////+  class If final : public Has_Block {+    ADD_PROPERTY(Expression_Obj, predicate)+    ADD_PROPERTY(Block_Obj, alternative)+  public:+    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()+  };++  /////////////////////////////////////+  // The Sass `@for` control directive.+  /////////////////////////////////////+  class For final : public Has_Block {+    ADD_CONSTREF(std::string, variable)+    ADD_PROPERTY(Expression_Obj, lower_bound)+    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);+    ATTACH_AST_OPERATIONS(For)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////////////////////////+  // The Sass `@each` control directive.+  //////////////////////////////////////+  class Each final : public Has_Block {+    ADD_PROPERTY(std::vector<std::string>, variables)+    ADD_PROPERTY(Expression_Obj, list)+  public:+    Each(ParserState pstate, std::vector<std::string> vars, Expression_Obj lst, Block_Obj b);+    ATTACH_AST_OPERATIONS(Each)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////+  // The Sass `@while` control directive.+  ///////////////////////////////////////+  class While final : public Has_Block {+    ADD_PROPERTY(Expression_Obj, predicate)+  public:+    While(ParserState pstate, Expression_Obj pred, Block_Obj b);+    ATTACH_AST_OPERATIONS(While)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////////////////////////////////////////////////+  // The @return directive for use inside SassScript functions.+  /////////////////////////////////////////////////////////////+  class Return final : public Statement {+    ADD_PROPERTY(Expression_Obj, value)+  public:+    Return(ParserState pstate, Expression_Obj val);+    ATTACH_AST_OPERATIONS(Return)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////////////////////////////////////////////////////////////////+  // Definitions for both mixins and functions. The two cases are distinguished+  // by a type tag.+  /////////////////////////////////////////////////////////////////////////////+  class Definition final : public Has_Block {+  public:+    enum Type { MIXIN, FUNCTION };+    ADD_CONSTREF(std::string, name)+    ADD_PROPERTY(Parameters_Obj, parameters)+    ADD_PROPERTY(Env*, environment)+    ADD_PROPERTY(Type, type)+    ADD_PROPERTY(Native_Function, native_function)+    ADD_PROPERTY(Sass_Function_Entry, c_function)+    ADD_PROPERTY(void*, cookie)+    ADD_PROPERTY(bool, is_overload_stub)+    ADD_PROPERTY(Signature, signature)+  public:+    Definition(ParserState pstate,+               std::string n,+               Parameters_Obj params,+               Block_Obj b,+               Type t);+    Definition(ParserState pstate,+               Signature sig,+               std::string n,+               Parameters_Obj params,+               Native_Function func_ptr,+               bool overload_stub = false);+    Definition(ParserState pstate,+               Signature sig,+               std::string n,+               Parameters_Obj params,+               Sass_Function_Entry c_func);+    ATTACH_AST_OPERATIONS(Definition)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////////////////////////+  // Mixin calls (i.e., `@include ...`).+  //////////////////////////////////////+  class Mixin_Call final : public Has_Block {+    ADD_CONSTREF(std::string, name)+    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 = {});+    ATTACH_AST_OPERATIONS(Mixin_Call)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////////////////+  // The @content directive for mixin content blocks.+  ///////////////////////////////////////////////////+  class Content final : public Statement {+    ADD_PROPERTY(Arguments_Obj, arguments)+  public:+    Content(ParserState pstate, Arguments_Obj args);+    ATTACH_AST_OPERATIONS(Content)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////////////////////////+  // Arithmetic negation (logical negation is just an ordinary function call).+  ////////////////////////////////////////////////////////////////////////////+  class Unary_Expression final : public Expression {+  public:+    enum Type { PLUS, MINUS, NOT, SLASH };+  private:+    HASH_PROPERTY(Type, optype)+    HASH_PROPERTY(Expression_Obj, operand)+    mutable size_t hash_;+  public:+    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()+  };++  ////////////////////////////////////////////////////////////+  // Individual argument objects for mixin and function calls.+  ////////////////////////////////////////////////////////////+  class Argument final : public Expression {+    HASH_PROPERTY(Expression_Obj, value)+    HASH_CONSTREF(std::string, name)+    ADD_PROPERTY(bool, is_rest_argument)+    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);+    void set_delayed(bool delayed) override;+    bool operator==(const Expression& rhs) const override;+    size_t hash() const override;+    ATTACH_AST_OPERATIONS(Argument)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////////////////////+  // Argument lists -- in their own class to facilitate context-sensitive+  // error checking (e.g., ensuring that all ordinal arguments precede all+  // named arguments).+  ////////////////////////////////////////////////////////////////////////+  class Arguments final : public Expression, public Vectorized<Argument_Obj> {+    ADD_PROPERTY(bool, has_named_arguments)+    ADD_PROPERTY(bool, has_rest_argument)+    ADD_PROPERTY(bool, has_keyword_argument)+  protected:+    void adjust_after_pushing(Argument_Obj a) override;+  public:+    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()+  };+++  // A Media Ruleset before it has been evaluated+  // Could be already final or an interpolation+  class MediaRule final : public Has_Block {+    ADD_PROPERTY(List_Obj, schema)+  public:+    MediaRule(ParserState pstate, Block_Obj block = {});++    bool bubbles() override { return true; };+    bool is_invisible() const override { return false; };+    ATTACH_AST_OPERATIONS(MediaRule)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  // A Media Ruleset after it has been evaluated+  // Representing the static or resulting css+  class CssMediaRule final : public Has_Block,+    public Vectorized<CssMediaQuery_Obj> {+  public:+    CssMediaRule(ParserState pstate, Block_Obj b);+    bool bubbles() override { return true; };+    bool isInvisible() const { return empty(); }+    bool is_invisible() const override { return false; };++  public:+    // Hash and equality implemtation from vector+    size_t hash() const override { return Vectorized::hash(); }+    // Check if two instances are considered equal+    bool operator== (const CssMediaRule& rhs) const {+      return Vectorized::operator== (rhs);+    }+    bool operator!=(const CssMediaRule& rhs) const {+      // Invert from equality+      return !(*this == rhs);+    }++    ATTACH_AST_OPERATIONS(CssMediaRule)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  // Media Queries after they have been evaluated+  // Representing the static or resulting css+  class CssMediaQuery final : public AST_Node {++    // The modifier, probably either "not" or "only".+    // This may be `null` if no modifier is in use.+    ADD_PROPERTY(std::string, modifier);++    // The media type, for example "screen" or "print".+    // This may be `null`. If so, [features] will not be empty.+    ADD_PROPERTY(std::string, type);++    // Feature queries, including parentheses.+    ADD_PROPERTY(std::vector<std::string>, features);++  public:+    CssMediaQuery(ParserState pstate);++    // Check if two instances are considered equal+    bool operator== (const CssMediaQuery& rhs) const;+    bool operator!=(const CssMediaQuery& rhs) const {+      // Invert from equality+      return !(*this == rhs);+    }++    // Returns true if this query is empty+    // Meaning it has no type and features+    bool empty() const {+      return type_.empty()+        && modifier_.empty()+        && features_.empty();+    }++    // Whether this media query matches all media types.+    bool matchesAllTypes() const {+      return type_.empty() || Util::equalsLiteral("all", type_);+    }++    // Merges this with [other] and adds a query that matches the intersection+    // of both inputs to [result]. Returns false if the result is unrepresentable+    CssMediaQuery_Obj merge(CssMediaQuery_Obj& other);++    ATTACH_AST_OPERATIONS(CssMediaQuery)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////+  // Media queries (replaced by MediaRule at al).+  // ToDo: only used for interpolation case+  ////////////////////////////////////////////////////+  class Media_Query final : public Expression,+                            public Vectorized<Media_Query_Expression_Obj> {+    ADD_PROPERTY(String_Obj, media_type)+    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);+    ATTACH_AST_OPERATIONS(Media_Query)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////+  // Media expressions (for use inside media queries).+  // ToDo: only used for interpolation case+  ////////////////////////////////////////////////////+  class Media_Query_Expression final : public Expression {+    ADD_PROPERTY(Expression_Obj, feature)+    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);+    ATTACH_AST_OPERATIONS(Media_Query_Expression)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////////////////////////////////////+  // At root expressions (for use inside @at-root).+  /////////////////////////////////////////////////+  class At_Root_Query final : public Expression {+  private:+    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);+    bool exclude(std::string str);+    ATTACH_AST_OPERATIONS(At_Root_Query)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////+  // At-root.+  ///////////+  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 = {});+    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.+  /////////////////////////////////////////////////////////+  class Parameter final : public AST_Node {+    ADD_CONSTREF(std::string, name)+    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);+    ATTACH_AST_OPERATIONS(Parameter)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////////////////////////////////////////////////////////////+  // Parameter lists -- in their own class to facilitate context-sensitive+  // error checking (e.g., ensuring that all optional parameters follow all+  // required parameters).+  /////////////////////////////////////////////////////////////////////////+  class Parameters final : public AST_Node, public Vectorized<Parameter_Obj> {+    ADD_PROPERTY(bool, has_optional_parameters)+    ADD_PROPERTY(bool, has_rest_parameter)+  protected:+    void adjust_after_pushing(Parameter_Obj p) override;+  public:+    Parameters(ParserState pstate);+    ATTACH_AST_OPERATIONS(Parameters)+    ATTACH_CRTP_PERFORM_METHODS()+  };++}++#include "ast_values.hpp"+#include "ast_supports.hpp"+#include "ast_selectors.hpp"++#ifdef __clang__++// #pragma clang diagnostic pop+// #pragma clang diagnostic push  #endif 
+ libsass/src/ast2c.cpp view
@@ -0,0 +1,80 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast2c.hpp"+#include "ast.hpp"++namespace Sass {++  union Sass_Value* AST2C::operator()(Boolean* b)+  { return sass_make_boolean(b->value()); }++  union Sass_Value* AST2C::operator()(Number* n)+  { return sass_make_number(n->value(), n->unit().c_str()); }++  union Sass_Value* AST2C::operator()(Custom_Warning* w)+  { return sass_make_warning(w->message().c_str()); }++  union Sass_Value* AST2C::operator()(Custom_Error* e)+  { return sass_make_error(e->message().c_str()); }++  union Sass_Value* AST2C::operator()(Color_RGBA* c)+  { return sass_make_color(c->r(), c->g(), c->b(), c->a()); }++  union Sass_Value* AST2C::operator()(Color_HSLA* c)+  {+    Color_RGBA_Obj rgba = c->copyAsRGBA();+    return operator()(rgba.ptr());+  }++  union Sass_Value* AST2C::operator()(String_Constant* s)+  {+    if (s->quote_mark()) {+      return sass_make_qstring(s->value().c_str());+    } else {+      return sass_make_string(s->value().c_str());+    }+  }++  union Sass_Value* AST2C::operator()(String_Quoted* s)+  { return sass_make_qstring(s->value().c_str()); }++  union Sass_Value* AST2C::operator()(List* l)+  {+    union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed());+    for (size_t i = 0, L = l->length(); i < L; ++i) {+      sass_list_set_value(v, i, (*l)[i]->perform(this));+    }+    return v;+  }++  union Sass_Value* AST2C::operator()(Map* m)+  {+    union Sass_Value* v = sass_make_map(m->length());+    int i = 0;+    for (auto key : m->keys()) {+      sass_map_set_key(v, i, key->perform(this));+      sass_map_set_value(v, i, m->at(key)->perform(this));+      i++;+    }+    return v;+  }++  union Sass_Value* AST2C::operator()(Arguments* a)+  {+    union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false);+    for (size_t i = 0, L = a->length(); i < L; ++i) {+      sass_list_set_value(v, i, (*a)[i]->perform(this));+    }+    return v;+  }++  union Sass_Value* AST2C::operator()(Argument* a)+  { return a->value()->perform(this); }++  // not strictly necessary because of the fallback+  union Sass_Value* AST2C::operator()(Null* n)+  { return sass_make_null(); }++};
+ libsass/src/ast2c.hpp view
@@ -0,0 +1,39 @@+#ifndef SASS_AST2C_H+#define SASS_AST2C_H++#include "ast_fwd_decl.hpp"+#include "operation.hpp"+#include "sass/values.h"++namespace Sass {++  class AST2C : public Operation_CRTP<union Sass_Value*, AST2C> {++  public:++    AST2C() { }+    ~AST2C() { }++    union Sass_Value* operator()(Boolean*);+    union Sass_Value* operator()(Number*);+    union Sass_Value* operator()(Color_RGBA*);+    union Sass_Value* operator()(Color_HSLA*);+    union Sass_Value* operator()(String_Constant*);+    union Sass_Value* operator()(String_Quoted*);+    union Sass_Value* operator()(Custom_Warning*);+    union Sass_Value* operator()(Custom_Error*);+    union Sass_Value* operator()(List*);+    union Sass_Value* operator()(Map*);+    union Sass_Value* operator()(Null*);+    union Sass_Value* operator()(Arguments*);+    union Sass_Value* operator()(Argument*);++    // return sass error if type is not supported+    union Sass_Value* fallback(AST_Node* x)+    { return sass_make_error("unknown type for C-API"); }++  };++}++#endif
libsass/src/ast_def_macros.hpp view
@@ -35,16 +35,6 @@   LocalOption<size_t> cnt_##name(name, name + 1); \   if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \ -#define ATTACH_OPERATIONS()\-virtual void perform(Operation<void>* op) { (*op)(this); }\-virtual AST_Node_Ptr perform(Operation<AST_Node_Ptr>* op) { return (*op)(this); }\-virtual Statement_Ptr perform(Operation<Statement_Ptr>* op) { return (*op)(this); }\-virtual Expression_Ptr perform(Operation<Expression_Ptr>* op) { return (*op)(this); }\-virtual Selector_Ptr perform(Operation<Selector_Ptr>* op) { return (*op)(this); }\-virtual std::string perform(Operation<std::string>* op) { return (*op)(this); }\-virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) { return (*op)(this); }\-virtual Value_Ptr perform(Operation<Value_Ptr>* op) { return (*op)(this); }- #define ADD_PROPERTY(type, name)\ protected:\   type name##_;\@@ -76,5 +66,75 @@   const type& name() const { return name##_; } \   void name(type name##__) { hash_ = 0; name##_ = name##__; } \ private:++#ifdef DEBUG_SHARED_PTR++#define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \+  virtual klass* copy(std::string, size_t) const = 0; \+  virtual klass* clone(std::string, size_t) const = 0; \++#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \+  klass(const klass* ptr); \+  virtual klass* copy(std::string, size_t) const override = 0; \+  virtual klass* clone(std::string, size_t) const override = 0; \++#define ATTACH_AST_OPERATIONS(klass) \+  klass(const klass* ptr); \+  virtual klass* copy(std::string, size_t) const override; \+  virtual klass* clone(std::string, size_t) const override; \++#else++#define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \+  virtual klass* copy() const = 0; \+  virtual klass* clone() const = 0; \++#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \+  klass(const klass* ptr); \+  virtual klass* copy() const override = 0; \+  virtual klass* clone() const override = 0; \++#define ATTACH_AST_OPERATIONS(klass) \+  klass(const klass* ptr); \+  virtual klass* copy() const override; \+  virtual klass* clone() const override; \++#endif++#define ATTACH_VIRTUAL_CMP_OPERATIONS(klass) \+  virtual bool operator==(const klass& rhs) const = 0; \+  virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \++#define ATTACH_CMP_OPERATIONS(klass) \+  virtual bool operator==(const klass& rhs) const; \+  virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \++#ifdef DEBUG_SHARED_PTR++  #define IMPLEMENT_AST_OPERATORS(klass) \+    klass* klass::copy(std::string file, size_t line) const { \+      klass* cpy = new klass(this); \+      cpy->trace(file, line); \+      return cpy; \+    } \+    klass* klass::clone(std::string file, size_t line) const { \+      klass* cpy = copy(file, line); \+      cpy->cloneChildren(); \+      return cpy; \+    } \++#else++  #define IMPLEMENT_AST_OPERATORS(klass) \+    klass* klass::copy() const { \+      return new klass(this); \+    } \+    klass* klass::clone() const { \+      klass* cpy = copy(); \+      cpy->cloneChildren(); \+      return cpy; \+    } \++#endif  #endif
libsass/src/ast_fwd_decl.cpp view
@@ -19,11 +19,13 @@   IMPLEMENT_BASE_CAST(Has_Block)   IMPLEMENT_BASE_CAST(PreValue)   IMPLEMENT_BASE_CAST(Value)+  IMPLEMENT_BASE_CAST(Color)   IMPLEMENT_BASE_CAST(List)   IMPLEMENT_BASE_CAST(String)   IMPLEMENT_BASE_CAST(String_Constant)   IMPLEMENT_BASE_CAST(Supports_Condition)   IMPLEMENT_BASE_CAST(Selector)-  IMPLEMENT_BASE_CAST(Simple_Selector)+  IMPLEMENT_BASE_CAST(SelectorComponent)+  IMPLEMENT_BASE_CAST(SimpleSelector)  }
libsass/src/ast_fwd_decl.hpp view
@@ -1,17 +1,12 @@ #ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H -#include <map>-#include <set>-#include <deque>-#include <vector>-#include <typeinfo>-#include <iostream>-#include <algorithm>-#include <unordered_map>-#include <unordered_set>-#include "memory/SharedPtr.hpp"+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include "sass/functions.h"+#include "memory/SharedPtr.hpp"  ///////////////////////////////////////////// // Forward declarations for the AST visitors.@@ -19,266 +14,119 @@ namespace Sass {    class AST_Node;-  typedef AST_Node* AST_Node_Ptr;-  typedef AST_Node const* AST_Node_Ptr_Const;    class Has_Block;-  typedef Has_Block* Has_Block_Ptr;-  typedef Has_Block const* Has_Block_Ptr_Const; -  class Simple_Selector;-  typedef Simple_Selector* Simple_Selector_Ptr;-  typedef Simple_Selector const* Simple_Selector_Ptr_Const;+  class SimpleSelector; +  class Parent_Reference;+   class PreValue;-  typedef PreValue* PreValue_Ptr;-  typedef PreValue const* PreValue_Ptr_Const;-  class Thunk;-  typedef Thunk* Thunk_Ptr;-  typedef Thunk const* Thunk_Ptr_Const;   class Block;-  typedef Block* Block_Ptr;-  typedef Block const* Block_Ptr_Const;   class Expression;-  typedef Expression* Expression_Ptr;-  typedef Expression const* Expression_Ptr_Const;   class Statement;-  typedef Statement* Statement_Ptr;-  typedef Statement const* Statement_Ptr_Const;   class Value;-  typedef Value* Value_Ptr;-  typedef Value const* Value_Ptr_Const;   class Declaration;-  typedef Declaration* Declaration_Ptr;-  typedef Declaration const* Declaration_Ptr_Const;   class Ruleset;-  typedef Ruleset* Ruleset_Ptr;-  typedef Ruleset const* Ruleset_Ptr_Const;   class Bubble;-  typedef Bubble* Bubble_Ptr;-  typedef Bubble const* Bubble_Ptr_Const;   class Trace;-  typedef Trace* Trace_Ptr;-  typedef Trace const* Trace_Ptr_Const; -  class Media_Block;-  typedef Media_Block* Media_Block_Ptr;-  typedef Media_Block const* Media_Block_Ptr_Const;+  class MediaRule;+  class CssMediaRule;+  class CssMediaQuery;+   class Supports_Block;-  typedef Supports_Block* Supports_Block_Ptr;-  typedef Supports_Block const* Supports_Block_Ptr_Const;   class Directive;-  typedef Directive* Directive_Ptr;-  typedef Directive const* Directive_Ptr_Const; -   class Keyframe_Rule;-  typedef Keyframe_Rule* Keyframe_Rule_Ptr;-  typedef Keyframe_Rule const* Keyframe_Rule_Ptr_Const;   class At_Root_Block;-  typedef At_Root_Block* At_Root_Block_Ptr;-  typedef At_Root_Block const* At_Root_Block_Ptr_Const;   class Assignment;-  typedef Assignment* Assignment_Ptr;-  typedef Assignment const* Assignment_Ptr_Const;    class Import;-  typedef Import* Import_Ptr;-  typedef Import const* Import_Ptr_Const;   class Import_Stub;-  typedef Import_Stub* Import_Stub_Ptr;-  typedef Import_Stub const* Import_Stub_Ptr_Const;   class Warning;-  typedef Warning* Warning_Ptr;-  typedef Warning const* Warning_Ptr_Const;    class Error;-  typedef Error* Error_Ptr;-  typedef Error const* Error_Ptr_Const;   class Debug;-  typedef Debug* Debug_Ptr;-  typedef Debug const* Debug_Ptr_Const;   class Comment;-  typedef Comment* Comment_Ptr;-  typedef Comment const* Comment_Ptr_Const;    class If;-  typedef If* If_Ptr;-  typedef If const* If_Ptr_Const;   class For;-  typedef For* For_Ptr;-  typedef For const* For_Ptr_Const;   class Each;-  typedef Each* Each_Ptr;-  typedef Each const* Each_Ptr_Const;   class While;-  typedef While* While_Ptr;-  typedef While const* While_Ptr_Const;   class Return;-  typedef Return* Return_Ptr;-  typedef Return const* Return_Ptr_Const;   class Content;-  typedef Content* Content_Ptr;-  typedef Content const* Content_Ptr_Const;-  class Extension;-  typedef Extension* Extension_Ptr;-  typedef Extension const* Extension_Ptr_Const;+  class ExtendRule;   class Definition;-  typedef Definition* Definition_Ptr;-  typedef Definition const* Definition_Ptr_Const;    class List;-  typedef List* List_Ptr;-  typedef List const* List_Ptr_Const;   class Map;-  typedef Map* Map_Ptr;-  typedef Map const* Map_Ptr_Const;   class Function;-  typedef Function* Function_Ptr;-  typedef Function const* Function_Ptr_Const;    class Mixin_Call;-  typedef Mixin_Call* Mixin_Call_Ptr;-  typedef Mixin_Call const* Mixin_Call_Ptr_Const;   class Binary_Expression;-  typedef Binary_Expression* Binary_Expression_Ptr;-  typedef Binary_Expression const* Binary_Expression_Ptr_Const;   class Unary_Expression;-  typedef Unary_Expression* Unary_Expression_Ptr;-  typedef Unary_Expression const* Unary_Expression_Ptr_Const;   class Function_Call;-  typedef Function_Call* Function_Call_Ptr;-  typedef Function_Call const* Function_Call_Ptr_Const;-  class Function_Call_Schema;-  typedef Function_Call_Schema* Function_Call_Schema_Ptr;-  typedef Function_Call_Schema const* Function_Call_Schema_Ptr_Const;   class Custom_Warning;-  typedef Custom_Warning* Custom_Warning_Ptr;-  typedef Custom_Warning const* Custom_Warning_Ptr_Const;   class Custom_Error;-  typedef Custom_Error* Custom_Error_Ptr;-  typedef Custom_Error const* Custom_Error_Ptr_Const;    class Variable;-  typedef Variable* Variable_Ptr;-  typedef Variable const* Variable_Ptr_Const;   class Number;-  typedef Number* Number_Ptr;-  typedef Number const* Number_Ptr_Const;   class Color;-  typedef Color* Color_Ptr;-  typedef Color const* Color_Ptr_Const;+  class Color_RGBA;+  class Color_HSLA;   class Boolean;-  typedef Boolean* Boolean_Ptr;-  typedef Boolean const* Boolean_Ptr_Const;   class String;-  typedef String* String_Ptr;-  typedef String const* String_Ptr_Const;+  class Null;    class String_Schema;-  typedef String_Schema* String_Schema_Ptr;-  typedef String_Schema const* String_Schema_Ptr_Const;   class String_Constant;-  typedef String_Constant* String_Constant_Ptr;-  typedef String_Constant const* String_Constant_Ptr_Const;   class String_Quoted;-  typedef String_Quoted* String_Quoted_Ptr;-  typedef String_Quoted const* String_Quoted_Ptr_Const;    class Media_Query;-  typedef Media_Query* Media_Query_Ptr;-  typedef Media_Query const* Media_Query_Ptr_Const;   class Media_Query_Expression;-  typedef Media_Query_Expression* Media_Query_Expression_Ptr;-  typedef Media_Query_Expression const* Media_Query_Expression_Ptr_Const;   class Supports_Condition;-  typedef Supports_Condition* Supports_Condition_Ptr;-  typedef Supports_Condition const* Supports_Condition_Ptr_Const;   class Supports_Operator;-  typedef Supports_Operator* Supports_Operator_Ptr;-  typedef Supports_Operator const* Supports_Operator_Ptr_Const;   class Supports_Negation;-  typedef Supports_Negation* Supports_Negation_Ptr;-  typedef Supports_Negation const* Supports_Negation_Ptr_Const;   class Supports_Declaration;-  typedef Supports_Declaration* Supports_Declaration_Ptr;-  typedef Supports_Declaration const* Supports_Declaration_Ptr_Const;   class Supports_Interpolation;-  typedef Supports_Interpolation* Supports_Interpolation_Ptr;-  typedef Supports_Interpolation const* Supports_Interpolation_Ptr_Const;---  class Null;-  typedef Null* Null_Ptr;-  typedef Null const* Null_Ptr_Const;-+     class At_Root_Query;-  typedef At_Root_Query* At_Root_Query_Ptr;-  typedef At_Root_Query const* At_Root_Query_Ptr_Const;-  class Parent_Selector;-  typedef Parent_Selector* Parent_Selector_Ptr;-  typedef Parent_Selector const* Parent_Selector_Ptr_Const;   class Parameter;-  typedef Parameter* Parameter_Ptr;-  typedef Parameter const* Parameter_Ptr_Const;   class Parameters;-  typedef Parameters* Parameters_Ptr;-  typedef Parameters const* Parameters_Ptr_Const;   class Argument;-  typedef Argument* Argument_Ptr;-  typedef Argument const* Argument_Ptr_Const;   class Arguments;-  typedef Arguments* Arguments_Ptr;-  typedef Arguments const* Arguments_Ptr_Const;   class Selector;-  typedef Selector* Selector_Ptr;-  typedef Selector const* Selector_Ptr_Const;     class Selector_Schema;-  typedef Selector_Schema* Selector_Schema_Ptr;-  typedef Selector_Schema const* Selector_Schema_Ptr_Const;   class Placeholder_Selector;-  typedef Placeholder_Selector* Placeholder_Selector_Ptr;-  typedef Placeholder_Selector const* Placeholder_Selector_Ptr_Const;-  class Element_Selector;-  typedef Element_Selector* Element_Selector_Ptr;-  typedef Element_Selector const* Element_Selector_Ptr_Const;+  class Type_Selector;   class Class_Selector;-  typedef Class_Selector* Class_Selector_Ptr;-  typedef Class_Selector const* Class_Selector_Ptr_Const;   class Id_Selector;-  typedef Id_Selector* Id_Selector_Ptr;-  typedef Id_Selector const* Id_Selector_Ptr_Const;   class Attribute_Selector;-  typedef Attribute_Selector* Attribute_Selector_Ptr;-  typedef Attribute_Selector const* Attribute_Selector_Ptr_Const;    class Pseudo_Selector;-  typedef Pseudo_Selector* Pseudo_Selector_Ptr;-  typedef Pseudo_Selector const * Pseudo_Selector_Ptr_Const;-  class Wrapped_Selector;-  typedef Wrapped_Selector* Wrapped_Selector_Ptr;-  typedef Wrapped_Selector const * Wrapped_Selector_Ptr_Const;-  class Compound_Selector;-  typedef Compound_Selector* Compound_Selector_Ptr;-  typedef Compound_Selector const * Compound_Selector_Ptr_Const;-  class Complex_Selector;-  typedef Complex_Selector* Complex_Selector_Ptr;-  typedef Complex_Selector const * Complex_Selector_Ptr_Const;-  class Selector_List;-  typedef Selector_List* Selector_List_Ptr;-  typedef Selector_List const * Selector_List_Ptr_Const;-+  +  class SelectorComponent;+  class SelectorCombinator;+  class CompoundSelector;+  class ComplexSelector;+  class SelectorList;    // common classes   class Context;   class Expand;   class Eval; +  class Extension;+   // declare classes that are instances of memory nodes-  // #define IMPL_MEM_OBJ(type) using type##_Obj = SharedImpl<type>-  #define IMPL_MEM_OBJ(type) typedef SharedImpl<type> type##_Obj+  // Note: also add a mapping without underscore+  // ToDo: move to camelCase vars in the future+  #define IMPL_MEM_OBJ(type) \+    typedef SharedImpl<type> type##Obj; \+    typedef SharedImpl<type> type##_Obj; \    IMPL_MEM_OBJ(AST_Node);   IMPL_MEM_OBJ(Statement);@@ -286,7 +134,9 @@   IMPL_MEM_OBJ(Ruleset);   IMPL_MEM_OBJ(Bubble);   IMPL_MEM_OBJ(Trace);-  IMPL_MEM_OBJ(Media_Block);+  IMPL_MEM_OBJ(MediaRule);+  IMPL_MEM_OBJ(CssMediaRule);+  IMPL_MEM_OBJ(CssMediaQuery);   IMPL_MEM_OBJ(Supports_Block);   IMPL_MEM_OBJ(Directive);   IMPL_MEM_OBJ(Keyframe_Rule);@@ -301,14 +151,13 @@   IMPL_MEM_OBJ(Comment);   IMPL_MEM_OBJ(PreValue);   IMPL_MEM_OBJ(Has_Block);-  IMPL_MEM_OBJ(Thunk);   IMPL_MEM_OBJ(If);   IMPL_MEM_OBJ(For);   IMPL_MEM_OBJ(Each);   IMPL_MEM_OBJ(While);   IMPL_MEM_OBJ(Return);   IMPL_MEM_OBJ(Content);-  IMPL_MEM_OBJ(Extension);+  IMPL_MEM_OBJ(ExtendRule);   IMPL_MEM_OBJ(Definition);   IMPL_MEM_OBJ(Mixin_Call);   IMPL_MEM_OBJ(Value);@@ -319,12 +168,13 @@   IMPL_MEM_OBJ(Binary_Expression);   IMPL_MEM_OBJ(Unary_Expression);   IMPL_MEM_OBJ(Function_Call);-  IMPL_MEM_OBJ(Function_Call_Schema);   IMPL_MEM_OBJ(Custom_Warning);   IMPL_MEM_OBJ(Custom_Error);   IMPL_MEM_OBJ(Variable);   IMPL_MEM_OBJ(Number);   IMPL_MEM_OBJ(Color);+  IMPL_MEM_OBJ(Color_RGBA);+  IMPL_MEM_OBJ(Color_HSLA);   IMPL_MEM_OBJ(Boolean);   IMPL_MEM_OBJ(String_Schema);   IMPL_MEM_OBJ(String);@@ -339,87 +189,37 @@   IMPL_MEM_OBJ(Supports_Interpolation);   IMPL_MEM_OBJ(At_Root_Query);   IMPL_MEM_OBJ(Null);-  IMPL_MEM_OBJ(Parent_Selector);+  IMPL_MEM_OBJ(Parent_Reference);   IMPL_MEM_OBJ(Parameter);   IMPL_MEM_OBJ(Parameters);   IMPL_MEM_OBJ(Argument);   IMPL_MEM_OBJ(Arguments);   IMPL_MEM_OBJ(Selector);   IMPL_MEM_OBJ(Selector_Schema);-  IMPL_MEM_OBJ(Simple_Selector);+  IMPL_MEM_OBJ(SimpleSelector);   IMPL_MEM_OBJ(Placeholder_Selector);-  IMPL_MEM_OBJ(Element_Selector);+  IMPL_MEM_OBJ(Type_Selector);   IMPL_MEM_OBJ(Class_Selector);   IMPL_MEM_OBJ(Id_Selector);   IMPL_MEM_OBJ(Attribute_Selector);   IMPL_MEM_OBJ(Pseudo_Selector);-  IMPL_MEM_OBJ(Wrapped_Selector);-  IMPL_MEM_OBJ(Compound_Selector);-  IMPL_MEM_OBJ(Complex_Selector);-  IMPL_MEM_OBJ(Selector_List); -  // ###########################################################################-  // Implement compare, order and hashing operations for AST Nodes-  // ###########################################################################--  struct HashNodes {-    template <class T>-    size_t operator() (const T& ex) const {-      return ex.isNull() ? 0 : ex->hash();-    }-  };-  struct OrderNodes {-    template <class T>-    bool operator() (const T& lhs, const T& rhs) const {-      return !lhs.isNull() && !rhs.isNull() && *lhs < *rhs;-    }-  };-  struct CompareNodes {-    template <class T>-    bool operator() (const T& lhs, const T& rhs) const {-      // code around sass logic issue. 1px == 1 is true-      // but both items are still different keys in maps-      if (dynamic_cast<Number*>(lhs.ptr()))-        if (dynamic_cast<Number*>(rhs.ptr()))-          return lhs->hash() == rhs->hash();-      return !lhs.isNull() && !rhs.isNull() && *lhs == *rhs;-    }-  };+  IMPL_MEM_OBJ(SelectorComponent);+  IMPL_MEM_OBJ(SelectorCombinator);+  IMPL_MEM_OBJ(CompoundSelector);+  IMPL_MEM_OBJ(ComplexSelector);+  IMPL_MEM_OBJ(SelectorList);    // ###########################################################################   // some often used typedefs   // ########################################################################### -  typedef std::unordered_map<-    Expression_Obj, // key-    Expression_Obj, // value-    HashNodes, // hasher-    CompareNodes // compare-  > ExpressionMap;-  typedef std::unordered_set<-    Expression_Obj, // value-    HashNodes, // hasher-    CompareNodes // compare-  > ExpressionSet;--  typedef std::string SubSetMapKey;-  typedef std::vector<std::string> SubSetMapKeys;--  typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> SubSetMapPair;-  typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SubSetMapLookup;-  typedef std::vector<SubSetMapPair> SubSetMapPairs;-  typedef std::vector<SubSetMapLookup> SubSetMapLookups;--  typedef std::pair<Complex_Selector_Obj, SubSetMapPairs> SubSetMapResult;-  typedef std::vector<SubSetMapResult> SubSetMapResults;--  typedef std::deque<Complex_Selector_Obj> ComplexSelectorDeque;-  typedef std::set<Simple_Selector_Obj, OrderNodes> SimpleSelectorSet;-  typedef std::set<Complex_Selector_Obj, OrderNodes> ComplexSelectorSet;-  typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet;-  typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict;--  typedef std::vector<Sass_Import_Entry>* ImporterStack;+  typedef std::vector<Block*> BlockStack;+  typedef std::vector<Sass_Callee> CalleeStack;+  typedef std::vector<AST_Node_Obj> CallStack;+  typedef std::vector<CssMediaRuleObj> MediaStack;+  typedef std::vector<SelectorListObj> SelectorStack;+  typedef std::vector<Sass_Import_Entry> ImporterStack;    // only to switch implementations for testing   #define environment_map std::map@@ -452,11 +252,13 @@   DECLARE_BASE_CAST(PreValue)   DECLARE_BASE_CAST(Value)   DECLARE_BASE_CAST(List)+  DECLARE_BASE_CAST(Color)   DECLARE_BASE_CAST(String)   DECLARE_BASE_CAST(String_Constant)   DECLARE_BASE_CAST(Supports_Condition)   DECLARE_BASE_CAST(Selector)-  DECLARE_BASE_CAST(Simple_Selector)+  DECLARE_BASE_CAST(SimpleSelector)+  DECLARE_BASE_CAST(SelectorComponent)  } 
+ libsass/src/ast_helpers.hpp view
@@ -0,0 +1,292 @@+#ifndef SASS_AST_HELPERS_H+#define SASS_AST_HELPERS_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include <algorithm>+#include <functional>+#include "util_string.hpp"++namespace Sass {++  // ###########################################################################+  // ###########################################################################++  // easier to search with name+  const bool DELAYED = true;++  // ToDo: should this really be hardcoded+  // Note: most methods follow precision option+  const double NUMBER_EPSILON = 1e-12;++  // macro to test if numbers are equal within a small error margin+  #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON++  // ###########################################################################+  // We define various functions and functors here.+  // Functions satisfy the BinaryPredicate requirement+  // Functors are structs used for e.g. unordered_map+  // ###########################################################################++  // ###########################################################################+  // Implement compare and hashing operations for raw pointers+  // ###########################################################################++  template <class T>+  size_t PtrHashFn(const T* ptr) {+    return std::hash<std::size_t>()((size_t)ptr);+  }++  struct PtrHash {+    template <class T>+    size_t operator() (const T* ptr) const {+      return PtrHashFn(ptr);+    }+  };++  template <class T>+  bool PtrEqualityFn(const T* lhs, const T* rhs) {+    return lhs == rhs; // compare raw pointers+  }++  struct PtrEquality {+    template <class T>+    bool operator() (const T* lhs, const T* rhs) const {+      return PtrEqualityFn<T>(lhs, rhs);+    }+  };++  // ###########################################################################+  // Implement compare and hashing operations for AST Nodes+  // ###########################################################################++  // TODO: get rid of funtions and use ObjEquality<T>++  template <class T>+  // Hash the raw pointer instead of object+  size_t ObjPtrHashFn(const T& obj) {+    return PtrHashFn(obj.ptr());+  }++  struct ObjPtrHash {+    template <class T>+    // Hash the raw pointer instead of object+    size_t operator() (const T& obj) const {+      return ObjPtrHashFn(obj);+    }+  };++  template <class T>+  // Hash the object and its content+  size_t ObjHashFn(const T& obj) {+    return obj ? obj->hash() : 0;+  }++  struct ObjHash {+    template <class T>+    // Hash the object and its content+    size_t operator() (const T& obj) const {+      return ObjHashFn(obj);+    }+  };++  template <class T>+  // Hash the object behind pointer+  size_t PtrObjHashFn(const T* obj) {+    return obj ? obj->hash() : 0;+  }++  struct PtrObjHash {+    template <class T>+    // Hash the object behind pointer+    size_t operator() (const T* obj) const {+      return PtrObjHashFn(obj);+    }+  };++  template <class T>+  // Compare raw pointers to the object+  bool ObjPtrEqualityFn(const T& lhs, const T& rhs) {+    return PtrEqualityFn(lhs.ptr(), rhs.ptr());+  }++  struct ObjPtrEquality {+    template <class T>+    // Compare raw pointers to the object+    bool operator() (const T& lhs, const T& rhs) const {+      return ObjPtrEqualityFn<T>(lhs, rhs);+    }+  };++  template <class T>+  // Compare the objects behind the pointers+  bool PtrObjEqualityFn(const T* lhs, const T* rhs) {+    if (lhs == nullptr) return rhs == nullptr;+    else if (rhs == nullptr) return false;+    else return *lhs == *rhs;+  }++  struct PtrObjEquality {+    template <class T>+    // Compare the objects behind the pointers+    bool operator() (const T* lhs, const T* rhs) const {+      return PtrObjEqualityFn<T>(lhs, rhs);+    }+  };++  template <class T>+  // Compare the objects and its contents+  bool ObjEqualityFn(const T& lhs, const T& rhs) {+    return PtrObjEqualityFn(lhs.ptr(), rhs.ptr());+  }++  struct ObjEquality {+    template <class T>+    // Compare the objects and its contents+    bool operator() (const T& lhs, const T& rhs) const {+      return ObjEqualityFn<T>(lhs, rhs);+    }+  };++  // ###########################################################################+  // Implement ordering operations for AST Nodes+  // ###########################################################################++  template <class T>+  // Compare the objects behind pointers+  bool PtrObjLessThanFn(const T* lhs, const T* rhs) {+    if (lhs == nullptr) return rhs != nullptr;+    else if (rhs == nullptr) return false;+    else return *lhs < *rhs;+  }++  struct PtrObjLessThan {+    template <class T>+    // Compare the objects behind pointers+    bool operator() (const T* lhs, const T* rhs) const {+      return PtrObjLessThanFn<T>(lhs, rhs);+    }+  };++  template <class T>+  // Compare the objects and its content+  bool ObjLessThanFn(const T& lhs, const T& rhs) {+    return PtrObjLessThanFn(lhs.ptr(), rhs.ptr());+  };++  struct ObjLessThan {+    template <class T>+    // Compare the objects and its content+    bool operator() (const T& lhs, const T& rhs) const {+      return ObjLessThanFn<T>(lhs, rhs);+    }+  };++  // ###########################################################################+  // Some STL helper functions+  // ###########################################################################++  // Check if all elements are equal+  template <class X, class Y,+    typename XT = typename X::value_type,+    typename YT = typename Y::value_type>+  bool ListEquality(const X& lhs, const Y& rhs,+    bool(*cmp)(const XT*, const YT*))+  {+    return lhs.size() == rhs.size() &&+      std::equal(lhs.begin(), lhs.end(),+        rhs.begin(), cmp);+  }++  // Return if Vector is empty+  template <class T>+  bool listIsEmpty(T* cnt) {+    return cnt && cnt->empty();+  }++  // Erase items from vector that match predicate+  template<class T, class UnaryPredicate>+  void listEraseItemIf(T& vec, UnaryPredicate* predicate)+  {+    vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end());+  }++  // Check that every item in `lhs` is also in `rhs`+  // Note: this works by comparing the raw pointers+  template <typename T>+  bool listIsSubsetOrEqual(const T& lhs, const T& rhs) {+    for (const auto& item : lhs) {+      if (std::find(rhs.begin(), rhs.end(), item) == rhs.end())+        return false;+    }+    return true;+  }++  // ##########################################################################+  // Returns whether [name] is the name of a pseudo-element+  // that can be written with pseudo-class syntax (CSS2 vs CSS3):+  // `:before`, `:after`, `:first-line`, or `:first-letter`+  // ##########################################################################+  inline bool isFakePseudoElement(const std::string& name)+  {+    return Util::equalsLiteral("after", name)+      || Util::equalsLiteral("before", name)+      || Util::equalsLiteral("first-line", name)+      || Util::equalsLiteral("first-letter", name);+  }++  // ##########################################################################+  // Names of pseudo selectors that take selectors as arguments,+  // and that are subselectors of their arguments.+  // For example, `.foo` is a superselector of `:matches(.foo)`.+  // ##########################################################################+  inline bool isSubselectorPseudo(const std::string& norm)+  {+    return Util::equalsLiteral("any", norm)+      || Util::equalsLiteral("matches", norm)+      || Util::equalsLiteral("nth-child", norm)+      || Util::equalsLiteral("nth-last-child", norm);+  }+  // EO isSubselectorPseudo++  // ###########################################################################+  // Pseudo-class selectors that take unadorned selectors as arguments.+  // ###########################################################################+  inline bool isSelectorPseudoClass(const std::string& test)+  {+    return Util::equalsLiteral("not", test)+      || Util::equalsLiteral("matches", test)+      || Util::equalsLiteral("current", test)+      || Util::equalsLiteral("any", test)+      || Util::equalsLiteral("has", test)+      || Util::equalsLiteral("host", test)+      || Util::equalsLiteral("host-context", test);+  }+  // EO isSelectorPseudoClass++  // ###########################################################################+  // Pseudo-element selectors that take unadorned selectors as arguments.+  // ###########################################################################+  inline bool isSelectorPseudoElement(const std::string& test)+  {+    return Util::equalsLiteral("slotted", test);+  }+  // EO isSelectorPseudoElement++  // ###########################################################################+  // Pseudo-element selectors that has binominals+  // ###########################################################################+  inline bool isSelectorPseudoBinominal(const std::string& test)+  {+    return Util::equalsLiteral("nth-child", test)+      || Util::equalsLiteral("nth-last-child", test);+  }+  // isSelectorPseudoBinominal++  // ###########################################################################+  // ###########################################################################++}++#endif
+ libsass/src/ast_sel_cmp.cpp view
@@ -0,0 +1,396 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast_selectors.hpp"++namespace Sass {++  /*#########################################################################*/+  // Compare against base class on right hand side+  // try to find the most specialized implementation+  /*#########################################################################*/++  // Selector lists can be compared to comma lists+  bool SelectorList::operator== (const Expression& rhs) const+  {+    if (auto l = Cast<List>(&rhs)) { return *this == *l; }+    if (auto s = Cast<Selector>(&rhs)) { return *this == *s; }+    if (Cast<String>(&rhs) || Cast<Null>(&rhs)) { return false; }+    throw std::runtime_error("invalid selector base classes to compare");+  }++  // Selector lists can be compared to comma lists+  bool SelectorList::operator== (const Selector& rhs) const+  {+    if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }+    if (auto list = Cast<List>(&rhs)) { return *this == *list; }+    throw std::runtime_error("invalid selector base classes to compare");+  }++  bool ComplexSelector::operator== (const Selector& rhs) const+  {+    if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<ComplexSelector>(&rhs)) { return *sel == *this; }+    if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }+    throw std::runtime_error("invalid selector base classes to compare");+  }++  bool SelectorCombinator::operator== (const Selector& rhs) const+  {+    if (auto cpx = Cast<SelectorCombinator>(&rhs)) { return *this == *cpx; }+    return false;+  }++  bool CompoundSelector::operator== (const Selector& rhs) const+  {+    if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+    throw std::runtime_error("invalid selector base classes to compare");+  }++  bool SimpleSelector::operator== (const Selector& rhs) const+  {+    if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+    if (auto sel = Cast<SimpleSelector>(&rhs)) return *this == *sel;+    throw std::runtime_error("invalid selector base classes to compare");+  }++  /*#########################################################################*/+  /*#########################################################################*/++  bool SelectorList::operator== (const SelectorList& rhs) const+  {+    if (&rhs == this) return true;+    if (rhs.length() != length()) return false;+    std::unordered_set<const ComplexSelector*, PtrObjHash, PtrObjEquality> lhs_set;+    lhs_set.reserve(length());+    for (const ComplexSelectorObj& element : elements()) {+      lhs_set.insert(element.ptr());+    }+    for (const ComplexSelectorObj& element : rhs.elements()) {+      if (lhs_set.find(element.ptr()) == lhs_set.end()) return false;+    }+    return true;+  }++++  /*#########################################################################*/+  // Compare SelectorList against all other selector types+  /*#########################################################################*/++  bool SelectorList::operator== (const ComplexSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (length() != 1) return false;+    // Compare simple selectors+    return *get(0) == rhs;+  }++  bool SelectorList::operator== (const CompoundSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (length() != 1) return false;+    // Compare simple selectors+    return *get(0) == rhs;+  }++  bool SelectorList::operator== (const SimpleSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (length() != 1) return false;+    // Compare simple selectors+    return *get(0) == rhs;+  }++  /*#########################################################################*/+  // Compare ComplexSelector against itself+  /*#########################################################################*/++  bool ComplexSelector::operator== (const ComplexSelector& rhs) const+  {+    size_t len = length();+    size_t rlen = rhs.length();+    if (len != rlen) return false;+    for (size_t i = 0; i < len; i += 1) {+      if (*get(i) != *rhs.get(i)) return false;+    }+    return true;+  }++  /*#########################################################################*/+  // Compare ComplexSelector against all other selector types+  /*#########################################################################*/++  bool ComplexSelector::operator== (const SelectorList& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (rhs.length() != 1) return false;+    // Compare complex selector+    return *this == *rhs.get(0);+  }++  bool ComplexSelector::operator== (const CompoundSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (length() != 1) return false;+    // Compare compound selector+    return *get(0) == rhs;+  }++  bool ComplexSelector::operator== (const SimpleSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (length() != 1) return false;+    // Compare simple selectors+    return *get(0) == rhs;+  }++  /*#########################################################################*/+  // Compare SelectorCombinator against itself+  /*#########################################################################*/++  bool SelectorCombinator::operator==(const SelectorCombinator& rhs) const+  {+    return combinator() == rhs.combinator();+  }++  /*#########################################################################*/+  // Compare SelectorCombinator against SelectorComponent+  /*#########################################################################*/++  bool SelectorCombinator::operator==(const SelectorComponent& rhs) const+  {+    if (const SelectorCombinator * sel = rhs.getCombinator()) {+      return *this == *sel;+    }+    return false;+  }++  bool CompoundSelector::operator==(const SelectorComponent& rhs) const+  {+    if (const CompoundSelector * sel = rhs.getCompound()) {+      return *this == *sel;+    }+    return false;+  }++  /*#########################################################################*/+  // Compare CompoundSelector against itself+  /*#########################################################################*/+  // ToDo: Verifiy implementation+  /*#########################################################################*/++  bool CompoundSelector::operator== (const CompoundSelector& rhs) const+  {+    // std::cerr << "comp vs comp\n";+    if (&rhs == this) return true;+    if (rhs.length() != length()) return false;+    std::unordered_set<const SimpleSelector*, PtrObjHash, PtrObjEquality> lhs_set;+    lhs_set.reserve(length());+    for (const SimpleSelectorObj& element : elements()) {+      lhs_set.insert(element.ptr());+    }+    // there is no break?!+    for (const SimpleSelectorObj& element : rhs.elements()) {+      if (lhs_set.find(element.ptr()) == lhs_set.end()) return false;+    }+    return true;+  }+++  /*#########################################################################*/+  // Compare CompoundSelector against all other selector types+  /*#########################################################################*/++  bool CompoundSelector::operator== (const SelectorList& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (rhs.length() != 1) return false;+    // Compare complex selector+    return *this == *rhs.get(0);+  }++  bool CompoundSelector::operator== (const ComplexSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (rhs.length() != 1) return false;+    // Compare compound selector+    return *this == *rhs.get(0);+  }++  bool CompoundSelector::operator== (const SimpleSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return false;+    // Must have exactly one item+    size_t rlen = length();+    if (rlen > 1) return false;+    if (rlen == 0) return true;+    // Compare simple selectors+    return *get(0) < rhs;+  }++  /*#########################################################################*/+  // Compare SimpleSelector against itself (upcast from abstract base)+  /*#########################################################################*/++  // DOES NOT EXIST FOR ABSTRACT BASE CLASS++  /*#########################################################################*/+  // Compare SimpleSelector against all other selector types+  /*#########################################################################*/++  bool SimpleSelector::operator== (const SelectorList& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (rhs.length() != 1) return false;+    // Compare complex selector+    return *this == *rhs.get(0);+  }++  bool SimpleSelector::operator== (const ComplexSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return true;+    // Must have exactly one item+    if (rhs.length() != 1) return false;+    // Compare compound selector+    return *this == *rhs.get(0);+  }++  bool SimpleSelector::operator== (const CompoundSelector& rhs) const+  {+    // If both are empty they are equal+    if (empty() && rhs.empty()) return false;+    // Must have exactly one item+    if (rhs.length() != 1) return false;+    // Compare simple selector+    return *this == *rhs.get(0);+  }++  /*#########################################################################*/+  /*#########################################################################*/++  bool Id_Selector::operator== (const SimpleSelector& rhs) const+  {+    auto sel = Cast<Id_Selector>(&rhs);+    return sel ? *this == *sel : false;+  }++  bool Type_Selector::operator== (const SimpleSelector& rhs) const+  {+    auto sel = Cast<Type_Selector>(&rhs);+    return sel ? *this == *sel : false;+  }++  bool Class_Selector::operator== (const SimpleSelector& rhs) const+  {+    auto sel = Cast<Class_Selector>(&rhs);+    return sel ? *this == *sel : false;+  }++  bool Pseudo_Selector::operator== (const SimpleSelector& rhs) const+  {+    auto sel = Cast<Pseudo_Selector>(&rhs);+    return sel ? *this == *sel : false;+  }++  bool Attribute_Selector::operator== (const SimpleSelector& rhs) const+  {+    auto sel = Cast<Attribute_Selector>(&rhs);+    return sel ? *this == *sel : false;+  }++  bool Placeholder_Selector::operator== (const SimpleSelector& rhs) const+  {+    auto sel = Cast<Placeholder_Selector>(&rhs);+    return sel ? *this == *sel : false;+  }++  /*#########################################################################*/+  /*#########################################################################*/++  bool Id_Selector::operator== (const Id_Selector& rhs) const+  {+    // ID has no namespacing+    return name() == rhs.name();+  }++  bool Type_Selector::operator== (const Type_Selector& rhs) const+  {+    return is_ns_eq(rhs) && name() == rhs.name();+  }++  bool Class_Selector::operator== (const Class_Selector& rhs) const+  {+    // Class has no namespacing+    return name() == rhs.name();+  }++  bool Placeholder_Selector::operator== (const Placeholder_Selector& rhs) const+  {+    // Placeholder has no namespacing+    return name() == rhs.name();+  }++  bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const+  {+    // smaller return, equal go on, bigger abort+    if (is_ns_eq(rhs)) {+      if (name() != rhs.name()) return false;+      if (matcher() != rhs.matcher()) return false;+      if (modifier() != rhs.modifier()) return false;+      const String* lhs_val = value();+      const String* rhs_val = rhs.value();+      return PtrObjEquality()(lhs_val, rhs_val);+    }+    else { return false; }+  }++  bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const+  {+    if (is_ns_eq(rhs)) {+      if (name() != rhs.name()) return false;+      if (isElement() != rhs.isElement()) return false;+      const String* lhs_arg = argument();+      const String* rhs_arg = rhs.argument();+      if (!PtrObjEquality()(lhs_arg, rhs_arg)) return false;+      const SelectorList* lhs_sel = selector();+      const SelectorList* rhs_sel = rhs.selector();+      return PtrObjEquality()(lhs_sel, rhs_sel);+    }+    else { return false; }+  }++  /*#########################################################################*/+  /*#########################################################################*/++}
+ libsass/src/ast_sel_super.cpp view
@@ -0,0 +1,539 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++#include "util_string.hpp"++namespace Sass {++  // ##########################################################################+  // To compare/debug against libsass you can use debugger.hpp:+  // c++: std::cerr << "result " << debug_vec(compound) << "\n";+  // dart: stderr.writeln("result " + compound.toString());+  // ##########################################################################++  // ##########################################################################+  // Returns whether [list1] is a superselector of [list2].+  // That is, whether [list1] matches every element that+  // [list2] matches, as well as possibly additional elements.+  // ##########################################################################+  bool listIsSuperslector(+    const std::vector<ComplexSelectorObj>& list1,+    const std::vector<ComplexSelectorObj>& list2);++  // ##########################################################################+  // Returns whether [complex1] is a superselector of [complex2].+  // That is, whether [complex1] matches every element that+  // [complex2] matches, as well as possibly additional elements.+  // ##########################################################################+  bool complexIsSuperselector(+    const std::vector<SelectorComponentObj>& complex1,+    const std::vector<SelectorComponentObj>& complex2);++  // ##########################################################################+  // Returns all pseudo selectors in [compound] that have+  // a selector argument, and that have the given [name].+  // ##########################################################################+  std::vector<Pseudo_Selector_Obj> selectorPseudoNamed(+    CompoundSelectorObj compound, std::string name)+  {+    std::vector<Pseudo_Selector_Obj> rv;+    for (SimpleSelectorObj sel : compound->elements()) {+      if (Pseudo_Selector_Obj pseudo = Cast<Pseudo_Selector>(sel)) {+        if (pseudo->isClass() && pseudo->selector()) {+          if (sel->name() == name) {+            rv.push_back(sel);+          }+        }+      }+    }+    return rv;+  }+  // EO selectorPseudoNamed++  // ##########################################################################+  // Returns whether [simple1] is a superselector of [simple2].+  // That is, whether [simple1] matches every element that+  // [simple2] matches, as well as possibly additional elements.+  // ##########################################################################+  bool simpleIsSuperselector(+    const SimpleSelectorObj& simple1,+    const SimpleSelectorObj& simple2)+  {+    // If they are equal they are superselectors+    if (ObjEqualityFn(simple1, simple2)) {+      return true;+    }+    // Some selector pseudoclasses can match normal selectors.+    if (const Pseudo_Selector* pseudo = Cast<Pseudo_Selector>(simple2)) {+      if (pseudo->selector() && isSubselectorPseudo(pseudo->normalized())) {+        for (auto complex : pseudo->selector()->elements()) {+          // Make sure we have exacly one items+          if (complex->length() != 1) {+            return false;+          }+          // That items must be a compound selector+          if (auto compound = Cast<CompoundSelector>(complex->at(0))) {+            // It must contain the lhs simple selector+            if (!compound->contains(simple1)) { +              return false;+            }+          }+        }+        return true;+      }+    }+    return false;+  }+  // EO simpleIsSuperselector++  // ##########################################################################+  // Returns whether [simple] is a superselector of [compound].+  // That is, whether [simple] matches every element that+  // [compound] matches, as well as possibly additional elements.+  // ##########################################################################+  bool simpleIsSuperselectorOfCompound(+    const SimpleSelectorObj& simple,+    const CompoundSelectorObj& compound)+  {+    for (SimpleSelectorObj simple2 : compound->elements()) {+      if (simpleIsSuperselector(simple, simple2)) {+        return true;+      }+    }+    return false;+  }+  // EO simpleIsSuperselectorOfCompound++  // ##########################################################################+  // ##########################################################################+  bool typeIsSuperselectorOfCompound(+    const Type_Selector_Obj& type,+    const CompoundSelectorObj& compound)+  {+    for (const SimpleSelectorObj& simple : compound->elements()) {+      if (const Type_Selector_Obj& rhs = Cast<Type_Selector>(simple)) {+        if (*type != *rhs) return true;+      }+    }+    return false;+  }+  // EO typeIsSuperselectorOfCompound++  // ##########################################################################+  // ##########################################################################+  bool idIsSuperselectorOfCompound(+    const Id_Selector_Obj& id,+    const CompoundSelectorObj& compound)+  {+    for (const SimpleSelectorObj& simple : compound->elements()) {+      if (const Id_Selector_Obj& rhs = Cast<Id_Selector>(simple)) {+        if (*id != *rhs) return true;+      }+    }+    return false;+  }+  // EO idIsSuperselectorOfCompound++  // ##########################################################################+  // ##########################################################################+  bool pseudoIsSuperselectorOfPseudo(+    const Pseudo_Selector_Obj& pseudo1,+    const Pseudo_Selector_Obj& pseudo2,+    const ComplexSelectorObj& parent+  )+  {+    if (!pseudo2->selector()) return false;+    if (pseudo1->name() == pseudo2->name()) {+      SelectorListObj list = pseudo2->selector();+      return listIsSuperslector(list->elements(), { parent });+    }+    return false;+  }+  // EO pseudoIsSuperselectorOfPseudo++  // ##########################################################################+  // ##########################################################################+  bool pseudoNotIsSuperselectorOfCompound(+    const Pseudo_Selector_Obj& pseudo1,+    const CompoundSelectorObj& compound2,+    const ComplexSelectorObj& parent)+  {+    for (const SimpleSelectorObj& simple2 : compound2->elements()) {+      if (const Type_Selector_Obj& type2 = Cast<Type_Selector>(simple2)) {+        if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) {+          if (typeIsSuperselectorOfCompound(type2, compound1)) return true;+        }+      }+      else if (const Id_Selector_Obj& id2 = Cast<Id_Selector>(simple2)) {+        if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) {+          if (idIsSuperselectorOfCompound(id2, compound1)) return true;+        }+      }+      else if (const Pseudo_Selector_Obj& pseudo2 = Cast<Pseudo_Selector>(simple2)) {+        if (pseudoIsSuperselectorOfPseudo(pseudo1, pseudo2, parent)) return true;+      }+    }+    return false;+  }+  // pseudoNotIsSuperselectorOfCompound++  // ##########################################################################+  // Returns whether [pseudo1] is a superselector of [compound2].+  // That is, whether [pseudo1] matches every element that [compound2]+  // matches, as well as possibly additional elements. This assumes that+  // [pseudo1]'s `selector` argument is not `null`. If [parents] is passed,+  // it represents the parents of [compound2]. This is relevant for pseudo+  // selectors with selector arguments, where we may need to know if the+  // parent selectors in the selector argument match [parents].+  // ##########################################################################+  bool selectorPseudoIsSuperselector(+    const Pseudo_Selector_Obj& pseudo1,+    const CompoundSelectorObj& compound2,+    // ToDo: is this really the most convenient way to do this?+    std::vector<SelectorComponentObj>::const_iterator parents_from,+    std::vector<SelectorComponentObj>::const_iterator parents_to)+  {++    // ToDo: move normalization function+    std::string name(Util::unvendor(pseudo1->name()));++    if (name == "matches" || name == "any") {+      std::vector<Pseudo_Selector_Obj> pseudos =+        selectorPseudoNamed(compound2, pseudo1->name());+      SelectorListObj selector1 = pseudo1->selector();+      for (Pseudo_Selector_Obj pseudo2 : pseudos) {+        SelectorListObj selector = pseudo2->selector();+        if (selector1->isSuperselectorOf(selector)) {+          return true;+        }+      }++      for (ComplexSelectorObj complex1 : selector1->elements()) {+        std::vector<SelectorComponentObj> parents;+        for (auto cur = parents_from; cur != parents_to; cur++) {+          parents.push_back(*cur);+        }+        parents.push_back(compound2);+        if (complexIsSuperselector(complex1->elements(), parents)) {+          return true;+        }+      }++    }+    else if (name == "has" || name == "host" || name == "host-context" || name == "slotted") {+      std::vector<Pseudo_Selector_Obj> pseudos =+        selectorPseudoNamed(compound2, pseudo1->name());+      SelectorListObj selector1 = pseudo1->selector();+      for (Pseudo_Selector_Obj pseudo2 : pseudos) {+        SelectorListObj selector = pseudo2->selector();+        if (selector1->isSuperselectorOf(selector)) {+          return true;+        }+      }++    }+    else if (name == "not") {+      for (ComplexSelectorObj complex : pseudo1->selector()->elements()) {+        if (!pseudoNotIsSuperselectorOfCompound(pseudo1, compound2, complex)) return false;+      }+      return true;+    }+    else if (name == "current") {+      std::vector<Pseudo_Selector_Obj> pseudos =+        selectorPseudoNamed(compound2, "current");+      for (Pseudo_Selector_Obj pseudo2 : pseudos) {+        if (ObjEqualityFn(pseudo1, pseudo2)) return true;+      }++    }+    else if (name == "nth-child" || name == "nth-last-child") {+      for (auto simple2 : compound2->elements()) {+        if (Pseudo_Selector_Obj pseudo2 = simple2->getPseudoSelector()) {+          if (pseudo1->name() != pseudo2->name()) continue;+          if (!ObjEqualityFn(pseudo1->argument(), pseudo2->argument())) continue;+          if (pseudo1->selector()->isSuperselectorOf(pseudo2->selector())) return true;+        }+      }+      return false;+    }++    return false;++  }+  // EO selectorPseudoIsSuperselector++  // ##########################################################################+  // Returns whether [compound1] is a superselector of [compound2].+  // That is, whether [compound1] matches every element that [compound2]+  // matches, as well as possibly additional elements. If [parents] is+  // passed, it represents the parents of [compound2]. This is relevant+  // for pseudo selectors with selector arguments, where we may need to+  // know if the parent selectors in the selector argument match [parents].+  // ##########################################################################+  bool compoundIsSuperselector(+    const CompoundSelectorObj& compound1,+    const CompoundSelectorObj& compound2,+    // ToDo: is this really the most convenient way to do this?+    const std::vector<SelectorComponentObj>::const_iterator parents_from,+    const std::vector<SelectorComponentObj>::const_iterator parents_to)+  {+    // Every selector in [compound1.components] must have+    // a matching selector in [compound2.components].+    for (SimpleSelectorObj simple1 : compound1->elements()) {+      Pseudo_Selector_Obj pseudo1 = Cast<Pseudo_Selector>(simple1);+      if (pseudo1 && pseudo1->selector()) {+        if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) {+          return false;+        }+      }+      else if (!simpleIsSuperselectorOfCompound(simple1, compound2)) {+        return false;+      }+    }+    // [compound1] can't be a superselector of a selector+    // with pseudo-elements that [compound2] doesn't share.+    for (SimpleSelectorObj simple2 : compound2->elements()) {+      Pseudo_Selector_Obj pseudo2 = Cast<Pseudo_Selector>(simple2);+      if (pseudo2 && pseudo2->isElement()) {+        if (!simpleIsSuperselectorOfCompound(pseudo2, compound1)) {+          return false;+        }+      }+    }+    return true;+  }+  // EO compoundIsSuperselector++  // ##########################################################################+  // Returns whether [compound1] is a superselector of [compound2].+  // That is, whether [compound1] matches every element that [compound2]+  // matches, as well as possibly additional elements. If [parents] is+  // passed, it represents the parents of [compound2]. This is relevant+  // for pseudo selectors with selector arguments, where we may need to+  // know if the parent selectors in the selector argument match [parents].+  // ##########################################################################+  bool compoundIsSuperselector(+    const CompoundSelectorObj& compound1,+    const CompoundSelectorObj& compound2,+    const std::vector<SelectorComponentObj>& parents)+  {+    return compoundIsSuperselector(+      compound1, compound2,+      parents.begin(), parents.end()+    );+  }+  // EO compoundIsSuperselector++  // ##########################################################################+  // Returns whether [complex1] is a superselector of [complex2].+  // That is, whether [complex1] matches every element that+  // [complex2] matches, as well as possibly additional elements.+  // ##########################################################################+  bool complexIsSuperselector(+    const std::vector<SelectorComponentObj>& complex1,+    const std::vector<SelectorComponentObj>& complex2)+  {++    // Selectors with trailing operators are neither superselectors nor subselectors.+    if (!complex1.empty() && Cast<SelectorCombinator>(complex1.back())) return false;+    if (!complex2.empty() && Cast<SelectorCombinator>(complex2.back())) return false;++    size_t i1 = 0, i2 = 0;+    while (true) {++      size_t remaining1 = complex1.size() - i1;+      size_t remaining2 = complex2.size() - i2;++      if (remaining1 == 0 || remaining2 == 0) {+        return false;+      }+      // More complex selectors are never+      // superselectors of less complex ones.+      if (remaining1 > remaining2) {+        return false;+      }++      // Selectors with leading operators are+      // neither superselectors nor subselectors.+      if (Cast<SelectorCombinator>(complex1[i1])) {+        return false;+      }+      if (Cast<SelectorCombinator>(complex2[i2])) {+        return false;+      }++      CompoundSelectorObj compound1 = Cast<CompoundSelector>(complex1[i1]);+      CompoundSelectorObj compound2 = Cast<CompoundSelector>(complex2.back());++      if (remaining1 == 1) {+        std::vector<SelectorComponentObj>::const_iterator parents_to = complex2.end();+        std::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin();+        std::advance(parents_from, i2 + 1); // equivalent to dart `.skip(i2 + 1)`+        bool rv = compoundIsSuperselector(compound1, compound2, parents_from, parents_to);+        std::vector<SelectorComponentObj> pp;++        std::vector<SelectorComponentObj>::const_iterator end = parents_to;+        std::vector<SelectorComponentObj>::const_iterator beg = parents_from;+        while (beg != end) {+          pp.push_back(*beg);+          beg++;+        }++        return rv;+      }++      // Find the first index where `complex2.sublist(i2, afterSuperselector)`+      // is a subselector of [compound1]. We stop before the superselector+      // would encompass all of [complex2] because we know [complex1] has +      // more than one element, and consuming all of [complex2] wouldn't +      // leave anything for the rest of [complex1] to match.+      size_t afterSuperselector = i2 + 1;+      for (; afterSuperselector < complex2.size(); afterSuperselector++) {+        SelectorComponentObj component2 = complex2[afterSuperselector - 1];+        if (CompoundSelectorObj compound2 = Cast<CompoundSelector>(component2)) {+          std::vector<SelectorComponentObj>::const_iterator parents_to = complex2.begin();+          std::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin();+          // complex2.take(afterSuperselector - 1).skip(i2 + 1)+          std::advance(parents_from, i2 + 1); // equivalent to dart `.skip`+          std::advance(parents_to, afterSuperselector); // equivalent to dart `.take`+          if (compoundIsSuperselector(compound1, compound2, parents_from, parents_to)) {+            break;+          }+        }+      }+      if (afterSuperselector == complex2.size()) {+        return false;+      }++      SelectorComponentObj component1 = complex1[i1 + 1],+        component2 = complex2[afterSuperselector];++      SelectorCombinatorObj combinator1 = Cast<SelectorCombinator>(component1);+      SelectorCombinatorObj combinator2 = Cast<SelectorCombinator>(component2);++      if (!combinator1.isNull()) {++        if (combinator2.isNull()) {+          return false;+        }+        // `.a ~ .b` is a superselector of `.a + .b`,+        // but otherwise the combinators must match.+        if (combinator1->isGeneralCombinator()) {+          if (combinator2->isChildCombinator()) {+            return false;+          }+        }+        else if (*combinator1 != *combinator2) {+          return false;+        }++        // `.foo > .baz` is not a superselector of `.foo > .bar > .baz` or+        // `.foo > .bar .baz`, despite the fact that `.baz` is a superselector of+        // `.bar > .baz` and `.bar .baz`. Same goes for `+` and `~`.+        if (remaining1 == 3 && remaining2 > 3) {+          return false;+        }++        i1 += 2; i2 = afterSuperselector + 1;++      }+      else if (!combinator2.isNull()) {+        if (!combinator2->isChildCombinator()) {+          return false;+        }+        i1 += 1; i2 = afterSuperselector + 1;+      }+      else {+        i1 += 1; i2 = afterSuperselector;+      }+    }++    return false;++  }+  // EO complexIsSuperselector++  // ##########################################################################+  // Like [complexIsSuperselector], but compares [complex1]+  // and [complex2] as though they shared an implicit base+  // [SimpleSelector]. For example, `B` is not normally a+  // superselector of `B A`, since it doesn't match elements+  // that match `A`. However, it *is* a parent superselector,+  // since `B X` is a superselector of `B A X`.+  // ##########################################################################+  bool complexIsParentSuperselector(+    const std::vector<SelectorComponentObj>& complex1,+    const std::vector<SelectorComponentObj>& complex2)+  {+    // Try some simple heuristics to see if we can avoid allocations.+    if (complex1.empty() && complex2.empty()) return false;+    if (Cast<SelectorCombinator>(complex1.front())) return false;+    if (Cast<SelectorCombinator>(complex2.front())) return false;+    if (complex1.size() > complex2.size()) return false;+    // TODO(nweiz): There's got to be a way to do this without a bunch of extra allocations...+    std::vector<SelectorComponentObj> cplx1(complex1);+    std::vector<SelectorComponentObj> cplx2(complex2);+    CompoundSelectorObj base = SASS_MEMORY_NEW(CompoundSelector, "[tmp]");+    cplx1.push_back(base); cplx2.push_back(base);+    return complexIsSuperselector(cplx1, cplx2);+  }+  // EO complexIsParentSuperselector++  // ##########################################################################+  // Returns whether [list] has a superselector for [complex].+  // That is, whether an item in [list] matches every element that+  // [complex] matches, as well as possibly additional elements.+  // ##########################################################################+  bool listHasSuperslectorForComplex(+    std::vector<ComplexSelectorObj> list,+    ComplexSelectorObj complex)+  {+    // Return true if every [complex] selector on [list2]+    // is a super selector of the full selector [list1].+    for (ComplexSelectorObj lhs : list) {+      if (complexIsSuperselector(lhs->elements(), complex->elements())) {+        return true;+      }+    }+    return false;+  }+  // listIsSuperslectorOfComplex++  // ##########################################################################+  // Returns whether [list1] is a superselector of [list2].+  // That is, whether [list1] matches every element that+  // [list2] matches, as well as possibly additional elements.+  // ##########################################################################+  bool listIsSuperslector(+    const std::vector<ComplexSelectorObj>& list1,+    const std::vector<ComplexSelectorObj>& list2)+  {+    // Return true if every [complex] selector on [list2]+    // is a super selector of the full selector [list1].+    for (ComplexSelectorObj complex : list2) {+      if (!listHasSuperslectorForComplex(list1, complex)) {+        return false;+      }+    }+    return true;+  }+  // EO listIsSuperslector++  // ##########################################################################+  // Implement selector methods (dispatch to functions)+  // ##########################################################################+  bool SelectorList::isSuperselectorOf(const SelectorList* sub) const+  {+    return listIsSuperslector(elements(), sub->elements());+  }+  bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const+  {+    return complexIsSuperselector(elements(), sub->elements());+  }++  // ##########################################################################+  // ##########################################################################++}
+ libsass/src/ast_sel_unify.cpp view
@@ -0,0 +1,275 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"++namespace Sass {++  // ##########################################################################+  // Returns the contents of a [SelectorList] that matches only +  // elements that are matched by both [complex1] and [complex2].+  // If no such list can be produced, returns `null`.+  // ##########################################################################+  // ToDo: fine-tune API to avoid unnecessary wrapper allocations+  // ##########################################################################+  std::vector<std::vector<SelectorComponentObj>> unifyComplex(+    const std::vector<std::vector<SelectorComponentObj>>& complexes)+  {++    SASS_ASSERT(!complexes.empty(), "Can't unify empty list");+    if (complexes.size() == 1) return complexes;++    CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, ParserState("[phony]"));+    for (auto complex : complexes) {+      SelectorComponentObj base = complex.back();+      if (CompoundSelector * comp = base->getCompound()) {+        if (unifiedBase->empty()) {+          unifiedBase->concat(comp);+        }+        else {+          for (SimpleSelectorObj simple : comp->elements()) {+            unifiedBase = simple->unifyWith(unifiedBase);+            if (unifiedBase.isNull()) return {};+          }+        }+      }+      else {+        return {};+      }+    }++    std::vector<std::vector<SelectorComponentObj>> complexesWithoutBases;+    for (size_t i = 0; i < complexes.size(); i += 1) {+      std::vector<SelectorComponentObj> sel = complexes[i];+      sel.pop_back(); // remove last item (base) from the list+      complexesWithoutBases.push_back(std::move(sel));+    }++    complexesWithoutBases.back().push_back(unifiedBase);++    return weave(complexesWithoutBases);++  }+  // EO unifyComplex++  // ##########################################################################+  // Returns a [CompoundSelector] that matches only elements+  // that are matched by both [compound1] and [compound2].+  // If no such selector can be produced, returns `null`.+  // ##########################################################################+  CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs)+  {+    if (empty()) return rhs;+    CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs);+    for (const SimpleSelectorObj& sel : elements()) {+      unified = sel->unifyWith(unified);+      if (unified.isNull()) break;+    }+    return unified.detach();+  }+  // EO CompoundSelector::unifyWith(CompoundSelector*)++  // ##########################################################################+  // Returns the compoments of a [CompoundSelector] that matches only elements+  // matched by both this and [compound]. By default, this just returns a copy+  // of [compound] with this selector added to the end, or returns the original+  // array if this selector already exists in it. Returns `null` if unification+  // is impossible—for example, if there are multiple ID selectors.+  // ##########################################################################+  // This is implemented in `selector/simple.dart` as `SimpleSelector::unify`+  // ##########################################################################+  CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs)+  {++    if (rhs->length() == 1) {+      if (rhs->get(0)->is_universal()) {+        CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate());+        this_compound->append(SASS_MEMORY_COPY(this));+        CompoundSelector* unified = rhs->get(0)->unifyWith(this_compound);+        if (unified == nullptr || unified != this_compound) delete this_compound;+        return unified;+      }+    }+    for (const SimpleSelectorObj& sel : rhs->elements()) {+      if (*this == *sel) {+        return rhs;+      }+    }++    CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate());++    bool addedThis = false;+    for (auto simple : rhs->elements()) {+      // Make sure pseudo selectors always come last.+      if (!addedThis && simple->getPseudoSelector()) {+        result->append(this);+        addedThis = true;+      }+      result->append(simple);+    }++    if (!addedThis) {+      result->append(this);+    }+    return result.detach();++  }+  // EO SimpleSelector::unifyWith(CompoundSelector*)++  // ##########################################################################+  // This is implemented in `selector/type.dart` as `PseudoSelector::unify`+  // ##########################################################################+  CompoundSelector* Type_Selector::unifyWith(CompoundSelector* rhs)+  {+    if (rhs->empty()) {+      rhs->append(this);+      return rhs;+    }+    Type_Selector* type = Cast<Type_Selector>(rhs->at(0));+    if (type != nullptr) {+      SimpleSelector* unified = unifyWith(type);+      if (unified == nullptr) {+        return nullptr;+      }+      rhs->elements()[0] = unified;+    }+    else if (!is_universal() || (has_ns_ && ns_ != "*")) {+      rhs->insert(rhs->begin(), this);+    }+    return rhs;+  }++  // ##########################################################################+  // This is implemented in `selector/id.dart` as `PseudoSelector::unify`+  // ##########################################################################+  CompoundSelector* Id_Selector::unifyWith(CompoundSelector* rhs)+  {+    for (const SimpleSelector* sel : rhs->elements()) {+      if (const Id_Selector* id_sel = Cast<Id_Selector>(sel)) {+        if (id_sel->name() != name()) return nullptr;+      }+    }+    return SimpleSelector::unifyWith(rhs);+  }++  // ##########################################################################+  // This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify`+  // ##########################################################################+  CompoundSelector* Pseudo_Selector::unifyWith(CompoundSelector* compound)+  {++    if (compound->length() == 1 && compound->first()->is_universal()) {+      // std::cerr << "implement universal pseudo\n";+    }++    for (const SimpleSelectorObj& sel : compound->elements()) {+      if (*this == *sel) {+        return compound;+      }+    }++    CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate());++    bool addedThis = false;+    for (auto simple : compound->elements()) {+      // Make sure pseudo selectors always come last.+      if (Pseudo_Selector_Obj pseudo = simple->getPseudoSelector()) {+        if (pseudo->isElement()) {+          // A given compound selector may only contain one pseudo element. If+          // [compound] has a different one than [this], unification fails.+          if (isElement()) {+            return {};+          }+          // Otherwise, this is a pseudo selector and+          // should come before pseduo elements.+          result->append(this);+          addedThis = true;+        }+      }+      result->append(simple);+    }++    if (!addedThis) {+      result->append(this);+    }++    return result.detach();++  }+  // EO Pseudo_Selector::unifyWith(CompoundSelector*++  // ##########################################################################+  // This is implemented in `extend/functions.dart` as `unifyUniversalAndElement`+  // Returns a [SimpleSelector] that matches only elements that are matched by+  // both [selector1] and [selector2], which must both be either [UniversalSelector]s+  // or [TypeSelector]s. If no such selector can be produced, returns `null`.+  // Note: libsass handles universal selector directly within the type selector+  // ##########################################################################+  SimpleSelector* Type_Selector::unifyWith(const SimpleSelector* rhs)+  {+    bool rhs_ns = false;+    if (!(is_ns_eq(*rhs) || rhs->is_universal_ns())) {+      if (!is_universal_ns()) {+        return nullptr;+      }+      rhs_ns = true;+    }+    bool rhs_name = false;+    if (!(name_ == rhs->name() || rhs->is_universal())) {+      if (!(is_universal())) {+        return nullptr;+      }+      rhs_name = true;+    }+    if (rhs_ns) {+      ns(rhs->ns());+      has_ns(rhs->has_ns());+    }+    if (rhs_name) name(rhs->name());+    return this;+  }+  // EO Type_Selector::unifyWith(const SimpleSelector*)++  // ##########################################################################+  // Unify two complex selectors. Internally calls `unifyComplex`+  // and then wraps the result in newly create ComplexSelectors.+  // ##########################################################################+  SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs)+  {+    SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate());+    std::vector<std::vector<SelectorComponentObj>> rv =+       unifyComplex({ elements(), rhs->elements() });+    for (std::vector<SelectorComponentObj> items : rv) {+      ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate());+      sel->elements() = std::move(items);+      list->append(sel);+    }+    return list.detach();+  }+  // EO ComplexSelector::unifyWith(ComplexSelector*)++  // ##########################################################################+  // only called from the sass function `selector-unify`+  // ##########################################################################+  SelectorList* SelectorList::unifyWith(SelectorList* rhs)+  {+    SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate());+    // Unify all of children with RHS's children,+    // storing the results in `unified_complex_selectors`+    for (ComplexSelectorObj& seq1 : elements()) {+      for (ComplexSelectorObj& seq2 : rhs->elements()) {+        if (SelectorListObj unified = seq1->unifyWith(seq2)) {+          std::move(unified->begin(), unified->end(),+            std::inserter(slist->elements(), slist->end()));+        }+      }+    }+    return slist;+  }+  // EO SelectorList::unifyWith(SelectorList*)++  // ##########################################################################+  // ##########################################################################++}
+ libsass/src/ast_sel_weave.cpp view
@@ -0,0 +1,616 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"+#include "permutate.hpp"+#include "dart_helpers.hpp"++namespace Sass {++  // ##########################################################################+  // Returns whether or not [compound] contains a `::root` selector.+  // ##########################################################################+  bool hasRoot(const CompoundSelector* compound)+  {+    // Libsass does not yet know the root selector+    return false;+  }+  // EO hasRoot++  // ##########################################################################+  // Returns whether a [CompoundSelector] may contain only+  // one simple selector of the same type as [simple].+  // ##########################################################################+  bool isUnique(const SimpleSelector* simple)+  {+    if (Cast<Id_Selector>(simple)) return true;+    if (const Pseudo_Selector * pseudo = Cast<Pseudo_Selector>(simple)) {+      if (pseudo->is_pseudo_element()) return true;+    }+    return false;+  }+  // EO isUnique++  // ##########################################################################+  // Returns whether [complex1] and [complex2] need to be unified to+  // produce a valid combined selector. This is necessary when both+  // selectors contain the same unique simple selector, such as an ID.+  // ##########################################################################+  bool mustUnify(+    const std::vector<SelectorComponentObj>& complex1,+    const std::vector<SelectorComponentObj>& complex2)+  {++    std::vector<const SimpleSelector*> uniqueSelectors1;+    for (const SelectorComponent* component : complex1) {+      if (const CompoundSelector * compound = component->getCompound()) {+        for (const SimpleSelector* sel : compound->elements()) {+          if (isUnique(sel)) {+            uniqueSelectors1.push_back(sel);+          }+        }+      }+    }+    if (uniqueSelectors1.empty()) return false;++    // ToDo: unsure if this is correct+    for (const SelectorComponent* component : complex2) {+      if (const CompoundSelector * compound = component->getCompound()) {+        for (const SimpleSelector* sel : compound->elements()) {+          if (isUnique(sel)) {+            for (auto check : uniqueSelectors1) {+              if (*check == *sel) return true;+            }+          }+        }+      }+    }++    return false;++  }+  // EO isUnique++  // ##########################################################################+  // Helper function used by `weaveParents`+  // ##########################################################################+  bool cmpGroups(+    const std::vector<SelectorComponentObj>& group1,+    const std::vector<SelectorComponentObj>& group2,+    std::vector<SelectorComponentObj>& select)+  {++    if (group1.size() == group2.size() && std::equal(group1.begin(), group1.end(), group2.begin(), PtrObjEqualityFn<SelectorComponent>)) {+      select = group1;+      return true;+    }++    if (!Cast<CompoundSelector>(group1.front())) {+      select = {};+      return false;+    }+    if (!Cast<CompoundSelector>(group2.front())) {+      select = {};+      return false;+    }++    if (complexIsParentSuperselector(group1, group2)) {+      select = group2;+      return true;+    }+    if (complexIsParentSuperselector(group2, group1)) {+      select = group1;+      return true;+    }++    if (!mustUnify(group1, group2)) {+      select = {};+      return false;+    }++    std::vector<std::vector<SelectorComponentObj>> unified+      = unifyComplex({ group1, group2 });+    if (unified.empty()) return false;+    if (unified.size() > 1) return false;+    select = unified.front();+    return true;+  }+  // EO cmpGroups++  // ##########################################################################+  // Helper function used by `weaveParents`+  // ##########################################################################+  template <class T>+  bool checkForEmptyChild(const T& item) {+    return item.empty();+  }+  // EO checkForEmptyChild++  // ##########################################################################+  // Helper function used by `weaveParents`+  // ##########################################################################+  bool cmpChunkForEmptySequence(+    const std::vector<std::vector<SelectorComponentObj>>& seq,+    const std::vector<SelectorComponentObj>& group)+  {+    return seq.empty();+  }+  // EO cmpChunkForEmptySequence++  // ##########################################################################+  // Helper function used by `weaveParents`+  // ##########################################################################+  bool cmpChunkForParentSuperselector(+    const std::vector< std::vector<SelectorComponentObj>>& seq,+    const std::vector<SelectorComponentObj>& group)+  {+    return seq.empty() || complexIsParentSuperselector(seq.front(), group);+  }+   // EO cmpChunkForParentSuperselector++  // ##########################################################################+  // Returns all orderings of initial subseqeuences of [queue1] and [queue2].+  // The [done] callback is used to determine the extent of the initial+  // subsequences. It's called with each queue until it returns `true`.+  // Destructively removes the initial subsequences of [queue1] and [queue2].+  // For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|` denoting+  // the boundary of the initial subsequence), this would return `[(A B C 1 2),+  // (1 2 A B C)]`. The queues would then contain `(D E)` and `(3 4 5)`.+  // ##########################################################################+  template <class T>+  std::vector<std::vector<T>> getChunks(+    std::vector<T>& queue1, std::vector<T>& queue2,+    const T& group, bool(*done)(const std::vector<T>&, const T&)+  ) {++    std::vector<T> chunk1;+    while (!done(queue1, group)) {+      chunk1.push_back(queue1.front());+      queue1.erase(queue1.begin());+    }++    std::vector<T> chunk2;+    while (!done(queue2, group)) {+      chunk2.push_back(queue2.front());+      queue2.erase(queue2.begin());+    }++    if (chunk1.empty() && chunk2.empty()) return {};+    else if (chunk1.empty()) return { chunk2 };+    else if (chunk2.empty()) return { chunk1 };++    std::vector<T> choice1(chunk1), choice2(chunk2);+    std::move(std::begin(chunk2), std::end(chunk2),+      std::inserter(choice1, std::end(choice1)));+    std::move(std::begin(chunk1), std::end(chunk1),+      std::inserter(choice2, std::end(choice2)));+    return { choice1, choice2 };+  }+  // EO getChunks++  // ##########################################################################+  // If the first element of [queue] has a `::root` +  // selector, removes and returns that element.+  // ##########################################################################+  CompoundSelectorObj getFirstIfRoot(std::vector<SelectorComponentObj>& queue) {+    if (queue.empty()) return {};+    SelectorComponent* first = queue.front();+    if (CompoundSelector* sel = Cast<CompoundSelector>(first)) {+      if (!hasRoot(sel)) return {};+      queue.erase(queue.begin());+      return sel;+    }+    return {};+  }+  // EO getFirstIfRoot++  // ##########################################################################+  // Returns [complex], grouped into sub-lists such that no sub-list+  // contains two adjacent [ComplexSelector]s. For example,+  // `(A B > C D + E ~ > G)` is grouped into `[(A) (B > C) (D + E ~ > G)]`.+  // ##########################################################################+  std::vector<std::vector<SelectorComponentObj>> groupSelectors(+    const std::vector<SelectorComponentObj>& components)+  {+    bool lastWasCompound = false;+    std::vector<SelectorComponentObj> group;+    std::vector<std::vector<SelectorComponentObj>> groups;+    for (size_t i = 0; i < components.size(); i += 1) {+      if (CompoundSelector* compound = components[i]->getCompound()) {+        if (lastWasCompound) {+          groups.push_back(group);+          group.clear();+        }+        group.push_back(compound);+        lastWasCompound = true;+      }+      else if (SelectorCombinator* combinator = components[i]->getCombinator()) {+        group.push_back(combinator);+        lastWasCompound = false;+      }+    }+    if (!group.empty()) {+      groups.push_back(group);+    }+    return groups;+  }+  // EO groupSelectors++  // ##########################################################################+  // Extracts leading [Combinator]s from [components1] and [components2]+  // and merges them together into a single list of combinators.+  // If there are no combinators to be merged, returns an empty list.+  // If the combinators can't be merged, returns `null`.+  // ##########################################################################+  bool mergeInitialCombinators(+    std::vector<SelectorComponentObj>& components1,+    std::vector<SelectorComponentObj>& components2,+    std::vector<SelectorComponentObj>& result)+  {++    std::vector<SelectorComponentObj> combinators1;+    while (!components1.empty() && Cast<SelectorCombinator>(components1.front())) {+      SelectorCombinatorObj front = Cast<SelectorCombinator>(components1.front());+      components1.erase(components1.begin());+      combinators1.push_back(front);+    }++    std::vector<SelectorComponentObj> combinators2;+    while (!components2.empty() && Cast<SelectorCombinator>(components2.front())) {+      SelectorCombinatorObj front = Cast<SelectorCombinator>(components2.front());+      components2.erase(components2.begin());+      combinators2.push_back(front);+    }++    // If neither sequence of combinators is a subsequence+    // of the other, they cannot be merged successfully.+    std::vector<SelectorComponentObj> LCS = lcs<SelectorComponentObj>(combinators1, combinators2);++    if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) {+      result = combinators2;+      return true;+    }+    if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) {+      result = combinators1;+      return true;+    }++    return false;++  }+  // EO mergeInitialCombinators++  // ##########################################################################+  // Extracts trailing [Combinator]s, and the selectors to which they apply,+  // from [components1] and [components2] and merges them together into a+  // single list. If there are no combinators to be merged, returns an+  // empty list. If the sequences can't be merged, returns `null`.+  // ##########################################################################+  bool mergeFinalCombinators(+    std::vector<SelectorComponentObj>& components1,+    std::vector<SelectorComponentObj>& components2,+    std::vector<std::vector<std::vector<SelectorComponentObj>>>& result)+  {++    if (components1.empty() || !Cast<SelectorCombinator>(components1.back())) {+      if (components2.empty() || !Cast<SelectorCombinator>(components2.back())) {+        return true;+      }+    }+    +    std::vector<SelectorComponentObj> combinators1;+    while (!components1.empty() && Cast<SelectorCombinator>(components1.back())) {+      SelectorCombinatorObj back = Cast<SelectorCombinator>(components1.back());+      components1.erase(components1.end() - 1);+      combinators1.push_back(back);+    }++    std::vector<SelectorComponentObj> combinators2;+    while (!components2.empty() && Cast<SelectorCombinator>(components2.back())) {+      SelectorCombinatorObj back = Cast<SelectorCombinator>(components2.back());+      components2.erase(components2.end() - 1);+      combinators2.push_back(back);+    }++    // reverse now as we used push_back (faster than new alloc)+    std::reverse(combinators1.begin(), combinators1.end());+    std::reverse(combinators2.begin(), combinators2.end());++    if (combinators1.size() > 1 || combinators2.size() > 1) {+      // If there are multiple combinators, something hacky's going on. If one+      // is a supersequence of the other, use that, otherwise give up.+      auto LCS = lcs<SelectorComponentObj>(combinators1, combinators2);+      if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) {+        result.push_back({ combinators2 });+      }+      else if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) {+        result.push_back({ combinators1 });+      }+      else {+        return false;+      }+      return true;+    }++    // This code looks complicated, but it's actually just a bunch of special+    // cases for interactions between different combinators.+    SelectorCombinatorObj combinator1, combinator2;+    if (!combinators1.empty()) combinator1 = combinators1.back();+    if (!combinators2.empty()) combinator2 = combinators2.back();++    if (!combinator1.isNull() && !combinator2.isNull()) {++      CompoundSelector* compound1 = Cast<CompoundSelector>(components1.back());+      CompoundSelector* compound2 = Cast<CompoundSelector>(components2.back());++      components1.pop_back();+      components2.pop_back();++      if (combinator1->isGeneralCombinator() && combinator2->isGeneralCombinator()) {++        if (compound1->isSuperselectorOf(compound2)) {+          result.push_back({ { compound2, combinator2 } });+        }+        else if (compound2->isSuperselectorOf(compound1)) {+          result.push_back({ { compound1, combinator1 } });+        }+        else {+          std::vector<std::vector<SelectorComponentObj>> choices;+          choices.push_back({ compound1, combinator1, compound2, combinator2 });+          choices.push_back({ compound2, combinator2, compound1, combinator1 });+          if (CompoundSelector* unified = compound1->unifyWith(compound2)) {+            choices.push_back({ unified, combinator1 });+          }+          result.push_back(choices);+        }+      }+      else if ((combinator1->isGeneralCombinator() && combinator2->isAdjacentCombinator()) ||+        (combinator1->isAdjacentCombinator() && combinator2->isGeneralCombinator())) {++        CompoundSelector* followingSiblingSelector = combinator1->isGeneralCombinator() ? compound1 : compound2;+        CompoundSelector* nextSiblingSelector = combinator1->isGeneralCombinator() ? compound2 : compound1;+        SelectorCombinator* followingSiblingCombinator = combinator1->isGeneralCombinator() ? combinator1 : combinator2;+        SelectorCombinator* nextSiblingCombinator = combinator1->isGeneralCombinator() ? combinator2 : combinator1;++        if (followingSiblingSelector->isSuperselectorOf(nextSiblingSelector)) {+          result.push_back({ { nextSiblingSelector, nextSiblingCombinator } });+        }+        else {+          CompoundSelectorObj unified = compound1->unifyWith(compound2);+          std::vector<std::vector<SelectorComponentObj>> items;+          +          if (!unified.isNull()) {+            items.push_back({+              unified, nextSiblingCombinator+            });+          }++          items.insert(items.begin(), {+            followingSiblingSelector,+            followingSiblingCombinator,+            nextSiblingSelector,+            nextSiblingCombinator,+          });++          result.push_back(items);+        }++      }+      else if (combinator1->isChildCombinator() && (combinator2->isAdjacentCombinator() || combinator2->isGeneralCombinator())) {+        result.push_back({ { compound2, combinator2 } });+        components1.push_back(compound1);+        components1.push_back(combinator1);+      }+      else if (combinator2->isChildCombinator() && (combinator1->isAdjacentCombinator() || combinator1->isGeneralCombinator())) {+        result.push_back({ { compound1, combinator1 } });+        components2.push_back(compound2);+        components2.push_back(combinator2);+      }+      else if (*combinator1 == *combinator2) {+        CompoundSelectorObj unified = compound1->unifyWith(compound2);+        if (unified.isNull()) return false;+        result.push_back({ { unified, combinator1 } });+      }+      else {+        return false;+      }++      return mergeFinalCombinators(components1, components2, result);++    }+    else if (!combinator1.isNull()) {++      if (combinator1->isChildCombinator() && !components2.empty()) {+        const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back());+        const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back());+        if (back1 && back2 && back2->isSuperselectorOf(back1)) {+          components2.pop_back();+        }+      }++      result.push_back({ { components1.back(), combinator1 } });++      components1.pop_back();++      return mergeFinalCombinators(components1, components2, result);++    }++    if (combinator2->isChildCombinator() && !components1.empty()) {+      const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back());+      const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back());+      if (back1 && back2 && back1->isSuperselectorOf(back2)) {+        components1.pop_back();+      }+    }++    result.push_back({ { components2.back(), combinator2 } });++    components2.pop_back();++    return mergeFinalCombinators(components1, components2, result);++  }+  // EO mergeFinalCombinators++  // ##########################################################################+  // Expands "parenthesized selectors" in [complexes]. That is, if+  // we have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually+  // expands into `.D .C, .D (.A .B)`, and this function translates+  // `.D (.A .B)` into `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B`+  // would also be required, but including merged selectors results in+  // exponential output for very little gain. The selector `.D (.A .B)`+  // is represented as the list `[[.D], [.A, .B]]`.+  // ##########################################################################+  std::vector<std::vector<SelectorComponentObj>> weave(+    const std::vector<std::vector<SelectorComponentObj>>& complexes) {++    std::vector<std::vector<SelectorComponentObj>> prefixes;++    prefixes.push_back(complexes.at(0));++    for (size_t i = 1; i < complexes.size(); i += 1) {++      if (complexes[i].empty()) {+        continue;+      }+      const std::vector<SelectorComponentObj>& complex = complexes[i];+      SelectorComponent* target = complex.back();+      if (complex.size() == 1) {+        for (auto& prefix : prefixes) {+          prefix.push_back(target);+        }+        continue;+      }++      std::vector<SelectorComponentObj> parents(complex);++      parents.pop_back();++      std::vector<std::vector<SelectorComponentObj>> newPrefixes;+      for (std::vector<SelectorComponentObj> prefix : prefixes) {+        std::vector<std::vector<SelectorComponentObj>>+          parentPrefixes = weaveParents(prefix, parents);+        if (parentPrefixes.empty()) continue;+        for (auto& parentPrefix : parentPrefixes) {+          parentPrefix.push_back(target);+          newPrefixes.push_back(parentPrefix);+        }+      }+      prefixes = newPrefixes;++    }+    return prefixes;++  }+  // EO weave++  // ##########################################################################+  // Interweaves [parents1] and [parents2] as parents of the same target+  // selector. Returns all possible orderings of the selectors in the+  // inputs (including using unification) that maintain the relative+  // ordering of the input. For example, given `.foo .bar` and `.baz .bang`,+  // this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`,+  // `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,+  // and so on until `.baz .bang .foo .bar`. Semantically, for selectors A+  // and B, this returns all selectors `AB_i` such that the union over all i+  // of elements matched by `AB_i X` is identical to the intersection of all+  // elements matched by `A X` and all elements matched by `B X`. Some `AB_i`+  // are elided to reduce the size of the output.+  // ##########################################################################+  std::vector<std::vector<SelectorComponentObj>> weaveParents(+    std::vector<SelectorComponentObj> queue1,+    std::vector<SelectorComponentObj> queue2)+  {++    std::vector<SelectorComponentObj> leads;+    std::vector<std::vector<std::vector<SelectorComponentObj>>> trails;+    if (!mergeInitialCombinators(queue1, queue2, leads)) return {};+    if (!mergeFinalCombinators(queue1, queue2, trails)) return {};+    // list comes out in reverse order for performance+    std::reverse(trails.begin(), trails.end());++    // Make sure there's at most one `:root` in the output.+    // Note: does not yet do anything in libsass (no root selector)+    CompoundSelectorObj root1 = getFirstIfRoot(queue1);+    CompoundSelectorObj root2 = getFirstIfRoot(queue2);++    if (!root1.isNull() && !root2.isNull()) {+      CompoundSelectorObj root = root1->unifyWith(root2);+      if (root.isNull()) return {}; // null+      queue1.insert(queue1.begin(), root);+      queue2.insert(queue2.begin(), root);+    }+    else if (!root1.isNull()) {+      queue2.insert(queue2.begin(), root1);+    }+    else if (!root2.isNull()) {+      queue1.insert(queue1.begin(), root2);+    }++    // group into sub-lists so no sub-list contains two adjacent ComplexSelectors.+    std::vector<std::vector<SelectorComponentObj>> groups1 = groupSelectors(queue1);+    std::vector<std::vector<SelectorComponentObj>> groups2 = groupSelectors(queue2);++    // The main array to store our choices that will be permutated+    std::vector<std::vector<std::vector<SelectorComponentObj>>> choices;++    // append initial combinators+    choices.push_back({ leads });++    std::vector<std::vector<SelectorComponentObj>> LCS =+      lcs<std::vector<SelectorComponentObj>>(groups1, groups2, cmpGroups);++    for (auto group : LCS) {++      // Create junks from groups1 and groups2+      std::vector<std::vector<std::vector<SelectorComponentObj>>>+        chunks = getChunks<std::vector<SelectorComponentObj>>(+          groups1, groups2, group, cmpChunkForParentSuperselector);++      // Create expanded array by flattening chunks2 inner+      std::vector<std::vector<SelectorComponentObj>>+        expanded = flattenInner(chunks);++      // Prepare data structures+      choices.push_back(expanded);+      choices.push_back({ group });+      if (!groups1.empty()) {+        groups1.erase(groups1.begin());+      }+      if (!groups2.empty()) {+        groups2.erase(groups2.begin());+      }++    }++    // Create junks from groups1 and groups2+    std::vector<std::vector<std::vector<SelectorComponentObj>>>+      chunks = getChunks<std::vector<SelectorComponentObj>>(+        groups1, groups2, {}, cmpChunkForEmptySequence);++    // Append chunks with inner arrays flattened+    choices.emplace_back(flattenInner(chunks));++    // append all trailing selectors to choices+    std::move(std::begin(trails), std::end(trails),+      std::inserter(choices, std::end(choices)));++    // move all non empty items to the front, then erase the trailing ones+    choices.erase(std::remove_if(choices.begin(), choices.end(), checkForEmptyChild+      <std::vector<std::vector<SelectorComponentObj>>>), choices.end());++    // permutate all possible paths through selectors+    std::vector<std::vector<SelectorComponentObj>>+      results = flattenInner(permutate(choices));++    return results;++  }+  // EO weaveParents++  // ##########################################################################+  // ##########################################################################++}
+ libsass/src/ast_selectors.cpp view
@@ -0,0 +1,1033 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"+#include "permutate.hpp"+#include "util_string.hpp"++namespace Sass {++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Selector::Selector(ParserState pstate)+  : Expression(pstate),+    hash_(0)+  { concrete_type(SELECTOR); }++  Selector::Selector(const Selector* ptr)+  : Expression(ptr),+    hash_(ptr->hash_)+  { concrete_type(SELECTOR); }+++  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),+    hash_(0)+  { }+  Selector_Schema::Selector_Schema(const Selector_Schema* ptr)+  : AST_Node(ptr),+    contents_(ptr->contents_),+    connect_parent_(ptr->connect_parent_),+    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());+    }+    return hash_;+  }++  bool Selector_Schema::has_real_parent_ref() const+  {+    // Note: disabled since it does not seem to do anything?+    // if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {+    // if (schema->empty()) return false;+    // const auto first = schema->first();+    // return Cast<Parent_Reference>(first);+    // }+    return false;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  SimpleSelector::SimpleSelector(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);+    }+  }+  SimpleSelector::SimpleSelector(const SimpleSelector* ptr)+  : Selector(ptr),+    ns_(ptr->ns_),+    name_(ptr->name_),+    has_ns_(ptr->has_ns_)+  { }++  std::string SimpleSelector::ns_name() const+  {+    if (!has_ns_) return name_;+    else return ns_ + "|" + name_;+  }++  size_t SimpleSelector::hash() const+  {+    if (hash_ == 0) {+      hash_combine(hash_, name());+      hash_combine(hash_, (int)SELECTOR);+      hash_combine(hash_, (int)simple_type());+      if (has_ns_) hash_combine(hash_, ns());+    }+    return hash_;+  }++  bool SimpleSelector::empty() const {+    return ns().empty() && name().empty();+  }++  // namespace compare functions+  bool SimpleSelector::is_ns_eq(const SimpleSelector& r) const+  {+    return has_ns_ == r.has_ns_ && ns_ == r.ns_;+  }++  // namespace query functions+  bool SimpleSelector::is_universal_ns() const+  {+    return has_ns_ && ns_ == "*";+  }++  bool SimpleSelector::is_empty_ns() const+  {+    return !has_ns_ || ns_ == "";+  }++  bool SimpleSelector::has_empty_ns() const+  {+    return has_ns_ && ns_ == "";+  }++  bool SimpleSelector::has_qualified_ns() const+  {+    return has_ns_ && ns_ != "" && ns_ != "*";+  }++  // name query functions+  bool SimpleSelector::is_universal() const+  {+    return name_ == "*";+  }++  bool SimpleSelector::has_placeholder()+  {+    return false;+  }++  bool SimpleSelector::has_real_parent_ref() const+  {+    return false;+  };++  bool SimpleSelector::is_pseudo_element() const+  {+    return false;+  }++  CompoundSelectorObj SimpleSelector::wrapInCompound()+  {+    CompoundSelectorObj selector =+      SASS_MEMORY_NEW(CompoundSelector, pstate());+    selector->append(this);+    return selector;+  }+  ComplexSelectorObj SimpleSelector::wrapInComplex()+  {+    ComplexSelectorObj selector =+      SASS_MEMORY_NEW(ComplexSelector, pstate());+    selector->append(wrapInCompound());+    return selector;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Placeholder_Selector::Placeholder_Selector(ParserState pstate, std::string n)+  : SimpleSelector(pstate, n)+  { simple_type(PLACEHOLDER_SEL); }+  Placeholder_Selector::Placeholder_Selector(const Placeholder_Selector* ptr)+  : SimpleSelector(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)+  : SimpleSelector(pstate, n)+  { simple_type(TYPE_SEL); }+  Type_Selector::Type_Selector(const Type_Selector* ptr)+  : SimpleSelector(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)+  : SimpleSelector(pstate, n)+  { simple_type(CLASS_SEL); }+  Class_Selector::Class_Selector(const Class_Selector* ptr)+  : SimpleSelector(ptr)+  { simple_type(CLASS_SEL); }++  unsigned long Class_Selector::specificity() const+  {+    return Constants::Specificity_Class;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Id_Selector::Id_Selector(ParserState pstate, std::string n)+  : SimpleSelector(pstate, n)+  { simple_type(ID_SEL); }+  Id_Selector::Id_Selector(const Id_Selector* ptr)+  : SimpleSelector(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)+  : SimpleSelector(pstate, n), matcher_(m), value_(v), modifier_(o)+  { simple_type(ATTRIBUTE_SEL); }+  Attribute_Selector::Attribute_Selector(const Attribute_Selector* ptr)+  : SimpleSelector(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_, SimpleSelector::hash());+      hash_combine(hash_, std::hash<std::string>()(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 name, bool element)+  : SimpleSelector(pstate, name),+    normalized_(Util::unvendor(name)),+    argument_({}),+    selector_({}),+    isSyntacticClass_(!element),+    isClass_(!element && !isFakePseudoElement(normalized_))+  { simple_type(PSEUDO_SEL); }+  Pseudo_Selector::Pseudo_Selector(const Pseudo_Selector* ptr)+  : SimpleSelector(ptr),+    normalized_(ptr->normalized()),+    argument_(ptr->argument()),+    selector_(ptr->selector()),+    isSyntacticClass_(ptr->isSyntacticClass()),+    isClass_(ptr->isClass())+  { 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 isElement();+  }++  size_t Pseudo_Selector::hash() const+  {+    if (hash_ == 0) {+      hash_combine(hash_, SimpleSelector::hash());+      if (selector_) hash_combine(hash_, selector_->hash());+      if (argument_) hash_combine(hash_, argument_->hash());+    }+    return hash_;+  }++  unsigned long Pseudo_Selector::specificity() const+  {+    if (is_pseudo_element())+      return Constants::Specificity_Element;+    return Constants::Specificity_Pseudo;+  }++  Pseudo_Selector_Obj Pseudo_Selector::withSelector(SelectorListObj selector)+  {+    Pseudo_Selector_Obj pseudo = SASS_MEMORY_COPY(this);+    pseudo->selector(selector);+    return pseudo;+  }++  bool Pseudo_Selector::empty() const+  {+    // Only considered empty if selector is+    // available but has no items in it.+    return selector() && selector()->empty();+  }++  void Pseudo_Selector::cloneChildren()+  {+    if (selector().isNull()) selector({});+    else selector(SASS_MEMORY_CLONE(selector()));+  }++  bool Pseudo_Selector::has_real_parent_ref() const {+    if (!selector()) return false;+    return selector()->has_real_parent_ref();+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  SelectorList::SelectorList(ParserState pstate, size_t s)+  : Selector(pstate),+    Vectorized<ComplexSelectorObj>(s),+    is_optional_(false)+  { }+  SelectorList::SelectorList(const SelectorList* ptr)+    : Selector(ptr),+    Vectorized<ComplexSelectorObj>(*ptr),+    is_optional_(ptr->is_optional_)+  { }++  size_t SelectorList::hash() const+  {+    if (Selector::hash_ == 0) {+      hash_combine(Selector::hash_, Vectorized::hash());+    }+    return Selector::hash_;+  }++  bool SelectorList::has_real_parent_ref() const+  {+    for (ComplexSelectorObj s : elements()) {+      if (s && s->has_real_parent_ref()) return true;+    }+    return false;+  }++  void SelectorList::cloneChildren()+  {+    for (size_t i = 0, l = length(); i < l; i++) {+      at(i) = SASS_MEMORY_CLONE(at(i));+    }+  }++  unsigned long SelectorList::specificity() const+  {+    return 0;+  }++  bool SelectorList::isInvisible() const+  {+    if (length() == 0) return true;+    for (size_t i = 0; i < length(); i += 1) {+      if (get(i)->isInvisible()) return true;+    }+    return false;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  ComplexSelector::ComplexSelector(ParserState pstate)+  : Selector(pstate),+    Vectorized<SelectorComponentObj>(),+    chroots_(false),+    hasPreLineFeed_(false)+  {+  }+  ComplexSelector::ComplexSelector(const ComplexSelector* ptr)+  : Selector(ptr),+    Vectorized<SelectorComponentObj>(ptr->elements()),+    chroots_(ptr->chroots()),+    hasPreLineFeed_(ptr->hasPreLineFeed())+  {+  }++  void ComplexSelector::cloneChildren()+  {+    for (size_t i = 0, l = length(); i < l; i++) {+      at(i) = SASS_MEMORY_CLONE(at(i));+    }+  }++  unsigned long ComplexSelector::specificity() const+  {+    int sum = 0;+    for (auto component : elements()) {+      sum += component->specificity();+    }+    return sum;+  }++  bool ComplexSelector::isInvisible() const+  {+    if (length() == 0) return true;+    for (size_t i = 0; i < length(); i += 1) {+      if (CompoundSelectorObj compound = get(i)->getCompound()) {+        if (compound->isInvisible()) return true;+      }+    }+    return false;+  }++  SelectorListObj ComplexSelector::wrapInList()+  {+    SelectorListObj selector =+      SASS_MEMORY_NEW(SelectorList, pstate());+    selector->append(this);+    return selector;+  }++  size_t ComplexSelector::hash() const+  {+    if (Selector::hash_ == 0) {+      hash_combine(Selector::hash_, Vectorized::hash());+      // ToDo: this breaks some extend lookup+      // hash_combine(Selector::hash_, chroots_);+    }+    return Selector::hash_;+  }++  bool ComplexSelector::has_placeholder() const {+    for (size_t i = 0, L = length(); i < L; ++i) {+      if (get(i)->has_placeholder()) return true;+    }+    return false;+  }++  bool ComplexSelector::has_real_parent_ref() const+  {+    for (auto item : elements()) {+      if (item->has_real_parent_ref()) return true;+    }+    return false;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  SelectorComponent::SelectorComponent(ParserState pstate, bool postLineBreak)+  : Selector(pstate),+    hasPostLineBreak_(postLineBreak)+  {+  }++  SelectorComponent::SelectorComponent(const SelectorComponent* ptr)+  : Selector(ptr),+    hasPostLineBreak_(ptr->hasPostLineBreak())+  { }++  void SelectorComponent::cloneChildren()+  {+  }++  unsigned long SelectorComponent::specificity() const+  {+    return 0;+  }++  // Wrap the compound selector with a complex selector+  ComplexSelector* SelectorComponent::wrapInComplex()+  {+    auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate());+    complex->append(this);+    return complex;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  SelectorCombinator::SelectorCombinator(ParserState pstate, SelectorCombinator::Combinator combinator, bool postLineBreak)+    : SelectorComponent(pstate, postLineBreak),+    combinator_(combinator)+  {+  }+  SelectorCombinator::SelectorCombinator(const SelectorCombinator* ptr)+    : SelectorComponent(ptr->pstate(), false),+      combinator_(ptr->combinator())+  { }++  void SelectorCombinator::cloneChildren()+  {+  }++  unsigned long SelectorCombinator::specificity() const+  {+    return 0;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  CompoundSelector::CompoundSelector(ParserState pstate, bool postLineBreak)+    : SelectorComponent(pstate, postLineBreak),+      Vectorized<SimpleSelectorObj>(),+      hasRealParent_(false),+      extended_(false)+  {+  }+  CompoundSelector::CompoundSelector(const CompoundSelector* ptr)+    : SelectorComponent(ptr),+      Vectorized<SimpleSelectorObj>(*ptr),+      hasRealParent_(ptr->hasRealParent()),+      extended_(ptr->extended())+  { }++  size_t CompoundSelector::hash() const+  {+    if (Selector::hash_ == 0) {+      hash_combine(Selector::hash_, Vectorized::hash());+      hash_combine(Selector::hash_, hasRealParent_);+    }+    return Selector::hash_;+  }++  bool CompoundSelector::has_real_parent_ref() const+  {+    if (hasRealParent()) return true;+    // ToDo: dart sass has another check?+    // if (Cast<Type_Selector>(front)) {+    //  if (front->ns() != "") return false;+    // }+    for (const SimpleSelector* s : elements()) {+      if (s && s->has_real_parent_ref()) return true;+    }+    return false;+  }++  bool CompoundSelector::has_placeholder() const+  {+    if (length() == 0) return false;+    for (SimpleSelectorObj ss : elements()) {+      if (ss->has_placeholder()) return true;+    }+    return false;+  }++  void CompoundSelector::cloneChildren()+  {+    for (size_t i = 0, l = length(); i < l; i++) {+      at(i) = SASS_MEMORY_CLONE(at(i));+    }+  }++  unsigned long CompoundSelector::specificity() const+  {+    int sum = 0;+    for (size_t i = 0, L = length(); i < L; ++i)+    { sum += get(i)->specificity(); }+    return sum;+  }++  bool CompoundSelector::isInvisible() const+  {+    for (size_t i = 0; i < length(); i += 1) {+      if (!get(i)->isInvisible()) return false;+    }+    return true;+  }++  bool CompoundSelector::isSuperselectorOf(const CompoundSelector* sub, std::string wrapped) const+  {+    CompoundSelector* rhs2 = const_cast<CompoundSelector*>(sub);+    CompoundSelector* lhs2 = const_cast<CompoundSelector*>(this);+    return compoundIsSuperselector(lhs2, rhs2, {});+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  MediaRule::MediaRule(ParserState pstate, Block_Obj block) :+    Has_Block(pstate, block),+    schema_({})+  {+    statement_type(MEDIA);+  }++  MediaRule::MediaRule(const MediaRule* ptr) :+    Has_Block(ptr),+    schema_(ptr->schema_)+  {+    statement_type(MEDIA);+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  CssMediaRule::CssMediaRule(ParserState pstate, Block_Obj block) :+    Has_Block(pstate, block),+    Vectorized()+  {+    statement_type(MEDIA);+  }++  CssMediaRule::CssMediaRule(const CssMediaRule* ptr) :+    Has_Block(ptr),+    Vectorized(*ptr)+  {+    statement_type(MEDIA);+  }++  CssMediaQuery::CssMediaQuery(ParserState pstate) :+    AST_Node(pstate),+    modifier_(""),+    type_(""),+    features_()+  {+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  bool CssMediaQuery::operator==(const CssMediaQuery& rhs) const+  {+    return type_ == rhs.type_+      && modifier_ == rhs.modifier_+      && features_ == rhs.features_;+  }++  // Implemented after dart-sass (maybe move to other class?)+  CssMediaQuery_Obj CssMediaQuery::merge(CssMediaQuery_Obj& other)+  {++    std::string ourType = this->type();+    Util::ascii_str_tolower(&ourType);++    std::string theirType = other->type();+    Util::ascii_str_tolower(&theirType);++    std::string ourModifier = this->modifier();+    Util::ascii_str_tolower(&ourModifier);++    std::string theirModifier = other->modifier();+    Util::ascii_str_tolower(&theirModifier);++    std::string type;+    std::string modifier;+    std::vector<std::string> features;++    if (ourType.empty() && theirType.empty()) {+      CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate());+      std::vector<std::string> f1(this->features());+      std::vector<std::string> f2(other->features());+      features.insert(features.end(), f1.begin(), f1.end());+      features.insert(features.end(), f2.begin(), f2.end());+      query->features(features);+      return query;+    }++    if ((ourModifier == "not") != (theirModifier == "not")) {+      if (ourType == theirType) {+        std::vector<std::string> negativeFeatures =+          ourModifier == "not" ? this->features() : other->features();+        std::vector<std::string> positiveFeatures =+          ourModifier == "not" ? other->features() : this->features();++        // If the negative features are a subset of the positive features, the+        // query is empty. For example, `not screen and (color)` has no+        // intersection with `screen and (color) and (grid)`.+        // However, `not screen and (color)` *does* intersect with `screen and+        // (grid)`, because it means `not (screen and (color))` and so it allows+        // a screen with no color but with a grid.+        if (listIsSubsetOrEqual(negativeFeatures, positiveFeatures)) {+          return SASS_MEMORY_NEW(CssMediaQuery, pstate());+        }+        else {+          return {};+        }+      }+      else if (this->matchesAllTypes() || other->matchesAllTypes()) {+        return {};+      }++      if (ourModifier == "not") {+        modifier = theirModifier;+        type = theirType;+        features = other->features();+      }+      else {+        modifier = ourModifier;+        type = ourType;+        features = this->features();+      }+    }+    else if (ourModifier == "not") {+      SASS_ASSERT(theirModifier == "not", "modifiers not is sync");++      // CSS has no way of representing "neither screen nor print".+      if (ourType != theirType) return {};++      auto moreFeatures = this->features().size() > other->features().size()+        ? this->features()+        : other->features();+      auto fewerFeatures = this->features().size() > other->features().size()+        ? other->features()+        : this->features();++      // If one set of features is a superset of the other,+      // use those features because they're strictly narrower.+      if (listIsSubsetOrEqual(fewerFeatures, moreFeatures)) {+        modifier = ourModifier; // "not"+        type = ourType;+        features = moreFeatures;+      }+      else {+        // Otherwise, there's no way to+        // represent the intersection.+        return {};+      }++    }+    else {+      if (this->matchesAllTypes()) {+        modifier = theirModifier;+        // Omit the type if either input query did, since that indicates that they+        // aren't targeting a browser that requires "all and".+        type = (other->matchesAllTypes() && ourType.empty()) ? "" : theirType;+        std::vector<std::string> f1(this->features());+        std::vector<std::string> f2(other->features());+        features.insert(features.end(), f1.begin(), f1.end());+        features.insert(features.end(), f2.begin(), f2.end());+      }+      else if (other->matchesAllTypes()) {+        modifier = ourModifier;+        type = ourType;+        std::vector<std::string> f1(this->features());+        std::vector<std::string> f2(other->features());+        features.insert(features.end(), f1.begin(), f1.end());+        features.insert(features.end(), f2.begin(), f2.end());+      }+      else if (ourType != theirType) {+        return SASS_MEMORY_NEW(CssMediaQuery, pstate());+      }+      else {+        modifier = ourModifier.empty() ? theirModifier : ourModifier;+        type = ourType;+        std::vector<std::string> f1(this->features());+        std::vector<std::string> f2(other->features());+        features.insert(features.end(), f1.begin(), f1.end());+        features.insert(features.end(), f2.begin(), f2.end());+      }+    }++    CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate());+    query->modifier(modifier == ourModifier ? this->modifier() : other->modifier());+    query->type(ourType.empty() ? other->type() : this->type());+    query->features(features);+    return query;+  }++  CssMediaQuery::CssMediaQuery(const CssMediaQuery* ptr) :+    AST_Node(*ptr),+    modifier_(ptr->modifier_),+    type_(ptr->type_),+    features_(ptr->features_)+  {+  }++  /////////////////////////////////////////////////////////////////////////+  // ToDo: finalize specificity implementation+  /////////////////////////////////////////////////////////////////////////++  size_t SelectorList::maxSpecificity() const+  {+    size_t specificity = 0;+    for (auto complex : elements()) {+      specificity = std::max(specificity, complex->maxSpecificity());+    }+    return specificity;+  }++  size_t SelectorList::minSpecificity() const+  {+    size_t specificity = 0;+    for (auto complex : elements()) {+      specificity = std::min(specificity, complex->minSpecificity());+    }+    return specificity;+  }++  size_t CompoundSelector::maxSpecificity() const+  {+    size_t specificity = 0;+    for (auto simple : elements()) {+      specificity += simple->maxSpecificity();+    }+    return specificity;+  }++  size_t CompoundSelector::minSpecificity() const+  {+    size_t specificity = 0;+    for (auto simple : elements()) {+      specificity += simple->minSpecificity();+    }+    return specificity;+  }++  size_t ComplexSelector::maxSpecificity() const+  {+    size_t specificity = 0;+    for (auto component : elements()) {+      specificity += component->maxSpecificity();+    }+    return specificity;+  }++  size_t ComplexSelector::minSpecificity() const+  {+    size_t specificity = 0;+    for (auto component : elements()) {+      specificity += component->minSpecificity();+    }+    return specificity;+  }++  /////////////////////////////////////////////////////////////////////////+  // ToDo: this might be done easier with new selector format+  /////////////////////////////////////////////////////////////////////////++  std::vector<ComplexSelectorObj>+    CompoundSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent)+  {++    auto parent = pstack.back();+    std::vector<ComplexSelectorObj> rv;++    for (SimpleSelectorObj simple : elements()) {+      if (Pseudo_Selector * pseudo = Cast<Pseudo_Selector>(simple)) {+        if (SelectorList* sel = Cast<SelectorList>(pseudo->selector())) {+          if (parent) {+            pseudo->selector(sel->resolve_parent_refs(+              pstack, traces, implicit_parent));+          }+        }+      }+    }++    // Mix with parents from stack+    if (hasRealParent()) {++      if (parent.isNull()) {+        return { wrapInComplex() };+      }+      else {+        for (auto complex : parent->elements()) {+          // The parent complex selector has a compound selector+          if (CompoundSelectorObj tail = Cast<CompoundSelector>(complex->last())) {+            // Create a copy to alter it+            complex = SASS_MEMORY_COPY(complex);+            tail = SASS_MEMORY_COPY(tail);++            // Check if we can merge front with back+            if (length() > 0 && tail->length() > 0) {+              SimpleSelectorObj back = tail->last();+              SimpleSelectorObj front = first();+              auto simple_back = Cast<SimpleSelector>(back);+              auto simple_front = Cast<Type_Selector>(front);+              if (simple_front && simple_back) {+                simple_back = SASS_MEMORY_COPY(simple_back);+                auto name = simple_back->name();+                name += simple_front->name();+                simple_back->name(name);+                tail->elements().back() = simple_back;+                tail->elements().insert(tail->end(),+                  begin() + 1, end());+              }+              else {+                tail->concat(this);+              }+            }+            else {+              tail->concat(this);+            }++            complex->elements().back() = tail;+            // Append to results+            rv.push_back(complex);+          }+          else {+            // Can't insert parent that ends with a combinator+            // where the parent selector is followed by something+            if (parent && length() > 0) {+              throw Exception::InvalidParent(parent, traces, this);+            }+            // Create a copy to alter it+            complex = SASS_MEMORY_COPY(complex);+            // Just append ourself+            complex->append(this);+            // Append to results+            rv.push_back(complex);+          }+        }+      }+    }++    // No parents+    else {+      // Create a new wrapper to wrap ourself+      auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate());+      // Just append ourself+      complex->append(this);+      // Append to results+      rv.push_back(complex);+    }++    return rv;++  }++  /* better return std::vector? only - is empty container anyway? */+  SelectorList* ComplexSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent)+  {++    std::vector<std::vector<ComplexSelectorObj>> vars;++    auto parent = pstack.back();++    if (has_real_parent_ref() && !parent) {+      throw Exception::TopLevelParent(traces, pstate());+    }++    if (!chroots() && parent) {++      if (!has_real_parent_ref() && !implicit_parent) {+        SelectorList* retval = SASS_MEMORY_NEW(SelectorList, pstate(), 1);+        retval->append(this);+        return retval;+      }++      vars.push_back(parent->elements());+    }++    for (auto sel : elements()) {+      if (CompoundSelectorObj comp = Cast<CompoundSelector>(sel)) {+        auto asd = comp->resolve_parent_refs(pstack, traces, implicit_parent);+        if (asd.size() > 0) vars.push_back(asd);+      }+      else {+        // ToDo: merge together sequences whenever possible+        auto cont = SASS_MEMORY_NEW(ComplexSelector, pstate());+        cont->append(sel);+        vars.push_back({ cont });+      }+    }++    // Need complex selectors to preserve linefeeds+    std::vector<std::vector<ComplexSelectorObj>> res = permutateAlt(vars);++    // std::reverse(std::begin(res), std::end(res));++    auto lst = SASS_MEMORY_NEW(SelectorList, pstate());+    for (auto items : res) {+      if (items.size() > 0) {+        ComplexSelectorObj first = SASS_MEMORY_COPY(items[0]);+        first->hasPreLineFeed(first->hasPreLineFeed() || (!has_real_parent_ref() && hasPreLineFeed()));+        // ToDo: remove once we know how to handle line feeds+        // ToDo: currently a mashup between ruby and dart sass+        // if (has_real_parent_ref()) first->has_line_feed(false);+        // first->has_line_break(first->has_line_break() || has_line_break());+        first->chroots(true); // has been resolved by now+        for (size_t i = 1; i < items.size(); i += 1) {+          first->concat(items[i]);+        }+        lst->append(first);+      }+    }++    return lst;++  }++  SelectorList* SelectorList::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent)+  {+    SelectorList* rv = SASS_MEMORY_NEW(SelectorList, pstate());+    for (auto sel : elements()) {+      // Note: this one is tricky as we get back a pointer from resolve parents ...+      SelectorListObj res = sel->resolve_parent_refs(pstack, traces, implicit_parent);+      // Note: ... and concat will only append the items in elements+      // Therefore by passing it directly, the container will leak!+      rv->concat(res);+    }+    return rv;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  IMPLEMENT_AST_OPERATORS(Selector_Schema);+  IMPLEMENT_AST_OPERATORS(Placeholder_Selector);+  IMPLEMENT_AST_OPERATORS(Attribute_Selector);+  IMPLEMENT_AST_OPERATORS(Type_Selector);+  IMPLEMENT_AST_OPERATORS(Class_Selector);+  IMPLEMENT_AST_OPERATORS(Id_Selector);+  IMPLEMENT_AST_OPERATORS(Pseudo_Selector);+  IMPLEMENT_AST_OPERATORS(SelectorCombinator);+  IMPLEMENT_AST_OPERATORS(CompoundSelector);+  IMPLEMENT_AST_OPERATORS(ComplexSelector);+  IMPLEMENT_AST_OPERATORS(SelectorList);++}
+ libsass/src/ast_selectors.hpp view
@@ -0,0 +1,512 @@+#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 std::vector<SelectorComponentObj>& parents);++  bool complexIsParentSuperselector(+    const std::vector<SelectorComponentObj>& complex1,+    const std::vector<SelectorComponentObj>& complex2);++    std::vector<std::vector<SelectorComponentObj>> weave(+    const std::vector<std::vector<SelectorComponentObj>>& complexes);++  std::vector<std::vector<SelectorComponentObj>> weaveParents(+    std::vector<SelectorComponentObj> parents1,+    std::vector<SelectorComponentObj> parents2);++  std::vector<SimpleSelectorObj> unifyCompound(+    const std::vector<SimpleSelectorObj>& compound1,+    const std::vector<SimpleSelectorObj>& compound2);++  std::vector<std::vector<SelectorComponentObj>> unifyComplex(+    const std::vector<std::vector<SelectorComponentObj>>& complexes);++  /////////////////////////////////////////+  // Abstract base class for CSS selectors.+  /////////////////////////////////////////+  class Selector : public Expression {+  protected:+    mutable size_t hash_;+  public:+    Selector(ParserState 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(ParserState 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(std::string, ns)+    HASH_CONSTREF(std::string, name)+    ADD_PROPERTY(Simple_Type, simple_type)+    HASH_PROPERTY(bool, has_ns)+  public:+    SimpleSelector(ParserState pstate, std::string n = "");+    virtual std::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 Id_Selector* getIdSelector() { return NULL; }+    virtual Type_Selector* getTypeSelector() { return NULL; }+    virtual Pseudo_Selector* 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 Placeholder_Selector final : public SimpleSelector {+  public:+    Placeholder_Selector(ParserState pstate, std::string n);+    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(Placeholder_Selector)+    ATTACH_AST_OPERATIONS(Placeholder_Selector)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  /////////////////////////////////////////////////////////////////////+  // Type selectors (and the universal selector) -- e.g., div, span, *.+  /////////////////////////////////////////////////////////////////////+  class Type_Selector final : public SimpleSelector {+  public:+    Type_Selector(ParserState pstate, std::string n);+    virtual unsigned long specificity() const override;+    SimpleSelector* unifyWith(const SimpleSelector*);+    CompoundSelector* unifyWith(CompoundSelector*) override;+    Type_Selector* getTypeSelector() override { return this; }+    bool operator==(const SimpleSelector& rhs) const final override;+    ATTACH_CMP_OPERATIONS(Type_Selector)+    ATTACH_AST_OPERATIONS(Type_Selector)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////+  // Class selectors  -- i.e., .foo.+  ////////////////////////////////////////////////+  class Class_Selector final : public SimpleSelector {+  public:+    Class_Selector(ParserState pstate, std::string n);+    virtual unsigned long specificity() const override;+    bool operator==(const SimpleSelector& rhs) const final override;+    ATTACH_CMP_OPERATIONS(Class_Selector)+    ATTACH_AST_OPERATIONS(Class_Selector)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////+  // ID selectors -- i.e., #foo.+  ////////////////////////////////////////////////+  class Id_Selector final : public SimpleSelector {+  public:+    Id_Selector(ParserState pstate, std::string n);+    virtual unsigned long specificity() const override;+    CompoundSelector* unifyWith(CompoundSelector*) override;+    Id_Selector* getIdSelector() final override { return this; }+    bool operator==(const SimpleSelector& rhs) const final override;+    ATTACH_CMP_OPERATIONS(Id_Selector)+    ATTACH_AST_OPERATIONS(Id_Selector)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////////////////+  // Attribute selectors -- e.g., [src*=".jpg"], etc.+  ///////////////////////////////////////////////////+  class Attribute_Selector final : public SimpleSelector {+    ADD_CONSTREF(std::string, matcher)+    // this cannot be changed to obj atm!!!!!!????!!!!!!!+    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);+    size_t hash() const override;+    virtual unsigned long specificity() const override;+    bool operator==(const SimpleSelector& rhs) const final override;+    ATTACH_CMP_OPERATIONS(Attribute_Selector)+    ATTACH_AST_OPERATIONS(Attribute_Selector)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////////////////////////////////////////////////////+  // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.+  //////////////////////////////////////////////////////////////////+  // Pseudo Selector cannot have any namespace?+  class Pseudo_Selector final : public SimpleSelector {+    ADD_PROPERTY(std::string, normalized)+    ADD_PROPERTY(String_Obj, argument)+    ADD_PROPERTY(SelectorListObj, selector)+    ADD_PROPERTY(bool, isSyntacticClass)+    ADD_PROPERTY(bool, isClass)+  public:+    Pseudo_Selector(ParserState pstate, std::string n, bool element = false);+    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;+    Pseudo_Selector_Obj withSelector(SelectorListObj selector);++    CompoundSelector* unifyWith(CompoundSelector*) override;+    Pseudo_Selector* getPseudoSelector() final override { return this; }+    bool operator==(const SimpleSelector& rhs) const final override;+    ATTACH_CMP_OPERATIONS(Pseudo_Selector)+    ATTACH_AST_OPERATIONS(Pseudo_Selector)+    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(ParserState pstate);++    // Returns true if the first components+    // is a compound selector and fullfills+    // a few other criteria.+    bool isInvisible() 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(ParserState 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(ParserState 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)+    ADD_PROPERTY(bool, extended)+  public:+    CompoundSelector(ParserState pstate, bool postLineBreak = false);++    // Returns true if this compound selector+    // fullfills 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, std::string wrapped = "") const;++    void cloneChildren() override;+    bool has_real_parent_ref() const override;+    bool has_placeholder() const override;+    std::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;++    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(ParserState pstate, size_t s = 0);+    std::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(ParserState pstate, SelectorListObj s);+    ExtendRule(ParserState pstate, Selector_Schema_Obj s);+    ATTACH_AST_OPERATIONS(ExtendRule)+    ATTACH_CRTP_PERFORM_METHODS()+  };++}++#endif
+ libsass/src/ast_supports.cpp view
@@ -0,0 +1,114 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.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<Supports_Operator>(cond)) {+      return op->operand() != operand();+    }+    return Cast<Supports_Negation>(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<Supports_Negation>(cond) ||+           Cast<Supports_Operator>(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);++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++}
+ libsass/src/ast_supports.hpp view
@@ -0,0 +1,121 @@+#ifndef SASS_AST_SUPPORTS_H+#define SASS_AST_SUPPORTS_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <set>+#include <deque>+#include <vector>+#include <string>+#include <sstream>+#include <iostream>+#include <typeinfo>+#include <algorithm>+#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
+ libsass/src/ast_values.cpp view
@@ -0,0 +1,1154 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++namespace Sass {++  void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v")+  {+    str.erase( str.find_last_not_of( delimiters ) + 1 );+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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<Expression_Obj>(size),+    separator_(sep),+    is_arglist_(argl),+    is_bracketed_(bracket),+    from_selector_(false)+  { concrete_type(LIST); }++  List::List(const List* ptr)+  : Value(ptr),+    Vectorized<Expression_Obj>(*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+  {+    if (hash_ == 0) {+      hash_ = std::hash<std::string>()(sep_string());+      hash_combine(hash_, std::hash<bool>()(is_bracketed()));+      for (size_t i = 0, L = length(); i < L; ++i)+        hash_combine(hash_, (elements()[i])->hash());+    }+    return hash_;+  }++  void List::set_delayed(bool delayed)+  {+    is_delayed(delayed);+    // don't set children+  }++  bool List::operator< (const Expression& rhs) const+  {+    if (auto r = Cast<List>(&rhs)) {+      if (length() < r->length()) return true;+      if (length() > r->length()) return false;+      const auto& left = elements();+      const auto& right = r->elements();+      for (size_t i = 0; i < left.size(); i += 1) {+        if (*left[i] < *right[i]) return true;+        if (*left[i] == *right[i]) continue;+        return false;+      }+      return false;+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool List::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<List>(&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* arg = Cast<Argument>(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* arg = Cast<Argument>(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<Map>(&rhs)) {+      if (length() < r->length()) return true;+      if (length() > r->length()) return false;+      const auto& lkeys = keys();+      const auto& rkeys = r->keys();+      for (size_t i = 0; i < lkeys.size(); i += 1) {+        if (*lkeys[i] < *rkeys[i]) return true;+        if (*lkeys[i] == *rkeys[i]) continue;+        return false;+      }+      const auto& lvals = values();+      const auto& rvals = r->values();+      for (size_t i = 0; i < lvals.size(); i += 1) {+        if (*lvals[i] < *rvals[i]) return true;+        if (*lvals[i] == *rvals[i]) continue;+        return false;+      }+      return false;+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Map::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Map>(&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<Binary_Expression>(&rhs)) {+      return type() < m->type() ||+        *left() < *m->left() ||+        *right() < *m->right();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Binary_Expression::operator==(const Expression& rhs) const+  {+    if (auto m = Cast<Binary_Expression>(&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<size_t>()(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<Function>(&rhs)) {+      auto d1 = Cast<Definition>(definition());+      auto d2 = Cast<Definition>(r->definition());+      if (d1 == nullptr) return d2 != nullptr;+      else if (d2 == nullptr) return false;+      if (is_css() == r->is_css()) {+        return d1 < d2;+      }+      return r->is_css();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Function::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Function>(&rhs)) {+      auto d1 = Cast<Definition>(definition());+      auto d2 = Cast<Definition>(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<Function_Call>(&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<std::string>()(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<Variable>(&rhs)) {+      return name() == e->name();+    }+    return false;+  }++  size_t Variable::hash() const+  {+    return std::hash<std::string>()(name());+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Number::Number(ParserState pstate, double val, std::string u, bool zero)+  : Value(pstate),+    Units(),+    value_(val),+    zero_(zero),+    hash_(0)+  {+    size_t l = 0;+    size_t r;+    if (!u.empty()) {+      bool nominator = true;+      while (true) {+        r = u.find_first_of("*/", l);+        std::string unit(u.substr(l, r == std::string::npos ? r : r - l));+        if (!unit.empty()) {+          if (nominator) numerators.push_back(unit);+          else denominators.push_back(unit);+        }+        if (r == std::string::npos) break;+        // ToDo: should error for multiple slashes+        // if (!nominator && u[r] == '/') error(...)+        if (u[r] == '/')+          nominator = false;+        // strange math parsing?+        // else if (u[r] == '*')+        //  nominator = true;+        l = r + 1;+      }+    }+    concrete_type(NUMBER);+  }++  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()+  {+    // apply conversion factor+    value_ *= this->Units::reduce();+  }++  void Number::normalize()+  {+    // apply conversion factor+    value_ *= this->Units::normalize();+  }++  size_t Number::hash() const+  {+    if (hash_ == 0) {+      hash_ = std::hash<double>()(value_);+      for (const auto numerator : numerators)+        hash_combine(hash_, std::hash<std::string>()(numerator));+      for (const auto denominator : denominators)+        hash_combine(hash_, std::hash<std::string>()(denominator));+    }+    return hash_;+  }++  bool Number::operator< (const Expression& rhs) const+  {+    if (auto n = Cast<Number>(&rhs)) {+      return *this < *n;+    }+    return false;+  }++  bool Number::operator== (const Expression& rhs) const+  {+    if (auto n = Cast<Number>(&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();+    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 &&+      NEAR_EQUAL(l.value(), r.value());+  }++  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();+    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 useful backtraces? */+      throw Exception::IncompatibleUnits(rhs, *this);+    }+    if (lhs_unit == rhs_unit) {+      return l.value() < r.value();+    } else {+      return lhs_unit < rhs_unit;+    }+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Color::Color(ParserState pstate, double a, const std::string disp)+  : Value(pstate),+    disp_(disp), a_(a),+    hash_(0)+  { concrete_type(COLOR); }++  Color::Color(const Color* ptr)+  : Value(ptr->pstate()),+    // reset on copy+    disp_(""),+    a_(ptr->a_),+    hash_(ptr->hash_)+  { concrete_type(COLOR); }++  bool Color::operator< (const Expression& rhs) const+  {+    if (auto r = Cast<Color_RGBA>(&rhs)) {+      return *this < *r;+    }+    else if (auto r = Cast<Color_HSLA>(&rhs)) {+      return *this < *r;+    }+    else if (auto r = Cast<Color>(&rhs)) {+      return a_ < r->a();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Color::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Color_RGBA>(&rhs)) {+      return *this == *r;+    }+    else if (auto r = Cast<Color_HSLA>(&rhs)) {+      return *this == *r;+    }+    else if (auto r = Cast<Color>(&rhs)) {+      return a_ == r->a();+    }+    return false;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Color_RGBA::Color_RGBA(ParserState pstate, double r, double g, double b, double a, const std::string disp)+  : Color(pstate, a, disp),+    r_(r), g_(g), b_(b)+  { concrete_type(COLOR); }++  Color_RGBA::Color_RGBA(const Color_RGBA* ptr)+  : Color(ptr),+    r_(ptr->r_),+    g_(ptr->g_),+    b_(ptr->b_)+  { concrete_type(COLOR); }++  bool Color_RGBA::operator< (const Expression& rhs) const+  {+    if (auto r = Cast<Color_RGBA>(&rhs)) {+      if (r_ < r->r()) return true;+      if (r_ > r->r()) return false;+      if (g_ < r->g()) return true;+      if (g_ > r->g()) return false;+      if (b_ < r->b()) return true;+      if (b_ > r->b()) return false;+      if (a_ < r->a()) return true;+      if (a_ > r->a()) return false;+      return false; // is equal+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Color_RGBA::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Color_RGBA>(&rhs)) {+      return r_ == r->r() &&+             g_ == r->g() &&+             b_ == r->b() &&+             a_ == r->a();+    }+    return false;+  }++  size_t Color_RGBA::hash() const+  {+    if (hash_ == 0) {+      hash_ = std::hash<std::string>()("RGBA");+      hash_combine(hash_, std::hash<double>()(a_));+      hash_combine(hash_, std::hash<double>()(r_));+      hash_combine(hash_, std::hash<double>()(g_));+      hash_combine(hash_, std::hash<double>()(b_));+    }+    return hash_;+  }++  Color_HSLA* Color_RGBA::copyAsHSLA() const+  {++    // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV+    double r = r_ / 255.0;+    double g = g_ / 255.0;+    double b = b_ / 255.0;++    double max = std::max(r, std::max(g, b));+    double min = std::min(r, std::min(g, b));+    double delta = max - min;++    double h = 0;+    double s;+    double l = (max + min) / 2.0;++    if (NEAR_EQUAL(max, min)) {+      h = s = 0; // achromatic+    }+    else {+      if (l < 0.5) s = delta / (max + min);+      else         s = delta / (2.0 - max - min);++      if      (r == max) h = (g - b) / delta + (g < b ? 6 : 0);+      else if (g == max) h = (b - r) / delta + 2;+      else if (b == max) h = (r - g) / delta + 4;+    }++    // HSL hsl_struct;+    h = h * 60;+    s = s * 100;+    l = l * 100;++    return SASS_MEMORY_NEW(Color_HSLA,+      pstate(), h, s, l, a(), ""+    );+  }++  Color_RGBA* Color_RGBA::copyAsRGBA() const+  {+    return SASS_MEMORY_COPY(this);+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Color_HSLA::Color_HSLA(ParserState pstate, double h, double s, double l, double a, const std::string disp)+  : Color(pstate, a, disp),+    h_(absmod(h, 360.0)),+    s_(clip(s, 0.0, 100.0)),+    l_(clip(l, 0.0, 100.0))+    // hash_(0)+  { concrete_type(COLOR); }++  Color_HSLA::Color_HSLA(const Color_HSLA* ptr)+  : Color(ptr),+    h_(ptr->h_),+    s_(ptr->s_),+    l_(ptr->l_)+    // hash_(ptr->hash_)+  { concrete_type(COLOR); }++  bool Color_HSLA::operator< (const Expression& rhs) const+  {+    if (auto r = Cast<Color_HSLA>(&rhs)) {+      if (h_ < r->h()) return true;+      if (h_ > r->h()) return false;+      if (s_ < r->s()) return true;+      if (s_ > r->s()) return false;+      if (l_ < r->l()) return true;+      if (l_ > r->l()) return false;+      if (a_ < r->a()) return true;+      if (a_ > r->a()) return false;+      return false; // is equal+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Color_HSLA::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Color_HSLA>(&rhs)) {+      return h_ == r->h() &&+             s_ == r->s() &&+             l_ == r->l() &&+             a_ == r->a();+    }+    return false;+  }++  size_t Color_HSLA::hash() const+  {+    if (hash_ == 0) {+      hash_ = std::hash<std::string>()("HSLA");+      hash_combine(hash_, std::hash<double>()(a_));+      hash_combine(hash_, std::hash<double>()(h_));+      hash_combine(hash_, std::hash<double>()(s_));+      hash_combine(hash_, std::hash<double>()(l_));+    }+    return hash_;+  }++  // hue to RGB helper function+  double h_to_rgb(double m1, double m2, double h)+  {+    h = absmod(h, 1.0);+    if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;+    if (h*2.0 < 1) return m2;+    if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;+    return m1;+  }++  Color_RGBA* Color_HSLA::copyAsRGBA() const+  {++    double h = absmod(h_ / 360.0, 1.0);+    double s = clip(s_ / 100.0, 0.0, 1.0);+    double l = clip(l_ / 100.0, 0.0, 1.0);++    // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.+    double m2;+    if (l <= 0.5) m2 = l*(s+1.0);+    else m2 = (l+s)-(l*s);+    double m1 = (l*2.0)-m2;+    // round the results -- consider moving this into the Color constructor+    double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0);+    double g = (h_to_rgb(m1, m2, h) * 255.0);+    double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0);++    return SASS_MEMORY_NEW(Color_RGBA,+      pstate(), r, g, b, a(), ""+    );+  }++  Color_HSLA* Color_HSLA::copyAsHSLA() const+  {+    return SASS_MEMORY_COPY(this);+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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 (auto r = Cast<Custom_Error>(&rhs)) {+      return message() < r->message();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Custom_Error::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Custom_Error>(&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<Custom_Warning>(&rhs)) {+      return message() < r->message();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Custom_Warning::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<Custom_Warning>(&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<Boolean>(&rhs)) {+      return (value() < r->value());+    }+    return false;+  }++ bool Boolean::operator== (const Expression& rhs) const+ {+   if (auto r = Cast<Boolean>(&rhs)) {+     return (value() == r->value());+   }+   return false;+ }++ size_t Boolean::hash() const+  {+    if (hash_ == 0) {+      hash_ = std::hash<bool>()(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<PreValue_Obj>(size), css_(css), hash_(0)+  { concrete_type(STRING); }++  String_Schema::String_Schema(const String_Schema* ptr)+  : String(ptr),+    Vectorized<PreValue_Obj>(*ptr),+    css_(ptr->css_),+    hash_(ptr->hash_)+  { concrete_type(STRING); }++  void String_Schema::rtrim()+  {+    if (!empty()) {+      if (String* str = Cast<String>(last())) str->rtrim();+    }+  }++  bool String_Schema::is_left_interpolant(void) const+  {+    return length() && first()->is_left_interpolant();+  }+  bool String_Schema::is_right_interpolant(void) const+  {+    return length() && last()->is_right_interpolant();+  }++  bool String_Schema::operator< (const Expression& rhs) const+  {+    if (auto r = Cast<String_Schema>(&rhs)) {+      if (length() < r->length()) return true;+      if (length() > r->length()) return false;+      for (size_t i = 0, L = length(); i < L; ++i) {+        if (*get(i) < *r->get(i)) return true;+        if (*get(i) == *r->get(i)) continue;+        return false;+      }+      // Is equal+      return false;+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool String_Schema::operator== (const Expression& rhs) const+  {+    if (auto r = Cast<String_Schema>(&rhs)) {+      if (length() != r->length()) return false;+      for (size_t i = 0, L = length(); i < L; ++i) {+        auto rv = (*r)[i];+        auto lv = (*this)[i];+        if (*lv != *rv) return false;+      }+      return true;+    }+    return false;+  }++  bool String_Schema::has_interpolants()+  {+    for (auto el : elements()) {+      if (el->is_interpolant()) return true;+    }+    return false;+  }++  size_t String_Schema::hash() const+  {+    if (hash_ == 0) {+      for (auto string : elements())+        hash_combine(hash_, string->hash());+    }+    return hash_;+  }++  void String_Schema::set_delayed(bool delayed)+  {+    is_delayed(delayed);+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  String_Constant::String_Constant(ParserState pstate, std::string val, bool css)+  : String(pstate), quote_mark_(0), value_(read_css_string(val, css)), hash_(0)+  { }+  String_Constant::String_Constant(ParserState pstate, const char* beg, bool css)+  : String(pstate), quote_mark_(0), 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), 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), 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_),+    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 (auto qstr = Cast<String_Quoted>(&rhs)) {+      return value() < qstr->value();+    }+    else if (auto cstr = Cast<String_Constant>(&rhs)) {+      return value() < cstr->value();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool String_Constant::operator== (const Expression& rhs) const+  {+    if (auto qstr = Cast<String_Quoted>(&rhs)) {+      return value() == qstr->value();+    }+    else if (auto cstr = Cast<String_Constant>(&rhs)) {+      return value() == cstr->value();+    }+    return false;+  }++  std::string String_Constant::inspect() const+  {+    return quote(value_, '*');+  }++  void String_Constant::rtrim()+  {+    str_rtrim(value_);+  }++  size_t String_Constant::hash() const+  {+    if (hash_ == 0) {+      hash_ = std::hash<std::string>()(value_);+    }+    return hash_;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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_, &quote_mark_, keep_utf8_escapes, strict_unquoting);+    }+    if (q && quote_mark_) quote_mark_ = q;+  }++  String_Quoted::String_Quoted(const String_Quoted* ptr)+  : String_Constant(ptr)+  { }++  bool String_Quoted::operator< (const Expression& rhs) const+  {+    if (auto qstr = Cast<String_Quoted>(&rhs)) {+      return value() < qstr->value();+    }+    else if (auto cstr = Cast<String_Constant>(&rhs)) {+      return value() < cstr->value();+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool String_Quoted::operator== (const Expression& rhs) const+  {+    if (auto qstr = Cast<String_Quoted>(&rhs)) {+      return value() == qstr->value();+    }+    else if (auto cstr = Cast<String_Constant>(&rhs)) {+      return value() == cstr->value();+    }+    return false;+  }++  std::string String_Quoted::inspect() const+  {+    return quote(value_, '*');+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  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+  {+    if (Cast<Null>(&rhs)) {+      return false;+    }+    // compare/sort by type+    return type() < rhs.type();+  }++  bool Null::operator== (const Expression& rhs) const+  {+    return Cast<Null>(&rhs) != nullptr;+  }++  size_t Null::hash() const+  {+    return -1;+  }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  Parent_Reference::Parent_Reference(ParserState pstate)+  : Value(pstate)+  { concrete_type(PARENT); }++  Parent_Reference::Parent_Reference(const Parent_Reference* ptr)+  : Value(ptr)+  { concrete_type(PARENT); }++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++  IMPLEMENT_AST_OPERATORS(List);+  IMPLEMENT_AST_OPERATORS(Map);+  IMPLEMENT_AST_OPERATORS(Binary_Expression);+  IMPLEMENT_AST_OPERATORS(Function);+  IMPLEMENT_AST_OPERATORS(Function_Call);+  IMPLEMENT_AST_OPERATORS(Variable);+  IMPLEMENT_AST_OPERATORS(Number);+  IMPLEMENT_AST_OPERATORS(Color_RGBA);+  IMPLEMENT_AST_OPERATORS(Color_HSLA);+  IMPLEMENT_AST_OPERATORS(Custom_Error);+  IMPLEMENT_AST_OPERATORS(Custom_Warning);+  IMPLEMENT_AST_OPERATORS(Boolean);+  IMPLEMENT_AST_OPERATORS(String_Schema);+  IMPLEMENT_AST_OPERATORS(String_Constant);+  IMPLEMENT_AST_OPERATORS(String_Quoted);+  IMPLEMENT_AST_OPERATORS(Null);+  IMPLEMENT_AST_OPERATORS(Parent_Reference);++  /////////////////////////////////////////////////////////////////////////+  /////////////////////////////////////////////////////////////////////////++}
+ libsass/src/ast_values.hpp view
@@ -0,0 +1,498 @@+#ifndef SASS_AST_VALUES_H+#define SASS_AST_VALUES_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++namespace Sass {++  //////////////////////////////////////////////////////////////////////+  // Still just an expression, but with a to_string method+  //////////////////////////////////////////////////////////////////////+  class PreValue : public Expression {+  public:+    PreValue(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);+    ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);+    virtual ~PreValue() { }+  };++  //////////////////////////////////////////////////////////////////////+  // base class for values that support operations+  //////////////////////////////////////////////////////////////////////+  class Value : public PreValue {+  public:+    Value(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);++    // Some obects are not meant to be compared+    // ToDo: maybe fallback to pointer comparison?+    virtual bool operator< (const Expression& rhs) const override = 0;+    virtual bool operator== (const Expression& rhs) const override = 0;++    // We can give some reasonable implementations by using+    // inverst operators on the specialized implementations+    virtual bool operator> (const Expression& rhs) const {+      return rhs < *this;+    }+    virtual bool operator!= (const Expression& rhs) const {+      return !(*this == rhs);+    }++    ATTACH_VIRTUAL_AST_OPERATIONS(Value);++  };++  ///////////////////////////////////////////////////////////////////////+  // Lists of values, both comma- and space-separated (distinguished by a+  // type-tag.) Also used to represent variable-length argument lists.+  ///////////////////////////////////////////////////////////////////////+  class List : public Value, public Vectorized<Expression_Obj> {+    void adjust_after_pushing(Expression_Obj e) override { is_expanded(false); }+  private:+    ADD_PROPERTY(enum Sass_Separator, separator)+    ADD_PROPERTY(bool, is_arglist)+    ADD_PROPERTY(bool, is_bracketed)+    ADD_PROPERTY(bool, from_selector)+  public:+    List(ParserState pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false);+    std::string type() const override { return is_arglist_ ? "arglist" : "list"; }+    static std::string type_name() { return "list"; }+    const char* sep_string(bool compressed = false) const {+      return separator() == SASS_SPACE ?+        " " : (compressed ? "," : ", ");+    }+    bool is_invisible() const override { return empty() && !is_bracketed(); }+    Expression_Obj value_at_index(size_t i);++    virtual size_t hash() const override;+    virtual size_t size() const;+    virtual void set_delayed(bool delayed) override;++    virtual bool operator< (const Expression& rhs) const override;+    virtual bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(List)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////////////////////////////////////////////////////+  // Key value paris.+  ///////////////////////////////////////////////////////////////////////+  class Map : public Value, public Hashed<Expression_Obj, Expression_Obj, Map_Obj> {+    void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) override { is_expanded(false); }+  public:+    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;++    virtual bool operator< (const Expression& rhs) const override;+    virtual bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(Map)+    ATTACH_CRTP_PERFORM_METHODS()+  };+++  //////////////////////////////////////////////////////////////////////////+  // Binary expressions. Represents logical, relational, and arithmetic+  // operations. Templatized to avoid large switch statements and repetitive+  // subclassing.+  //////////////////////////////////////////////////////////////////////////+  class Binary_Expression : public PreValue {+  private:+    HASH_PROPERTY(Operand, op)+    HASH_PROPERTY(Expression_Obj, left)+    HASH_PROPERTY(Expression_Obj, right)+    mutable size_t hash_;+  public:+    Binary_Expression(ParserState pstate,+                      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;++    virtual void set_delayed(bool delayed) override;++    virtual bool operator< (const Expression& rhs) const 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()+  };++  ////////////////////////////////////////////////////+  // Function reference.+  ////////////////////////////////////////////////////+  class Function final : public Value {+  public:+    ADD_PROPERTY(Definition_Obj, definition)+    ADD_PROPERTY(bool, is_css)+  public:+    Function(ParserState pstate, Definition_Obj def, bool css);++    std::string type() const override { return "function"; }+    static std::string type_name() { return "function"; }+    bool is_invisible() const override { return true; }++    std::string name();++    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(Function)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////+  // Function calls.+  //////////////////+  class Function_Call final : public PreValue {+    HASH_CONSTREF(String_Obj, sname)+    HASH_PROPERTY(Arguments_Obj, arguments)+    HASH_PROPERTY(Function_Obj, func)+    ADD_PROPERTY(bool, via_call)+    ADD_PROPERTY(void*, cookie)+    mutable size_t hash_;+  public:+    Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie);+    Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func);+    Function_Call(ParserState pstate, std::string n, Arguments_Obj args);++    Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, void* cookie);+    Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, Function_Obj func);+    Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args);++    std::string name() const;+    bool is_css();++    bool operator==(const Expression& rhs) const override;++    size_t hash() const override;++    ATTACH_AST_OPERATIONS(Function_Call)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ///////////////////////+  // Variable references.+  ///////////////////////+  class Variable final : public PreValue {+    ADD_CONSTREF(std::string, name)+  public:+    Variable(ParserState pstate, std::string n);+    virtual bool operator==(const Expression& rhs) const override;+    virtual size_t hash() const override;+    ATTACH_AST_OPERATIONS(Variable)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////+  // Numbers, percentages, dimensions, and colors.+  ////////////////////////////////////////////////+  class Number final : public Value, public Units {+    HASH_PROPERTY(double, value)+    ADD_PROPERTY(bool, zero)+    mutable size_t hash_;+  public:+    Number(ParserState pstate, double val, std::string u = "", bool zero = true);++    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;++    bool operator< (const Number& rhs) const;+    bool operator== (const Number& rhs) const;+    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;+    ATTACH_AST_OPERATIONS(Number)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////+  // Colors.+  //////////+  class Color : public Value {+    ADD_CONSTREF(std::string, disp)+    HASH_PROPERTY(double, a)+  protected:+    mutable size_t hash_;+  public:+    Color(ParserState pstate, double a = 1, const std::string disp = "");++    std::string type() const override { return "color"; }+    static std::string type_name() { return "color"; }++    virtual size_t hash() const override = 0;++    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;++    virtual Color_RGBA* copyAsRGBA() const = 0;+    virtual Color_RGBA* toRGBA() = 0;++    virtual Color_HSLA* copyAsHSLA() const = 0;+    virtual Color_HSLA* toHSLA() = 0;++    ATTACH_VIRTUAL_AST_OPERATIONS(Color)+  };++  //////////+  // Colors.+  //////////+  class Color_RGBA final : public Color {+    HASH_PROPERTY(double, r)+    HASH_PROPERTY(double, g)+    HASH_PROPERTY(double, b)+  public:+    Color_RGBA(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;++    Color_RGBA* copyAsRGBA() const override;+    Color_RGBA* toRGBA() override { return this; }++    Color_HSLA* copyAsHSLA() const override;+    Color_HSLA* toHSLA() override { return copyAsHSLA(); }++    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(Color_RGBA)+    ATTACH_CRTP_PERFORM_METHODS()+  };+++  //////////+  // Colors.+  //////////+  class Color_HSLA final : public Color {+    HASH_PROPERTY(double, h)+    HASH_PROPERTY(double, s)+    HASH_PROPERTY(double, l)+  public:+    Color_HSLA(ParserState pstate, double h, double s, double l, 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;++    Color_RGBA* copyAsRGBA() const override;+    Color_RGBA* toRGBA() override { return copyAsRGBA(); }++    Color_HSLA* copyAsHSLA() const override;+    Color_HSLA* toHSLA() override { return this; }++    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(Color_HSLA)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////////////////+  // Errors from Sass_Values.+  //////////////////////////////+  class Custom_Error final : public Value {+    ADD_CONSTREF(std::string, message)+  public:+    Custom_Error(ParserState pstate, std::string msg);+    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;+    ATTACH_AST_OPERATIONS(Custom_Error)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////////////////+  // Warnings from Sass_Values.+  //////////////////////////////+  class Custom_Warning final : public Value {+    ADD_CONSTREF(std::string, message)+  public:+    Custom_Warning(ParserState pstate, std::string msg);+    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;+    ATTACH_AST_OPERATIONS(Custom_Warning)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////+  // Booleans.+  ////////////+  class Boolean final : public Value {+    HASH_PROPERTY(bool, value)+    mutable size_t hash_;+  public:+    Boolean(ParserState pstate, bool val);+    operator bool() override { return value_; }++    std::string type() const override { return "bool"; }+    static std::string type_name() { return "bool"; }++    size_t hash() const override;++    bool is_false() override { return !value_; }++    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(Boolean)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////////////////////+  // Abstract base class for Sass string values. Includes interpolated and+  // "flat" strings.+  ////////////////////////////////////////////////////////////////////////+  class String : public Value {+  public:+    String(ParserState pstate, bool delayed = false);+    static std::string type_name() { return "string"; }+    virtual ~String() = 0;+    virtual void rtrim() = 0;+    virtual bool operator<(const Expression& rhs) const override {+      return this->to_string() < rhs.to_string();+    };+    virtual bool operator==(const Expression& rhs) const override {+      return this->to_string() == rhs.to_string();+    };+    ATTACH_VIRTUAL_AST_OPERATIONS(String);+    ATTACH_CRTP_PERFORM_METHODS()+  };+  inline String::~String() { };++  ///////////////////////////////////////////////////////////////////////+  // Interpolated strings. Meant to be reduced to flat strings during the+  // evaluation phase.+  ///////////////////////////////////////////////////////////////////////+  class String_Schema final : public String, public Vectorized<PreValue_Obj> {+    ADD_PROPERTY(bool, css)+    mutable size_t hash_;+  public:+    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;++    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;+    bool operator==(const Expression& rhs) const override;+    ATTACH_AST_OPERATIONS(String_Schema)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////+  // Flat strings -- the lowest level of raw textual data.+  ////////////////////////////////////////////////////////+  class String_Constant : public String {+    ADD_PROPERTY(char, quote_mark)+    HASH_CONSTREF(std::string, value)+  protected:+    mutable size_t hash_;+  public:+    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;+    bool operator< (const Expression& rhs) const override;+    bool operator==(const Expression& rhs) const override;+    // quotes are forced on inspection+    virtual std::string inspect() const override;+    ATTACH_AST_OPERATIONS(String_Constant)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  ////////////////////////////////////////////////////////+  // Possibly quoted string (unquote on instantiation)+  ////////////////////////////////////////////////////////+  class String_Quoted final : public String_Constant {+  public:+    String_Quoted(ParserState pstate, std::string val, char q = 0,+      bool keep_utf8_escapes = false, bool skip_unquoting = false,+      bool strict_unquoting = true, bool css = true);+    bool operator< (const Expression& rhs) const override;+    bool operator==(const Expression& rhs) const override;+    // quotes are forced on inspection+    std::string inspect() const override;+    ATTACH_AST_OPERATIONS(String_Quoted)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////+  // The null value.+  //////////////////+  class Null final : public Value {+  public:+    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;++    bool operator< (const Expression& rhs) const override;+    bool operator== (const Expression& rhs) const override;++    ATTACH_AST_OPERATIONS(Null)+    ATTACH_CRTP_PERFORM_METHODS()+  };++  //////////////////////////////////+  // The Parent Reference Expression.+  //////////////////////////////////+  class Parent_Reference final : public Value {+  public:+    Parent_Reference(ParserState pstate);+    std::string type() const override { return "parent"; }+    static std::string type_name() { return "parent"; }+    bool operator< (const Expression& rhs) const override {+      return false; // they are always equal+    }+    bool operator==(const Expression& rhs) const override {+      return true; // they are always equal+    };+    ATTACH_AST_OPERATIONS(Parent_Reference)+    ATTACH_CRTP_PERFORM_METHODS()+  };++}++#endif
libsass/src/backtrace.cpp view
@@ -24,6 +24,8 @@         ss << indent;         ss << "on line ";         ss << trace.pstate.line + 1;+        ss << ":";+        ss << trace.pstate.column + 1;         ss << " of " << rel_path;         // ss << trace.caller;         first = false;@@ -33,6 +35,8 @@         ss << indent;         ss << "from line ";         ss << trace.pstate.line + 1;+        ss << ":";+        ss << trace.pstate.column + 1;         ss << " of " << rel_path;       } 
libsass/src/base64vlq.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "base64vlq.hpp"  namespace Sass {
libsass/src/bind.cpp view
@@ -1,6 +1,7 @@ #include "sass.hpp" #include "bind.hpp" #include "ast.hpp"+#include "backtrace.hpp" #include "context.hpp" #include "expand.hpp" #include "eval.hpp"@@ -10,7 +11,7 @@  namespace Sass { -  void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval)+  void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces)   {     std::string callee(type + " " + name); @@ -54,7 +55,7 @@         std::stringstream msg;         msg << "wrong number of arguments (" << LA << " for " << LP << ")";         msg << " for `" << name << "'";-        return error(msg.str(), as->pstate(), eval->exp.traces);+        return error(msg.str(), as->pstate(), traces);       }       Parameter_Obj p = ps->at(ip); @@ -66,7 +67,7 @@           // We should always get a list for rest arguments           if (List_Obj rest = Cast<List>(a->value())) {               // create a new list object for wrapped items-              List_Ptr arglist = SASS_MEMORY_NEW(List,+              List* arglist = SASS_MEMORY_NEW(List,                                               p->pstate(),                                               0,                                               rest->separator(),@@ -94,7 +95,7 @@         } else if (a->is_keyword_argument()) {            // expand keyword arguments into their parameters-          List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true);+          List* arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true);           env->local_frame()[p->name()] = arglist;           Map_Obj argmap = Cast<Map>(a->value());           for (auto key : argmap->keys()) {@@ -107,8 +108,8 @@                                               false,                                               false));             } else {-              eval->exp.traces.push_back(Backtrace(key->pstate()));-              throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);+              traces.push_back(Backtrace(key->pstate()));+              throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a);             }           } @@ -167,7 +168,7 @@         }         // consumed parameter         ++ip;-        // no more paramaters+        // no more parameters         break;       } @@ -203,7 +204,7 @@         // otherwise move one of the rest args into the param, converting to argument if necessary         Expression_Obj obj = arglist->at(0);         if (!(a = Cast<Argument>(obj))) {-          Expression_Ptr a_to_convert = obj;+          Expression* a_to_convert = obj;           a = SASS_MEMORY_NEW(Argument,                               a_to_convert->pstate(),                               a_to_convert,@@ -220,17 +221,17 @@         Map_Obj argmap = Cast<Map>(a->value());          for (auto key : argmap->keys()) {-          String_Constant_Ptr val = Cast<String_Constant>(key);+          String_Constant* val = Cast<String_Constant>(key);           if (val == NULL) {-            eval->exp.traces.push_back(Backtrace(key->pstate()));-            throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);+            traces.push_back(Backtrace(key->pstate()));+            throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a);           }           std::string param = "$" + unquote(val->value());            if (!param_map.count(param)) {             std::stringstream msg;             msg << callee << " has no parameter named " << param;-            error(msg.str(), a->pstate(), eval->exp.traces);+            error(msg.str(), a->pstate(), traces);           }           env->local_frame()[param] = argmap->at(key);         }@@ -245,7 +246,7 @@           std::stringstream msg;           msg << "parameter " << p->name()           << " provided more than once in call to " << callee;-          error(msg.str(), a->pstate(), eval->exp.traces);+          error(msg.str(), a->pstate(), traces);         }         // ordinal arg -- bind it to the next param         env->local_frame()[p->name()] = a->value();@@ -259,7 +260,7 @@           } else {             std::stringstream msg;             msg << callee << " has no parameter named " << a->name();-            error(msg.str(), a->pstate(), eval->exp.traces);+            error(msg.str(), a->pstate(), traces);           }         }         if (param_map[a->name()]) {@@ -267,14 +268,14 @@             std::stringstream msg;             msg << "argument " << a->name() << " of " << callee                 << "cannot be used as named argument";-            error(msg.str(), a->pstate(), eval->exp.traces);+            error(msg.str(), a->pstate(), traces);           }         }         if (env->has_local(a->name())) {           std::stringstream msg;           msg << "parameter " << p->name()               << "provided more than once in call to " << callee;-          error(msg.str(), a->pstate(), eval->exp.traces);+          error(msg.str(), a->pstate(), traces);         }         env->local_frame()[a->name()] = a->value();       }@@ -294,12 +295,12 @@           env->local_frame()[leftover->name()] = varargs;         }         else if (leftover->default_value()) {-          Expression_Ptr dv = leftover->default_value()->perform(eval);+          Expression* dv = leftover->default_value()->perform(eval);           env->local_frame()[leftover->name()] = dv;         }         else {           // param is unbound and has no default value -- error-          throw Exception::MissingArgument(as->pstate(), eval->exp.traces, name, leftover->name(), type);+          throw Exception::MissingArgument(as->pstate(), traces, name, leftover->name(), type);         }       }     }
libsass/src/bind.hpp view
@@ -2,12 +2,14 @@ #define SASS_BIND_H  #include <string>+#include "backtrace.hpp" #include "environment.hpp" #include "ast_fwd_decl.hpp"  namespace Sass { -  void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Context*, Env*, Eval*);+  void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Env*, Eval*, Backtraces& traces);+ }  #endif
+ libsass/src/c2ast.cpp view
@@ -0,0 +1,64 @@+#include "ast.hpp"+#include "units.hpp"+#include "position.hpp"+#include "backtrace.hpp"+#include "sass/values.h"+#include "ast_fwd_decl.hpp"+#include "error_handling.hpp"++namespace Sass {++  Value* c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate)+  {+    using std::strlen;+    using std::strcpy;+    Value* e = NULL;+    switch (sass_value_get_tag(v)) {+      case SASS_BOOLEAN: {+        e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));+      } break;+      case SASS_NUMBER: {+        e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));+      } break;+      case SASS_COLOR: {+        e = SASS_MEMORY_NEW(Color_RGBA, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));+      } break;+      case SASS_STRING: {+        if (sass_string_is_quoted(v))+          e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));+        else {+          e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));+        }+      } break;+      case SASS_LIST: {+        List* l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));+        for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {+          l->append(c2ast(sass_list_get_value(v, i), traces, pstate));+        }+        l->is_bracketed(sass_list_get_is_bracketed(v));+        e = l;+      } break;+      case SASS_MAP: {+        Map* m = SASS_MEMORY_NEW(Map, pstate);+        for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {+          *m << std::make_pair(+            c2ast(sass_map_get_key(v, i), traces, pstate),+            c2ast(sass_map_get_value(v, i), traces, pstate));+        }+        e = m;+      } break;+      case SASS_NULL: {+        e = SASS_MEMORY_NEW(Null, pstate);+      } break;+      case SASS_ERROR: {+        error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces);+      } break;+      case SASS_WARNING: {+        error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces);+      } break;+      default: break;+    }+    return e;+  }++}
+ libsass/src/c2ast.hpp view
@@ -0,0 +1,14 @@+#ifndef SASS_C2AST_H+#define SASS_C2AST_H++#include "position.hpp"+#include "backtrace.hpp"+#include "ast_fwd_decl.hpp"++namespace Sass {++  Value* c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate);++}++#endif
libsass/src/cencode.c view
@@ -46,9 +46,8 @@ 			result = (fragment & 0x0fc) >> 2; 			*codechar++ = base64_encode_value(result); 			result = (fragment & 0x003) << 4;-			#ifndef _MSC_VER-				/* fall through */-			#endif+			/* fall through */+ 	case step_B: 			if (plainchar == plaintextend) 			{@@ -60,9 +59,8 @@ 			result |= (fragment & 0x0f0) >> 4; 			*codechar++ = base64_encode_value(result); 			result = (fragment & 0x00f) << 2;-			#ifndef _MSC_VER-				/* fall through */-			#endif+			/* fall through */+ 	case step_C: 			if (plainchar == plaintextend) 			{
libsass/src/check_nesting.cpp view
@@ -1,31 +1,32 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <vector>-+#include "ast.hpp" #include "check_nesting.hpp"  namespace Sass {    CheckNesting::CheckNesting()-  : parents(std::vector<Statement_Ptr>()),+  : parents(std::vector<Statement*>()),     traces(std::vector<Backtrace>()),     parent(0), current_mixin_definition(0)   { } -  void error(AST_Node_Ptr node, Backtraces traces, std::string msg) {+  void error(AST_Node* node, Backtraces traces, std::string msg) {     traces.push_back(Backtrace(node->pstate()));     throw Exception::InvalidSass(node->pstate(), traces, msg);   } -  Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)+  Statement* CheckNesting::visit_children(Statement* parent)   {-    Statement_Ptr old_parent = this->parent;+    Statement* old_parent = this->parent; -    if (At_Root_Block_Ptr root = Cast<At_Root_Block>(parent)) {-      std::vector<Statement_Ptr> old_parents = this->parents;-      std::vector<Statement_Ptr> new_parents;+    if (At_Root_Block* root = Cast<At_Root_Block>(parent)) {+      std::vector<Statement*> old_parents = this->parents;+      std::vector<Statement*> new_parents;        for (size_t i = 0, L = this->parents.size(); i < L; i++) {-        Statement_Ptr p = this->parents.at(i);+        Statement* p = this->parents.at(i);         if (!root->exclude_node(p)) {           new_parents.push_back(p);         }@@ -33,8 +34,8 @@       this->parents = new_parents;        for (size_t i = this->parents.size(); i > 0; i--) {-        Statement_Ptr p = 0;-        Statement_Ptr gp = 0;+        Statement* p = 0;+        Statement* gp = 0;         if (i > 0) p = this->parents.at(i - 1);         if (i > 1) gp = this->parents.at(i - 2); @@ -44,8 +45,8 @@         }       } -      At_Root_Block_Ptr ar = Cast<At_Root_Block>(parent);-      Block_Ptr ret = ar->block();+      At_Root_Block* ar = Cast<At_Root_Block>(parent);+      Block* ret = ar->block();        if (ret != NULL) {         for (auto n : ret->elements()) {@@ -65,16 +66,16 @@      this->parents.push_back(parent); -    Block_Ptr b = Cast<Block>(parent);+    Block* b = Cast<Block>(parent); -    if (Trace_Ptr trace = Cast<Trace>(parent)) {+    if (Trace* trace = Cast<Trace>(parent)) {       if (trace->type() == 'i') {         this->traces.push_back(Backtrace(trace->pstate()));       }     }      if (!b) {-      if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {+      if (Has_Block* bb = Cast<Has_Block>(parent)) {         b = bb->block();       }     }@@ -88,7 +89,7 @@     this->parent = old_parent;     this->parents.pop_back(); -    if (Trace_Ptr trace = Cast<Trace>(parent)) {+    if (Trace* trace = Cast<Trace>(parent)) {       if (trace->type() == 'i') {         this->traces.pop_back();       }@@ -98,12 +99,12 @@   }  -  Statement_Ptr CheckNesting::operator()(Block_Ptr b)+  Statement* CheckNesting::operator()(Block* b)   {     return this->visit_children(b);   } -  Statement_Ptr CheckNesting::operator()(Definition_Ptr n)+  Statement* CheckNesting::operator()(Definition* n)   {     if (!this->should_visit(n)) return NULL;     if (!is_mixin(n)) {@@ -111,7 +112,7 @@       return n;     } -    Definition_Ptr old_mixin_definition = this->current_mixin_definition;+    Definition* old_mixin_definition = this->current_mixin_definition;     this->current_mixin_definition = n;      visit_children(n);@@ -121,25 +122,18 @@     return n;   } -  Statement_Ptr CheckNesting::operator()(If_Ptr i)+  Statement* CheckNesting::operator()(If* i)   {     this->visit_children(i); -    if (Block_Ptr b = Cast<Block>(i->alternative())) {+    if (Block* b = Cast<Block>(i->alternative())) {       for (auto n : b->elements()) n->perform(this);     }      return i;   } -  Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)-  {-    Block_Ptr b1 = Cast<Block>(s);-    Has_Block_Ptr b2 = Cast<Has_Block>(s);-    return b1 || b2 ? visit_children(s) : s;-  }--  bool CheckNesting::should_visit(Statement_Ptr node)+  bool CheckNesting::should_visit(Statement* node)   {     if (!this->parent) return true; @@ -149,7 +143,7 @@     if (is_charset(node))     { this->invalid_charset_parent(this->parent, node); } -    if (Cast<Extension>(node))+    if (Cast<ExtendRule>(node))     { this->invalid_extend_parent(this->parent, node); }      // if (Cast<Import>(node))@@ -164,7 +158,7 @@     if (this->is_function(this->parent))     { this->invalid_function_child(node); } -    if (Declaration_Ptr d = Cast<Declaration>(node))+    if (Declaration* d = Cast<Declaration>(node))     {       this->invalid_prop_parent(this->parent, node);       this->invalid_value_child(d->value());@@ -179,14 +173,14 @@     return true;   } -  void CheckNesting::invalid_content_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_content_parent(Statement* parent, AST_Node* node)   {     if (!this->current_mixin_definition) {       error(node, traces, "@content may only be used within a mixin.");     }   } -  void CheckNesting::invalid_charset_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_charset_parent(Statement* parent, AST_Node* node)   {     if (!(         is_root_node(parent)@@ -195,7 +189,7 @@     }   } -  void CheckNesting::invalid_extend_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_extend_parent(Statement* parent, AST_Node* node)   {     if (!(         Cast<Ruleset>(parent) ||@@ -206,7 +200,7 @@     }   } -  // void CheckNesting::invalid_import_parent(Statement_Ptr parent, AST_Node_Ptr node)+  // void CheckNesting::invalid_import_parent(Statement* parent, AST_Node* node)   // {   //   for (auto pp : this->parents) {   //     if (@@ -231,9 +225,9 @@   //   }   // } -  void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_mixin_definition_parent(Statement* parent, AST_Node* node)   {-    for (Statement_Ptr pp : this->parents) {+    for (Statement* pp : this->parents) {       if (           Cast<Each>(pp) ||           Cast<For>(pp) ||@@ -248,9 +242,9 @@     }   } -  void CheckNesting::invalid_function_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_function_parent(Statement* parent, AST_Node* node)   {-    for (Statement_Ptr pp : this->parents) {+    for (Statement* pp : this->parents) {       if (           Cast<Each>(pp) ||           Cast<For>(pp) ||@@ -265,7 +259,7 @@     }   } -  void CheckNesting::invalid_function_child(Statement_Ptr child)+  void CheckNesting::invalid_function_child(Statement* child)   {     if (!(         Cast<Each>(child) ||@@ -286,7 +280,7 @@     }   } -  void CheckNesting::invalid_prop_child(Statement_Ptr child)+  void CheckNesting::invalid_prop_child(Statement* child)   {     if (!(         Cast<Each>(child) ||@@ -302,7 +296,7 @@     }   } -  void CheckNesting::invalid_prop_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_prop_parent(Statement* parent, AST_Node* node)   {     if (!(         is_mixin(parent) ||@@ -316,13 +310,13 @@     }   } -  void CheckNesting::invalid_value_child(AST_Node_Ptr d)+  void CheckNesting::invalid_value_child(AST_Node* d)   {-    if (Map_Ptr m = Cast<Map>(d)) {+    if (Map* m = Cast<Map>(d)) {       traces.push_back(Backtrace(m->pstate()));       throw Exception::InvalidValue(traces, *m);     }-    if (Number_Ptr n = Cast<Number>(d)) {+    if (Number* n = Cast<Number>(d)) {       if (!n->is_valid_css_unit()) {         traces.push_back(Backtrace(n->pstate()));         throw Exception::InvalidValue(traces, *n);@@ -333,14 +327,14 @@    } -  void CheckNesting::invalid_return_parent(Statement_Ptr parent, AST_Node_Ptr node)+  void CheckNesting::invalid_return_parent(Statement* parent, AST_Node* node)   {     if (!this->is_function(parent)) {       error(node, traces, "@return may only be used within a function.");     }   } -  bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)+  bool CheckNesting::is_transparent_parent(Statement* parent, Statement* grandparent)   {     bool parent_bubbles = parent && parent->bubbles(); @@ -357,42 +351,43 @@            valid_bubble_node;   } -  bool CheckNesting::is_charset(Statement_Ptr n)+  bool CheckNesting::is_charset(Statement* n)   {-    Directive_Ptr d = Cast<Directive>(n);+    Directive* d = Cast<Directive>(n);     return d && d->keyword() == "charset";   } -  bool CheckNesting::is_mixin(Statement_Ptr n)+  bool CheckNesting::is_mixin(Statement* n)   {-    Definition_Ptr def = Cast<Definition>(n);+    Definition* def = Cast<Definition>(n);     return def && def->type() == Definition::MIXIN;   } -  bool CheckNesting::is_function(Statement_Ptr n)+  bool CheckNesting::is_function(Statement* n)   {-    Definition_Ptr def = Cast<Definition>(n);+    Definition* def = Cast<Definition>(n);     return def && def->type() == Definition::FUNCTION;   } -  bool CheckNesting::is_root_node(Statement_Ptr n)+  bool CheckNesting::is_root_node(Statement* n)   {     if (Cast<Ruleset>(n)) return false; -    Block_Ptr b = Cast<Block>(n);+    Block* b = Cast<Block>(n);     return b && b->is_root();   } -  bool CheckNesting::is_at_root_node(Statement_Ptr n)+  bool CheckNesting::is_at_root_node(Statement* n)   {     return Cast<At_Root_Block>(n) != NULL;   } -  bool CheckNesting::is_directive_node(Statement_Ptr n)+  bool CheckNesting::is_directive_node(Statement* n)   {     return Cast<Directive>(n) ||            Cast<Import>(n) ||-           Cast<Media_Block>(n) ||-           Cast<Supports_Block>(n);+      Cast<MediaRule>(n) ||+      Cast<CssMediaRule>(n) ||+      Cast<Supports_Block>(n);   } }
libsass/src/check_nesting.hpp view
@@ -1,63 +1,68 @@ #ifndef SASS_CHECK_NESTING_H #define SASS_CHECK_NESTING_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp" #include "ast.hpp" #include "operation.hpp"+#include <vector>  namespace Sass { -  class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> {+  class CheckNesting : public Operation_CRTP<Statement*, CheckNesting> { -    std::vector<Statement_Ptr>  parents;+    std::vector<Statement*>  parents;     Backtraces                  traces;-    Statement_Ptr               parent;-    Definition_Ptr              current_mixin_definition;+    Statement*               parent;+    Definition*              current_mixin_definition; -    Statement_Ptr fallback_impl(Statement_Ptr);-    Statement_Ptr before(Statement_Ptr);-    Statement_Ptr visit_children(Statement_Ptr);+    Statement* before(Statement*);+    Statement* visit_children(Statement*);    public:     CheckNesting();     ~CheckNesting() { } -    Statement_Ptr operator()(Block_Ptr);-    Statement_Ptr operator()(Definition_Ptr);-    Statement_Ptr operator()(If_Ptr);+    Statement* operator()(Block*);+    Statement* operator()(Definition*);+    Statement* operator()(If*);      template <typename U>-    Statement_Ptr fallback(U x) {-      Statement_Ptr n = Cast<Statement>(x);-      if (this->should_visit(n)) {-        return fallback_impl(n);+    Statement* fallback(U x) {+      Statement* s = Cast<Statement>(x);+      if (s && this->should_visit(s)) {+        Block* b1 = Cast<Block>(s);+        Has_Block* b2 = Cast<Has_Block>(s);+        if (b1 || b2) return visit_children(s);       }-      return NULL;+      return s;     }    private:-    void invalid_content_parent(Statement_Ptr, AST_Node_Ptr);-    void invalid_charset_parent(Statement_Ptr, AST_Node_Ptr);-    void invalid_extend_parent(Statement_Ptr, AST_Node_Ptr);-    // void invalid_import_parent(Statement_Ptr);-    void invalid_mixin_definition_parent(Statement_Ptr, AST_Node_Ptr);-    void invalid_function_parent(Statement_Ptr, AST_Node_Ptr);+    void invalid_content_parent(Statement*, AST_Node*);+    void invalid_charset_parent(Statement*, AST_Node*);+    void invalid_extend_parent(Statement*, AST_Node*);+    // void invalid_import_parent(Statement*);+    void invalid_mixin_definition_parent(Statement*, AST_Node*);+    void invalid_function_parent(Statement*, AST_Node*); -    void invalid_function_child(Statement_Ptr);-    void invalid_prop_child(Statement_Ptr);-    void invalid_prop_parent(Statement_Ptr, AST_Node_Ptr);-    void invalid_return_parent(Statement_Ptr, AST_Node_Ptr);-    void invalid_value_child(AST_Node_Ptr);+    void invalid_function_child(Statement*);+    void invalid_prop_child(Statement*);+    void invalid_prop_parent(Statement*, AST_Node*);+    void invalid_return_parent(Statement*, AST_Node*);+    void invalid_value_child(AST_Node*); -    bool is_transparent_parent(Statement_Ptr, Statement_Ptr);+    bool is_transparent_parent(Statement*, Statement*); -    bool should_visit(Statement_Ptr);+    bool should_visit(Statement*); -    bool is_charset(Statement_Ptr);-    bool is_mixin(Statement_Ptr);-    bool is_function(Statement_Ptr);-    bool is_root_node(Statement_Ptr);-    bool is_at_root_node(Statement_Ptr);-    bool is_directive_node(Statement_Ptr);+    bool is_charset(Statement*);+    bool is_mixin(Statement*);+    bool is_function(Statement*);+    bool is_root_node(Statement*);+    bool is_at_root_node(Statement*);+    bool is_directive_node(Statement*);   };  }
libsass/src/color_maps.cpp view
@@ -1,6 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "color_maps.hpp"+#include "util_string.hpp"  namespace Sass { @@ -159,158 +163,158 @@    namespace Colors {     const ParserState color_table("[COLOR TABLE]");-    const Color aliceblue(color_table, 240, 248, 255, 1);-    const Color antiquewhite(color_table, 250, 235, 215, 1);-    const Color cyan(color_table, 0, 255, 255, 1);-    const Color aqua(color_table, 0, 255, 255, 1);-    const Color aquamarine(color_table, 127, 255, 212, 1);-    const Color azure(color_table, 240, 255, 255, 1);-    const Color beige(color_table, 245, 245, 220, 1);-    const Color bisque(color_table, 255, 228, 196, 1);-    const Color black(color_table, 0, 0, 0, 1);-    const Color blanchedalmond(color_table, 255, 235, 205, 1);-    const Color blue(color_table, 0, 0, 255, 1);-    const Color blueviolet(color_table, 138, 43, 226, 1);-    const Color brown(color_table, 165, 42, 42, 1);-    const Color burlywood(color_table, 222, 184, 135, 1);-    const Color cadetblue(color_table, 95, 158, 160, 1);-    const Color chartreuse(color_table, 127, 255, 0, 1);-    const Color chocolate(color_table, 210, 105, 30, 1);-    const Color coral(color_table, 255, 127, 80, 1);-    const Color cornflowerblue(color_table, 100, 149, 237, 1);-    const Color cornsilk(color_table, 255, 248, 220, 1);-    const Color crimson(color_table, 220, 20, 60, 1);-    const Color darkblue(color_table, 0, 0, 139, 1);-    const Color darkcyan(color_table, 0, 139, 139, 1);-    const Color darkgoldenrod(color_table, 184, 134, 11, 1);-    const Color darkgray(color_table, 169, 169, 169, 1);-    const Color darkgrey(color_table, 169, 169, 169, 1);-    const Color darkgreen(color_table, 0, 100, 0, 1);-    const Color darkkhaki(color_table, 189, 183, 107, 1);-    const Color darkmagenta(color_table, 139, 0, 139, 1);-    const Color darkolivegreen(color_table, 85, 107, 47, 1);-    const Color darkorange(color_table, 255, 140, 0, 1);-    const Color darkorchid(color_table, 153, 50, 204, 1);-    const Color darkred(color_table, 139, 0, 0, 1);-    const Color darksalmon(color_table, 233, 150, 122, 1);-    const Color darkseagreen(color_table, 143, 188, 143, 1);-    const Color darkslateblue(color_table, 72, 61, 139, 1);-    const Color darkslategray(color_table, 47, 79, 79, 1);-    const Color darkslategrey(color_table, 47, 79, 79, 1);-    const Color darkturquoise(color_table, 0, 206, 209, 1);-    const Color darkviolet(color_table, 148, 0, 211, 1);-    const Color deeppink(color_table, 255, 20, 147, 1);-    const Color deepskyblue(color_table, 0, 191, 255, 1);-    const Color dimgray(color_table, 105, 105, 105, 1);-    const Color dimgrey(color_table, 105, 105, 105, 1);-    const Color dodgerblue(color_table, 30, 144, 255, 1);-    const Color firebrick(color_table, 178, 34, 34, 1);-    const Color floralwhite(color_table, 255, 250, 240, 1);-    const Color forestgreen(color_table, 34, 139, 34, 1);-    const Color magenta(color_table, 255, 0, 255, 1);-    const Color fuchsia(color_table, 255, 0, 255, 1);-    const Color gainsboro(color_table, 220, 220, 220, 1);-    const Color ghostwhite(color_table, 248, 248, 255, 1);-    const Color gold(color_table, 255, 215, 0, 1);-    const Color goldenrod(color_table, 218, 165, 32, 1);-    const Color gray(color_table, 128, 128, 128, 1);-    const Color grey(color_table, 128, 128, 128, 1);-    const Color green(color_table, 0, 128, 0, 1);-    const Color greenyellow(color_table, 173, 255, 47, 1);-    const Color honeydew(color_table, 240, 255, 240, 1);-    const Color hotpink(color_table, 255, 105, 180, 1);-    const Color indianred(color_table, 205, 92, 92, 1);-    const Color indigo(color_table, 75, 0, 130, 1);-    const Color ivory(color_table, 255, 255, 240, 1);-    const Color khaki(color_table, 240, 230, 140, 1);-    const Color lavender(color_table, 230, 230, 250, 1);-    const Color lavenderblush(color_table, 255, 240, 245, 1);-    const Color lawngreen(color_table, 124, 252, 0, 1);-    const Color lemonchiffon(color_table, 255, 250, 205, 1);-    const Color lightblue(color_table, 173, 216, 230, 1);-    const Color lightcoral(color_table, 240, 128, 128, 1);-    const Color lightcyan(color_table, 224, 255, 255, 1);-    const Color lightgoldenrodyellow(color_table, 250, 250, 210, 1);-    const Color lightgray(color_table, 211, 211, 211, 1);-    const Color lightgrey(color_table, 211, 211, 211, 1);-    const Color lightgreen(color_table, 144, 238, 144, 1);-    const Color lightpink(color_table, 255, 182, 193, 1);-    const Color lightsalmon(color_table, 255, 160, 122, 1);-    const Color lightseagreen(color_table, 32, 178, 170, 1);-    const Color lightskyblue(color_table, 135, 206, 250, 1);-    const Color lightslategray(color_table, 119, 136, 153, 1);-    const Color lightslategrey(color_table, 119, 136, 153, 1);-    const Color lightsteelblue(color_table, 176, 196, 222, 1);-    const Color lightyellow(color_table, 255, 255, 224, 1);-    const Color lime(color_table, 0, 255, 0, 1);-    const Color limegreen(color_table, 50, 205, 50, 1);-    const Color linen(color_table, 250, 240, 230, 1);-    const Color maroon(color_table, 128, 0, 0, 1);-    const Color mediumaquamarine(color_table, 102, 205, 170, 1);-    const Color mediumblue(color_table, 0, 0, 205, 1);-    const Color mediumorchid(color_table, 186, 85, 211, 1);-    const Color mediumpurple(color_table, 147, 112, 219, 1);-    const Color mediumseagreen(color_table, 60, 179, 113, 1);-    const Color mediumslateblue(color_table, 123, 104, 238, 1);-    const Color mediumspringgreen(color_table, 0, 250, 154, 1);-    const Color mediumturquoise(color_table, 72, 209, 204, 1);-    const Color mediumvioletred(color_table, 199, 21, 133, 1);-    const Color midnightblue(color_table, 25, 25, 112, 1);-    const Color mintcream(color_table, 245, 255, 250, 1);-    const Color mistyrose(color_table, 255, 228, 225, 1);-    const Color moccasin(color_table, 255, 228, 181, 1);-    const Color navajowhite(color_table, 255, 222, 173, 1);-    const Color navy(color_table, 0, 0, 128, 1);-    const Color oldlace(color_table, 253, 245, 230, 1);-    const Color olive(color_table, 128, 128, 0, 1);-    const Color olivedrab(color_table, 107, 142, 35, 1);-    const Color orange(color_table, 255, 165, 0, 1);-    const Color orangered(color_table, 255, 69, 0, 1);-    const Color orchid(color_table, 218, 112, 214, 1);-    const Color palegoldenrod(color_table, 238, 232, 170, 1);-    const Color palegreen(color_table, 152, 251, 152, 1);-    const Color paleturquoise(color_table, 175, 238, 238, 1);-    const Color palevioletred(color_table, 219, 112, 147, 1);-    const Color papayawhip(color_table, 255, 239, 213, 1);-    const Color peachpuff(color_table, 255, 218, 185, 1);-    const Color peru(color_table, 205, 133, 63, 1);-    const Color pink(color_table, 255, 192, 203, 1);-    const Color plum(color_table, 221, 160, 221, 1);-    const Color powderblue(color_table, 176, 224, 230, 1);-    const Color purple(color_table, 128, 0, 128, 1);-    const Color red(color_table, 255, 0, 0, 1);-    const Color rosybrown(color_table, 188, 143, 143, 1);-    const Color royalblue(color_table, 65, 105, 225, 1);-    const Color saddlebrown(color_table, 139, 69, 19, 1);-    const Color salmon(color_table, 250, 128, 114, 1);-    const Color sandybrown(color_table, 244, 164, 96, 1);-    const Color seagreen(color_table, 46, 139, 87, 1);-    const Color seashell(color_table, 255, 245, 238, 1);-    const Color sienna(color_table, 160, 82, 45, 1);-    const Color silver(color_table, 192, 192, 192, 1);-    const Color skyblue(color_table, 135, 206, 235, 1);-    const Color slateblue(color_table, 106, 90, 205, 1);-    const Color slategray(color_table, 112, 128, 144, 1);-    const Color slategrey(color_table, 112, 128, 144, 1);-    const Color snow(color_table, 255, 250, 250, 1);-    const Color springgreen(color_table, 0, 255, 127, 1);-    const Color steelblue(color_table, 70, 130, 180, 1);-    const Color tan(color_table, 210, 180, 140, 1);-    const Color teal(color_table, 0, 128, 128, 1);-    const Color thistle(color_table, 216, 191, 216, 1);-    const Color tomato(color_table, 255, 99, 71, 1);-    const Color turquoise(color_table, 64, 224, 208, 1);-    const Color violet(color_table, 238, 130, 238, 1);-    const Color wheat(color_table, 245, 222, 179, 1);-    const Color white(color_table, 255, 255, 255, 1);-    const Color whitesmoke(color_table, 245, 245, 245, 1);-    const Color yellow(color_table, 255, 255, 0, 1);-    const Color yellowgreen(color_table, 154, 205, 50, 1);-    const Color rebeccapurple(color_table, 102, 51, 153, 1);-    const Color transparent(color_table, 0, 0, 0, 0);+    const Color_RGBA aliceblue(color_table, 240, 248, 255, 1);+    const Color_RGBA antiquewhite(color_table, 250, 235, 215, 1);+    const Color_RGBA cyan(color_table, 0, 255, 255, 1);+    const Color_RGBA aqua(color_table, 0, 255, 255, 1);+    const Color_RGBA aquamarine(color_table, 127, 255, 212, 1);+    const Color_RGBA azure(color_table, 240, 255, 255, 1);+    const Color_RGBA beige(color_table, 245, 245, 220, 1);+    const Color_RGBA bisque(color_table, 255, 228, 196, 1);+    const Color_RGBA black(color_table, 0, 0, 0, 1);+    const Color_RGBA blanchedalmond(color_table, 255, 235, 205, 1);+    const Color_RGBA blue(color_table, 0, 0, 255, 1);+    const Color_RGBA blueviolet(color_table, 138, 43, 226, 1);+    const Color_RGBA brown(color_table, 165, 42, 42, 1);+    const Color_RGBA burlywood(color_table, 222, 184, 135, 1);+    const Color_RGBA cadetblue(color_table, 95, 158, 160, 1);+    const Color_RGBA chartreuse(color_table, 127, 255, 0, 1);+    const Color_RGBA chocolate(color_table, 210, 105, 30, 1);+    const Color_RGBA coral(color_table, 255, 127, 80, 1);+    const Color_RGBA cornflowerblue(color_table, 100, 149, 237, 1);+    const Color_RGBA cornsilk(color_table, 255, 248, 220, 1);+    const Color_RGBA crimson(color_table, 220, 20, 60, 1);+    const Color_RGBA darkblue(color_table, 0, 0, 139, 1);+    const Color_RGBA darkcyan(color_table, 0, 139, 139, 1);+    const Color_RGBA darkgoldenrod(color_table, 184, 134, 11, 1);+    const Color_RGBA darkgray(color_table, 169, 169, 169, 1);+    const Color_RGBA darkgrey(color_table, 169, 169, 169, 1);+    const Color_RGBA darkgreen(color_table, 0, 100, 0, 1);+    const Color_RGBA darkkhaki(color_table, 189, 183, 107, 1);+    const Color_RGBA darkmagenta(color_table, 139, 0, 139, 1);+    const Color_RGBA darkolivegreen(color_table, 85, 107, 47, 1);+    const Color_RGBA darkorange(color_table, 255, 140, 0, 1);+    const Color_RGBA darkorchid(color_table, 153, 50, 204, 1);+    const Color_RGBA darkred(color_table, 139, 0, 0, 1);+    const Color_RGBA darksalmon(color_table, 233, 150, 122, 1);+    const Color_RGBA darkseagreen(color_table, 143, 188, 143, 1);+    const Color_RGBA darkslateblue(color_table, 72, 61, 139, 1);+    const Color_RGBA darkslategray(color_table, 47, 79, 79, 1);+    const Color_RGBA darkslategrey(color_table, 47, 79, 79, 1);+    const Color_RGBA darkturquoise(color_table, 0, 206, 209, 1);+    const Color_RGBA darkviolet(color_table, 148, 0, 211, 1);+    const Color_RGBA deeppink(color_table, 255, 20, 147, 1);+    const Color_RGBA deepskyblue(color_table, 0, 191, 255, 1);+    const Color_RGBA dimgray(color_table, 105, 105, 105, 1);+    const Color_RGBA dimgrey(color_table, 105, 105, 105, 1);+    const Color_RGBA dodgerblue(color_table, 30, 144, 255, 1);+    const Color_RGBA firebrick(color_table, 178, 34, 34, 1);+    const Color_RGBA floralwhite(color_table, 255, 250, 240, 1);+    const Color_RGBA forestgreen(color_table, 34, 139, 34, 1);+    const Color_RGBA magenta(color_table, 255, 0, 255, 1);+    const Color_RGBA fuchsia(color_table, 255, 0, 255, 1);+    const Color_RGBA gainsboro(color_table, 220, 220, 220, 1);+    const Color_RGBA ghostwhite(color_table, 248, 248, 255, 1);+    const Color_RGBA gold(color_table, 255, 215, 0, 1);+    const Color_RGBA goldenrod(color_table, 218, 165, 32, 1);+    const Color_RGBA gray(color_table, 128, 128, 128, 1);+    const Color_RGBA grey(color_table, 128, 128, 128, 1);+    const Color_RGBA green(color_table, 0, 128, 0, 1);+    const Color_RGBA greenyellow(color_table, 173, 255, 47, 1);+    const Color_RGBA honeydew(color_table, 240, 255, 240, 1);+    const Color_RGBA hotpink(color_table, 255, 105, 180, 1);+    const Color_RGBA indianred(color_table, 205, 92, 92, 1);+    const Color_RGBA indigo(color_table, 75, 0, 130, 1);+    const Color_RGBA ivory(color_table, 255, 255, 240, 1);+    const Color_RGBA khaki(color_table, 240, 230, 140, 1);+    const Color_RGBA lavender(color_table, 230, 230, 250, 1);+    const Color_RGBA lavenderblush(color_table, 255, 240, 245, 1);+    const Color_RGBA lawngreen(color_table, 124, 252, 0, 1);+    const Color_RGBA lemonchiffon(color_table, 255, 250, 205, 1);+    const Color_RGBA lightblue(color_table, 173, 216, 230, 1);+    const Color_RGBA lightcoral(color_table, 240, 128, 128, 1);+    const Color_RGBA lightcyan(color_table, 224, 255, 255, 1);+    const Color_RGBA lightgoldenrodyellow(color_table, 250, 250, 210, 1);+    const Color_RGBA lightgray(color_table, 211, 211, 211, 1);+    const Color_RGBA lightgrey(color_table, 211, 211, 211, 1);+    const Color_RGBA lightgreen(color_table, 144, 238, 144, 1);+    const Color_RGBA lightpink(color_table, 255, 182, 193, 1);+    const Color_RGBA lightsalmon(color_table, 255, 160, 122, 1);+    const Color_RGBA lightseagreen(color_table, 32, 178, 170, 1);+    const Color_RGBA lightskyblue(color_table, 135, 206, 250, 1);+    const Color_RGBA lightslategray(color_table, 119, 136, 153, 1);+    const Color_RGBA lightslategrey(color_table, 119, 136, 153, 1);+    const Color_RGBA lightsteelblue(color_table, 176, 196, 222, 1);+    const Color_RGBA lightyellow(color_table, 255, 255, 224, 1);+    const Color_RGBA lime(color_table, 0, 255, 0, 1);+    const Color_RGBA limegreen(color_table, 50, 205, 50, 1);+    const Color_RGBA linen(color_table, 250, 240, 230, 1);+    const Color_RGBA maroon(color_table, 128, 0, 0, 1);+    const Color_RGBA mediumaquamarine(color_table, 102, 205, 170, 1);+    const Color_RGBA mediumblue(color_table, 0, 0, 205, 1);+    const Color_RGBA mediumorchid(color_table, 186, 85, 211, 1);+    const Color_RGBA mediumpurple(color_table, 147, 112, 219, 1);+    const Color_RGBA mediumseagreen(color_table, 60, 179, 113, 1);+    const Color_RGBA mediumslateblue(color_table, 123, 104, 238, 1);+    const Color_RGBA mediumspringgreen(color_table, 0, 250, 154, 1);+    const Color_RGBA mediumturquoise(color_table, 72, 209, 204, 1);+    const Color_RGBA mediumvioletred(color_table, 199, 21, 133, 1);+    const Color_RGBA midnightblue(color_table, 25, 25, 112, 1);+    const Color_RGBA mintcream(color_table, 245, 255, 250, 1);+    const Color_RGBA mistyrose(color_table, 255, 228, 225, 1);+    const Color_RGBA moccasin(color_table, 255, 228, 181, 1);+    const Color_RGBA navajowhite(color_table, 255, 222, 173, 1);+    const Color_RGBA navy(color_table, 0, 0, 128, 1);+    const Color_RGBA oldlace(color_table, 253, 245, 230, 1);+    const Color_RGBA olive(color_table, 128, 128, 0, 1);+    const Color_RGBA olivedrab(color_table, 107, 142, 35, 1);+    const Color_RGBA orange(color_table, 255, 165, 0, 1);+    const Color_RGBA orangered(color_table, 255, 69, 0, 1);+    const Color_RGBA orchid(color_table, 218, 112, 214, 1);+    const Color_RGBA palegoldenrod(color_table, 238, 232, 170, 1);+    const Color_RGBA palegreen(color_table, 152, 251, 152, 1);+    const Color_RGBA paleturquoise(color_table, 175, 238, 238, 1);+    const Color_RGBA palevioletred(color_table, 219, 112, 147, 1);+    const Color_RGBA papayawhip(color_table, 255, 239, 213, 1);+    const Color_RGBA peachpuff(color_table, 255, 218, 185, 1);+    const Color_RGBA peru(color_table, 205, 133, 63, 1);+    const Color_RGBA pink(color_table, 255, 192, 203, 1);+    const Color_RGBA plum(color_table, 221, 160, 221, 1);+    const Color_RGBA powderblue(color_table, 176, 224, 230, 1);+    const Color_RGBA purple(color_table, 128, 0, 128, 1);+    const Color_RGBA red(color_table, 255, 0, 0, 1);+    const Color_RGBA rosybrown(color_table, 188, 143, 143, 1);+    const Color_RGBA royalblue(color_table, 65, 105, 225, 1);+    const Color_RGBA saddlebrown(color_table, 139, 69, 19, 1);+    const Color_RGBA salmon(color_table, 250, 128, 114, 1);+    const Color_RGBA sandybrown(color_table, 244, 164, 96, 1);+    const Color_RGBA seagreen(color_table, 46, 139, 87, 1);+    const Color_RGBA seashell(color_table, 255, 245, 238, 1);+    const Color_RGBA sienna(color_table, 160, 82, 45, 1);+    const Color_RGBA silver(color_table, 192, 192, 192, 1);+    const Color_RGBA skyblue(color_table, 135, 206, 235, 1);+    const Color_RGBA slateblue(color_table, 106, 90, 205, 1);+    const Color_RGBA slategray(color_table, 112, 128, 144, 1);+    const Color_RGBA slategrey(color_table, 112, 128, 144, 1);+    const Color_RGBA snow(color_table, 255, 250, 250, 1);+    const Color_RGBA springgreen(color_table, 0, 255, 127, 1);+    const Color_RGBA steelblue(color_table, 70, 130, 180, 1);+    const Color_RGBA tan(color_table, 210, 180, 140, 1);+    const Color_RGBA teal(color_table, 0, 128, 128, 1);+    const Color_RGBA thistle(color_table, 216, 191, 216, 1);+    const Color_RGBA tomato(color_table, 255, 99, 71, 1);+    const Color_RGBA turquoise(color_table, 64, 224, 208, 1);+    const Color_RGBA violet(color_table, 238, 130, 238, 1);+    const Color_RGBA wheat(color_table, 245, 222, 179, 1);+    const Color_RGBA white(color_table, 255, 255, 255, 1);+    const Color_RGBA whitesmoke(color_table, 245, 245, 245, 1);+    const Color_RGBA yellow(color_table, 255, 255, 0, 1);+    const Color_RGBA yellowgreen(color_table, 154, 205, 50, 1);+    const Color_RGBA rebeccapurple(color_table, 102, 51, 153, 1);+    const Color_RGBA transparent(color_table, 0, 0, 0, 0);   } -  const std::map<const int, const char*> colors_to_names {+  static const auto* const colors_to_names = new std::unordered_map<int, const char*> {     { 240 * 0x10000 + 248 * 0x100 + 255, ColorNames::aliceblue },     { 250 * 0x10000 + 235 * 0x100 + 215, ColorNames::antiquewhite },     {   0 * 0x10000 + 255 * 0x100 + 255, ColorNames::cyan },@@ -452,7 +456,7 @@     { 102 * 0x10000 +  51 * 0x100 + 153, ColorNames::rebeccapurple }   }; -  const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors+  static const auto *const names_to_colors = new std::unordered_map<std::string, const Color_RGBA*>   {     { ColorNames::aliceblue, &Colors::aliceblue },     { ColorNames::antiquewhite, &Colors::antiquewhite },@@ -605,31 +609,31 @@     { ColorNames::transparent, &Colors::transparent }   }; -  Color_Ptr_Const name_to_color(const char* key)+  const Color_RGBA* name_to_color(const char* key)   {     return name_to_color(std::string(key));   } -  Color_Ptr_Const name_to_color(const std::string& key)+  const Color_RGBA* name_to_color(const std::string& key)   {     // case insensitive lookup.  See #2462-    std::string lower{key};-    std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);+    std::string lower = key;+    Util::ascii_str_tolower(&lower); -    auto p = names_to_colors.find(lower.c_str());-    if (p != names_to_colors.end()) {+    auto p = names_to_colors->find(lower);+    if (p != names_to_colors->end()) {       return p->second;     }-    return 0;+    return nullptr;   }    const char* color_to_name(const int key)   {-    auto p = colors_to_names.find(key);-    if (p != colors_to_names.end()) {+    auto p = colors_to_names->find(key);+    if (p != colors_to_names->end()) {       return p->second;     }-    return 0;+    return nullptr;   }    const char* color_to_name(const double key)@@ -637,7 +641,7 @@     return color_to_name((int)key);   } -  const char* color_to_name(const Color& c)+  const char* color_to_name(const Color_RGBA& c)   {     double key = c.r() * 0x10000                + c.g() * 0x100
libsass/src/color_maps.hpp view
@@ -7,14 +7,6 @@  namespace Sass { -  struct map_cmp_str-  {-    bool operator()(char const *a, char const *b) const-    {-      return std::strcmp(a, b) < 0;-    }-  };-   namespace ColorNames   {     extern const char aliceblue[];@@ -169,161 +161,161 @@   }    namespace Colors {-    extern const Color aliceblue;-    extern const Color antiquewhite;-    extern const Color cyan;-    extern const Color aqua;-    extern const Color aquamarine;-    extern const Color azure;-    extern const Color beige;-    extern const Color bisque;-    extern const Color black;-    extern const Color blanchedalmond;-    extern const Color blue;-    extern const Color blueviolet;-    extern const Color brown;-    extern const Color burlywood;-    extern const Color cadetblue;-    extern const Color chartreuse;-    extern const Color chocolate;-    extern const Color coral;-    extern const Color cornflowerblue;-    extern const Color cornsilk;-    extern const Color crimson;-    extern const Color darkblue;-    extern const Color darkcyan;-    extern const Color darkgoldenrod;-    extern const Color darkgray;-    extern const Color darkgrey;-    extern const Color darkgreen;-    extern const Color darkkhaki;-    extern const Color darkmagenta;-    extern const Color darkolivegreen;-    extern const Color darkorange;-    extern const Color darkorchid;-    extern const Color darkred;-    extern const Color darksalmon;-    extern const Color darkseagreen;-    extern const Color darkslateblue;-    extern const Color darkslategray;-    extern const Color darkslategrey;-    extern const Color darkturquoise;-    extern const Color darkviolet;-    extern const Color deeppink;-    extern const Color deepskyblue;-    extern const Color dimgray;-    extern const Color dimgrey;-    extern const Color dodgerblue;-    extern const Color firebrick;-    extern const Color floralwhite;-    extern const Color forestgreen;-    extern const Color magenta;-    extern const Color fuchsia;-    extern const Color gainsboro;-    extern const Color ghostwhite;-    extern const Color gold;-    extern const Color goldenrod;-    extern const Color gray;-    extern const Color grey;-    extern const Color green;-    extern const Color greenyellow;-    extern const Color honeydew;-    extern const Color hotpink;-    extern const Color indianred;-    extern const Color indigo;-    extern const Color ivory;-    extern const Color khaki;-    extern const Color lavender;-    extern const Color lavenderblush;-    extern const Color lawngreen;-    extern const Color lemonchiffon;-    extern const Color lightblue;-    extern const Color lightcoral;-    extern const Color lightcyan;-    extern const Color lightgoldenrodyellow;-    extern const Color lightgray;-    extern const Color lightgrey;-    extern const Color lightgreen;-    extern const Color lightpink;-    extern const Color lightsalmon;-    extern const Color lightseagreen;-    extern const Color lightskyblue;-    extern const Color lightslategray;-    extern const Color lightslategrey;-    extern const Color lightsteelblue;-    extern const Color lightyellow;-    extern const Color lime;-    extern const Color limegreen;-    extern const Color linen;-    extern const Color maroon;-    extern const Color mediumaquamarine;-    extern const Color mediumblue;-    extern const Color mediumorchid;-    extern const Color mediumpurple;-    extern const Color mediumseagreen;-    extern const Color mediumslateblue;-    extern const Color mediumspringgreen;-    extern const Color mediumturquoise;-    extern const Color mediumvioletred;-    extern const Color midnightblue;-    extern const Color mintcream;-    extern const Color mistyrose;-    extern const Color moccasin;-    extern const Color navajowhite;-    extern const Color navy;-    extern const Color oldlace;-    extern const Color olive;-    extern const Color olivedrab;-    extern const Color orange;-    extern const Color orangered;-    extern const Color orchid;-    extern const Color palegoldenrod;-    extern const Color palegreen;-    extern const Color paleturquoise;-    extern const Color palevioletred;-    extern const Color papayawhip;-    extern const Color peachpuff;-    extern const Color peru;-    extern const Color pink;-    extern const Color plum;-    extern const Color powderblue;-    extern const Color purple;-    extern const Color red;-    extern const Color rosybrown;-    extern const Color royalblue;-    extern const Color saddlebrown;-    extern const Color salmon;-    extern const Color sandybrown;-    extern const Color seagreen;-    extern const Color seashell;-    extern const Color sienna;-    extern const Color silver;-    extern const Color skyblue;-    extern const Color slateblue;-    extern const Color slategray;-    extern const Color slategrey;-    extern const Color snow;-    extern const Color springgreen;-    extern const Color steelblue;-    extern const Color tan;-    extern const Color teal;-    extern const Color thistle;-    extern const Color tomato;-    extern const Color turquoise;-    extern const Color violet;-    extern const Color wheat;-    extern const Color white;-    extern const Color whitesmoke;-    extern const Color yellow;-    extern const Color yellowgreen;-    extern const Color rebeccapurple;-    extern const Color transparent;+    extern const Color_RGBA aliceblue;+    extern const Color_RGBA antiquewhite;+    extern const Color_RGBA cyan;+    extern const Color_RGBA aqua;+    extern const Color_RGBA aquamarine;+    extern const Color_RGBA azure;+    extern const Color_RGBA beige;+    extern const Color_RGBA bisque;+    extern const Color_RGBA black;+    extern const Color_RGBA blanchedalmond;+    extern const Color_RGBA blue;+    extern const Color_RGBA blueviolet;+    extern const Color_RGBA brown;+    extern const Color_RGBA burlywood;+    extern const Color_RGBA cadetblue;+    extern const Color_RGBA chartreuse;+    extern const Color_RGBA chocolate;+    extern const Color_RGBA coral;+    extern const Color_RGBA cornflowerblue;+    extern const Color_RGBA cornsilk;+    extern const Color_RGBA crimson;+    extern const Color_RGBA darkblue;+    extern const Color_RGBA darkcyan;+    extern const Color_RGBA darkgoldenrod;+    extern const Color_RGBA darkgray;+    extern const Color_RGBA darkgrey;+    extern const Color_RGBA darkgreen;+    extern const Color_RGBA darkkhaki;+    extern const Color_RGBA darkmagenta;+    extern const Color_RGBA darkolivegreen;+    extern const Color_RGBA darkorange;+    extern const Color_RGBA darkorchid;+    extern const Color_RGBA darkred;+    extern const Color_RGBA darksalmon;+    extern const Color_RGBA darkseagreen;+    extern const Color_RGBA darkslateblue;+    extern const Color_RGBA darkslategray;+    extern const Color_RGBA darkslategrey;+    extern const Color_RGBA darkturquoise;+    extern const Color_RGBA darkviolet;+    extern const Color_RGBA deeppink;+    extern const Color_RGBA deepskyblue;+    extern const Color_RGBA dimgray;+    extern const Color_RGBA dimgrey;+    extern const Color_RGBA dodgerblue;+    extern const Color_RGBA firebrick;+    extern const Color_RGBA floralwhite;+    extern const Color_RGBA forestgreen;+    extern const Color_RGBA magenta;+    extern const Color_RGBA fuchsia;+    extern const Color_RGBA gainsboro;+    extern const Color_RGBA ghostwhite;+    extern const Color_RGBA gold;+    extern const Color_RGBA goldenrod;+    extern const Color_RGBA gray;+    extern const Color_RGBA grey;+    extern const Color_RGBA green;+    extern const Color_RGBA greenyellow;+    extern const Color_RGBA honeydew;+    extern const Color_RGBA hotpink;+    extern const Color_RGBA indianred;+    extern const Color_RGBA indigo;+    extern const Color_RGBA ivory;+    extern const Color_RGBA khaki;+    extern const Color_RGBA lavender;+    extern const Color_RGBA lavenderblush;+    extern const Color_RGBA lawngreen;+    extern const Color_RGBA lemonchiffon;+    extern const Color_RGBA lightblue;+    extern const Color_RGBA lightcoral;+    extern const Color_RGBA lightcyan;+    extern const Color_RGBA lightgoldenrodyellow;+    extern const Color_RGBA lightgray;+    extern const Color_RGBA lightgrey;+    extern const Color_RGBA lightgreen;+    extern const Color_RGBA lightpink;+    extern const Color_RGBA lightsalmon;+    extern const Color_RGBA lightseagreen;+    extern const Color_RGBA lightskyblue;+    extern const Color_RGBA lightslategray;+    extern const Color_RGBA lightslategrey;+    extern const Color_RGBA lightsteelblue;+    extern const Color_RGBA lightyellow;+    extern const Color_RGBA lime;+    extern const Color_RGBA limegreen;+    extern const Color_RGBA linen;+    extern const Color_RGBA maroon;+    extern const Color_RGBA mediumaquamarine;+    extern const Color_RGBA mediumblue;+    extern const Color_RGBA mediumorchid;+    extern const Color_RGBA mediumpurple;+    extern const Color_RGBA mediumseagreen;+    extern const Color_RGBA mediumslateblue;+    extern const Color_RGBA mediumspringgreen;+    extern const Color_RGBA mediumturquoise;+    extern const Color_RGBA mediumvioletred;+    extern const Color_RGBA midnightblue;+    extern const Color_RGBA mintcream;+    extern const Color_RGBA mistyrose;+    extern const Color_RGBA moccasin;+    extern const Color_RGBA navajowhite;+    extern const Color_RGBA navy;+    extern const Color_RGBA oldlace;+    extern const Color_RGBA olive;+    extern const Color_RGBA olivedrab;+    extern const Color_RGBA orange;+    extern const Color_RGBA orangered;+    extern const Color_RGBA orchid;+    extern const Color_RGBA palegoldenrod;+    extern const Color_RGBA palegreen;+    extern const Color_RGBA paleturquoise;+    extern const Color_RGBA palevioletred;+    extern const Color_RGBA papayawhip;+    extern const Color_RGBA peachpuff;+    extern const Color_RGBA peru;+    extern const Color_RGBA pink;+    extern const Color_RGBA plum;+    extern const Color_RGBA powderblue;+    extern const Color_RGBA purple;+    extern const Color_RGBA red;+    extern const Color_RGBA rosybrown;+    extern const Color_RGBA royalblue;+    extern const Color_RGBA saddlebrown;+    extern const Color_RGBA salmon;+    extern const Color_RGBA sandybrown;+    extern const Color_RGBA seagreen;+    extern const Color_RGBA seashell;+    extern const Color_RGBA sienna;+    extern const Color_RGBA silver;+    extern const Color_RGBA skyblue;+    extern const Color_RGBA slateblue;+    extern const Color_RGBA slategray;+    extern const Color_RGBA slategrey;+    extern const Color_RGBA snow;+    extern const Color_RGBA springgreen;+    extern const Color_RGBA steelblue;+    extern const Color_RGBA tan;+    extern const Color_RGBA teal;+    extern const Color_RGBA thistle;+    extern const Color_RGBA tomato;+    extern const Color_RGBA turquoise;+    extern const Color_RGBA violet;+    extern const Color_RGBA wheat;+    extern const Color_RGBA white;+    extern const Color_RGBA whitesmoke;+    extern const Color_RGBA yellow;+    extern const Color_RGBA yellowgreen;+    extern const Color_RGBA rebeccapurple;+    extern const Color_RGBA transparent;   } -  Color_Ptr_Const name_to_color(const char*);-  Color_Ptr_Const name_to_color(const std::string&);+  const Color_RGBA* name_to_color(const char*);+  const Color_RGBA* name_to_color(const std::string&);   const char* color_to_name(const int);-  const char* color_to_name(const Color&);+  const char* color_to_name(const Color_RGBA&);   const char* color_to_name(const double);  }
libsass/src/constants.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "constants.hpp"  namespace Sass {@@ -18,6 +21,15 @@     extern const unsigned long Specificity_Pseudo = 1000;     extern const unsigned long Specificity_ID = 1000000; +    extern const int UnificationOrder_Element = 1;+    extern const int UnificationOrder_Id = 2;+    extern const int UnificationOrder_Class = 2;+    extern const int UnificationOrder_Attribute = 3;+    extern const int UnificationOrder_PseudoClass = 4;+    extern const int UnificationOrder_Wrapped = 5;+    extern const int UnificationOrder_PseudoElement = 6;+    extern const int UnificationOrder_Placeholder = 7;+     // sass keywords     extern const char at_root_kwd[]       = "@at-root";     extern const char import_kwd[]        = "@import";@@ -33,6 +45,7 @@     extern const char for_kwd[]           = "@for";     extern const char from_kwd[]          = "from";     extern const char to_kwd[]            = "to";+    extern const char of_kwd[]            = "of";     extern const char through_kwd[]       = "through";     extern const char each_kwd[]          = "@each";     extern const char in_kwd[]            = "in";@@ -116,6 +129,9 @@     extern const char true_kwd[]  = "true";     extern const char false_kwd[] = "false"; +    // definition keywords+    extern const char using_kwd[]   = "using";+     // miscellaneous punctuation and delimiters     extern const char percent_str[]     = "%";     extern const char empty_str[]       = "";@@ -145,6 +161,10 @@     // constants for uri parsing (RFC 3986 Appendix A.)     extern const char uri_chars[]  = ":;/?!%&#@|[]{}'`^\"*+-.,_=~";     extern const char real_uri_chars[]  = "#%&";++    extern const char selector_combinator_child[] = ">";+    extern const char selector_combinator_general[] = "~";+    extern const char selector_combinator_adjacent[] = "+";      // some specific constant character classes     // they must be static to be useable by lexer
libsass/src/constants.hpp view
@@ -18,6 +18,16 @@     extern const unsigned long Specificity_Pseudo;     extern const unsigned long Specificity_ID; +    // Selector unification order;+    extern const int UnificationOrder_Element;+    extern const int UnificationOrder_Id;+    extern const int UnificationOrder_Class;+    extern const int UnificationOrder_Attribute;+    extern const int UnificationOrder_PseudoClass;+    extern const int UnificationOrder_Wrapped;+    extern const int UnificationOrder_PseudoElement;+    extern const int UnificationOrder_Placeholder;+     // sass keywords     extern const char at_root_kwd[];     extern const char import_kwd[];@@ -33,6 +43,7 @@     extern const char for_kwd[];     extern const char from_kwd[];     extern const char to_kwd[];+    extern const char of_kwd[];     extern const char through_kwd[];     extern const char each_kwd[];     extern const char in_kwd[];@@ -117,6 +128,9 @@     extern const char true_kwd[];     extern const char false_kwd[]; +    // definition keywords+    extern const char using_kwd[];+     // miscellaneous punctuation and delimiters     extern const char percent_str[];     extern const char empty_str[];@@ -147,6 +161,11 @@     // constants for uri parsing (RFC 3986 Appendix A.)     extern const char uri_chars[];     extern const char real_uri_chars[];++    // constants for selector combinators+    extern const char selector_combinator_child[];+    extern const char selector_combinator_general[];+    extern const char selector_combinator_adjacent[];      // some specific constant character classes     // they must be static to be useable by lexer
libsass/src/context.cpp view
@@ -1,34 +1,22 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <string>-#include <cstdlib>-#include <cstring>-#include <iomanip>-#include <sstream>-#include <iostream>- #include "ast.hpp"-#include "util.hpp"-#include "sass.h"++#include "remove_placeholders.hpp"+#include "sass_functions.hpp"+#include "check_nesting.hpp"+#include "fn_selectors.hpp"+#include "fn_strings.hpp"+#include "fn_numbers.hpp"+#include "fn_colors.hpp"+#include "fn_miscs.hpp"+#include "fn_lists.hpp"+#include "fn_maps.hpp" #include "context.hpp"-#include "plugins.hpp"-#include "constants.hpp"-#include "parser.hpp"-#include "file.hpp"-#include "inspect.hpp"-#include "output.hpp" #include "expand.hpp"-#include "eval.hpp"-#include "check_nesting.hpp"+#include "parser.hpp" #include "cssize.hpp"-#include "listize.hpp"-#include "extend.hpp"-#include "remove_placeholders.hpp"-#include "functions.hpp"-#include "sass_functions.hpp"-#include "backtrace.hpp"-#include "sass2scss.h"-#include "prelexer.hpp"-#include "emitter.hpp"  namespace Sass {   using namespace Constants;@@ -40,23 +28,17 @@    static std::string safe_input(const char* in_path)   {-    // enforce some safe defaults-    // used to create relative file links-    std::string safe_path(in_path ? in_path : "");-    return safe_path == "" ? "stdin" : safe_path;+    if (in_path == nullptr || in_path[0] == '\0') return "stdin";+    return in_path;   } -  static std::string safe_output(const char* out_path, const std::string& input_path = "")+  static std::string safe_output(const char* out_path, std::string input_path)   {-    std::string safe_path(out_path ? out_path : "");-    // maybe we can extract an output path from input path-    if (safe_path == "" && input_path != "") {-      int lastindex = static_cast<int>(input_path.find_last_of("."));-      return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";+    if (out_path == nullptr || out_path[0] == '\0') {+      if (input_path.empty()) return "stdout";+      return input_path.substr(0, input_path.find_last_of(".")) + ".css";     }-    // enforce some safe defaults-    // used to create relative file links-    return safe_path == "" ? "stdout" : safe_path;+    return out_path;   }    Context::Context(struct Sass_Context& c_ctx)@@ -71,10 +53,10 @@     strings(),     resources(),     sheets(),-    subset_map(),     import_stack(),     callee_stack(),     traces(),+    extender(Extender::NORMAL, traces),     c_compiler(NULL),      c_headers               (std::vector<Sass_Importer_Entry>()),@@ -150,7 +132,7 @@     }     // clear inner structures (vectors) and input source     resources.clear(); import_stack.clear();-    subset_map.clear(), sheets.clear();+    sheets.clear();   }    Data_Context::~Data_Context()@@ -382,7 +364,7 @@    } -  void Context::import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path) {+  void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) {      ParserState pstate(imp->pstate());     std::string imp_path(unquote(load_path));@@ -395,17 +377,17 @@       // if (protocol.compare("file") && true) { }     } -    // add urls (protocol other than file) and urls without procotol to `urls` member+    // add urls (protocol other than file) and urls without protocol to `urls` member     // ToDo: if ctx_path is already a file resource, we should not add it here?     if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {       imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path));     }     else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {-      String_Constant_Ptr loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));+      String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));       Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);       Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);       loc_args->append(loc_arg);-      Function_Call_Ptr new_url = SASS_MEMORY_NEW(Function_Call, pstate, "url", loc_args);+      Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, std::string("url"), loc_args);       imp->urls().push_back(new_url);     }     else {@@ -421,7 +403,7 @@     // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet-  bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one)+  bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one)   {     // unique counter     size_t count = 0;@@ -519,7 +501,7 @@     OutputBuffer emitted = emitter.get_buffer();     // should we append a source map url?     if (!c_options.omit_source_map_url) {-      // generate an embeded source map+      // generate an embedded source map       if (c_options.source_map_embed) {         emitted.buffer += linefeed;         emitted.buffer += format_embedded_source_map();@@ -556,7 +538,7 @@   {      // check if entry file is given-    if (input_path.empty()) return 0;+    if (input_path.empty()) return {};      // create absolute path from input filename     // ToDo: this should be resolved via custom importers@@ -602,7 +584,7 @@   {      // check if source string is given-    if (!source_c_str) return 0;+    if (!source_c_str) return {};      // convert indented sass syntax     if(c_options.is_indented_syntax_src) {@@ -639,17 +621,15 @@     return compile();   } --   // parse root block from includes   Block_Obj Context::compile()   {     // abort if there is no data-    if (resources.size() == 0) return 0;+    if (resources.size() == 0) return {};     // get root block from the first style sheet     Block_Obj root = sheets.at(entry_path).root;     // abort on invalid root-    if (root.isNull()) return 0;+    if (root.isNull()) return {};     Env global; // create root environment     // register built-in functions on env     register_built_in_functions(*this, &global);@@ -668,23 +648,23 @@     }     // expand and eval the tree     root = expand(root);++    Extension unsatisfied;+    // check that all extends were used+    if (extender.checkForUnsatisfiedExtends(unsatisfied)) {+      throw Exception::UnsatisfiedExtend(traces, unsatisfied);+    }+     // check nesting     check_nesting(root);     // merge and bubble certain rules     root = cssize(root);-    // should we extend something?-    if (!subset_map.empty()) {-      // create crtp visitor object-      Extend extend(subset_map);-      extend.setEval(expand.eval);-      // extend tree nodes-      extend(root);-    }      // clean up by removing empty placeholders     // ToDo: maybe we can do this somewhere else?     Remove_Placeholders remove_placeholders;     root->perform(&remove_placeholders);+     // return processed tree     return root;   }@@ -732,14 +712,14 @@    void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)   {-    Definition_Ptr def = make_native_function(sig, f, ctx);+    Definition* def = make_native_function(sig, f, ctx);     def->environment(env);     (*env)[def->name() + "[f]"] = def;   }    void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env)   {-    Definition_Ptr def = make_native_function(sig, f, ctx);+    Definition* def = make_native_function(sig, f, ctx);     std::stringstream ss;     ss << def->name() << "[f]" << arity;     def->environment(env);@@ -748,11 +728,11 @@    void register_overload_stub(Context& ctx, std::string name, Env* env)   {-    Definition_Ptr stub = SASS_MEMORY_NEW(Definition,+    Definition* stub = SASS_MEMORY_NEW(Definition,                                        ParserState("[built-in function]"),                                        0,                                        name,-                                       0,+                                       {},                                        0,                                        true);     (*env)[name + "[f]"] = stub;@@ -872,7 +852,7 @@   }   void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)   {-    Definition_Ptr def = make_c_function(descr, ctx);+    Definition* def = make_c_function(descr, ctx);     def->environment(env);     (*env)[def->name() + "[f]"] = def;   }
libsass/src/context.hpp view
@@ -1,40 +1,32 @@ #ifndef SASS_CONTEXT_H #define SASS_CONTEXT_H -#include <string>-#include <vector>-#include <map>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp" + #define BUFFERSIZE 255 #include "b64/encode.h" -#include "ast_fwd_decl.hpp"-#include "kwd_arg_macros.hpp"-#include "ast_fwd_decl.hpp" #include "sass_context.hpp"-#include "environment.hpp"-#include "source_map.hpp"-#include "subset_map.hpp"-#include "backtrace.hpp"-#include "output.hpp"+#include "stylesheet.hpp" #include "plugins.hpp"-#include "file.hpp"---struct Sass_Function;+#include "output.hpp"  namespace Sass {    class Context {   public:-    void import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path);-    bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp)+    void import_url (Import* imp, std::string load_path, const std::string& ctx_path);+    bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp)     { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); };-    bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp)+    bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp)     { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); };    private:-    bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);+    bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);    public:     const std::string CWD;@@ -46,16 +38,16 @@      // generic ast node garbage container     // used to avoid possible circular refs-    std::vector<AST_Node_Obj> ast_gc;+    CallStack ast_gc;     // resources add under our control     // these are guaranteed to be freed     std::vector<char*> strings;     std::vector<Resource> resources;     std::map<const std::string, StyleSheet> sheets;-    Subset_Map subset_map;-    std::vector<Sass_Import_Entry> import_stack;+    ImporterStack import_stack;     std::vector<Sass_Callee> callee_stack;     std::vector<Backtrace> traces;+    Extender extender;      struct Sass_Compiler* c_compiler; @@ -67,10 +59,6 @@      std::vector<std::string> plugin_paths; // relative paths to load plugins     std::vector<std::string> include_paths; // lookup paths for includes----      void apply_custom_headers(Block_Obj root, const char* path, ParserState pstate); 
libsass/src/cssize.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include <typeinfo> #include <vector>@@ -9,18 +12,17 @@ namespace Sass {    Cssize::Cssize(Context& ctx)-  : ctx(ctx),-    traces(ctx.traces),-    block_stack(std::vector<Block_Ptr>()),-    p_stack(std::vector<Statement_Ptr>())+  : traces(ctx.traces),+    block_stack(BlockStack()),+    p_stack(std::vector<Statement*>())   { } -  Statement_Ptr Cssize::parent()+  Statement* Cssize::parent()   {     return p_stack.size() ? p_stack.back() : block_stack.front();   } -  Block_Ptr Cssize::operator()(Block_Ptr b)+  Block* Cssize::operator()(Block* b)   {     Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root());     // bb->tabs(b->tabs());@@ -30,7 +32,7 @@     return bb.detach();   } -  Statement_Ptr Cssize::operator()(Trace_Ptr t)+  Statement* Cssize::operator()(Trace* t)   {     traces.push_back(Backtrace(t->pstate()));     auto result = t->block()->perform(this);@@ -38,11 +40,11 @@     return result;   } -  Statement_Ptr Cssize::operator()(Declaration_Ptr d)+  Statement* Cssize::operator()(Declaration* d)   {     String_Obj property = Cast<String>(d->property()); -    if (Declaration_Ptr dd = Cast<Declaration>(parent())) {+    if (Declaration* dd = Cast<Declaration>(parent())) {       String_Obj parent_property = Cast<String>(dd->property());       property = SASS_MEMORY_NEW(String_Constant,                                  d->property()->pstate(),@@ -78,7 +80,7 @@     return 0;   } -  Statement_Ptr Cssize::operator()(Directive_Ptr r)+  Statement* Cssize::operator()(Directive* r)   {     if (!r->block() || !r->block()->length()) return r; @@ -110,10 +112,10 @@      } -    Block_Ptr result = SASS_MEMORY_NEW(Block, rr->pstate());+    Block* result = SASS_MEMORY_NEW(Block, rr->pstate());     if (!(directive_exists || rr->is_keyframes()))     {-      Directive_Ptr empty_node = Cast<Directive>(rr);+      Directive* empty_node = Cast<Directive>(rr);       empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate()));       result->append(empty_node);     }@@ -128,7 +130,7 @@     return result;   } -  Statement_Ptr Cssize::operator()(Keyframe_Rule_Ptr r)+  Statement* Cssize::operator()(Keyframe_Rule* r)   {     if (!r->block() || !r->block()->length()) return r; @@ -140,14 +142,14 @@     return debubble(rr->block(), rr);   } -  Statement_Ptr Cssize::operator()(Ruleset_Ptr r)+  Statement* Cssize::operator()(Ruleset* r)   {     p_stack.push_back(r);     // this can return a string schema     // string schema is not a statement!     // r->block() is already a string schema-    // and that is comming from propset expand-    Block_Ptr bb = operator()(r->block());+    // and that is coming from propset expand+    Block* bb = operator()(r->block());     // this should protect us (at least a bit) from our mess     // fixing this properly is harder that it should be ...     if (Cast<Statement>(bb) == NULL) {@@ -167,10 +169,10 @@     }      Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());-    Block_Ptr rules = SASS_MEMORY_NEW(Block, rr->block()->pstate());+    Block* rules = SASS_MEMORY_NEW(Block, rr->block()->pstate());     for (size_t i = 0, L = rr->block()->length(); i < L; i++)     {-      Statement_Ptr s = rr->block()->at(i);+      Statement* s = rr->block()->at(i);       if (bubblable(s)) rules->append(s);       if (!bubblable(s)) props->append(s);     }@@ -183,14 +185,14 @@        for (size_t i = 0, L = rules->length(); i < L; i++)       {-        Statement_Ptr stm = rules->at(i);+        Statement* stm = rules->at(i);         stm->tabs(stm->tabs() + 1);       }        rules->unshift(rr);     } -    Block_Ptr ptr = rules;+    Block* ptr = rules;     rules = debubble(rules);     void* lp = ptr;     void* rp = rules;@@ -207,25 +209,28 @@     return rules;   } -  Statement_Ptr Cssize::operator()(Null_Ptr m)+  Statement* Cssize::operator()(Null* m)   {     return 0;   } -  Statement_Ptr Cssize::operator()(Media_Block_Ptr m)+  Statement* Cssize::operator()(CssMediaRule* m)   {     if (parent()->statement_type() == Statement::RULESET)-    { return bubble(m); }+    {+      return bubble(m);+    }      if (parent()->statement_type() == Statement::MEDIA)-    { return SASS_MEMORY_NEW(Bubble, m->pstate(), m); }+    {+      return SASS_MEMORY_NEW(Bubble, m->pstate(), m);+    }      p_stack.push_back(m); -    Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block,-                                      m->pstate(),-                                      m->media_queries(),-                                      operator()(m->block()));+    CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());+    mm->concat(m->elements());+    mm->block(operator()(m->block()));     mm->tabs(m->tabs());      p_stack.pop_back();@@ -233,7 +238,7 @@     return debubble(mm->block(), mm);   } -  Statement_Ptr Cssize::operator()(Supports_Block_Ptr m)+  Statement* Cssize::operator()(Supports_Block* m)   {     if (!m->block()->length())     { return m; }@@ -254,17 +259,17 @@     return debubble(mm->block(), mm);   } -  Statement_Ptr Cssize::operator()(At_Root_Block_Ptr m)+  Statement* Cssize::operator()(At_Root_Block* m)   {     bool tmp = false;     for (size_t i = 0, L = p_stack.size(); i < L; ++i) {-      Statement_Ptr s = p_stack[i];+      Statement* s = p_stack[i];       tmp |= m->exclude_node(s);     }      if (!tmp && m->block())     {-      Block_Ptr bb = operator()(m->block());+      Block* bb = operator()(m->block());       for (size_t i = 0, L = bb->length(); i < L; ++i) {         // (bb->elements())[i]->tabs(m->tabs());         Statement_Obj stm = bb->at(i);@@ -282,9 +287,9 @@     return bubble(m);   } -  Statement_Ptr Cssize::bubble(Directive_Ptr m)+  Statement* Cssize::bubble(Directive* m)   {-    Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+    Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());     Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent()));     new_rule->block(bb);     new_rule->tabs(this->parent()->tabs());@@ -299,16 +304,16 @@                                   wrapper_block);     if (m->value()) mm->value(m->value()); -    Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);+    Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);     return bubble;   } -  Statement_Ptr Cssize::bubble(At_Root_Block_Ptr m)+  Statement* Cssize::bubble(At_Root_Block* m)   {     if (!m || !m->block()) return NULL;-    Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+    Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());     Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent()));-    Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+    Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());     if (new_rule) {       new_rule->block(bb);       new_rule->tabs(this->parent()->tabs());@@ -316,74 +321,74 @@       wrapper_block->append(new_rule);     } -    At_Root_Block_Ptr mm = SASS_MEMORY_NEW(At_Root_Block,+    At_Root_Block* mm = SASS_MEMORY_NEW(At_Root_Block,                                         m->pstate(),                                         wrapper_block,                                         m->expression());-    Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);+    Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);     return bubble;   } -  Statement_Ptr Cssize::bubble(Supports_Block_Ptr m)+  Statement* Cssize::bubble(Supports_Block* m)   {     Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); -    Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());-    Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,+    Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+    Ruleset* new_rule = SASS_MEMORY_NEW(Ruleset,                                         parent->pstate(),                                         parent->selector(),                                         bb);     new_rule->tabs(parent->tabs());     new_rule->block()->concat(m->block()); -    Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+    Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());     wrapper_block->append(new_rule);-    Supports_Block_Ptr mm = SASS_MEMORY_NEW(Supports_Block,+    Supports_Block* mm = SASS_MEMORY_NEW(Supports_Block,                                        m->pstate(),                                        m->condition(),                                        wrapper_block);      mm->tabs(m->tabs()); -    Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);+    Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);     return bubble;   } -  Statement_Ptr Cssize::bubble(Media_Block_Ptr m)+  Statement* Cssize::bubble(CssMediaRule* m)   {     Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); -    Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());-    Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,-                                        parent->pstate(),-                                        parent->selector(),-                                        bb);+    Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+    Ruleset* new_rule = SASS_MEMORY_NEW(Ruleset,+      parent->pstate(),+      parent->selector(),+      bb);     new_rule->tabs(parent->tabs());     new_rule->block()->concat(m->block()); -    Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+    Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());     wrapper_block->append(new_rule);-    Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block,-                                      m->pstate(),-                                      m->media_queries(),-                                      wrapper_block);+    CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule,+      m->pstate(),+      wrapper_block);+    mm->concat(m->elements());      mm->tabs(m->tabs());      return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);   } -  bool Cssize::bubblable(Statement_Ptr s)+  bool Cssize::bubblable(Statement* s)   {     return Cast<Ruleset>(s) || s->bubbles();   } -  Block_Ptr Cssize::flatten(Block_Ptr b)+  Block* Cssize::flatten(const Block* b)   {-    Block_Ptr result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root());+    Block* result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root());     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Ptr ss = b->at(i);-      if (Block_Ptr bb = Cast<Block>(ss)) {+      Statement* ss = b->at(i);+      if (const Block* bb = Cast<Block>(ss)) {         Block_Obj bs = flatten(bb);         for (size_t j = 0, K = bs->length(); j < K; ++j) {           result->append(bs->at(j));@@ -396,7 +401,7 @@     return result;   } -  std::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block_Ptr b)+  std::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block* b)   {     std::vector<std::pair<bool, Block_Obj>> results; @@ -411,7 +416,7 @@       }       else       {-        Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());+        Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());         wrapper_block->append(value);         results.push_back(std::make_pair(key, wrapper_block));       }@@ -419,9 +424,9 @@     return results;   } -  Block_Ptr Cssize::debubble(Block_Ptr children, Statement_Ptr parent)+  Block* Cssize::debubble(Block* children, Statement* parent)   {-    Has_Block_Obj previous_parent = 0;+    Has_Block_Obj previous_parent;     std::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children);     Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate()); @@ -437,7 +442,7 @@           previous_parent->block()->concat(slice);         }         else {-          previous_parent = Cast<Has_Block>(SASS_MEMORY_COPY(parent));+          previous_parent = SASS_MEMORY_COPY(parent);           previous_parent->block(slice);           previous_parent->tabs(parent->tabs()); @@ -448,37 +453,25 @@        for (size_t j = 0, K = slice->length(); j < K; ++j)       {-        Statement_Ptr ss;+        Statement_Obj ss;         Statement_Obj stm = slice->at(j);         // this has to go now here (too bad)         Bubble_Obj node = Cast<Bubble>(stm);-        Media_Block_Ptr m1 = NULL;-        Media_Block_Ptr m2 = NULL;-        if (parent) m1 = Cast<Media_Block>(parent);-        if (node) m2 = Cast<Media_Block>(node->node());-        if (!parent ||-            parent->statement_type() != Statement::MEDIA ||-            node->node()->statement_type() != Statement::MEDIA ||-            (m1 && m2 && *m1->media_queries() == *m2->media_queries())-          )-        {-          ss = node->node();-        }-        else-        {-          List_Obj mq = merge_media_queries(-            Cast<Media_Block>(node->node()),-            Cast<Media_Block>(parent)-          );-          if (!mq->length()) continue;-          if (Media_Block* b = Cast<Media_Block>(node->node())) {-            b->media_queries(mq);-          }++        CssMediaRule* rule1 = NULL;+        CssMediaRule* rule2 = NULL;+        if (parent) rule1 = Cast<CssMediaRule>(parent);+        if (node) rule2 = Cast<CssMediaRule>(node->node());+        if (rule1 || rule2) {           ss = node->node();         } -        if (!ss) continue;+        ss = node->node(); +        if (!ss) {+          continue;+        }+         ss->tabs(ss->tabs() + node->tabs());         ss->group_end(node->group_end()); @@ -493,11 +486,11 @@                                               children->length(),                                               children->is_root()); -        Block_Ptr wrapper = flatten(bb);+        Block* wrapper = flatten(bb);         wrapper_block->append(wrapper);          if (wrapper->length()) {-          previous_parent = NULL;+          previous_parent = {};         }          if (wrapper_block) {@@ -509,16 +502,11 @@     return flatten(result);   } -  Statement_Ptr Cssize::fallback_impl(AST_Node_Ptr n)-  {-    return static_cast<Statement_Ptr>(n);-  }--  void Cssize::append_block(Block_Ptr b, Block_Ptr cur)+  void Cssize::append_block(Block* b, Block* cur)   {     for (size_t i = 0, L = b->length(); i < L; ++i) {       Statement_Obj ith = b->at(i)->perform(this);-      if (Block_Ptr bb = Cast<Block>(ith)) {+      if (Block_Obj bb = Cast<Block>(ith)) {         for (size_t j = 0, K = bb->length(); j < K; ++j) {           cur->append(bb->at(j));         }@@ -529,78 +517,4 @@     }   } -  List_Ptr Cssize::merge_media_queries(Media_Block_Ptr m1, Media_Block_Ptr m2)-  {-    List_Ptr qq = SASS_MEMORY_NEW(List,-                               m1->media_queries()->pstate(),-                               m1->media_queries()->length(),-                               SASS_COMMA);--    for (size_t i = 0, L = m1->media_queries()->length(); i < L; i++) {-      for (size_t j = 0, K = m2->media_queries()->length(); j < K; j++) {-        Expression_Obj l1 = m1->media_queries()->at(i);-        Expression_Obj l2 = m2->media_queries()->at(j);-        Media_Query_Ptr mq1 = Cast<Media_Query>(l1);-        Media_Query_Ptr mq2 = Cast<Media_Query>(l2);-        Media_Query_Ptr mq = merge_media_query(mq1, mq2);-        if (mq) qq->append(mq);-      }-    }--    return qq;-  }---  Media_Query_Ptr Cssize::merge_media_query(Media_Query_Ptr mq1, Media_Query_Ptr mq2)-  {--    std::string type;-    std::string mod;--    std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");-    std::string t1 = mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : "";-    std::string m2 = std::string(mq2->is_restricted() ? "only" : mq2->is_negated() ? "not" : "");-    std::string t2 = mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : "";---    if (t1.empty()) t1 = t2;-    if (t2.empty()) t2 = t1;--    if ((m1 == "not") ^ (m2 == "not")) {-      if (t1 == t2) {-        return 0;-      }-      type = m1 == "not" ? t2 : t1;-      mod = m1 == "not" ? m2 : m1;-    }-    else if (m1 == "not" && m2 == "not") {-      if (t1 != t2) {-        return 0;-      }-      type = t1;-      mod = "not";-    }-    else if (t1 != t2) {-      return 0;-    } else {-      type = t1;-      mod = m1.empty() ? m2 : m1;-    }--    Media_Query_Ptr mm = SASS_MEMORY_NEW(Media_Query,-                                         mq1->pstate(),-                                         0,-                                         mq1->length() + mq2->length(),-                                         mod == "not",-                                         mod == "only");--    if (!type.empty()) {-      mm->media_type(SASS_MEMORY_NEW(String_Quoted, mq1->pstate(), type));-    }--    mm->concat(mq2);-    mm->concat(mq1);--    return mm;-  } }
libsass/src/cssize.hpp view
@@ -10,66 +10,60 @@    struct Backtrace; -  class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> {+  class Cssize : public Operation_CRTP<Statement*, Cssize> { -    Context&                    ctx;     Backtraces&                 traces;-    std::vector<Block_Ptr>      block_stack;-    std::vector<Statement_Ptr>  p_stack;--    Statement_Ptr fallback_impl(AST_Node_Ptr n);+    BlockStack      block_stack;+    std::vector<Statement*>  p_stack;    public:     Cssize(Context&);     ~Cssize() { } -    Selector_List_Ptr selector();--    Block_Ptr operator()(Block_Ptr);-    Statement_Ptr operator()(Ruleset_Ptr);-    // Statement_Ptr operator()(Bubble_Ptr);-    Statement_Ptr operator()(Media_Block_Ptr);-    Statement_Ptr operator()(Supports_Block_Ptr);-    Statement_Ptr operator()(At_Root_Block_Ptr);-    Statement_Ptr operator()(Directive_Ptr);-    Statement_Ptr operator()(Keyframe_Rule_Ptr);-    Statement_Ptr operator()(Trace_Ptr);-    Statement_Ptr operator()(Declaration_Ptr);-    // Statement_Ptr operator()(Assignment_Ptr);-    // Statement_Ptr operator()(Import_Ptr);-    // Statement_Ptr operator()(Import_Stub_Ptr);-    // Statement_Ptr operator()(Warning_Ptr);-    // Statement_Ptr operator()(Error_Ptr);-    // Statement_Ptr operator()(Comment_Ptr);-    // Statement_Ptr operator()(If_Ptr);-    // Statement_Ptr operator()(For_Ptr);-    // Statement_Ptr operator()(Each_Ptr);-    // Statement_Ptr operator()(While_Ptr);-    // Statement_Ptr operator()(Return_Ptr);-    // Statement_Ptr operator()(Extension_Ptr);-    // Statement_Ptr operator()(Definition_Ptr);-    // Statement_Ptr operator()(Mixin_Call_Ptr);-    // Statement_Ptr operator()(Content_Ptr);-    Statement_Ptr operator()(Null_Ptr);--    Statement_Ptr parent();-    std::vector<std::pair<bool, Block_Obj>> slice_by_bubble(Block_Ptr);-    Statement_Ptr bubble(Directive_Ptr);-    Statement_Ptr bubble(At_Root_Block_Ptr);-    Statement_Ptr bubble(Media_Block_Ptr);-    Statement_Ptr bubble(Supports_Block_Ptr);+    Block* operator()(Block*);+    Statement* operator()(Ruleset*);+    // Statement* operator()(Bubble*);+    Statement* operator()(CssMediaRule*);+    Statement* operator()(Supports_Block*);+    Statement* operator()(At_Root_Block*);+    Statement* operator()(Directive*);+    Statement* operator()(Keyframe_Rule*);+    Statement* operator()(Trace*);+    Statement* operator()(Declaration*);+    // Statement* operator()(Assignment*);+    // Statement* operator()(Import*);+    // Statement* operator()(Import_Stub*);+    // Statement* operator()(Warning*);+    // Statement* operator()(Error*);+    // Statement* operator()(Comment*);+    // Statement* operator()(If*);+    // Statement* operator()(For*);+    // Statement* operator()(Each*);+    // Statement* operator()(While*);+    // Statement* operator()(Return*);+    // Statement* operator()(ExtendRule*);+    // Statement* operator()(Definition*);+    // Statement* operator()(Mixin_Call*);+    // Statement* operator()(Content*);+    Statement* operator()(Null*); -    Block_Ptr debubble(Block_Ptr children, Statement_Ptr parent = 0);-    Block_Ptr flatten(Block_Ptr);-    bool bubblable(Statement_Ptr);+    Statement* parent();+    std::vector<std::pair<bool, Block_Obj>> slice_by_bubble(Block*);+    Statement* bubble(Directive*);+    Statement* bubble(At_Root_Block*);+    Statement* bubble(CssMediaRule*);+    Statement* bubble(Supports_Block*); -    List_Ptr merge_media_queries(Media_Block_Ptr, Media_Block_Ptr);-    Media_Query_Ptr merge_media_query(Media_Query_Ptr, Media_Query_Ptr);+    Block* debubble(Block* children, Statement* parent = 0);+    Block* flatten(const Block*);+    bool bubblable(Statement*); +    // generic fallback     template <typename U>-    Statement_Ptr fallback(U x) { return fallback_impl(x); }+    Statement* fallback(U x)+    { return Cast<Statement>(x); } -    void append_block(Block_Ptr, Block_Ptr);+    void append_block(Block*, Block*);   };  }
+ libsass/src/dart_helpers.hpp view
@@ -0,0 +1,199 @@+#ifndef SASS_DART_HELPERS_H+#define SASS_DART_HELPERS_H++#include <vector>+#include <utility>+#include <iterator>+#include <functional>++namespace Sass {++  // ##########################################################################+  // Flatten `vector<vector<T>>` to `vector<T>`+  // ##########################################################################+  template <class T>+  T flatten(const std::vector<T>& all)+  {+    T flattened;+    for (const auto& sub : all) {+      std::copy(std::begin(sub), std::end(sub),+        std::back_inserter(flattened));+    }+    return flattened;+  }++  // ##########################################################################+  // Expands each element of this Iterable into zero or more elements.+  // Calls a function on every element and ads all results to flat array+  // ##########################################################################+  // Equivalent to dart `cnt.any`+  // Pass additional closure variables to `fn`+  template <class T, class U, typename ...Args>+  T expand(const T& cnt, U fn, Args... args) {+    T flattened;+    for (const auto& sub : cnt) {+      auto rv = fn(sub, args...);+      flattened.insert(flattened.end(),+        rv.begin(), rv.end());+    }+    return flattened;+  }++  // ##########################################################################+  // ##########################################################################+  template <class T>+  T flattenInner(const std::vector<T>& vec)+  {+    T outer;+    for (const auto& sub : vec) {+      outer.emplace_back(std::move(flatten(sub)));+    }+    return outer;+  }+  // EO flattenInner++  // ##########################################################################+  // Equivalent to dart `cnt.any`+  // Pass additional closure variables to `fn`+  // ##########################################################################+  template <class T, class U, typename ...Args>+  bool hasAny(const T& cnt, U fn, Args... args) {+    for (const auto& sub : cnt) {+      if (fn(sub, args...)) {+        return true;+      }+    }+    return false;+  }+  // EO hasAny++  // ##########################################################################+  // Equivalent to dart `cnt.take(len).any`+  // Pass additional closure variables to `fn`+  // ##########################################################################+  template <class T, class U, typename ...Args>+  bool hasSubAny(const T& cnt, size_t len, U fn, Args... args) {+    for (size_t i = 0; i < len; i++) {+      if (fn(cnt[i], args...)) {+        return true;+      }+    }+    return false;+  }++  // ##########################################################################+  // Default predicate for lcs algorithm+  // ##########################################################################+  template <class T>+  inline bool lcsIdentityCmp(const T& X, const T& Y, T& result)+  {+    // Assert equality+    if (!ObjEqualityFn(X, Y)) {+      return false;+    }+    // Store in reference+    result = X;+    // Return success+    return true;+  }+  // EO lcsIdentityCmp++  // ##########################################################################+  // Longest common subsequence with predicate+  // ##########################################################################+  template <class T>+  std::vector<T> lcs(+    const std::vector<T>& X, const std::vector<T>& Y,+    bool(*select)(const T&, const T&, T&) = lcsIdentityCmp<T>)+  {++    std::size_t m = X.size(), mm = X.size() + 1;+    std::size_t n = Y.size(), nn = Y.size() + 1;++    if (m == 0) return {};+    if (n == 0) return {};++    // MSVC does not support variable-length arrays+    // To circumvent, allocate one array on the heap+    // Then use a macro to access via double index+    // e.g. `size_t L[m][n]` is supported by gcc+    size_t* len = new size_t[mm * nn + 1];+    bool* acc = new bool[mm * nn + 1];+    T* res = new T[mm * nn + 1];++    #define LEN(x, y) len[(x) * nn + (y)]+    #define ACC(x, y) acc[(x) * nn + (y)]+    #define RES(x, y) res[(x) * nn + (y)]++    /* Following steps build L[m+1][n+1] in bottom up fashion. Note+      that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1] */+    for (size_t i = 0; i <= m; i++) {+      for (size_t j = 0; j <= n; j++) {+        if (i == 0 || j == 0)+          LEN(i, j) = 0;+        else {+          ACC(i - 1, j - 1) = select(X[i - 1], Y[j - 1], RES(i - 1, j - 1));+          if (ACC(i - 1, j - 1))+            LEN(i, j) = LEN(i - 1, j - 1) + 1;+          else+            LEN(i, j) = std::max(LEN(i - 1, j), LEN(i, j - 1));+        }+      }+    }++    // Following code is used to print LCS+    std::vector<T> lcs;+    std::size_t index = LEN(m, n);+    lcs.reserve(index);++    // Start from the right-most-bottom-most corner+    // and one by one store objects in lcs[]+    std::size_t i = m, j = n;+    while (i > 0 && j > 0) {++      // If current objects in X[] and Y are same,+      // then current object is part of LCS+      if (ACC(i - 1, j - 1))+      {+        // Put the stored object in result+        // Note: we push instead of unshift+        // Note: reverse the vector later+        // ToDo: is deque more performant?+        lcs.push_back(RES(i - 1, j - 1));+        // reduce values of i, j and index+        i -= 1; j -= 1; index -= 1;+      }++      // If not same, then find the larger of two and+      // go in the direction of larger value+      else if (LEN(i - 1, j) > LEN(i, j - 1)) {+        i--;+      }+      else {+        j--;+      }++    }++    // reverse now as we used push_back+    std::reverse(lcs.begin(), lcs.end());++    // Delete temp memory on heap+    delete[] len;+    delete[] acc;+    delete[] res;++    #undef LEN+    #undef ACC+    #undef RES++    return lcs;+  }+  // EO lcs++  // ##########################################################################+  // ##########################################################################++}++#endif
libsass/src/debugger.hpp view
@@ -1,29 +1,288 @@ #ifndef SASS_DEBUGGER_H #define SASS_DEBUGGER_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <queue>+#include <vector> #include <string> #include <sstream>-#include "node.hpp"+#include "ast.hpp" #include "ast_fwd_decl.hpp"+#include "extension.hpp" +#include "ordered_map.hpp"+ using namespace Sass; -inline void debug_ast(AST_Node_Ptr node, std::string ind = "", Env* env = 0);+inline void debug_ast(AST_Node* node, std::string ind = "", Env* env = 0); -inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {-  debug_ast(const_cast<AST_Node*>(node), ind, env);+inline std::string debug_vec(const AST_Node* node) {+  if (node == NULL) return "null";+  else return node->to_string(); } -inline void debug_sources_set(ComplexSelectorSet& set, std::string ind = "")-{-  if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-  for(auto const &pair : set) {-    debug_ast(pair, ind + "");-    // debug_ast(set[pair], ind + "first: ");+inline std::string debug_dude(std::vector<std::vector<int>> vec) {+  std::stringstream out;+  out << "{";+  bool joinOut = false;+  for (auto ct : vec) {+    if (joinOut) out << ", ";+    joinOut = true;+    out << "{";+    bool joinIn = false;+    for (auto nr : ct) {+      if (joinIn) out << ", ";+      joinIn = true;+      out << nr;+    }+    out << "}";   }-  if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+  out << "}";+  return out.str(); } +inline std::string debug_vec(std::string& str) {+  return str;+}++inline std::string debug_vec(Extension& ext) {+  std::stringstream out;+  out << debug_vec(ext.extender);+  out << " {@extend ";+  out << debug_vec(ext.target);+  if (ext.isOptional) {+    out << " !optional";+  }+  out << "}";+  return out.str();+}++template <class T>+inline std::string debug_vec(std::vector<T> vec) {+  std::stringstream out;+  out << "[";+  for (size_t i = 0; i < vec.size(); i += 1) {+    if (i > 0) out << ", ";+    out << debug_vec(vec[i]);+  }+  out << "]";+  return out.str();+}++template <class T>+inline std::string debug_vec(std::queue<T> vec) {+  std::stringstream out;+  out << "{";+  for (size_t i = 0; i < vec.size(); i += 1) {+    if (i > 0) out << ", ";+    out << debug_vec(vec[i]);+  }+  out << "}";+  return out.str();+}++template <class T, class U, class O>+inline std::string debug_vec(std::map<T, U, O> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(it->first) // string (key)+      << ": "+      << debug_vec(it->second); // string's value+    joinit = true;+  }+  out << "}";+  return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_vec(const ordered_map<T, U, O, V>& vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(*it); // string (key)+    // << debug_vec(it->second); // string's value+    joinit = true;+  }+  out << "}";+  return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_vec(std::unordered_map<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(it->first) // string (key)+      << ": "+      << debug_vec(it->second); // string's value+    joinit = true;+  }+  out << "}";+  return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_keys(std::unordered_map<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(it->first); // string (key)+    joinit = true;+  }+  out << "}";+  return out.str();+}++inline std::string debug_vec(ExtListSelSet& vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(*it); // string (key)+    joinit = true;+  }+  out << "}";+  return out.str();+}++/*+template <class T, class U, class O, class V>+inline std::string debug_values(tsl::ordered_map<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(const_cast<U&>(it->second)); // string's value+    joinit = true;+  }+  out << "}";+  return out.str();+}+ +template <class T, class U, class O, class V>+inline std::string debug_vec(tsl::ordered_map<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(it->first) // string (key)+      << ": "+      << debug_vec(const_cast<U&>(it->second)); // string's value+    joinit = true;+  }+  out << "}";+  return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_vals(tsl::ordered_map<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(const_cast<U&>(it->second)); // string's value+    joinit = true;+  }+  out << "}";+  return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_keys(tsl::ordered_map<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto it = vec.begin(); it != vec.end(); it++)+  {+    if (joinit) out << ", ";+    out << debug_vec(it->first);+    joinit = true;+  }+  out << "}";+  return out.str();+}+*/++template <class T, class U>+inline std::string debug_vec(std::set<T, U> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto item : vec) {+    if (joinit) out << ", ";+    out << debug_vec(item);+    joinit = true;+  }+  out << "}";+  return out.str();+}++/*+template <class T, class U, class O, class V>+inline std::string debug_vec(tsl::ordered_set<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto item : vec) {+    if (joinit) out << ", ";+    out << debug_vec(item);+    joinit = true;+  }+  out << "}";+  return out.str();+}+*/++template <class T, class U, class O, class V>+inline std::string debug_vec(std::unordered_set<T, U, O, V> vec) {+  std::stringstream out;+  out << "{";+  bool joinit = false;+  for (auto item : vec) {+    if (joinit) out << ", ";+    out << debug_vec(item);+    joinit = true;+  }+  out << "}";+  return out.str();+}++inline std::string debug_bool(bool val) {+  return val ? "true" : "false";+}+inline std::string debug_vec(ExtSmplSelSet* node) {+  if (node == NULL) return "null";+  else return debug_vec(*node);+}++inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {+  debug_ast(const_cast<AST_Node*>(node), ind, env);+}+ inline std::string str_replace(std::string str, const std::string& oldStr, const std::string& newStr) {   size_t pos = 0;@@ -48,12 +307,12 @@   return is.str(); } -inline std::string pstate_source_position(AST_Node_Ptr node)+inline std::string pstate_source_position(AST_Node* node) {   std::stringstream str;   Position start(node->pstate());   Position end(start + node->pstate().offset);-  str << (start.file == std::string::npos ? -1 : start.file)+  str << (start.file == std::string::npos ? 99999999 : start.file)     << "@[" << start.line << ":" << start.column << "]"     << "-[" << end.line << ":" << end.column << "]"; #ifdef DEBUG_SHARED_PTR@@ -64,203 +323,163 @@   return str.str(); } -inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)+inline void debug_ast(AST_Node* node, std::string ind, Env* env) {   if (node == 0) return;   if (ind == "") std::cerr << "####################################################################\n";   if (Cast<Bubble>(node)) {-    Bubble_Ptr bubble = Cast<Bubble>(node);+    Bubble* bubble = Cast<Bubble>(node);     std::cerr << ind << "Bubble " << bubble;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << bubble->tabs();     std::cerr << std::endl;     debug_ast(bubble->node(), ind + " ", env);   } else if (Cast<Trace>(node)) {-    Trace_Ptr trace = Cast<Trace>(node);+    Trace* trace = Cast<Trace>(node);     std::cerr << ind << "Trace " << trace;     std::cerr << " (" << pstate_source_position(node) << ")"     << " [name:" << trace->name() << ", type: " << trace->type() << "]"     << std::endl;     debug_ast(trace->block(), ind + " ", env);   } else if (Cast<At_Root_Block>(node)) {-    At_Root_Block_Ptr root_block = Cast<At_Root_Block>(node);+    At_Root_Block* root_block = Cast<At_Root_Block>(node);     std::cerr << ind << "At_Root_Block " << root_block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << root_block->tabs();     std::cerr << std::endl;     debug_ast(root_block->expression(), ind + ":", env);     debug_ast(root_block->block(), ind + " ", env);-  } else if (Cast<Selector_List>(node)) {-    Selector_List_Ptr selector = Cast<Selector_List>(node);-    std::cerr << ind << "Selector_List " << selector;+  } else if (Cast<SelectorList>(node)) {+    SelectorList* selector = Cast<SelectorList>(node);+    std::cerr << ind << "SelectorList " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";-    std::cerr << " [@media:" << selector->media_block() << "]";-    std::cerr << (selector->is_invisible() ? " [INVISIBLE]": " -");-    std::cerr << (selector->has_placeholder() ? " [PLACEHOLDER]": " -");-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+    std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -");+    std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -");+    std::cerr << (selector->has_real_parent_ref() ? " [real-parent]": " -");     std::cerr << std::endl;-    debug_ast(selector->schema(), ind + "#{} "); -    for(const Complex_Selector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }+    for(const ComplexSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); } -//  } else if (Cast<Expression>(node)) {-//    Expression_Ptr expression = Cast<Expression>(node);-//    std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl;+  } else if (Cast<ComplexSelector>(node)) {+    ComplexSelector* selector = Cast<ComplexSelector>(node);+    std::cerr << ind << "ComplexSelector " << selector+      << " (" << pstate_source_position(node) << ")"+      << " <" << selector->hash() << ">"+      << " [" << (selector->chroots() ? "CHROOT" : "CONNECT") << "]"+      << " [length:" << longToHex(selector->length()) << "]"+      << " [weight:" << longToHex(selector->specificity()) << "]"+      << (selector->is_invisible() ? " [is_invisible]" : " -")+      << (selector->isInvisible() ? " [isInvisible]" : " -")+      << (selector->hasPreLineFeed() ? " [hasPreLineFeed]" : " -") -  } else if (Cast<Parent_Selector>(node)) {-    Parent_Selector_Ptr selector = Cast<Parent_Selector>(node);-    std::cerr << ind << "Parent_Selector " << selector;-//    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 << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;-//    debug_ast(selector->selector(), ind + "->", env);+      // << (selector->is_invisible() ? " [INVISIBLE]": " -")+      // << (selector->has_placeholder() ? " [PLACEHOLDER]": " -")+      // << (selector->is_optional() ? " [is_optional]": " -")+      << (selector->has_real_parent_ref() ? " [real parent]": " -")+      // << (selector->has_line_feed() ? " [line-feed]": " -")+      // << (selector->has_line_break() ? " [line-break]": " -")+      << " -- \n"; -  } else if (Cast<Complex_Selector>(node)) {-    Complex_Selector_Ptr selector = Cast<Complex_Selector>(node);-    std::cerr << ind << "Complex_Selector " << selector+    for(const SelectorComponentObj& i : selector->elements()) { debug_ast(i, ind + " ", env); }++  } else if (Cast<SelectorCombinator>(node)) {+    SelectorCombinator* selector = Cast<SelectorCombinator>(node);+    std::cerr << ind << "SelectorCombinator " << selector       << " (" << pstate_source_position(node) << ")"       << " <" << selector->hash() << ">"-      << " [length:" << longToHex(selector->length()) << "]"       << " [weight:" << longToHex(selector->specificity()) << "]"-      << " [@media:" << selector->media_block() << "]"-      << (selector->is_invisible() ? " [INVISIBLE]": " -")-      << (selector->has_placeholder() ? " [PLACEHOLDER]": " -")-      << (selector->is_optional() ? " [is_optional]": " -")-      << (selector->has_parent_ref() ? " [has parent]": " -")-      << (selector->has_line_feed() ? " [line-feed]": " -")-      << (selector->has_line_break() ? " [line-break]": " -")+      << (selector->has_real_parent_ref() ? " [real parent]": " -")       << " -- ";+       std::string del;       switch (selector->combinator()) {-        case Complex_Selector::PARENT_OF:   del = ">"; break;-        case Complex_Selector::PRECEDES:    del = "~"; break;-        case Complex_Selector::ADJACENT_TO: del = "+"; break;-        case Complex_Selector::ANCESTOR_OF: del = " "; break;-        case Complex_Selector::REFERENCE:   del = "//"; break;+        case SelectorCombinator::CHILD:    del = ">"; break;+        case SelectorCombinator::GENERAL:  del = "~"; break;+        case SelectorCombinator::ADJACENT: del = "+"; break;       }-      // if (del = "/") del += selector->reference()->perform(&to_string) + "/";-    std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;-    debug_ast(selector->head(), ind + " " /* + "[" + del + "]" */, env);-    if (selector->tail()) {-      debug_ast(selector->tail(), ind + "{" + del + "}", env);-    } else if(del != " ") {-      std::cerr << ind << " |" << del << "| {trailing op}" << std::endl;-    }-    ComplexSelectorSet set = selector->sources();-    // debug_sources_set(set, ind + "  @--> ");-  } else if (Cast<Compound_Selector>(node)) {-    Compound_Selector_Ptr selector = Cast<Compound_Selector>(node);-    std::cerr << ind << "Compound_Selector " << selector;++      std::cerr << "[" << del << "]" << "\n";++  } else if (Cast<CompoundSelector>(node)) {+    CompoundSelector* selector = Cast<CompoundSelector>(node);+    std::cerr << ind << "CompoundSelector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";+    std::cerr << (selector->hasRealParent() ? " [REAL PARENT]" : "") << ">";     std::cerr << " [weight:" << longToHex(selector->specificity()) << "]";-    std::cerr << " [@media:" << selector->media_block() << "]";-    std::cerr << (selector->extended() ? " [extended]": " -");-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");-    std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;-    for(const Simple_Selector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }-  } else if (Cast<Wrapped_Selector>(node)) {-    Wrapped_Selector_Ptr selector = Cast<Wrapped_Selector>(node);-    std::cerr << ind << "Wrapped_Selector " << selector;+    std::cerr << (selector->hasPostLineBreak() ? " [hasPostLineBreak]" : " -");+    std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -");+    std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -");+    std::cerr << "\n";+    for(const SimpleSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }++  } else if (Cast<Parent_Reference>(node)) {+    Parent_Reference* selector = Cast<Parent_Reference>(node);+    std::cerr << ind << "Parent_Reference " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";-    std::cerr << " <<" << selector->ns_name() << ">>";-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");-    std::cerr << std::endl;-    debug_ast(selector->selector(), ind + " () ", env);+    std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;+   } else if (Cast<Pseudo_Selector>(node)) {-    Pseudo_Selector_Ptr selector = Cast<Pseudo_Selector>(node);+    Pseudo_Selector* selector = Cast<Pseudo_Selector>(node);     std::cerr << ind << "Pseudo_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";     std::cerr << " <<" << selector->ns_name() << ">>";-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+    std::cerr << (selector->isClass() ? " [isClass]": " -");+    std::cerr << (selector->isSyntacticClass() ? " [isSyntacticClass]": " -");     std::cerr << std::endl;-    debug_ast(selector->expression(), ind + " <= ", env);+    debug_ast(selector->argument(), ind + " <= ", env);+    debug_ast(selector->selector(), ind + " || ", env);   } else if (Cast<Attribute_Selector>(node)) {-    Attribute_Selector_Ptr selector = Cast<Attribute_Selector>(node);+    Attribute_Selector* selector = Cast<Attribute_Selector>(node);     std::cerr << ind << "Attribute_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";     std::cerr << " <<" << selector->ns_name() << ">>";-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");     std::cerr << std::endl;     debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);   } else if (Cast<Class_Selector>(node)) {-    Class_Selector_Ptr selector = Cast<Class_Selector>(node);+    Class_Selector* selector = Cast<Class_Selector>(node);     std::cerr << ind << "Class_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";     std::cerr << " <<" << selector->ns_name() << ">>";-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");     std::cerr << std::endl;   } else if (Cast<Id_Selector>(node)) {-    Id_Selector_Ptr selector = Cast<Id_Selector>(node);+    Id_Selector* selector = Cast<Id_Selector>(node);     std::cerr << ind << "Id_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";     std::cerr << " <<" << selector->ns_name() << ">>";-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");     std::cerr << std::endl;-  } else if (Cast<Element_Selector>(node)) {-    Element_Selector_Ptr selector = Cast<Element_Selector>(node);-    std::cerr << ind << "Element_Selector " << selector;+  } else if (Cast<Type_Selector>(node)) {+    Type_Selector* selector = Cast<Type_Selector>(node);+    std::cerr << ind << "Type_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <" << selector->hash() << ">";     std::cerr << " <<" << selector->ns_name() << ">>";-    std::cerr << (selector->is_optional() ? " [is_optional]": " -");-    std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");-    std::cerr << (selector->has_line_break() ? " [line-break]": " -");-    std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");     std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">";     std::cerr << std::endl;   } else if (Cast<Placeholder_Selector>(node)) { -    Placeholder_Selector_Ptr selector = Cast<Placeholder_Selector>(node);+    Placeholder_Selector* selector = Cast<Placeholder_Selector>(node);     std::cerr << ind << "Placeholder_Selector [" << selector->ns_name() << "] " << selector;     std::cerr << " (" << pstate_source_position(selector) << ")"       << " <" << selector->hash() << ">"-      << " [@media:" << selector->media_block() << "]"-      << (selector->is_optional() ? " [is_optional]": " -")-      << (selector->has_line_break() ? " [line-break]": " -")-      << (selector->has_line_feed() ? " [line-feed]": " -")+      << (selector->isInvisible() ? " [isInvisible]" : " -")     << std::endl; -  } else if (Cast<Simple_Selector>(node)) {-    Simple_Selector* selector = Cast<Simple_Selector>(node);-    std::cerr << ind << "Simple_Selector " << selector;+  } else if (Cast<SimpleSelector>(node)) {+    SimpleSelector* selector = Cast<SimpleSelector>(node);+    std::cerr << ind << "SimpleSelector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;    } else if (Cast<Selector_Schema>(node)) {-    Selector_Schema_Ptr selector = Cast<Selector_Schema>(node);+    Selector_Schema* selector = Cast<Selector_Schema>(node);     std::cerr << ind << "Selector_Schema " << selector;     std::cerr << " (" << pstate_source_position(node) << ")"-      << " [@media:" << selector->media_block() << "]"       << (selector->connect_parent() ? " [connect-parent]": " -")     << std::endl; @@ -268,15 +487,13 @@     // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }    } else if (Cast<Selector>(node)) {-    Selector_Ptr selector = Cast<Selector>(node);+    Selector* selector = Cast<Selector>(node);     std::cerr << ind << "Selector " << selector;-    std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << (selector->has_line_break() ? " [line-break]": " -")-      << (selector->has_line_feed() ? " [line-feed]": " -")+    std::cerr << " (" << pstate_source_position(node) << ")"     << std::endl;    } else if (Cast<Media_Query_Expression>(node)) {-    Media_Query_Expression_Ptr block = Cast<Media_Query_Expression>(node);+    Media_Query_Expression* block = Cast<Media_Query_Expression>(node);     std::cerr << ind << "Media_Query_Expression " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -")@@ -285,7 +502,7 @@     debug_ast(block->value(), ind + " value) ");    } else if (Cast<Media_Query>(node)) {-    Media_Query_Ptr block = Cast<Media_Query>(node);+    Media_Query* block = Cast<Media_Query>(node);     std::cerr << ind << "Media_Query " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << (block->is_negated() ? " [is_negated]": " -")@@ -293,81 +510,101 @@     << std::endl;     debug_ast(block->media_type(), ind + " ");     for(const auto& i : block->elements()) { debug_ast(i, ind + " ", env); }--  } else if (Cast<Media_Block>(node)) {-    Media_Block_Ptr block = Cast<Media_Block>(node);-    std::cerr << ind << "Media_Block " << block;-    std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->media_queries(), ind + " =@ ");-    if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+  }+  else if (Cast<MediaRule>(node)) {+    MediaRule* rule = Cast<MediaRule>(node);+    std::cerr << ind << "MediaRule " << rule;+    std::cerr << " (" << pstate_source_position(rule) << ")";+    std::cerr << " " << rule->tabs() << std::endl;+    debug_ast(rule->schema(), ind + " =@ ");+    debug_ast(rule->block(), ind + " ");+  }+  else if (Cast<CssMediaRule>(node)) {+    CssMediaRule* rule = Cast<CssMediaRule>(node);+    std::cerr << ind << "CssMediaRule " << rule;+    std::cerr << " (" << pstate_source_position(rule) << ")";+    std::cerr << " " << rule->tabs() << std::endl;+    for (auto item : rule->elements()) {+      debug_ast(item, ind + " == ");+    }+    debug_ast(rule->block(), ind + " ");+  }+  else if (Cast<CssMediaQuery>(node)) {+    CssMediaQuery* query = Cast<CssMediaQuery>(node);+    std::cerr << ind << "CssMediaQuery " << query;+    std::cerr << " (" << pstate_source_position(query) << ")";+    std::cerr << " [" << (query->modifier()) << "] ";+    std::cerr << " [" << (query->type()) << "] ";+    std::cerr << " " << debug_vec(query->features());+    std::cerr << std::endl;   } else if (Cast<Supports_Block>(node)) {-    Supports_Block_Ptr block = Cast<Supports_Block>(node);+    Supports_Block* block = Cast<Supports_Block>(node);     std::cerr << ind << "Supports_Block " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     debug_ast(block->condition(), ind + " =@ ");     debug_ast(block->block(), ind + " <>");   } else if (Cast<Supports_Operator>(node)) {-    Supports_Operator_Ptr block = Cast<Supports_Operator>(node);+    Supports_Operator* block = Cast<Supports_Operator>(node);     std::cerr << ind << "Supports_Operator " << block;     std::cerr << " (" << pstate_source_position(node) << ")"     << std::endl;     debug_ast(block->left(), ind + " left) ");     debug_ast(block->right(), ind + " right) ");   } else if (Cast<Supports_Negation>(node)) {-    Supports_Negation_Ptr block = Cast<Supports_Negation>(node);+    Supports_Negation* block = Cast<Supports_Negation>(node);     std::cerr << ind << "Supports_Negation " << block;     std::cerr << " (" << pstate_source_position(node) << ")"     << std::endl;     debug_ast(block->condition(), ind + " condition) ");   } else if (Cast<At_Root_Query>(node)) {-    At_Root_Query_Ptr block = Cast<At_Root_Query>(node);+    At_Root_Query* block = Cast<At_Root_Query>(node);     std::cerr << ind << "At_Root_Query " << block;     std::cerr << " (" << pstate_source_position(node) << ")"     << std::endl;     debug_ast(block->feature(), ind + " feature) ");     debug_ast(block->value(), ind + " value) ");   } else if (Cast<Supports_Declaration>(node)) {-    Supports_Declaration_Ptr block = Cast<Supports_Declaration>(node);+    Supports_Declaration* block = Cast<Supports_Declaration>(node);     std::cerr << ind << "Supports_Declaration " << block;     std::cerr << " (" << pstate_source_position(node) << ")"     << std::endl;     debug_ast(block->feature(), ind + " feature) ");     debug_ast(block->value(), ind + " value) ");   } else if (Cast<Block>(node)) {-    Block_Ptr root_block = Cast<Block>(node);+    Block* root_block = Cast<Block>(node);     std::cerr << ind << "Block " << root_block;     std::cerr << " (" << pstate_source_position(node) << ")";     if (root_block->is_root()) std::cerr << " [root]";+    if (root_block->isInvisible()) std::cerr << " [isInvisible]";     std::cerr << " " << root_block->tabs() << std::endl;     for(const Statement_Obj& i : root_block->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Warning>(node)) {-    Warning_Ptr block = Cast<Warning>(node);+    Warning* block = Cast<Warning>(node);     std::cerr << ind << "Warning " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     debug_ast(block->message(), ind + " : ");   } else if (Cast<Error>(node)) {-    Error_Ptr block = Cast<Error>(node);+    Error* block = Cast<Error>(node);     std::cerr << ind << "Error " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;   } else if (Cast<Debug>(node)) {-    Debug_Ptr block = Cast<Debug>(node);+    Debug* block = Cast<Debug>(node);     std::cerr << ind << "Debug " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     debug_ast(block->value(), ind + " ");   } else if (Cast<Comment>(node)) {-    Comment_Ptr block = Cast<Comment>(node);+    Comment* block = Cast<Comment>(node);     std::cerr << ind << "Comment " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() <<       " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl;     debug_ast(block->text(), ind + "// ", env);   } else if (Cast<If>(node)) {-    If_Ptr block = Cast<If>(node);+    If* block = Cast<If>(node);     std::cerr << ind << "If " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;@@ -375,30 +612,31 @@     debug_ast(block->block(), ind + " <>");     debug_ast(block->alternative(), ind + " ><");   } else if (Cast<Return>(node)) {-    Return_Ptr block = Cast<Return>(node);+    Return* block = Cast<Return>(node);     std::cerr << ind << "Return " << block;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " " << block->tabs() << std::endl;-  } else if (Cast<Extension>(node)) {-    Extension_Ptr block = Cast<Extension>(node);-    std::cerr << ind << "Extension " << block;+    std::cerr << " " << block->tabs();+    std::cerr << " [" << block->value()->to_string() << "]" << std::endl;+  } else if (Cast<ExtendRule>(node)) {+    ExtendRule* block = Cast<ExtendRule>(node);+    std::cerr << ind << "ExtendRule " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     debug_ast(block->selector(), ind + "-> ", env);   } else if (Cast<Content>(node)) {-    Content_Ptr block = Cast<Content>(node);+    Content* block = Cast<Content>(node);     std::cerr << ind << "Content " << block;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " [@media:" << block->media_block() << "]";     std::cerr << " " << block->tabs() << std::endl;+    debug_ast(block->arguments(), ind + " args: ", env);   } else if (Cast<Import_Stub>(node)) {-    Import_Stub_Ptr block = Cast<Import_Stub>(node);+    Import_Stub* block = Cast<Import_Stub>(node);     std::cerr << ind << "Import_Stub " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << block->imp_path() << "] ";     std::cerr << " " << block->tabs() << std::endl;   } else if (Cast<Import>(node)) {-    Import_Ptr block = Cast<Import>(node);+    Import* block = Cast<Import>(node);     std::cerr << ind << "Import " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;@@ -406,13 +644,13 @@     for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env);     debug_ast(block->import_queries(), ind + "@@ ");   } else if (Cast<Assignment>(node)) {-    Assignment_Ptr block = Cast<Assignment>(node);+    Assignment* block = Cast<Assignment>(node);     std::cerr << ind << "Assignment " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " <<" << block->variable() << ">> " << block->tabs() << std::endl;     debug_ast(block->value(), ind + "=", env);   } else if (Cast<Declaration>(node)) {-    Declaration_Ptr block = Cast<Declaration>(node);+    Declaration* block = Cast<Declaration>(node);     std::cerr << ind << "Declaration " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [is_custom_property: " << block->is_custom_property() << "] ";@@ -421,14 +659,14 @@     debug_ast(block->value(), ind + " value: ", env);     debug_ast(block->block(), ind + " ", env);   } else if (Cast<Keyframe_Rule>(node)) {-    Keyframe_Rule_Ptr has_block = Cast<Keyframe_Rule>(node);+    Keyframe_Rule* has_block = Cast<Keyframe_Rule>(node);     std::cerr << ind << "Keyframe_Rule " << has_block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << has_block->tabs() << std::endl;     if (has_block->name()) debug_ast(has_block->name(), ind + "@");     if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Directive>(node)) {-    Directive_Ptr block = Cast<Directive>(node);+    Directive* block = Cast<Directive>(node);     std::cerr << ind << "Directive " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl;@@ -436,25 +674,25 @@     debug_ast(block->value(), ind + "+", env);     if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Each>(node)) {-    Each_Ptr block = Cast<Each>(node);+    Each* block = Cast<Each>(node);     std::cerr << ind << "Each " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<For>(node)) {-    For_Ptr block = Cast<For>(node);+    For* block = Cast<For>(node);     std::cerr << ind << "For " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<While>(node)) {-    While_Ptr block = Cast<While>(node);+    While* block = Cast<While>(node);     std::cerr << ind << "While " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;     if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Definition>(node)) {-    Definition_Ptr block = Cast<Definition>(node);+    Definition* block = Cast<Definition>(node);     std::cerr << ind << "Definition " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [name: " << block->name() << "] ";@@ -466,14 +704,15 @@     debug_ast(block->parameters(), ind + " params: ", env);     if (block->block()) debug_ast(block->block(), ind + " ", env);   } else if (Cast<Mixin_Call>(node)) {-    Mixin_Call_Ptr block = Cast<Mixin_Call>(node);+    Mixin_Call* block = Cast<Mixin_Call>(node);     std::cerr << ind << "Mixin_Call " << block << " " << block->tabs();     std::cerr << " (" << pstate_source_position(block) << ")";     std::cerr << " [" <<  block->name() << "]";     std::cerr << " [has_content: " << block->has_content() << "] " << std::endl;-    debug_ast(block->arguments(), ind + " args: ");+    debug_ast(block->arguments(), ind + " args: ", env);+    debug_ast(block->block_parameters(), ind + " block_params: ", env);     if (block->block()) debug_ast(block->block(), ind + " ", env);-  } else if (Ruleset_Ptr ruleset = Cast<Ruleset>(node)) {+  } else if (Ruleset* ruleset = Cast<Ruleset>(node)) {     std::cerr << ind << "Ruleset " << ruleset;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [indent: " << ruleset->tabs() << "]";@@ -483,30 +722,22 @@     debug_ast(ruleset->selector(), ind + ">");     debug_ast(ruleset->block(), ind + " ");   } else if (Cast<Block>(node)) {-    Block_Ptr block = Cast<Block>(node);+    Block* block = Cast<Block>(node);     std::cerr << ind << "Block " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << (block->is_invisible() ? " [INVISIBLE]" : "");     std::cerr << " [indent: " << block->tabs() << "]" << std::endl;     for(const Statement_Obj& i : block->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Variable>(node)) {-    Variable_Ptr expression = Cast<Variable>(node);+    Variable* expression = Cast<Variable>(node);     std::cerr << ind << "Variable " << expression;     std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << expression->name() << "]" << std::endl;     std::string name(expression->name());     if (env && env->has(name)) debug_ast(Cast<Expression>((*env)[name]), ind + " -> ", env);-  } else if (Cast<Function_Call_Schema>(node)) {-    Function_Call_Schema_Ptr expression = Cast<Function_Call_Schema>(node);-    std::cerr << ind << "Function_Call_Schema " << expression;-    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";-    std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << "" << std::endl;-    debug_ast(expression->name(), ind + "name: ", env);-    debug_ast(expression->arguments(), ind + " args: ", env);   } else if (Cast<Function_Call>(node)) {-    Function_Call_Ptr expression = Cast<Function_Call>(node);+    Function_Call* expression = Cast<Function_Call>(node);     std::cerr << ind << "Function_Call " << expression;     std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";@@ -518,14 +749,14 @@     debug_ast(expression->arguments(), ind + " args: ", env);     debug_ast(expression->func(), ind + " func: ", env);   } else if (Cast<Function>(node)) {-    Function_Ptr expression = Cast<Function>(node);+    Function* expression = Cast<Function>(node);     std::cerr << ind << "Function " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     if (expression->is_css()) std::cerr << " [css]";     std::cerr << std::endl;     debug_ast(expression->definition(), ind + " definition: ", env);   } else if (Cast<Arguments>(node)) {-    Arguments_Ptr expression = Cast<Arguments>(node);+    Arguments* expression = Cast<Arguments>(node);     std::cerr << ind << "Arguments " << expression;     if (expression->is_delayed()) std::cerr << " [delayed]";     std::cerr << " (" << pstate_source_position(node) << ")";@@ -535,7 +766,7 @@     std::cerr << std::endl;     for(const Argument_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Argument>(node)) {-    Argument_Ptr expression = Cast<Argument>(node);+    Argument* expression = Cast<Argument>(node);     std::cerr << ind << "Argument " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << expression->value().ptr() << "]";@@ -544,7 +775,7 @@     std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl;     debug_ast(expression->value(), ind + " value: ", env);   } else if (Cast<Parameters>(node)) {-    Parameters_Ptr expression = Cast<Parameters>(node);+    Parameters* expression = Cast<Parameters>(node);     std::cerr << ind << "Parameters " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] ";@@ -552,14 +783,14 @@     std::cerr << std::endl;     for(const Parameter_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Parameter>(node)) {-    Parameter_Ptr expression = Cast<Parameter>(node);+    Parameter* expression = Cast<Parameter>(node);     std::cerr << ind << "Parameter " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [name: " << expression->name() << "] ";     std::cerr << " [default: " << expression->default_value().ptr() << "] ";     std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl;   } else if (Cast<Unary_Expression>(node)) {-    Unary_Expression_Ptr expression = Cast<Unary_Expression>(node);+    Unary_Expression* expression = Cast<Unary_Expression>(node);     std::cerr << ind << "Unary_Expression " << expression;     std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " [delayed: " << expression->is_delayed() << "] ";@@ -567,7 +798,7 @@     std::cerr << " [" << expression->type() << "]" << std::endl;     debug_ast(expression->operand(), ind + " operand: ", env);   } else if (Cast<Binary_Expression>(node)) {-    Binary_Expression_Ptr expression = Cast<Binary_Expression>(node);+    Binary_Expression* expression = Cast<Binary_Expression>(node);     std::cerr << ind << "Binary_Expression " << expression;     if (expression->is_interpolant()) std::cerr << " [is interpolant] ";     if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";@@ -580,7 +811,7 @@     debug_ast(expression->left(), ind + " left:  ", env);     debug_ast(expression->right(), ind + " right: ", env);   } else if (Cast<Map>(node)) {-    Map_Ptr expression = Cast<Map>(node);+    Map* expression = Cast<Map>(node);     std::cerr << ind << "Map " << expression;     std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";@@ -590,7 +821,7 @@       debug_ast(i.second, ind + " val: ");     }   } else if (Cast<List>(node)) {-    List_Ptr expression = Cast<List>(node);+    List* expression = Cast<List>(node);     std::cerr << ind << "List " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " (" << expression->length() << ") " <<@@ -604,27 +835,30 @@       " [hash: " << expression->hash() << "] " <<       std::endl;     for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }-  } else if (Cast<Content>(node)) {-    Content_Ptr expression = Cast<Content>(node);-    std::cerr << ind << "Content " << expression;-    std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " [@media:" << expression->media_block() << "]";-    std::cerr << " [Statement]" << std::endl;   } else if (Cast<Boolean>(node)) {-    Boolean_Ptr expression = Cast<Boolean>(node);+    Boolean* expression = Cast<Boolean>(node);     std::cerr << ind << "Boolean " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " [" << expression->value() << "]" << std::endl;-  } else if (Cast<Color>(node)) {-    Color_Ptr expression = Cast<Color>(node);+  } else if (Cast<Color_RGBA>(node)) {+    Color_RGBA* expression = Cast<Color_RGBA>(node);     std::cerr << ind << "Color " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";+    std::cerr << " [name: " << expression->disp() << "] ";     std::cerr << " [delayed: " << expression->is_delayed() << "] ";     std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";-    std::cerr << " [" << expression->r() << ":"  << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;+    std::cerr << " rgba[" << expression->r() << ":"  << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;+  } else if (Cast<Color_HSLA>(node)) {+    Color_HSLA* expression = Cast<Color_HSLA>(node);+    std::cerr << ind << "Color " << expression;+    std::cerr << " (" << pstate_source_position(node) << ")";+    std::cerr << " [name: " << expression->disp() << "] ";+    std::cerr << " [delayed: " << expression->is_delayed() << "] ";+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";+    std::cerr << " hsla[" << expression->h() << ":"  << expression->s() << ":" << expression->l() << "@" << expression->a() << "]" << std::endl;   } else if (Cast<Number>(node)) {-    Number_Ptr expression = Cast<Number>(node);+    Number* expression = Cast<Number>(node);     std::cerr << ind << "Number " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [delayed: " << expression->is_delayed() << "] ";@@ -633,14 +867,14 @@       " [hash: " << expression->hash() << "] " <<       std::endl;   } else if (Cast<Null>(node)) {-    Null_Ptr expression = Cast<Null>(node);+    Null* expression = Cast<Null>(node);     std::cerr << ind << "Null " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [interpolant: " << expression->is_interpolant() << "] "       // " [hash: " << expression->hash() << "] "       << std::endl;   } else if (Cast<String_Quoted>(node)) {-    String_Quoted_Ptr expression = Cast<String_Quoted>(node);+    String_Quoted* expression = Cast<String_Quoted>(node);     std::cerr << ind << "String_Quoted " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << prettyprint(expression->value()) << "]";@@ -649,7 +883,7 @@     if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;   } else if (Cast<String_Constant>(node)) {-    String_Constant_Ptr expression = Cast<String_Constant>(node);+    String_Constant* expression = Cast<String_Constant>(node);     std::cerr << ind << "String_Constant " << expression;     if (expression->concrete_type()) {       std::cerr << " " << expression->concrete_type();@@ -660,7 +894,7 @@     if (expression->is_interpolant()) std::cerr << " [interpolant]";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;   } else if (Cast<String_Schema>(node)) {-    String_Schema_Ptr expression = Cast<String_Schema>(node);+    String_Schema* expression = Cast<String_Schema>(node);     std::cerr << ind << "String_Schema " << expression;     std::cerr << " (" << pstate_source_position(expression) << ")";     std::cerr << " " << expression->concrete_type();@@ -674,42 +908,43 @@     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;     for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<String>(node)) {-    String_Ptr expression = Cast<String>(node);+    String* expression = Cast<String>(node);     std::cerr << ind << "String " << expression;     std::cerr << " " << expression->concrete_type();     std::cerr << " (" << pstate_source_position(node) << ")";     if (expression->is_interpolant()) std::cerr << " [interpolant]";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;   } else if (Cast<Expression>(node)) {-    Expression_Ptr expression = Cast<Expression>(node);+    Expression* expression = Cast<Expression>(node);     std::cerr << ind << "Expression " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     switch (expression->concrete_type()) {-      case Expression::Concrete_Type::NONE: std::cerr << " [NONE]"; break;-      case Expression::Concrete_Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break;-      case Expression::Concrete_Type::NUMBER: std::cerr << " [NUMBER]"; break;-      case Expression::Concrete_Type::COLOR: std::cerr << " [COLOR]"; break;-      case Expression::Concrete_Type::STRING: std::cerr << " [STRING]"; break;-      case Expression::Concrete_Type::LIST: std::cerr << " [LIST]"; break;-      case Expression::Concrete_Type::MAP: std::cerr << " [MAP]"; break;-      case Expression::Concrete_Type::SELECTOR: std::cerr << " [SELECTOR]"; break;-      case Expression::Concrete_Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break;-      case Expression::Concrete_Type::C_WARNING: std::cerr << " [C_WARNING]"; break;-      case Expression::Concrete_Type::C_ERROR: std::cerr << " [C_ERROR]"; break;-      case Expression::Concrete_Type::FUNCTION: std::cerr << " [FUNCTION]"; break;-      case Expression::Concrete_Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;-      case Expression::Concrete_Type::VARIABLE: std::cerr << " [VARIABLE]"; break;-      case Expression::Concrete_Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break;+      case Expression::Type::NONE: std::cerr << " [NONE]"; break;+      case Expression::Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break;+      case Expression::Type::NUMBER: std::cerr << " [NUMBER]"; break;+      case Expression::Type::COLOR: std::cerr << " [COLOR]"; break;+      case Expression::Type::STRING: std::cerr << " [STRING]"; break;+      case Expression::Type::LIST: std::cerr << " [LIST]"; break;+      case Expression::Type::MAP: std::cerr << " [MAP]"; break;+      case Expression::Type::SELECTOR: std::cerr << " [SELECTOR]"; break;+      case Expression::Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break;+      case Expression::Type::C_WARNING: std::cerr << " [C_WARNING]"; break;+      case Expression::Type::C_ERROR: std::cerr << " [C_ERROR]"; break;+      case Expression::Type::FUNCTION: std::cerr << " [FUNCTION]"; break;+      case Expression::Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;+      case Expression::Type::VARIABLE: std::cerr << " [VARIABLE]"; break;+      case Expression::Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break;+      case Expression::Type::PARENT: std::cerr << " [PARENT]"; break;     }     std::cerr << std::endl;   } else if (Cast<Has_Block>(node)) {-    Has_Block_Ptr has_block = Cast<Has_Block>(node);+    Has_Block* has_block = Cast<Has_Block>(node);     std::cerr << ind << "Has_Block " << has_block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << has_block->tabs() << std::endl;     if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }   } else if (Cast<Statement>(node)) {-    Statement_Ptr statement = Cast<Statement>(node);+    Statement* statement = Cast<Statement>(node);     std::cerr << ind << "Statement " << statement;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << statement->tabs() << std::endl;@@ -718,84 +953,12 @@   if (ind == "") std::cerr << "####################################################################\n"; } -inline void debug_node(Node* node, std::string ind = "")-{-  if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";-  if (node->isCombinator()) {-    std::cerr << ind;-    std::cerr << "Combinator ";-    std::cerr << node << " ";-    if (node->got_line_feed) std::cerr << "[LF] ";-    switch (node->combinator()) {-      case Complex_Selector::ADJACENT_TO: std::cerr << "{+} "; break;-      case Complex_Selector::PARENT_OF:   std::cerr << "{>} "; break;-      case Complex_Selector::PRECEDES:    std::cerr << "{~} "; break;-      case Complex_Selector::REFERENCE:   std::cerr << "{@} "; break;-      case Complex_Selector::ANCESTOR_OF: std::cerr << "{ } "; break;-    }-    std::cerr << std::endl;-    // debug_ast(node->combinator(), ind + "  ");-  } else if (node->isSelector()) {-    std::cerr << ind;-    std::cerr << "Selector ";-    std::cerr << node << " ";-    if (node->got_line_feed) std::cerr << "[LF] ";-    std::cerr << std::endl;-    debug_ast(node->selector(), ind + "  ");-  } else if (node->isCollection()) {-    std::cerr << ind;-    std::cerr << "Collection ";-    std::cerr << node << " ";-    if (node->got_line_feed) std::cerr << "[LF] ";-    std::cerr << std::endl;-    for(auto n : (*node->collection())) {-      debug_node(&n, ind + "  ");-    }-  } else if (node->isNil()) {-    std::cerr << ind;-    std::cerr << "Nil ";-    std::cerr << node << " ";-    if (node->got_line_feed) std::cerr << "[LF] ";-    std::cerr << std::endl;-  } else {-    std::cerr << ind;-    std::cerr << "OTHER ";-    std::cerr << node << " ";-    if (node->got_line_feed) std::cerr << "[LF] ";-    std::cerr << std::endl;-  }-  if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";-}  /*-inline void debug_ast(const AST_Node_Ptr node, std::string ind = "", Env* env = 0)+inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {-  debug_ast(const_cast<AST_Node_Ptr>(node), ind, env);+  debug_ast(const_cast<AST_Node*>(node), ind, env); } */-inline void debug_node(const Node* node, std::string ind = "")-{-  debug_node(const_cast<Node*>(node), ind);-}--inline void debug_subset_map(Sass::Subset_Map& map, std::string ind = "")-{-  if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-  for(auto const &it : map.values()) {-    debug_ast(it.first, ind + "first: ");-    debug_ast(it.second, ind + "second: ");-  }-  if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-}--inline void debug_subset_entries(SubSetMapPairs* entries, std::string ind = "")-{-  if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-  for(auto const &pair : *entries) {-    debug_ast(pair.first, ind + "first: ");-    debug_ast(pair.second, ind + "second: ");-  }-  if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-}  #endif // SASS_DEBUGGER
libsass/src/emitter.cpp view
@@ -1,9 +1,9 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include "util.hpp"-#include "context.hpp"-#include "output.hpp" #include "emitter.hpp"-#include "utf8_string.hpp"+#include "util_string.hpp"+#include "util.hpp"  namespace Sass { @@ -47,11 +47,11 @@   void Emitter::set_filename(const std::string& str)   { wbuf.smap.file = str; } -  void Emitter::schedule_mapping(const AST_Node_Ptr node)+  void Emitter::schedule_mapping(const AST_Node* node)   { scheduled_mapping = node; }-  void Emitter::add_open_mapping(const AST_Node_Ptr node)+  void Emitter::add_open_mapping(const AST_Node* node)   { wbuf.smap.add_open_mapping(node); }-  void Emitter::add_close_mapping(const AST_Node_Ptr node)+  void Emitter::add_close_mapping(const AST_Node* node)   { wbuf.smap.add_close_mapping(node); }   ParserState Emitter::remap(const ParserState& pstate)   { return wbuf.smap.remap(pstate); }@@ -134,13 +134,13 @@     // write space/lf     flush_schedules(); -    if (in_comment && output_style() == COMPACT) {-      // unescape comment nodes-      std::string out = comment_to_string(text);-      // add to buffer-      wbuf.buffer += out;-      // account for data in source-maps+    if (in_comment) {+      std::string out = Util::normalize_newlines(text);+      if (output_style() == COMPACT) {+        out = comment_to_compact_string(out);+      }       wbuf.smap.append(Offset(out));+      wbuf.buffer += std::move(out);     } else {       // add to buffer       wbuf.buffer += text;@@ -161,7 +161,7 @@    // append some text or token to the buffer   // this adds source-mappings for node start and end-  void Emitter::append_token(const std::string& text, const AST_Node_Ptr node)+  void Emitter::append_token(const std::string& text, const AST_Node* node)   {     flush_schedules();     add_open_mapping(node);@@ -263,7 +263,7 @@     }   } -  void Emitter::append_scope_opener(AST_Node_Ptr node)+  void Emitter::append_scope_opener(AST_Node* node)   {     scheduled_linefeed = 0;     append_optional_space();@@ -274,7 +274,7 @@     // append_optional_space();     ++ indentation;   }-  void Emitter::append_scope_closer(AST_Node_Ptr node)+  void Emitter::append_scope_closer(AST_Node* node)   {     -- indentation;     scheduled_linefeed = 0;
libsass/src/emitter.hpp view
@@ -1,8 +1,10 @@ #ifndef SASS_EMITTER_H #define SASS_EMITTER_H -#include <string>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp"@@ -25,9 +27,9 @@       // proxy methods for source maps       void add_source_index(size_t idx);       void set_filename(const std::string& str);-      void add_open_mapping(const AST_Node_Ptr node);-      void add_close_mapping(const AST_Node_Ptr node);-      void schedule_mapping(const AST_Node_Ptr node);+      void add_open_mapping(const AST_Node* node);+      void add_close_mapping(const AST_Node* node);+      void schedule_mapping(const AST_Node* node);       std::string render_srcmap(Context &ctx);       ParserState remap(const ParserState& pstate); @@ -37,8 +39,8 @@       size_t scheduled_space;       size_t scheduled_linefeed;       bool scheduled_delimiter;-      AST_Node_Ptr scheduled_crutch;-      AST_Node_Ptr scheduled_mapping;+      const AST_Node* scheduled_crutch;+      const AST_Node* scheduled_mapping;      public:       // output strings different in custom css properties@@ -75,7 +77,7 @@       void append_wspace(const std::string& text);       // append some text or token to the buffer       // this adds source-mappings for node start and end-      void append_token(const std::string& text, const AST_Node_Ptr node);+      void append_token(const std::string& text, const AST_Node* node);       // query last appended character       char last_char(); @@ -86,8 +88,8 @@       void append_special_linefeed(void);       void append_optional_linefeed(void);       void append_mandatory_linefeed(void);-      void append_scope_opener(AST_Node_Ptr node = 0);-      void append_scope_closer(AST_Node_Ptr node = 0);+      void append_scope_opener(AST_Node* node = 0);+      void append_scope_closer(AST_Node* node = 0);       void append_comma_separator(void);       void append_colon_separator(void);       void append_delimiter(void);
libsass/src/environment.cpp view
@@ -208,6 +208,20 @@    // use array access for getter and setter functions   template <typename T>+  T& Environment<T>::get(const std::string& key)+  {+    auto cur = this;+    while (cur) {+      if (cur->has_local(key)) {+        return cur->get_local(key);+      }+      cur = cur->parent_;+    }+    return get_local(key);+  }++  // use array access for getter and setter functions+  template <typename T>   T& Environment<T>::operator[](const std::string& key)   {     auto cur = this;@@ -230,7 +244,7 @@     for (typename environment_map<std::string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) {       if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) {         std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second;-        if (Value_Ptr val = Cast<Value>(i->second))+        if (Value* val = Cast<Value>(i->second))         { std::cerr << " : " << val->to_string(); }         std::cerr << std::endl;       }
libsass/src/environment.hpp view
@@ -1,12 +1,18 @@ #ifndef SASS_ENVIRONMENT_H #define SASS_ENVIRONMENT_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <map> #include <string> #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp"  namespace Sass { +  // this defeats the whole purpose of environment being templatable!!   typedef environment_map<std::string, AST_Node_Obj>::iterator EnvIter;    class EnvResult {@@ -94,6 +100,10 @@      // look on the full stack for key     // include all scopes available+    T& get(const std::string& key);++    // look on the full stack for key+    // include all scopes available     EnvResult find(const std::string& key);      // use array access for getter and setter functions@@ -107,6 +117,7 @@    // define typedef for our use case   typedef Environment<AST_Node_Obj> Env;+  typedef std::vector<Env*> EnvStack;  } 
libsass/src/error_handling.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp"@@ -15,43 +18,38 @@       prefix("Error"), pstate(pstate), traces(traces)     { } -    InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg)-    : Base(pstate, msg, traces)+    InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg, char* owned_src)+    : Base(pstate, msg, traces), owned_src(owned_src)     { }  -    InvalidParent::InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector)+    InvalidParent::InvalidParent(Selector* parent, Backtraces traces, Selector* selector)     : Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector)     {-      msg = "Invalid parent selector for \"";-      msg += selector->to_string(Sass_Inspect_Options());-      msg += "\": \"";-      msg += parent->to_string(Sass_Inspect_Options());-      msg += "\"";+      msg = "Invalid parent selector for "+        "\"" + selector->to_string(Sass_Inspect_Options()) + "\": "+        "\"" + parent->to_string(Sass_Inspect_Options()) + "\"";     } -    InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg)+    InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument* arg)     : Base(pstate, def_msg, traces), name(name), arg(arg)     {-      msg = "Variable keyword argument map must have string keys.\n";-      msg += name + " is not a string in " + arg->to_string() + ".";+      msg = "Variable keyword argument map must have string keys.\n" ++        name + " is not a string in " + arg->to_string() + ".";     } -    InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value)+    InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value* value)     : Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value)     {-      msg  = arg + ": \"";+      msg = arg + ": \"";       if (value) msg += value->to_string(Sass_Inspect_Options());-      msg += "\" is not a " + type;-      msg += " for `" + fn + "'";+      msg += "\" is not a " + type + " for `" + fn + "'";     }      MissingArgument::MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype)     : Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype)     {-      msg  = fntype + " " + fn;-      msg += " is missing argument ";-      msg += arg + ".";+      msg = fntype + " " + fn + " is missing argument " + arg + ".";     }      InvalidSyntax::InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg)@@ -65,87 +63,66 @@     DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org)     : Base(org.pstate(), def_msg, traces), dup(dup), org(org)     {-      msg  = "Duplicate key ";-      msg += dup.get_duplicate_key()->inspect();-      msg += " in map (";-      msg += org.inspect();-      msg += ").";+      msg = "Duplicate key " + dup.get_duplicate_key()->inspect() + " in map (" + org.inspect() + ").";     }      TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const std::string type)     : Base(var.pstate(), def_msg, traces), var(var), type(type)     {-      msg  = var.to_string();-      msg += " is not an ";-      msg += type;-      msg += ".";+      msg = var.to_string() + " is not an " + type + ".";     }      InvalidValue::InvalidValue(Backtraces traces, const Expression& val)     : Base(val.pstate(), def_msg, traces), val(val)     {-      msg  = val.to_string();-      msg += " isn't a valid CSS value.";+      msg = val.to_string() + " isn't a valid CSS value.";     }      StackError::StackError(Backtraces traces, const AST_Node& node)     : Base(node.pstate(), def_msg, traces), node(node)     {-      msg  = "stack level too deep";+      msg = "stack level too deep";     }      IncompatibleUnits::IncompatibleUnits(const Units& lhs, const Units& rhs)     {-      msg  = "Incompatible units: '";-      msg += rhs.unit();-      msg += "' and '";-      msg += lhs.unit();-      msg += "'.";+      msg = "Incompatible units: '" + rhs.unit() + "' and '" + lhs.unit() + "'.";     }      IncompatibleUnits::IncompatibleUnits(const UnitType lhs, const UnitType rhs)     {-      msg  = "Incompatible units: '";-      msg += unit_to_string(rhs);-      msg += "' and '";-      msg += unit_to_string(lhs);-      msg += "'.";+      msg = std::string("Incompatible units: '") + unit_to_string(rhs) + "' and '" + unit_to_string(lhs) + "'.";     } -    AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+    AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op)     : OperationError(), lhs(lhs), rhs(rhs), op(op)     {-      msg  = "Alpha channels must be equal: ";-      msg += lhs->to_string({ NESTED, 5 });-      msg += " " + sass_op_to_name(op) + " ";-      msg += rhs->to_string({ NESTED, 5 });-      msg += ".";+      msg = "Alpha channels must be equal: " ++        lhs->to_string({ NESTED, 5 }) ++        " " + sass_op_to_name(op) + " " ++        rhs->to_string({ NESTED, 5 }) + ".";     }      ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)     : OperationError(), lhs(lhs), rhs(rhs)     {-      msg  = "divided by 0";+      msg = "divided by 0";     } -    UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+    UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op)     : OperationError(), lhs(lhs), rhs(rhs), op(op)     {-      msg  = def_op_msg + ": \"";-      msg += lhs->to_string({ NESTED, 5 });-      msg += " " + sass_op_to_name(op) + " ";-      msg += rhs->to_string({ TO_SASS, 5 });-      msg += "\".";+      msg = def_op_msg + ": \"" ++        lhs->to_string({ NESTED, 5 }) ++        " " + sass_op_to_name(op) + " " ++        rhs->to_string({ TO_SASS, 5 }) ++        "\".";     } -    InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+    InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op)     : UndefinedOperation(lhs, rhs, op)     {-      msg  = def_op_null_msg + ": \"";-      msg += lhs->inspect();-      msg += " " + sass_op_to_name(op) + " ";-      msg += rhs->inspect();-      msg += "\".";+      msg = def_op_null_msg + ": \"" + lhs->inspect() + " " + sass_op_to_name(op) + " " + rhs->inspect() + "\".";     }      SassValueError::SassValueError(Backtraces traces, ParserState pstate, OperationError& err)@@ -154,6 +131,27 @@       msg = err.what();       prefix = err.errtype();     }++    TopLevelParent::TopLevelParent(Backtraces traces, ParserState pstate)+      : Base(pstate, "Top-level selectors may not contain the parent selector \"&\".", traces)+    {++    }++    UnsatisfiedExtend::UnsatisfiedExtend(Backtraces traces, Extension extension)+      : Base(extension.target->pstate(), "The target selector was not found.\n"+        "Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces)+    {++    }++    ExtendAcrossMedia::ExtendAcrossMedia(Backtraces traces, Extension extension)+      : Base(extension.target->pstate(), "You may not @extend selectors across media queries.\n"+        "Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces)+    {++    }+        } 
libsass/src/error_handling.hpp view
@@ -1,9 +1,14 @@ #ifndef SASS_ERROR_HANDLING_H #define SASS_ERROR_HANDLING_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include <string> #include <sstream> #include <stdexcept>+#include "units.hpp" #include "position.hpp" #include "backtrace.hpp" #include "ast_fwd_decl.hpp"@@ -36,16 +41,28 @@      class InvalidSass : public Base {       public:-        InvalidSass(ParserState pstate, Backtraces traces, std::string msg);-        virtual ~InvalidSass() throw() {};+        InvalidSass(InvalidSass& other) : Base(other), owned_src(other.owned_src) {+          // Assumes that `this` will outlive `other`.+          other.owned_src = nullptr;+        }++        // Required because the copy constructor's argument is not const.+        // Can't use `std::move` here because we build on Visual Studio 2013.+        InvalidSass(InvalidSass &&other) : Base(other), owned_src(other.owned_src) {+          other.owned_src = nullptr;+        }++        InvalidSass(ParserState pstate, Backtraces traces, std::string msg, char* owned_src = nullptr);+        virtual ~InvalidSass() throw() { sass_free_memory(owned_src); };+        char *owned_src;     };      class InvalidParent : public Base {       protected:-        Selector_Ptr parent;-        Selector_Ptr selector;+        Selector* parent;+        Selector* selector;       public:-        InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector);+        InvalidParent(Selector* parent, Backtraces traces, Selector* selector);         virtual ~InvalidParent() throw() {};     }; @@ -64,18 +81,18 @@         std::string fn;         std::string arg;         std::string type;-        const Value_Ptr value;+        const Value* value;       public:-        InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0);+        InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value* value = 0);         virtual ~InvalidArgumentType() throw() {};     };      class InvalidVarKwdType : public Base {       protected:         std::string name;-        const Argument_Ptr arg;+        const Argument* arg;       public:-        InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg = 0);+        InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument* arg = 0);         virtual ~InvalidVarKwdType() throw() {};     }; @@ -165,36 +182,54 @@      class UndefinedOperation : public OperationError {       protected:-        Expression_Ptr_Const lhs;-        Expression_Ptr_Const rhs;+        const Expression* lhs;+        const Expression* rhs;         const Sass_OP op;       public:-        UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);+        UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op);         // virtual const char* errtype() const { return "Error"; }         virtual ~UndefinedOperation() throw() {};     };      class InvalidNullOperation : public UndefinedOperation {       public:-        InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);+        InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op);         virtual ~InvalidNullOperation() throw() {};     };      class AlphaChannelsNotEqual : public OperationError {       protected:-        Expression_Ptr_Const lhs;-        Expression_Ptr_Const rhs;+        const Expression* lhs;+        const Expression* rhs;         const Sass_OP op;       public:-        AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);+        AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op);         // virtual const char* errtype() const { return "Error"; }         virtual ~AlphaChannelsNotEqual() throw() {};     };      class SassValueError : public Base {-      public:-        SassValueError(Backtraces traces, ParserState pstate, OperationError& err);-        virtual ~SassValueError() throw() {};+    public:+      SassValueError(Backtraces traces, ParserState pstate, OperationError& err);+      virtual ~SassValueError() throw() {};+    };++    class TopLevelParent : public Base {+    public:+      TopLevelParent(Backtraces traces, ParserState pstate);+      virtual ~TopLevelParent() throw() {};+    };++    class UnsatisfiedExtend : public Base {+    public:+      UnsatisfiedExtend(Backtraces traces, Extension extension);+      virtual ~UnsatisfiedExtend() throw() {};+    };++    class ExtendAcrossMedia : public Base {+    public:+      ExtendAcrossMedia(Backtraces traces, Extension extension);+      virtual ~ExtendAcrossMedia() throw() {};     };    }
libsass/src/eval.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstdlib> #include <cmath> #include <iostream>@@ -17,7 +20,8 @@ #include "position.hpp" #include "sass/values.h" #include "to_value.hpp"-#include "to_c.hpp"+#include "ast2c.hpp"+#include "c2ast.hpp" #include "context.hpp" #include "backtrace.hpp" #include "lexer.hpp"@@ -26,6 +30,7 @@ #include "expand.hpp" #include "color_maps.hpp" #include "sass_functions.hpp"+#include "util_string.hpp"  namespace Sass { @@ -47,14 +52,34 @@     return exp.environment();   } -  Selector_List_Obj Eval::selector()+  const std::string Eval::cwd()   {-    return exp.selector();+    return ctx.cwd();   } -  Expression_Ptr Eval::operator()(Block_Ptr b)+  struct Sass_Inspect_Options& Eval::options()   {-    Expression_Ptr val = 0;+    return ctx.c_options;+  }++  struct Sass_Compiler* Eval::compiler()+  {+    return ctx.c_compiler;+  }++  EnvStack& Eval::env_stack()+  {+    return exp.env_stack;+  }++  std::vector<Sass_Callee>& Eval::callee_stack()+  {+    return ctx.callee_stack;+  }++  Expression* Eval::operator()(Block* b)+  {+    Expression* val = 0;     for (size_t i = 0, L = b->length(); i < L; ++i) {       val = b->at(i)->perform(this);       if (val) return val;@@ -62,14 +87,14 @@     return val;   } -  Expression_Ptr Eval::operator()(Assignment_Ptr a)+  Expression* Eval::operator()(Assignment* a)   {-    Env* env = exp.environment();+    Env* env = environment();     std::string var(a->variable());     if (a->is_global()) {       if (a->is_default()) {         if (env->has_global(var)) {-          Expression_Ptr e = Cast<Expression>(env->get_global(var));+          Expression* e = Cast<Expression>(env->get_global(var));           if (!e || e->concrete_type() == Expression::NULL_VAL) {             env->set_global(var, a->value()->perform(this));           }@@ -88,7 +113,7 @@         while (cur && cur->is_lexical()) {           if (cur->has_local(var)) {             if (AST_Node_Obj node = cur->get_local(var)) {-              Expression_Ptr e = Cast<Expression>(node);+              Expression* e = Cast<Expression>(node);               if (!e || e->concrete_type() == Expression::NULL_VAL) {                 cur->set_local(var, a->value()->perform(this));               }@@ -104,7 +129,7 @@       }       else if (env->has_global(var)) {         if (AST_Node_Obj node = env->get_global(var)) {-          Expression_Ptr e = Cast<Expression>(node);+          Expression* e = Cast<Expression>(node);           if (!e || e->concrete_type() == Expression::NULL_VAL) {             env->set_global(var, a->value()->perform(this));           }@@ -123,11 +148,11 @@     return 0;   } -  Expression_Ptr Eval::operator()(If_Ptr i)+  Expression* Eval::operator()(If* i)   {-    Expression_Obj rv = 0;-    Env env(exp.environment());-    exp.env_stack.push_back(&env);+    Expression_Obj rv;+    Env env(environment());+    env_stack().push_back(&env);     Expression_Obj cond = i->predicate()->perform(this);     if (!cond->is_false()) {       rv = i->block()->perform(this);@@ -136,13 +161,13 @@       Block_Obj alt = i->alternative();       if (alt) rv = alt->perform(this);     }-    exp.env_stack.pop_back();+    env_stack().pop_back();     return rv.detach();   }    // For does not create a new env scope   // But iteration vars are reset afterwards-  Expression_Ptr Eval::operator()(For_Ptr f)+  Expression* Eval::operator()(For* f)   {     std::string variable(f->variable());     Expression_Obj low = f->lower_bound()->perform(this);@@ -168,9 +193,9 @@     double end = sass_end->value();     // only create iterator once in this environment     Env env(environment(), true);-    exp.env_stack.push_back(&env);+    env_stack().push_back(&env);     Block_Obj body = f->block();-    Expression_Ptr val = 0;+    Expression* val = 0;     if (start < end) {       if (f->is_inclusive()) ++end;       for (double i = start;@@ -192,26 +217,25 @@         if (val) break;       }     }-    exp.env_stack.pop_back();+    env_stack().pop_back();     return val;   }    // Eval does not create a new env scope   // But iteration vars are reset afterwards-  Expression_Ptr Eval::operator()(Each_Ptr e)+  Expression* Eval::operator()(Each* e)   {     std::vector<std::string> variables(e->variables());     Expression_Obj expr = e->list()->perform(this);     Env env(environment(), true);-    exp.env_stack.push_back(&env);-    List_Obj list = 0;-    Map_Ptr map = 0;+    env_stack().push_back(&env);+    List_Obj list;+    Map* map = nullptr;     if (expr->concrete_type() == Expression::MAP) {       map = Cast<Map>(expr);     }-    else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {-      Listize listize;-      Expression_Obj rv = ls->perform(&listize);+    else if (SelectorList * ls = Cast<SelectorList>(expr)) {+      Expression_Obj rv = Listize::perform(ls);       list = Cast<List>(rv);     }     else if (expr->concrete_type() != Expression::LIST) {@@ -223,14 +247,14 @@     }      Block_Obj body = e->block();-    Expression_Obj val = 0;+    Expression_Obj val;      if (map) {       for (Expression_Obj key : map->keys()) {         Expression_Obj value = map->at(key);          if (variables.size() == 1) {-          List_Ptr variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);+          List* variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);           variable->append(key);           variable->append(value);           env.set_local(variables[0], variable);@@ -244,22 +268,22 @@       }     }     else {-      if (list->length() == 1 && Cast<Selector_List>(list)) {+      if (list->length() == 1 && Cast<SelectorList>(list)) {         list = Cast<List>(list);       }       for (size_t i = 0, L = list->length(); i < L; ++i) {-        Expression_Ptr item = list->at(i);+        Expression* item = list->at(i);         // unwrap value if the expression is an argument-        if (Argument_Ptr arg = Cast<Argument>(item)) item = arg->value();+        if (Argument* arg = Cast<Argument>(item)) item = arg->value();         // check if we got passed a list of args (investigate)-        if (List_Ptr scalars = Cast<List>(item)) {+        if (List* scalars = Cast<List>(item)) {           if (variables.size() == 1) {-            Expression_Ptr var = scalars;+            Expression* var = scalars;             env.set_local(variables[0], var);           } else {             // XXX: this is never hit via spec tests             for (size_t j = 0, K = variables.size(); j < K; ++j) {-              Expression_Ptr res = j >= scalars->length()+              Expression* res = j >= scalars->length()                 ? SASS_MEMORY_NEW(Null, expr->pstate())                 : scalars->at(j);               env.set_local(variables[j], res);@@ -270,7 +294,7 @@             env.set_local(variables.at(0), item);             for (size_t j = 1, K = variables.size(); j < K; ++j) {               // XXX: this is never hit via spec tests-              Expression_Ptr res = SASS_MEMORY_NEW(Null, expr->pstate());+              Expression* res = SASS_MEMORY_NEW(Null, expr->pstate());               env.set_local(variables[j], res);             }           }@@ -279,46 +303,46 @@         if (val) break;       }     }-    exp.env_stack.pop_back();+    env_stack().pop_back();     return val.detach();   } -  Expression_Ptr Eval::operator()(While_Ptr w)+  Expression* Eval::operator()(While* w)   {     Expression_Obj pred = w->predicate();     Block_Obj body = w->block();     Env env(environment(), true);-    exp.env_stack.push_back(&env);+    env_stack().push_back(&env);     Expression_Obj cond = pred->perform(this);     while (!cond->is_false()) {       Expression_Obj val = body->perform(this);       if (val) {-        exp.env_stack.pop_back();+        env_stack().pop_back();         return val.detach();       }       cond = pred->perform(this);     }-    exp.env_stack.pop_back();+    env_stack().pop_back();     return 0;   } -  Expression_Ptr Eval::operator()(Return_Ptr r)+  Expression* Eval::operator()(Return* r)   {     return r->value()->perform(this);   } -  Expression_Ptr Eval::operator()(Warning_Ptr w)+  Expression* Eval::operator()(Warning* w)   {-    Sass_Output_Style outstyle = ctx.c_options.output_style;-    ctx.c_options.output_style = NESTED;+    Sass_Output_Style outstyle = options().output_style;+    options().output_style = NESTED;     Expression_Obj message = w->message()->perform(this);-    Env* env = exp.environment();+    Env* env = environment();      // try to use generic function     if (env->has("@warn[f]")) {        // add call stack entry-      ctx.callee_stack.push_back({+      callee_stack().push_back({         "@warn",         w->pstate().path,         w->pstate().line + 1,@@ -327,18 +351,18 @@         { env }       }); -      Definition_Ptr def = Cast<Definition>((*env)["@warn[f]"]);+      Definition* def = Cast<Definition>((*env)["@warn[f]"]);       // Block_Obj          body   = def->block();       // Native_Function func   = def->native_function();       Sass_Function_Entry c_function = def->c_function();       Sass_Function_Fn c_func = sass_function_get_function(c_function); -      To_C to_c;+      AST2C ast2c;       union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);-      sass_list_set_value(c_args, 0, message->perform(&to_c));-      union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);-      ctx.c_options.output_style = outstyle;-      ctx.callee_stack.pop_back();+      sass_list_set_value(c_args, 0, message->perform(&ast2c));+      union Sass_Value* c_val = c_func(c_args, c_function, compiler());+      options().output_style = outstyle;+      callee_stack().pop_back();       sass_delete_value(c_args);       sass_delete_value(c_val);       return 0;@@ -350,23 +374,23 @@     traces.push_back(Backtrace(w->pstate()));     std::cerr << traces_to_string(traces, "         ");     std::cerr << std::endl;-    ctx.c_options.output_style = outstyle;+    options().output_style = outstyle;     traces.pop_back();     return 0;   } -  Expression_Ptr Eval::operator()(Error_Ptr e)+  Expression* Eval::operator()(Error* e)   {-    Sass_Output_Style outstyle = ctx.c_options.output_style;-    ctx.c_options.output_style = NESTED;+    Sass_Output_Style outstyle = options().output_style;+    options().output_style = NESTED;     Expression_Obj message = e->message()->perform(this);-    Env* env = exp.environment();+    Env* env = environment();      // try to use generic function     if (env->has("@error[f]")) {        // add call stack entry-      ctx.callee_stack.push_back({+      callee_stack().push_back({         "@error",         e->pstate().path,         e->pstate().line + 1,@@ -375,18 +399,18 @@         { env }       }); -      Definition_Ptr def = Cast<Definition>((*env)["@error[f]"]);+      Definition* def = Cast<Definition>((*env)["@error[f]"]);       // Block_Obj          body   = def->block();       // Native_Function func   = def->native_function();       Sass_Function_Entry c_function = def->c_function();       Sass_Function_Fn c_func = sass_function_get_function(c_function); -      To_C to_c;+      AST2C ast2c;       union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);-      sass_list_set_value(c_args, 0, message->perform(&to_c));-      union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);-      ctx.c_options.output_style = outstyle;-      ctx.callee_stack.pop_back();+      sass_list_set_value(c_args, 0, message->perform(&ast2c));+      union Sass_Value* c_val = c_func(c_args, c_function, compiler());+      options().output_style = outstyle;+      callee_stack().pop_back();       sass_delete_value(c_args);       sass_delete_value(c_val);       return 0;@@ -394,23 +418,23 @@     }      std::string result(unquote(message->to_sass()));-    ctx.c_options.output_style = outstyle;+    options().output_style = outstyle;     error(result, e->pstate(), traces);     return 0;   } -  Expression_Ptr Eval::operator()(Debug_Ptr d)+  Expression* Eval::operator()(Debug* d)   {-    Sass_Output_Style outstyle = ctx.c_options.output_style;-    ctx.c_options.output_style = NESTED;+    Sass_Output_Style outstyle = options().output_style;+    options().output_style = NESTED;     Expression_Obj message = d->value()->perform(this);-    Env* env = exp.environment();+    Env* env = environment();      // try to use generic function     if (env->has("@debug[f]")) {        // add call stack entry-      ctx.callee_stack.push_back({+      callee_stack().push_back({         "@debug",         d->pstate().path,         d->pstate().line + 1,@@ -419,37 +443,37 @@         { env }       }); -      Definition_Ptr def = Cast<Definition>((*env)["@debug[f]"]);+      Definition* def = Cast<Definition>((*env)["@debug[f]"]);       // Block_Obj          body   = def->block();       // Native_Function func   = def->native_function();       Sass_Function_Entry c_function = def->c_function();       Sass_Function_Fn c_func = sass_function_get_function(c_function); -      To_C to_c;+      AST2C ast2c;       union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);-      sass_list_set_value(c_args, 0, message->perform(&to_c));-      union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);-      ctx.c_options.output_style = outstyle;-      ctx.callee_stack.pop_back();+      sass_list_set_value(c_args, 0, message->perform(&ast2c));+      union Sass_Value* c_val = c_func(c_args, c_function, compiler());+      options().output_style = outstyle;+      callee_stack().pop_back();       sass_delete_value(c_args);       sass_delete_value(c_val);       return 0;      } -    std::string cwd(ctx.cwd());     std::string result(unquote(message->to_sass()));-    std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));-    std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));+    std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd(), cwd()));+    std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd(), cwd()));     std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));-    ctx.c_options.output_style = outstyle;+    options().output_style = outstyle;      std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;     std::cerr << std::endl;     return 0;   } -  Expression_Ptr Eval::operator()(List_Ptr l)++  Expression* Eval::operator()(List* l)   {     // special case for unevaluated map     if (l->separator() == SASS_HASH) {@@ -490,7 +514,7 @@     return ll.detach();   } -  Expression_Ptr Eval::operator()(Map_Ptr m)+  Expression* Eval::operator()(Map* m)   {     if (m->is_expanded()) return m; @@ -505,8 +529,8 @@                                 m->pstate(),                                 m->length());     for (auto key : m->keys()) {-      Expression_Ptr ex_key = key->perform(this);-      Expression_Ptr ex_val = m->at(key);+      Expression* ex_key = key->perform(this);+      Expression* ex_val = m->at(key);       if (ex_val == NULL) continue;       ex_val = ex_val->perform(this);       *mm << std::make_pair(ex_key, ex_val);@@ -522,7 +546,7 @@     return mm.detach();   } -  Expression_Ptr Eval::operator()(Binary_Expression_Ptr b_in)+  Expression* Eval::operator()(Binary_Expression* b_in)   {      Expression_Obj lhs = b_in->left();@@ -543,21 +567,21 @@     }      // Evaluate variables as early o-    while (Variable_Ptr l_v = Cast<Variable>(lhs)) {+    while (Variable* l_v = Cast<Variable>(lhs)) {       lhs = operator()(l_v);     }-    while (Variable_Ptr r_v = Cast<Variable>(rhs)) {+    while (Variable* r_v = Cast<Variable>(rhs)) {       rhs = operator()(r_v);     }      Binary_Expression_Obj b = b_in;      // Evaluate sub-expressions early on-    while (Binary_Expression_Ptr l_b = Cast<Binary_Expression>(lhs)) {+    while (Binary_Expression* l_b = Cast<Binary_Expression>(lhs)) {       if (!force && l_b->is_delayed()) break;       lhs = operator()(l_b);     }-    while (Binary_Expression_Ptr r_b = Cast<Binary_Expression>(rhs)) {+    while (Binary_Expression* r_b = Cast<Binary_Expression>(rhs)) {       if (!force && r_b->is_delayed()) break;       rhs = operator()(r_b);     }@@ -571,9 +595,9 @@      // specific types we know are final     // handle them early to avoid overhead-    if (Number_Ptr l_n = Cast<Number>(lhs)) {+    if (Number* l_n = Cast<Number>(lhs)) {       // lhs is number and rhs is number-      if (Number_Ptr r_n = Cast<Number>(rhs)) {+      if (Number* r_n = Cast<Number>(rhs)) {         try {           switch (op_type) {             case Sass_OP::EQ: return *l_n == *r_n ? bool_true : bool_false;@@ -583,7 +607,7 @@             case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false;             case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true;             case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:-              return Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, b_in->pstate());+              return Operators::op_numbers(op_type, *l_n, *r_n, options(), b_in->pstate());             default: break;           }         }@@ -594,13 +618,15 @@         }       }       // lhs is number and rhs is color-      else if (Color_Ptr r_c = Cast<Color>(rhs)) {+      // Todo: allow to work with HSLA colors+      else if (Color* r_col = Cast<Color>(rhs)) {+        Color_RGBA_Obj r_c = r_col->toRGBA();         try {           switch (op_type) {             case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false;             case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true;             case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:-              return Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate());+              return Operators::op_number_color(op_type, *l_n, *r_c, options(), b_in->pstate());             default: break;           }         }@@ -611,9 +637,11 @@         }       }     }-    else if (Color_Ptr l_c = Cast<Color>(lhs)) {+    else if (Color* l_col = Cast<Color>(lhs)) {+      Color_RGBA_Obj l_c = l_col->toRGBA();       // lhs is color and rhs is color-      if (Color_Ptr r_c = Cast<Color>(rhs)) {+      if (Color* r_col = Cast<Color>(rhs)) {+        Color_RGBA_Obj r_c = r_col->toRGBA();         try {           switch (op_type) {             case Sass_OP::EQ: return *l_c == *r_c ? bool_true : bool_false;@@ -623,7 +651,7 @@             case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false;             case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true;             case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:-              return Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, b_in->pstate());+              return Operators::op_colors(op_type, *l_c, *r_c, options(), b_in->pstate());             default: break;           }         }@@ -634,13 +662,13 @@         }       }       // lhs is color and rhs is number-      else if (Number_Ptr r_n = Cast<Number>(rhs)) {+      else if (Number* r_n = Cast<Number>(rhs)) {         try {           switch (op_type) {             case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false;             case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true;             case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:-              return Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate());+              return Operators::op_color_number(op_type, *l_c, *r_n, options(), b_in->pstate());             default: break;           }         }@@ -655,7 +683,7 @@     String_Schema_Obj ret_schema;      // only the last item will be used to eval the binary expression-    if (String_Schema_Ptr s_l = Cast<String_Schema>(b->left())) {+    if (String_Schema* s_l = Cast<String_Schema>(b->left())) {       if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) {         ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());         Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),@@ -668,7 +696,7 @@         return ret_schema->perform(this);       }     }-    if (String_Schema_Ptr s_r = Cast<String_Schema>(b->right())) {+    if (String_Schema* s_r = Cast<String_Schema>(b->right())) {        if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) {         ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());@@ -708,8 +736,8 @@     AST_Node_Obj lu = lhs;     AST_Node_Obj ru = rhs; -    Expression::Concrete_Type l_type;-    Expression::Concrete_Type r_type;+    Expression::Type l_type;+    Expression::Type r_type;      // Is one of the operands an interpolant?     String_Schema_Obj s1 = Cast<String_Schema>(b->left());@@ -729,7 +757,7 @@       if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB ||           op_type == Sass_OP::EQ) {         // If possible upgrade LHS to a number (for number to string compare)-        if (String_Constant_Ptr str = Cast<String_Constant>(lhs)) {+        if (String_Constant* str = Cast<String_Constant>(lhs)) {           std::string value(str->value());           const char* start = value.c_str();           if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) {@@ -737,7 +765,7 @@           }         }         // If possible upgrade RHS to a number (for string to number compare)-        if (String_Constant_Ptr str = Cast<String_Constant>(rhs)) {+        if (String_Constant* str = Cast<String_Constant>(rhs)) {           std::string value(str->value());           const char* start = value.c_str();           if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) {@@ -752,12 +780,12 @@        if (force_delay) {         std::string str("");-        str += v_l->to_string(ctx.c_options);+        str += v_l->to_string(options());         if (b->op().ws_before) str += " ";         str += b->separator();         if (b->op().ws_after) str += " ";-        str += v_r->to_string(ctx.c_options);-        String_Constant_Ptr val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str);+        str += v_r->to_string(options());+        String_Constant* val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str);         val->is_interpolant(b->left()->has_interpolant());         return val;       }@@ -777,7 +805,6 @@     }     catch (Exception::OperationError& err)     {-      // throw Exception::Base(b->pstate(), err.what());       traces.push_back(Backtrace(b->pstate()));       throw Exception::SassValueError(traces, b->pstate(), err);     }@@ -791,25 +818,25 @@     try {       ParserState pstate(b->pstate());       if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {-        Number_Ptr l_n = Cast<Number>(lhs);-        Number_Ptr r_n = Cast<Number>(rhs);+        Number* l_n = Cast<Number>(lhs);+        Number* r_n = Cast<Number>(rhs);         l_n->reduce(); r_n->reduce();-        rv = Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, pstate);+        rv = Operators::op_numbers(op_type, *l_n, *r_n, options(), pstate);       }       else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {-        Number_Ptr l_n = Cast<Number>(lhs);-        Color_Ptr r_c = Cast<Color>(rhs);-        rv = Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, pstate);+        Number* l_n = Cast<Number>(lhs);+        Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA();+        rv = Operators::op_number_color(op_type, *l_n, *r_c, options(), pstate);       }       else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {-        Color_Ptr l_c = Cast<Color>(lhs);-        Number_Ptr r_n = Cast<Number>(rhs);-        rv = Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, pstate);+        Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+        Number* r_n = Cast<Number>(rhs);+        rv = Operators::op_color_number(op_type, *l_c, *r_n, options(), pstate);       }       else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {-        Color_Ptr l_c = Cast<Color>(lhs);-        Color_Ptr r_c = Cast<Color>(rhs);-        rv = Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, pstate);+        Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+        Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA();+        rv = Operators::op_colors(op_type, *l_c, *r_c, options(), pstate);       }       else {         To_Value to_value(ctx);@@ -830,15 +857,15 @@           traces.push_back(Backtrace(v_r->pstate()));           throw Exception::InvalidValue(traces, *v_r);         }-        Value_Ptr ex = Operators::op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress-        if (String_Constant_Ptr str = Cast<String_Constant>(ex))+        Value* ex = Operators::op_strings(b->op(), *v_l, *v_r, options(), pstate, !interpolant); // pass true to compress+        if (String_Constant* str = Cast<String_Constant>(ex))         {           if (str->concrete_type() == Expression::STRING)           {-            String_Constant_Ptr lstr = Cast<String_Constant>(lhs);-            String_Constant_Ptr rstr = Cast<String_Constant>(rhs);+            String_Constant* lstr = Cast<String_Constant>(lhs);+            String_Constant* rstr = Cast<String_Constant>(rhs);             if (op_type != Sass_OP::SUB) {-              if (String_Constant_Ptr org = lstr ? lstr : rstr)+              if (String_Constant* org = lstr ? lstr : rstr)               { str->quote_mark(org->quote_mark()); }             }           }@@ -866,11 +893,11 @@    } -  Expression_Ptr Eval::operator()(Unary_Expression_Ptr u)+  Expression* Eval::operator()(Unary_Expression* u)   {     Expression_Obj operand = u->operand()->perform(this);     if (u->optype() == Unary_Expression::NOT) {-      Boolean_Ptr result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);+      Boolean* result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);       result->value(!result->value());       return result;     }@@ -882,28 +909,35 @@         return cpy.detach(); // return the copy       }       else if (u->optype() == Unary_Expression::SLASH) {-        std::string str = '/' + nr->to_string(ctx.c_options);+        std::string str = '/' + nr->to_string(options());         return SASS_MEMORY_NEW(String_Constant, u->pstate(), str);       }       // nothing for positive       return nr.detach();     }     else {-      // Special cases: +/- variables which evaluate to null ouput just +/-,+      // Special cases: +/- variables which evaluate to null output just +/-,       // but +/- null itself outputs the string       if (operand->concrete_type() == Expression::NULL_VAL && Cast<Variable>(u->operand())) {         u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), ""));       }       // Never apply unary opertions on colors @see #2140-      else if (Color_Ptr color = Cast<Color>(operand)) {+      else if (Color* color = Cast<Color>(operand)) {         // Use the color name if this was eval with one         if (color->disp().length() > 0) {-          operand = SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp());-          u->operand(operand);+          Unary_Expression_Obj cpy = SASS_MEMORY_COPY(u);+          cpy->operand(SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp()));+          return SASS_MEMORY_NEW(String_Quoted,+                                 cpy->pstate(),+                                 cpy->inspect());         }       }       else {-        u->operand(operand);+        Unary_Expression_Obj cpy = SASS_MEMORY_COPY(u);+        cpy->operand(operand);+        return SASS_MEMORY_NEW(String_Quoted,+                               cpy->pstate(),+                               cpy->inspect());       }        return SASS_MEMORY_NEW(String_Quoted,@@ -914,7 +948,7 @@     return u;   } -  Expression_Ptr Eval::operator()(Function_Call_Ptr c)+  Expression* Eval::operator()(Function_Call* c)   {     if (traces.size() > Constants::MaxCallStack) {         // XXX: this is never hit via spec tests@@ -922,8 +956,18 @@         stm << "Stack depth exceeded max of " << Constants::MaxCallStack;         error(stm.str(), c->pstate(), traces);     }++    if (Cast<String_Schema>(c->sname())) {+      Expression_Obj evaluated_name = c->sname()->perform(this);+      Expression_Obj evaluated_args = c->arguments()->perform(this);+      std::string str(evaluated_name->to_string());+      str += evaluated_args->to_string();+      return SASS_MEMORY_NEW(String_Constant, c->pstate(), str);+    }+     std::string name(Util::normalize_underscores(c->name()));     std::string full_name(name + "[f]");+     // we make a clone here, need to implement that further     Arguments_Obj args = c->arguments(); @@ -941,11 +985,11 @@                                              c->name(),                                              args);         if (args->has_named_arguments()) {-          error("Function " + c->name() + " doesn't support keyword arguments", c->pstate(), traces);+          error("Plain CSS function " + c->name() + " doesn't support keyword arguments", c->pstate(), traces);         }-        String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted,+        String_Quoted* str = SASS_MEMORY_NEW(String_Quoted,                                              c->pstate(),-                                             lit->to_string(ctx.c_options));+                                             lit->to_string(options()));         str->is_interpolant(c->is_interpolant());         return str;       } else {@@ -961,7 +1005,7 @@     if (full_name != "if[f]") {       args = Cast<Arguments>(args->perform(this));     }-    Definition_Ptr def = Cast<Definition>((*env)[full_name]);+    Definition* def = Cast<Definition>((*env)[full_name]);      if (c->func()) def = c->func()->definition(); @@ -971,7 +1015,7 @@       // account for rest arguments       if (args->has_rest_argument() && args->length() > 0) {         // get the rest arguments list-        List_Ptr rest = Cast<List>(args->last()->value());+        List* rest = Cast<List>(args->last()->value());         // arguments before rest argument plus rest         if (rest) L += rest->length() - 1;       }@@ -991,13 +1035,13 @@      Parameters_Obj params = def->parameters();     Env fn_env(def->environment());-    exp.env_stack.push_back(&fn_env);+    env_stack().push_back(&fn_env);      if (func || body) {-      bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+      bind(std::string("Function"), c->name(), params, args, &fn_env, this, traces);       std::string msg(", in function `" + c->name() + "`");       traces.push_back(Backtrace(c->pstate(), msg));-      ctx.callee_stack.push_back({+      callee_stack().push_back({         c->name().c_str(),         c->pstate().path,         c->pstate().line + 1,@@ -1011,12 +1055,12 @@         result = body->perform(this);       }       else if (func) {-        result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.selector_stack);+        result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.getSelectorStack(), exp.originalStack);       }       if (!result) {         error(std::string("Function ") + c->name() + " finished without @return", c->pstate(), traces);       }-      ctx.callee_stack.pop_back();+      callee_stack().pop_back();       traces.pop_back();     } @@ -1034,10 +1078,10 @@        // populates env with default values for params       std::string ff(c->name());-      bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+      bind(std::string("Function"), c->name(), params, args, &fn_env, this, traces);       std::string msg(", in function `" + c->name() + "`");       traces.push_back(Backtrace(c->pstate(), msg));-      ctx.callee_stack.push_back({+      callee_stack().push_back({         c->name().c_str(),         c->pstate().path,         c->pstate().line + 1,@@ -1046,24 +1090,30 @@         { env }       }); -      To_C to_c;+      AST2C ast2c;       union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA, false);       for(size_t i = 0; i < params->length(); i++) {         Parameter_Obj param = params->at(i);         std::string key = param->name();         AST_Node_Obj node = fn_env.get_local(key);         Expression_Obj arg = Cast<Expression>(node);-        sass_list_set_value(c_args, i, arg->perform(&to_c));+        sass_list_set_value(c_args, i, arg->perform(&ast2c));       }-      union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+      union Sass_Value* c_val = c_func(c_args, c_function, compiler());       if (sass_value_get_tag(c_val) == SASS_ERROR) {-        error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), traces);+        std::string message("error in C function " + c->name() + ": " + sass_error_get_message(c_val));+        sass_delete_value(c_val);+        sass_delete_value(c_args);+        error(message, c->pstate(), traces);       } else if (sass_value_get_tag(c_val) == SASS_WARNING) {-        error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), traces);+        std::string message("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val));+        sass_delete_value(c_val);+        sass_delete_value(c_args);+        error(message, c->pstate(), traces);       }-      result = cval_to_astnode(c_val, traces, c->pstate());+      result = c2ast(c_val, traces, c->pstate()); -      ctx.callee_stack.pop_back();+      callee_stack().pop_back();       traces.pop_back();       sass_delete_value(c_args);       if (c_val != c_args)@@ -1077,30 +1127,20 @@      result = result->perform(this);     result->is_interpolant(c->is_interpolant());-    exp.env_stack.pop_back();+    env_stack().pop_back();     return result.detach();   } -  Expression_Ptr Eval::operator()(Function_Call_Schema_Ptr s)-  {-    Expression_Ptr evaluated_name = s->name()->perform(this);-    Expression_Ptr evaluated_args = s->arguments()->perform(this);-    String_Schema_Obj ss = SASS_MEMORY_NEW(String_Schema, s->pstate(), 2);-    ss->append(evaluated_name);-    ss->append(evaluated_args);-    return ss->perform(this);-  }--  Expression_Ptr Eval::operator()(Variable_Ptr v)+  Expression* Eval::operator()(Variable* v)   {-    Expression_Obj value = 0;+    Expression_Obj value;     Env* env = environment();     const std::string& name(v->name());     EnvResult rv(env->find(name));     if (rv.found) value = static_cast<Expression*>(rv.it->second.ptr());     else error("Undefined variable: \"" + v->name() + "\".", v->pstate(), traces);-    if (Argument_Ptr arg = Cast<Argument>(value)) value = arg->value();-    if (Number_Ptr nr = Cast<Number>(value)) nr->zero(true); // force flag+    if (Argument* arg = Cast<Argument>(value)) value = arg->value();+    if (Number* nr = Cast<Number>(value)) nr->zero(true); // force flag     value->is_interpolant(v->is_interpolant());     if (force) value->is_expanded(false);     value->set_delayed(false); // verified@@ -1109,17 +1149,22 @@     return value.detach();   } -  Expression_Ptr Eval::operator()(Color_Ptr c)+  Expression* Eval::operator()(Color_RGBA* c)   {     return c;   } -  Expression_Ptr Eval::operator()(Number_Ptr n)+  Expression* Eval::operator()(Color_HSLA* c)   {+    return c;+  }++  Expression* Eval::operator()(Number* n)+  {     return n;   } -  Expression_Ptr Eval::operator()(Boolean_Ptr b)+  Expression* Eval::operator()(Boolean* b)   {     return b;   }@@ -1128,8 +1173,8 @@      bool needs_closing_brace = false; -    if (Arguments_Ptr args = Cast<Arguments>(ex)) {-      List_Ptr ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA);+    if (Arguments* args = Cast<Arguments>(ex)) {+      List* ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA);       for(auto arg : args->elements()) {         ll->append(arg->value());       }@@ -1138,7 +1183,7 @@       res += "(";       ex = ll;     }-    if (Number_Ptr nr = Cast<Number>(ex)) {+    if (Number* nr = Cast<Number>(ex)) {       Number reduced(nr);       reduced.reduce();       if (!reduced.is_valid_css_unit()) {@@ -1146,10 +1191,10 @@         throw Exception::InvalidValue(traces, *nr);       }     }-    if (Argument_Ptr arg = Cast<Argument>(ex)) {+    if (Argument* arg = Cast<Argument>(ex)) {       ex = arg->value();     }-    if (String_Quoted_Ptr sq = Cast<String_Quoted>(ex)) {+    if (String_Quoted* sq = Cast<String_Quoted>(ex)) {       if (was_itpl) {         bool was_interpolant = ex->is_interpolant();         ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value());@@ -1160,12 +1205,12 @@     if (Cast<Null>(ex)) { return; }      // parent selector needs another go-    if (Cast<Parent_Selector>(ex)) {+    if (Cast<Parent_Reference>(ex)) {       // XXX: this is never hit via spec tests       ex = ex->perform(this);     } -    if (List_Ptr l = Cast<List>(ex)) {+    if (List* l = Cast<List>(ex)) {       List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator());       // this fixes an issue with bourbon sample, not really sure why       // if (l->size() && Cast<Null>((*l)[0])) { res += ""; }@@ -1179,12 +1224,12 @@       // here. Normally single list items are already unwrapped.       if (l->size() > 1) {         // string_to_output would fail "#{'_\a' '_\a'}";-        std::string str(ll->to_string(ctx.c_options));+        std::string str(ll->to_string(options()));         str = read_hex_escapes(str); // read escapes         newline_to_space(str); // replace directly         res += str; // append to result string       } else {-        res += (ll->to_string(ctx.c_options));+        res += (ll->to_string(options()));       }       ll->is_interpolant(l->is_interpolant());     }@@ -1194,14 +1239,13 @@     // Selector_List     // String_Quoted     // String_Constant-    // Parent_Selector     // Binary_Expression     else {       // ex = ex->perform(this);       if (into_quotes && ex->is_interpolant()) {-        res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : "");+        res += evacuate_escapes(ex ? ex->to_string(options()) : "");       } else {-        std::string str(ex ? ex->to_string(ctx.c_options) : "");+        std::string str(ex ? ex->to_string(options()) : "");         if (into_quotes) str = read_hex_escapes(str);         res += str; // append to result string       }@@ -1211,14 +1255,14 @@    } -  Expression_Ptr Eval::operator()(String_Schema_Ptr s)+  Expression* Eval::operator()(String_Schema* s)   {     size_t L = s->length();     bool into_quotes = false;     if (L > 1) {       if (!Cast<String_Quoted>((*s)[0]) && !Cast<String_Quoted>((*s)[L - 1])) {-      if (String_Constant_Ptr l = Cast<String_Constant>((*s)[0])) {-        if (String_Constant_Ptr r = Cast<String_Constant>((*s)[L - 1])) {+      if (String_Constant* l = Cast<String_Constant>((*s)[0])) {+        if (String_Constant* r = Cast<String_Constant>((*s)[L - 1])) {           if (r->value().size() > 0) {             if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true;             if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;@@ -1247,7 +1291,7 @@     // string schema seems to have a special unquoting behavior (also handles "nested" quotes)     String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false, s->css());     // if (s->is_interpolant()) str->quote_mark(0);-    // String_Constant_Ptr str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);+    // String_Constant* str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);     if (str->quote_mark()) str->quote_mark('*');     else if (!is_in_comment) str->value(string_to_output(str->value()));     str->is_interpolant(s->is_interpolant());@@ -1255,25 +1299,25 @@   }  -  Expression_Ptr Eval::operator()(String_Constant_Ptr s)+  Expression* Eval::operator()(String_Constant* s)   {     return s;   } -  Expression_Ptr Eval::operator()(String_Quoted_Ptr s)+  Expression* Eval::operator()(String_Quoted* s)   {-    String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), "");+    String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), "");     str->value(s->value());     str->quote_mark(s->quote_mark());     str->is_interpolant(s->is_interpolant());     return str;   } -  Expression_Ptr Eval::operator()(Supports_Operator_Ptr c)+  Expression* Eval::operator()(Supports_Operator* c)   {-    Expression_Ptr left = c->left()->perform(this);-    Expression_Ptr right = c->right()->perform(this);-    Supports_Operator_Ptr cc = SASS_MEMORY_NEW(Supports_Operator,+    Expression* left = c->left()->perform(this);+    Expression* right = c->right()->perform(this);+    Supports_Operator* cc = SASS_MEMORY_NEW(Supports_Operator,                                  c->pstate(),                                  Cast<Supports_Condition>(left),                                  Cast<Supports_Condition>(right),@@ -1281,52 +1325,52 @@     return cc;   } -  Expression_Ptr Eval::operator()(Supports_Negation_Ptr c)+  Expression* Eval::operator()(Supports_Negation* c)   {-    Expression_Ptr condition = c->condition()->perform(this);-    Supports_Negation_Ptr cc = SASS_MEMORY_NEW(Supports_Negation,+    Expression* condition = c->condition()->perform(this);+    Supports_Negation* cc = SASS_MEMORY_NEW(Supports_Negation,                                  c->pstate(),                                  Cast<Supports_Condition>(condition));     return cc;   } -  Expression_Ptr Eval::operator()(Supports_Declaration_Ptr c)+  Expression* Eval::operator()(Supports_Declaration* c)   {-    Expression_Ptr feature = c->feature()->perform(this);-    Expression_Ptr value = c->value()->perform(this);-    Supports_Declaration_Ptr cc = SASS_MEMORY_NEW(Supports_Declaration,+    Expression* feature = c->feature()->perform(this);+    Expression* value = c->value()->perform(this);+    Supports_Declaration* cc = SASS_MEMORY_NEW(Supports_Declaration,                               c->pstate(),                               feature,                               value);     return cc;   } -  Expression_Ptr Eval::operator()(Supports_Interpolation_Ptr c)+  Expression* Eval::operator()(Supports_Interpolation* c)   {-    Expression_Ptr value = c->value()->perform(this);-    Supports_Interpolation_Ptr cc = SASS_MEMORY_NEW(Supports_Interpolation,+    Expression* value = c->value()->perform(this);+    Supports_Interpolation* cc = SASS_MEMORY_NEW(Supports_Interpolation,                             c->pstate(),                             value);     return cc;   } -  Expression_Ptr Eval::operator()(At_Root_Query_Ptr e)+  Expression* Eval::operator()(At_Root_Query* e)   {     Expression_Obj feature = e->feature();     feature = (feature ? feature->perform(this) : 0);     Expression_Obj value = e->value();     value = (value ? value->perform(this) : 0);-    Expression_Ptr ee = SASS_MEMORY_NEW(At_Root_Query,+    Expression* ee = SASS_MEMORY_NEW(At_Root_Query,                                      e->pstate(),                                      Cast<String>(feature),                                      value);     return ee;   } -  Media_Query_Ptr Eval::operator()(Media_Query_Ptr q)+  Media_Query* Eval::operator()(Media_Query* q)   {     String_Obj t = q->media_type();-    t = static_cast<String_Ptr>(t.isNull() ? 0 : t->perform(this));+    t = static_cast<String*>(t.isNull() ? 0 : t->perform(this));     Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query,                                       q->pstate(),                                       t,@@ -1334,12 +1378,12 @@                                       q->is_negated(),                                       q->is_restricted());     for (size_t i = 0, L = q->length(); i < L; ++i) {-      qq->append(static_cast<Media_Query_Expression_Ptr>((*q)[i]->perform(this)));+      qq->append(static_cast<Media_Query_Expression*>((*q)[i]->perform(this)));     }     return qq.detach();   } -  Expression_Ptr Eval::operator()(Media_Query_Expression_Ptr e)+  Expression* Eval::operator()(Media_Query_Expression* e)   {     Expression_Obj feature = e->feature();     feature = (feature ? feature->perform(this) : 0);@@ -1363,12 +1407,12 @@                            e->is_interpolated());   } -  Expression_Ptr Eval::operator()(Null_Ptr n)+  Expression* Eval::operator()(Null* n)   {     return n;   } -  Expression_Ptr Eval::operator()(Argument_Ptr a)+  Expression* Eval::operator()(Argument* a)   {     Expression_Obj val = a->value()->perform(this);     bool is_rest_argument = a->is_rest_argument();@@ -1397,13 +1441,13 @@                            is_keyword_argument);   } -  Expression_Ptr Eval::operator()(Arguments_Ptr a)+  Expression* Eval::operator()(Arguments* a)   {     Arguments_Obj aa = SASS_MEMORY_NEW(Arguments, a->pstate());     if (a->length() == 0) return aa.detach();     for (size_t i = 0, L = a->length(); i < L; ++i) {       Expression_Obj rv = (*a)[i]->perform(this);-      Argument_Ptr arg = Cast<Argument>(rv);+      Argument* arg = Cast<Argument>(rv);       if (!(arg->is_rest_argument() || arg->is_keyword_argument())) {         aa->append(arg);       }@@ -1414,8 +1458,8 @@       Expression_Obj splat = Cast<Argument>(rest)->value()->perform(this);        Sass_Separator separator = SASS_COMMA;-      List_Ptr ls = Cast<List>(splat);-      Map_Ptr ms = Cast<Map>(splat);+      List* ls = Cast<List>(splat);+      Map* ms = Cast<Map>(splat);        List_Obj arglist = SASS_MEMORY_NEW(List,                                       splat->pstate(),@@ -1439,7 +1483,7 @@      if (a->has_keyword_argument()) {       Expression_Obj rv = a->get_keyword_argument()->perform(this);-      Argument_Ptr rvarg = Cast<Argument>(rv);+      Argument* rvarg = Cast<Argument>(rv);       Expression_Obj kwarg = rvarg->value()->perform(this);        aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "", false, true));@@ -1447,217 +1491,47 @@     return aa.detach();   } -  Expression_Ptr Eval::operator()(Comment_Ptr c)+  Expression* Eval::operator()(Comment* c)   {     return 0;   } -  inline Expression_Ptr Eval::fallback_impl(AST_Node_Ptr n)-  {-    return static_cast<Expression_Ptr>(n);-  }--  // All the binary helpers.--  Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate)-  {-    using std::strlen;-    using std::strcpy;-    Expression_Ptr e = NULL;-    switch (sass_value_get_tag(v)) {-      case SASS_BOOLEAN: {-        e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));-      } break;-      case SASS_NUMBER: {-        e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));-      } break;-      case SASS_COLOR: {-        e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));-      } break;-      case SASS_STRING: {-        if (sass_string_is_quoted(v))-          e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));-        else {-          e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));-        }-      } break;-      case SASS_LIST: {-        List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));-        for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {-          l->append(cval_to_astnode(sass_list_get_value(v, i), traces, pstate));-        }-        l->is_bracketed(sass_list_get_is_bracketed(v));-        e = l;-      } break;-      case SASS_MAP: {-        Map_Ptr m = SASS_MEMORY_NEW(Map, pstate);-        for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {-          *m << std::make_pair(-            cval_to_astnode(sass_map_get_key(v, i), traces, pstate),-            cval_to_astnode(sass_map_get_value(v, i), traces, pstate));-        }-        e = m;-      } break;-      case SASS_NULL: {-        e = SASS_MEMORY_NEW(Null, pstate);-      } break;-      case SASS_ERROR: {-        error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces);-      } break;-      case SASS_WARNING: {-        error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces);-      } break;-      default: break;-    }-    return e;-  }--  Selector_List_Ptr Eval::operator()(Selector_List_Ptr s)-  {-    std::vector<Selector_List_Obj> rv;-    Selector_List_Obj sl = SASS_MEMORY_NEW(Selector_List, s->pstate());-    sl->is_optional(s->is_optional());-    sl->media_block(s->media_block());-    sl->is_optional(s->is_optional());-    for (size_t i = 0, iL = s->length(); i < iL; ++i) {-      rv.push_back(operator()((*s)[i]));-    }--    // we should actually permutate parent first-    // but here we have permutated the selector first-    size_t round = 0;-    while (round != std::string::npos) {-      bool abort = true;-      for (size_t i = 0, iL = rv.size(); i < iL; ++i) {-        if (rv[i]->length() > round) {-          sl->append((*rv[i])[round]);-          abort = false;-        }-      }-      if (abort) {-        round = std::string::npos;-      } else {-        ++ round;-      }--    }-    return sl.detach();-  }---  Selector_List_Ptr Eval::operator()(Complex_Selector_Ptr s)-  {-    bool implicit_parent = !exp.old_at_root_without_rule;-    if (is_in_selector_schema) exp.selector_stack.push_back(0);-    Selector_List_Obj resolved = s->resolve_parent_refs(exp.selector_stack, traces, implicit_parent);-    if (is_in_selector_schema) exp.selector_stack.pop_back();-    for (size_t i = 0; i < resolved->length(); i++) {-      Complex_Selector_Ptr is = resolved->at(i)->first();-      while (is) {-        if (is->head()) {-          is->head(operator()(is->head()));-        }-        is = is->tail();-      }-    }-    return resolved.detach();-  }--  Compound_Selector_Ptr Eval::operator()(Compound_Selector_Ptr s)-  {-    for (size_t i = 0; i < s->length(); i++) {-      Simple_Selector_Ptr ss = s->at(i);-      // skip parents here (called via resolve_parent_refs)-      if (ss == NULL || Cast<Parent_Selector>(ss)) continue;-      s->at(i) = Cast<Simple_Selector>(ss->perform(this));-    }-    return s;-  }--  Selector_List_Ptr Eval::operator()(Selector_Schema_Ptr s)+  SelectorList* Eval::operator()(Selector_Schema* s)   {     LOCAL_FLAG(is_in_selector_schema, true);     // the parser will look for a brace to end the selector-    ctx.c_options.in_selector = true; // do not compress colors     Expression_Obj sel = s->contents()->perform(this);-    std::string result_str(sel->to_string(ctx.c_options));-    ctx.c_options.in_selector = false; // flag temporary only+    std::string result_str(sel->to_string(options()));     result_str = unquote(Util::rtrim(result_str));     char* temp_cstr = sass_copy_c_string(result_str.c_str());     ctx.strings.push_back(temp_cstr); // attach to context     Parser p = Parser::from_c_str(temp_cstr, ctx, traces, s->pstate());-    p.last_media_block = s->media_block();-    // a selector schema may or may not connect to parent?-    bool chroot = s->connect_parent() == false;-    Selector_List_Obj sl = p.parse_selector_list(chroot);-    auto vec_str_rend = ctx.strings.rend();-    auto vec_str_rbegin = ctx.strings.rbegin();-    // remove the first item searching from the back-    // we cannot assume our item is still the last one-    // order is not important, so we can optimize this-    auto it = std::find(vec_str_rbegin, vec_str_rend, temp_cstr);-    // undefined behavior if not found!-    if (it != vec_str_rend) {-      // overwrite with last item-      *it = ctx.strings.back();-      // remove last one from vector-      ctx.strings.pop_back();-      // free temporary copy-      free(temp_cstr);-    }++    // If a schema contains a reference to parent it is already+    // connected to it, so don't connect implicitly anymore+    SelectorListObj parsed = p.parseSelectorList(true);     flag_is_in_selector_schema.reset();-    return operator()(sl);+    return parsed.detach();   } -  Expression_Ptr Eval::operator()(Parent_Selector_Ptr p)+  Expression* Eval::operator()(Parent_Reference* p)   {-    if (Selector_List_Obj pr = selector()) {-      exp.selector_stack.pop_back();-      Selector_List_Obj rv = operator()(pr);-      exp.selector_stack.push_back(rv);-      return rv.detach();+    if (SelectorListObj pr = exp.original()) {+      return operator()(pr);     } else {       return SASS_MEMORY_NEW(Null, p->pstate());     }   } -  Simple_Selector_Ptr Eval::operator()(Simple_Selector_Ptr s)+  SimpleSelector* Eval::operator()(SimpleSelector* s)   {     return s;   } -  // hotfix to avoid invalid nested `:not` selectors-  // probably the wrong place, but this should ultimately-  // be fixed by implement superselector correctly for `:not`-  // first use of "find" (ATM only implemented for selectors)-  bool hasNotSelector(AST_Node_Obj obj) {-    if (Wrapped_Selector_Ptr w = Cast<Wrapped_Selector>(obj)) {-      return w->name() == ":not";-    }-    return false;-  }--  Wrapped_Selector_Ptr Eval::operator()(Wrapped_Selector_Ptr s)+  Pseudo_Selector* Eval::operator()(Pseudo_Selector* pseudo)   {--    if (s->name() == ":not") {-      if (exp.selector_stack.back()) {-        if (s->selector()->find(hasNotSelector)) {-          s->selector()->clear();-          s->name(" ");-        } else if (s->selector()->length() == 1) {-          Complex_Selector_Ptr cs = s->selector()->at(0);-          if (cs->tail()) {-            s->selector()->clear();-            s->name(" ");-          }-        } else if (s->selector()->length() > 1) {-          s->selector()->clear();-          s->name(" ");-        }-      }-    }-    return s;+    // ToDo: should we eval selector?+    return pseudo;   };  }
libsass/src/eval.hpp view
@@ -1,7 +1,11 @@ #ifndef SASS_EVAL_H #define SASS_EVAL_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp" #include "ast.hpp"+ #include "context.hpp" #include "listize.hpp" #include "operation.hpp"@@ -12,10 +16,7 @@   class Expand;   class Context; -  class Eval : public Operation_CRTP<Expression_Ptr, Eval> {--   private:-    Expression_Ptr fallback_impl(AST_Node_Ptr n);+  class Eval : public Operation_CRTP<Expression*, Eval> {     public:     Expand& exp;@@ -32,71 +33,77 @@     Boolean_Obj bool_false;      Env* environment();-    Selector_List_Obj selector();+    EnvStack& env_stack();+    const std::string cwd();+    CalleeStack& callee_stack();+    struct Sass_Inspect_Options& options();+    struct Sass_Compiler* compiler();      // for evaluating function bodies-    Expression_Ptr operator()(Block_Ptr);-    Expression_Ptr operator()(Assignment_Ptr);-    Expression_Ptr operator()(If_Ptr);-    Expression_Ptr operator()(For_Ptr);-    Expression_Ptr operator()(Each_Ptr);-    Expression_Ptr operator()(While_Ptr);-    Expression_Ptr operator()(Return_Ptr);-    Expression_Ptr operator()(Warning_Ptr);-    Expression_Ptr operator()(Error_Ptr);-    Expression_Ptr operator()(Debug_Ptr);+    Expression* operator()(Block*);+    Expression* operator()(Assignment*);+    Expression* operator()(If*);+    Expression* operator()(For*);+    Expression* operator()(Each*);+    Expression* operator()(While*);+    Expression* operator()(Return*);+    Expression* operator()(Warning*);+    Expression* operator()(Error*);+    Expression* operator()(Debug*); -    Expression_Ptr operator()(List_Ptr);-    Expression_Ptr operator()(Map_Ptr);-    Expression_Ptr operator()(Binary_Expression_Ptr);-    Expression_Ptr operator()(Unary_Expression_Ptr);-    Expression_Ptr operator()(Function_Call_Ptr);-    Expression_Ptr operator()(Function_Call_Schema_Ptr);-    Expression_Ptr operator()(Variable_Ptr);-    Expression_Ptr operator()(Number_Ptr);-    Expression_Ptr operator()(Color_Ptr);-    Expression_Ptr operator()(Boolean_Ptr);-    Expression_Ptr operator()(String_Schema_Ptr);-    Expression_Ptr operator()(String_Quoted_Ptr);-    Expression_Ptr operator()(String_Constant_Ptr);-    // Expression_Ptr operator()(Selector_List_Ptr);-    Media_Query_Ptr operator()(Media_Query_Ptr);-    Expression_Ptr operator()(Media_Query_Expression_Ptr);-    Expression_Ptr operator()(At_Root_Query_Ptr);-    Expression_Ptr operator()(Supports_Operator_Ptr);-    Expression_Ptr operator()(Supports_Negation_Ptr);-    Expression_Ptr operator()(Supports_Declaration_Ptr);-    Expression_Ptr operator()(Supports_Interpolation_Ptr);-    Expression_Ptr operator()(Null_Ptr);-    Expression_Ptr operator()(Argument_Ptr);-    Expression_Ptr operator()(Arguments_Ptr);-    Expression_Ptr operator()(Comment_Ptr);+    Expression* operator()(List*);+    Expression* operator()(Map*);+    Expression* operator()(Binary_Expression*);+    Expression* operator()(Unary_Expression*);+    Expression* operator()(Function_Call*);+    Expression* operator()(Variable*);+    Expression* operator()(Number*);+    Expression* operator()(Color_RGBA*);+    Expression* operator()(Color_HSLA*);+    Expression* operator()(Boolean*);+    Expression* operator()(String_Schema*);+    Expression* operator()(String_Quoted*);+    Expression* operator()(String_Constant*);+    Media_Query* operator()(Media_Query*);+    Expression* operator()(Media_Query_Expression*);+    Expression* operator()(At_Root_Query*);+    Expression* operator()(Supports_Operator*);+    Expression* operator()(Supports_Negation*);+    Expression* operator()(Supports_Declaration*);+    Expression* operator()(Supports_Interpolation*);+    Expression* operator()(Null*);+    Expression* operator()(Argument*);+    Expression* operator()(Arguments*);+    Expression* operator()(Comment*);      // these will return selectors-    Selector_List_Ptr operator()(Selector_List_Ptr);-    Selector_List_Ptr operator()(Complex_Selector_Ptr);-    Compound_Selector_Ptr operator()(Compound_Selector_Ptr);-    Simple_Selector_Ptr operator()(Simple_Selector_Ptr s);-    Wrapped_Selector_Ptr operator()(Wrapped_Selector_Ptr s);+    SelectorList* operator()(SelectorList*);+    SelectorList* operator()(ComplexSelector*);+    CompoundSelector* operator()(CompoundSelector*);+    SelectorComponent* operator()(SelectorComponent*);+    SimpleSelector* operator()(SimpleSelector* s);+    Pseudo_Selector* operator()(Pseudo_Selector* s);+     // they don't have any specific implementation (yet)-    // Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; };-    // Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; };-    // Class_Selector_Ptr operator()(Class_Selector_Ptr s) { return s; };-    // Id_Selector_Ptr operator()(Id_Selector_Ptr s) { return s; };-    // Placeholder_Selector_Ptr operator()(Placeholder_Selector_Ptr s) { return s; };+    Id_Selector* operator()(Id_Selector* s) { return s; };+    Class_Selector* operator()(Class_Selector* s) { return s; };+    Type_Selector* operator()(Type_Selector* s) { return s; };+    Attribute_Selector* operator()(Attribute_Selector* s) { return s; };+    Placeholder_Selector* operator()(Placeholder_Selector* s) { return s; };+     // actual evaluated selectors-    Selector_List_Ptr operator()(Selector_Schema_Ptr);-    Expression_Ptr operator()(Parent_Selector_Ptr);+    SelectorList* operator()(Selector_Schema*);+    Expression* operator()(Parent_Reference*); +    // generic fallback     template <typename U>-    Expression_Ptr fallback(U x) { return fallback_impl(x); }+    Expression* fallback(U x)+    { return Cast<Expression>(x); }    private:     void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false);    };--  Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate = ParserState("[AST]"));  } 
+ libsass/src/eval_selectors.cpp view
@@ -0,0 +1,75 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "expand.hpp"+#include "eval.hpp"+#include "ast.hpp"+++namespace Sass {++  SelectorList* Eval::operator()(SelectorList* s)+  {+    std::vector<SelectorListObj> rv;+    SelectorListObj sl = SASS_MEMORY_NEW(SelectorList, s->pstate());+    for (size_t i = 0, iL = s->length(); i < iL; ++i) {+      rv.push_back(operator()(s->get(i)));+    }++    // we should actually permutate parent first+    // but here we have permutated the selector first+    size_t round = 0;+    while (round != std::string::npos) {+      bool abort = true;+      for (size_t i = 0, iL = rv.size(); i < iL; ++i) {+        if (rv[i]->length() > round) {+          sl->append((*rv[i])[round]);+          abort = false;+        }+      }+      if (abort) {+        round = std::string::npos;+      }+      else {+        ++round;+      }++    }+    return sl.detach();+  }++  SelectorComponent* Eval::operator()(SelectorComponent* s)+  {+    return {};+  }++  SelectorList* Eval::operator()(ComplexSelector* s)+  {+    bool implicit_parent = !exp.old_at_root_without_rule;+    if (is_in_selector_schema) exp.pushNullSelector();+    SelectorListObj other = s->resolve_parent_refs(+      exp.getOriginalStack(), traces, implicit_parent);+    if (is_in_selector_schema) exp.popNullSelector();++    for (size_t i = 0; i < other->length(); i++) {+      ComplexSelectorObj sel = other->at(i);+      for (size_t n = 0; n < sel->length(); n++) {+        if (CompoundSelectorObj comp = Cast<CompoundSelector>(sel->at(n))) {+          sel->at(n) = operator()(comp);+        }+      }+    }++    return other.detach();+  }++  CompoundSelector* Eval::operator()(CompoundSelector* s)+  {+    for (size_t i = 0; i < s->length(); i++) {+      SimpleSelector* ss = s->at(i);+      // skip parents here (called via resolve_parent_refs)+      s->at(i) = Cast<SimpleSelector>(ss->perform(this));+    }+    return s;+  }+}
libsass/src/expand.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include <typeinfo> @@ -16,7 +19,7 @@   // simple endless recursion protection   const size_t maxRecursion = 500; -  Expand::Expand(Context& ctx, Env* env, std::vector<Selector_List_Obj>* stack)+  Expand::Expand(Context& ctx, Env* env, SelectorStack* stack, SelectorStack* originals)   : ctx(ctx),     traces(ctx.traces),     eval(Eval(*this)),@@ -24,19 +27,32 @@     in_keyframes(false),     at_root_without_rule(false),     old_at_root_without_rule(false),-    env_stack(std::vector<Env*>()),-    block_stack(std::vector<Block_Ptr>()),-    call_stack(std::vector<AST_Node_Obj>()),-    selector_stack(std::vector<Selector_List_Obj>()),-    media_block_stack(std::vector<Media_Block_Ptr>())+    env_stack(),+    block_stack(),+    call_stack(),+    selector_stack(),+    originalStack(),+    mediaStack()   {-    env_stack.push_back(0);+    env_stack.push_back(nullptr);     env_stack.push_back(env);-    block_stack.push_back(0);-    call_stack.push_back(0);-    if (stack == NULL) { selector_stack.push_back(0); }-    else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); }-    media_block_stack.push_back(0);+    block_stack.push_back(nullptr);+    call_stack.push_back({});+    if (stack == NULL) { pushToSelectorStack({}); }+    else {+      for (auto item : *stack) {+        if (item.isNull()) pushToSelectorStack({});+        else pushToSelectorStack(item);+      }+    }+    if (originals == NULL) { pushToOriginalStack({}); }+    else {+      for (auto item : *stack) {+        if (item.isNull()) pushToOriginalStack({});+        else pushToOriginalStack(item);+      }+    }+    mediaStack.push_back({});   }    Env* Expand::environment()@@ -46,15 +62,84 @@     return 0;   } -  Selector_List_Obj Expand::selector()+  void Expand::pushNullSelector()   {-    if (selector_stack.size() > 0)-      return selector_stack.back();-    return 0;+    pushToSelectorStack({});+    pushToOriginalStack({});   } +  void Expand::popNullSelector()+  {+    popFromOriginalStack();+    popFromSelectorStack();+  }++  SelectorStack Expand::getOriginalStack()+  {+    return originalStack;+  }++  SelectorStack Expand::getSelectorStack()+  {+    return selector_stack;+  }++  SelectorListObj& Expand::selector()+  {+    if (selector_stack.size() > 0) {+      auto& sel = selector_stack.back();+      if (sel.isNull()) return sel;+      return sel;+    }+    // Avoid the need to return copies+    // We always want an empty first item+    selector_stack.push_back({});+    return selector_stack.back();;+  }++  SelectorListObj& Expand::original()+  {+    if (originalStack.size() > 0) {+      auto& sel = originalStack.back();+      if (sel.isNull()) return sel;+      return sel;+    }+    // Avoid the need to return copies+    // We always want an empty first item+    originalStack.push_back({});+    return originalStack.back();+  }++  SelectorListObj Expand::popFromSelectorStack()+  {+    SelectorListObj last = selector_stack.back();+    if (selector_stack.size() > 0)    +      selector_stack.pop_back();+    if (last.isNull()) return {};+    return last;+  }++  void Expand::pushToSelectorStack(SelectorListObj selector)+  {+    selector_stack.push_back(selector);+  }++  SelectorListObj Expand::popFromOriginalStack()+  {+    SelectorListObj last = originalStack.back();+    if (originalStack.size() > 0)+      originalStack.pop_back();+    if (last.isNull()) return {};+    return last;+  }++  void Expand::pushToOriginalStack(SelectorListObj selector)+  {+    originalStack.push_back(selector);+  }+   // blocks create new variable scopes-  Block_Ptr Expand::operator()(Block_Ptr b)+  Block* Expand::operator()(Block* b)   {     // create new local environment     // set the current env as parent@@ -77,76 +162,62 @@     return bb.detach();   } -  Statement_Ptr Expand::operator()(Ruleset_Ptr r)+  Statement* Expand::operator()(Ruleset* r)   {     LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule);      if (in_keyframes) {-      Block_Ptr bb = operator()(r->block());+      Block* bb = operator()(r->block());       Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);-      if (r->selector()) {-        if (Selector_List_Ptr s = r->selector()) {-          selector_stack.push_back(0);-          k->name(s->eval(eval));-          selector_stack.pop_back();+      if (r->schema()) {+        pushNullSelector();+        k->name(eval(r->schema()));+        popNullSelector();+      }+      else if (r->selector()) {+        if (SelectorListObj s = r->selector()) {+          pushNullSelector();+          k->name(eval(s));+          popNullSelector();         }       }+       return k.detach();     } -    // reset when leaving scope-    LOCAL_FLAG(at_root_without_rule, false);+    if (r->schema()) {+      SelectorListObj sel = eval(r->schema());+      r->selector(sel);+      bool chroot = sel->has_real_parent_ref();+      for (auto complex : sel->elements()) {+        complex->chroots(chroot);+      } -    // `&` is allowed in `@at-root`!-    bool has_parent_selector = false;-    for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) {-      Selector_List_Obj ll = selector_stack.at(i);-      has_parent_selector = ll != 0 && ll->length() > 0;     } -    Selector_List_Obj sel = r->selector();-    if (sel) sel = sel->eval(eval);--    // check for parent selectors in base level rules-    if (r->is_root() || (block_stack.back() && block_stack.back()->is_root())) {-      if (Selector_List_Ptr selector_list = Cast<Selector_List>(r->selector())) {-        for (Complex_Selector_Obj complex_selector : selector_list->elements()) {-          Complex_Selector_Ptr tail = complex_selector;-          while (tail) {-            if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {-              Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header);-              if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue;-              std::string sel_str(complex_selector->to_string(ctx.c_options));-              error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), traces);-            }-            tail = tail->tail();-          }-        }-      }-    }-    else {-      if (sel->length() == 0 || sel->has_parent_ref()) {-        if (sel->has_real_parent_ref() && !has_parent_selector) {-          error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), traces);-        }-      }-    }+    // reset when leaving scope+    LOCAL_FLAG(at_root_without_rule, false); +    SelectorListObj evaled = eval(r->selector());     // do not connect parent again-    sel->remove_parent_selectors();-    selector_stack.push_back(sel);     Env env(environment());     if (block_stack.back()->is_root()) {       env_stack.push_back(&env);     }-    sel->set_media_block(media_block_stack.back());-    Block_Obj blk = 0;+    Block_Obj blk;+    pushToSelectorStack(evaled);+    // The copy is needed for parent reference evaluation+    // dart-sass stores it as `originalSelector` member+    pushToOriginalStack(SASS_MEMORY_COPY(evaled));+    ctx.extender.addSelector(evaled, mediaStack.back());     if (r->block()) blk = operator()(r->block());-    Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset,+    popFromOriginalStack();+    popFromSelectorStack();+    Ruleset* rr = SASS_MEMORY_NEW(Ruleset,                                   r->pstate(),-                                  sel,+                                  evaled,                                   blk);-    selector_stack.pop_back();+     if (block_stack.back()->is_root()) {       env_stack.pop_back();     }@@ -157,7 +228,7 @@     return rr;   } -  Statement_Ptr Expand::operator()(Supports_Block_Ptr f)+  Statement* Expand::operator()(Supports_Block* f)   {     Expression_Obj condition = f->condition()->perform(&eval);     Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block,@@ -167,34 +238,47 @@     return ff.detach();   } -  Statement_Ptr Expand::operator()(Media_Block_Ptr m)+  std::vector<CssMediaQuery_Obj> Expand::mergeMediaQueries(+    const std::vector<CssMediaQuery_Obj>& lhs,+    const std::vector<CssMediaQuery_Obj>& rhs)   {-    Media_Block_Obj cpy = SASS_MEMORY_COPY(m);-    // Media_Blocks are prone to have circular references-    // Copy could leak memory if it does not get picked up-    // Looks like we are able to reset block reference for copy-    // Good as it will ensure a low memory overhead for this fix-    // So this is a cheap solution with a minimal price-    ctx.ast_gc.push_back(cpy); cpy->block(0);-    Expression_Obj mq = eval(m->media_queries());-    std::string str_mq(mq->to_string(ctx.c_options));+    std::vector<CssMediaQuery_Obj> queries;+    for (CssMediaQuery_Obj query1 : lhs) {+      for (CssMediaQuery_Obj query2 : rhs) {+        CssMediaQuery_Obj result = query1->merge(query2);+        if (result && !result->empty()) {+          queries.push_back(result);+        }+      }+    }+    return queries;+  }++  Statement* Expand::operator()(MediaRule* m)+  {+    Expression_Obj mq = eval(m->schema());+    std::string str_mq(mq->to_css(ctx.c_options));     char* str = sass_copy_c_string(str_mq.c_str());     ctx.strings.push_back(str);-    Parser p(Parser::from_c_str(str, ctx, traces, mq->pstate()));-    mq = p.parse_media_queries(); // re-assign now-    cpy->media_queries(mq);-    media_block_stack.push_back(cpy);-    Block_Obj blk = operator()(m->block());-    Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block,-                                      m->pstate(),-                                      mq,-                                      blk);-    media_block_stack.pop_back();-    mm->tabs(m->tabs());-    return mm;+    Parser parser(Parser::from_c_str(str, ctx, traces, mq->pstate()));+    // Create a new CSS only representation of the media rule+    CssMediaRuleObj css = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());+    std::vector<CssMediaQuery_Obj> parsed = parser.parseCssMediaQueries();+    if (mediaStack.size() && mediaStack.back()) {+      auto& parent = mediaStack.back()->elements();+      css->concat(mergeMediaQueries(parent, parsed));+    }+    else {+      css->concat(parsed);+    }+    mediaStack.push_back(css);+    css->block(operator()(m->block()));+    mediaStack.pop_back();+    return css.detach();+   } -  Statement_Ptr Expand::operator()(At_Root_Block_Ptr a)+  Statement* Expand::operator()(At_Root_Block* a)   {     Block_Obj ab = a->block();     Expression_Obj ae = a->expression();@@ -202,7 +286,7 @@     if (ae) ae = ae->perform(&eval);     else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate()); -    LOCAL_FLAG(at_root_without_rule, true);+    LOCAL_FLAG(at_root_without_rule, Cast<At_Root_Query>(ae)->exclude("rule"));     LOCAL_FLAG(in_keyframes, false);                                         ;@@ -215,18 +299,18 @@     return aa.detach();   } -  Statement_Ptr Expand::operator()(Directive_Ptr a)+  Statement* Expand::operator()(Directive* a)   {     LOCAL_FLAG(in_keyframes, a->is_keyframes());-    Block_Ptr ab = a->block();-    Selector_List_Ptr as = a->selector();-    Expression_Ptr av = a->value();-    selector_stack.push_back(0);+    Block* ab = a->block();+    SelectorList* as = a->selector();+    Expression* av = a->value();+    pushNullSelector();     if (av) av = av->perform(&eval);     if (as) as = eval(as);-    selector_stack.pop_back();-    Block_Ptr bb = ab ? operator()(ab) : NULL;-    Directive_Ptr aa = SASS_MEMORY_NEW(Directive,+    popNullSelector();+    Block* bb = ab ? operator()(ab) : NULL;+    Directive* aa = SASS_MEMORY_NEW(Directive,                                   a->pstate(),                                   a->keyword(),                                   as,@@ -235,7 +319,7 @@     return aa;   } -  Statement_Ptr Expand::operator()(Declaration_Ptr d)+  Statement* Expand::operator()(Declaration* d)   {     Block_Obj ab = d->block();     String_Obj old_p = d->property();@@ -250,9 +334,15 @@     if (value) value = value->perform(&eval);     Block_Obj bb = ab ? operator()(ab) : NULL;     if (!bb) {-      if (!value || (value->is_invisible() && !d->is_important())) return 0;+      if (!value || (value->is_invisible() && !d->is_important())) {+        if (d->is_custom_property()) {+          error("Custom property values may not be empty.", d->value()->pstate(), traces);+        } else {+          return nullptr;+        }+      }     }-    Declaration_Ptr decl = SASS_MEMORY_NEW(Declaration,+    Declaration* decl = SASS_MEMORY_NEW(Declaration,                                         d->pstate(),                                         new_p,                                         value,@@ -263,7 +353,7 @@     return decl;   } -  Statement_Ptr Expand::operator()(Assignment_Ptr a)+  Statement* Expand::operator()(Assignment* a)   {     Env* env = environment();     const std::string& var(a->variable());@@ -324,7 +414,7 @@     return 0;   } -  Statement_Ptr Expand::operator()(Import_Ptr imp)+  Statement* Expand::operator()(Import* imp)   {     Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate());     if (imp->import_queries() && imp->import_queries()->size()) {@@ -339,7 +429,7 @@     return result.detach();   } -  Statement_Ptr Expand::operator()(Import_Stub_Ptr i)+  Statement* Expand::operator()(Import_Stub* i)   {     traces.push_back(Backtrace(i->pstate()));     // get parent node from call stack@@ -369,28 +459,28 @@     return 0;   } -  Statement_Ptr Expand::operator()(Warning_Ptr w)+  Statement* Expand::operator()(Warning* w)   {     // eval handles this too, because warnings may occur in functions     w->perform(&eval);     return 0;   } -  Statement_Ptr Expand::operator()(Error_Ptr e)+  Statement* Expand::operator()(Error* e)   {     // eval handles this too, because errors may occur in functions     e->perform(&eval);     return 0;   } -  Statement_Ptr Expand::operator()(Debug_Ptr d)+  Statement* Expand::operator()(Debug* d)   {     // eval handles this too, because warnings may occur in functions     d->perform(&eval);     return 0;   } -  Statement_Ptr Expand::operator()(Comment_Ptr c)+  Statement* Expand::operator()(Comment* c)   {     if (ctx.output_style() == COMPRESSED) {       // comments should not be evaluated in compact@@ -398,13 +488,13 @@       if (!c->is_important()) return NULL;     }     eval.is_in_comment = true;-    Comment_Ptr rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important());+    Comment* rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important());     eval.is_in_comment = false;     // TODO: eval the text, once we're parsing/storing it as a String_Schema     return rv;   } -  Statement_Ptr Expand::operator()(If_Ptr i)+  Statement* Expand::operator()(If* i)   {     Env env(environment(), true);     env_stack.push_back(&env);@@ -414,7 +504,7 @@       append_block(i->block());     }     else {-      Block_Ptr alt = i->alternative();+      Block* alt = i->alternative();       if (alt) append_block(alt);     }     call_stack.pop_back();@@ -424,7 +514,7 @@    // For does not create a new env scope   // But iteration vars are reset afterwards-  Statement_Ptr Expand::operator()(For_Ptr f)+  Statement* Expand::operator()(For* f)   {     std::string variable(f->variable());     Expression_Obj low = f->lower_bound()->perform(&eval);@@ -452,7 +542,7 @@     Env env(environment(), true);     env_stack.push_back(&env);     call_stack.push_back(f);-    Block_Ptr body = f->block();+    Block* body = f->block();     if (start < end) {       if (f->is_inclusive()) ++end;       for (double i = start;@@ -479,18 +569,17 @@    // Eval does not create a new env scope   // But iteration vars are reset afterwards-  Statement_Ptr Expand::operator()(Each_Ptr e)+  Statement* Expand::operator()(Each* e)   {     std::vector<std::string> variables(e->variables());     Expression_Obj expr = e->list()->perform(&eval);-    List_Obj list = 0;+    List_Obj list;     Map_Obj map;     if (expr->concrete_type() == Expression::MAP) {       map = Cast<Map>(expr);     }-    else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {-      Listize listize;-      Expression_Obj rv = ls->perform(&listize);+    else if (SelectorList * ls = Cast<SelectorList>(expr)) {+      Expression_Obj rv = Listize::perform(ls);       list = Cast<List>(rv);     }     else if (expr->concrete_type() != Expression::LIST) {@@ -504,7 +593,7 @@     Env env(environment(), true);     env_stack.push_back(&env);     call_stack.push_back(e);-    Block_Ptr body = e->block();+    Block* body = e->block();      if (map) {       for (auto key : map->keys()) {@@ -525,7 +614,7 @@     }     else {       // bool arglist = list->is_arglist();-      if (list->length() == 1 && Cast<Selector_List>(list)) {+      if (list->length() == 1 && Cast<SelectorList>(list)) {         list = Cast<List>(list);       }       for (size_t i = 0, L = list->length(); i < L; ++i) {@@ -563,10 +652,10 @@     return 0;   } -  Statement_Ptr Expand::operator()(While_Ptr w)+  Statement* Expand::operator()(While* w)   {     Expression_Obj pred = w->predicate();-    Block_Ptr body = w->block();+    Block* body = w->block();     Env env(environment(), true);     env_stack.push_back(&env);     call_stack.push_back(w);@@ -580,96 +669,72 @@     return 0;   } -  Statement_Ptr Expand::operator()(Return_Ptr r)+  Statement* Expand::operator()(Return* r)   {     error("@return may only be used within a function", r->pstate(), traces);     return 0;   } --  void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {+  Statement* Expand::operator()(ExtendRule* e)+  { -    if (Selector_List_Obj sl = Cast<Selector_List>(s)) {-      for (Complex_Selector_Obj complex_selector : sl->elements()) {-        Complex_Selector_Obj tail = complex_selector;-        while (tail) {-          if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {-            if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others-            std::string sel_str(complex_selector->to_string(ctx.c_options));-            error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), traces);-          }-          tail = tail->tail();-        }-      }+    // evaluate schema first+    if (e->schema()) {+      e->selector(eval(e->schema()));+      e->isOptional(e->selector()->is_optional());     }+    // evaluate the selector+    e->selector(eval(e->selector())); +    if (e->selector()) { -    Selector_List_Obj contextualized = Cast<Selector_List>(s->perform(&eval));-    if (contextualized == false) return;-    for (auto complex_sel : contextualized->elements()) {-      Complex_Selector_Obj c = complex_sel;-      if (!c->head() || c->tail()) {-        std::string sel_str(contextualized->to_string(ctx.c_options));-        error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), traces);-      }-      Compound_Selector_Obj target = c->head();-      if (contextualized->is_optional()) target->is_optional(true);-      for (size_t i = 0, L = extender->length(); i < L; ++i) {-        Complex_Selector_Obj sel = (*extender)[i];-        if (!(sel->head() && sel->head()->length() > 0 &&-            Cast<Parent_Selector>((*sel->head())[0])))-        {-          Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate());-          hh->media_block((*extender)[i]->media_block());-          Complex_Selector_Obj ssel = SASS_MEMORY_NEW(Complex_Selector, (*extender)[i]->pstate());-          ssel->media_block((*extender)[i]->media_block());-          if (sel->has_line_feed()) ssel->has_line_feed(true);-          Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate());-          ps->media_block((*extender)[i]->media_block());-          hh->append(ps);-          ssel->tail(sel);-          ssel->head(hh);-          sel = ssel;+      for (auto complex : e->selector()->elements()) {++        if (complex->length() != 1) {+          error("complex selectors may not be extended.", complex->pstate(), traces);         }-        // if (c->has_line_feed()) sel->has_line_feed(true);-        ctx.subset_map.put(target, std::make_pair(sel, target));-      }-    } -  }+        if (const CompoundSelector* compound = complex->first()->getCompound()) { -  Statement* Expand::operator()(Extension_Ptr e)-  {-    if (Selector_List_Ptr extender = selector()) {-      Selector_List_Ptr sl = e->selector();-      // abort on invalid selector-      if (sl == NULL) return NULL;-      if (Selector_Schema_Ptr schema = sl->schema()) {-        if (schema->has_real_parent_ref()) {-          // put root block on stack again (ignore parents)-          // selector schema must not connect in eval!-          block_stack.push_back(block_stack.at(1));-          sl = eval(sl->schema());-          block_stack.pop_back();-        } else {-          selector_stack.push_back(0);-          sl = eval(sl->schema());-          selector_stack.pop_back();+          if (compound->length() != 1) {++            std::stringstream sels; bool addComma = false;+            sels << "Compound selectors may no longer be extended.\n";+            sels << "Consider `@extend ";+            for (auto sel : compound->elements()) {+              if (addComma) sels << ", ";+              sels << sel->to_sass();+              addComma = true;+            }+            sels << "` instead.\n";+            sels << "See http://bit.ly/ExtendCompound for details.";++            warning(sels.str(), compound->pstate());++            // Make this an error once deprecation is over+            for (SimpleSelectorObj simple : compound->elements()) {+              // Pass every selector we ever see to extender (to make them findable for extend)+              ctx.extender.addExtension(selector(), simple, mediaStack.back(), e->isOptional());+            }++          }+          else {+            // Pass every selector we ever see to extender (to make them findable for extend)+            ctx.extender.addExtension(selector(), compound->first(), mediaStack.back(), e->isOptional());+          }+         }-      }-      for (Complex_Selector_Obj cs : sl->elements()) {-        if (!cs.isNull() && !cs->head().isNull()) {-          cs->head()->media_block(media_block_stack.back());+        else {+          error("complex selectors may not be extended.", complex->pstate(), traces);         }       }-      selector_stack.push_back(0);-      expand_selector_list(sl, extender);-      selector_stack.pop_back();     }-    return 0;++    return nullptr;+   } -  Statement_Ptr Expand::operator()(Definition_Ptr d)+  Statement* Expand::operator()(Definition* d)   {     Env* env = environment();     Definition_Obj dd = SASS_MEMORY_COPY(d);@@ -694,8 +759,9 @@     return 0;   } -  Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)+  Statement* Expand::operator()(Mixin_Call* c)   {+     if (recursions > maxRecursion) {       throw Exception::StackError(traces, *c);     }@@ -730,29 +796,31 @@     Env new_env(def->environment());     env_stack.push_back(&new_env);     if (c->block()) {+      Parameters_Obj params = c->block_parameters();+      if (!params) params = SASS_MEMORY_NEW(Parameters, c->pstate());       // represent mixin content blocks as thunks/closures       Definition_Obj thunk = SASS_MEMORY_NEW(Definition,                                           c->pstate(),                                           "@content",-                                          SASS_MEMORY_NEW(Parameters, c->pstate()),+                                          params,                                           c->block(),                                           Definition::MIXIN);       thunk->environment(env);       new_env.local_frame()["@content[m]"] = thunk;     } -    bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);+    bind(std::string("Mixin"), c->name(), params, args, &new_env, &eval, traces);      Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());     Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);      env->set_global("is_in_mixin", bool_true);-    if (Block_Ptr pr = block_stack.back()) {+    if (Block* pr = block_stack.back()) {       trace_block->is_root(pr->is_root());     }     block_stack.push_back(trace_block);     for (auto bb : body->elements()) {-      if (Ruleset_Ptr r = Cast<Ruleset>(bb)) {+      if (Ruleset* r = Cast<Ruleset>(bb)) {         r->is_root(trace_block->is_root());       }       Statement_Obj ith = bb->perform(this);@@ -769,45 +837,29 @@     return trace.detach();   } -  Statement_Ptr Expand::operator()(Content_Ptr c)+  Statement* Expand::operator()(Content* c)   {     Env* env = environment();     // convert @content directives into mixin calls to the underlying thunk     if (!env->has("@content[m]")) return 0;--    if (block_stack.back()->is_root()) {-      selector_stack.push_back(0);-    }+    Arguments_Obj args = c->arguments();+    if (!args) args = SASS_MEMORY_NEW(Arguments, c->pstate());      Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call,                                        c->pstate(),                                        "@content",-                                       SASS_MEMORY_NEW(Arguments, c->pstate()));+                                       args);      Trace_Obj trace = Cast<Trace>(call->perform(this));--    if (block_stack.back()->is_root()) {-      selector_stack.pop_back();-    }-     return trace.detach();   } -  // produce an error if something is not implemented-  inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n)-  {-    std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();-    String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err);-    error("unknown internal error; please contact the LibSass maintainers", n->pstate(), traces);-    return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg);-  }-   // process and add to last block on stack-  inline void Expand::append_block(Block_Ptr b)+  inline void Expand::append_block(Block* b)   {     if (b->is_root()) call_stack.push_back(b);     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Ptr stm = b->at(i);+      Statement* stm = b->at(i);       Statement_Obj ith = stm->perform(this);       if (ith) block_stack.back()->append(ith);     }
libsass/src/expand.hpp view
@@ -15,12 +15,23 @@   class Eval;   struct Backtrace; -  class Expand : public Operation_CRTP<Statement_Ptr, Expand> {+  class Expand : public Operation_CRTP<Statement*, Expand> {   public:      Env* environment();-    Selector_List_Obj selector();+    SelectorListObj& selector();+    SelectorListObj& original();+    SelectorListObj popFromSelectorStack();+    SelectorStack getOriginalStack();+    SelectorStack getSelectorStack();+    void pushNullSelector();+    void popNullSelector();+    void pushToSelectorStack(SelectorListObj selector); +    SelectorListObj popFromOriginalStack();++    void pushToOriginalStack(SelectorListObj selector);+     Context&          ctx;     Backtraces&       traces;     Eval              eval;@@ -30,51 +41,56 @@     bool              old_at_root_without_rule;      // it's easier to work with vectors-    std::vector<Env*>              env_stack;-    std::vector<Block_Ptr>         block_stack;-    std::vector<AST_Node_Obj>      call_stack;-    std::vector<Selector_List_Obj> selector_stack;-    std::vector<Media_Block_Ptr>   media_block_stack;+    EnvStack      env_stack;+    BlockStack    block_stack;+    CallStack     call_stack;+  private:+    SelectorStack selector_stack;+  public:+    SelectorStack originalStack;+    MediaStack    mediaStack;      Boolean_Obj bool_true; -    Statement_Ptr fallback_impl(AST_Node_Ptr n);-   private:-    void expand_selector_list(Selector_Obj, Selector_List_Obj extender); +    std::vector<CssMediaQuery_Obj> mergeMediaQueries(const std::vector<CssMediaQuery_Obj>& lhs, const std::vector<CssMediaQuery_Obj>& rhs);+   public:-    Expand(Context&, Env*, std::vector<Selector_List_Obj>* stack = NULL);+    Expand(Context&, Env*, SelectorStack* stack = nullptr, SelectorStack* original = nullptr);     ~Expand() { } -    Block_Ptr operator()(Block_Ptr);-    Statement_Ptr operator()(Ruleset_Ptr);-    Statement_Ptr operator()(Media_Block_Ptr);-    Statement_Ptr operator()(Supports_Block_Ptr);-    Statement_Ptr operator()(At_Root_Block_Ptr);-    Statement_Ptr operator()(Directive_Ptr);-    Statement_Ptr operator()(Declaration_Ptr);-    Statement_Ptr operator()(Assignment_Ptr);-    Statement_Ptr operator()(Import_Ptr);-    Statement_Ptr operator()(Import_Stub_Ptr);-    Statement_Ptr operator()(Warning_Ptr);-    Statement_Ptr operator()(Error_Ptr);-    Statement_Ptr operator()(Debug_Ptr);-    Statement_Ptr operator()(Comment_Ptr);-    Statement_Ptr operator()(If_Ptr);-    Statement_Ptr operator()(For_Ptr);-    Statement_Ptr operator()(Each_Ptr);-    Statement_Ptr operator()(While_Ptr);-    Statement_Ptr operator()(Return_Ptr);-    Statement_Ptr operator()(Extension_Ptr);-    Statement_Ptr operator()(Definition_Ptr);-    Statement_Ptr operator()(Mixin_Call_Ptr);-    Statement_Ptr operator()(Content_Ptr);+    Block* operator()(Block*);+    Statement* operator()(Ruleset*); -    template <typename U>-    Statement_Ptr fallback(U x) { return fallback_impl(x); }+    Statement* operator()(MediaRule*); -    void append_block(Block_Ptr);+    // Css Ruleset is already static+    // Statement* operator()(CssMediaRule*);++    Statement* operator()(Supports_Block*);+    Statement* operator()(At_Root_Block*);+    Statement* operator()(Directive*);+    Statement* operator()(Declaration*);+    Statement* operator()(Assignment*);+    Statement* operator()(Import*);+    Statement* operator()(Import_Stub*);+    Statement* operator()(Warning*);+    Statement* operator()(Error*);+    Statement* operator()(Debug*);+    Statement* operator()(Comment*);+    Statement* operator()(If*);+    Statement* operator()(For*);+    Statement* operator()(Each*);+    Statement* operator()(While*);+    Statement* operator()(Return*);+    Statement* operator()(ExtendRule*);+    Statement* operator()(Definition*);+    Statement* operator()(Mixin_Call*);+    Statement* operator()(Content*);++    void append_block(Block*);+   };  }
− libsass/src/extend.cpp
@@ -1,2130 +0,0 @@-#include "sass.hpp"-#include "extend.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "paths.hpp"-#include "parser.hpp"-#include "expand.hpp"-#include "node.hpp"-#include "sass_util.hpp"-#include "remove_placeholders.hpp"-#include "debug.hpp"-#include <iostream>-#include <deque>-#include <set>--/*- NOTES:-- - The print* functions print to cerr. This allows our testing frameworks (like sass-spec) to ignore the output, which-   is very helpful when debugging. The format of the output is mainly to wrap things in square brackets to match what-   ruby already outputs (to make comparisons easier).-- - For the direct porting effort, we're trying to port method-for-method until we get all the tests passing.-   Where applicable, I've tried to include the ruby code above the function for reference until all our tests pass.-   The ruby code isn't always directly portable, so I've tried to include any modified ruby code that was actually-   used for the porting.-- - DO NOT try to optimize yet. We get a tremendous benefit out of comparing the output of each stage of the extend to the ruby-   output at the same stage. This makes it much easier to determine where problems are. Try to keep as close to-   the ruby code as you can until we have all the sass-spec tests passing. Then, we should optimize. However, if you see-   something that could probably be optimized, let's not forget it. Add a // TODO: or // IMPROVEMENT: comment.-- - Coding conventions in this file (these may need to be changed before merging back into master)-   - Very basic hungarian notation:-     p prefix for pointers (pSelector)-     no prefix for value types and references (selector)-   - Use STL iterators where possible-   - prefer verbose naming over terse naming-   - use typedefs for STL container types for make maintenance easier-- - You may see a lot of comments that say "// TODO: is this the correct combinator?". See the comment referring to combinators-   in extendCompoundSelector for a more extensive explanation of my confusion. I think our divergence in data model from ruby-   sass causes this to be necessary.--- GLOBAL TODOS:-- - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode.-- - consider making the extend* functions member functions to avoid passing around ctx and subset_map map around. This has the-   drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and-   can cause additional compile time dependencies.-- - mark the helper methods in this file static to given them compilation unit linkage.-- - implement parent directive matching-- - fix compilation warnings for unused Extend members if we really don't need those references anymore.- */---namespace Sass {----#ifdef DEBUG--  // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp-  std::ostream& operator<<(std::ostream& os, const Complex_Selector::Combinator combinator) {-    switch (combinator) {-      case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;-      case Complex_Selector::PARENT_OF:   os << "\">\""; break;-      case Complex_Selector::PRECEDES:    os << "\"~\""; break;-      case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;-      case Complex_Selector::REFERENCE:   os << "\"/\""; break;-    }--    return os;-  }---  std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) {-    for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) {-      if (i > 0) os << ", ";-      os << compoundSelector[i]->to_string();-    }-    return os;-  }--  std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) {-    os << simpleSelector.to_string();-    return os;-  }--  // Print a string representation of a Compound_Selector-  static void printSimpleSelector(Simple_Selector* pSimpleSelector, const char* message=NULL, bool newline=true) {--    if (message) {-      std::cerr << message;-    }--    if (pSimpleSelector) {-      std::cerr << "[" << *pSimpleSelector << "]";-    } else {-      std::cerr << "NULL";-    }--    if (newline) {-      std::cerr << std::endl;-    }-  }--  // Print a string representation of a Compound_Selector-  static void printCompoundSelector(Compound_Selector_Ptr pCompoundSelector, const char* message=NULL, bool newline=true) {--    if (message) {-      std::cerr << message;-    }--    if (pCompoundSelector) {-      std::cerr << "[" << *pCompoundSelector << "]";-    } else {-      std::cerr << "NULL";-    }--    if (newline) {-      std::cerr << std::endl;-    }-  }---  std::ostream& operator<<(std::ostream& os, Complex_Selector& complexSelector) {--    os << "[";-    Complex_Selector_Ptr pIter = &complexSelector;-    bool first = true;-    while (pIter) {-      if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {-        if (!first) {-          os << ", ";-        }-        first = false;-        os << pIter->combinator();-      }--      if (!first) {-        os << ", ";-      }-      first = false;--      if (pIter->head()) {-        os << pIter->head()->to_string();-      } else {-        os << "NULL_HEAD";-      }--      pIter = pIter->tail();-    }-    os << "]";--    return os;-  }---  // Print a string representation of a Complex_Selector-  static void printComplexSelector(Complex_Selector_Ptr pComplexSelector, const char* message=NULL, bool newline=true) {--    if (message) {-      std::cerr << message;-    }--    if (pComplexSelector) {-      std::cerr << *pComplexSelector;-    } else {-      std::cerr << "NULL";-    }--    if (newline) {-      std::cerr << std::endl;-    }-  }--  static void printSelsNewSeqPairCollection(SubSetMapLookups& collection, const char* message=NULL, bool newline=true) {--    if (message) {-      std::cerr << message;-    }-    bool first = true;-    std::cerr << "[";-    for(SubSetMapLookup& pair : collection) {-      if (first) {-        first = false;-      } else {-        std::cerr << ", ";-      }-      std::cerr << "[";-      Compound_Selector_Ptr pSels = pair.first;-      Complex_Selector_Ptr pNewSelector = pair.second;-      std::cerr << "[" << *pSels << "], ";-      printComplexSelector(pNewSelector, NULL, false);-    }-    std::cerr << "]";--    if (newline) {-      std::cerr << std::endl;-    }-  }--  // Print a string representation of a ComplexSelectorSet-  static void printSourcesSet(ComplexSelectorSet& sources, const char* message=NULL, bool newline=true) {--    if (message) {-      std::cerr << message;-    }--    // Convert to a deque of strings so we can sort since order doesn't matter in a set. This should cut down on-    // the differences we see when debug printing.-    typedef std::deque<std::string> SourceStrings;-    SourceStrings sourceStrings;-    for (ComplexSelectorSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {-      Complex_Selector_Ptr pSource = *iterator;-      std::stringstream sstream;-      sstream << complexSelectorToNode(pSource);-      sourceStrings.push_back(sstream.str());-    }--    // Sort to get consistent output-    std::sort(sourceStrings.begin(), sourceStrings.end());--    std::cerr << "ComplexSelectorSet[";-    for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) {-      std::string source = *iterator;-      if (iterator != sourceStrings.begin()) {-        std::cerr << ", ";-      }-      std::cerr << source;-    }-    std::cerr << "]";--    if (newline) {-      std::cerr << std::endl;-    }-  }---  std::ostream& operator<<(std::ostream& os, SubSetMapPairs& entries) {-    os << "SUBSET_MAP_ENTRIES[";--    for (SubSetMapPairs::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {-      Complex_Selector_Obj pExtComplexSelector = iterator->first;    // The selector up to where the @extend is (ie, the thing to merge)-      Compound_Selector_Obj pExtCompoundSelector = iterator->second; // The stuff after the @extend--      if (iterator != entries.begin()) {-        os << ", ";-      }--      os << "(";--      if (pExtComplexSelector) {-        std::cerr << *pExtComplexSelector;-      } else {-        std::cerr << "NULL";-      }--      os << " -> ";--      if (pExtCompoundSelector) {-        std::cerr << *pExtCompoundSelector;-      } else {-        std::cerr << "NULL";-      }--      os << ")";--    }--    os << "]";--    return os;-  }-#endif--  static bool parentSuperselector(Complex_Selector_Ptr pOne, Complex_Selector_Ptr pTwo) {-    // TODO: figure out a better way to create a Complex_Selector from scratch-    // TODO: There's got to be a better way. This got ugly quick...-    Element_Selector_Obj fakeParent = SASS_MEMORY_NEW(Element_Selector, ParserState("[FAKE]"), "temp");-    Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);-    fakeHead->elements().push_back(fakeParent);-    Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, NULL /*tail*/);--    pOne->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);-    pTwo->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);--    bool isSuperselector = pOne->is_superselector_of(pTwo);--    pOne->clear_innermost();-    pTwo->clear_innermost();--    return isSuperselector;-  }--  void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out) {-    for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {-      Node& child = *iter;-      out.push_back(nodeToComplexSelector(child));-    }-  }--  Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque) {-    Node result = Node::createCollection();--    for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {-      Complex_Selector_Obj pChild = *iter;-      result.collection()->push_back(complexSelectorToNode(pChild));-    }--    return result;-  }--  class LcsCollectionComparator {-  public:-    LcsCollectionComparator() {}--    bool operator()(Complex_Selector_Obj pOne, Complex_Selector_Obj pTwo, Complex_Selector_Obj& pOut) const {-      /*-      This code is based on the following block from ruby sass' subweave-        do |s1, s2|-          next s1 if s1 == s2-          next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)-          next s2 if parent_superselector?(s1, s2)-          next s1 if parent_superselector?(s2, s1)-        end-      */--      if (*pOne == *pTwo) {-        pOut = pOne;-        return true;-      }--      if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) {-        return false;-      }--      if (parentSuperselector(pOne, pTwo)) {-        pOut = pTwo;-        return true;-      }--      if (parentSuperselector(pTwo, pOne)) {-        pOut = pOne;-        return true;-      }--      return false;-    }-  };---  /*-  This is the equivalent of ruby's Sass::Util.lcs_backtrace.--  # Computes a single longest common subsequence for arrays x and y.-  # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS-  */-  void lcs_backtrace(const LCSTable& c, ComplexSelectorDeque& x, ComplexSelectorDeque& y, int i, int j, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {-    //DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)-    // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output--    if (i == 0 || j == 0) {-      DEBUG_PRINTLN(LCS, "RETURNING EMPTY")-      return;-    }---    Complex_Selector_Obj pCompareOut;-    if (comparator(x[i], y[j], pCompareOut)) {-      DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")-      lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out);-      out.push_back(pCompareOut);-      return;-    }--    if (c[i][j - 1] > c[i - 1][j]) {-      DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")-      lcs_backtrace(c, x, y, i, j - 1, comparator, out);-      return;-    }--    DEBUG_PRINTLN(LCS, "FINAL RETURN")-    lcs_backtrace(c, x, y, i - 1, j, comparator, out);-    return;-  }--  /*-  This is the equivalent of ruby's Sass::Util.lcs_table.--  # Calculates the memoization table for the Least Common Subsequence algorithm.-  # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS-  */-  void lcs_table(const ComplexSelectorDeque& x, const ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, LCSTable& out) {-    //DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)-    // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output--    LCSTable c(x.size(), std::vector<int>(y.size()));--    // These shouldn't be necessary since the vector will be initialized to 0 already.-    // x.size.times {|i| c[i][0] = 0}-    // y.size.times {|j| c[0][j] = 0}--    for (size_t i = 1; i < x.size(); i++) {-      for (size_t j = 1; j < y.size(); j++) {-        Complex_Selector_Obj pCompareOut;--        if (comparator(x[i], y[j], pCompareOut)) {-          c[i][j] = c[i - 1][j - 1] + 1;-        } else {-          c[i][j] = std::max(c[i][j - 1], c[i - 1][j]);-        }-      }-    }--    out = c;-  }--  /*-  This is the equivalent of ruby's Sass::Util.lcs.--  # Computes a single longest common subsequence for `x` and `y`.-  # If there are more than one longest common subsequences,-  # the one returned is that which starts first in `x`.--  # @param x [NodeCollection]-  # @param y [NodeCollection]-  # @comparator An equality check between elements of `x` and `y`.-  # @return [NodeCollection] The LCS--  http://en.wikipedia.org/wiki/Longest_common_subsequence_problem-  */-  void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {-    //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)-    // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output--    x.push_front(NULL);-    y.push_front(NULL);--    LCSTable table;-    lcs_table(x, y, comparator, table);--    return lcs_backtrace(table, x, y, static_cast<int>(x.size()) - 1, static_cast<int>(y.size()) - 1, comparator, out);-  }---  /*-   This is the equivalent of ruby's Sequence.trim.--   The following is the modified version of the ruby code that was more portable to C++. You-   should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.--        # Avoid truly horrific quadratic behavior. TODO: I think there-        # may be a way to get perfect trimming without going quadratic.-        return seqses if seqses.size > 100--        # Keep the results in a separate array so we can be sure we aren't-        # comparing against an already-trimmed selector. This ensures that two-        # identical selectors don't mutually trim one another.-        result = seqses.dup--        # This is n^2 on the sequences, but only comparing between-        # separate sequences should limit the quadratic behavior.-        seqses.each_with_index do |seqs1, i|-          tempResult = []--          for seq1 in seqs1 do-            max_spec = 0-            for seq in _sources(seq1) do-              max_spec = [max_spec, seq.specificity].max-            end---            isMoreSpecificOuter = false-            for seqs2 in result do-              if seqs1.equal?(seqs2) then-                next-              end--              # Second Law of Extend: the specificity of a generated selector-              # should never be less than the specificity of the extending-              # selector.-              #-              # See https://github.com/nex3/sass/issues/324.-              isMoreSpecificInner = false-              for seq2 in seqs2 do-                isMoreSpecificInner = _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)-                if isMoreSpecificInner then-                  break-                end-              end--              if isMoreSpecificInner then-                isMoreSpecificOuter = true-                break-              end-            end--            if !isMoreSpecificOuter then-              tempResult.push(seq1)-            end-          end--          result[i] = tempResult--        end--        result-   */-  /*-   - IMPROVEMENT: We could probably work directly in the output trimmed deque.-   */-  Node Extend::trim(Node& seqses, bool isReplace) {-    // See the comments in the above ruby code before embarking on understanding this function.--    // Avoid poor performance in extreme cases.-    if (seqses.collection()->size() > 100) {-      return seqses;-    }---    DEBUG_PRINTLN(TRIM, "TRIM: " << seqses)---    Node result = Node::createCollection();-    result.plus(seqses);--    DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result)--    // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're-    // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track-    // of the index manually.-    int toTrimIndex = 0;--    for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) {-      Node& seqs1 = *seqsesIter;--      DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex)--      Node tempResult = Node::createCollection();-      tempResult.got_line_feed = seqs1.got_line_feed;--      for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) {-        Node& seq1 = *seqs1Iter;--        Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1);--        // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code-        // for a good description of sources.-        //-        // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test.-        // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We-        // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My-        // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely-        // a guess though.-        unsigned long maxSpecificity = isReplace ? pSeq1->specificity() : 0;-        ComplexSelectorSet sources = pSeq1->sources();--        DEBUG_PRINTLN(TRIM, "TRIM SEQ1: " << seq1)-        DEBUG_EXEC(TRIM, printSourcesSet(sources, "TRIM SOURCES: "))--        for (ComplexSelectorSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {-          const Complex_Selector_Obj& pCurrentSelector = *sourcesSetIterator;-          maxSpecificity = std::max(maxSpecificity, pCurrentSelector->specificity());-        }--        DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity)--        bool isMoreSpecificOuter = false;--        int resultIndex = 0;--        for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) {-          Node& seqs2 = *resultIter;--          DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1)-          DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2)--          // Do not compare the same sequence to itself. The ruby call we're trying to-          // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision.-          // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is-          // derived from seqses and seqs2 is derived from result.-          if (seqs1.collection() == seqs2.collection()) {-            DEBUG_PRINTLN(TRIM, "CONTINUE")-            continue;-          }--          bool isMoreSpecificInner = false;--          for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) {-            Node& seq2 = *seqs2Iter;--            Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2);--            DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity())-            DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))-            DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false"))--            isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1);--            if (isMoreSpecificInner) {-              DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC")-              break;-            }-          }--          // If we found something more specific, we're done. Let the outer loop know and stop iterating.-          if (isMoreSpecificInner) {-            isMoreSpecificOuter = true;-            break;-          }--          resultIndex++;-        }--        if (!isMoreSpecificOuter) {-          DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1)-          tempResult.collection()->push_back(seq1);-        }--      }--      DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result)-      DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult)-      (*result.collection())[toTrimIndex] = tempResult;--      toTrimIndex++;--      DEBUG_PRINTLN(TRIM, "RESULT: " << result)-    }--    return result;-  }----  static bool parentSuperselector(const Node& one, const Node& two) {-    // TODO: figure out a better way to create a Complex_Selector from scratch-    // TODO: There's got to be a better way. This got ugly quick...-    Element_Selector_Obj fakeParent = SASS_MEMORY_NEW(Element_Selector, ParserState("[FAKE]"), "temp");-    Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);-    fakeHead->elements().push_back(fakeParent);-    Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, NULL /*tail*/);--    Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one);-    pOneWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);-    Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two);-    pTwoWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);--    return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);-  }---  class ParentSuperselectorChunker {-  public:-    ParentSuperselectorChunker(Node& lcs) : mLcs(lcs) {}-    Node& mLcs;--    bool operator()(const Node& seq) const {-      // {|s| parent_superselector?(s.first, lcs.first)}-      if (seq.collection()->size() == 0) return false;-      return parentSuperselector(seq.collection()->front(), mLcs.collection()->front());-    }-  };--  class SubweaveEmptyChunker {-  public:-    bool operator()(const Node& seq) const {-      // {|s| s.empty?}--      return seq.collection()->empty();-    }-  };--  /*-  # Takes initial subsequences of `seq1` and `seq2` and returns all-  # orderings of those subsequences. The initial subsequences are determined-  # by a block.-  #-  # Destructively removes the initial subsequences of `seq1` and `seq2`.-  #-  # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`-  # denoting the boundary of the initial subsequence), this would return-  # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and-  # `(3 4 5)`.-  #-  # @param seq1 [Array]-  # @param seq2 [Array]-  # @yield [a] Used to determine when to cut off the initial subsequences.-  #   Called repeatedly for each sequence until it returns true.-  # @yieldparam a [Array] A final subsequence of one input sequence after-  #   cutting off some initial subsequence.-  # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence-  #   here.-  # @return [Array<Array>] All possible orderings of the initial subsequences.-  def chunks(seq1, seq2)-    chunk1 = []-    chunk1 << seq1.shift until yield seq1-    chunk2 = []-    chunk2 << seq2.shift until yield seq2-    return [] if chunk1.empty? && chunk2.empty?-    return [chunk2] if chunk1.empty?-    return [chunk1] if chunk2.empty?-    [chunk1 + chunk2, chunk2 + chunk1]-  end-  */-  template<typename ChunkerType>-  static Node chunks(Node& seq1, Node& seq2, const ChunkerType& chunker) {-    Node chunk1 = Node::createCollection();-    while (seq1.collection()->size() && !chunker(seq1)) {-      chunk1.collection()->push_back(seq1.collection()->front());-      seq1.collection()->pop_front();-    }--    Node chunk2 = Node::createCollection();-    while (!seq2.collection()->empty() && !chunker(seq2)) {-      chunk2.collection()->push_back(seq2.collection()->front());-      seq2.collection()->pop_front();-    }--    if (chunk1.collection()->empty() && chunk2.collection()->empty()) {-      DEBUG_PRINTLN(CHUNKS, "RETURNING BOTH EMPTY")-      return Node::createCollection();-    }--    if (chunk1.collection()->empty()) {-      Node chunk2Wrapper = Node::createCollection();-      chunk2Wrapper.collection()->push_back(chunk2);-      DEBUG_PRINTLN(CHUNKS, "RETURNING ONE EMPTY")-      return chunk2Wrapper;-    }--    if (chunk2.collection()->empty()) {-      Node chunk1Wrapper = Node::createCollection();-      chunk1Wrapper.collection()->push_back(chunk1);-      DEBUG_PRINTLN(CHUNKS, "RETURNING TWO EMPTY")-      return chunk1Wrapper;-    }--    Node perms = Node::createCollection();--    Node firstPermutation = Node::createCollection();-    firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());-    firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());-    perms.collection()->push_back(firstPermutation);--    Node secondPermutation = Node::createCollection();-    secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());-    secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());-    perms.collection()->push_back(secondPermutation);--    DEBUG_PRINTLN(CHUNKS, "RETURNING PERM")--    return perms;-  }---  static Node groupSelectors(Node& seq) {-    Node newSeq = Node::createCollection();--    Node tail = Node::createCollection();-    tail.plus(seq);--    while (!tail.collection()->empty()) {-      Node head = Node::createCollection();--      do {-        head.collection()->push_back(tail.collection()->front());-        tail.collection()->pop_front();-      } while (!tail.collection()->empty() && (head.collection()->back().isCombinator() || tail.collection()->front().isCombinator()));--      newSeq.collection()->push_back(head);-    }--    return newSeq;-  }---  static void getAndRemoveInitialOps(Node& seq, Node& ops) {-    NodeDeque& seqCollection = *(seq.collection());-    NodeDeque& opsCollection = *(ops.collection());--    while (seqCollection.size() > 0 && seqCollection.front().isCombinator()) {-      opsCollection.push_back(seqCollection.front());-      seqCollection.pop_front();-    }-  }---  static void getAndRemoveFinalOps(Node& seq, Node& ops) {-    NodeDeque& seqCollection = *(seq.collection());-    NodeDeque& opsCollection = *(ops.collection());--    while (seqCollection.size() > 0 && seqCollection.back().isCombinator()) {-      opsCollection.push_back(seqCollection.back()); // Purposefully reversed to match ruby code-      seqCollection.pop_back();-    }-  }---  /*-      def merge_initial_ops(seq1, seq2)-        ops1, ops2 = [], []-        ops1 << seq1.shift while seq1.first.is_a?(String)-        ops2 << seq2.shift while seq2.first.is_a?(String)--        newline = false-        newline ||= !!ops1.shift if ops1.first == "\n"-        newline ||= !!ops2.shift if ops2.first == "\n"--        # If neither sequence is a subsequence of the other, they cannot be-        # merged successfully-        lcs = Sass::Util.lcs(ops1, ops2)-        return unless lcs == ops1 || lcs == ops2-        return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)-      end-  */-  static Node mergeInitialOps(Node& seq1, Node& seq2) {-    Node ops1 = Node::createCollection();-    Node ops2 = Node::createCollection();--    getAndRemoveInitialOps(seq1, ops1);-    getAndRemoveInitialOps(seq2, ops2);--    // TODO: Do we have this information available to us?-    // newline = false-    // newline ||= !!ops1.shift if ops1.first == "\n"-    // newline ||= !!ops2.shift if ops2.first == "\n"--    // If neither sequence is a subsequence of the other, they cannot be merged successfully-    DefaultLcsComparator lcsDefaultComparator;-    Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator);--    if (!(opsLcs == ops1 || opsLcs == ops2)) {-      return Node::createNil();-    }--    // TODO: more newline logic-    // return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)--    return (ops1.collection()->size() > ops2.collection()->size() ? ops1 : ops2);-  }---  /*-      def merge_final_ops(seq1, seq2, res = [])---        # This code looks complicated, but it's actually just a bunch of special-        # cases for interactions between different combinators.-        op1, op2 = ops1.first, ops2.first-        if op1 && op2-          sel1 = seq1.pop-          sel2 = seq2.pop-          if op1 == '~' && op2 == '~'-            if sel1.superselector?(sel2)-              res.unshift sel2, '~'-            elsif sel2.superselector?(sel1)-              res.unshift sel1, '~'-            else-              merged = sel1.unify(sel2.members, sel2.subject?)-              res.unshift [-                [sel1, '~', sel2, '~'],-                [sel2, '~', sel1, '~'],-                ([merged, '~'] if merged)-              ].compact-            end-          elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')-            if op1 == '~'-              tilde_sel, plus_sel = sel1, sel2-            else-              tilde_sel, plus_sel = sel2, sel1-            end--            if tilde_sel.superselector?(plus_sel)-              res.unshift plus_sel, '+'-            else-              merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)-              res.unshift [-                [tilde_sel, '~', plus_sel, '+'],-                ([merged, '+'] if merged)-              ].compact-            end-          elsif op1 == '>' && %w[~ +].include?(op2)-            res.unshift sel2, op2-            seq1.push sel1, op1-          elsif op2 == '>' && %w[~ +].include?(op1)-            res.unshift sel1, op1-            seq2.push sel2, op2-          elsif op1 == op2-            return unless merged = sel1.unify(sel2.members, sel2.subject?)-            res.unshift merged, op1-          else-            # Unknown selector combinators can't be unified-            return-          end-          return merge_final_ops(seq1, seq2, res)-        elsif op1-          seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last)-          res.unshift seq1.pop, op1-          return merge_final_ops(seq1, seq2, res)-        else # op2-          seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last)-          res.unshift seq2.pop, op2-          return merge_final_ops(seq1, seq2, res)-        end-      end-  */-  static Node mergeFinalOps(Node& seq1, Node& seq2, Node& res) {--    Node ops1 = Node::createCollection();-    Node ops2 = Node::createCollection();--    getAndRemoveFinalOps(seq1, ops1);-    getAndRemoveFinalOps(seq2, ops2);--    // TODO: do we have newlines to remove?-    // ops1.reject! {|o| o == "\n"}-    // ops2.reject! {|o| o == "\n"}--    if (ops1.collection()->empty() && ops2.collection()->empty()) {-      return res;-    }--    if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) {-      DefaultLcsComparator lcsDefaultComparator;-      Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator);--      // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up.--      if (!(opsLcs == ops1 || opsLcs == ops2)) {-        return Node::createNil();-      }--      if (ops1.collection()->size() > ops2.collection()->size()) {-        res.collection()->insert(res.collection()->begin(), ops1.collection()->rbegin(), ops1.collection()->rend());-      } else {-        res.collection()->insert(res.collection()->begin(), ops2.collection()->rbegin(), ops2.collection()->rend());-      }--      return res;-    }--    if (!ops1.collection()->empty() && !ops2.collection()->empty()) {--      Node op1 = ops1.collection()->front();-      Node op2 = ops2.collection()->front();--      Node sel1 = seq1.collection()->back();-      seq1.collection()->pop_back();--      Node sel2 = seq2.collection()->back();-      seq2.collection()->pop_back();--      if (op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::PRECEDES) {--        if (sel1.selector()->is_superselector_of(sel2.selector())) {--          res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);-          res.collection()->push_front(sel2);--        } else if (sel2.selector()->is_superselector_of(sel1.selector())) {--          res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);-          res.collection()->push_front(sel1);--        } else {--          DEBUG_PRINTLN(ALL, "sel1: " << sel1)-          DEBUG_PRINTLN(ALL, "sel2: " << sel2)--          Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result-          // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)-          Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head());-          pMergedWrapper->head(pMerged);--          DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))--          Node newRes = Node::createCollection();--          Node firstPerm = Node::createCollection();-          firstPerm.collection()->push_back(sel1);-          firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));-          firstPerm.collection()->push_back(sel2);-          firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));-          newRes.collection()->push_back(firstPerm);--          Node secondPerm = Node::createCollection();-          secondPerm.collection()->push_back(sel2);-          secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));-          secondPerm.collection()->push_back(sel1);-          secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));-          newRes.collection()->push_back(secondPerm);--          if (pMerged) {-            Node mergedPerm = Node::createCollection();-            mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper));-            mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));-            newRes.collection()->push_back(mergedPerm);-          }--          res.collection()->push_front(newRes);--          DEBUG_PRINTLN(ALL, "RESULT: " << res)--        }--      } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) {--          Node tildeSel = sel1;-          Node plusSel = sel2;-          Node plusOp = op2;-          if (op1.combinator() != Complex_Selector::PRECEDES) {-            tildeSel = sel2;-            plusSel = sel1;-            plusOp = op1;-          }--          if (tildeSel.selector()->is_superselector_of(plusSel.selector())) {--            res.collection()->push_front(plusOp);-            res.collection()->push_front(plusSel);--          } else {--            DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel)-            DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel)--            Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(plusSel.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result-            // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)-            Compound_Selector_Ptr pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head());-            pMergedWrapper->head(pMerged);--            DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))--            Node newRes = Node::createCollection();--            Node firstPerm = Node::createCollection();-            firstPerm.collection()->push_back(tildeSel);-            firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));-            firstPerm.collection()->push_back(plusSel);-            firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));-            newRes.collection()->push_back(firstPerm);--            if (pMerged) {-              Node mergedPerm = Node::createCollection();-              mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper));-              mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));-              newRes.collection()->push_back(mergedPerm);-            }--            res.collection()->push_front(newRes);--            DEBUG_PRINTLN(ALL, "RESULT: " << res)--          }-      } else if (op1.combinator() == Complex_Selector::PARENT_OF && (op2.combinator() == Complex_Selector::PRECEDES || op2.combinator() == Complex_Selector::ADJACENT_TO)) {--        res.collection()->push_front(op2);-        res.collection()->push_front(sel2);--        seq1.collection()->push_back(sel1);-        seq1.collection()->push_back(op1);--      } else if (op2.combinator() == Complex_Selector::PARENT_OF && (op1.combinator() == Complex_Selector::PRECEDES || op1.combinator() == Complex_Selector::ADJACENT_TO)) {--        res.collection()->push_front(op1);-        res.collection()->push_front(sel1);--        seq2.collection()->push_back(sel2);-        seq2.collection()->push_back(op2);--      } else if (op1.combinator() == op2.combinator()) {--        DEBUG_PRINTLN(ALL, "sel1: " << sel1)-        DEBUG_PRINTLN(ALL, "sel2: " << sel2)--        Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result-        // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)-        Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head());-        pMergedWrapper->head(pMerged);--        DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))--        if (!pMerged) {-          return Node::createNil();-        }--        res.collection()->push_front(op1);-        res.collection()->push_front(Node::createSelector(pMergedWrapper));--        DEBUG_PRINTLN(ALL, "RESULT: " << res)--      } else {-        return Node::createNil();-      }--      return mergeFinalOps(seq1, seq2, res);--    } else if (!ops1.collection()->empty()) {--      Node op1 = ops1.collection()->front();--      if (op1.combinator() == Complex_Selector::PARENT_OF && !seq2.collection()->empty() && seq2.collection()->back().selector()->is_superselector_of(seq1.collection()->back().selector())) {-        seq2.collection()->pop_back();-      }--      // TODO: consider unshift(NodeCollection, Node)-      res.collection()->push_front(op1);-      res.collection()->push_front(seq1.collection()->back());-      seq1.collection()->pop_back();--      return mergeFinalOps(seq1, seq2, res);--    } else { // !ops2.collection()->empty()--      Node op2 = ops2.collection()->front();--      if (op2.combinator() == Complex_Selector::PARENT_OF && !seq1.collection()->empty() && seq1.collection()->back().selector()->is_superselector_of(seq2.collection()->back().selector())) {-        seq1.collection()->pop_back();-      }--      res.collection()->push_front(op2);-      res.collection()->push_front(seq2.collection()->back());-      seq2.collection()->pop_back();--      return mergeFinalOps(seq1, seq2, res);--    }--  }---  /*-    This is the equivalent of ruby's Sequence.subweave.--    Here is the original subweave code for reference during porting.--      def subweave(seq1, seq2)-        return [seq2] if seq1.empty?-        return [seq1] if seq2.empty?--        seq1, seq2 = seq1.dup, seq2.dup-        return unless init = merge_initial_ops(seq1, seq2)-        return unless fin = merge_final_ops(seq1, seq2)-        seq1 = group_selectors(seq1)-        seq2 = group_selectors(seq2)-        lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|-          next s1 if s1 == s2-          next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)-          next s2 if parent_superselector?(s1, s2)-          next s1 if parent_superselector?(s2, s1)-        end--        diff = [[init]]-        until lcs.empty?-          diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]-          seq1.shift-          seq2.shift-        end-        diff << chunks(seq1, seq2) {|s| s.empty?}-        diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}-        diff.reject! {|c| c.empty?}--        result = Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}--        result-      end-  */-  Node subweave(Node& one, Node& two) {-    // Check for the simple cases-    if (one.collection()->size() == 0) {-      Node out = Node::createCollection();-      out.collection()->push_back(two);-      return out;-    }-    if (two.collection()->size() == 0) {-      Node out = Node::createCollection();-      out.collection()->push_back(one);-      return out;-    }--    Node seq1 = Node::createCollection();-    seq1.plus(one);-    Node seq2 = Node::createCollection();-    seq2.plus(two);--    DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1)-    DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2)--    Node init = mergeInitialOps(seq1, seq2);-    if (init.isNil()) {-      return Node::createNil();-    }--    DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init)--    Node res = Node::createCollection();-    Node fin = mergeFinalOps(seq1, seq2, res);-    if (fin.isNil()) {-      return Node::createNil();-    }--    DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin)---    // Moving this line up since fin isn't modified between now and when it happened before-    // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}--    for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end();-           finIter != finEndIter; ++finIter) {--      Node& childNode = *finIter;--      if (!childNode.isCollection()) {-        Node wrapper = Node::createCollection();-        wrapper.collection()->push_back(childNode);-        childNode = wrapper;-      }--    }--    DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin)----    Node groupSeq1 = groupSelectors(seq1);-    DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1)--    Node groupSeq2 = groupSelectors(seq2);-    DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2)---    ComplexSelectorDeque groupSeq1Converted;-    nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted);--    ComplexSelectorDeque groupSeq2Converted;-    nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted);--    ComplexSelectorDeque out;-    LcsCollectionComparator collectionComparator;-    lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, out);-    Node seqLcs = complexSelectorDequeToNode(out);--    DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs)---    Node initWrapper = Node::createCollection();-    initWrapper.collection()->push_back(init);-    Node diff = Node::createCollection();-    diff.collection()->push_back(initWrapper);--    DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff)---    while (!seqLcs.collection()->empty()) {-      ParentSuperselectorChunker superselectorChunker(seqLcs);-      Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker);-      diff.collection()->push_back(chunksResult);--      Node lcsWrapper = Node::createCollection();-      lcsWrapper.collection()->push_back(seqLcs.collection()->front());-      seqLcs.collection()->pop_front();-      diff.collection()->push_back(lcsWrapper);--      if (groupSeq1.collection()->size()) groupSeq1.collection()->pop_front();-      if (groupSeq2.collection()->size()) groupSeq2.collection()->pop_front();-    }--    DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff)---    DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2)---    SubweaveEmptyChunker emptyChunker;-    Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker);-    diff.collection()->push_back(chunksResult);---    DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff)---    diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end());--    DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff)--    // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection)-    Node diffFiltered = Node::createCollection();-    for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end();-           diffIter != diffEndIter; ++diffIter) {-      Node& node = *diffIter;-      if (node.collection() && !node.collection()->empty()) {-        diffFiltered.collection()->push_back(node);-      }-    }-    diff = diffFiltered;--    DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff)---    Node pathsResult = paths(diff);--    DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult)---    // We're flattening in place-    for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end();-      pathsIter != pathsEndIter; ++pathsIter) {--      Node& child = *pathsIter;-      child = flatten(child);-    }--    DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult)---    /*-      TODO: implement-      rejected = mapped.reject {|p| path_has_two_subjects?(p)}-      $stderr.puts "REJECTED: #{rejected}"-     */---    return pathsResult;--  }-  /*-  // disabled to avoid clang warning [-Wunused-function]-  static Node subweaveNaive(const Node& one, const Node& two) {-    Node out = Node::createCollection();--    // Check for the simple cases-    if (one.isNil()) {-      out.collection()->push_back(two.klone());-    } else if (two.isNil()) {-      out.collection()->push_back(one.klone());-    } else {-      // Do the naive implementation. pOne = A B and pTwo = C D ...yields...  A B C D and C D A B-      // See https://gist.github.com/nex3/7609394 for details.--      Node firstPerm = one.klone();-      Node twoCloned = two.klone();-      firstPerm.plus(twoCloned);-      out.collection()->push_back(firstPerm);--      Node secondPerm = two.klone();-      Node oneCloned = one.klone();-      secondPerm.plus(oneCloned );-      out.collection()->push_back(secondPerm);-    }--    return out;-  }-  */---  /*-   This is the equivalent of ruby's Sequence.weave.--   The following is the modified version of the ruby code that was more portable to C++. You-   should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.--      def weave(path)-        # This function works by moving through the selector path left-to-right,-        # building all possible prefixes simultaneously. These prefixes are-        # `befores`, while the remaining parenthesized suffixes is `afters`.-        befores = [[]]-        afters = path.dup--        until afters.empty?-          current = afters.shift.dup-          last_current = [current.pop]--          tempResult = []--          for before in befores do-            sub = subweave(before, current)-            if sub.nil?-              next-            end--            for seqs in sub do-              tempResult.push(seqs + last_current)-            end-          end--          befores = tempResult--        end--        return befores-      end-   */-  /*-      def weave(path)-        befores = [[]]-        afters = path.dup--        until afters.empty?-          current = afters.shift.dup--          last_current = [current.pop]---          tempResult = []--          for before in befores do-            sub = subweave(before, current)--            if sub.nil?-              next []-            end---            for seqs in sub do-              toPush = seqs + last_current--              tempResult.push(seqs + last_current)-            end--          end--          befores = tempResult--        end--        return befores-      end-  */-  Node Extend::weave(Node& path) {--    DEBUG_PRINTLN(WEAVE, "WEAVE: " << path)--    Node befores = Node::createCollection();-    befores.collection()->push_back(Node::createCollection());--    Node afters = Node::createCollection();-    afters.plus(path);--    while (!afters.collection()->empty()) {-      Node current = afters.collection()->front().klone();-      afters.collection()->pop_front();-      DEBUG_PRINTLN(WEAVE, "CURRENT: " << current)-      if (current.collection()->size() == 0) continue;--      Node last_current = Node::createCollection();-      last_current.collection()->push_back(current.collection()->back());-      current.collection()->pop_back();-      DEBUG_PRINTLN(WEAVE, "CURRENT POST POP: " << current)-      DEBUG_PRINTLN(WEAVE, "LAST CURRENT: " << last_current)--      Node tempResult = Node::createCollection();--      for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) {-        Node& before = *beforesIter;--        Node sub = subweave(before, current);--        DEBUG_PRINTLN(WEAVE, "SUB: " << sub)--        if (sub.isNil()) {-          return Node::createCollection();-        }--        for (NodeDeque::iterator subIter = sub.collection()->begin(), subEndIter = sub.collection()->end(); subIter != subEndIter; subIter++) {-          Node& seqs = *subIter;--          Node toPush = Node::createCollection();-          toPush.plus(seqs);-          toPush.plus(last_current);--          // move line feed from inner to outer selector (very hacky indeed)-          if (last_current.collection() && last_current.collection()->front().selector()) {-            toPush.got_line_feed = last_current.collection()->front().got_line_feed;-            last_current.collection()->front().selector()->has_line_feed(false);-            last_current.collection()->front().got_line_feed = false;-          }--          tempResult.collection()->push_back(toPush);--        }-      }--      befores = tempResult;--    }--    return befores;-  }----  /*-   This is the equivalent of ruby's SimpleSequence.do_extend.--    // TODO: I think I have some modified ruby code to put here. Check.-  */-  /*-   ISSUES:-   - Previous TODO: Do we need to group the results by extender?-   - What does subject do in?: next unless unified = seq.members.last.unify(self_without_sel, subject?)-   - IMPROVEMENT: The search for uniqueness at the end is not ideal since it's has to loop over everything...-   - IMPROVEMENT: Check if the final search for uniqueness is doing anything that extendComplexSelector isn't already doing...-   */-  template<typename KeyType>-  class GroupByToAFunctor {-  public:-    KeyType operator()(SubSetMapPair& extPair) const {-      Complex_Selector_Obj pSelector = extPair.first;-      return pSelector;-    }-  };-  Node Extend::extendCompoundSelector(Compound_Selector_Ptr pSelector, CompoundSelectorSet& seen, bool isReplace) {--    /* this turned out to be too much overhead-       probably due to holding a "Node" object-    // check if we already extended this selector-    // we can do this since subset_map is "static"-    auto memoized = memoizeCompound.find(pSelector);-    if (memoized != memoizeCompound.end()) {-      return memoized->second.klone();-    }-    */--    DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))-    // TODO: Ruby has another loop here to skip certain members?--    // let RESULTS be an empty list of complex selectors-    Node results = Node::createCollection();-    // extendedSelectors.got_line_feed = true;--    SubSetMapPairs entries = subset_map.get_v(pSelector);--    GroupByToAFunctor<Complex_Selector_Obj> extPairKeyFunctor;-    SubSetMapResults arr;-    group_by_to_a(entries, extPairKeyFunctor, arr);--    SubSetMapLookups holder;--    // for each (EXTENDER, TARGET) in MAP.get(COMPOUND):-    for (SubSetMapResult& groupedPair : arr) {--      Complex_Selector_Obj seq = groupedPair.first;-      SubSetMapPairs& group = groupedPair.second;--      DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(seq, "SEQ: "))--      Compound_Selector_Obj pSels = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());-      for (SubSetMapPair& pair : group) {-        pair.second->extended(true);-        pSels->concat(pair.second);-      }--      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))--      // The selector up to where the @extend is (ie, the thing to merge)-      Complex_Selector_Ptr pExtComplexSelector = seq;--      // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL?-      // RUBY: self_without_sel = Sass::Util.array_minus(members, sels)-      Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(pSels);--      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: "))-      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: "))--      Compound_Selector_Obj pInnermostCompoundSelector = pExtComplexSelector->last()->head();--      if (!pInnermostCompoundSelector) {-        pInnermostCompoundSelector = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());-      }-      Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors);--      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: "))-      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))-      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: "))--      // RUBY: next unless unified-      if (!pUnifiedSelector || pUnifiedSelector->length() == 0) {-        continue;-      }--      // TODO: implement the parent directive match (if necessary based on test failures)-      // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?--      // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just-      // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more-      // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered-      // out and aren't operated on.-      Complex_Selector_Obj pNewSelector = SASS_MEMORY_CLONE(pExtComplexSelector); // ->first();--      Complex_Selector_Obj pNewInnerMost = SASS_MEMORY_NEW(Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL);--      Complex_Selector::Combinator combinator = pNewSelector->clear_innermost();-      pNewSelector->set_innermost(pNewInnerMost, combinator);--#ifdef DEBUG-      ComplexSelectorSet debugSet;-      debugSet = pNewSelector->sources();-      if (debugSet.size() > 0) {-        throw std::runtime_error("The new selector should start with no sources. Something needs to be cloned to fix this.");-      }-      debugSet = pExtComplexSelector->sources();-      if (debugSet.size() > 0) {-        throw std::runtime_error("The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this.");-      }-#endif---      // if (pSelector && pSelector->has_line_feed()) pNewInnerMost->has_line_feed(true);-      // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending.-      DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector))--      DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, "SOURCES NEW SEQ BEGIN: "))--      // I actually want to create a copy here (performance!)-      ComplexSelectorSet newSourcesSet = pSelector->sources(); // XXX-      DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES THIS EXTEND: "))--      newSourcesSet.insert(pExtComplexSelector);-      DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES WITH NEW SOURCE: "))--      // RUBY: new_seq.add_sources!(sources + [seq])-      pNewSelector->addSources(newSourcesSet);--      DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet newSet = pNewSelector->sources(); printSourcesSet(newSet, "SOURCES ON NEW SELECTOR AFTER ADD: "))-      DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))---      if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);--      holder.push_back(std::make_pair(pSels, pNewSelector));-    }---    for (SubSetMapLookup& pair : holder) {--      Compound_Selector_Obj pSels = pair.first;-      Complex_Selector_Obj pNewSelector = pair.second;---      // RUBY??: next [] if seen.include?(sels)-      if (seen.find(pSels) != seen.end()) {-        continue;-      }---      CompoundSelectorSet recurseSeen(seen);-      recurseSeen.insert(pSels);---      DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector))-      Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, recurseSeen, isReplace, false); // !:isOriginal--      DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors)--      for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end();-           iterator != endIterator; ++iterator) {-        Node newSelector = *iterator;--//        DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << results)-//        DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << results.contains(newSelector, false /*simpleSelectorOrderDependent*/));--        if (!results.contains(newSelector)) {-//          DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR")-          results.collection()->push_back(newSelector);-        }-      }-    }--    DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: "))--    // this turned out to be too much overhead-    // memory results in a map table - since extending is very expensive-    // memoizeCompound.insert(std::pair<Compound_Selector_Obj, Node>(pSelector, results));--    return results;-  }---  // check if selector has something to be extended by subset_map-  bool Extend::complexSelectorHasExtension(Complex_Selector_Ptr selector, CompoundSelectorSet& seen) {--    bool hasExtension = false;--    Complex_Selector_Obj pIter = selector;--    while (!hasExtension && pIter) {-      Compound_Selector_Obj pHead = pIter->head();--      if (pHead) {-        SubSetMapPairs entries = subset_map.get_v(pHead);-        for (SubSetMapPair ext : entries) {-          // check if both selectors have the same media block parent-          // if (ext.first->media_block() == pComplexSelector->media_block()) continue;-          if (ext.second->media_block() == 0) continue;-          if (pHead->media_block() &&-              ext.second->media_block()->media_queries() &&-              pHead->media_block()->media_queries()-          ) {-            std::string query_left(ext.second->media_block()->media_queries()->to_string());-            std::string query_right(pHead->media_block()->media_queries()->to_string());-            if (query_left == query_right) continue;-          }--          // fail if one goes across media block boundaries-          std::stringstream err;-          std::string cwd(Sass::File::get_cwd());-          ParserState pstate(ext.second->pstate());-          std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));-          err << "You may not @extend an outer selector from within @media.\n";-          err << "You may only @extend selectors within the same directive.\n";-          err << "From \"@extend " << ext.second->to_string() << "\"";-          err << " on line " << pstate.line+1 << " of " << rel_path << "\n";-          error(err.str(), selector->pstate(), eval->exp.traces);-        }-        if (entries.size() > 0) hasExtension = true;-      }--      pIter = pIter->tail();-    }--    return hasExtension;-  }---  /*-   This is the equivalent of ruby's Sequence.do_extend.--   // TODO: I think I have some modified ruby code to put here. Check.-   */-  /*-   ISSUES:-   - check to automatically include combinators doesn't transfer over to libsass' data model where-     the combinator and compound selector are one unit-     next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)-   */-  Node Extend::extendComplexSelector(Complex_Selector_Ptr selector, CompoundSelectorSet& seen, bool isReplace, bool isOriginal) {--    // check if we already extended this selector-    // we can do this since subset_map is "static"-    auto memoized = memoizeComplex.find(selector);-    if (memoized != memoizeComplex.end()) {-      return memoized->second;-    }--    // convert the input selector to extend node format-    Node complexSelector = complexSelectorToNode(selector);-    DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector)--    // let CHOICES be an empty list of selector-lists-    // create new collection to hold the results-    Node choices = Node::createCollection();--    // for each compound selector COMPOUND in COMPLEX:-    for (Node& sseqOrOp : *complexSelector.collection()) {--      DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp)--      // If it's not a selector (meaning it's a combinator), just include it automatically-      // RUBY: next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)-      if (!sseqOrOp.isSelector()) {-        // Wrap our Combinator in two collections to match ruby. This is essentially making a collection Node-        // with one collection child. The collection child represents a Complex_Selector that is only a combinator.-        Node outer = Node::createCollection();-        Node inner = Node::createCollection();-        outer.collection()->push_back(inner);-        inner.collection()->push_back(sseqOrOp);-        choices.collection()->push_back(outer);-        continue;-      }--      // verified now that node is a valid selector-      Complex_Selector_Obj sseqSel = sseqOrOp.selector();-      Compound_Selector_Obj sseqHead = sseqSel->head();--      // let EXTENDED be extend_compound(COMPOUND, SEEN)-      // extend the compound selector against the given subset_map-      // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)-      Node extended = extendCompoundSelector(sseqHead, seen, isReplace); // slow(17%)!-      if (sseqOrOp.got_line_feed) extended.got_line_feed = true;-      DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended)--      // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with a ruby-      // Array instead of a Sequence due to the member mapping: choices = extended.map {|seq| seq.members}-      // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder?-      if (isOriginal && !selector->has_placeholder()) {-        ComplexSelectorSet srcset;-        srcset.insert(selector);-        sseqSel->addSources(srcset);-        // DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector)-      }--      bool isSuperselector = false;-      // if no complex selector in EXTENDED is a superselector of COMPOUND:-      for (Node& childNode : *extended.collection()) {-        Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode);-        if (pExtensionSelector->is_superselector_of(sseqSel)) {-          isSuperselector = true;-          break;-        }-      }--      if (!isSuperselector) {-        // add a complex selector composed only of COMPOUND to EXTENDED-        if (sseqOrOp.got_line_feed) sseqSel->has_line_feed(sseqOrOp.got_line_feed);-        extended.collection()->push_front(complexSelectorToNode(sseqSel));-      }--      DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)--      // add EXTENDED to CHOICES-      // Aggregate our current extensions-      choices.collection()->push_back(extended);-    }---    DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << choices)----    // Ruby Equivalent: paths-    Node paths = Sass::paths(choices);--    DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths)--    // let WEAVES be an empty list of selector lists-    Node weaves = Node::createCollection();--    // for each list of complex selectors PATH in paths(CHOICES):-    for (Node& path : *paths.collection()) {-      // add weave(PATH) to WEAVES-      Node weaved = weave(path); // slow(12%)!-      weaved.got_line_feed = path.got_line_feed;-      weaves.collection()->push_back(weaved);-    }--    DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves)--    // Ruby Equivalent: trim-    Node trimmed(trim(weaves, isReplace)); // slow(19%)!--    DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed)--    // Ruby Equivalent: flatten-    Node flattened(flatten(trimmed, 1));--    DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors)-    DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector)--    // memory results in a map table - since extending is very expensive-    memoizeComplex.insert(std::pair<Complex_Selector_Obj, Node>(selector, flattened));--    // return trim(WEAVES)-    return flattened;-  }----  /*-   This is the equivalent of ruby's CommaSequence.do_extend.-  */-  // We get a selector list with has something to extend and a subset_map with-  // all extenders. Pick the ones that match our selectors in the list.-  Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen) {--    Selector_List_Obj pNewSelectors = SASS_MEMORY_NEW(Selector_List, pSelectorList->pstate(), pSelectorList->length());--    // check if we already extended this selector-    // we can do this since subset_map is "static"-    auto memoized = memoizeList.find(pSelectorList);-    if (memoized != memoizeList.end()) {-      extendedSomething = true;-      return memoized->second;-    }--    extendedSomething = false;-    // process each comlplex selector in the selector list.-    // Find the ones that can be extended by given subset_map.-    for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {-      Complex_Selector_Obj pSelector = (*pSelectorList)[index];--      // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that.-      // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to-      // run through the extend code (which does a data model transformation), check if there is anything to extend before doing-      // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps-      // when debugging).-      if (!complexSelectorHasExtension(pSelector, seen)) {-        pNewSelectors->append(pSelector);-        continue;-      }--      // complexSelectorHasExtension was true!-      extendedSomething = true;--      // now do the actual extension of the complex selector-      Node extendedSelectors = extendComplexSelector(pSelector, seen, isReplace, true);--      if (!pSelector->has_placeholder()) {-        Node nSelector(complexSelectorToNode(pSelector));-        if (!extendedSelectors.contains(nSelector)) {-          pNewSelectors->append(pSelector);-          continue;-        }-      }--      bool doReplace = isReplace;-      for (Node& childNode : *extendedSelectors.collection()) {-        // When it is a replace, skip the first one, unless there is only one-        if(doReplace && extendedSelectors.collection()->size() > 1 ) {-          doReplace = false;-          continue;-        }-        pNewSelectors->append(nodeToComplexSelector(childNode));-      }-    }--    Remove_Placeholders remove_placeholders;-    // it seems that we have to remove the place holders early here-    // normally we do this as the very last step (compare to ruby sass)-    pNewSelectors = remove_placeholders.remove_placeholders(pNewSelectors);--    // unwrap all wrapped selectors with inner lists-    for (Complex_Selector_Obj cur : pNewSelectors->elements()) {-      // process tails-      while (cur) {-        // process header-        if (cur->head() && seen.find(cur->head()) == seen.end()) {-          CompoundSelectorSet recseen(seen);-          recseen.insert(cur->head());-          // create a copy since we add multiple items if stuff get unwrapped-          Compound_Selector_Obj cpy_head = SASS_MEMORY_NEW(Compound_Selector, cur->pstate());-          for (Simple_Selector_Obj hs : *cur->head()) {-            if (Wrapped_Selector_Obj ws = Cast<Wrapped_Selector>(hs)) {-              ws->selector(SASS_MEMORY_CLONE(ws->selector()));-              if (Selector_List_Obj sl = Cast<Selector_List>(ws->selector())) {-                // special case for ruby ass-                if (sl->empty()) {-                  // this seems inconsistent but it is how ruby sass seems to remove parentheses-                  cpy_head->append(SASS_MEMORY_NEW(Element_Selector, hs->pstate(), ws->name()));-                }-                // has wrapped not selectors-                else if (ws->name() == ":not") {-                  // extend the inner list of wrapped selector-                  bool extended = false;-                  Selector_List_Obj ext_sl = extendSelectorList(sl, false, extended, recseen);-                  for (size_t i = 0; i < ext_sl->length(); i += 1) {-                    if (Complex_Selector_Obj ext_cs = ext_sl->at(i)) {-                      // create clones for wrapped selector and the inner list-                      Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);-                      Selector_List_Obj cpy_ws_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());-                      // remove parent selectors from inner selector-                      Compound_Selector_Obj ext_head = NULL;-                      if (ext_cs->first()) ext_head = ext_cs->first()->head();-                      if (ext_head && ext_head && ext_head->length() > 0) {-                        cpy_ws_sl->append(ext_cs->first());-                      }-                      // assign list to clone-                      cpy_ws->selector(cpy_ws_sl);-                      // append the clone-                      cpy_head->append(cpy_ws);-                    }-                  }-                  if (eval && extended) {-                    eval->exp.selector_stack.push_back(pNewSelectors);-                    cpy_head->perform(eval);-                    eval->exp.selector_stack.pop_back();-                  }-                }-                // has wrapped selectors-                else {-                  Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);-                  Selector_List_Obj ext_sl = extendSelectorList(sl, recseen);-                  cpy_ws->selector(ext_sl);-                  cpy_head->append(cpy_ws);-                }-              } else {-                cpy_head->append(hs);-              }-            } else {-              cpy_head->append(hs);-            }-          }-          // replace header-          cur->head(cpy_head);-        }-        // process tail-        cur = cur->tail();-      }-    }--    // memory results in a map table - since extending is very expensive-    memoizeList.insert(std::pair<Selector_List_Obj, Selector_List_Obj>(pSelectorList, pNewSelectors));--    return pNewSelectors.detach();--  }---  bool shouldExtendBlock(Block_Obj b) {--    // If a block is empty, there's no reason to extend it since any rules placed on this block-    // won't have any output. The main benefit of this is for structures like:-    //-    //    .a {-    //      .b {-    //        x: y;-    //      }-    //    }-    //-    // We end up visiting two rulesets (one with the selector .a and the other with the selector .a .b).-    // In this case, we don't want to try to pull rules onto .a since they won't get output anyway since-    // there are no child statements. However .a .b should have extensions applied.--    for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Obj stm = b->at(i);--      if (Cast<Ruleset>(stm)) {-        // Do nothing. This doesn't count as a statement that causes extension since we'll-        // iterate over this rule set in a future visit and try to extend it.-      }-      else {-        return true;-      }-    }--    return false;--  }---  // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change.-  // Every Ruleset in the whole tree is calling this function. We decide if there-  // was is @extend that matches our selector. If we find one, we will go further-  // and call the extend magic for our selector. The subset_map contains all blocks-  // where @extend was found. Pick the ones that match our selector!-  void Extend::extendObjectWithSelectorAndBlock(Ruleset_Ptr pObject) {--    DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << Cast<Selector_List>(pObject->selector())->to_string())--    // Ruby sass seems to filter nodes that don't have any content well before we get here.-    // I'm not sure the repercussions of doing so, so for now, let's just not extend things-    // that won't be output later. Profiling shows this may us 0.2% or so.-    if (!shouldExtendBlock(pObject->block())) {-      DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT")-      return;-    }--    bool extendedSomething = false;--    CompoundSelectorSet seen;-    Selector_List_Obj pNewSelectorList = extendSelectorList(pObject->selector(), false, extendedSomething, seen);--    if (extendedSomething && pNewSelectorList) {-      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << pObject->selector()->to_string())-      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string())-      pNewSelectorList->remove_parent_selectors();-      pObject->selector(pNewSelectorList);-    } else {-      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING")-    }-  }--  Extend::Extend(Subset_Map& ssm)-  : subset_map(ssm), eval(NULL)-  { }--  void Extend::setEval(Eval& e) {-    eval = &e;-  }--  void Extend::operator()(Block_Ptr b)-  {-    for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Obj stm = b->at(i);-      stm->perform(this);-    }-    // do final check if everything was extended-    // we set `extended` flag on extended selectors-    if (b->is_root()) {-      // debug_subset_map(subset_map);-      for(auto const &it : subset_map.values()) {-        Complex_Selector_Ptr sel = NULL;-        Compound_Selector_Ptr ext = NULL;-        if (it.first) sel = it.first->first();-        if (it.second) ext = it.second;-        if (ext && (ext->extended() || ext->is_optional())) continue;-        std::string str_sel(sel ? sel->to_string({ NESTED, 5 }) : "NULL");-        std::string str_ext(ext ? ext->to_string({ NESTED, 5 }) : "NULL");-        // debug_ast(sel, "sel: ");-        // debug_ast(ext, "ext: ");-        error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n"-              "The selector \"" + str_ext + "\" was not found.\n"-              "Use \"@extend " + str_ext + " !optional\" if the"-              " extend should be able to fail.", (ext ? ext->pstate() : NULL), eval->exp.traces);-      }-    }--  }--  void Extend::operator()(Ruleset_Ptr pRuleset)-  {-    extendObjectWithSelectorAndBlock( pRuleset );-    pRuleset->block()->perform(this);-  }--  void Extend::operator()(Supports_Block_Ptr pFeatureBlock)-  {-    pFeatureBlock->block()->perform(this);-  }--  void Extend::operator()(Media_Block_Ptr pMediaBlock)-  {-    pMediaBlock->block()->perform(this);-  }--  void Extend::operator()(Directive_Ptr a)-  {-    // Selector_List_Ptr ls = Cast<Selector_List>(a->selector());-    // selector_stack.push_back(ls);-    if (a->block()) a->block()->perform(this);-    // exp.selector_stack.pop_back();-  }-}
− libsass/src/extend.hpp
@@ -1,86 +0,0 @@-#ifndef SASS_EXTEND_H-#define SASS_EXTEND_H--#include <string>-#include <set>--#include "ast.hpp"-#include "node.hpp"-#include "eval.hpp"-#include "operation.hpp"-#include "subset_map.hpp"-#include "ast_fwd_decl.hpp"--namespace Sass {--  Node subweave(Node& one, Node& two);--  class Extend : public Operation_CRTP<void, Extend> {--    Subset_Map& subset_map;-    Eval* eval;--    void fallback_impl(AST_Node_Ptr n) { }--  private:--    std::unordered_map<-      Selector_List_Obj, // key-      Selector_List_Obj, // value-      HashNodes, // hasher-      CompareNodes // compare-    > memoizeList;--    std::unordered_map<-      Complex_Selector_Obj, // key-      Node, // value-      HashNodes, // hasher-      CompareNodes // compare-    > memoizeComplex;--    /* this turned out to be too much overhead-       re-evaluate once we store an ast selector-    std::unordered_map<-      Compound_Selector_Obj, // key-      Node, // value-      HashNodes, // hasher-      CompareNodes // compare-    > memoizeCompound;-    */--    void extendObjectWithSelectorAndBlock(Ruleset_Ptr pObject);-    Node extendComplexSelector(Complex_Selector_Ptr sel, CompoundSelectorSet& seen, bool isReplace, bool isOriginal);-    Node extendCompoundSelector(Compound_Selector_Ptr sel, CompoundSelectorSet& seen, bool isReplace);-    bool complexSelectorHasExtension(Complex_Selector_Ptr selector, CompoundSelectorSet& seen);-    Node trim(Node& seqses, bool isReplace);-    Node weave(Node& path);--  public:-    void setEval(Eval& eval);-    Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen);-    Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace = false) {-      bool extendedSomething = false;-      CompoundSelectorSet seen;-      return extendSelectorList(pSelectorList, isReplace, extendedSomething, seen);-    }-    Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, CompoundSelectorSet& seen) {-      bool isReplace = false;-      bool extendedSomething = false;-      return extendSelectorList(pSelectorList, isReplace, extendedSomething, seen);-    }-    Extend(Subset_Map&);-    ~Extend() { }--    void operator()(Block_Ptr);-    void operator()(Ruleset_Ptr);-    void operator()(Supports_Block_Ptr);-    void operator()(Media_Block_Ptr);-    void operator()(Directive_Ptr);--    template <typename U>-    void fallback(U x) { return fallback_impl(x); }-  };--}--#endif
+ libsass/src/extender.cpp view
@@ -0,0 +1,1186 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++#include "extender.hpp"+#include "permutate.hpp"+#include "dart_helpers.hpp"++namespace Sass {++  // ##########################################################################+  // Constructor without default [mode].+  // [traces] are needed to throw errors.+  // ##########################################################################+  Extender::Extender(Backtraces& traces) :+    mode(NORMAL),+    traces(traces),+    selectors(),+    extensions(),+    extensionsByExtender(),+    mediaContexts(),+    sourceSpecificity(),+    originals()+  {}++  // ##########################################################################+  // Constructor with specific [mode].+  // [traces] are needed to throw errors.+  // ##########################################################################+  Extender::Extender(ExtendMode mode, Backtraces& traces) :+    mode(mode),+    traces(traces),+    selectors(),+    extensions(),+    extensionsByExtender(),+    mediaContexts(),+    sourceSpecificity(),+    originals()+  {}++  // ##########################################################################+  // Extends [selector] with [source] extender and [targets] extendees.+  // This works as though `source {@extend target}` were written in the+  // stylesheet, with the exception that [target] can contain compound+  // selectors which must be extended as a unit.+  // ##########################################################################+  SelectorListObj Extender::extend(+    SelectorListObj& selector,+    const SelectorListObj& source,+    const SelectorListObj& targets,+    Backtraces& traces)+  {+    return extendOrReplace(selector, source, targets, ExtendMode::TARGETS, traces);+  }+  // EO Extender::extend++  // ##########################################################################+  // Returns a copy of [selector] with [targets] replaced by [source].+  // ##########################################################################+  SelectorListObj Extender::replace(+    SelectorListObj& selector,+    const SelectorListObj& source,+    const SelectorListObj& targets,+    Backtraces& traces)+  {+    return extendOrReplace(selector, source, targets, ExtendMode::REPLACE, traces);+  }+  // EO Extender::replace++  // ##########################################################################+  // A helper function for [extend] and [replace].+  // ##########################################################################+  SelectorListObj Extender::extendOrReplace(+    SelectorListObj& selector,+    const SelectorListObj& source,+    const SelectorListObj& targets,+    const ExtendMode mode,+    Backtraces& traces)+  {+    ExtSelExtMapEntry extenders;++    for (auto complex : source->elements()) {+      // Extension.oneOff(complex as ComplexSelector)+      extenders.insert(complex, Extension(complex));+    }++    for (auto complex : targets->elements()) {++      // This seems superfluous, check is done before!?+      // if (complex->length() != 1) {+      //   error("complex selectors may not be extended.", complex->pstate(), traces);+      // }++      if (const CompoundSelector* compound = complex->first()->getCompound()) {++        ExtSelExtMap extensions;++        for (const SimpleSelectorObj& simple : compound->elements()) {+          extensions.insert(std::make_pair(simple, extenders));+        }++        Extender extender(mode, traces);++        if (!selector->is_invisible()) {+          for (auto sel : selector->elements()) {+            extender.originals.insert(sel);+          }+        }++        selector = extender.extendList(selector, extensions, {});++      }++    }++    return selector;++  }+  // EO extendOrReplace++  // ##########################################################################+  // The set of all simple selectors in style rules handled+  // by this extender. This includes simple selectors that+  // were added because of downstream extensions.+  // ##########################################################################+  ExtSmplSelSet Extender::getSimpleSelectors() const+  {+    ExtSmplSelSet set;+    for (auto& entry : selectors) {+      set.insert(entry.first);+    }+    return set;+  }+  // EO getSimpleSelectors++  // ##########################################################################+  // Check for extends that have not been satisfied.+  // Returns true if any non-optional extension did not+  // extend any selector. Updates the passed reference+  // to point to that Extension for further analysis.+  // ##########################################################################+  bool Extender::checkForUnsatisfiedExtends(Extension& unsatisfied) const+  {+    ExtSmplSelSet originals = getSimpleSelectors();+    for (auto target : extensions) {+      SimpleSelector* key = target.first;+      ExtSelExtMapEntry& val = target.second;+      if (originals.find(key) == originals.end()) {+        const Extension& extension = val.front().second;+        if (extension.isOptional) continue;+        unsatisfied = extension;+        return true;+      }+    }+    return false;+  }+  // EO checkUnsatisfiedExtends++  // ##########################################################################+  // Adds [selector] to this extender, with [selectorSpan] as the span covering+  // the selector and [ruleSpan] as the span covering the entire style rule.+  // Extends [selector] using any registered extensions, then returns an empty+  // [ModifiableCssStyleRule] with the resulting selector. If any more relevant+  // extensions are added, the returned rule is automatically updated.+  // The [mediaContext] is the media query context in which the selector was+  // defined, or `null` if it was defined at the top level of the document.+  // ##########################################################################+  void Extender::addSelector(+    const SelectorListObj& selector,+    const CssMediaRuleObj& mediaContext)+  {++    // Note: dart-sass makes a copy here AFAICT+    // Note: probably why we have originalStack+    // SelectorListObj original = selector;++    if (!selector->isInvisible()) {+      for (auto complex : selector->elements()) {+        originals.insert(complex);+      }+    }++    if (!extensions.empty()) {++      SelectorListObj res = extendList(selector, extensions, mediaContext);++      selector->elements(res->elements());++    }++    if (!mediaContext.isNull()) {+      mediaContexts.insert(selector, mediaContext);+    }++    registerSelector(selector, selector);++  }+  // EO addSelector++  // ##########################################################################+  // Registers the [SimpleSelector]s in [list]+  // to point to [rule] in [selectors].+  // ##########################################################################+  void Extender::registerSelector(+    const SelectorListObj& list,+    const SelectorListObj& rule)+  {+    if (list.isNull() || list->empty()) return;+    for (auto complex : list->elements()) {+      for (auto component : complex->elements()) {+        if (auto compound = component->getCompound()) {+          for (SimpleSelector* simple : compound->elements()) {+            selectors[simple].insert(rule);+            if (auto pseudo = simple->getPseudoSelector()) {+              if (pseudo->selector()) {+                auto sel = pseudo->selector();+                registerSelector(sel, rule);+              }+            }+          }+        }+      }+    }+  }+  // EO registerSelector++  // ##########################################################################+  // Returns an extension that combines [left] and [right]. Throws +  // a [SassException] if [left] and [right] have incompatible +  // media contexts. Throws an [ArgumentError] if [left]+  // and [right] don't have the same extender and target.+  // ##########################################################################+  Extension Extender::mergeExtension(+    const Extension& lhs,+    const Extension& rhs)+  {+    // If one extension is optional and doesn't add a+    // special media context, it doesn't need to be merged.+    if (rhs.isOptional && rhs.mediaContext.isNull()) return lhs;+    if (lhs.isOptional && lhs.mediaContext.isNull()) return rhs;++    Extension rv(lhs);+    // ToDo: is this right?+    rv.isOptional = true;+    rv.isOriginal = false;+    return rv;+  }+  // EO mergeExtension++  // ##########################################################################+  // Helper function to copy extension between maps+  // ##########################################################################+  // Seems only relevant for sass 4.0 modules+  // ##########################################################################+  /* void mapCopyExts(+    ExtSelExtMap& dest,+    const ExtSelExtMap& source)+  {+    for (auto it : source) {+      SimpleSelectorObj key = it.first;+      ExtSelExtMapEntry& inner = it.second;+      ExtSelExtMap::iterator dmap = dest.find(key);+      if (dmap == dest.end()) {+        dest.insert(std::make_pair(key, inner));+      }+      else {+        ExtSelExtMapEntry& imap = dmap->second;+        // ToDo: optimize ordered_map API!+        // ToDo: we iterate and fetch the value+        for (ComplexSelectorObj& it2 : inner) {+          imap.insert(it2, inner.get(it2));+        }+      }+    }+  } */+  // EO mapCopyExts++  // ##########################################################################+  // Adds an extension to this extender. The [extender] is the selector for the+  // style rule in which the extension is defined, and [target] is the selector+  // passed to `@extend`. The [extend] provides the extend span and indicates +  // whether the extension is optional. The [mediaContext] defines the media query+  // context in which the extension is defined. It can only extend selectors+  // within the same context. A `null` context indicates no media queries.+  // ##########################################################################+  // ToDo: rename extender to parent, since it is not involved in extending stuff+  // ToDo: check why dart sass passes the ExtendRule around (is this the main selector?)+  // ##########################################################################+  // Note: this function could need some logic cleanup+  // ##########################################################################+  void Extender::addExtension(+    const SelectorListObj& extender,+    const SimpleSelectorObj& target,+    const CssMediaRuleObj& mediaQueryContext,+    bool is_optional)+  {++    auto rules = selectors.find(target);+    bool hasRule = rules != selectors.end();++    ExtSelExtMapEntry newExtensions;++    // ToDo: we check this here first and fetch the same? item again after the loop!?+    bool hasExistingExtensions = extensionsByExtender.find(target) != extensionsByExtender.end();++    ExtSelExtMapEntry& sources = extensions[target];++    for (auto& complex : extender->elements()) {++      Extension state(complex);+      // ToDo: fine-tune public API+      state.target = target;+      state.isOptional = is_optional;+      state.mediaContext = mediaQueryContext;++      if (sources.hasKey(complex)) {+        // If there's already an extend from [extender] to [target],+        // we don't need to re-run the extension. We may need to+        // mark the extension as mandatory, though.+        // sources.insert(complex, mergeExtension(existingState->second, state);+        // ToDo: implement behavior once use case is found!?+        continue;+      }++      sources.insert(complex, state);++      for (auto& component : complex->elements()) {+        if (auto compound = component->getCompound()) {+          for (auto& simple : compound->elements()) {+            extensionsByExtender[simple].push_back(state);+            if (sourceSpecificity.find(simple) == sourceSpecificity.end()) {+              // Only source specificity for the original selector is relevant.+              // Selectors generated by `@extend` don't get new specificity.+              sourceSpecificity[simple] = complex->maxSpecificity();+            }+          }+        }+      }++      if (hasRule || hasExistingExtensions) {+        newExtensions.insert(complex, state);+      }++    }+    // EO foreach complex++    if (newExtensions.empty()) {+      return;+    }++    ExtSelExtMap newExtensionsByTarget;+    newExtensionsByTarget.insert(std::make_pair(target, newExtensions));+    // ToDo: do we really need to fetch again (see top off fn) +    auto existingExtensions = extensionsByExtender.find(target);+    if (existingExtensions != extensionsByExtender.end()) {+      if (hasExistingExtensions && !existingExtensions->second.empty()) {+        // Seems only relevant for sass 4.0 modules+        // auto additionalExtensions =+          extendExistingExtensions(existingExtensions->second, newExtensionsByTarget);+        // Seems only relevant for sass 4.0 modules+        /* if (!additionalExtensions.empty()) {+          mapCopyExts(newExtensionsByTarget, additionalExtensions);+        } */+      }+    }++    if (hasRule) {+      extendExistingStyleRules(selectors[target], newExtensionsByTarget);+    }++  }+  // EO addExtension+  +  // ##########################################################################+  // Extend [extensions] using [newExtensions].+  // ##########################################################################+  // Note: dart-sass throws an error in here+  // ##########################################################################+  void Extender::extendExistingStyleRules(+    const ExtListSelSet& rules,+    const ExtSelExtMap& newExtensions)+  {+    // Is a modifyableCssStyleRUle in dart sass+    for (const SelectorListObj& rule : rules) {+      const SelectorListObj& oldValue = SASS_MEMORY_COPY(rule);+      CssMediaRuleObj mediaContext;+      if (mediaContexts.hasKey(rule)) mediaContext = mediaContexts.get(rule);+      SelectorListObj ext = extendList(rule, newExtensions, mediaContext);+      // If no extends actually happenedit (for example becaues unification+      // failed), we don't need to re-register the selector.+      if (ObjEqualityFn(oldValue, ext)) continue;+      rule->elements(ext->elements());+      registerSelector(rule, rule);++    }+  }+  // EO extendExistingStyleRules++  // ##########################################################################+  // Extend [extensions] using [newExtensions]. Note that this does duplicate+  // some work done by [_extendExistingStyleRules],  but it's necessary to+  // expand each extension's extender separately without reference to the full+  // selector list, so that relevant results don't get trimmed too early.+  //+  // Returns extensions that should be added to [newExtensions] before+  // extending selectors in order to properly handle extension loops such as:+  //+  //     .c {x: y; @extend .a}+  //     .x.y.a {@extend .b}+  //     .z.b {@extend .c}+  //+  // Returns `null` (Note: empty map) if there are no extensions to add.+  // ##########################################################################+  // Note: maybe refactor to return `bool` (and pass reference)+  // Note: dart-sass throws an error in here+  // ##########################################################################+  ExtSelExtMap Extender::extendExistingExtensions(+    // Taking in a reference here makes MSVC debug stuck!?+    const std::vector<Extension>& oldExtensions,+    const ExtSelExtMap& newExtensions)+  {++    ExtSelExtMap additionalExtensions;++    // During the loop `oldExtensions` vector might be changed.+    // Callers normally pass this from `extensionsByExtender` and+    // that points back to the `sources` vector from `extensions`.+    for (size_t i = 0, iL = oldExtensions.size(); i < iL; i += 1) {+      const Extension& extension = oldExtensions[i];+      ExtSelExtMapEntry& sources = extensions[extension.target];+      std::vector<ComplexSelectorObj> selectors(extendComplex(+        extension.extender,+        newExtensions,+        extension.mediaContext+      ));++      if (selectors.empty()) {+        continue;+      }++      // ToDo: "catch" error from extend++      bool first = false, containsExtension =+        ObjEqualityFn(selectors.front(), extension.extender);+      for (const ComplexSelectorObj& complex : selectors) {+        // If the output contains the original complex +        // selector, there's no need to recreate it.+        if (containsExtension && first) {+          first = false;+          continue;+        }++        const Extension withExtender =+          extension.withExtender(complex);+        if (sources.hasKey(complex)) {+          sources.insert(complex, mergeExtension(+            sources.get(complex), withExtender));+        }+        else {+          sources.insert(complex, withExtender);+          /*+          // Seems only relevant for sass 4.0 modules+          for (auto& component : complex->elements()) {+            if (auto compound = component->getCompound()) {+              for (auto& simple : compound->elements()) {+                extensionsByExtender[simple].push_back(withExtender);+              }+            }+          }+          if (newExtensions.find(extension.target) != newExtensions.end()) {+            additionalExtensions[extension.target].insert(complex, withExtender);+          }+          */+        }+      }++      // If [selectors] doesn't contain [extension.extender],+      // for example if it was replaced due to :not() expansion,+      // we must get rid of the old version.+      /*+      // Seems only relevant for sass 4.0 modules+      if (!containsExtension) {+        sources.erase(extension.extender);+      }+      */++    }++    return additionalExtensions;++  }+  // EO extendExistingExtensions++  // ##########################################################################+  // Extends [list] using [extensions].+  // ##########################################################################+  SelectorListObj Extender::extendList(+    const SelectorListObj& list,+    const ExtSelExtMap& extensions,+    const CssMediaRuleObj& mediaQueryContext)+  {++    // This could be written more simply using [List.map], but we want to+    // avoid any allocations in the common case where no extends apply.+    std::vector<ComplexSelectorObj> extended;+    for (size_t i = 0; i < list->length(); i++) {+      const ComplexSelectorObj& complex = list->get(i);+      std::vector<ComplexSelectorObj> result =+        extendComplex(complex, extensions, mediaQueryContext);+      if (result.empty()) {+        if (!extended.empty()) {+          extended.push_back(complex);+        }+      }+      else {+        if (extended.empty()) {+          for (size_t n = 0; n < i; n += 1) {+            extended.push_back(list->get(n));+          }+        }+        for (auto sel : result) {+          extended.push_back(sel);+        }+      }+    }++    if (extended.empty()) {+      return list;+    }++    SelectorListObj rv = SASS_MEMORY_NEW(SelectorList, list->pstate());+    rv->concat(trim(extended, originals));+    return rv;++  }+  // EO extendList++  // ##########################################################################+  // Extends [complex] using [extensions], and+  // returns the contents of a [SelectorList].+  // ##########################################################################+  std::vector<ComplexSelectorObj> Extender::extendComplex(+    // Taking in a reference here makes MSVC debug stuck!?+    const ComplexSelectorObj& complex,+    const ExtSelExtMap& extensions,+    const CssMediaRuleObj& mediaQueryContext)+  {++    // The complex selectors that each compound selector in [complex.components]+    // can expand to.+    //+    // For example, given+    //+    //     .a .b {...}+    //     .x .y {@extend .b}+    //+    // this will contain+    //+    //     [+    //       [.a],+    //       [.b, .x .y]+    //     ]+    //+    // This could be written more simply using [List.map], but we want to avoid+    // any allocations in the common case where no extends apply.++    std::vector<ComplexSelectorObj> result;+    std::vector<std::vector<ComplexSelectorObj>> extendedNotExpanded;+    bool isOriginal = originals.find(complex) != originals.end();+    for (size_t i = 0; i < complex->length(); i += 1) {+      const SelectorComponentObj& component = complex->get(i);+      if (CompoundSelector* compound = Cast<CompoundSelector>(component)) {+        std::vector<ComplexSelectorObj> extended = extendCompound(+          compound, extensions, mediaQueryContext, isOriginal);+        if (extended.empty()) {+          if (!extendedNotExpanded.empty()) {+            extendedNotExpanded.push_back({+              compound->wrapInComplex()+            });+          }+        }+        else {+          // Note: dart-sass checks for null!?+          if (extendedNotExpanded.empty()) {+            for (size_t n = 0; n < i; n++) {+                extendedNotExpanded.push_back({+                  complex->at(n)->wrapInComplex()+                });+            }+          }+          extendedNotExpanded.push_back(extended);+        }+      }+      else {+        // Note: dart-sass checks for null!?+        if (!extendedNotExpanded.empty()) {+          extendedNotExpanded.push_back({+            component->wrapInComplex()+          });+        }+      }+    }++    // Note: dart-sass checks for null!?+    if (extendedNotExpanded.empty()) {+      return {};+    }++    bool first = true;++    // ToDo: either change weave or paths to work with the same data?+    std::vector<std::vector<ComplexSelectorObj>>+      paths = permutate(extendedNotExpanded);+    +    for (const std::vector<ComplexSelectorObj>& path : paths) {+      // Unpack the inner complex selector to component list+      std::vector<std::vector<SelectorComponentObj>> _paths;+      for (const ComplexSelectorObj& sel : path) {+        _paths.insert(_paths.end(), sel->elements());+      }++      std::vector<std::vector<SelectorComponentObj>> weaved = weave(_paths);++      for (std::vector<SelectorComponentObj>& components : weaved) {++        ComplexSelectorObj cplx = SASS_MEMORY_NEW(ComplexSelector, "[phony]");+        cplx->hasPreLineFeed(complex->hasPreLineFeed());+        for (auto& pp : path) {+          if (pp->hasPreLineFeed()) {+            cplx->hasPreLineFeed(true);+          }+        }+        cplx->elements(components);++        // Make sure that copies of [complex] retain their status+        // as "original" selectors. This includes selectors that+        // are modified because a :not() was extended into.+        if (first && originals.find(complex) != originals.end()) {+          originals.insert(cplx);+        }+        first = false;++        result.push_back(cplx);++      }++    }++    return result;+  }+  // EO extendComplex++  // ##########################################################################+  // Returns a one-off [Extension] whose+  // extender is composed solely of [simple].+  // ##########################################################################+  Extension Extender::extensionForSimple(+    const SimpleSelectorObj& simple) const+  {+    Extension extension(simple->wrapInComplex());+    extension.specificity = maxSourceSpecificity(simple);+    extension.isOriginal = true;+    return extension;+  }+  // Extender::extensionForSimple++  // ##########################################################################+  // Returns a one-off [Extension] whose extender is composed+  // solely of a compound selector containing [simples].+  // ##########################################################################+  Extension Extender::extensionForCompound(+    // Taking in a reference here makes MSVC debug stuck!?+    const std::vector<SimpleSelectorObj>& simples) const+  {+    CompoundSelectorObj compound = SASS_MEMORY_NEW(CompoundSelector, ParserState("[ext]"));+    compound->concat(simples);+    Extension extension(compound->wrapInComplex());+    // extension.specificity = sourceSpecificity[simple];+    extension.isOriginal = true;+    return extension;+  }+  // EO extensionForCompound++  // ##########################################################################+  // Extends [compound] using [extensions], and returns the+  // contents of a [SelectorList]. The [inOriginal] parameter+  // indicates whether this is in an original complex selector,+  // meaning that [compound] should not be trimmed out.+  // ##########################################################################+  std::vector<ComplexSelectorObj> Extender::extendCompound(+    const CompoundSelectorObj& compound,+    const ExtSelExtMap& extensions,+    const CssMediaRuleObj& mediaQueryContext,+    bool inOriginal)+  {++    // If there's more than one target and they all need to+    // match, we track which targets are actually extended.+    ExtSmplSelSet targetsUsed2;++    ExtSmplSelSet* targetsUsed = nullptr;++    if (mode != ExtendMode::NORMAL && extensions.size() > 1) {+      targetsUsed = &targetsUsed2;+    }++    std::vector<ComplexSelectorObj> result;+    // The complex selectors produced from each component of [compound].+    std::vector<std::vector<Extension>> options;++    for (size_t i = 0; i < compound->length(); i++) {+      const SimpleSelectorObj& simple = compound->get(i);+      auto extended = extendSimple(simple, extensions, mediaQueryContext, targetsUsed);+      if (extended.empty()) {+        if (!options.empty()) {+          options.push_back({ extensionForSimple(simple) });+        }+      }+      else {+        if (options.empty()) {+          if (i != 0) {+            std::vector<SimpleSelectorObj> in;+            for (size_t n = 0; n < i; n += 1) {+              in.push_back(compound->get(n));+            }+            options.push_back({ extensionForCompound(in) });+          }+        }+        options.insert(options.end(),+          extended.begin(), extended.end());+      }+    }++    if (options.empty()) {+      return {};+    }++    // If [_mode] isn't [ExtendMode.normal] and we didn't use all+    // the targets in [extensions], extension fails for [compound].+    if (targetsUsed != nullptr) {++      if (targetsUsed->size() != extensions.size()) {+        if (!targetsUsed->empty()) {+          return {};+        }+      }+    }++    // Optimize for the simple case of a single simple+    // selector that doesn't need any unification.+    if (options.size() == 1) {+      std::vector<Extension> exts = options[0];+      for (size_t n = 0; n < exts.size(); n += 1) {+        exts[n].assertCompatibleMediaContext(mediaQueryContext, traces);+        result.push_back(exts[n].extender);+      }+      return result;+    }++    // Find all paths through [options]. In this case, each path represents a+    // different unification of the base selector. For example, if we have:+    //+    //     .a.b {...}+    //     .w .x {@extend .a}+    //     .y .z {@extend .b}+    //+    // then [options] is `[[.a, .w .x], [.b, .y .z]]` and `paths(options)` is+    //+    //     [+    //       [.a, .b],+    //       [.a, .y .z],+    //       [.w .x, .b],+    //       [.w .x, .y .z]+    //     ]+    //+    // We then unify each path to get a list of complex selectors:+    //+    //     [+    //       [.a.b],+    //       [.y .a.z],+    //       [.w .x.b],+    //       [.w .y .x.z, .y .w .x.z]+    //     ]++    bool first = mode != ExtendMode::REPLACE;+    std::vector<ComplexSelectorObj> unifiedPaths;+    std::vector<std::vector<Extension>> prePaths = permutate(options);++    for (size_t i = 0; i < prePaths.size(); i += 1) {+      std::vector<std::vector<SelectorComponentObj>> complexes;+      const std::vector<Extension>& path = prePaths[i];+      if (first) {+        // The first path is always the original selector. We can't just+        // return [compound] directly because pseudo selectors may be+        // modified, but we don't have to do any unification.+        first = false;+        CompoundSelectorObj mergedSelector =+          SASS_MEMORY_NEW(CompoundSelector, "[ext]");+        for (size_t n = 0; n < path.size(); n += 1) {+          const ComplexSelectorObj& sel = path[n].extender;+          if (CompoundSelectorObj compound = Cast<CompoundSelector>(sel->last())) {+            mergedSelector->concat(compound->elements());+          }+        }+        complexes.push_back({ mergedSelector });+      }+      else {+        std::vector<SimpleSelectorObj> originals;+        std::vector<std::vector<SelectorComponentObj>> toUnify;++        for (auto& state : path) {+          if (state.isOriginal) {+            const ComplexSelectorObj& sel = state.extender;+            if (const CompoundSelector* compound = Cast<CompoundSelector>(sel->last())) {+              originals.insert(originals.end(), compound->last());+            }+          }+          else {+            toUnify.push_back(state.extender->elements());+          }+        }+        if (!originals.empty()) {+          CompoundSelectorObj merged =+            SASS_MEMORY_NEW(CompoundSelector, "[phony]");+          merged->concat(originals);+          toUnify.insert(toUnify.begin(), { merged });+        }+        complexes = unifyComplex(toUnify);+        if (complexes.empty()) {+          return {};+        }++      }++      bool lineBreak = false;+      // var specificity = _sourceSpecificityFor(compound);+      for (const Extension& state : path) {+        state.assertCompatibleMediaContext(mediaQueryContext, traces);+        lineBreak = lineBreak || state.extender->hasPreLineFeed();+        // specificity = math.max(specificity, state.specificity);+      }++      for (std::vector<SelectorComponentObj>& components : complexes) {+        auto sel = SASS_MEMORY_NEW(ComplexSelector, "[ext]");+        sel->hasPreLineFeed(lineBreak);+        sel->elements(components);+        unifiedPaths.push_back(sel);+      }++    }++    return unifiedPaths;+  }+  // EO extendCompound++  // ##########################################################################+  // Extends [simple] without extending the+  // contents of any selector pseudos it contains.+  // ##########################################################################+  std::vector<Extension> Extender::extendWithoutPseudo(+    const SimpleSelectorObj& simple,+    const ExtSelExtMap& extensions,+    ExtSmplSelSet* targetsUsed) const+  {++    auto extension = extensions.find(simple);+    if (extension == extensions.end()) return {};+    const ExtSelExtMapEntry& extenders = extension->second;++    if (targetsUsed != nullptr) {+      targetsUsed->insert(simple);+    }+    if (mode == ExtendMode::REPLACE) {+      return extenders.values();+    }++    const std::vector<Extension>&+      values = extenders.values();+    std::vector<Extension> result;+    result.reserve(values.size() + 1);+    result.push_back(extensionForSimple(simple));+    result.insert(result.end(), values.begin(), values.end());+    return result;+  }+  // EO extendWithoutPseudo++  // ##########################################################################+  // Extends [simple] and also extending the+  // contents of any selector pseudos it contains.+  // ##########################################################################+  std::vector<std::vector<Extension>> Extender::extendSimple(+    const SimpleSelectorObj& simple,+    const ExtSelExtMap& extensions,+    const CssMediaRuleObj& mediaQueryContext,+    ExtSmplSelSet* targetsUsed)+  {+    if (Pseudo_Selector* pseudo = Cast<Pseudo_Selector>(simple)) {+      if (pseudo->selector()) {+        std::vector<std::vector<Extension>> merged;+        std::vector<Pseudo_Selector_Obj> extended =+          extendPseudo(pseudo, extensions, mediaQueryContext);+        for (Pseudo_Selector_Obj& extend : extended) {+          SimpleSelectorObj simple = extend;+          std::vector<Extension> result =+            extendWithoutPseudo(simple, extensions, targetsUsed);+          if (result.empty()) result = { extensionForSimple(extend) };+          merged.push_back(result);+        }+        if (!extended.empty()) {+          return merged;+        }+      }+    }+    std::vector<Extension> result =+      extendWithoutPseudo(simple, extensions, targetsUsed);+    if (result.empty()) return {};+    return { result };+  }+  // extendSimple++  // ##########################################################################+  // Inner loop helper for [extendPseudo] function+  // ##########################################################################+  std::vector<ComplexSelectorObj> Extender::extendPseudoComplex(+    const ComplexSelectorObj& complex,+    const Pseudo_Selector_Obj& pseudo,+    const CssMediaRuleObj& mediaQueryContext)+  {++    if (complex->length() != 1) { return { complex }; }+    auto compound = Cast<CompoundSelector>(complex->get(0));+    if (compound == nullptr) { return { complex }; }+    if (compound->length() != 1) { return { complex }; }+    auto innerPseudo = Cast<Pseudo_Selector>(compound->get(0));+    if (innerPseudo == nullptr) { return { complex }; }+    if (!innerPseudo->selector()) { return { complex }; }++    std::string name(pseudo->normalized());++    if (name == "not") {+      // In theory, if there's a `:not` nested within another `:not`, the+      // inner `:not`'s contents should be unified with the return value.+      // For example, if `:not(.foo)` extends `.bar`, `:not(.bar)` should+      // become `.foo:not(.bar)`. However, this is a narrow edge case and+      // supporting it properly would make this code and the code calling it+      // a lot more complicated, so it's not supported for now.+      if (innerPseudo->normalized() != "matches") return {};+      return innerPseudo->selector()->elements();+    }+    else if (name == "matches" && name == "any" && name == "current" && name == "nth-child" && name == "nth-last-child") {+      // As above, we could theoretically support :not within :matches, but+      // doing so would require this method and its callers to handle much+      // more complex cases that likely aren't worth the pain.+      if (innerPseudo->name() != pseudo->name()) return {};+      if (!ObjEquality()(innerPseudo->argument(), pseudo->argument())) return {};+      return innerPseudo->selector()->elements();+    }+    else if (name == "has" && name == "host" && name == "host-context" && name == "slotted") {+      // We can't expand nested selectors here, because each layer adds an+      // additional layer of semantics. For example, `:has(:has(img))`+      // doesn't match `<div><img></div>` but `:has(img)` does.+      return { complex };+    }++    return {};++  }+  // EO extendPseudoComplex++  // ##########################################################################+  // Extends [pseudo] using [extensions], and returns+  // a list of resulting pseudo selectors.+  // ##########################################################################+  std::vector<Pseudo_Selector_Obj> Extender::extendPseudo(+    const Pseudo_Selector_Obj& pseudo,+    const ExtSelExtMap& extensions,+    const CssMediaRuleObj& mediaQueryContext)+  {+    auto selector = pseudo->selector();+    SelectorListObj extended = extendList(+      selector, extensions, mediaQueryContext);+    if (!extended || !pseudo || !pseudo->selector()) { return {}; }+    if (ObjEqualityFn(pseudo->selector(), extended)) { return {}; }++    // For `:not()`, we usually want to get rid of any complex selectors because+    // that will cause the selector to fail to parse on all browsers at time of+    // writing. We can keep them if either the original selector had a complex+    // selector, or the result of extending has only complex selectors, because+    // either way we aren't breaking anything that isn't already broken.+    std::vector<ComplexSelectorObj> complexes = extended->elements();++    if (pseudo->normalized() == "not") {+      if (!hasAny(pseudo->selector()->elements(), hasMoreThanOne)) {+        if (hasAny(extended->elements(), hasExactlyOne)) {+          complexes.clear();+          for (auto& complex : extended->elements()) {+            if (complex->length() <= 1) {+              complexes.push_back(complex);+            }+          }+        }+      }+    }+    +    std::vector<ComplexSelectorObj> expanded = expand(+      complexes, extendPseudoComplex, pseudo, mediaQueryContext);++    // Older browsers support `:not`, but only with a single complex selector.+    // In order to support those browsers, we break up the contents of a `:not`+    // unless it originally contained a selector list.+    if (pseudo->normalized() == "not") {+      if (pseudo->selector()->length() == 1) {+        std::vector<Pseudo_Selector_Obj> pseudos;+        for (size_t i = 0; i < expanded.size(); i += 1) {+          pseudos.push_back(pseudo->withSelector(+            expanded[i]->wrapInList()+          ));+        }+        return pseudos;+      }+    }++    SelectorListObj list = SASS_MEMORY_NEW(SelectorList, "[phony]");+    list->concat(complexes);+    return { pseudo->withSelector(list) };++  }+  // EO extendPseudo++  // ##########################################################################+  // Rotates the element in list from [start] (inclusive) to [end] (exclusive)+  // one index higher, looping the final element back to [start].+  // ##########################################################################+  void Extender::rotateSlice(+    std::vector<ComplexSelectorObj>& list,+    size_t start, size_t end)+  {+    auto element = list[end - 1];+    for (size_t i = start; i < end; i++) {+      auto next = list[i];+      list[i] = element;+      element = next;+    }+  }+  // EO rotateSlice++  // ##########################################################################+  // Removes elements from [selectors] if they're subselectors of other+  // elements. The [isOriginal] callback indicates which selectors are+  // original to the document, and thus should never be trimmed.+  // ##########################################################################+  // Note: for adaption I pass in the set directly, there is some+  // code path in selector-trim that might need this special callback+  // ##########################################################################+  std::vector<ComplexSelectorObj> Extender::trim(+    const std::vector<ComplexSelectorObj>& selectors,+    const ExtCplxSelSet& existing) const+  {++    // Avoid truly horrific quadratic behavior.+    // TODO(nweiz): I think there may be a way to get perfect trimming +    // without going quadratic by building some sort of trie-like+    // data structure that can be used to look up superselectors.+    // TODO(mgreter): Check how this perfoms in C++ (up the limit)+    if (selectors.size() > 100) return selectors;++    // This is n² on the sequences, but only comparing between separate sequences+    // should limit the quadratic behavior. We iterate from last to first and reverse+    // the result so that, if two selectors are identical, we keep the first one.+    std::vector<ComplexSelectorObj> result; size_t numOriginals = 0;++    size_t i = selectors.size();+  outer: // Use label to continue loop+    while (--i != std::string::npos) {++      const ComplexSelectorObj& complex1 = selectors[i];+      // Check if selector in known in existing "originals"+      // For custom behavior dart-sass had `isOriginal(complex1)`+      if (existing.find(complex1) != existing.end()) {+        // Make sure we don't include duplicate originals, which could+        // happen if a style rule extends a component of its own selector.+        for (size_t j = 0; j < numOriginals; j++) {+          if (ObjEqualityFn(result[j], complex1)) {+            rotateSlice(result, 0, j + 1);+            goto outer;+          }+        }+        result.insert(result.begin(), complex1);+        numOriginals++;+        continue;+      }++      // The maximum specificity of the sources that caused [complex1]+      // to be generated. In order for [complex1] to be removed, there+      // must be another selector that's a superselector of it *and*+      // that has specificity greater or equal to this.+      size_t maxSpecificity = 0;+      for (const SelectorComponentObj& component : complex1->elements()) {+        if (const CompoundSelectorObj compound = Cast<CompoundSelector>(component)) {+          maxSpecificity = std::max(maxSpecificity, maxSourceSpecificity(compound));+        }+      }+      ++      // Look in [result] rather than [selectors] for selectors after [i]. This+      // ensures we aren't comparing against a selector that's already been trimmed,+      // and thus that if there are two identical selectors only one is trimmed.+      if (hasAny(result, dontTrimComplex, complex1, maxSpecificity)) {+        continue;+      }++      // Check if any element (up to [i]) from [selector] returns true+      // when passed to [dontTrimComplex]. The arguments [complex1] and+      // [maxSepcificity] will be passed to the invoked function.+      if (hasSubAny(selectors, i, dontTrimComplex, complex1, maxSpecificity)) {+        continue;+      }++      // ToDo: Maybe use deque for front insert?+      result.insert(result.begin(), complex1);++    }++    return result;++  }+  // EO trim++  // ##########################################################################+  // Returns the maximum specificity of the given [simple] source selector.+  // ##########################################################################+  size_t Extender::maxSourceSpecificity(const SimpleSelectorObj& simple) const+  {+    auto it = sourceSpecificity.find(simple);+    if (it == sourceSpecificity.end()) return 0;+    return it->second;+  }+  // EO maxSourceSpecificity(SimpleSelectorObj)++  // ##########################################################################+  // Returns the maximum specificity for sources that went into producing [compound].+  // ##########################################################################+  size_t Extender::maxSourceSpecificity(const CompoundSelectorObj& compound) const+  {+    size_t specificity = 0;+    for (auto simple : compound->elements()) {+      size_t src = maxSourceSpecificity(simple);+      specificity = std::max(specificity, src);+    }+    return specificity;+  }+  // EO maxSourceSpecificity(CompoundSelectorObj)++  // ##########################################################################+  // Helper function used as callbacks on lists+  // ##########################################################################+  bool Extender::dontTrimComplex(+    const ComplexSelector* complex2,+    const ComplexSelector* complex1,+    const size_t maxSpecificity)+  {+    if (complex2->minSpecificity() < maxSpecificity) return false;+    return complex2->isSuperselectorOf(complex1);+  }+  // EO dontTrimComplex++  // ##########################################################################+  // Helper function used as callbacks on lists+  // ##########################################################################+  bool Extender::hasExactlyOne(const ComplexSelectorObj& vec)+  {+    return vec->length() == 1;+  }+  // EO hasExactlyOne++  // ##########################################################################+  // Helper function used as callbacks on lists+  // ##########################################################################+  bool Extender::hasMoreThanOne(const ComplexSelectorObj& vec)+  {+    return vec->length() > 1;+  }+  // hasMoreThanOne++}
+ libsass/src/extender.hpp view
@@ -0,0 +1,399 @@+#ifndef SASS_EXTENDER_H+#define SASS_EXTENDER_H++#include <set>+#include <map>+#include <string>++#include "ast_helpers.hpp"+#include "ast_fwd_decl.hpp"+#include "operation.hpp"+#include "extension.hpp"+#include "backtrace.hpp"+#include "ordered_map.hpp"++namespace Sass {++  // ##########################################################################+  // Different hash map types used by extender+  // ##########################################################################++  // This is special (ptrs!)+  typedef std::unordered_set<+    ComplexSelectorObj,+    ObjPtrHash,+    ObjPtrEquality+  > ExtCplxSelSet;++  typedef std::unordered_set<+    SimpleSelectorObj,+    ObjHash,+    ObjEquality+  > ExtSmplSelSet;++  typedef std::unordered_set<+    SelectorListObj,+    ObjPtrHash,+    ObjPtrEquality+  > ExtListSelSet;++  typedef std::unordered_map<+    SimpleSelectorObj,+    ExtListSelSet,+    ObjHash,+    ObjEquality+  > ExtSelMap;++  typedef ordered_map<+    ComplexSelectorObj,+    Extension,+    ObjHash,+    ObjEquality+  > ExtSelExtMapEntry;++  typedef std::unordered_map<+    SimpleSelectorObj,+    ExtSelExtMapEntry,+    ObjHash,+    ObjEquality+  > ExtSelExtMap;++  typedef std::unordered_map <+    SimpleSelectorObj,+    std::vector<+      Extension+    >,+    ObjHash,+    ObjEquality+  > ExtByExtMap;++  class Extender : public Operation_CRTP<void, Extender> {++  public:++    enum ExtendMode { TARGETS, REPLACE, NORMAL, };++  private:++    // ##########################################################################+    // The mode that controls this extender's behavior.+    // ##########################################################################+    ExtendMode mode;++    // ##########################################################################+    // Shared backtraces with context and expander. Needed the throw+    // errors when e.g. extending across media query boundaries.+    // ##########################################################################+    Backtraces& traces;++    // ##########################################################################+    // A map from all simple selectors in the stylesheet to the rules that+    // contain them.This is used to find which rules an `@extend` applies to.+    // ##########################################################################+    ExtSelMap selectors;++    // ##########################################################################+    // A map from all extended simple selectors+    // to the sources of those extensions.+    // ##########################################################################+    ExtSelExtMap extensions;++    // ##########################################################################+    // A map from all simple selectors in extenders to+    // the extensions that those extenders define.+    // ##########################################################################+    ExtByExtMap extensionsByExtender;++    // ##########################################################################+    // A map from CSS rules to the media query contexts they're defined in.+    // This tracks the contexts in which each style rule is defined.+    // If a rule is defined at the top level, it doesn't have an entry.+    // ##########################################################################+    ordered_map<+      SelectorListObj,+      CssMediaRuleObj,+      ObjPtrHash,+      ObjPtrEquality+    > mediaContexts;+    +    // ##########################################################################+    // A map from [SimpleSelector]s to the specificity of their source selectors.+    // This tracks the maximum specificity of the [ComplexSelector] that originally +    // contained each [SimpleSelector]. This allows us to ensure we don't trim any+    // selectors that need to exist to satisfy the [second law that of extend][].+    // [second law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184+    // ##########################################################################+    std::unordered_map<+      SimpleSelectorObj,+      size_t,+      ObjPtrHash,+      ObjPtrEquality+    > sourceSpecificity;++    // ##########################################################################+    // A set of [ComplexSelector]s that were originally part of their+    // component [SelectorList]s, as opposed to being added by `@extend`.+    // This allows us to ensure that we don't trim any selectors+    // that need to exist to satisfy the [first law of extend][].+    // ##########################################################################+    ExtCplxSelSet originals;++  public:++    // Constructor without default [mode].+    // [traces] are needed to throw errors.+    Extender(Backtraces& traces);++    // ##########################################################################+    // Constructor with specific [mode].+    // [traces] are needed to throw errors.+    // ##########################################################################+    Extender(ExtendMode mode, Backtraces& traces);++    // ##########################################################################+    // Empty desctructor+    // ##########################################################################+    ~Extender() {};++    // ##########################################################################+    // Extends [selector] with [source] extender and [targets] extendees.+    // This works as though `source {@extend target}` were written in the+    // stylesheet, with the exception that [target] can contain compound+    // selectors which must be extended as a unit.+    // ##########################################################################+    static SelectorListObj extend(+      SelectorListObj& selector,+      const SelectorListObj& source,+      const SelectorListObj& target,+      Backtraces& traces);++    // ##########################################################################+    // Returns a copy of [selector] with [targets] replaced by [source].+    // ##########################################################################+    static SelectorListObj replace(+      SelectorListObj& selector,+      const SelectorListObj& source,+      const SelectorListObj& target,+      Backtraces& traces);++    // ##########################################################################+    // Adds [selector] to this extender, with [selectorSpan] as the span covering+    // the selector and [ruleSpan] as the span covering the entire style rule.+    // Extends [selector] using any registered extensions, then returns an empty+    // [ModifiableCssStyleRule] with the resulting selector. If any more relevant+    // extensions are added, the returned rule is automatically updated.+    // The [mediaContext] is the media query context in which the selector was+    // defined, or `null` if it was defined at the top level of the document.+    // ##########################################################################+    void addSelector(+      const SelectorListObj& selector,+      const CssMediaRuleObj& mediaContext);++    // ##########################################################################+    // Registers the [SimpleSelector]s in [list]+    // to point to [rule] in [selectors].+    // ##########################################################################+    void registerSelector(+      const SelectorListObj& list,+      const SelectorListObj& rule);++    // ##########################################################################+    // Adds an extension to this extender. The [extender] is the selector for the+    // style rule in which the extension is defined, and [target] is the selector+    // passed to `@extend`. The [extend] provides the extend span and indicates +    // whether the extension is optional. The [mediaContext] defines the media query+    // context in which the extension is defined. It can only extend selectors+    // within the same context. A `null` context indicates no media queries.+    // ##########################################################################+    void addExtension(+      const SelectorListObj& extender,+      const SimpleSelectorObj& target,+      const CssMediaRuleObj& mediaQueryContext,+      bool is_optional = false);++    // ##########################################################################+    // The set of all simple selectors in style rules handled+    // by this extender. This includes simple selectors that+    // were added because of downstream extensions.+    // ##########################################################################+    ExtSmplSelSet getSimpleSelectors() const;++    // ##########################################################################+    // Check for extends that have not been satisfied.+    // Returns true if any non-optional extension did not+    // extend any selector. Updates the passed reference+    // to point to that Extension for further analysis.+    // ##########################################################################+    bool checkForUnsatisfiedExtends(+      Extension& unsatisfied) const;++  private:++    // ##########################################################################+    // A helper function for [extend] and [replace].+    // ##########################################################################+    static SelectorListObj extendOrReplace(+      SelectorListObj& selector,+      const SelectorListObj& source,+      const SelectorListObj& target,+      const ExtendMode mode,+      Backtraces& traces);++    // ##########################################################################+    // Returns an extension that combines [left] and [right]. Throws +    // a [SassException] if [left] and [right] have incompatible +    // media contexts. Throws an [ArgumentError] if [left]+    // and [right] don't have the same extender and target.+    // ##########################################################################+    static Extension mergeExtension(+      const Extension& lhs,+      const Extension& rhs);++    // ##########################################################################+    // Extend [extensions] using [newExtensions].+    // ##########################################################################+    // Note: dart-sass throws an error in here+    // ##########################################################################+    void extendExistingStyleRules(+      const ExtListSelSet& rules,+      const ExtSelExtMap& newExtensions);++    // ##########################################################################+    // Extend [extensions] using [newExtensions]. Note that this does duplicate+    // some work done by [_extendExistingStyleRules],  but it's necessary to+    // expand each extension's extender separately without reference to the full+    // selector list, so that relevant results don't get trimmed too early.+    // Returns `null` (Note: empty map) if there are no extensions to add.+    // ##########################################################################+    ExtSelExtMap extendExistingExtensions(+      // Taking in a reference here makes MSVC debug stuck!?+      const std::vector<Extension>& extensions,+      const ExtSelExtMap& newExtensions);++    // ##########################################################################+    // Extends [list] using [extensions].+    // ##########################################################################+    SelectorListObj extendList(+      const SelectorListObj& list,+      const ExtSelExtMap& extensions,+      const CssMediaRuleObj& mediaContext);++    // ##########################################################################+    // Extends [complex] using [extensions], and+    // returns the contents of a [SelectorList].+    // ##########################################################################+    std::vector<ComplexSelectorObj> extendComplex(+      // Taking in a reference here makes MSVC debug stuck!?+      const ComplexSelectorObj& list,+      const ExtSelExtMap& extensions,+      const CssMediaRuleObj& mediaQueryContext);++    // ##########################################################################+    // Returns a one-off [Extension] whose+    // extender is composed solely of [simple].+    // ##########################################################################+    Extension extensionForSimple(+      const SimpleSelectorObj& simple) const;++    // ##########################################################################+    // Returns a one-off [Extension] whose extender is composed+    // solely of a compound selector containing [simples].+    // ##########################################################################+    Extension extensionForCompound(+      // Taking in a reference here makes MSVC debug stuck!?+      const std::vector<SimpleSelectorObj>& simples) const;++    // ##########################################################################+    // Extends [compound] using [extensions], and returns the+    // contents of a [SelectorList]. The [inOriginal] parameter+    // indicates whether this is in an original complex selector,+    // meaning that [compound] should not be trimmed out.+    // ##########################################################################+    std::vector<ComplexSelectorObj> extendCompound(+      const CompoundSelectorObj& compound,+      const ExtSelExtMap& extensions,+      const CssMediaRuleObj& mediaQueryContext,+      bool inOriginal = false);++    // ##########################################################################+    // Extends [simple] without extending the+    // contents of any selector pseudos it contains.+    // ##########################################################################+    std::vector<Extension> extendWithoutPseudo(+      const SimpleSelectorObj& simple,+      const ExtSelExtMap& extensions,+      ExtSmplSelSet* targetsUsed) const;++    // ##########################################################################+    // Extends [simple] and also extending the+    // contents of any selector pseudos it contains.+    // ##########################################################################+    std::vector<std::vector<Extension>> extendSimple(+      const SimpleSelectorObj& simple,+      const ExtSelExtMap& extensions,+      const CssMediaRuleObj& mediaQueryContext,+      ExtSmplSelSet* targetsUsed);++    // ##########################################################################+    // Inner loop helper for [extendPseudo] function+    // ##########################################################################+    static std::vector<ComplexSelectorObj> extendPseudoComplex(+      const ComplexSelectorObj& complex,+      const Pseudo_Selector_Obj& pseudo,+      const CssMediaRuleObj& mediaQueryContext);++    // ##########################################################################+    // Extends [pseudo] using [extensions], and returns+    // a list of resulting pseudo selectors.+    // ##########################################################################+    std::vector<Pseudo_Selector_Obj> extendPseudo(+      const Pseudo_Selector_Obj& pseudo,+      const ExtSelExtMap& extensions,+      const CssMediaRuleObj& mediaQueryContext);++    // ##########################################################################+    // Rotates the element in list from [start] (inclusive) to [end] (exclusive)+    // one index higher, looping the final element back to [start].+    // ##########################################################################+    static void rotateSlice(+      std::vector<ComplexSelectorObj>& list,+      size_t start, size_t end);++    // ##########################################################################+    // Removes elements from [selectors] if they're subselectors of other+    // elements. The [isOriginal] callback indicates which selectors are+    // original to the document, and thus should never be trimmed.+    // ##########################################################################+    std::vector<ComplexSelectorObj> trim(+      const std::vector<ComplexSelectorObj>& selectors,+      const ExtCplxSelSet& set) const;++    // ##########################################################################+    // Returns the maximum specificity of the given [simple] source selector.+    // ##########################################################################+    size_t maxSourceSpecificity(const SimpleSelectorObj& simple) const;++    // ##########################################################################+    // Returns the maximum specificity for sources that went into producing [compound].+    // ##########################################################################+    size_t maxSourceSpecificity(const CompoundSelectorObj& compound) const;++    // ##########################################################################+    // Helper function used as callbacks on lists+    // ##########################################################################+    static bool dontTrimComplex(+      const ComplexSelector* complex2,+      const ComplexSelector* complex1,+      const size_t maxSpecificity);++    // ##########################################################################+    // Helper function used as callbacks on lists+    // ##########################################################################+    static bool hasExactlyOne(const ComplexSelectorObj& vec);+    static bool hasMoreThanOne(const ComplexSelectorObj& vec);++  };++}++#endif
+ libsass/src/extension.cpp view
@@ -0,0 +1,43 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast_helpers.hpp"+#include "extension.hpp"+#include "ast.hpp"++namespace Sass {++  // ##########################################################################+  // Static function to create a copy with a new extender+  // ##########################################################################+  Extension Extension::withExtender(const ComplexSelectorObj& newExtender) const+  {+    Extension extension(newExtender);+    extension.specificity = specificity;+    extension.isOptional = isOptional;+    extension.target = target;+    return extension;+  }++  // ##########################################################################+  // Asserts that the [mediaContext] for a selector is+  // compatible with the query context for this extender.+  // ##########################################################################+  void Extension::assertCompatibleMediaContext(CssMediaRuleObj mediaQueryContext, Backtraces& traces) const+  {++    if (this->mediaContext.isNull()) return;++    if (mediaQueryContext && ObjPtrEqualityFn(mediaContext->block(), mediaQueryContext->block())) return;++    if (ObjEqualityFn<CssMediaRuleObj>(mediaQueryContext, mediaContext)) return;++    throw Exception::ExtendAcrossMedia(traces, *this);++  }++  // ##########################################################################+  // ##########################################################################++}
+ libsass/src/extension.hpp view
@@ -0,0 +1,89 @@+#ifndef SASS_EXTENSION_H+#define SASS_EXTENSION_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <unordered_map>+#include <unordered_set>+#include "ast_fwd_decl.hpp"+#include "backtrace.hpp"++namespace Sass {++  class Extension {++  public:++    // The selector in which the `@extend` appeared.+    ComplexSelectorObj extender;++    // The selector that's being extended.+    // `null` for one-off extensions.+    SimpleSelectorObj target;++    // The minimum specificity required for any+    // selector generated from this extender.+    size_t specificity;++    // Whether this extension is optional.+    bool isOptional;++    // Whether this is a one-off extender representing a selector that was+    // originally in the document, rather than one defined with `@extend`.+    bool isOriginal;++    bool isSatisfied;++    // The media query context to which this extend is restricted,+    // or `null` if it can apply within any context.+    CssMediaRuleObj mediaContext;++    // Creates a one-off extension that's not intended to be modified over time.+    // If [specificity] isn't passed, it defaults to `extender.maxSpecificity`.+    Extension(ComplexSelectorObj extender) :+      extender(extender),+      target({}),+      specificity(0),+      isOptional(true),+      isOriginal(false),+      isSatisfied(false),+      mediaContext({}) {++    }++    // Copy constructor+    Extension(const Extension& extension) :+      extender(extension.extender),+      target(extension.target),+      specificity(extension.specificity),+      isOptional(extension.isOptional),+      isOriginal(extension.isOriginal),+      isSatisfied(extension.isSatisfied),+      mediaContext(extension.mediaContext) {++    }++    // Default constructor+    Extension() :+      extender({}),+      target({}),+      specificity(0),+      isOptional(false),+      isOriginal(false),+      isSatisfied(false),+      mediaContext({}) {+    }++    // Asserts that the [mediaContext] for a selector is +    // compatible with the query context for this extender.+    void assertCompatibleMediaContext(CssMediaRuleObj mediaContext, Backtraces& traces) const;++    Extension withExtender(const ComplexSelectorObj& newExtender) const;++  };++}++#endif
libsass/src/file.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #ifdef _WIN32 # ifdef __MINGW32__ #  ifndef off64_t@@ -10,9 +13,7 @@ #else # include <unistd.h> #endif-#include <iostream>-#include <fstream>-#include <cctype>+#include <cstdio> #include <vector> #include <algorithm> #include <sys/stat.h>@@ -21,6 +22,9 @@ #include "prelexer.hpp" #include "utf8_string.hpp" #include "sass_functions.hpp"+#include "error_handling.hpp"+#include "util.hpp"+#include "util_string.hpp" #include "sass2scss.h"  #ifdef _WIN32@@ -79,7 +83,10 @@         wchar_t resolved[32768];         // windows unicode filepaths are encoded in utf16         std::string abspath(join_paths(get_cwd(), path));-        std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));+        if (!(abspath[0] == '/' && abspath[1] == '/')) {+          abspath = "//?/" + abspath;+        }+        std::wstring wpath(UTF_8::convert_to_utf16(abspath));         std::replace(wpath.begin(), wpath.end(), '/', '\\');         DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);         if (rv > 32767) throw Exception::OperationError("Path is too long");@@ -99,19 +106,19 @@     bool is_absolute_path(const std::string& path)     {       #ifdef _WIN32-        if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;+        if (path.length() >= 2 && Util::ascii_isalpha(path[0]) && path[1] == ':') return true;       #endif       size_t i = 0;       // check if we have a protocol-      if (path[i] && Prelexer::is_alpha(path[i])) {+      if (path[i] && Util::ascii_isalpha(static_cast<unsigned char>(path[i]))) {         // skip over all alphanumeric characters-        while (path[i] && Prelexer::is_alnum(path[i])) ++i;+        while (path[i] && Util::ascii_isalnum(static_cast<unsigned char>(path[i]))) ++i;         i = i && path[i] == ':' ? i + 1 : 0;       }       return path[i] == '/';     } -    // helper function to find the last directory seperator+    // helper function to find the last directory separator     inline size_t find_last_folder_separator(const std::string& path, size_t limit = std::string::npos)     {       size_t pos;@@ -166,15 +173,15 @@       while((pos = path.find("/./", pos)) != std::string::npos) path.erase(pos, 2);        // remove all leading and trailing self references-      while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);-      while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);+      while(path.size() >= 2 && path[0] == '.' && path[1] == '/') path.erase(0, 2);+      while((pos = path.length()) > 1 && path[pos - 2] == '/' && path[pos - 1] == '.') path.erase(pos - 2);         size_t proto = 0;       // check if we have a protocol-      if (path[proto] && Prelexer::is_alpha(path[proto])) {+      if (path[proto] && Util::ascii_isalpha(static_cast<unsigned char>(path[proto]))) {         // skip over all alphanumeric characters-        while (path[proto] && Prelexer::is_alnum(path[proto++])) {}+        while (path[proto] && Util::ascii_isalnum(static_cast<unsigned char>(path[proto++]))) {}         // then skip over the mandatory colon         if (proto && path[proto] == ':') ++ proto;       }@@ -253,9 +260,9 @@        size_t proto = 0;       // check if we have a protocol-      if (path[proto] && Prelexer::is_alpha(path[proto])) {+      if (path[proto] && Util::ascii_isalpha(static_cast<unsigned char>(path[proto]))) {         // skip over all alphanumeric characters-        while (path[proto] && Prelexer::is_alnum(path[proto++])) {}+        while (path[proto] && Util::ascii_isalnum(static_cast<unsigned char>(path[proto++]))) {}         // then skip over the mandatory colon         if (proto && path[proto] == ':') ++ proto;       }@@ -281,7 +288,8 @@         #else           // compare the charactes in a case insensitive manner           // windows fs is only case insensitive in ascii ranges-          if (tolower(abs_path[i]) != tolower(abs_base[i])) break;+          if (Util::ascii_tolower(static_cast<unsigned char>(abs_path[i])) !=+              Util::ascii_tolower(static_cast<unsigned char>(abs_base[i]))) break;         #endif         if (abs_path[i] == '/') index = i + 1;       }@@ -323,6 +331,8 @@     // (2) underscore + given     // (3) underscore + given + extension     // (4) given + extension+    // (5) given + _index.scss+    // (6) given + _index.sass     std::vector<Include> resolve_includes(const std::string& root, const std::string& file, const std::vector<std::string>& exts)     {       std::string filename = join_paths(root, file);@@ -350,6 +360,25 @@         abs_path = join_paths(root, rel_path);         if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });       }+      // index files+      if (includes.size() == 0) {+        // ignore directories that look like @import'able filename+        for(auto ext : exts) {+          if (ends_with(name, ext)) return includes;+        }+        // next test underscore index exts+        for(auto ext : exts) {+          rel_path = join_paths(base, join_paths(name, "_index" + ext));+          abs_path = join_paths(root, rel_path);+          if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+        }+        // next test plain index exts+        for(auto ext : exts) {+          rel_path = join_paths(base, join_paths(name, "index" + ext));+          abs_path = join_paths(root, rel_path);+          if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+        }+      }       // nothing found       return includes;     }@@ -411,7 +440,10 @@         wchar_t resolved[32768];         // windows unicode filepaths are encoded in utf16         std::string abspath(join_paths(get_cwd(), path));-        std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));+        if (!(abspath[0] == '/' && abspath[1] == '/')) {+          abspath = "//?/" + abspath;+        }+        std::wstring wpath(UTF_8::convert_to_utf16(abspath));         std::replace(wpath.begin(), wpath.end(), '/', '\\');         DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);         if (rv > 32767) throw Exception::OperationError("Path is too long");@@ -430,28 +462,34 @@         // just convert from unsigned char*         char* contents = (char*) pBuffer;       #else+        // Read the file using `<cstdio>` instead of `<fstream>` for better portability.+        // The `<fstream>` header initializes `<locale>` and this buggy in GCC4/5 with static linking.+        // See:+        // https://www.spinics.net/lists/gcchelp/msg46851.html+        // https://github.com/sass/sassc-ruby/issues/128         struct stat st;         if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;-        std::ifstream file(path.c_str(), std::ios::in | std::ios::binary | std::ios::ate);-        char* contents = 0;-        if (file.is_open()) {-          size_t size = file.tellg();-          // allocate an extra byte for the null char-          // and another one for edge-cases in lexer-          contents = (char*) malloc((size+2)*sizeof(char));-          file.seekg(0, std::ios::beg);-          file.read(contents, size);-          contents[size+0] = '\0';-          contents[size+1] = '\0';-          file.close();+        FILE* fd = std::fopen(path.c_str(), "rb");+        if (fd == nullptr) return nullptr;+        const std::size_t size = st.st_size;+        char* contents = static_cast<char*>(malloc(st.st_size + 2 * sizeof(char)));+        if (std::fread(static_cast<void*>(contents), 1, size, fd) != size) {+          free(contents);+          std::fclose(fd);+          return nullptr;         }+        if (std::fclose(fd) != 0) {+          free(contents);+          return nullptr;+        }+        contents[size] = '\0';+        contents[size + 1] = '\0';       #endif       std::string extension;       if (path.length() > 5) {         extension = path.substr(path.length() - 5, 5);       }-      for(size_t i=0; i<extension.size();++i)-        extension[i] = tolower(extension[i]);+      Util::ascii_str_tolower(&extension);       if (extension == ".sass" && contents != 0) {         char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);         free(contents); // free the indented contents
libsass/src/file.hpp view
@@ -1,6 +1,10 @@ #ifndef SASS_FILE_H #define SASS_FILE_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include <string> #include <vector> @@ -100,22 +104,11 @@     public:       // the file contents       char* contents;-      // conected sourcemap+      // connected sourcemap       char* srcmap;     public:       Resource(char* contents, char* srcmap)       : contents(contents), srcmap(srcmap)-      { }-  };--  // parsed stylesheet from loaded resource-  class StyleSheet : public Resource {-    public:-      // parsed root block-      Block_Obj root;-    public:-      StyleSheet(const Resource& res, Block_Obj root)-      : Resource(res), root(root)       { }   }; 
+ libsass/src/fn_colors.cpp view
@@ -0,0 +1,596 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <iomanip>+#include "ast.hpp"+#include "fn_utils.hpp"+#include "fn_colors.hpp"+#include "util.hpp"+#include "util_string.hpp"++namespace Sass {++  namespace Functions {++    bool string_argument(AST_Node_Obj obj) {+      String_Constant* s = Cast<String_Constant>(obj);+      if (s == nullptr) return false;+      const std::string& str = s->value();+      return starts_with(str, "calc(") ||+             starts_with(str, "var(");+    }++    void hsla_alpha_percent_deprecation(const ParserState& pstate, const std::string val)+    {++      std::string msg("Passing a percentage as the alpha value to hsla() will be interpreted");+      std::string tail("differently in future versions of Sass. For now, use " + val + " instead.");++      deprecated(msg, tail, false, pstate);++    }++    Signature rgb_sig = "rgb($red, $green, $blue)";+    BUILT_IN(rgb)+    {+      if (+        string_argument(env["$red"]) ||+        string_argument(env["$green"]) ||+        string_argument(env["$blue"])+      ) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("+                                                        + env["$red"]->to_string()+                                                        + ", "+                                                        + env["$green"]->to_string()+                                                        + ", "+                                                        + env["$blue"]->to_string()+                                                        + ")"+        );+      }++      return SASS_MEMORY_NEW(Color_RGBA,+                             pstate,+                             COLOR_NUM("$red"),+                             COLOR_NUM("$green"),+                             COLOR_NUM("$blue"));+    }++    Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";+    BUILT_IN(rgba_4)+    {+      if (+        string_argument(env["$red"]) ||+        string_argument(env["$green"]) ||+        string_argument(env["$blue"]) ||+        string_argument(env["$alpha"])+      ) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("+                                                        + env["$red"]->to_string()+                                                        + ", "+                                                        + env["$green"]->to_string()+                                                        + ", "+                                                        + env["$blue"]->to_string()+                                                        + ", "+                                                        + env["$alpha"]->to_string()+                                                        + ")"+        );+      }++      return SASS_MEMORY_NEW(Color_RGBA,+                             pstate,+                             COLOR_NUM("$red"),+                             COLOR_NUM("$green"),+                             COLOR_NUM("$blue"),+                             ALPHA_NUM("$alpha"));+    }++    Signature rgba_2_sig = "rgba($color, $alpha)";+    BUILT_IN(rgba_2)+    {+      if (+        string_argument(env["$color"])+      ) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("+                                                        + env["$color"]->to_string()+                                                        + ", "+                                                        + env["$alpha"]->to_string()+                                                        + ")"+        );+      }++      Color_RGBA_Obj c_arg = ARG("$color", Color)->toRGBA();++      if (+        string_argument(env["$alpha"])+      ) {+        std::stringstream strm;+        strm << "rgba("+                 << (int)c_arg->r() << ", "+                 << (int)c_arg->g() << ", "+                 << (int)c_arg->b() << ", "+                 << env["$alpha"]->to_string()+             << ")";+        return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());+      }++      Color_RGBA_Obj new_c = SASS_MEMORY_COPY(c_arg);+      new_c->a(ALPHA_NUM("$alpha"));+      new_c->disp("");+      return new_c.detach();+    }++    ////////////////+    // RGB FUNCTIONS+    ////////////////++    Signature red_sig = "red($color)";+    BUILT_IN(red)+    {+      Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();+      return SASS_MEMORY_NEW(Number, pstate, color->r());+    }++    Signature green_sig = "green($color)";+    BUILT_IN(green)+    {+      Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();+      return SASS_MEMORY_NEW(Number, pstate, color->g());+    }++    Signature blue_sig = "blue($color)";+    BUILT_IN(blue)+    {+      Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();+      return SASS_MEMORY_NEW(Number, pstate, color->b());+    }++    Color_RGBA* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) {+      Color_RGBA_Obj c1 = color1->toRGBA();+      Color_RGBA_Obj c2 = color2->toRGBA();+      double p = weight/100;+      double w = 2*p - 1;+      double a = c1->a() - c2->a();++      double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;+      double w2 = 1 - w1;++      return SASS_MEMORY_NEW(Color_RGBA,+                             pstate,+                             Sass::round(w1*c1->r() + w2*c2->r(), ctx.c_options.precision),+                             Sass::round(w1*c1->g() + w2*c2->g(), ctx.c_options.precision),+                             Sass::round(w1*c1->b() + w2*c2->b(), ctx.c_options.precision),+                             c1->a()*p + c2->a()*(1-p));+    }++    Signature mix_sig = "mix($color1, $color2, $weight: 50%)";+    BUILT_IN(mix)+    {+      Color_Obj  color1 = ARG("$color1", Color);+      Color_Obj  color2 = ARG("$color2", Color);+      double weight = DARG_U_PRCT("$weight");+      return colormix(ctx, pstate, color1, color2, weight);++    }++    ////////////////+    // HSL FUNCTIONS+    ////////////////++    Signature hsl_sig = "hsl($hue, $saturation, $lightness)";+    BUILT_IN(hsl)+    {+      if (+        string_argument(env["$hue"]) ||+        string_argument(env["$saturation"]) ||+        string_argument(env["$lightness"])+      ) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("+                                                        + env["$hue"]->to_string()+                                                        + ", "+                                                        + env["$saturation"]->to_string()+                                                        + ", "+                                                        + env["$lightness"]->to_string()+                                                        + ")"+        );+      }++      return SASS_MEMORY_NEW(Color_HSLA,+        pstate,+        ARGVAL("$hue"),+        ARGVAL("$saturation"),+        ARGVAL("$lightness"),+        1.0);++    }++    Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";+    BUILT_IN(hsla)+    {+      if (+        string_argument(env["$hue"]) ||+        string_argument(env["$saturation"]) ||+        string_argument(env["$lightness"]) ||+        string_argument(env["$alpha"])+      ) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("+                                                        + env["$hue"]->to_string()+                                                        + ", "+                                                        + env["$saturation"]->to_string()+                                                        + ", "+                                                        + env["$lightness"]->to_string()+                                                        + ", "+                                                        + env["$alpha"]->to_string()+                                                        + ")"+        );+      }++      Number* alpha = ARG("$alpha", Number);+      if (alpha && alpha->unit() == "%") {+        Number_Obj val = SASS_MEMORY_COPY(alpha);+        val->numerators.clear(); // convert+        val->value(val->value() / 100.0);+        std::string nr(val->to_string(ctx.c_options));+        hsla_alpha_percent_deprecation(pstate, nr);+      }++      return SASS_MEMORY_NEW(Color_HSLA,+        pstate,+        ARGVAL("$hue"),+        ARGVAL("$saturation"),+        ARGVAL("$lightness"),+        ARGVAL("$alpha"));++    }++    /////////////////////////////////////////////////////////////////////////+    // Query functions+    /////////////////////////////////////////////////////////////////////////++    Signature hue_sig = "hue($color)";+    BUILT_IN(hue)+    {+      Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();+      return SASS_MEMORY_NEW(Number, pstate, col->h(), "deg");+    }++    Signature saturation_sig = "saturation($color)";+    BUILT_IN(saturation)+    {+      Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();+      return SASS_MEMORY_NEW(Number, pstate, col->s(), "%");+    }++    Signature lightness_sig = "lightness($color)";+    BUILT_IN(lightness)+    {+      Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();+      return SASS_MEMORY_NEW(Number, pstate, col->l(), "%");+    }++    /////////////////////////////////////////////////////////////////////////+    // HSL manipulation functions+    /////////////////////////////////////////////////////////////////////////++    Signature adjust_hue_sig = "adjust-hue($color, $degrees)";+    BUILT_IN(adjust_hue)+    {+      Color* col = ARG("$color", Color);+      double degrees = ARGVAL("$degrees");+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->h(absmod(copy->h() + degrees, 360.0));+      return copy.detach();+    }++    Signature lighten_sig = "lighten($color, $amount)";+    BUILT_IN(lighten)+    {+      Color* col = ARG("$color", Color);+      double amount = DARG_U_PRCT("$amount");+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->l(clip(copy->l() + amount, 0.0, 100.0));+      return copy.detach();++    }++    Signature darken_sig = "darken($color, $amount)";+    BUILT_IN(darken)+    {+      Color* col = ARG("$color", Color);+      double amount = DARG_U_PRCT("$amount");+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->l(clip(copy->l() - amount, 0.0, 100.0));+      return copy.detach();+    }++    Signature saturate_sig = "saturate($color, $amount: false)";+    BUILT_IN(saturate)+    {+      // CSS3 filter function overload: pass literal through directly+      if (!Cast<Number>(env["$amount"])) {+        return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");+      }++      Color* col = ARG("$color", Color);+      double amount = DARG_U_PRCT("$amount");+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->s(clip(copy->s() + amount, 0.0, 100.0));+      return copy.detach();+    }++    Signature desaturate_sig = "desaturate($color, $amount)";+    BUILT_IN(desaturate)+    {+      Color* col = ARG("$color", Color);+      double amount = DARG_U_PRCT("$amount");+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->s(clip(copy->s() - amount, 0.0, 100.0));+      return copy.detach();+    }++    Signature grayscale_sig = "grayscale($color)";+    BUILT_IN(grayscale)+    {+      // CSS3 filter function overload: pass literal through directly+      Number* amount = Cast<Number>(env["$color"]);+      if (amount) {+        return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");+      }++      Color* col = ARG("$color", Color);+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->s(0.0); // just reset saturation+      return copy.detach();+    }++    /////////////////////////////////////////////////////////////////////////+    // Misc manipulation functions+    /////////////////////////////////////////////////////////////////////////++    Signature complement_sig = "complement($color)";+    BUILT_IN(complement)+    {+      Color* col = ARG("$color", Color);+      Color_HSLA_Obj copy = col->copyAsHSLA();+      copy->h(absmod(copy->h() - 180.0, 360.0));+      return copy.detach();+    }++    Signature invert_sig = "invert($color, $weight: 100%)";+    BUILT_IN(invert)+    {+      // CSS3 filter function overload: pass literal through directly+      Number* amount = Cast<Number>(env["$color"]);+      double weight = DARG_U_PRCT("$weight");+      if (amount) {+        // TODO: does not throw on 100% manually passed as value+        if (weight < 100.0) {+          error("Only one argument may be passed to the plain-CSS invert() function.", pstate, traces);+        }+        return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");+      }++      Color* col = ARG("$color", Color);+      Color_RGBA_Obj inv = col->copyAsRGBA();+      inv->r(clip(255.0 - inv->r(), 0.0, 255.0));+      inv->g(clip(255.0 - inv->g(), 0.0, 255.0));+      inv->b(clip(255.0 - inv->b(), 0.0, 255.0));+      return colormix(ctx, pstate, inv, col, weight);+    }++    /////////////////////////////////////////////////////////////////////////+    // Opacity functions+    /////////////////////////////////////////////////////////////////////////++    Signature alpha_sig = "alpha($color)";+    Signature opacity_sig = "opacity($color)";+    BUILT_IN(alpha)+    {+      String_Constant* ie_kwd = Cast<String_Constant>(env["$color"]);+      if (ie_kwd) {+        return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");+      }++      // CSS3 filter function overload: pass literal through directly+      Number* amount = Cast<Number>(env["$color"]);+      if (amount) {+        return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");+      }++      return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a());+    }++    Signature opacify_sig = "opacify($color, $amount)";+    Signature fade_in_sig = "fade-in($color, $amount)";+    BUILT_IN(opacify)+    {+      Color* col = ARG("$color", Color);+      double amount = DARG_U_FACT("$amount");+      Color_Obj copy = SASS_MEMORY_COPY(col);+      copy->a(clip(col->a() + amount, 0.0, 1.0));+      return copy.detach();+    }++    Signature transparentize_sig = "transparentize($color, $amount)";+    Signature fade_out_sig = "fade-out($color, $amount)";+    BUILT_IN(transparentize)+    {+      Color* col = ARG("$color", Color);+      double amount = DARG_U_FACT("$amount");+      Color_Obj copy = SASS_MEMORY_COPY(col);+      copy->a(std::max(col->a() - amount, 0.0));+      return copy.detach();+    }++    ////////////////////////+    // OTHER COLOR FUNCTIONS+    ////////////////////////++    Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+    BUILT_IN(adjust_color)+    {+      Color* col = ARG("$color", Color);+      Number* r = Cast<Number>(env["$red"]);+      Number* g = Cast<Number>(env["$green"]);+      Number* b = Cast<Number>(env["$blue"]);+      Number* h = Cast<Number>(env["$hue"]);+      Number* s = Cast<Number>(env["$saturation"]);+      Number* l = Cast<Number>(env["$lightness"]);+      Number* a = Cast<Number>(env["$alpha"]);++      bool rgb = r || g || b;+      bool hsl = h || s || l;++      if (rgb && hsl) {+        error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);+      }+      else if (rgb) {+        Color_RGBA_Obj c = col->copyAsRGBA();+        if (r) c->r(c->r() + DARG_R_BYTE("$red"));+        if (g) c->g(c->g() + DARG_R_BYTE("$green"));+        if (b) c->b(c->b() + DARG_R_BYTE("$blue"));+        if (a) c->a(c->a() + DARG_R_FACT("$alpha"));+        return c.detach();+      }+      else if (hsl) {+        Color_HSLA_Obj c = col->copyAsHSLA();+        if (h) c->h(c->h() + absmod(h->value(), 360.0));+        if (s) c->s(c->s() + DARG_R_PRCT("$saturation"));+        if (l) c->l(c->l() + DARG_R_PRCT("$lightness"));+        if (a) c->a(c->a() + DARG_R_FACT("$alpha"));+        return c.detach();+      }+      else if (a) {+        Color_Obj c = SASS_MEMORY_COPY(col);+        c->a(c->a() + DARG_R_FACT("$alpha"));+        c->a(clip(c->a(), 0.0, 1.0));+        return c.detach();+      }+      error("not enough arguments for `adjust-color'", pstate, traces);+      // unreachable+      return col;+    }++    Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+    BUILT_IN(scale_color)+    {+      Color* col = ARG("$color", Color);+      Number* r = Cast<Number>(env["$red"]);+      Number* g = Cast<Number>(env["$green"]);+      Number* b = Cast<Number>(env["$blue"]);+      Number* h = Cast<Number>(env["$hue"]);+      Number* s = Cast<Number>(env["$saturation"]);+      Number* l = Cast<Number>(env["$lightness"]);+      Number* a = Cast<Number>(env["$alpha"]);++      bool rgb = r || g || b;+      bool hsl = h || s || l;++      if (rgb && hsl) {+        error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);+      }+      else if (rgb) {+        Color_RGBA_Obj c = col->copyAsRGBA();+        double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;+        double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;+        double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;+        double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;+        if (rscale) c->r(c->r() + rscale * (rscale > 0.0 ? 255.0 - c->r() : c->r()));+        if (gscale) c->g(c->g() + gscale * (gscale > 0.0 ? 255.0 - c->g() : c->g()));+        if (bscale) c->b(c->b() + bscale * (bscale > 0.0 ? 255.0 - c->b() : c->b()));+        if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));+        return c.detach();+      }+      else if (hsl) {+        Color_HSLA_Obj c = col->copyAsHSLA();+        double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;+        double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;+        double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;+        double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;+        if (hscale) c->h(c->h() + hscale * (hscale > 0.0 ? 360.0 - c->h() : c->h()));+        if (sscale) c->s(c->s() + sscale * (sscale > 0.0 ? 100.0 - c->s() : c->s()));+        if (lscale) c->l(c->l() + lscale * (lscale > 0.0 ? 100.0 - c->l() : c->l()));+        if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));+        return c.detach();+      }+      else if (a) {+        Color_Obj c = SASS_MEMORY_COPY(col);+        double ascale = DARG_R_PRCT("$alpha") / 100.0;+        c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));+        c->a(clip(c->a(), 0.0, 1.0));+        return c.detach();+      }+      error("not enough arguments for `scale-color'", pstate, traces);+      // unreachable+      return col;+    }++    Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+    BUILT_IN(change_color)+    {+      Color* col = ARG("$color", Color);+      Number* r = Cast<Number>(env["$red"]);+      Number* g = Cast<Number>(env["$green"]);+      Number* b = Cast<Number>(env["$blue"]);+      Number* h = Cast<Number>(env["$hue"]);+      Number* s = Cast<Number>(env["$saturation"]);+      Number* l = Cast<Number>(env["$lightness"]);+      Number* a = Cast<Number>(env["$alpha"]);++      bool rgb = r || g || b;+      bool hsl = h || s || l;++      if (rgb && hsl) {+        error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);+      }+      else if (rgb) {+        Color_RGBA_Obj c = col->copyAsRGBA();+        if (r) c->r(DARG_U_BYTE("$red"));+        if (g) c->g(DARG_U_BYTE("$green"));+        if (b) c->b(DARG_U_BYTE("$blue"));+        if (a) c->a(DARG_U_FACT("$alpha"));+        return c.detach();+      }+      else if (hsl) {+        Color_HSLA_Obj c = col->copyAsHSLA();+        if (h) c->h(absmod(h->value(), 360.0));+        if (s) c->s(DARG_U_PRCT("$saturation"));+        if (l) c->l(DARG_U_PRCT("$lightness"));+        if (a) c->a(DARG_U_FACT("$alpha"));+        return c.detach();+      }+      else if (a) {+        Color_Obj c = SASS_MEMORY_COPY(col);+        c->a(clip(DARG_U_FACT("$alpha"), 0.0, 1.0));+        return c.detach();+      }+      error("not enough arguments for `change-color'", pstate, traces);+      // unreachable+      return col;+    }++    Signature ie_hex_str_sig = "ie-hex-str($color)";+    BUILT_IN(ie_hex_str)+    {+      Color* col = ARG("$color", Color);+      Color_RGBA_Obj c = col->toRGBA();+      double r = clip(c->r(), 0.0, 255.0);+      double g = clip(c->g(), 0.0, 255.0);+      double b = clip(c->b(), 0.0, 255.0);+      double a = clip(c->a(), 0.0, 1.0) * 255.0;++      std::stringstream ss;+      ss << '#' << std::setw(2) << std::setfill('0');+      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));+      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));+      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));+      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision));++      std::string result = ss.str();+      Util::ascii_str_toupper(&result);+      return SASS_MEMORY_NEW(String_Quoted, pstate, result);+    }++  }++}
+ libsass/src/fn_colors.hpp view
@@ -0,0 +1,85 @@+#ifndef SASS_FN_COLORS_H+#define SASS_FN_COLORS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    // macros for common ranges (u mean unsigned or upper, r for full range)+    #define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double+    #define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double+    #define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double+    #define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double+    #define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double+    #define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double++    // macros for color related inputs (rbg and alpha/opacity values)+    #define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double+    #define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double++    extern Signature rgb_sig;+    extern Signature rgba_4_sig;+    extern Signature rgba_2_sig;+    extern Signature red_sig;+    extern Signature green_sig;+    extern Signature blue_sig;+    extern Signature mix_sig;+    extern Signature hsl_sig;+    extern Signature hsla_sig;+    extern Signature hue_sig;+    extern Signature saturation_sig;+    extern Signature lightness_sig;+    extern Signature adjust_hue_sig;+    extern Signature lighten_sig;+    extern Signature darken_sig;+    extern Signature saturate_sig;+    extern Signature desaturate_sig;+    extern Signature grayscale_sig;+    extern Signature complement_sig;+    extern Signature invert_sig;+    extern Signature alpha_sig;+    extern Signature opacity_sig;+    extern Signature opacify_sig;+    extern Signature fade_in_sig;+    extern Signature transparentize_sig;+    extern Signature fade_out_sig;+    extern Signature adjust_color_sig;+    extern Signature scale_color_sig;+    extern Signature change_color_sig;+    extern Signature ie_hex_str_sig;++    BUILT_IN(rgb);+    BUILT_IN(rgba_4);+    BUILT_IN(rgba_2);+    BUILT_IN(red);+    BUILT_IN(green);+    BUILT_IN(blue);+    BUILT_IN(mix);+    BUILT_IN(hsl);+    BUILT_IN(hsla);+    BUILT_IN(hue);+    BUILT_IN(saturation);+    BUILT_IN(lightness);+    BUILT_IN(adjust_hue);+    BUILT_IN(lighten);+    BUILT_IN(darken);+    BUILT_IN(saturate);+    BUILT_IN(desaturate);+    BUILT_IN(grayscale);+    BUILT_IN(complement);+    BUILT_IN(invert);+    BUILT_IN(alpha);+    BUILT_IN(opacify);+    BUILT_IN(transparentize);+    BUILT_IN(adjust_color);+    BUILT_IN(scale_color);+    BUILT_IN(change_color);+    BUILT_IN(ie_hex_str);++  }++}++#endif
+ libsass/src/fn_lists.cpp view
@@ -0,0 +1,285 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "listize.hpp"+#include "operators.hpp"+#include "fn_utils.hpp"+#include "fn_lists.hpp"++namespace Sass {++  namespace Functions {++    /////////////////+    // LIST FUNCTIONS+    /////////////////++    Signature keywords_sig = "keywords($args)";+    BUILT_IN(keywords)+    {+      List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy+      Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1);+      for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {+        Expression_Obj obj = arglist->at(i);+        Argument_Obj arg = (Argument*) obj.ptr(); // XXX+        std::string name = std::string(arg->name());+        name = name.erase(0, 1); // sanitize name (remove dollar sign)+        *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,+                 pstate, name),+                 arg->value());+      }+      return result.detach();+    }++    Signature length_sig = "length($list)";+    BUILT_IN(length)+    {+      if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {+        return SASS_MEMORY_NEW(Number, pstate, (double) sl->length());+      }+      Expression* v = ARG("$list", Expression);+      if (v->concrete_type() == Expression::MAP) {+        Map* map = Cast<Map>(env["$list"]);+        return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1));+      }+      if (v->concrete_type() == Expression::SELECTOR) {+        if (CompoundSelector * h = Cast<CompoundSelector>(v)) {+          return SASS_MEMORY_NEW(Number, pstate, (double)h->length());+        } else if (SelectorList * ls = Cast<SelectorList>(v)) {+          return SASS_MEMORY_NEW(Number, pstate, (double)ls->length());+        } else {+          return SASS_MEMORY_NEW(Number, pstate, 1);+        }+      }++      List* list = Cast<List>(env["$list"]);+      return SASS_MEMORY_NEW(Number,+                             pstate,+                             (double)(list ? list->size() : 1));+    }++    Signature nth_sig = "nth($list, $n)";+    BUILT_IN(nth)+    {+      double nr = ARGVAL("$n");+      Map* m = Cast<Map>(env["$list"]);+      if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {+        size_t len = m ? m->length() : sl->length();+        bool empty = m ? m->empty() : sl->empty();+        if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+        double index = std::floor(nr < 0 ? len + nr : nr - 1);+        if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);+        return Cast<Value>(Listize::perform(sl->get(static_cast<int>(index))));+      }+      List_Obj l = Cast<List>(env["$list"]);+      if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces);+      // if the argument isn't a list, then wrap it in a singleton list+      if (!m && !l) {+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));+      }+      size_t len = m ? m->length() : l->length();+      bool empty = m ? m->empty() : l->empty();+      if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+      double index = std::floor(nr < 0 ? len + nr : nr - 1);+      if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);++      if (m) {+        l = SASS_MEMORY_NEW(List, pstate, 2);+        l->append(m->keys()[static_cast<unsigned int>(index)]);+        l->append(m->at(m->keys()[static_cast<unsigned int>(index)]));+        return l.detach();+      }+      else {+        Value_Obj rv = l->value_at_index(static_cast<int>(index));+        rv->set_delayed(false);+        return rv.detach();+      }+    }++    Signature set_nth_sig = "set-nth($list, $n, $value)";+    BUILT_IN(set_nth)+    {+      Map_Obj m = Cast<Map>(env["$list"]);+      List_Obj l = Cast<List>(env["$list"]);+      Number_Obj n = ARG("$n", Number);+      Expression_Obj v = ARG("$value", Expression);+      if (!l) {+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));+      }+      if (m) {+        l = m->to_list(pstate);+      }+      if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+      double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);+      if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);+      List* result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed());+      for (size_t i = 0, L = l->length(); i < L; ++i) {+        result->append(((i == index) ? v : (*l)[i]));+      }+      return result;+    }++    Signature index_sig = "index($list, $value)";+    BUILT_IN(index)+    {+      Map_Obj m = Cast<Map>(env["$list"]);+      List_Obj l = Cast<List>(env["$list"]);+      Expression_Obj v = ARG("$value", Expression);+      if (!l) {+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));+      }+      if (m) {+        l = m->to_list(pstate);+      }+      for (size_t i = 0, L = l->length(); i < L; ++i) {+        if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));+      }+      return SASS_MEMORY_NEW(Null, pstate);+    }++    Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)";+    BUILT_IN(join)+    {+      Map_Obj m1 = Cast<Map>(env["$list1"]);+      Map_Obj m2 = Cast<Map>(env["$list2"]);+      List_Obj l1 = Cast<List>(env["$list1"]);+      List_Obj l2 = Cast<List>(env["$list2"]);+      String_Constant_Obj sep = ARG("$separator", String_Constant);+      enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);+      Value* bracketed = ARG("$bracketed", Value);+      bool is_bracketed = (l1 ? l1->is_bracketed() : false);+      if (!l1) {+        l1 = SASS_MEMORY_NEW(List, pstate, 1);+        l1->append(ARG("$list1", Expression));+        sep_val = (l2 ? l2->separator() : SASS_SPACE);+        is_bracketed = (l2 ? l2->is_bracketed() : false);+      }+      if (!l2) {+        l2 = SASS_MEMORY_NEW(List, pstate, 1);+        l2->append(ARG("$list2", Expression));+      }+      if (m1) {+        l1 = m1->to_list(pstate);+        sep_val = SASS_COMMA;+      }+      if (m2) {+        l2 = m2->to_list(pstate);+      }+      size_t len = l1->length() + l2->length();+      std::string sep_str = unquote(sep->value());+      if (sep_str == "space") sep_val = SASS_SPACE;+      else if (sep_str == "comma") sep_val = SASS_COMMA;+      else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);+      String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);+      bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";+      if (!bracketed_is_auto) {+        is_bracketed = !bracketed->is_false();+      }+      List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed);+      result->concat(l1);+      result->concat(l2);+      return result.detach();+    }++    Signature append_sig = "append($list, $val, $separator: auto)";+    BUILT_IN(append)+    {+      Map_Obj m = Cast<Map>(env["$list"]);+      List_Obj l = Cast<List>(env["$list"]);+      Expression_Obj v = ARG("$val", Expression);+      if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {+        l = Cast<List>(Listize::perform(sl));+      }+      String_Constant_Obj sep = ARG("$separator", String_Constant);+      if (!l) {+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));+      }+      if (m) {+        l = m->to_list(pstate);+      }+      List* result = SASS_MEMORY_COPY(l);+      std::string sep_str(unquote(sep->value()));+      if (sep_str != "auto") { // check default first+        if (sep_str == "space") result->separator(SASS_SPACE);+        else if (sep_str == "comma") result->separator(SASS_COMMA);+        else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);+      }+      if (l->is_arglist()) {+        result->append(SASS_MEMORY_NEW(Argument,+                                       v->pstate(),+                                       v,+                                       "",+                                       false,+                                       false));++      } else {+        result->append(v);+      }+      return result;+    }++    Signature zip_sig = "zip($lists...)";+    BUILT_IN(zip)+    {+      List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List));+      size_t shortest = 0;+      for (size_t i = 0, L = arglist->length(); i < L; ++i) {+        List_Obj ith = Cast<List>(arglist->value_at_index(i));+        Map_Obj mith = Cast<Map>(arglist->value_at_index(i));+        if (!ith) {+          if (mith) {+            ith = mith->to_list(pstate);+          } else {+            ith = SASS_MEMORY_NEW(List, pstate, 1);+            ith->append(arglist->value_at_index(i));+          }+          if (arglist->is_arglist()) {+            Argument_Obj arg = (Argument*)(arglist->at(i).ptr()); // XXX+            arg->value(ith);+          } else {+            (*arglist)[i] = ith;+          }+        }+        shortest = (i ? std::min(shortest, ith->length()) : ith->length());+      }+      List* zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA);+      size_t L = arglist->length();+      for (size_t i = 0; i < shortest; ++i) {+        List* zipper = SASS_MEMORY_NEW(List, pstate, L);+        for (size_t j = 0; j < L; ++j) {+          zipper->append(Cast<List>(arglist->value_at_index(j))->at(i));+        }+        zippers->append(zipper);+      }+      return zippers;+    }++    Signature list_separator_sig = "list_separator($list)";+    BUILT_IN(list_separator)+    {+      List_Obj l = Cast<List>(env["$list"]);+      if (!l) {+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));+      }+      return SASS_MEMORY_NEW(String_Quoted,+                               pstate,+                               l->separator() == SASS_COMMA ? "comma" : "space");+    }++    Signature is_bracketed_sig = "is-bracketed($list)";+    BUILT_IN(is_bracketed)+    {+      Value_Obj value = ARG("$list", Value);+      List_Obj list = Cast<List>(value);+      return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed());+    }++  }++}
+ libsass/src/fn_lists.hpp view
@@ -0,0 +1,34 @@+#ifndef SASS_FN_LISTS_H+#define SASS_FN_LISTS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    extern Signature length_sig;+    extern Signature nth_sig;+    extern Signature index_sig;+    extern Signature join_sig;+    extern Signature append_sig;+    extern Signature zip_sig;+    extern Signature list_separator_sig;+    extern Signature is_bracketed_sig;+    extern Signature keywords_sig;++    BUILT_IN(length);+    BUILT_IN(nth);+    BUILT_IN(index);+    BUILT_IN(join);+    BUILT_IN(append);+    BUILT_IN(zip);+    BUILT_IN(list_separator);+    BUILT_IN(is_bracketed);+    BUILT_IN(keywords);++  }++}++#endif
+ libsass/src/fn_maps.cpp view
@@ -0,0 +1,94 @@+#include "operators.hpp"+#include "fn_utils.hpp"+#include "fn_maps.hpp"++namespace Sass {++  namespace Functions {++    /////////////////+    // MAP FUNCTIONS+    /////////////////++    Signature map_get_sig = "map-get($map, $key)";+    BUILT_IN(map_get)+    {+      // leaks for "map-get((), foo)" if not Obj+      // investigate why this is (unexpected)+      Map_Obj m = ARGM("$map", Map);+      Expression_Obj v = ARG("$key", Expression);+      try {+        Value_Obj val = m->at(v);+        if (!val) return SASS_MEMORY_NEW(Null, pstate);+        val->set_delayed(false);+        return val.detach();+      } catch (const std::out_of_range&) {+        return SASS_MEMORY_NEW(Null, pstate);+      }+      catch (...) { throw; }+    }++    Signature map_has_key_sig = "map-has-key($map, $key)";+    BUILT_IN(map_has_key)+    {+      Map_Obj m = ARGM("$map", Map);+      Expression_Obj v = ARG("$key", Expression);+      return SASS_MEMORY_NEW(Boolean, pstate, m->has(v));+    }++    Signature map_keys_sig = "map-keys($map)";+    BUILT_IN(map_keys)+    {+      Map_Obj m = ARGM("$map", Map);+      List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);+      for ( auto key : m->keys()) {+        result->append(key);+      }+      return result;+    }++    Signature map_values_sig = "map-values($map)";+    BUILT_IN(map_values)+    {+      Map_Obj m = ARGM("$map", Map);+      List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);+      for ( auto key : m->keys()) {+        result->append(m->at(key));+      }+      return result;+    }++    Signature map_merge_sig = "map-merge($map1, $map2)";+    BUILT_IN(map_merge)+    {+      Map_Obj m1 = ARGM("$map1", Map);+      Map_Obj m2 = ARGM("$map2", Map);++      size_t len = m1->length() + m2->length();+      Map* result = SASS_MEMORY_NEW(Map, pstate, len);+      // concat not implemented for maps+      *result += m1;+      *result += m2;+      return result;+    }++    Signature map_remove_sig = "map-remove($map, $keys...)";+    BUILT_IN(map_remove)+    {+      bool remove;+      Map_Obj m = ARGM("$map", Map);+      List_Obj arglist = ARG("$keys", List);+      Map* result = SASS_MEMORY_NEW(Map, pstate, 1);+      for (auto key : m->keys()) {+        remove = false;+        for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {+          remove = Operators::eq(key, arglist->value_at_index(j));+        }+        if (!remove) *result << std::make_pair(key, m->at(key));+      }+      return result;+    }++  }++}
+ libsass/src/fn_maps.hpp view
@@ -0,0 +1,30 @@+#ifndef SASS_FN_MAPS_H+#define SASS_FN_MAPS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    #define ARGM(argname, argtype) get_arg_m(argname, env, sig, pstate, traces)++    extern Signature map_get_sig;+    extern Signature map_merge_sig;+    extern Signature map_remove_sig;+    extern Signature map_keys_sig;+    extern Signature map_values_sig;+    extern Signature map_has_key_sig;++    BUILT_IN(map_get);+    BUILT_IN(map_merge);+    BUILT_IN(map_remove);+    BUILT_IN(map_keys);+    BUILT_IN(map_values);+    BUILT_IN(map_has_key);++  }++}++#endif
+ libsass/src/fn_miscs.cpp view
@@ -0,0 +1,244 @@+#include "ast.hpp"+#include "expand.hpp"+#include "fn_utils.hpp"+#include "fn_miscs.hpp"+#include "util_string.hpp"++namespace Sass {++  namespace Functions {++    //////////////////////////+    // INTROSPECTION FUNCTIONS+    //////////////////////////++    Signature type_of_sig = "type-of($value)";+    BUILT_IN(type_of)+    {+      Expression* v = ARG("$value", Expression);+      return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());+    }++    Signature variable_exists_sig = "variable-exists($name)";+    BUILT_IN(variable_exists)+    {+      std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++      if(d_env.has("$"+s)) {+        return SASS_MEMORY_NEW(Boolean, pstate, true);+      }+      else {+        return SASS_MEMORY_NEW(Boolean, pstate, false);+      }+    }++    Signature global_variable_exists_sig = "global-variable-exists($name)";+    BUILT_IN(global_variable_exists)+    {+      std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++      if(d_env.has_global("$"+s)) {+        return SASS_MEMORY_NEW(Boolean, pstate, true);+      }+      else {+        return SASS_MEMORY_NEW(Boolean, pstate, false);+      }+    }++    Signature function_exists_sig = "function-exists($name)";+    BUILT_IN(function_exists)+    {+      String_Constant* ss = Cast<String_Constant>(env["$name"]);+      if (!ss) {+        error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);+      }++      std::string name = Util::normalize_underscores(unquote(ss->value()));++      if(d_env.has(name+"[f]")) {+        return SASS_MEMORY_NEW(Boolean, pstate, true);+      }+      else {+        return SASS_MEMORY_NEW(Boolean, pstate, false);+      }+    }++    Signature mixin_exists_sig = "mixin-exists($name)";+    BUILT_IN(mixin_exists)+    {+      std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++      if(d_env.has(s+"[m]")) {+        return SASS_MEMORY_NEW(Boolean, pstate, true);+      }+      else {+        return SASS_MEMORY_NEW(Boolean, pstate, false);+      }+    }++    Signature feature_exists_sig = "feature-exists($feature)";+    BUILT_IN(feature_exists)+    {+      std::string s = unquote(ARG("$feature", String_Constant)->value());++      static const auto *const features = new std::unordered_set<std::string> {+        "global-variable-shadowing",+        "extend-selector-pseudoclass",+        "at-error",+        "units-level-3",+        "custom-property"+      };+      return SASS_MEMORY_NEW(Boolean, pstate, features->find(s) != features->end());+    }++    Signature call_sig = "call($function, $args...)";+    BUILT_IN(call)+    {+      std::string function;+      Function* ff = Cast<Function>(env["$function"]);+      String_Constant* ss = Cast<String_Constant>(env["$function"]);++      if (ss) {+        function = Util::normalize_underscores(unquote(ss->value()));+        std::cerr << "DEPRECATION WARNING: ";+        std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;+        std::cerr << "in Sass 4.0. Use call(get-function(" + quote(function) + ")) instead." << std::endl;+        std::cerr << std::endl;+      } else if (ff) {+        function = ff->name();+      }++      List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));++      Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);+      // std::string full_name(name + "[f]");+      // Definition* def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0;+      // Parameters* params = def ? def->parameters() : 0;+      // size_t param_size = params ? params->length() : 0;+      for (size_t i = 0, L = arglist->length(); i < L; ++i) {+        Expression_Obj expr = arglist->value_at_index(i);+        // if (params && params->has_rest_parameter()) {+        //   Parameter_Obj p = param_size > i ? (*params)[i] : 0;+        //   List* list = Cast<List>(expr);+        //   if (list && p && !p->is_rest_parameter()) expr = (*list)[0];+        // }+        if (arglist->is_arglist()) {+          Expression_Obj obj = arglist->at(i);+          Argument_Obj arg = (Argument*) obj.ptr(); // XXX+          args->append(SASS_MEMORY_NEW(Argument,+                                       pstate,+                                       expr,+                                       arg ? arg->name() : "",+                                       arg ? arg->is_rest_argument() : false,+                                       arg ? arg->is_keyword_argument() : false));+        } else {+          args->append(SASS_MEMORY_NEW(Argument, pstate, expr));+        }+      }+      Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, function, args);++      Expand expand(ctx, &d_env, &selector_stack, &original_stack);+      func->via_call(true); // calc invoke is allowed+      if (ff) func->func(ff);+      return Cast<PreValue>(func->perform(&expand.eval));+    }++    ////////////////////+    // BOOLEAN FUNCTIONS+    ////////////////////++    Signature not_sig = "not($value)";+    BUILT_IN(sass_not)+    {+      return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());+    }++    Signature if_sig = "if($condition, $if-true, $if-false)";+    BUILT_IN(sass_if)+    {+      Expand expand(ctx, &d_env, &selector_stack, &original_stack);+      Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);+      bool is_true = !cond->is_false();+      Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression);+      Value_Obj qwe = Cast<Value>(res->perform(&expand.eval));+      // res = res->perform(&expand.eval.val_eval);+      qwe->set_delayed(false); // clone?+      return qwe.detach();+    }++    //////////////////////////+    // MISCELLANEOUS FUNCTIONS+    //////////////////////////++    Signature inspect_sig = "inspect($value)";+    BUILT_IN(inspect)+    {+      Expression* v = ARG("$value", Expression);+      if (v->concrete_type() == Expression::NULL_VAL) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "null");+      } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {+        return SASS_MEMORY_NEW(String_Constant, pstate, "false");+      } else if (v->concrete_type() == Expression::STRING) {+        String_Constant *s = Cast<String_Constant>(v);+        if (s->quote_mark()) {+          return SASS_MEMORY_NEW(String_Constant, pstate, quote(s->value(), s->quote_mark()));+        } else {+          return s;+        }+      } else {+        // ToDo: fix to_sass for nested parentheses+        Sass_Output_Style old_style;+        old_style = ctx.c_options.output_style;+        ctx.c_options.output_style = TO_SASS;+        Emitter emitter(ctx.c_options);+        Inspect i(emitter);+        i.in_declaration = false;+        v->perform(&i);+        ctx.c_options.output_style = old_style;+        return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer());+      }+    }++    Signature content_exists_sig = "content-exists()";+    BUILT_IN(content_exists)+    {+      if (!d_env.has_global("is_in_mixin")) {+        error("Cannot call content-exists() except within a mixin.", pstate, traces);+      }+      return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));+    }++    Signature get_function_sig = "get-function($name, $css: false)";+    BUILT_IN(get_function)+    {+      String_Constant* ss = Cast<String_Constant>(env["$name"]);+      if (!ss) {+        error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);+      }++      std::string name = Util::normalize_underscores(unquote(ss->value()));+      std::string full_name = name + "[f]";++      Boolean_Obj css = ARG("$css", Boolean);+      if (!css->is_false()) {+        Definition* def = SASS_MEMORY_NEW(Definition,+                                         pstate,+                                         name,+                                         SASS_MEMORY_NEW(Parameters, pstate),+                                         SASS_MEMORY_NEW(Block, pstate, 0, false),+                                         Definition::FUNCTION);+        return SASS_MEMORY_NEW(Function, pstate, def, true);+      }+++      if (!d_env.has_global(full_name)) {+        error("Function not found: " + name, pstate, traces);+      }++      Definition* def = Cast<Definition>(d_env[full_name]);+      return SASS_MEMORY_NEW(Function, pstate, def, false);+    }++  }++}
+ libsass/src/fn_miscs.hpp view
@@ -0,0 +1,40 @@+#ifndef SASS_FN_MISCS_H+#define SASS_FN_MISCS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    extern Signature type_of_sig;+    extern Signature variable_exists_sig;+    extern Signature global_variable_exists_sig;+    extern Signature function_exists_sig;+    extern Signature mixin_exists_sig;+    extern Signature feature_exists_sig;+    extern Signature call_sig;+    extern Signature not_sig;+    extern Signature if_sig;+    extern Signature set_nth_sig;+    extern Signature content_exists_sig;+    extern Signature get_function_sig;++    BUILT_IN(type_of);+    BUILT_IN(variable_exists);+    BUILT_IN(global_variable_exists);+    BUILT_IN(function_exists);+    BUILT_IN(mixin_exists);+    BUILT_IN(feature_exists);+    BUILT_IN(call);+    BUILT_IN(sass_not);+    BUILT_IN(sass_if);+    BUILT_IN(set_nth);+    BUILT_IN(content_exists);+    BUILT_IN(get_function);++  }++}++#endif
+ libsass/src/fn_numbers.cpp view
@@ -0,0 +1,227 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <cstdint>+#include <cstdlib>+#include <cmath>+#include <random>+#include <sstream>+#include <iomanip>+#include <algorithm>++#include "ast.hpp"+#include "units.hpp"+#include "fn_utils.hpp"+#include "fn_numbers.hpp"++#ifdef __MINGW32__+#include "windows.h"+#include "wincrypt.h"+#endif++namespace Sass {++  namespace Functions {++    #ifdef __MINGW32__+      uint64_t GetSeed()+      {+        HCRYPTPROV hp = 0;+        BYTE rb[8];+        CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);+        CryptGenRandom(hp, sizeof(rb), rb);+        CryptReleaseContext(hp, 0);++        uint64_t seed;+        memcpy(&seed, &rb[0], sizeof(seed));++        return seed;+      }+    #else+      uint64_t GetSeed()+      {+        std::random_device rd;+        return rd();+      }+    #endif++    // note: the performance of many implementations of+    // random_device degrades sharply once the entropy pool+    // is exhausted. For practical use, random_device is+    // generally only used to seed a PRNG such as mt19937.+    static std::mt19937 rand(static_cast<unsigned int>(GetSeed()));++    ///////////////////+    // NUMBER FUNCTIONS+    ///////////////////++    Signature percentage_sig = "percentage($number)";+    BUILT_IN(percentage)+    {+      Number_Obj n = ARGN("$number");+      if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces);+      return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");+    }++    Signature round_sig = "round($number)";+    BUILT_IN(round)+    {+      Number_Obj r = ARGN("$number");+      r->value(Sass::round(r->value(), ctx.c_options.precision));+      r->pstate(pstate);+      return r.detach();+    }++    Signature ceil_sig = "ceil($number)";+    BUILT_IN(ceil)+    {+      Number_Obj r = ARGN("$number");+      r->value(std::ceil(r->value()));+      r->pstate(pstate);+      return r.detach();+    }++    Signature floor_sig = "floor($number)";+    BUILT_IN(floor)+    {+      Number_Obj r = ARGN("$number");+      r->value(std::floor(r->value()));+      r->pstate(pstate);+      return r.detach();+    }++    Signature abs_sig = "abs($number)";+    BUILT_IN(abs)+    {+      Number_Obj r = ARGN("$number");+      r->value(std::abs(r->value()));+      r->pstate(pstate);+      return r.detach();+    }++    Signature min_sig = "min($numbers...)";+    BUILT_IN(min)+    {+      List* arglist = ARG("$numbers", List);+      Number_Obj least;+      size_t L = arglist->length();+      if (L == 0) {+        error("At least one argument must be passed.", pstate, traces);+      }+      for (size_t i = 0; i < L; ++i) {+        Expression_Obj val = arglist->value_at_index(i);+        Number_Obj xi = Cast<Number>(val);+        if (!xi) {+          error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces);+        }+        if (least) {+          if (*xi < *least) least = xi;+        } else least = xi;+      }+      return least.detach();+    }++    Signature max_sig = "max($numbers...)";+    BUILT_IN(max)+    {+      List* arglist = ARG("$numbers", List);+      Number_Obj greatest;+      size_t L = arglist->length();+      if (L == 0) {+        error("At least one argument must be passed.", pstate, traces);+      }+      for (size_t i = 0; i < L; ++i) {+        Expression_Obj val = arglist->value_at_index(i);+        Number_Obj xi = Cast<Number>(val);+        if (!xi) {+          error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces);+        }+        if (greatest) {+          if (*greatest < *xi) greatest = xi;+        } else greatest = xi;+      }+      return greatest.detach();+    }++    Signature random_sig = "random($limit:false)";+    BUILT_IN(random)+    {+      AST_Node_Obj arg = env["$limit"];+      Value* v = Cast<Value>(arg);+      Number* l = Cast<Number>(arg);+      Boolean* b = Cast<Boolean>(arg);+      if (l) {+        double lv = l->value();+        if (lv < 1) {+          std::stringstream err;+          err << "$limit " << lv << " must be greater than or equal to 1 for `random'";+          error(err.str(), pstate, traces);+        }+        bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON;+        if (!eq_int) {+          std::stringstream err;+          err << "Expected $limit to be an integer but got " << lv << " for `random'";+          error(err.str(), pstate, traces);+        }+        std::uniform_real_distribution<> distributor(1, lv + 1);+        uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+        return SASS_MEMORY_NEW(Number, pstate, (double)distributed);+      }+      else if (b) {+        std::uniform_real_distribution<> distributor(0, 1);+        double distributed = static_cast<double>(distributor(rand));+        return SASS_MEMORY_NEW(Number, pstate, distributed);+      } else if (v) {+        traces.push_back(Backtrace(pstate));+        throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);+      } else {+        traces.push_back(Backtrace(pstate));+        throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");+      }+    }++    Signature unique_id_sig = "unique-id()";+    BUILT_IN(unique_id)+    {+      std::stringstream ss;+      std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8+      uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+      ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;+      return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str());+    }++    Signature unit_sig = "unit($number)";+    BUILT_IN(unit)+    {+      Number_Obj arg = ARGN("$number");+      std::string str(quote(arg->unit(), '"'));+      return SASS_MEMORY_NEW(String_Quoted, pstate, str);+    }++    Signature unitless_sig = "unitless($number)";+    BUILT_IN(unitless)+    {+      Number_Obj arg = ARGN("$number");+      bool unitless = arg->is_unitless();+      return SASS_MEMORY_NEW(Boolean, pstate, unitless);+    }++    Signature comparable_sig = "comparable($number1, $number2)";+    BUILT_IN(comparable)+    {+      Number_Obj n1 = ARGN("$number1");+      Number_Obj n2 = ARGN("$number2");+      if (n1->is_unitless() || n2->is_unitless()) {+        return SASS_MEMORY_NEW(Boolean, pstate, true);+      }+      // normalize into main units+      n1->normalize(); n2->normalize();+      Units &lhs_unit = *n1, &rhs_unit = *n2;+      bool is_comparable = (lhs_unit == rhs_unit);+      return SASS_MEMORY_NEW(Boolean, pstate, is_comparable);+    }++  }++}
+ libsass/src/fn_numbers.hpp view
@@ -0,0 +1,45 @@+#ifndef SASS_FN_NUMBERS_H+#define SASS_FN_NUMBERS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    // return a number object (copied since we want to have reduced units)+    #define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy++    extern Signature percentage_sig;+    extern Signature round_sig;+    extern Signature ceil_sig;+    extern Signature floor_sig;+    extern Signature abs_sig;+    extern Signature min_sig;+    extern Signature max_sig;+    extern Signature inspect_sig;+    extern Signature random_sig;+    extern Signature unique_id_sig;+    extern Signature unit_sig;+    extern Signature unitless_sig;+    extern Signature comparable_sig;++    BUILT_IN(percentage);+    BUILT_IN(round);+    BUILT_IN(ceil);+    BUILT_IN(floor);+    BUILT_IN(abs);+    BUILT_IN(min);+    BUILT_IN(max);+    BUILT_IN(inspect);+    BUILT_IN(random);+    BUILT_IN(unique_id);+    BUILT_IN(unit);+    BUILT_IN(unitless);+    BUILT_IN(comparable);++  }++}++#endif
+ libsass/src/fn_selectors.cpp view
@@ -0,0 +1,205 @@+#include <numeric>++#include "parser.hpp"+#include "extender.hpp"+#include "listize.hpp"+#include "fn_utils.hpp"+#include "fn_selectors.hpp"++namespace Sass {++  namespace Functions {++    Signature selector_nest_sig = "selector-nest($selectors...)";+    BUILT_IN(selector_nest)+    {+      List* arglist = ARG("$selectors", List);++      // Not enough parameters+      if (arglist->length() == 0) {+        error(+          "$selectors: At least one selector must be passed for `selector-nest'",+          pstate, traces);+      }++      // Parse args into vector of selectors+      SelectorStack parsedSelectors;+      for (size_t i = 0, L = arglist->length(); i < L; ++i) {+        Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i));+        if (exp->concrete_type() == Expression::NULL_VAL) {+          error(+            "$selectors: null is not a valid selector: it must be a string,\n"+            "a list of strings, or a list of lists of strings for 'selector-nest'",+            pstate, traces);+        }+        if (String_Constant_Obj str = Cast<String_Constant>(exp)) {+          str->quote_mark(0);+        }+        std::string exp_src = exp->to_string(ctx.c_options);+        SelectorListObj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);+        parsedSelectors.push_back(sel);+      }++      // Nothing to do+      if( parsedSelectors.empty() ) {+        return SASS_MEMORY_NEW(Null, pstate);+      }++      // Set the first element as the `result`, keep+      // appending to as we go down the parsedSelector vector.+      SelectorStack::iterator itr = parsedSelectors.begin();+      SelectorListObj& result = *itr;+      ++itr;++      for(;itr != parsedSelectors.end(); ++itr) {+        SelectorListObj& child = *itr;+        original_stack.push_back(result);+        SelectorListObj rv = child->resolve_parent_refs(original_stack, traces);+        result->elements(rv->elements());+        original_stack.pop_back();+      }++      return Cast<Value>(Listize::perform(result));+    }++    Signature selector_append_sig = "selector-append($selectors...)";+    BUILT_IN(selector_append)+    {+      List* arglist = ARG("$selectors", List);++      // Not enough parameters+      if (arglist->empty()) {+        error(+          "$selectors: At least one selector must be "+          "passed for `selector-append'",+          pstate, traces);+      }++      // Parse args into vector of selectors+      SelectorStack parsedSelectors;+      parsedSelectors.push_back({});+      for (size_t i = 0, L = arglist->length(); i < L; ++i) {+        Expression* exp = Cast<Expression>(arglist->value_at_index(i));+        if (exp->concrete_type() == Expression::NULL_VAL) {+          error(+            "$selectors: null is not a valid selector: it must be a string,\n"+             "a list of strings, or a list of lists of strings for 'selector-append'",+            pstate, traces);+        }+        if (String_Constant* str = Cast<String_Constant>(exp)) {+          str->quote_mark(0);+        }+        std::string exp_src = exp->to_string();+        SelectorListObj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces,+                                                     exp->pstate(), pstate.src,+                                                     /*allow_parent=*/true);++        for (auto& complex : sel->elements()) {+          if (complex->empty()) {+            complex->append(SASS_MEMORY_NEW(CompoundSelector, "[phony]"));+          }+          if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {+            comp->hasRealParent(true);+            complex->chroots(true);+          }+        }++        if (parsedSelectors.size() > 1) {++          if (!sel->has_real_parent_ref()) {+            auto parent = parsedSelectors.back();+            for (auto& complex : parent->elements()) {+              if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {+                comp->hasRealParent(false);+              }+            }+            error("Can't append \"" + sel->to_string() + "\" to \"" ++              parent->to_string() + "\" for `selector-append'",+              pstate, traces);+          }++          // Build the resolved stack from the left. It's cheaper to directly+          // calculate and update each resolved selcted from the left, than to+          // recursively calculate them from the right side, as we would need+          // to go through the whole stack depth to build the final results.+          // E.g. 'a', 'b', 'x, y' => 'a' => 'a b' => 'a b x, a b y'+          // vs 'a', 'b', 'x, y' => 'x' => 'b x' => 'a b x', 'y' ...+          parsedSelectors.push_back(sel->resolve_parent_refs(parsedSelectors, traces, true));+        }+        else {+          parsedSelectors.push_back(sel);+        }+      }++      // Nothing to do+      if( parsedSelectors.empty() ) {+        return SASS_MEMORY_NEW(Null, pstate);+      }++      return Cast<Value>(Listize::perform(parsedSelectors.back()));+    }++    Signature selector_unify_sig = "selector-unify($selector1, $selector2)";+    BUILT_IN(selector_unify)+    {+      SelectorListObj selector1 = ARGSELS("$selector1");+      SelectorListObj selector2 = ARGSELS("$selector2");+      SelectorListObj result = selector1->unifyWith(selector2);+      return Cast<Value>(Listize::perform(result));+    }++    Signature simple_selectors_sig = "simple-selectors($selector)";+    BUILT_IN(simple_selectors)+    {+      CompoundSelectorObj sel = ARGSEL("$selector");++      List* l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);++      for (size_t i = 0, L = sel->length(); i < L; ++i) {+        const SimpleSelectorObj& ss = sel->get(i);+        std::string ss_string = ss->to_string() ;+        l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));+      }++      return l;+    }++    Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";+    BUILT_IN(selector_extend)+    {+      SelectorListObj selector = ARGSELS("$selector");+      SelectorListObj target = ARGSELS("$extendee");+      SelectorListObj source = ARGSELS("$extender");+      SelectorListObj result = Extender::extend(selector, source, target, traces);+      return Cast<Value>(Listize::perform(result));+    }++    Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";+    BUILT_IN(selector_replace)+    {+      SelectorListObj selector = ARGSELS("$selector");+      SelectorListObj target = ARGSELS("$original");+      SelectorListObj source = ARGSELS("$replacement");+      SelectorListObj result = Extender::replace(selector, source, target, traces);+      return Cast<Value>(Listize::perform(result));+    }++    Signature selector_parse_sig = "selector-parse($selector)";+    BUILT_IN(selector_parse)+    {+      SelectorListObj selector = ARGSELS("$selector");+      return Cast<Value>(Listize::perform(selector));+    }++    Signature is_superselector_sig = "is-superselector($super, $sub)";+    BUILT_IN(is_superselector)+    {+      SelectorListObj sel_sup = ARGSELS("$super");+      SelectorListObj sel_sub = ARGSELS("$sub");+      bool result = sel_sup->isSuperselectorOf(sel_sub);+      return SASS_MEMORY_NEW(Boolean, pstate, result);+    }++  }++}
+ libsass/src/fn_selectors.hpp view
@@ -0,0 +1,35 @@+#ifndef SASS_FN_SELECTORS_H+#define SASS_FN_SELECTORS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    #define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, traces, ctx)+    #define ARGSELS(argname) get_arg_sels(argname, env, sig, pstate, traces, ctx)++    BUILT_IN(selector_nest);+    BUILT_IN(selector_append);+    BUILT_IN(selector_extend);+    BUILT_IN(selector_replace);+    BUILT_IN(selector_unify);+    BUILT_IN(is_superselector);+    BUILT_IN(simple_selectors);+    BUILT_IN(selector_parse);++    extern Signature selector_nest_sig;+    extern Signature selector_append_sig;+    extern Signature selector_extend_sig;+    extern Signature selector_replace_sig;+    extern Signature selector_unify_sig;+    extern Signature is_superselector_sig;+    extern Signature simple_selectors_sig;+    extern Signature selector_parse_sig;++  }++}++#endif
+ libsass/src/fn_strings.cpp view
@@ -0,0 +1,256 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "utf8.h"+#include "ast.hpp"+#include "fn_utils.hpp"+#include "fn_strings.hpp"+#include "util_string.hpp"++namespace Sass {++  namespace Functions {++    void handle_utf8_error (const ParserState& pstate, Backtraces traces)+    {+      try {+       throw;+      }+      catch (utf8::invalid_code_point&) {+        std::string msg("utf8::invalid_code_point");+        error(msg, pstate, traces);+      }+      catch (utf8::not_enough_room&) {+        std::string msg("utf8::not_enough_room");+        error(msg, pstate, traces);+      }+      catch (utf8::invalid_utf8&) {+        std::string msg("utf8::invalid_utf8");+        error(msg, pstate, traces);+      }+      catch (...) { throw; }+    }++    ///////////////////+    // STRING FUNCTIONS+    ///////////////////++    Signature unquote_sig = "unquote($string)";+    BUILT_IN(sass_unquote)+    {+      AST_Node_Obj arg = env["$string"];+      if (String_Quoted* string_quoted = Cast<String_Quoted>(arg)) {+        String_Constant* result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value());+        // remember if the string was quoted (color tokens)+        result->is_delayed(true); // delay colors+        return result;+      }+      else if (String_Constant* str = Cast<String_Constant>(arg)) {+        return str;+      }+      else if (Value* ex = Cast<Value>(arg)) {+        Sass_Output_Style oldstyle = ctx.c_options.output_style;+        ctx.c_options.output_style = SASS_STYLE_NESTED;+        std::string val(arg->to_string(ctx.c_options));+        val = Cast<Null>(arg) ? "null" : val;+        ctx.c_options.output_style = oldstyle;++        deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);+        return ex;+      }+      throw std::runtime_error("Invalid Data Type for unquote");+    }++    Signature quote_sig = "quote($string)";+    BUILT_IN(sass_quote)+    {+      const String_Constant* s = ARG("$string", String_Constant);+      String_Quoted *result = SASS_MEMORY_NEW(+          String_Quoted, pstate, s->value(),+          /*q=*/'\0', /*keep_utf8_escapes=*/false, /*skip_unquoting=*/true);+      result->quote_mark('*');+      return result;+    }++    Signature str_length_sig = "str-length($string)";+    BUILT_IN(str_length)+    {+      size_t len = std::string::npos;+      try {+        String_Constant* s = ARG("$string", String_Constant);+        len = UTF_8::code_point_count(s->value(), 0, s->value().size());++      }+      // handle any invalid utf8 errors+      // other errors will be re-thrown+      catch (...) { handle_utf8_error(pstate, traces); }+      // return something even if we had an error (-1)+      return SASS_MEMORY_NEW(Number, pstate, (double)len);+    }++    Signature str_insert_sig = "str-insert($string, $insert, $index)";+    BUILT_IN(str_insert)+    {+      std::string str;+      try {+        String_Constant* s = ARG("$string", String_Constant);+        str = s->value();+        String_Constant* i = ARG("$insert", String_Constant);+        std::string ins = i->value();+        double index = ARGVAL("$index");+        if (index != (int)index) {+          error("$index: " + std::to_string(index) + " is not an int", pstate, traces);+        }+        size_t len = UTF_8::code_point_count(str, 0, str.size());++        if (index > 0 && index <= len) {+          // positive and within string length+          str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins);+        }+        else if (index > len) {+          // positive and past string length+          str += ins;+        }+        else if (index == 0) {+          str = ins + str;+        }+        else if (std::abs(index) <= len) {+          // negative and within string length+          index += len + 1;+          str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins);+        }+        else {+          // negative and past string length+          str = ins + str;+        }++        if (String_Quoted* ss = Cast<String_Quoted>(s)) {+          if (ss->quote_mark()) str = quote(str);+        }+      }+      // handle any invalid utf8 errors+      // other errors will be re-thrown+      catch (...) { handle_utf8_error(pstate, traces); }+      return SASS_MEMORY_NEW(String_Quoted, pstate, str);+    }++    Signature str_index_sig = "str-index($string, $substring)";+    BUILT_IN(str_index)+    {+      size_t index = std::string::npos;+      try {+        String_Constant* s = ARG("$string", String_Constant);+        String_Constant* t = ARG("$substring", String_Constant);+        std::string str = s->value();+        std::string substr = t->value();++        size_t c_index = str.find(substr);+        if(c_index == std::string::npos) {+          return SASS_MEMORY_NEW(Null, pstate);+        }+        index = UTF_8::code_point_count(str, 0, c_index) + 1;+      }+      // handle any invalid utf8 errors+      // other errors will be re-thrown+      catch (...) { handle_utf8_error(pstate, traces); }+      // return something even if we had an error (-1)+      return SASS_MEMORY_NEW(Number, pstate, (double)index);+    }++    Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";+    BUILT_IN(str_slice)+    {+      std::string newstr;+      try {+        String_Constant* s = ARG("$string", String_Constant);+        double start_at = ARGVAL("$start-at");+        double end_at = ARGVAL("$end-at");++        if (start_at != (int)start_at) {+          error("$start-at: " + std::to_string(start_at) + " is not an int", pstate, traces);+        }++        String_Quoted* ss = Cast<String_Quoted>(s);++        std::string str(s->value());++        size_t size = utf8::distance(str.begin(), str.end());++        if (!Cast<Number>(env["$end-at"])) {+          end_at = -1;+        }++        if (end_at != (int)end_at) {+          error("$end-at: " + std::to_string(end_at) + " is not an int", pstate, traces);+        }++        if (end_at == 0 || (end_at + size) < 0) {+          if (ss && ss->quote_mark()) newstr = quote("");+          return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);+        }++        if (end_at < 0) {+          end_at += size + 1;+          if (end_at == 0) end_at = 1;+        }+        if (end_at > size) { end_at = (double)size; }+        if (start_at < 0) {+          start_at += size + 1;+          if (start_at <= 0) start_at = 1;+        }+        else if (start_at == 0) { ++ start_at; }++        if (start_at <= end_at)+        {+          std::string::iterator start = str.begin();+          utf8::advance(start, start_at - 1, str.end());+          std::string::iterator end = start;+          utf8::advance(end, end_at - start_at + 1, str.end());+          newstr = std::string(start, end);+        }+        if (ss) {+          if(ss->quote_mark()) newstr = quote(newstr);+        }+      }+      // handle any invalid utf8 errors+      // other errors will be re-thrown+      catch (...) { handle_utf8_error(pstate, traces); }+      return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);+    }++    Signature to_upper_case_sig = "to-upper-case($string)";+    BUILT_IN(to_upper_case)+    {+      String_Constant* s = ARG("$string", String_Constant);+      std::string str = s->value();+      Util::ascii_str_toupper(&str);++      if (String_Quoted* ss = Cast<String_Quoted>(s)) {+        String_Quoted* cpy = SASS_MEMORY_COPY(ss);+        cpy->value(str);+        return cpy;+      } else {+        return SASS_MEMORY_NEW(String_Quoted, pstate, str);+      }+    }++    Signature to_lower_case_sig = "to-lower-case($string)";+    BUILT_IN(to_lower_case)+    {+      String_Constant* s = ARG("$string", String_Constant);+      std::string str = s->value();+      Util::ascii_str_tolower(&str);++      if (String_Quoted* ss = Cast<String_Quoted>(s)) {+        String_Quoted* cpy = SASS_MEMORY_COPY(ss);+        cpy->value(str);+        return cpy;+      } else {+        return SASS_MEMORY_NEW(String_Quoted, pstate, str);+      }+    }++  }++}
+ libsass/src/fn_strings.hpp view
@@ -0,0 +1,34 @@+#ifndef SASS_FN_STRINGS_H+#define SASS_FN_STRINGS_H++#include "fn_utils.hpp"++namespace Sass {++  namespace Functions {++    extern Signature unquote_sig;+    extern Signature quote_sig;+    extern Signature str_length_sig;+    extern Signature str_insert_sig;+    extern Signature str_index_sig;+    extern Signature str_slice_sig;+    extern Signature to_upper_case_sig;+    extern Signature to_lower_case_sig;+    extern Signature length_sig;++    BUILT_IN(sass_unquote);+    BUILT_IN(sass_quote);+    BUILT_IN(str_length);+    BUILT_IN(str_insert);+    BUILT_IN(str_index);+    BUILT_IN(str_slice);+    BUILT_IN(to_upper_case);+    BUILT_IN(to_lower_case);+    BUILT_IN(length);++  }++}++#endif
+ libsass/src/fn_utils.cpp view
@@ -0,0 +1,155 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "parser.hpp"+#include "fn_utils.hpp"+#include "util_string.hpp"++namespace Sass {++  Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)+  {+    Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]"));+    sig_parser.lex<Prelexer::identifier>();+    std::string name(Util::normalize_underscores(sig_parser.lexed));+    Parameters_Obj params = sig_parser.parse_parameters();+    return SASS_MEMORY_NEW(Definition,+                          ParserState("[built-in function]"),+                          sig,+                          name,+                          params,+                          func,+                          false);+  }++  Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)+  {+    using namespace Prelexer;++    const char* sig = sass_function_get_signature(c_func);+    Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]"));+    // allow to overload generic callback plus @warn, @error and @debug with custom functions+    sig_parser.lex < alternatives < identifier, exactly <'*'>,+                                    exactly < Constants::warn_kwd >,+                                    exactly < Constants::error_kwd >,+                                    exactly < Constants::debug_kwd >+                  >              >();+    std::string name(Util::normalize_underscores(sig_parser.lexed));+    Parameters_Obj params = sig_parser.parse_parameters();+    return SASS_MEMORY_NEW(Definition,+                          ParserState("[c function]"),+                          sig,+                          name,+                          params,+                          c_func);+  }++  namespace Functions {++    std::string function_name(Signature sig)+    {+      std::string str(sig);+      return str.substr(0, str.find('('));+    }++    Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+    {+      AST_Node* value = env[argname];+      if (Map* map = Cast<Map>(value)) return map;+      List* list = Cast<List>(value);+      if (list && list->length() == 0) {+        return SASS_MEMORY_NEW(Map, pstate, 0);+      }+      return get_arg<Map>(argname, env, sig, pstate, traces);+    }++    double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi)+    {+      Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+      Number tmpnr(val);+      tmpnr.reduce();+      double v = tmpnr.value();+      if (!(lo <= v && v <= hi)) {+        std::stringstream msg;+        msg << "argument `" << argname << "` of `" << sig << "` must be between ";+        msg << lo << " and " << hi;+        error(msg.str(), pstate, traces);+      }+      return v;+    }++    Number* get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+    {+      Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+      val = SASS_MEMORY_COPY(val);+      val->reduce();+      return val;+    }++    double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+    {+      Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+      Number tmpnr(val);+      tmpnr.reduce();+      return tmpnr.value();+    }++    double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+    {+      Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+      Number tmpnr(val);+      tmpnr.reduce();+      if (tmpnr.unit() == "%") {+        return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0);+      } else {+        return std::min(std::max(tmpnr.value(), 0.0), 255.0);+      }+    }++    double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) {+      Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+      Number tmpnr(val);+      tmpnr.reduce();+      if (tmpnr.unit() == "%") {+        return std::min(std::max(tmpnr.value(), 0.0), 100.0);+      } else {+        return std::min(std::max(tmpnr.value(), 0.0), 1.0);+      }+    }++    SelectorListObj get_arg_sels(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {+      Expression_Obj exp = ARG(argname, Expression);+      if (exp->concrete_type() == Expression::NULL_VAL) {+        std::stringstream msg;+        msg << argname << ": null is not a valid selector: it must be a string,\n";+        msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";+        error(msg.str(), exp->pstate(), traces);+      }+      if (String_Constant* str = Cast<String_Constant>(exp)) {+        str->quote_mark(0);+      }+      std::string exp_src = exp->to_string(ctx.c_options);+      return Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src, /*allow_parent=*/false);+    }++    CompoundSelectorObj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {+      Expression_Obj exp = ARG(argname, Expression);+      if (exp->concrete_type() == Expression::NULL_VAL) {+        std::stringstream msg;+        msg << argname << ": null is not a string for `" << function_name(sig) << "'";+        error(msg.str(), exp->pstate(), traces);+      }+      if (String_Constant* str = Cast<String_Constant>(exp)) {+        str->quote_mark(0);+      }+      std::string exp_src = exp->to_string(ctx.c_options);+      SelectorListObj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src, /*allow_parent=*/false);+      if (sel_list->length() == 0) return {};+      return sel_list->first()->first();+    }+++  }++}
+ libsass/src/fn_utils.hpp view
@@ -0,0 +1,62 @@+#ifndef SASS_FN_UTILS_H+#define SASS_FN_UTILS_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "units.hpp"+#include "backtrace.hpp"+#include "environment.hpp"+#include "ast_fwd_decl.hpp"+#include "error_handling.hpp"++namespace Sass {++  #define FN_PROTOTYPE \+    Env& env, \+    Env& d_env, \+    Context& ctx, \+    Signature sig, \+    ParserState pstate, \+    Backtraces& traces, \+    SelectorStack selector_stack, \+    SelectorStack original_stack \++  typedef const char* Signature;+  typedef PreValue* (*Native_Function)(FN_PROTOTYPE);+  #define BUILT_IN(name) PreValue* name(FN_PROTOTYPE)++  #define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)+  // special function for weird hsla percent (10px == 10% == 10 != 0.1)+  #define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double++  Definition* make_native_function(Signature, Native_Function, Context& ctx);+  Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);++  namespace Functions {++    template <typename T>+    T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+    {+      T* val = Cast<T>(env[argname]);+      if (!val) {+        error("argument `" + argname + "` of `" + sig + "` must be a " + T::type_name(), pstate, traces);+      }+      return val;+    }++    Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // maps only+    Number* get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // numbers only+    double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // colors only+    double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // colors only+    double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi); // colors only+    double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // shared+    SelectorListObj get_arg_sels(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); // selectors only+    CompoundSelectorObj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); // selectors only++  }++}++#endif
− libsass/src/functions.cpp
@@ -1,2234 +0,0 @@-#include "sass.hpp"-#include "functions.hpp"-#include "ast.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "parser.hpp"-#include "constants.hpp"-#include "inspect.hpp"-#include "extend.hpp"-#include "eval.hpp"-#include "util.hpp"-#include "expand.hpp"-#include "operators.hpp"-#include "utf8_string.hpp"-#include "sass/base.h"-#include "utf8.h"--#include <cstdint>-#include <cstdlib>-#include <cmath>-#include <cctype>-#include <sstream>-#include <string>-#include <iomanip>-#include <iostream>-#include <random>-#include <set>--#ifdef __MINGW32__-#include "windows.h"-#include "wincrypt.h"-#endif--#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)-#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, traces, ctx)--// return a number object (copied since we want to have reduced units)-#define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy--// special function for weird hsla percent (10px == 10% == 10 != 0.1)-#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double--// macros for common ranges (u mean unsigned or upper, r for full range)-#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double-#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double-#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double-#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double-#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double-#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double--// macros for color related inputs (rbg and alpha/opacity values)-#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double-#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double--namespace Sass {-  using std::stringstream;-  using std::endl;--  Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx)-  {-    Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]"));-    sig_parser.lex<Prelexer::identifier>();-    std::string name(Util::normalize_underscores(sig_parser.lexed));-    Parameters_Obj params = sig_parser.parse_parameters();-    return SASS_MEMORY_NEW(Definition,-                           ParserState("[built-in function]"),-                           sig,-                           name,-                           params,-                           func,-                           false);-  }--  Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx)-  {-    using namespace Prelexer;--    const char* sig = sass_function_get_signature(c_func);-    Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]"));-    // allow to overload generic callback plus @warn, @error and @debug with custom functions-    sig_parser.lex < alternatives < identifier, exactly <'*'>,-                                    exactly < Constants::warn_kwd >,-                                    exactly < Constants::error_kwd >,-                                    exactly < Constants::debug_kwd >-                   >              >();-    std::string name(Util::normalize_underscores(sig_parser.lexed));-    Parameters_Obj params = sig_parser.parse_parameters();-    return SASS_MEMORY_NEW(Definition,-                           ParserState("[c function]"),-                           sig,-                           name,-                           params,-                           c_func,-                           false, true);-  }--  std::string function_name(Signature sig)-  {-    std::string str(sig);-    return str.substr(0, str.find('('));-  }--  namespace Functions {--    inline void handle_utf8_error (const ParserState& pstate, Backtraces traces)-    {-      try {-       throw;-      }-      catch (utf8::invalid_code_point) {-        std::string msg("utf8::invalid_code_point");-        error(msg, pstate, traces);-      }-      catch (utf8::not_enough_room) {-        std::string msg("utf8::not_enough_room");-        error(msg, pstate, traces);-      }-      catch (utf8::invalid_utf8) {-        std::string msg("utf8::invalid_utf8");-        error(msg, pstate, traces);-      }-      catch (...) { throw; }-    }--    template <typename T>-    T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)-    {-      // Minimal error handling -- the expectation is that built-ins will be written correctly!-      T* val = Cast<T>(env[argname]);-      if (!val) {-        std::string msg("argument `");-        msg += argname;-        msg += "` of `";-        msg += sig;-        msg += "` must be a ";-        msg += T::type_name();-        error(msg, pstate, traces);-      }-      return val;-    }--    Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx)-    {-      // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Map_Ptr val = Cast<Map>(env[argname]);-      if (val) return val;--      List_Ptr lval = Cast<List>(env[argname]);-      if (lval && lval->length() == 0) return SASS_MEMORY_NEW(Map, pstate, 0);--      // fallback on get_arg for error handling-      val = get_arg<Map>(argname, env, sig, pstate, traces);-      return val;-    }--    double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi)-    {-      // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);-      Number tmpnr(val);-      tmpnr.reduce();-      double v = tmpnr.value();-      if (!(lo <= v && v <= hi)) {-        std::stringstream msg;-        msg << "argument `" << argname << "` of `" << sig << "` must be between ";-        msg << lo << " and " << hi;-        error(msg.str(), pstate, traces);-      }-      return v;-    }--    Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)-    {-      // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);-      val = SASS_MEMORY_COPY(val);-      val->reduce();-      return val;-    }--    double get_arg_v(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)-    {-      // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);-      Number tmpnr(val);-      tmpnr.reduce();-      /*-      if (tmpnr.unit() == "%") {-        tmpnr.value(tmpnr.value() / 100);-        tmpnr.numerators.clear();-      } else {-        if (!tmpnr.is_unitless()) error("argument " + argname + " of `" + std::string(sig) + "` must be unitless", pstate);-      }-      */-      return tmpnr.value();-    }--    double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)-    {-      // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);-      Number tmpnr(val);-      tmpnr.reduce();-      return tmpnr.value();-    }--    double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)-    {-      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);-      Number tmpnr(val);-      tmpnr.reduce();-      if (tmpnr.unit() == "%") {-        return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0);-      } else {-        return std::min(std::max(tmpnr.value(), 0.0), 255.0);-      }-    }---    inline double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) {-      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);-      Number tmpnr(val);-      tmpnr.reduce();-      if (tmpnr.unit() == "%") {-        return std::min(std::max(tmpnr.value(), 0.0), 100.0);-      } else {-        return std::min(std::max(tmpnr.value(), 0.0), 1.0);-      }-    }--    #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, traces, ctx)--    template <typename T>-    T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx);--    template <>-    Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {-      Expression_Obj exp = ARG(argname, Expression);-      if (exp->concrete_type() == Expression::NULL_VAL) {-        std::stringstream msg;-        msg << argname << ": null is not a valid selector: it must be a string,\n";-        msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";-        error(msg.str(), pstate, traces);-      }-      if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {-        str->quote_mark(0);-      }-      std::string exp_src = exp->to_string(ctx.c_options);-      return Parser::parse_selector(exp_src.c_str(), ctx, traces);-    }--    template <>-    Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {-      Expression_Obj exp = ARG(argname, Expression);-      if (exp->concrete_type() == Expression::NULL_VAL) {-        std::stringstream msg;-        msg << argname << ": null is not a string for `" << function_name(sig) << "'";-        error(msg.str(), pstate, traces);-      }-      if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {-        str->quote_mark(0);-      }-      std::string exp_src = exp->to_string(ctx.c_options);-      Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces);-      if (sel_list->length() == 0) return NULL;-      Complex_Selector_Obj first = sel_list->first();-      if (!first->tail()) return first->head();-      return first->tail()->head();-    }--    #ifdef __MINGW32__-    uint64_t GetSeed()-    {-      HCRYPTPROV hp = 0;-      BYTE rb[8];-      CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);-      CryptGenRandom(hp, sizeof(rb), rb);-      CryptReleaseContext(hp, 0);--      uint64_t seed;-      memcpy(&seed, &rb[0], sizeof(seed));--      return seed;-    }-    #else-    uint64_t GetSeed()-    {-      std::random_device rd;-      return rd();-    }-    #endif--    // note: the performance of many  implementations of-    // random_device degrades sharply once the entropy pool-    // is exhausted. For practical use, random_device is-    // generally only used to seed a PRNG such as mt19937.-    static std::mt19937 rand(static_cast<unsigned int>(GetSeed()));--    // features-    static std::set<std::string> features {-      "global-variable-shadowing",-      "extend-selector-pseudoclass",-      "at-error",-      "units-level-3",-      "custom-property"-    };--    ////////////////-    // RGB FUNCTIONS-    ////////////////--    inline bool special_number(String_Constant_Ptr s) {-      if (s) {-        std::string calc("calc(");-        std::string var("var(");-        std::string ss(s->value());-        return std::equal(calc.begin(), calc.end(), ss.begin()) ||-               std::equal(var.begin(), var.end(), ss.begin());-      }-      return false;-    }--    Signature rgb_sig = "rgb($red, $green, $blue)";-    BUILT_IN(rgb)-    {-      if (-        special_number(Cast<String_Constant>(env["$red"])) ||-        special_number(Cast<String_Constant>(env["$green"])) ||-        special_number(Cast<String_Constant>(env["$blue"]))-      ) {-        return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("-                                                        + env["$red"]->to_string()-                                                        + ", "-                                                        + env["$green"]->to_string()-                                                        + ", "-                                                        + env["$blue"]->to_string()-                                                        + ")"-        );-      }--      return SASS_MEMORY_NEW(Color,-                             pstate,-                             COLOR_NUM("$red"),-                             COLOR_NUM("$green"),-                             COLOR_NUM("$blue"));-    }--    Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";-    BUILT_IN(rgba_4)-    {-      if (-        special_number(Cast<String_Constant>(env["$red"])) ||-        special_number(Cast<String_Constant>(env["$green"])) ||-        special_number(Cast<String_Constant>(env["$blue"])) ||-        special_number(Cast<String_Constant>(env["$alpha"]))-      ) {-        return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("-                                                        + env["$red"]->to_string()-                                                        + ", "-                                                        + env["$green"]->to_string()-                                                        + ", "-                                                        + env["$blue"]->to_string()-                                                        + ", "-                                                        + env["$alpha"]->to_string()-                                                        + ")"-        );-      }--      return SASS_MEMORY_NEW(Color,-                             pstate,-                             COLOR_NUM("$red"),-                             COLOR_NUM("$green"),-                             COLOR_NUM("$blue"),-                             ALPHA_NUM("$alpha"));-    }--    Signature rgba_2_sig = "rgba($color, $alpha)";-    BUILT_IN(rgba_2)-    {-      if (-        special_number(Cast<String_Constant>(env["$color"]))-      ) {-        return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("-                                                        + env["$color"]->to_string()-                                                        + ", "-                                                        + env["$alpha"]->to_string()-                                                        + ")"-        );-      }--      Color_Ptr c_arg = ARG("$color", Color);--      if (-        special_number(Cast<String_Constant>(env["$alpha"]))-      ) {-        std::stringstream strm;-        strm << "rgba("-                 << (int)c_arg->r() << ", "-                 << (int)c_arg->g() << ", "-                 << (int)c_arg->b() << ", "-                 << env["$alpha"]->to_string()-             << ")";-        return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());-      }--      Color_Ptr new_c = SASS_MEMORY_COPY(c_arg);-      new_c->a(ALPHA_NUM("$alpha"));-      new_c->disp("");-      return new_c;-    }--    Signature red_sig = "red($color)";-    BUILT_IN(red)-    { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->r()); }--    Signature green_sig = "green($color)";-    BUILT_IN(green)-    { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->g()); }--    Signature blue_sig = "blue($color)";-    BUILT_IN(blue)-    { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); }--    Color* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) {-      double p = weight/100;-      double w = 2*p - 1;-      double a = color1->a() - color2->a();--      double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;-      double w2 = 1 - w1;--      return SASS_MEMORY_NEW(Color,-                             pstate,-                             Sass::round(w1*color1->r() + w2*color2->r(), ctx.c_options.precision),-                             Sass::round(w1*color1->g() + w2*color2->g(), ctx.c_options.precision),-                             Sass::round(w1*color1->b() + w2*color2->b(), ctx.c_options.precision),-                             color1->a()*p + color2->a()*(1-p));-    }--    Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";-    BUILT_IN(mix)-    {-      Color_Obj  color1 = ARG("$color-1", Color);-      Color_Obj  color2 = ARG("$color-2", Color);-      double weight = DARG_U_PRCT("$weight");-      return colormix(ctx, pstate, color1, color2, weight);--    }--    ////////////////-    // HSL FUNCTIONS-    ////////////////--    // RGB to HSL helper function-    struct HSL { double h; double s; double l; };-    HSL rgb_to_hsl(double r, double g, double b)-    {--      // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV-      r /= 255.0; g /= 255.0; b /= 255.0;--      double max = std::max(r, std::max(g, b));-      double min = std::min(r, std::min(g, b));-      double delta = max - min;--      double h = 0;-      double s;-      double l = (max + min) / 2.0;--      if (NEAR_EQUAL(max, min)) {-        h = s = 0; // achromatic-      }-      else {-        if (l < 0.5) s = delta / (max + min);-        else         s = delta / (2.0 - max - min);--        if      (r == max) h = (g - b) / delta + (g < b ? 6 : 0);-        else if (g == max) h = (b - r) / delta + 2;-        else if (b == max) h = (r - g) / delta + 4;-      }--      HSL hsl_struct;-      hsl_struct.h = h / 6 * 360;-      hsl_struct.s = s * 100;-      hsl_struct.l = l * 100;--      return hsl_struct;-    }--    // hue to RGB helper function-    double h_to_rgb(double m1, double m2, double h) {-      while (h < 0) h += 1;-      while (h > 1) h -= 1;-      if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;-      if (h*2.0 < 1) return m2;-      if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;-      return m1;-    }--    Color_Ptr hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate)-    {-      h /= 360.0;-      s /= 100.0;-      l /= 100.0;--      if (l < 0) l = 0;-      if (s < 0) s = 0;-      if (l > 1) l = 1;-      if (s > 1) s = 1;-      while (h < 0) h += 1;-      while (h > 1) h -= 1;--      // if saturation is exacly zero, we loose-      // information for hue, since it will evaluate-      // to zero if converted back from rgb. Setting-      // saturation to a very tiny number solves this.-      if (s == 0) s = 1e-10;--      // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.-      double m2;-      if (l <= 0.5) m2 = l*(s+1.0);-      else m2 = (l+s)-(l*s);-      double m1 = (l*2.0)-m2;-      // round the results -- consider moving this into the Color constructor-      double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0);-      double g = (h_to_rgb(m1, m2, h) * 255.0);-      double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0);--      return SASS_MEMORY_NEW(Color, pstate, r, g, b, a);-    }--    Signature hsl_sig = "hsl($hue, $saturation, $lightness)";-    BUILT_IN(hsl)-    {-      if (-        special_number(Cast<String_Constant>(env["$hue"])) ||-        special_number(Cast<String_Constant>(env["$saturation"])) ||-        special_number(Cast<String_Constant>(env["$lightness"]))-      ) {-        return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("-                                                        + env["$hue"]->to_string()-                                                        + ", "-                                                        + env["$saturation"]->to_string()-                                                        + ", "-                                                        + env["$lightness"]->to_string()-                                                        + ")"-        );-      }--      return hsla_impl(ARGVAL("$hue"),-                       ARGVAL("$saturation"),-                       ARGVAL("$lightness"),-                       1.0,-                       ctx,-                       pstate);-    }--    Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";-    BUILT_IN(hsla)-    {-      if (-        special_number(Cast<String_Constant>(env["$hue"])) ||-        special_number(Cast<String_Constant>(env["$saturation"])) ||-        special_number(Cast<String_Constant>(env["$lightness"])) ||-        special_number(Cast<String_Constant>(env["$alpha"]))-      ) {-        return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("-                                                        + env["$hue"]->to_string()-                                                        + ", "-                                                        + env["$saturation"]->to_string()-                                                        + ", "-                                                        + env["$lightness"]->to_string()-                                                        + ", "-                                                        + env["$alpha"]->to_string()-                                                        + ")"-        );-      }--      return hsla_impl(ARGVAL("$hue"),-                       ARGVAL("$saturation"),-                       ARGVAL("$lightness"),-                       ARGVAL("$alpha"),-                       ctx,-                       pstate);-    }--    Signature hue_sig = "hue($color)";-    BUILT_IN(hue)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      return SASS_MEMORY_NEW(Number, pstate, hsl_color.h, "deg");-    }--    Signature saturation_sig = "saturation($color)";-    BUILT_IN(saturation)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      return SASS_MEMORY_NEW(Number, pstate, hsl_color.s, "%");-    }--    Signature lightness_sig = "lightness($color)";-    BUILT_IN(lightness)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      return SASS_MEMORY_NEW(Number, pstate, hsl_color.l, "%");-    }--    Signature adjust_hue_sig = "adjust-hue($color, $degrees)";-    BUILT_IN(adjust_hue)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      double degrees = ARGVAL("$degrees");-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      return hsla_impl(hsl_color.h + degrees,-                       hsl_color.s,-                       hsl_color.l,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature lighten_sig = "lighten($color, $amount)";-    BUILT_IN(lighten)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      double amount = DARG_U_PRCT("$amount");-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      //Check lightness is not negative before lighten it-      double hslcolorL = hsl_color.l;-      if (hslcolorL < 0) {-        hslcolorL = 0;-      }--      return hsla_impl(hsl_color.h,-                       hsl_color.s,-                       hslcolorL + amount,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature darken_sig = "darken($color, $amount)";-    BUILT_IN(darken)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      double amount = DARG_U_PRCT("$amount");-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());--      //Check lightness if not over 100, before darken it-      double hslcolorL = hsl_color.l;-      if (hslcolorL > 100) {-        hslcolorL = 100;-      }--      return hsla_impl(hsl_color.h,-                       hsl_color.s,-                       hslcolorL - amount,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature saturate_sig = "saturate($color, $amount: false)";-    BUILT_IN(saturate)-    {-      // CSS3 filter function overload: pass literal through directly-      if (!Cast<Number>(env["$amount"])) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");-      }--      double amount = DARG_U_PRCT("$amount");-      Color_Ptr rgb_color = ARG("$color", Color);-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());--      double hslcolorS = hsl_color.s + amount;--      // Saturation cannot be below 0 or above 100-      if (hslcolorS < 0) {-        hslcolorS = 0;-      }-      if (hslcolorS > 100) {-        hslcolorS = 100;-      }--      return hsla_impl(hsl_color.h,-                       hslcolorS,-                       hsl_color.l,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature desaturate_sig = "desaturate($color, $amount)";-    BUILT_IN(desaturate)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      double amount = DARG_U_PRCT("$amount");-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());--      double hslcolorS = hsl_color.s - amount;--      // Saturation cannot be below 0 or above 100-      if (hslcolorS <= 0) {-        hslcolorS = 0;-      }-      if (hslcolorS > 100) {-        hslcolorS = 100;-      }--      return hsla_impl(hsl_color.h,-                       hslcolorS,-                       hsl_color.l,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature grayscale_sig = "grayscale($color)";-    BUILT_IN(grayscale)-    {-      // CSS3 filter function overload: pass literal through directly-      Number_Ptr amount = Cast<Number>(env["$color"]);-      if (amount) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");-      }--      Color_Ptr rgb_color = ARG("$color", Color);-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      return hsla_impl(hsl_color.h,-                       0.0,-                       hsl_color.l,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature complement_sig = "complement($color)";-    BUILT_IN(complement)-    {-      Color_Ptr rgb_color = ARG("$color", Color);-      HSL hsl_color = rgb_to_hsl(rgb_color->r(),-                                 rgb_color->g(),-                                 rgb_color->b());-      return hsla_impl(hsl_color.h - 180.0,-                       hsl_color.s,-                       hsl_color.l,-                       rgb_color->a(),-                       ctx,-                       pstate);-    }--    Signature invert_sig = "invert($color, $weight: 100%)";-    BUILT_IN(invert)-    {-      // CSS3 filter function overload: pass literal through directly-      Number_Ptr amount = Cast<Number>(env["$color"]);-      if (amount) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");-      }--      double weight = DARG_U_PRCT("$weight");-      Color_Ptr rgb_color = ARG("$color", Color);-      Color_Obj inv = SASS_MEMORY_NEW(Color,-                             pstate,-                             255 - rgb_color->r(),-                             255 - rgb_color->g(),-                             255 - rgb_color->b(),-                             rgb_color->a());-      return colormix(ctx, pstate, inv, rgb_color, weight);-    }--    ////////////////////-    // OPACITY FUNCTIONS-    ////////////////////-    Signature alpha_sig = "alpha($color)";-    Signature opacity_sig = "opacity($color)";-    BUILT_IN(alpha)-    {-      String_Constant_Ptr ie_kwd = Cast<String_Constant>(env["$color"]);-      if (ie_kwd) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");-      }--      // CSS3 filter function overload: pass literal through directly-      Number_Ptr amount = Cast<Number>(env["$color"]);-      if (amount) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");-      }--      return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a());-    }--    Signature opacify_sig = "opacify($color, $amount)";-    Signature fade_in_sig = "fade-in($color, $amount)";-    BUILT_IN(opacify)-    {-      Color_Ptr color = ARG("$color", Color);-      double amount = DARG_U_FACT("$amount");-      double alpha = std::min(color->a() + amount, 1.0);-      return SASS_MEMORY_NEW(Color,-                             pstate,-                             color->r(),-                             color->g(),-                             color->b(),-                             alpha);-    }--    Signature transparentize_sig = "transparentize($color, $amount)";-    Signature fade_out_sig = "fade-out($color, $amount)";-    BUILT_IN(transparentize)-    {-      Color_Ptr color = ARG("$color", Color);-      double amount = DARG_U_FACT("$amount");-      double alpha = std::max(color->a() - amount, 0.0);-      return SASS_MEMORY_NEW(Color,-                             pstate,-                             color->r(),-                             color->g(),-                             color->b(),-                             alpha);-    }--    ////////////////////////-    // OTHER COLOR FUNCTIONS-    ////////////////////////--    Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";-    BUILT_IN(adjust_color)-    {-      Color_Ptr color = ARG("$color", Color);-      Number_Ptr r = Cast<Number>(env["$red"]);-      Number_Ptr g = Cast<Number>(env["$green"]);-      Number_Ptr b = Cast<Number>(env["$blue"]);-      Number_Ptr h = Cast<Number>(env["$hue"]);-      Number_Ptr s = Cast<Number>(env["$saturation"]);-      Number_Ptr l = Cast<Number>(env["$lightness"]);-      Number_Ptr a = Cast<Number>(env["$alpha"]);--      bool rgb = r || g || b;-      bool hsl = h || s || l;--      if (rgb && hsl) {-        error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);-      }-      if (rgb) {-        double rr = r ? DARG_R_BYTE("$red") : 0;-        double gg = g ? DARG_R_BYTE("$green") : 0;-        double bb = b ? DARG_R_BYTE("$blue") : 0;-        double aa = a ? DARG_R_FACT("$alpha") : 0;-        return SASS_MEMORY_NEW(Color,-                               pstate,-                               color->r() + rr,-                               color->g() + gg,-                               color->b() + bb,-                               color->a() + aa);-      }-      if (hsl) {-        HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());-        double ss = s ? DARG_R_PRCT("$saturation") : 0;-        double ll = l ? DARG_R_PRCT("$lightness") : 0;-        double aa = a ? DARG_R_FACT("$alpha") : 0;-        return hsla_impl(hsl_struct.h + (h ? h->value() : 0),-                         hsl_struct.s + ss,-                         hsl_struct.l + ll,-                         color->a() + aa,-                         ctx,-                         pstate);-      }-      if (a) {-        return SASS_MEMORY_NEW(Color,-                               pstate,-                               color->r(),-                               color->g(),-                               color->b(),-                               color->a() + (a ? a->value() : 0));-      }-      error("not enough arguments for `adjust-color'", pstate, traces);-      // unreachable-      return color;-    }--    Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";-    BUILT_IN(scale_color)-    {-      Color_Ptr color = ARG("$color", Color);-      Number_Ptr r = Cast<Number>(env["$red"]);-      Number_Ptr g = Cast<Number>(env["$green"]);-      Number_Ptr b = Cast<Number>(env["$blue"]);-      Number_Ptr h = Cast<Number>(env["$hue"]);-      Number_Ptr s = Cast<Number>(env["$saturation"]);-      Number_Ptr l = Cast<Number>(env["$lightness"]);-      Number_Ptr a = Cast<Number>(env["$alpha"]);--      bool rgb = r || g || b;-      bool hsl = h || s || l;--      if (rgb && hsl) {-        error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);-      }-      if (rgb) {-        double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;-        double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;-        double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;-        double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;-        return SASS_MEMORY_NEW(Color,-                               pstate,-                               color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()),-                               color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()),-                               color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()),-                               color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));-      }-      if (hsl) {-        double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;-        double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;-        double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;-        double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;-        HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());-        hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h);-        hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s);-        hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l);-        double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a());-        return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate);-      }-      if (a) {-        double ascale = (DARG_R_PRCT("$alpha")) / 100.0;-        return SASS_MEMORY_NEW(Color,-                               pstate,-                               color->r(),-                               color->g(),-                               color->b(),-                               color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));-      }-      error("not enough arguments for `scale-color'", pstate, traces);-      // unreachable-      return color;-    }--    Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";-    BUILT_IN(change_color)-    {-      Color_Ptr color = ARG("$color", Color);-      Number_Ptr r = Cast<Number>(env["$red"]);-      Number_Ptr g = Cast<Number>(env["$green"]);-      Number_Ptr b = Cast<Number>(env["$blue"]);-      Number_Ptr h = Cast<Number>(env["$hue"]);-      Number_Ptr s = Cast<Number>(env["$saturation"]);-      Number_Ptr l = Cast<Number>(env["$lightness"]);-      Number_Ptr a = Cast<Number>(env["$alpha"]);--      bool rgb = r || g || b;-      bool hsl = h || s || l;--      if (rgb && hsl) {-        error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);-      }-      if (rgb) {-        return SASS_MEMORY_NEW(Color,-                               pstate,-                               r ? DARG_U_BYTE("$red") : color->r(),-                               g ? DARG_U_BYTE("$green") : color->g(),-                               b ? DARG_U_BYTE("$blue") : color->b(),-                               a ? DARG_U_BYTE("$alpha") : color->a());-      }-      if (hsl) {-        HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());-        if (h) hsl_struct.h = std::fmod(h->value(), 360.0);-        if (s) hsl_struct.s = DARG_U_PRCT("$saturation");-        if (l) hsl_struct.l = DARG_U_PRCT("$lightness");-        double alpha = a ? DARG_U_FACT("$alpha") : color->a();-        return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate);-      }-      if (a) {-        double alpha = DARG_U_FACT("$alpha");-        return SASS_MEMORY_NEW(Color,-                               pstate,-                               color->r(),-                               color->g(),-                               color->b(),-                               alpha);-      }-      error("not enough arguments for `change-color'", pstate, traces);-      // unreachable-      return color;-    }--    template <size_t range>-    static double cap_channel(double c) {-      if      (c > range) return range;-      else if (c < 0)     return 0;-      else                return c;-    }--    Signature ie_hex_str_sig = "ie-hex-str($color)";-    BUILT_IN(ie_hex_str)-    {-      Color_Ptr c = ARG("$color", Color);-      double r = cap_channel<0xff>(c->r());-      double g = cap_channel<0xff>(c->g());-      double b = cap_channel<0xff>(c->b());-      double a = cap_channel<1>   (c->a()) * 255;--      std::stringstream ss;-      ss << '#' << std::setw(2) << std::setfill('0');-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision));--      std::string result(ss.str());-      for (size_t i = 0, L = result.length(); i < L; ++i) {-        result[i] = std::toupper(result[i]);-      }-      return SASS_MEMORY_NEW(String_Quoted, pstate, result);-    }--    ///////////////////-    // STRING FUNCTIONS-    ///////////////////--    Signature unquote_sig = "unquote($string)";-    BUILT_IN(sass_unquote)-    {-      AST_Node_Obj arg = env["$string"];-      if (String_Quoted_Ptr string_quoted = Cast<String_Quoted>(arg)) {-        String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value());-        // remember if the string was quoted (color tokens)-        result->is_delayed(true); // delay colors-        return result;-      }-      else if (String_Constant_Ptr str = Cast<String_Constant>(arg)) {-        return str;-      }-      else if (Expression_Ptr ex = Cast<Expression>(arg)) {-        Sass_Output_Style oldstyle = ctx.c_options.output_style;-        ctx.c_options.output_style = SASS_STYLE_NESTED;-        std::string val(arg->to_string(ctx.c_options));-        val = Cast<Null>(arg) ? "null" : val;-        ctx.c_options.output_style = oldstyle;--        deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);-        return ex;-      }-      throw std::runtime_error("Invalid Data Type for unquote");-    }--    Signature quote_sig = "quote($string)";-    BUILT_IN(sass_quote)-    {-      AST_Node_Obj arg = env["$string"];-      // only set quote mark to true if already a string-      if (String_Quoted_Ptr qstr = Cast<String_Quoted>(arg)) {-        qstr->quote_mark('*');-        return qstr;-      }-      // all other nodes must be converted to a string node-      std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote()));-      String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str);-      result->quote_mark('*');-      return result;-    }---    Signature str_length_sig = "str-length($string)";-    BUILT_IN(str_length)-    {-      size_t len = std::string::npos;-      try {-        String_Constant_Ptr s = ARG("$string", String_Constant);-        len = UTF_8::code_point_count(s->value(), 0, s->value().size());--      }-      // handle any invalid utf8 errors-      // other errors will be re-thrown-      catch (...) { handle_utf8_error(pstate, traces); }-      // return something even if we had an error (-1)-      return SASS_MEMORY_NEW(Number, pstate, (double)len);-    }--    Signature str_insert_sig = "str-insert($string, $insert, $index)";-    BUILT_IN(str_insert)-    {-      std::string str;-      try {-        String_Constant_Ptr s = ARG("$string", String_Constant);-        str = s->value();-        str = unquote(str);-        String_Constant_Ptr i = ARG("$insert", String_Constant);-        std::string ins = i->value();-        ins = unquote(ins);-        double index = ARGVAL("$index");-        size_t len = UTF_8::code_point_count(str, 0, str.size());--        if (index > 0 && index <= len) {-          // positive and within string length-          str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins);-        }-        else if (index > len) {-          // positive and past string length-          str += ins;-        }-        else if (index == 0) {-          str = ins + str;-        }-        else if (std::abs(index) <= len) {-          // negative and within string length-          index += len + 1;-          str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins);-        }-        else {-          // negative and past string length-          str = ins + str;-        }--        if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) {-          if (ss->quote_mark()) str = quote(str);-        }-      }-      // handle any invalid utf8 errors-      // other errors will be re-thrown-      catch (...) { handle_utf8_error(pstate, traces); }-      return SASS_MEMORY_NEW(String_Quoted, pstate, str);-    }--    Signature str_index_sig = "str-index($string, $substring)";-    BUILT_IN(str_index)-    {-      size_t index = std::string::npos;-      try {-        String_Constant_Ptr s = ARG("$string", String_Constant);-        String_Constant_Ptr t = ARG("$substring", String_Constant);-        std::string str = s->value();-        str = unquote(str);-        std::string substr = t->value();-        substr = unquote(substr);--        size_t c_index = str.find(substr);-        if(c_index == std::string::npos) {-          return SASS_MEMORY_NEW(Null, pstate);-        }-        index = UTF_8::code_point_count(str, 0, c_index) + 1;-      }-      // handle any invalid utf8 errors-      // other errors will be re-thrown-      catch (...) { handle_utf8_error(pstate, traces); }-      // return something even if we had an error (-1)-      return SASS_MEMORY_NEW(Number, pstate, (double)index);-    }--    Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";-    BUILT_IN(str_slice)-    {-      std::string newstr;-      try {-        String_Constant_Ptr s = ARG("$string", String_Constant);-        double start_at = ARGVAL("$start-at");-        double end_at = ARGVAL("$end-at");-        String_Quoted_Ptr ss = Cast<String_Quoted>(s);--        std::string str = unquote(s->value());--        size_t size = utf8::distance(str.begin(), str.end());--        if (!Cast<Number>(env["$end-at"])) {-          end_at = -1;-        }--        if (end_at == 0 || (end_at + size) < 0) {-          if (ss && ss->quote_mark()) newstr = quote("");-          return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);-        }--        if (end_at < 0) {-          end_at += size + 1;-          if (end_at == 0) end_at = 1;-        }-        if (end_at > size) { end_at = (double)size; }-        if (start_at < 0) {-          start_at += size + 1;-          if (start_at < 0)  start_at = 0;-        }-        else if (start_at == 0) { ++ start_at; }--        if (start_at <= end_at)-        {-          std::string::iterator start = str.begin();-          utf8::advance(start, start_at - 1, str.end());-          std::string::iterator end = start;-          utf8::advance(end, end_at - start_at + 1, str.end());-          newstr = std::string(start, end);-        }-        if (ss) {-          if(ss->quote_mark()) newstr = quote(newstr);-        }-      }-      // handle any invalid utf8 errors-      // other errors will be re-thrown-      catch (...) { handle_utf8_error(pstate, traces); }-      return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);-    }--    Signature to_upper_case_sig = "to-upper-case($string)";-    BUILT_IN(to_upper_case)-    {-      String_Constant_Ptr s = ARG("$string", String_Constant);-      std::string str = s->value();--      for (size_t i = 0, L = str.length(); i < L; ++i) {-        if (Sass::Util::isAscii(str[i])) {-          str[i] = std::toupper(str[i]);-        }-      }--      if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) {-        String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss);-        cpy->value(str);-        return cpy;-      } else {-        return SASS_MEMORY_NEW(String_Quoted, pstate, str);-      }-    }--    Signature to_lower_case_sig = "to-lower-case($string)";-    BUILT_IN(to_lower_case)-    {-      String_Constant_Ptr s = ARG("$string", String_Constant);-      std::string str = s->value();--      for (size_t i = 0, L = str.length(); i < L; ++i) {-        if (Sass::Util::isAscii(str[i])) {-          str[i] = std::tolower(str[i]);-        }-      }--      if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) {-        String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss);-        cpy->value(str);-        return cpy;-      } else {-        return SASS_MEMORY_NEW(String_Quoted, pstate, str);-      }-    }--    ///////////////////-    // NUMBER FUNCTIONS-    ///////////////////--    Signature percentage_sig = "percentage($number)";-    BUILT_IN(percentage)-    {-      Number_Obj n = ARGN("$number");-      if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces);-      return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");-    }--    Signature round_sig = "round($number)";-    BUILT_IN(round)-    {-      Number_Obj r = ARGN("$number");-      r->value(Sass::round(r->value(), ctx.c_options.precision));-      r->pstate(pstate);-      return r.detach();-    }--    Signature ceil_sig = "ceil($number)";-    BUILT_IN(ceil)-    {-      Number_Obj r = ARGN("$number");-      r->value(std::ceil(r->value()));-      r->pstate(pstate);-      return r.detach();-    }--    Signature floor_sig = "floor($number)";-    BUILT_IN(floor)-    {-      Number_Obj r = ARGN("$number");-      r->value(std::floor(r->value()));-      r->pstate(pstate);-      return r.detach();-    }--    Signature abs_sig = "abs($number)";-    BUILT_IN(abs)-    {-      Number_Obj r = ARGN("$number");-      r->value(std::abs(r->value()));-      r->pstate(pstate);-      return r.detach();-    }--    Signature min_sig = "min($numbers...)";-    BUILT_IN(min)-    {-      List_Ptr arglist = ARG("$numbers", List);-      Number_Obj least = NULL;-      for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression_Obj val = arglist->value_at_index(i);-        Number_Obj xi = Cast<Number>(val);-        if (!xi) {-          error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces);-        }-        if (least) {-          if (*xi < *least) least = xi;-        } else least = xi;-      }-      return least.detach();-    }--    Signature max_sig = "max($numbers...)";-    BUILT_IN(max)-    {-      List_Ptr arglist = ARG("$numbers", List);-      Number_Obj greatest = NULL;-      for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression_Obj val = arglist->value_at_index(i);-        Number_Obj xi = Cast<Number>(val);-        if (!xi) {-          error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces);-        }-        if (greatest) {-          if (*greatest < *xi) greatest = xi;-        } else greatest = xi;-      }-      return greatest.detach();-    }--    Signature random_sig = "random($limit:false)";-    BUILT_IN(random)-    {-      AST_Node_Obj arg = env["$limit"];-      Value_Ptr v = Cast<Value>(arg);-      Number_Ptr l = Cast<Number>(arg);-      Boolean_Ptr b = Cast<Boolean>(arg);-      if (l) {-        double lv = l->value();-        if (lv < 1) {-          stringstream err;-          err << "$limit " << lv << " must be greater than or equal to 1 for `random'";-          error(err.str(), pstate, traces);-        }-        bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON;-        if (!eq_int) {-          stringstream err;-          err << "Expected $limit to be an integer but got " << lv << " for `random'";-          error(err.str(), pstate, traces);-        }-        std::uniform_real_distribution<> distributor(1, lv + 1);-        uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));-        return SASS_MEMORY_NEW(Number, pstate, (double)distributed);-      }-      else if (b) {-        std::uniform_real_distribution<> distributor(0, 1);-        double distributed = static_cast<double>(distributor(rand));-        return SASS_MEMORY_NEW(Number, pstate, distributed);-      } else if (v) {-        traces.push_back(Backtrace(pstate));-        throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);-      } else {-        traces.push_back(Backtrace(pstate));-        throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");-      }-    }--    /////////////////-    // LIST FUNCTIONS-    /////////////////--    Signature length_sig = "length($list)";-    BUILT_IN(length)-    {-      if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {-        return SASS_MEMORY_NEW(Number, pstate, (double)sl->length());-      }-      Expression_Ptr v = ARG("$list", Expression);-      if (v->concrete_type() == Expression::MAP) {-        Map_Ptr map = Cast<Map>(env["$list"]);-        return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1));-      }-      if (v->concrete_type() == Expression::SELECTOR) {-        if (Compound_Selector_Ptr h = Cast<Compound_Selector>(v)) {-          return SASS_MEMORY_NEW(Number, pstate, (double)h->length());-        } else if (Selector_List_Ptr ls = Cast<Selector_List>(v)) {-          return SASS_MEMORY_NEW(Number, pstate, (double)ls->length());-        } else {-          return SASS_MEMORY_NEW(Number, pstate, 1);-        }-      }--      List_Ptr list = Cast<List>(env["$list"]);-      return SASS_MEMORY_NEW(Number,-                             pstate,-                             (double)(list ? list->size() : 1));-    }--    Signature nth_sig = "nth($list, $n)";-    BUILT_IN(nth)-    {-      double nr = ARGVAL("$n");-      Map_Ptr m = Cast<Map>(env["$list"]);-      if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {-        size_t len = m ? m->length() : sl->length();-        bool empty = m ? m->empty() : sl->empty();-        if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);-        double index = std::floor(nr < 0 ? len + nr : nr - 1);-        if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);-        // return (*sl)[static_cast<int>(index)];-        Listize listize;-        return (*sl)[static_cast<int>(index)]->perform(&listize);-      }-      List_Obj l = Cast<List>(env["$list"]);-      if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces);-      // if the argument isn't a list, then wrap it in a singleton list-      if (!m && !l) {-        l = SASS_MEMORY_NEW(List, pstate, 1);-        l->append(ARG("$list", Expression));-      }-      size_t len = m ? m->length() : l->length();-      bool empty = m ? m->empty() : l->empty();-      if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);-      double index = std::floor(nr < 0 ? len + nr : nr - 1);-      if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);--      if (m) {-        l = SASS_MEMORY_NEW(List, pstate, 1);-        l->append(m->keys()[static_cast<unsigned int>(index)]);-        l->append(m->at(m->keys()[static_cast<unsigned int>(index)]));-        return l.detach();-      }-      else {-        Expression_Obj rv = l->value_at_index(static_cast<int>(index));-        rv->set_delayed(false);-        return rv.detach();-      }-    }--    Signature set_nth_sig = "set-nth($list, $n, $value)";-    BUILT_IN(set_nth)-    {-      Map_Obj m = Cast<Map>(env["$list"]);-      List_Obj l = Cast<List>(env["$list"]);-      Number_Obj n = ARG("$n", Number);-      Expression_Obj v = ARG("$value", Expression);-      if (!l) {-        l = SASS_MEMORY_NEW(List, pstate, 1);-        l->append(ARG("$list", Expression));-      }-      if (m) {-        l = m->to_list(pstate);-      }-      if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);-      double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);-      if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);-      List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed());-      for (size_t i = 0, L = l->length(); i < L; ++i) {-        result->append(((i == index) ? v : (*l)[i]));-      }-      return result;-    }--    Signature index_sig = "index($list, $value)";-    BUILT_IN(index)-    {-      Map_Obj m = Cast<Map>(env["$list"]);-      List_Obj l = Cast<List>(env["$list"]);-      Expression_Obj v = ARG("$value", Expression);-      if (!l) {-        l = SASS_MEMORY_NEW(List, pstate, 1);-        l->append(ARG("$list", Expression));-      }-      if (m) {-        l = m->to_list(pstate);-      }-      for (size_t i = 0, L = l->length(); i < L; ++i) {-        if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));-      }-      return SASS_MEMORY_NEW(Null, pstate);-    }--    Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)";-    BUILT_IN(join)-    {-      Map_Obj m1 = Cast<Map>(env["$list1"]);-      Map_Obj m2 = Cast<Map>(env["$list2"]);-      List_Obj l1 = Cast<List>(env["$list1"]);-      List_Obj l2 = Cast<List>(env["$list2"]);-      String_Constant_Obj sep = ARG("$separator", String_Constant);-      enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);-      Value* bracketed = ARG("$bracketed", Value);-      bool is_bracketed = (l1 ? l1->is_bracketed() : false);-      if (!l1) {-        l1 = SASS_MEMORY_NEW(List, pstate, 1);-        l1->append(ARG("$list1", Expression));-        sep_val = (l2 ? l2->separator() : SASS_SPACE);-        is_bracketed = (l2 ? l2->is_bracketed() : false);-      }-      if (!l2) {-        l2 = SASS_MEMORY_NEW(List, pstate, 1);-        l2->append(ARG("$list2", Expression));-      }-      if (m1) {-        l1 = m1->to_list(pstate);-        sep_val = SASS_COMMA;-      }-      if (m2) {-        l2 = m2->to_list(pstate);-      }-      size_t len = l1->length() + l2->length();-      std::string sep_str = unquote(sep->value());-      if (sep_str == "space") sep_val = SASS_SPACE;-      else if (sep_str == "comma") sep_val = SASS_COMMA;-      else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);-      String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);-      bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";-      if (!bracketed_is_auto) {-        is_bracketed = !bracketed->is_false();-      }-      List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed);-      result->concat(l1);-      result->concat(l2);-      return result.detach();-    }--    Signature append_sig = "append($list, $val, $separator: auto)";-    BUILT_IN(append)-    {-      Map_Obj m = Cast<Map>(env["$list"]);-      List_Obj l = Cast<List>(env["$list"]);-      Expression_Obj v = ARG("$val", Expression);-      if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {-        Listize listize;-        l = Cast<List>(sl->perform(&listize));-      }-      String_Constant_Obj sep = ARG("$separator", String_Constant);-      if (!l) {-        l = SASS_MEMORY_NEW(List, pstate, 1);-        l->append(ARG("$list", Expression));-      }-      if (m) {-        l = m->to_list(pstate);-      }-      List_Ptr result = SASS_MEMORY_COPY(l);-      std::string sep_str(unquote(sep->value()));-      if (sep_str != "auto") { // check default first-        if (sep_str == "space") result->separator(SASS_SPACE);-        else if (sep_str == "comma") result->separator(SASS_COMMA);-        else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);-      }-      if (l->is_arglist()) {-        result->append(SASS_MEMORY_NEW(Argument,-                                       v->pstate(),-                                       v,-                                       "",-                                       false,-                                       false));--      } else {-        result->append(v);-      }-      return result;-    }--    Signature zip_sig = "zip($lists...)";-    BUILT_IN(zip)-    {-      List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List));-      size_t shortest = 0;-      for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        List_Obj ith = Cast<List>(arglist->value_at_index(i));-        Map_Obj mith = Cast<Map>(arglist->value_at_index(i));-        if (!ith) {-          if (mith) {-            ith = mith->to_list(pstate);-          } else {-            ith = SASS_MEMORY_NEW(List, pstate, 1);-            ith->append(arglist->value_at_index(i));-          }-          if (arglist->is_arglist()) {-            Argument_Obj arg = (Argument_Ptr)(arglist->at(i).ptr()); // XXX-            arg->value(ith);-          } else {-            (*arglist)[i] = ith;-          }-        }-        shortest = (i ? std::min(shortest, ith->length()) : ith->length());-      }-      List_Ptr zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA);-      size_t L = arglist->length();-      for (size_t i = 0; i < shortest; ++i) {-        List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L);-        for (size_t j = 0; j < L; ++j) {-          zipper->append(Cast<List>(arglist->value_at_index(j))->at(i));-        }-        zippers->append(zipper);-      }-      return zippers;-    }--    Signature list_separator_sig = "list_separator($list)";-    BUILT_IN(list_separator)-    {-      List_Obj l = Cast<List>(env["$list"]);-      if (!l) {-        l = SASS_MEMORY_NEW(List, pstate, 1);-        l->append(ARG("$list", Expression));-      }-      return SASS_MEMORY_NEW(String_Quoted,-                               pstate,-                               l->separator() == SASS_COMMA ? "comma" : "space");-    }--    /////////////////-    // MAP FUNCTIONS-    /////////////////--    Signature map_get_sig = "map-get($map, $key)";-    BUILT_IN(map_get)-    {-      // leaks for "map-get((), foo)" if not Obj-      // investigate why this is (unexpected)-      Map_Obj m = ARGM("$map", Map, ctx);-      Expression_Obj v = ARG("$key", Expression);-      try {-        Expression_Obj val = m->at(v);-        if (!val) return SASS_MEMORY_NEW(Null, pstate);-        val->set_delayed(false);-        return val.detach();-      } catch (const std::out_of_range&) {-        return SASS_MEMORY_NEW(Null, pstate);-      }-      catch (...) { throw; }-    }--    Signature map_has_key_sig = "map-has-key($map, $key)";-    BUILT_IN(map_has_key)-    {-      Map_Obj m = ARGM("$map", Map, ctx);-      Expression_Obj v = ARG("$key", Expression);-      return SASS_MEMORY_NEW(Boolean, pstate, m->has(v));-    }--    Signature map_keys_sig = "map-keys($map)";-    BUILT_IN(map_keys)-    {-      Map_Obj m = ARGM("$map", Map, ctx);-      List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);-      for ( auto key : m->keys()) {-        result->append(key);-      }-      return result;-    }--    Signature map_values_sig = "map-values($map)";-    BUILT_IN(map_values)-    {-      Map_Obj m = ARGM("$map", Map, ctx);-      List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);-      for ( auto key : m->keys()) {-        result->append(m->at(key));-      }-      return result;-    }--    Signature map_merge_sig = "map-merge($map1, $map2)";-    BUILT_IN(map_merge)-    {-      Map_Obj m1 = ARGM("$map1", Map, ctx);-      Map_Obj m2 = ARGM("$map2", Map, ctx);--      size_t len = m1->length() + m2->length();-      Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len);-      // concat not implemented for maps-      *result += m1;-      *result += m2;-      return result;-    }--    Signature map_remove_sig = "map-remove($map, $keys...)";-    BUILT_IN(map_remove)-    {-      bool remove;-      Map_Obj m = ARGM("$map", Map, ctx);-      List_Obj arglist = ARG("$keys", List);-      Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, 1);-      for (auto key : m->keys()) {-        remove = false;-        for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {-          remove = Operators::eq(key, arglist->value_at_index(j));-        }-        if (!remove) *result << std::make_pair(key, m->at(key));-      }-      return result;-    }--    Signature keywords_sig = "keywords($args)";-    BUILT_IN(keywords)-    {-      List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy-      Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1);-      for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {-        Expression_Obj obj = arglist->at(i);-        Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX-        std::string name = std::string(arg->name());-        name = name.erase(0, 1); // sanitize name (remove dollar sign)-        *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,-                 pstate, name),-                 arg->value());-      }-      return result.detach();-    }--    //////////////////////////-    // INTROSPECTION FUNCTIONS-    //////////////////////////--    Signature type_of_sig = "type-of($value)";-    BUILT_IN(type_of)-    {-      Expression_Ptr v = ARG("$value", Expression);-      return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());-    }--    Signature unit_sig = "unit($number)";-    BUILT_IN(unit)-    {-      Number_Obj arg = ARGN("$number");-      std::string str(quote(arg->unit(), '"'));-      return SASS_MEMORY_NEW(String_Quoted, pstate, str);-    }--    Signature unitless_sig = "unitless($number)";-    BUILT_IN(unitless)-    {-      Number_Obj arg = ARGN("$number");-      bool unitless = arg->is_unitless();-      return SASS_MEMORY_NEW(Boolean, pstate, unitless);-    }--    Signature comparable_sig = "comparable($number-1, $number-2)";-    BUILT_IN(comparable)-    {-      Number_Obj n1 = ARGN("$number-1");-      Number_Obj n2 = ARGN("$number-2");-      if (n1->is_unitless() || n2->is_unitless()) {-        return SASS_MEMORY_NEW(Boolean, pstate, true);-      }-      // normalize into main units-      n1->normalize(); n2->normalize();-      Units &lhs_unit = *n1, &rhs_unit = *n2;-      bool is_comparable = (lhs_unit == rhs_unit);-      return SASS_MEMORY_NEW(Boolean, pstate, is_comparable);-    }--    Signature variable_exists_sig = "variable-exists($name)";-    BUILT_IN(variable_exists)-    {-      std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));--      if(d_env.has("$"+s)) {-        return SASS_MEMORY_NEW(Boolean, pstate, true);-      }-      else {-        return SASS_MEMORY_NEW(Boolean, pstate, false);-      }-    }--    Signature global_variable_exists_sig = "global-variable-exists($name)";-    BUILT_IN(global_variable_exists)-    {-      std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));--      if(d_env.has_global("$"+s)) {-        return SASS_MEMORY_NEW(Boolean, pstate, true);-      }-      else {-        return SASS_MEMORY_NEW(Boolean, pstate, false);-      }-    }--    Signature function_exists_sig = "function-exists($name)";-    BUILT_IN(function_exists)-    {-      String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);-      if (!ss) {-        error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);-      }--      std::string name = Util::normalize_underscores(unquote(ss->value()));--      if(d_env.has_global(name+"[f]")) {-        return SASS_MEMORY_NEW(Boolean, pstate, true);-      }-      else {-        return SASS_MEMORY_NEW(Boolean, pstate, false);-      }-    }--    Signature mixin_exists_sig = "mixin-exists($name)";-    BUILT_IN(mixin_exists)-    {-      std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));--      if(d_env.has_global(s+"[m]")) {-        return SASS_MEMORY_NEW(Boolean, pstate, true);-      }-      else {-        return SASS_MEMORY_NEW(Boolean, pstate, false);-      }-    }--    Signature feature_exists_sig = "feature-exists($name)";-    BUILT_IN(feature_exists)-    {-      std::string s = unquote(ARG("$name", String_Constant)->value());--      if(features.find(s) == features.end()) {-        return SASS_MEMORY_NEW(Boolean, pstate, false);-      }-      else {-        return SASS_MEMORY_NEW(Boolean, pstate, true);-      }-    }--    Signature call_sig = "call($name, $args...)";-    BUILT_IN(call)-    {-      std::string name;-      Function_Ptr ff = Cast<Function>(env["$name"]);-      String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);--      if (ss) {-        name = Util::normalize_underscores(unquote(ss->value()));-        std::cerr << "DEPRECATION WARNING: ";-        std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;-        std::cerr << "in Sass 4.0. Use call(get-function(" + quote(name) + ")) instead." << std::endl;-        std::cerr << std::endl;-      } else if (ff) {-        name = ff->name();-      }--      List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));--      Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);-      // std::string full_name(name + "[f]");-      // Definition_Ptr def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0;-      // Parameters_Ptr params = def ? def->parameters() : 0;-      // size_t param_size = params ? params->length() : 0;-      for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression_Obj expr = arglist->value_at_index(i);-        // if (params && params->has_rest_parameter()) {-        //   Parameter_Obj p = param_size > i ? (*params)[i] : 0;-        //   List_Ptr list = Cast<List>(expr);-        //   if (list && p && !p->is_rest_parameter()) expr = (*list)[0];-        // }-        if (arglist->is_arglist()) {-          Expression_Obj obj = arglist->at(i);-          Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX-          args->append(SASS_MEMORY_NEW(Argument,-                                       pstate,-                                       expr,-                                       arg ? arg->name() : "",-                                       arg ? arg->is_rest_argument() : false,-                                       arg ? arg->is_keyword_argument() : false));-        } else {-          args->append(SASS_MEMORY_NEW(Argument, pstate, expr));-        }-      }-      Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args);-      Expand expand(ctx, &d_env, &selector_stack);-      func->via_call(true); // calc invoke is allowed-      if (ff) func->func(ff);-      return func->perform(&expand.eval);-    }--    ////////////////////-    // BOOLEAN FUNCTIONS-    ////////////////////--    Signature not_sig = "not($value)";-    BUILT_IN(sass_not)-    {-      return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());-    }--    Signature if_sig = "if($condition, $if-true, $if-false)";-    // BUILT_IN(sass_if)-    // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }-    BUILT_IN(sass_if)-    {-      Expand expand(ctx, &d_env, &selector_stack);-      Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);-      bool is_true = !cond->is_false();-      Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression);-      res = res->perform(&expand.eval);-      res->set_delayed(false); // clone?-      return res.detach();-    }--    //////////////////////////-    // MISCELLANEOUS FUNCTIONS-    //////////////////////////--    // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String)-    // unquoted_string(value.to_sass)--    Signature inspect_sig = "inspect($value)";-    BUILT_IN(inspect)-    {-      Expression_Ptr v = ARG("$value", Expression);-      if (v->concrete_type() == Expression::NULL_VAL) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "null");-      } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {-        return SASS_MEMORY_NEW(String_Quoted, pstate, "false");-      } else if (v->concrete_type() == Expression::STRING) {-        return v;-      } else {-        // ToDo: fix to_sass for nested parentheses-        Sass_Output_Style old_style;-        old_style = ctx.c_options.output_style;-        ctx.c_options.output_style = TO_SASS;-        Emitter emitter(ctx.c_options);-        Inspect i(emitter);-        i.in_declaration = false;-        v->perform(&i);-        ctx.c_options.output_style = old_style;-        return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer());-      }-      // return v;-    }-    Signature selector_nest_sig = "selector-nest($selectors...)";-    BUILT_IN(selector_nest)-    {-      List_Ptr arglist = ARG("$selectors", List);--      // Not enough parameters-      if( arglist->length() == 0 )-        error("$selectors: At least one selector must be passed for `selector-nest'", pstate, traces);--      // Parse args into vector of selectors-      std::vector<Selector_List_Obj> parsedSelectors;-      for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i));-        if (exp->concrete_type() == Expression::NULL_VAL) {-          std::stringstream msg;-          msg << "$selectors: null is not a valid selector: it must be a string,\n";-          msg << "a list of strings, or a list of lists of strings for 'selector-nest'";-          error(msg.str(), pstate, traces);-        }-        if (String_Constant_Obj str = Cast<String_Constant>(exp)) {-          str->quote_mark(0);-        }-        std::string exp_src = exp->to_string(ctx.c_options);-        Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);-        parsedSelectors.push_back(sel);-      }--      // Nothing to do-      if( parsedSelectors.empty() ) {-        return SASS_MEMORY_NEW(Null, pstate);-      }--      // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.-      std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();-      Selector_List_Obj result = *itr;-      ++itr;--      for(;itr != parsedSelectors.end(); ++itr) {-        Selector_List_Obj child = *itr;-        std::vector<Complex_Selector_Obj> exploded;-        selector_stack.push_back(result);-        Selector_List_Obj rv = child->resolve_parent_refs(selector_stack, traces);-        selector_stack.pop_back();-        for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) {-          exploded.push_back((*rv)[m]);-        }-        result->elements(exploded);-      }--      Listize listize;-      return result->perform(&listize);-    }--    Signature selector_append_sig = "selector-append($selectors...)";-    BUILT_IN(selector_append)-    {-      List_Ptr arglist = ARG("$selectors", List);--      // Not enough parameters-      if( arglist->length() == 0 )-        error("$selectors: At least one selector must be passed for `selector-append'", pstate, traces);--      // Parse args into vector of selectors-      std::vector<Selector_List_Obj> parsedSelectors;-      for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i));-        if (exp->concrete_type() == Expression::NULL_VAL) {-          std::stringstream msg;-          msg << "$selectors: null is not a valid selector: it must be a string,\n";-          msg << "a list of strings, or a list of lists of strings for 'selector-append'";-          error(msg.str(), pstate, traces);-        }-        if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {-          str->quote_mark(0);-        }-        std::string exp_src = exp->to_string();-        Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);-        parsedSelectors.push_back(sel);-      }--      // Nothing to do-      if( parsedSelectors.empty() ) {-        return SASS_MEMORY_NEW(Null, pstate);-      }--      // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.-      std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();-      Selector_List_Obj result = *itr;-      ++itr;--      for(;itr != parsedSelectors.end(); ++itr) {-        Selector_List_Obj child = *itr;-        std::vector<Complex_Selector_Obj> newElements;--        // For every COMPLEX_SELECTOR in `result`-        // For every COMPLEX_SELECTOR in `child`-          // let parentSeqClone equal a copy of result->elements[i]-          // let childSeq equal child->elements[j]-          // Append all of childSeq head elements into parentSeqClone-          // Set the innermost tail of parentSeqClone, to childSeq's tail-        // Replace result->elements with newElements-        for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) {-          for (size_t j = 0, childLen = child->length(); j < childLen; ++j) {-            Complex_Selector_Obj parentSeqClone = SASS_MEMORY_CLONE((*result)[i]);-            Complex_Selector_Obj childSeq = (*child)[j];-            Complex_Selector_Obj base = childSeq->tail();--            // Must be a simple sequence-            if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) {-              std::string msg("Can't append \"");-              msg += childSeq->to_string();-              msg += "\" to \"";-              msg += parentSeqClone->to_string();-              msg += "\" for `selector-append'";-              error(msg, pstate, traces);-            }--            // Cannot be a Universal selector-            Element_Selector_Obj pType = Cast<Element_Selector>(childSeq->head()->first());-            if(pType && pType->name() == "*") {-              std::string msg("Can't append \"");-              msg += childSeq->to_string();-              msg += "\" to \"";-              msg += parentSeqClone->to_string();-              msg += "\" for `selector-append'";-              error(msg, pstate, traces);-            }--            // TODO: Add check for namespace stuff--            // append any selectors in childSeq's head-            parentSeqClone->innermost()->head()->concat(base->head());--            // Set parentSeqClone new tail-            parentSeqClone->innermost()->tail( base->tail() );--            newElements.push_back(parentSeqClone);-          }-        }--        result->elements(newElements);-      }--      Listize listize;-      return result->perform(&listize);-    }--    Signature selector_unify_sig = "selector-unify($selector1, $selector2)";-    BUILT_IN(selector_unify)-    {-      Selector_List_Obj selector1 = ARGSEL("$selector1", Selector_List_Obj, p_contextualize);-      Selector_List_Obj selector2 = ARGSEL("$selector2", Selector_List_Obj, p_contextualize);--      Selector_List_Obj result = selector1->unify_with(selector2);-      Listize listize;-      return result->perform(&listize);-    }--    Signature simple_selectors_sig = "simple-selectors($selector)";-    BUILT_IN(simple_selectors)-    {-      Compound_Selector_Obj sel = ARGSEL("$selector", Compound_Selector_Obj, p_contextualize);--      List_Ptr l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);--      for (size_t i = 0, L = sel->length(); i < L; ++i) {-        Simple_Selector_Obj ss = (*sel)[i];-        std::string ss_string = ss->to_string() ;--        l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));-      }--      return l;-    }--    Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";-    BUILT_IN(selector_extend)-    {-      Selector_List_Obj  selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize);-      Selector_List_Obj  extendee = ARGSEL("$extendee", Selector_List_Obj, p_contextualize);-      Selector_List_Obj  extender = ARGSEL("$extender", Selector_List_Obj, p_contextualize);--      Subset_Map subset_map;-      extender->populate_extends(extendee, subset_map);-      Extend extend(subset_map);--      Selector_List_Obj result = extend.extendSelectorList(selector, false);--      Listize listize;-      return result->perform(&listize);-    }--    Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";-    BUILT_IN(selector_replace)-    {-      Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize);-      Selector_List_Obj original = ARGSEL("$original", Selector_List_Obj, p_contextualize);-      Selector_List_Obj replacement = ARGSEL("$replacement", Selector_List_Obj, p_contextualize);-      Subset_Map subset_map;-      replacement->populate_extends(original, subset_map);-      Extend extend(subset_map);--      Selector_List_Obj result = extend.extendSelectorList(selector, true);--      Listize listize;-      return result->perform(&listize);-    }--    Signature selector_parse_sig = "selector-parse($selector)";-    BUILT_IN(selector_parse)-    {-      Selector_List_Obj sel = ARGSEL("$selector", Selector_List_Obj, p_contextualize);--      Listize listize;-      return sel->perform(&listize);-    }--    Signature is_superselector_sig = "is-superselector($super, $sub)";-    BUILT_IN(is_superselector)-    {-      Selector_List_Obj  sel_sup = ARGSEL("$super", Selector_List_Obj, p_contextualize);-      Selector_List_Obj  sel_sub = ARGSEL("$sub", Selector_List_Obj, p_contextualize);-      bool result = sel_sup->is_superselector_of(sel_sub);-      return SASS_MEMORY_NEW(Boolean, pstate, result);-    }--    Signature unique_id_sig = "unique-id()";-    BUILT_IN(unique_id)-    {-      std::stringstream ss;-      std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8-      uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));-      ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;-      return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str());-    }--    Signature is_bracketed_sig = "is-bracketed($list)";-    BUILT_IN(is_bracketed)-    {-      Value_Obj value = ARG("$list", Value);-      List_Obj list = Cast<List>(value);-      return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed());-    }--    Signature content_exists_sig = "content-exists()";-    BUILT_IN(content_exists)-    {-      if (!d_env.has_global("is_in_mixin")) {-        error("Cannot call content-exists() except within a mixin.", pstate, traces);-      }-      return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));-    }--    Signature get_function_sig = "get-function($name, $css: false)";-    BUILT_IN(get_function)-    {-      String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);-      if (!ss) {-        error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);-      }--      std::string name = Util::normalize_underscores(unquote(ss->value()));-      std::string full_name = name + "[f]";--      Boolean_Obj css = ARG("$css", Boolean);-      if (!css->is_false()) {-        Definition_Ptr def = SASS_MEMORY_NEW(Definition,-                                         pstate,-                                         name,-                                         SASS_MEMORY_NEW(Parameters, pstate),-                                         SASS_MEMORY_NEW(Block, pstate, 0, false),-                                         Definition::FUNCTION);-        return SASS_MEMORY_NEW(Function, pstate, def, true);-      }---      if (!d_env.has_global(full_name)) {-        error("Function not found: " + name, pstate, traces);-      }--      Definition_Ptr def = Cast<Definition>(d_env[full_name]);-      return SASS_MEMORY_NEW(Function, pstate, def, false);-    }-  }-}
− libsass/src/functions.hpp
@@ -1,198 +0,0 @@-#ifndef SASS_FUNCTIONS_H-#define SASS_FUNCTIONS_H--#include "listize.hpp"-#include "position.hpp"-#include "environment.hpp"-#include "ast_fwd_decl.hpp"-#include "sass/functions.h"--#define BUILT_IN(name) Expression_Ptr \-name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtraces traces, std::vector<Selector_List_Obj> selector_stack)--namespace Sass {-  struct Backtrace;-  typedef const char* Signature;-  typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>);--  Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx);-  Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx);--  std::string function_name(Signature);--  namespace Functions {--    extern Signature rgb_sig;-    extern Signature rgba_4_sig;-    extern Signature rgba_2_sig;-    extern Signature red_sig;-    extern Signature green_sig;-    extern Signature blue_sig;-    extern Signature mix_sig;-    extern Signature hsl_sig;-    extern Signature hsla_sig;-    extern Signature hue_sig;-    extern Signature saturation_sig;-    extern Signature lightness_sig;-    extern Signature adjust_hue_sig;-    extern Signature lighten_sig;-    extern Signature darken_sig;-    extern Signature saturate_sig;-    extern Signature desaturate_sig;-    extern Signature grayscale_sig;-    extern Signature complement_sig;-    extern Signature invert_sig;-    extern Signature alpha_sig;-    extern Signature opacity_sig;-    extern Signature opacify_sig;-    extern Signature fade_in_sig;-    extern Signature transparentize_sig;-    extern Signature fade_out_sig;-    extern Signature adjust_color_sig;-    extern Signature scale_color_sig;-    extern Signature change_color_sig;-    extern Signature ie_hex_str_sig;-    extern Signature unquote_sig;-    extern Signature quote_sig;-    extern Signature str_length_sig;-    extern Signature str_insert_sig;-    extern Signature str_index_sig;-    extern Signature str_slice_sig;-    extern Signature to_upper_case_sig;-    extern Signature to_lower_case_sig;-    extern Signature percentage_sig;-    extern Signature round_sig;-    extern Signature ceil_sig;-    extern Signature floor_sig;-    extern Signature abs_sig;-    extern Signature min_sig;-    extern Signature max_sig;-    extern Signature inspect_sig;-    extern Signature random_sig;-    extern Signature length_sig;-    extern Signature nth_sig;-    extern Signature index_sig;-    extern Signature join_sig;-    extern Signature append_sig;-    extern Signature zip_sig;-    extern Signature list_separator_sig;-    extern Signature type_of_sig;-    extern Signature unit_sig;-    extern Signature unitless_sig;-    extern Signature comparable_sig;-    extern Signature variable_exists_sig;-    extern Signature global_variable_exists_sig;-    extern Signature function_exists_sig;-    extern Signature mixin_exists_sig;-    extern Signature feature_exists_sig;-    extern Signature call_sig;-    extern Signature not_sig;-    extern Signature if_sig;-    extern Signature map_get_sig;-    extern Signature map_merge_sig;-    extern Signature map_remove_sig;-    extern Signature map_keys_sig;-    extern Signature map_values_sig;-    extern Signature map_has_key_sig;-    extern Signature keywords_sig;-    extern Signature set_nth_sig;-    extern Signature unique_id_sig;-    extern Signature selector_nest_sig;-    extern Signature selector_append_sig;-    extern Signature selector_extend_sig;-    extern Signature selector_replace_sig;-    extern Signature selector_unify_sig;-    extern Signature is_superselector_sig;-    extern Signature simple_selectors_sig;-    extern Signature selector_parse_sig;-    extern Signature is_bracketed_sig;-    extern Signature content_exists_sig;-    extern Signature get_function_sig;--    BUILT_IN(rgb);-    BUILT_IN(rgba_4);-    BUILT_IN(rgba_2);-    BUILT_IN(red);-    BUILT_IN(green);-    BUILT_IN(blue);-    BUILT_IN(mix);-    BUILT_IN(hsl);-    BUILT_IN(hsla);-    BUILT_IN(hue);-    BUILT_IN(saturation);-    BUILT_IN(lightness);-    BUILT_IN(adjust_hue);-    BUILT_IN(lighten);-    BUILT_IN(darken);-    BUILT_IN(saturate);-    BUILT_IN(desaturate);-    BUILT_IN(grayscale);-    BUILT_IN(complement);-    BUILT_IN(invert);-    BUILT_IN(alpha);-    BUILT_IN(opacify);-    BUILT_IN(transparentize);-    BUILT_IN(adjust_color);-    BUILT_IN(scale_color);-    BUILT_IN(change_color);-    BUILT_IN(ie_hex_str);-    BUILT_IN(sass_unquote);-    BUILT_IN(sass_quote);-    BUILT_IN(str_length);-    BUILT_IN(str_insert);-    BUILT_IN(str_index);-    BUILT_IN(str_slice);-    BUILT_IN(to_upper_case);-    BUILT_IN(to_lower_case);-    BUILT_IN(percentage);-    BUILT_IN(round);-    BUILT_IN(ceil);-    BUILT_IN(floor);-    BUILT_IN(abs);-    BUILT_IN(min);-    BUILT_IN(max);-    BUILT_IN(inspect);-    BUILT_IN(random);-    BUILT_IN(length);-    BUILT_IN(nth);-    BUILT_IN(index);-    BUILT_IN(join);-    BUILT_IN(append);-    BUILT_IN(zip);-    BUILT_IN(list_separator);-    BUILT_IN(type_of);-    BUILT_IN(unit);-    BUILT_IN(unitless);-    BUILT_IN(comparable);-    BUILT_IN(variable_exists);-    BUILT_IN(global_variable_exists);-    BUILT_IN(function_exists);-    BUILT_IN(mixin_exists);-    BUILT_IN(feature_exists);-    BUILT_IN(call);-    BUILT_IN(sass_not);-    BUILT_IN(sass_if);-    BUILT_IN(map_get);-    BUILT_IN(map_merge);-    BUILT_IN(map_remove);-    BUILT_IN(map_keys);-    BUILT_IN(map_values);-    BUILT_IN(map_has_key);-    BUILT_IN(keywords);-    BUILT_IN(set_nth);-    BUILT_IN(unique_id);-    BUILT_IN(selector_nest);-    BUILT_IN(selector_append);-    BUILT_IN(selector_extend);-    BUILT_IN(selector_replace);-    BUILT_IN(selector_unify);-    BUILT_IN(is_superselector);-    BUILT_IN(simple_selectors);-    BUILT_IN(selector_parse);-    BUILT_IN(is_bracketed);-    BUILT_IN(content_exists);-    BUILT_IN(get_function);-  }-}--#endif
libsass/src/inspect.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cmath> #include <string> #include <iostream>@@ -21,7 +24,7 @@   Inspect::~Inspect() { }    // statements-  void Inspect::operator()(Block_Ptr block)+  void Inspect::operator()(Block* block)   {     if (!block->is_root()) {       add_open_mapping(block);@@ -39,25 +42,23 @@    } -  void Inspect::operator()(Ruleset_Ptr ruleset)+  void Inspect::operator()(Ruleset* ruleset)   {     if (ruleset->selector()) {-      opt.in_selector = true;       ruleset->selector()->perform(this);-      opt.in_selector = false;     }     if (ruleset->block()) {       ruleset->block()->perform(this);     }   } -  void Inspect::operator()(Keyframe_Rule_Ptr rule)+  void Inspect::operator()(Keyframe_Rule* rule)   {     if (rule->name()) rule->name()->perform(this);     if (rule->block()) rule->block()->perform(this);   } -  void Inspect::operator()(Bubble_Ptr bubble)+  void Inspect::operator()(Bubble* bubble)   {     append_indentation();     append_token("::BUBBLE", bubble);@@ -66,19 +67,65 @@     append_scope_closer();   } -  void Inspect::operator()(Media_Block_Ptr media_block)+  void Inspect::operator()(MediaRule* rule)   {     append_indentation();-    append_token("@media", media_block);+    append_token("@media", rule);     append_mandatory_space();+    if (rule->block()) {+      rule->block()->perform(this);+    }+  }++  void Inspect::operator()(CssMediaRule* rule)+  {+    if (output_style() == NESTED)+      indentation += rule->tabs();+    append_indentation();+    append_token("@media", rule);+    append_mandatory_space();     in_media_block = true;-    media_block->media_queries()->perform(this);+    bool joinIt = false;+    for (auto query : rule->elements()) {+      if (joinIt) {+        append_comma_separator();+        append_optional_space();+      }+      operator()(query);+      joinIt = true;+    }+    if (rule->block()) {+      rule->block()->perform(this);+    }     in_media_block = false;-    media_block->block()->perform(this);+    if (output_style() == NESTED)+      indentation -= rule->tabs();   } -  void Inspect::operator()(Supports_Block_Ptr feature_block)+  void Inspect::operator()(CssMediaQuery* query)   {+    bool joinIt = false;+    if (!query->modifier().empty()) {+      append_string(query->modifier());+      append_mandatory_space();+    }+    if (!query->type().empty()) {+      append_string(query->type());+      joinIt = true;+    }+    for (auto feature : query->features()) {+      if (joinIt) {+        append_mandatory_space();+        append_string("and");+        append_mandatory_space();+      }+      append_string(feature);+      joinIt = true;+    }+  }++  void Inspect::operator()(Supports_Block* feature_block)+  {     append_indentation();     append_token("@supports", feature_block);     append_mandatory_space();@@ -86,7 +133,7 @@     feature_block->block()->perform(this);   } -  void Inspect::operator()(At_Root_Block_Ptr at_root_block)+  void Inspect::operator()(At_Root_Block* at_root_block)   {     append_indentation();     append_token("@at-root ", at_root_block);@@ -95,7 +142,7 @@     if(at_root_block->block()) at_root_block->block()->perform(this);   } -  void Inspect::operator()(Directive_Ptr at_rule)+  void Inspect::operator()(Directive* at_rule)   {     append_indentation();     append_token(at_rule->keyword(), at_rule);@@ -118,7 +165,7 @@     }   } -  void Inspect::operator()(Declaration_Ptr dec)+  void Inspect::operator()(Declaration* dec)   {     if (dec->value()->concrete_type() == Expression::NULL_VAL) return;     bool was_decl = in_declaration;@@ -133,8 +180,7 @@     append_colon_separator();      if (dec->value()->concrete_type() == Expression::SELECTOR) {-      Listize listize;-      Expression_Obj ls = dec->value()->perform(&listize);+      Expression_Obj ls = Listize::perform(dec->value());       ls->perform(this);     } else {       dec->value()->perform(this);@@ -150,7 +196,7 @@     in_declaration = was_decl;   } -  void Inspect::operator()(Assignment_Ptr assn)+  void Inspect::operator()(Assignment* assn)   {     append_token(assn->variable(), assn);     append_colon_separator();@@ -162,7 +208,7 @@     append_delimiter();   } -  void Inspect::operator()(Import_Ptr import)+  void Inspect::operator()(Import* import)   {     if (!import->urls().empty()) {       append_token("@import", import);@@ -193,7 +239,7 @@     }   } -  void Inspect::operator()(Import_Stub_Ptr import)+  void Inspect::operator()(Import_Stub* import)   {     append_indentation();     append_token("@import", import);@@ -202,7 +248,7 @@     append_delimiter();   } -  void Inspect::operator()(Warning_Ptr warning)+  void Inspect::operator()(Warning* warning)   {     append_indentation();     append_token("@warn", warning);@@ -211,7 +257,7 @@     append_delimiter();   } -  void Inspect::operator()(Error_Ptr error)+  void Inspect::operator()(Error* error)   {     append_indentation();     append_token("@error", error);@@ -220,7 +266,7 @@     append_delimiter();   } -  void Inspect::operator()(Debug_Ptr debug)+  void Inspect::operator()(Debug* debug)   {     append_indentation();     append_token("@debug", debug);@@ -229,14 +275,14 @@     append_delimiter();   } -  void Inspect::operator()(Comment_Ptr comment)+  void Inspect::operator()(Comment* comment)   {     in_comment = true;     comment->text()->perform(this);     in_comment = false;   } -  void Inspect::operator()(If_Ptr cond)+  void Inspect::operator()(If* cond)   {     append_indentation();     append_token("@if", cond);@@ -251,7 +297,7 @@     }   } -  void Inspect::operator()(For_Ptr loop)+  void Inspect::operator()(For* loop)   {     append_indentation();     append_token("@for", loop);@@ -264,7 +310,7 @@     loop->block()->perform(this);   } -  void Inspect::operator()(Each_Ptr loop)+  void Inspect::operator()(Each* loop)   {     append_indentation();     append_token("@each", loop);@@ -279,7 +325,7 @@     loop->block()->perform(this);   } -  void Inspect::operator()(While_Ptr loop)+  void Inspect::operator()(While* loop)   {     append_indentation();     append_token("@while", loop);@@ -288,7 +334,7 @@     loop->block()->perform(this);   } -  void Inspect::operator()(Return_Ptr ret)+  void Inspect::operator()(Return* ret)   {     append_indentation();     append_token("@return", ret);@@ -297,7 +343,7 @@     append_delimiter();   } -  void Inspect::operator()(Extension_Ptr extend)+  void Inspect::operator()(ExtendRule* extend)   {     append_indentation();     append_token("@extend", extend);@@ -306,7 +352,7 @@     append_delimiter();   } -  void Inspect::operator()(Definition_Ptr def)+  void Inspect::operator()(Definition* def)   {     append_indentation();     if (def->type() == Definition::MIXIN) {@@ -321,7 +367,7 @@     def->block()->perform(this);   } -  void Inspect::operator()(Mixin_Call_Ptr call)+  void Inspect::operator()(Mixin_Call* call)   {     append_indentation();     append_token("@include", call);@@ -337,14 +383,14 @@     if (!call->block()) append_delimiter();   } -  void Inspect::operator()(Content_Ptr content)+  void Inspect::operator()(Content* content)   {     append_indentation();     append_token("@content", content);     append_delimiter();   } -  void Inspect::operator()(Map_Ptr map)+  void Inspect::operator()(Map* map)   {     if (output_style() == TO_SASS && map->empty()) {       append_string("()");@@ -366,15 +412,15 @@     append_string(")");   } -  std::string Inspect::lbracket(List_Ptr list) {+  std::string Inspect::lbracket(List* list) {     return list->is_bracketed() ? "[" : "(";   } -  std::string Inspect::rbracket(List_Ptr list) {+  std::string Inspect::rbracket(List* list) {     return list->is_bracketed() ? "]" : ")";   } -  void Inspect::operator()(List_Ptr list)+  void Inspect::operator()(List* list)   {     if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) {       append_string(lbracket(list));@@ -398,7 +444,7 @@         list->length() == 1 &&         !list->from_selector() &&         !Cast<List>(list->at(0)) &&-        !Cast<Selector_List>(list->at(0))+        !Cast<SelectorList>(list->at(0))     ) {       append_string(lbracket(list));     }@@ -448,7 +494,7 @@         list->length() == 1 &&         !list->from_selector() &&         !Cast<List>(list->at(0)) &&-        !Cast<Selector_List>(list->at(0))+        !Cast<SelectorList>(list->at(0))     ) {       append_string(",");       append_string(rbracket(list));@@ -462,7 +508,7 @@    } -  void Inspect::operator()(Binary_Expression_Ptr expr)+  void Inspect::operator()(Binary_Expression* expr)   {     expr->left()->perform(this);     if ( in_media_block ||@@ -499,7 +545,7 @@     expr->right()->perform(this);   } -  void Inspect::operator()(Unary_Expression_Ptr expr)+  void Inspect::operator()(Unary_Expression* expr)   {     if (expr->optype() == Unary_Expression::PLUS)       append_string("+");     else if (expr->optype() == Unary_Expression::SLASH) append_string("/");@@ -507,91 +553,42 @@     expr->operand()->perform(this);   } -  void Inspect::operator()(Function_Call_Ptr call)+  void Inspect::operator()(Function_Call* call)   {     append_token(call->name(), call);     call->arguments()->perform(this);   } -  void Inspect::operator()(Function_Call_Schema_Ptr call)-  {-    call->name()->perform(this);-    call->arguments()->perform(this);-  }--  void Inspect::operator()(Variable_Ptr var)+  void Inspect::operator()(Variable* var)   {     append_token(var->name(), var);   } -  void Inspect::operator()(Number_Ptr n)+  void Inspect::operator()(Number* n)   { -    std::string res;-     // reduce units     n->reduce(); -    // check if the fractional part of the value equals to zero-    // neat trick from http://stackoverflow.com/a/1521682/1550314-    // double int_part; bool is_int = modf(value, &int_part) == 0.0;--    // this all cannot be done with one run only, since fixed-    // output differs from normal output and regular output-    // can contain scientific notation which we do not want!--    // first sample     std::stringstream ss;-    ss.precision(12);-    ss << n->value();--    // check if we got scientific notation in result-    if (ss.str().find_first_of("e") != std::string::npos) {-      ss.clear(); ss.str(std::string());-      ss.precision(std::max(12, opt.precision));-      ss << std::fixed << n->value();-    }--    std::string tmp = ss.str();-    size_t pos_point = tmp.find_first_of(".,");-    size_t pos_fract = tmp.find_last_not_of("0");-    bool is_int = pos_point == pos_fract ||-                  pos_point == std::string::npos;+    ss.precision(opt.precision);+    ss << std::fixed << n->value(); -    // reset stream for another run-    ss.clear(); ss.str(std::string());+    std::string res = ss.str();+    size_t s = res.length(); -    // take a shortcut for integers-    if (is_int)-    {-      ss.precision(0);-      ss << std::fixed << n->value();-      res = std::string(ss.str());-    }-    // process floats-    else+    // delete trailing zeros+    for(s = s - 1; s > 0; --s)     {-      // do we have have too much precision?-      if (pos_fract < opt.precision + pos_point)-      { ss.precision((int)(pos_fract - pos_point)); }-      else { ss.precision(opt.precision); }-      // round value again-      ss << std::fixed << n->value();-      res = std::string(ss.str());-      // maybe we truncated up to decimal point-      size_t pos = res.find_last_not_of("0");-      // handle case where we have a "0"-      if (pos == std::string::npos) {-        res = "0.0";-      } else {-        bool at_dec_point = res[pos] == '.' ||-                            res[pos] == ',';-        // don't leave a blank point-        if (at_dec_point) ++ pos;-        res.resize (pos + 1);-      }+        if(res[s] == '0') {+          res.erase(s, 1);+        }+        else break;     } +    // delete trailing decimal separator+    if(res[s] == '.') res.erase(s, 1);+     // some final cosmetics     if (res == "0.0") res = "0";     else if (res == "") res = "0";@@ -599,15 +596,22 @@     else if (res == "-0.0") res = "0";     else if (opt.output_style == COMPRESSED)     {-      // check if handling negative nr-      size_t off = res[0] == '-' ? 1 : 0;-      // remove leading zero from floating point in compressed mode-      if (n->zero() && res[off] == '0' && res[off+1] == '.') res.erase(off, 1);+      if (n->zero()) {+        // check if handling negative nr+        size_t off = res[0] == '-' ? 1 : 0;+        // remove leading zero from floating point in compressed mode+        if (res[off] == '0' && res[off+1] == '.') res.erase(off, 1);+      }     }      // add unit now     res += n->unit(); +    if (opt.output_style == TO_CSS && !n->is_valid_css_unit()) {+      // traces.push_back(Backtrace(nr->pstate()));+      throw Exception::InvalidValue({}, *n);+    }+     // output the final token     append_token(res, n);   }@@ -620,7 +624,7 @@     else                return c;   } -  void Inspect::operator()(Color_Ptr c)+  void Inspect::operator()(Color_RGBA* c)   {     // output the final token     std::stringstream ss;@@ -629,11 +633,6 @@     // maybe an unknown token     std::string name = c->disp(); -    if (opt.in_selector && name != "") {-      append_token(name, c);-      return;-    }-     // resolved color     std::string res_name = name; @@ -644,7 +643,7 @@      // get color from given name (if one was given at all)     if (name != "" && name_to_color(name)) {-      Color_Ptr_Const n = name_to_color(name);+      const Color_RGBA* n = name_to_color(name);       r = Sass::round(cap_channel<0xff>(n->r()), opt.precision);       g = Sass::round(cap_channel<0xff>(n->g()), opt.precision);       b = Sass::round(cap_channel<0xff>(n->b()), opt.precision);@@ -711,13 +710,19 @@    } -  void Inspect::operator()(Boolean_Ptr b)+  void Inspect::operator()(Color_HSLA* c)   {+    Color_RGBA_Obj rgba = c->toRGBA();+    operator()(rgba);+  }++  void Inspect::operator()(Boolean* b)+  {     // output the final token     append_token(b->value() ? "true" : "false", b);   } -  void Inspect::operator()(String_Schema_Ptr ss)+  void Inspect::operator()(String_Schema* ss)   {     // Evaluation should turn these into String_Constants,     // so this method is only for inspection purposes.@@ -728,12 +733,12 @@     }   } -  void Inspect::operator()(String_Constant_Ptr s)+  void Inspect::operator()(String_Constant* s)   {     append_token(s->value(), s);   } -  void Inspect::operator()(String_Quoted_Ptr s)+  void Inspect::operator()(String_Quoted* s)   {     if (const char q = s->quote_mark()) {       append_token(quote(s->value(), q), s);@@ -742,17 +747,17 @@     }   } -  void Inspect::operator()(Custom_Error_Ptr e)+  void Inspect::operator()(Custom_Error* e)   {     append_token(e->message(), e);   } -  void Inspect::operator()(Custom_Warning_Ptr w)+  void Inspect::operator()(Custom_Warning* w)   {     append_token(w->message(), w);   } -  void Inspect::operator()(Supports_Operator_Ptr so)+  void Inspect::operator()(Supports_Operator* so)   {      if (so->needs_parens(so->left())) append_string("(");@@ -774,7 +779,7 @@     if (so->needs_parens(so->right())) append_string(")");   } -  void Inspect::operator()(Supports_Negation_Ptr sn)+  void Inspect::operator()(Supports_Negation* sn)   {     append_token("not", sn);     append_mandatory_space();@@ -783,7 +788,7 @@     if (sn->needs_parens(sn->condition())) append_string(")");   } -  void Inspect::operator()(Supports_Declaration_Ptr sd)+  void Inspect::operator()(Supports_Declaration* sd)   {     append_string("(");     sd->feature()->perform(this);@@ -792,12 +797,12 @@     append_string(")");   } -  void Inspect::operator()(Supports_Interpolation_Ptr sd)+  void Inspect::operator()(Supports_Interpolation* sd)   {     sd->value()->perform(this);   } -  void Inspect::operator()(Media_Query_Ptr mq)+  void Inspect::operator()(Media_Query* mq)   {     size_t i = 0;     if (mq->media_type()) {@@ -814,7 +819,7 @@     }   } -  void Inspect::operator()(Media_Query_Expression_Ptr mqe)+  void Inspect::operator()(Media_Query_Expression* mqe)   {     if (mqe->is_interpolated()) {       mqe->feature()->perform(this);@@ -830,7 +835,7 @@     }   } -  void Inspect::operator()(At_Root_Query_Ptr ae)+  void Inspect::operator()(At_Root_Query* ae)   {     if (ae->feature()) {       append_string("(");@@ -843,7 +848,7 @@     }   } -  void Inspect::operator()(Function_Ptr f)+  void Inspect::operator()(Function* f)   {     append_token("get-function", f);     append_string("(");@@ -851,14 +856,14 @@     append_string(")");   } -  void Inspect::operator()(Null_Ptr n)+  void Inspect::operator()(Null* n)   {     // output the final token     append_token("null", n);   }    // parameters and arguments-  void Inspect::operator()(Parameter_Ptr p)+  void Inspect::operator()(Parameter* p)   {     append_token(p->name(), p);     if (p->default_value()) {@@ -870,7 +875,7 @@     }   } -  void Inspect::operator()(Parameters_Ptr p)+  void Inspect::operator()(Parameters* p)   {     append_string("(");     if (!p->empty()) {@@ -883,7 +888,7 @@     append_string(")");   } -  void Inspect::operator()(Argument_Ptr a)+  void Inspect::operator()(Argument* a)   {     if (!a->name().empty()) {       append_token(a->name(), a);@@ -895,7 +900,7 @@       return;     }     if (a->value()->concrete_type() == Expression::STRING) {-      String_Constant_Ptr s = Cast<String_Constant>(a->value());+      String_Constant* s = Cast<String_Constant>(a->value());       if (s) s->perform(this);     } else {       a->value()->perform(this);@@ -905,7 +910,7 @@     }   } -  void Inspect::operator()(Arguments_Ptr a)+  void Inspect::operator()(Arguments* a)   {     append_string("(");     if (!a->empty()) {@@ -919,46 +924,38 @@     append_string(")");   } -  void Inspect::operator()(Selector_Schema_Ptr s)+  void Inspect::operator()(Selector_Schema* s)   {-    opt.in_selector = true;     s->contents()->perform(this);-    opt.in_selector = false;   } -  void Inspect::operator()(Parent_Selector_Ptr p)+  void Inspect::operator()(Parent_Reference* p)   {-    if (p->is_real_parent_ref()) append_string("&");+    append_string("&");   } -  void Inspect::operator()(Placeholder_Selector_Ptr s)+  void Inspect::operator()(Placeholder_Selector* s)   {     append_token(s->name(), s);-    if (s->has_line_break()) append_optional_linefeed();-    if (s->has_line_break()) append_indentation();    } -  void Inspect::operator()(Element_Selector_Ptr s)+  void Inspect::operator()(Type_Selector* s)   {     append_token(s->ns_name(), s);   } -  void Inspect::operator()(Class_Selector_Ptr s)+  void Inspect::operator()(Class_Selector* s)   {     append_token(s->ns_name(), s);-    if (s->has_line_break()) append_optional_linefeed();-    if (s->has_line_break()) append_indentation();   } -  void Inspect::operator()(Id_Selector_Ptr s)+  void Inspect::operator()(Id_Selector* s)   {     append_token(s->ns_name(), s);-    if (s->has_line_break()) append_optional_linefeed();-    if (s->has_line_break()) append_indentation();   } -  void Inspect::operator()(Attribute_Selector_Ptr s)+  void Inspect::operator()(Attribute_Selector* s)   {     append_string("[");     add_open_mapping(s);@@ -977,111 +974,38 @@     append_string("]");   } -  void Inspect::operator()(Pseudo_Selector_Ptr s)+  void Inspect::operator()(Pseudo_Selector* s)   {-    append_token(s->ns_name(), s);-    if (s->expression()) {-      append_string("(");-      s->expression()->perform(this);-      append_string(")");-    }-  } -  void Inspect::operator()(Wrapped_Selector_Ptr s)-  {-    if (s->name() == " ") {-      append_string("");-    } else {-      bool was = in_wrapped;-      in_wrapped = true;-      append_token(s->name(), s);-      append_string("(");-      bool was_comma_array = in_comma_array;-      in_comma_array = false;-      s->selector()->perform(this);-      in_comma_array = was_comma_array;-      append_string(")");-      in_wrapped = was;-    }-  }--  void Inspect::operator()(Compound_Selector_Ptr s)-  {-    for (size_t i = 0, L = s->length(); i < L; ++i) {-      (*s)[i]->perform(this);-    }-    if (s->has_line_break()) {-      if (output_style() != COMPACT) {-        append_optional_linefeed();-      }-    }-  }--  void Inspect::operator()(Complex_Selector_Ptr c)-  {-    Compound_Selector_Obj      head = c->head();-    Complex_Selector_Obj            tail = c->tail();-    Complex_Selector::Combinator comb = c->combinator();--    if (comb == Complex_Selector::ANCESTOR_OF && (!head || head->empty())) {-      if (tail) tail->perform(this);-      return;-    }--    if (c->has_line_feed()) {-      if (!(c->has_parent_ref())) {-        append_optional_linefeed();-        append_indentation();+    if (s->name() != "") {+      append_string(":");+      if (s->isSyntacticElement()) {+        append_string(":");       }-    }--    if (head && head->length() != 0) head->perform(this);-    bool is_empty = !head || head->length() == 0 || head->is_empty_reference();-    bool is_tail = head && !head->is_empty_reference() && tail;-    if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;--    switch (comb) {-      case Complex_Selector::ANCESTOR_OF:-        if (is_tail) append_mandatory_space();-      break;-      case Complex_Selector::PARENT_OF:-        append_optional_space();-        append_string(">");-        append_optional_space();-      break;-      case Complex_Selector::ADJACENT_TO:-        append_optional_space();-        append_string("+");-        append_optional_space();-      break;-      case Complex_Selector::REFERENCE:-        append_mandatory_space();-        append_string("/");-        if (c->reference()) c->reference()->perform(this);-        append_string("/");-        append_mandatory_space();-      break;-      case Complex_Selector::PRECEDES:-        if (is_empty) append_optional_space();-        else append_mandatory_space();-        append_string("~");-        if (tail) append_mandatory_space();-        else append_optional_space();-      break;-      default: break;-    }-    if (tail && comb != Complex_Selector::ANCESTOR_OF) {-      if (c->has_line_break()) append_optional_linefeed();-    }-    if (tail) tail->perform(this);-    if (!tail && c->has_line_break()) {-      if (output_style() == COMPACT) {-        append_mandatory_space();+      append_token(s->ns_name(), s);+      if (s->selector() || s->argument()) {+        bool was = in_wrapped;+        in_wrapped = true;+        append_string("(");+        if (s->argument()) {+          s->argument()->perform(this);+        }+        if (s->selector() && s->argument()) {+          append_mandatory_space();+        }+        bool was_comma_array = in_comma_array;+        in_comma_array = false;+        if (s->selector()) {+          s->selector()->perform(this);+        }+        in_comma_array = was_comma_array;+        append_string(")");+        in_wrapped = was;       }     }   } -  void Inspect::operator()(Selector_List_Ptr g)+  void Inspect::operator()(SelectorList* g)   {      if (g->empty()) {@@ -1096,7 +1020,7 @@     // probably ruby sass eqivalent of element_needs_parens     if (output_style() == TO_SASS && g->length() == 1 &&       (!Cast<List>((*g)[0]) &&-       !Cast<Selector_List>((*g)[0]))) {+        !Cast<SelectorList>((*g)[0]))) {       append_string("(");     }     else if (!in_declaration && in_comma_array) {@@ -1106,8 +1030,9 @@     if (in_declaration) in_comma_array = true;      for (size_t i = 0, L = g->length(); i < L; ++i) {+       if (!in_wrapped && i == 0) append_indentation();-      if ((*g)[i] == 0) continue;+      if ((*g)[i] == nullptr) continue;       schedule_mapping(g->at(i)->last());       // add_open_mapping((*g)[i]->last());       (*g)[i]->perform(this);@@ -1122,7 +1047,7 @@     // probably ruby sass eqivalent of element_needs_parens     if (output_style() == TO_SASS && g->length() == 1 &&       (!Cast<List>((*g)[0]) &&-       !Cast<Selector_List>((*g)[0]))) {+        !Cast<SelectorList>((*g)[0]))) {       append_string(",)");     }     else if (!in_declaration && in_comma_array) {@@ -1130,9 +1055,70 @@     }    }+  void Inspect::operator()(ComplexSelector* sel)+  {+    if (sel->hasPreLineFeed()) {+      append_optional_linefeed();+      if (!in_wrapped && output_style() == NESTED) {+        append_indentation();+      }+    }+    const SelectorComponent* prev = nullptr;+    for (auto& item : sel->elements()) {+      if (prev != nullptr) {+        if (typeid(*item) == typeid(SelectorCombinator) ||+            typeid(*prev) == typeid(SelectorCombinator)) {+          append_optional_space();+        } else {+          append_mandatory_space();+        }+      }+      item->perform(this);+      prev = item.ptr();+    }+  } -  void Inspect::fallback_impl(AST_Node_Ptr n)+  void Inspect::operator()(SelectorComponent* sel)   {+    // You should probably never call this method directly+    // But in case anyone does, we will do the upcasting+    if (auto comp = Cast<CompoundSelector>(sel)) operator()(comp);+    if (auto comb = Cast<SelectorCombinator>(sel)) operator()(comb);+  }++  void Inspect::operator()(CompoundSelector* sel)+  {+    if (sel->hasRealParent()) {+      append_string("&");+    }+    for (auto& item : sel->elements()) {+      item->perform(this);+    }+    // Add the post line break (from ruby sass)+    // Dart sass uses another logic for newlines+    if (sel->hasPostLineBreak()) {+      if (output_style() != COMPACT) {+        append_optional_linefeed();+      }+    }+  }++  void Inspect::operator()(SelectorCombinator* sel)+  {+    append_optional_space();+    switch (sel->combinator()) {+      case SelectorCombinator::Combinator::CHILD: append_string(">"); break;+      case SelectorCombinator::Combinator::GENERAL: append_string("~"); break;+      case SelectorCombinator::Combinator::ADJACENT: append_string("+"); break;+    }+    append_optional_space();+    // Add the post line break (from ruby sass)+    // Dart sass uses another logic for newlines+    if (sel->hasPostLineBreak()) {+      if (output_style() != COMPACT) {+        // append_optional_linefeed();+      }+    }   }  }
libsass/src/inspect.hpp view
@@ -13,90 +13,88 @@     // import all the class-specific methods and override as desired     using Operation_CRTP<void, Inspect>::operator(); -    void fallback_impl(AST_Node_Ptr n);-   public:      Inspect(const Emitter& emi);     virtual ~Inspect();      // statements-    virtual void operator()(Block_Ptr);-    virtual void operator()(Ruleset_Ptr);-    virtual void operator()(Bubble_Ptr);-    virtual void operator()(Supports_Block_Ptr);-    virtual void operator()(Media_Block_Ptr);-    virtual void operator()(At_Root_Block_Ptr);-    virtual void operator()(Directive_Ptr);-    virtual void operator()(Keyframe_Rule_Ptr);-    virtual void operator()(Declaration_Ptr);-    virtual void operator()(Assignment_Ptr);-    virtual void operator()(Import_Ptr);-    virtual void operator()(Import_Stub_Ptr);-    virtual void operator()(Warning_Ptr);-    virtual void operator()(Error_Ptr);-    virtual void operator()(Debug_Ptr);-    virtual void operator()(Comment_Ptr);-    virtual void operator()(If_Ptr);-    virtual void operator()(For_Ptr);-    virtual void operator()(Each_Ptr);-    virtual void operator()(While_Ptr);-    virtual void operator()(Return_Ptr);-    virtual void operator()(Extension_Ptr);-    virtual void operator()(Definition_Ptr);-    virtual void operator()(Mixin_Call_Ptr);-    virtual void operator()(Content_Ptr);+    virtual void operator()(Block*);+    virtual void operator()(Ruleset*);+    virtual void operator()(Bubble*);+    virtual void operator()(Supports_Block*);+    virtual void operator()(At_Root_Block*);+    virtual void operator()(Directive*);+    virtual void operator()(Keyframe_Rule*);+    virtual void operator()(Declaration*);+    virtual void operator()(Assignment*);+    virtual void operator()(Import*);+    virtual void operator()(Import_Stub*);+    virtual void operator()(Warning*);+    virtual void operator()(Error*);+    virtual void operator()(Debug*);+    virtual void operator()(Comment*);+    virtual void operator()(If*);+    virtual void operator()(For*);+    virtual void operator()(Each*);+    virtual void operator()(While*);+    virtual void operator()(Return*);+    virtual void operator()(ExtendRule*);+    virtual void operator()(Definition*);+    virtual void operator()(Mixin_Call*);+    virtual void operator()(Content*);     // expressions-    virtual void operator()(Map_Ptr);-    virtual void operator()(Function_Ptr);-    virtual void operator()(List_Ptr);-    virtual void operator()(Binary_Expression_Ptr);-    virtual void operator()(Unary_Expression_Ptr);-    virtual void operator()(Function_Call_Ptr);-    virtual void operator()(Function_Call_Schema_Ptr);-    // virtual void operator()(Custom_Warning_Ptr);-    // virtual void operator()(Custom_Error_Ptr);-    virtual void operator()(Variable_Ptr);-    virtual void operator()(Number_Ptr);-    virtual void operator()(Color_Ptr);-    virtual void operator()(Boolean_Ptr);-    virtual void operator()(String_Schema_Ptr);-    virtual void operator()(String_Constant_Ptr);-    virtual void operator()(String_Quoted_Ptr);-    virtual void operator()(Custom_Error_Ptr);-    virtual void operator()(Custom_Warning_Ptr);-    virtual void operator()(Supports_Operator_Ptr);-    virtual void operator()(Supports_Negation_Ptr);-    virtual void operator()(Supports_Declaration_Ptr);-    virtual void operator()(Supports_Interpolation_Ptr);-    virtual void operator()(Media_Query_Ptr);-    virtual void operator()(Media_Query_Expression_Ptr);-    virtual void operator()(At_Root_Query_Ptr);-    virtual void operator()(Null_Ptr);-    virtual void operator()(Parent_Selector_Ptr p);+    virtual void operator()(Map*);+    virtual void operator()(Function*);+    virtual void operator()(List*);+    virtual void operator()(Binary_Expression*);+    virtual void operator()(Unary_Expression*);+    virtual void operator()(Function_Call*);+    // virtual void operator()(Custom_Warning*);+    // virtual void operator()(Custom_Error*);+    virtual void operator()(Variable*);+    virtual void operator()(Number*);+    virtual void operator()(Color_RGBA*);+    virtual void operator()(Color_HSLA*);+    virtual void operator()(Boolean*);+    virtual void operator()(String_Schema*);+    virtual void operator()(String_Constant*);+    virtual void operator()(String_Quoted*);+    virtual void operator()(Custom_Error*);+    virtual void operator()(Custom_Warning*);+    virtual void operator()(Supports_Operator*);+    virtual void operator()(Supports_Negation*);+    virtual void operator()(Supports_Declaration*);+    virtual void operator()(Supports_Interpolation*);+    virtual void operator()(MediaRule*);+    virtual void operator()(CssMediaRule*);+    virtual void operator()(CssMediaQuery*);+    virtual void operator()(Media_Query*);+    virtual void operator()(Media_Query_Expression*);+    virtual void operator()(At_Root_Query*);+    virtual void operator()(Null*);+    virtual void operator()(Parent_Reference* p);     // parameters and arguments-    virtual void operator()(Parameter_Ptr);-    virtual void operator()(Parameters_Ptr);-    virtual void operator()(Argument_Ptr);-    virtual void operator()(Arguments_Ptr);+    virtual void operator()(Parameter*);+    virtual void operator()(Parameters*);+    virtual void operator()(Argument*);+    virtual void operator()(Arguments*);     // selectors-    virtual void operator()(Selector_Schema_Ptr);-    virtual void operator()(Placeholder_Selector_Ptr);-    virtual void operator()(Element_Selector_Ptr);-    virtual void operator()(Class_Selector_Ptr);-    virtual void operator()(Id_Selector_Ptr);-    virtual void operator()(Attribute_Selector_Ptr);-    virtual void operator()(Pseudo_Selector_Ptr);-    virtual void operator()(Wrapped_Selector_Ptr);-    virtual void operator()(Compound_Selector_Ptr);-    virtual void operator()(Complex_Selector_Ptr);-    virtual void operator()(Selector_List_Ptr);--    virtual std::string lbracket(List_Ptr);-    virtual std::string rbracket(List_Ptr);+    virtual void operator()(Selector_Schema*);+    virtual void operator()(Placeholder_Selector*);+    virtual void operator()(Type_Selector*);+    virtual void operator()(Class_Selector*);+    virtual void operator()(Id_Selector*);+    virtual void operator()(Attribute_Selector*);+    virtual void operator()(Pseudo_Selector*);+    virtual void operator()(SelectorComponent*);+    virtual void operator()(SelectorCombinator*);+    virtual void operator()(CompoundSelector*);+    virtual void operator()(ComplexSelector*);+    virtual void operator()(SelectorList*);+    virtual std::string lbracket(List*);+    virtual std::string rbracket(List*); -    // template <typename U>-    // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node_Ptr>(x)); }   };  }
libsass/src/json.cpp view
@@ -402,7 +402,7 @@   try {     emit_string(&sb, str);   }-  catch (std::exception) {+  catch (std::exception&) {     sb_free(&sb);     throw;   }@@ -421,7 +421,7 @@     else       emit_value(&sb, node);   }-  catch (std::exception) {+  catch (std::exception&) {     sb_free(&sb);     throw;   }
libsass/src/lexer.cpp view
@@ -1,9 +1,12 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <cctype>+ #include <iostream> #include <iomanip> #include "lexer.hpp" #include "constants.hpp"+#include "util_string.hpp"   namespace Sass {@@ -25,77 +28,14 @@     const char* kwd_minus(const char* src) { return exactly<'-'>(src); };     const char* kwd_slash(const char* src) { return exactly<'/'>(src); }; -    //####################################-    // implement some function that do exist in the standard-    // but those are locale aware which brought some trouble-    // this even seems to improve performance by quite a bit-    //####################################--    bool is_alpha(const char& chr)-    {-      return unsigned(chr - 'A') <= 'Z' - 'A' ||-             unsigned(chr - 'a') <= 'z' - 'a';-    }--    bool is_space(const char& chr)-    {-      // adapted the technique from is_alpha-      return chr == ' ' || unsigned(chr - '\t') <= '\r' - '\t';-    }--    bool is_digit(const char& chr)-    {-      // adapted the technique from is_alpha-      return unsigned(chr - '0') <= '9' - '0';-    }--    bool is_number(const char& chr)-    {-      // adapted the technique from is_alpha-      return is_digit(chr) || chr == '-' || chr == '+';-    }--    bool is_xdigit(const char& chr)-    {-      // adapted the technique from is_alpha-      return unsigned(chr - '0') <= '9' - '0' ||-             unsigned(chr - 'a') <= 'f' - 'a' ||-             unsigned(chr - 'A') <= 'F' - 'A';-    }--    bool is_punct(const char& chr)-    {-      // locale independent-      return chr == '.';-    }--    bool is_alnum(const char& chr)-    {-      return is_alpha(chr) || is_digit(chr);-    }--    // check if char is outside ascii range-    bool is_unicode(const char& chr)-    {-      // check for unicode range-      return unsigned(chr) > 127;-    }--    // check if char is outside ascii range-    // but with specific ranges (copied from Ruby Sass)-    bool is_nonascii(const char& chr)-    {-      unsigned int cmp = unsigned(chr);-      return (-        (cmp >= 128 && cmp <= 15572911) ||-        (cmp >= 15630464 && cmp <= 15712189) ||-        (cmp >= 4036001920)-      );+    bool is_number(char chr) {+      return Util::ascii_isdigit(static_cast<unsigned char>(chr)) ||+        chr == '-' || chr == '+';     }      // check if char is within a reduced ascii range     // valid in a uri (copied from Ruby Sass)-    bool is_uri_character(const char& chr)+    bool is_uri_character(char chr)     {       unsigned int cmp = unsigned(chr);       return (cmp > 41 && cmp < 127) ||@@ -104,17 +44,19 @@      // check if char is within a reduced ascii range     // valid for escaping (copied from Ruby Sass)-    bool is_escapable_character(const char& chr)+    bool is_escapable_character(char chr)     {       unsigned int cmp = unsigned(chr);       return cmp > 31 && cmp < 127;     }      // Match word character (look ahead)-    bool is_character(const char& chr)+    bool is_character(char chr)     {       // valid alpha, numeric or unicode char (plus hyphen)-      return is_alnum(chr) || is_unicode(chr) || chr == '-';+      return Util::ascii_isalnum(static_cast<unsigned char>(chr)) ||+        !Util::ascii_isascii(static_cast<unsigned char>(chr)) ||+        chr == '-';     }      //####################################@@ -122,16 +64,13 @@     //####################################      // create matchers that advance the position-    const char* space(const char* src) { return is_space(*src) ? src + 1 : 0; }-    const char* alpha(const char* src) { return is_alpha(*src) ? src + 1 : 0; }-    const char* unicode(const char* src) { return is_unicode(*src) ? src + 1 : 0; }-    const char* nonascii(const char* src) { return is_nonascii(*src) ? src + 1 : 0; }-    const char* digit(const char* src) { return is_digit(*src) ? src + 1 : 0; }-    const char* xdigit(const char* src) { return is_xdigit(*src) ? src + 1 : 0; }-    const char* alnum(const char* src) { return is_alnum(*src) ? src + 1 : 0; }-    const char* punct(const char* src) { return is_punct(*src) ? src + 1 : 0; }-    const char* hyphen(const char* src) { return *src && *src == '-' ? src + 1 : 0; }-    const char* character(const char* src) { return is_character(*src) ? src + 1 : 0; }+    const char* space(const char* src) { return Util::ascii_isspace(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+    const char* alpha(const char* src) { return Util::ascii_isalpha(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+    const char* nonascii(const char* src) { return Util::ascii_isascii(static_cast<unsigned char>(*src)) ? nullptr : src + 1; }+    const char* digit(const char* src) { return Util::ascii_isdigit(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+    const char* xdigit(const char* src) { return Util::ascii_isxdigit(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+    const char* alnum(const char* src) { return Util::ascii_isalnum(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+    const char* hyphen(const char* src) { return *src == '-' ? src + 1 : 0; }     const char* uri_character(const char* src) { return is_uri_character(*src) ? src + 1 : 0; }     const char* escapable_character(const char* src) { return is_escapable_character(*src) ? src + 1 : 0; } @@ -150,11 +89,11 @@     // Match word boundary (zero-width lookahead).     const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; } -    // Match linefeed /(?:\n|\r\n?)/+    // Match linefeed /(?:\n|\r\n?|\f)/     const char* re_linebreak(const char* src)     {       // end of file or unix linefeed return here-      if (*src == 0 || *src == '\n') return src + 1;+      if (*src == 0 || *src == '\n' || *src == '\f') return src + 1;       // a carriage return may optionally be followed by a linefeed       if (*src == '\r') return *(src + 1) == '\n' ? src + 2 : src + 1;       // no linefeed@@ -166,7 +105,7 @@     const char* end_of_line(const char* src)     {       // end of file or unix linefeed return here-      return *src == 0 || *src == '\n' || *src == '\r' ? src : 0;+      return *src == 0 || *src == '\n' || *src == '\r' || *src == '\f' ? src : 0;     }      // Assert end_of_file boundary (/\z/)
libsass/src/lexer.hpp view
@@ -24,31 +24,20 @@     // BASIC CLASS MATCHERS     //#################################### -    // These are locale independant-    bool is_space(const char& src);-    bool is_alpha(const char& src);-    bool is_punct(const char& src);-    bool is_digit(const char& src);-    bool is_number(const char& src);-    bool is_alnum(const char& src);-    bool is_xdigit(const char& src);-    bool is_unicode(const char& src);-    bool is_nonascii(const char& src);-    bool is_character(const char& src);-    bool is_uri_character(const char& src);-    bool escapable_character(const char& src);+    // Matches ASCII digits, +, and -.+    bool is_number(char src); +    bool is_uri_character(char src);+    bool escapable_character(char src);+     // Match a single ctype predicate.     const char* space(const char* src);     const char* alpha(const char* src);     const char* digit(const char* src);     const char* xdigit(const char* src);     const char* alnum(const char* src);-    const char* punct(const char* src);     const char* hyphen(const char* src);-    const char* unicode(const char* src);     const char* nonascii(const char* src);-    const char* character(const char* src);     const char* uri_character(const char* src);     const char* escapable_character(const char* src); 
libsass/src/listize.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include <typeinfo> #include <string>@@ -13,8 +16,14 @@   Listize::Listize()   {  } -  Expression_Ptr Listize::operator()(Selector_List_Ptr sel)+  Expression* Listize::perform(AST_Node* node)   {+    Listize listize;+    return node->perform(&listize);+  }++  Expression* Listize::operator()(SelectorList* sel)+  {     List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);     l->from_selector(true);     for (size_t i = 0, L = sel->length(); i < L; ++i) {@@ -25,62 +34,37 @@     return SASS_MEMORY_NEW(Null, l->pstate());   } -  Expression_Ptr Listize::operator()(Compound_Selector_Ptr sel)+  Expression* Listize::operator()(CompoundSelector* sel)   {     std::string str;     for (size_t i = 0, L = sel->length(); i < L; ++i) {-      Expression_Ptr e = (*sel)[i]->perform(this);+      Expression* e = (*sel)[i]->perform(this);       if (e) str += e->to_string();     }     return SASS_MEMORY_NEW(String_Quoted, sel->pstate(), str);   } -  Expression_Ptr Listize::operator()(Complex_Selector_Ptr sel)+  Expression* Listize::operator()(ComplexSelector* sel)   {-    List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), 2);+    List_Obj l = SASS_MEMORY_NEW(List, sel->pstate());+    // ToDo: investigate what this does+    // Note: seems reated to parent ref     l->from_selector(true);-    Compound_Selector_Obj head = sel->head();-    if (head && !head->is_empty_reference())-    {-      Expression_Ptr hh = head->perform(this);-      if (hh) l->append(hh);-    } -    std::string reference = ! sel->reference() ? ""-      : sel->reference()->to_string();-    switch(sel->combinator())-    {-      case Complex_Selector::PARENT_OF:-        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), ">"));-      break;-      case Complex_Selector::ADJACENT_TO:-        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "+"));-      break;-      case Complex_Selector::REFERENCE:-        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "/" + reference + "/"));-      break;-      case Complex_Selector::PRECEDES:-        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "~"));-      break;-      case Complex_Selector::ANCESTOR_OF:-      break;-      default: break;+    for (auto component : sel->elements()) {+      if (CompoundSelectorObj compound = Cast<CompoundSelector>(component)) {+        if (!compound->empty()) {+          Expression_Obj hh = compound->perform(this);+          if (hh) l->append(hh);+        }+      }+      else if (component) {+        l->append(SASS_MEMORY_NEW(String_Quoted, component->pstate(), component->to_string()));+      }     } -    Complex_Selector_Obj tail = sel->tail();-    if (tail)-    {-      Expression_Obj tt = tail->perform(this);-      if (List_Ptr ls = Cast<List>(tt))-      { l->concat(ls); }-    }     if (l->length() == 0) return 0;     return l.detach();-  }--  Expression_Ptr Listize::fallback_impl(AST_Node_Ptr n)-  {-    return Cast<Expression>(n);   }  }
libsass/src/listize.hpp view
@@ -1,32 +1,35 @@ #ifndef SASS_LISTIZE_H #define SASS_LISTIZE_H -#include <vector>-#include <iostream>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp" -#include "ast.hpp"-#include "context.hpp"+#include "ast_fwd_decl.hpp" #include "operation.hpp"-#include "environment.hpp"  namespace Sass {    struct Backtrace; -  class Listize : public Operation_CRTP<Expression_Ptr, Listize> {+  class Listize : public Operation_CRTP<Expression*, Listize> { -    Expression_Ptr fallback_impl(AST_Node_Ptr n);+  public: +    static Expression* perform(AST_Node* node);+   public:     Listize();     ~Listize() { } -    Expression_Ptr operator()(Selector_List_Ptr);-    Expression_Ptr operator()(Complex_Selector_Ptr);-    Expression_Ptr operator()(Compound_Selector_Ptr);+    Expression* operator()(SelectorList*);+    Expression* operator()(ComplexSelector*);+    Expression* operator()(CompoundSelector*); +    // generic fallback     template <typename U>-    Expression_Ptr fallback(U x) { return fallback_impl(x); }+    Expression* fallback(U x)+    { return Cast<Expression>(x); }   };  }
libsass/src/memory/SharedPtr.cpp view
@@ -18,7 +18,7 @@       std::cerr << "# REPORTING MISSING DEALLOCATIONS #\n";       std::cerr << "###################################\n";       for (SharedObj* var : all) {-        if (AST_Node_Ptr ast = dynamic_cast<AST_Node*>(var)) {+        if (AST_Node* ast = dynamic_cast<AST_Node*>(var)) {           debug_ast(ast);         } else {           std::cerr << "LEAKED " << var << "\n";@@ -30,85 +30,4 @@   #endif    bool SharedObj::taint = false;--  SharedObj::SharedObj()-  : detached(false)-    #ifdef DEBUG_SHARED_PTR-    , dbg(false)-    #endif-  {-    refcounter = 0;-    #ifdef DEBUG_SHARED_PTR-      if (taint) all.push_back(this);-    #endif-  };--  SharedObj::~SharedObj() {-    #ifdef DEBUG_SHARED_PTR-      if (dbg) std::cerr << "Destruct " << this << "\n";-      if(!all.empty()) { // check needed for MSVC (no clue why?)-        all.erase(std::remove(all.begin(), all.end(), this), all.end());-      }-    #endif-  };--  void SharedPtr::decRefCount() {-    if (node) {-      -- node->refcounter;-      #ifdef DEBUG_SHARED_PTR-        if (node->dbg)  std::cerr << "- " << node << " X " << node->refcounter << " (" << this << ") " << "\n";-      #endif-      if (node->refcounter == 0) {-        #ifdef DEBUG_SHARED_PTR-          // AST_Node_Ptr ast = dynamic_cast<AST_Node*>(node);-          if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";-        #endif-        if (!node->detached) {-          delete(node);-        }-      }-    }-  }--  void SharedPtr::incRefCount() {-    if (node) {-      ++ node->refcounter;-      node->detached = false;-      #ifdef DEBUG_SHARED_PTR-        if (node->dbg) {-          std::cerr << "+ " << node << " X " << node->refcounter << " (" << this << ") " << "\n";-        }-      #endif-    }-  }--  SharedPtr::~SharedPtr() {-    decRefCount();-  }---  // the create constructor-  SharedPtr::SharedPtr(SharedObj* ptr)-  : node(ptr) {-    incRefCount();-  }-  // copy assignment operator-  SharedPtr& SharedPtr::operator=(const SharedPtr& rhs) {-    void* cur_ptr = (void*) node;-    void* rhs_ptr = (void*) rhs.node;-    if (cur_ptr == rhs_ptr) {-      return *this;-    }-    decRefCount();-    node = rhs.node;-    incRefCount();-    return *this;-  }--  // the copy constructor-  SharedPtr::SharedPtr(const SharedPtr& obj)-  : node(obj.node) {-    incRefCount();-  }- }
libsass/src/memory/SharedPtr.hpp view
@@ -3,8 +3,16 @@  #include "sass/base.h" +#include <cstddef>+#include <iostream>+#include <string>+#include <type_traits> #include <vector> +// https://lokiastari.com/blog/2014/12/30/c-plus-plus-by-example-smart-pointer/index.html+// https://lokiastari.com/blog/2015/01/15/c-plus-plus-by-example-smart-pointer-part-ii/index.html+// https://lokiastari.com/blog/2015/01/23/c-plus-plus-by-example-smart-pointer-part-iii/index.html+ namespace Sass {    class SharedPtr;@@ -38,169 +46,275 @@    #endif +  // SharedObj is the base class for all objects that can be stored as a shared object+  // It adds the reference counter and other values directly to the objects+  // This gives a slight overhead when directly used as a stack object, but has some+  // advantages for our code. It is safe to create two shared pointers from the same+  // objects, as the "control block" is directly attached to it. This would lead+  // to undefined behavior with std::shared_ptr. This also avoids the need to+  // allocate additional control blocks and/or the need to dereference two+  // pointers on each operation. This can be optimized in `std::shared_ptr`+  // too by using `std::make_shared` (where the control block and the actual+  // object are allocated in one continuous memory block via one single call).   class SharedObj {-  protected:-  friend class SharedPtr;-  friend class Memory_Manager;+   public:+    SharedObj() : refcount(0), detached(false) {+      #ifdef DEBUG_SHARED_PTR+      if (taint) all.push_back(this);+      #endif+    }+    virtual ~SharedObj() {+      #ifdef DEBUG_SHARED_PTR+      for (size_t i = 0; i < all.size(); i++) {+        if (all[i] == this) {+          all.erase(all.begin() + i);+          break;+        }+      }+      #endif+    }+     #ifdef DEBUG_SHARED_PTR-      static std::vector<SharedObj*> all;-      std::string file;-      size_t line;+    static void dumpMemLeaks();+    SharedObj* trace(std::string file, size_t line) {+      this->file = file;+      this->line = line;+      return this;+    }+    std::string getDbgFile() { return file; }+    size_t getDbgLine() { return line; }+    void setDbg(bool dbg) { this->dbg = dbg; }+    size_t getRefCount() const { return refcount; }     #endif-    static bool taint;-    long refcounter;-    // long refcount;++    static void setTaint(bool val) { taint = val; }++    virtual std::string to_string() const = 0;+   protected:+    friend class SharedPtr;+    friend class Memory_Manager;+    size_t refcount;     bool detached;+    static bool taint;     #ifdef DEBUG_SHARED_PTR-      bool dbg;+    std::string file;+    size_t line;+    bool dbg = false;+    static std::vector<SharedObj*> all;     #endif-  public:-    #ifdef DEBUG_SHARED_PTR-      static void dumpMemLeaks();-      SharedObj* trace(std::string file, size_t line) {-        this->file = file;-        this->line = line;-        return this;+  };++  // SharedPtr is a intermediate (template-less) base class for SharedImpl.+  // ToDo: there should be a way to include this in SharedImpl and to get+  // ToDo: rid of all the static_cast that are now needed in SharedImpl.+  class SharedPtr {+   public:+    SharedPtr() : node(nullptr) {}+    SharedPtr(SharedObj* ptr) : node(ptr) {+      incRefCount();+    }+    SharedPtr(const SharedPtr& obj) : SharedPtr(obj.node) {}+    ~SharedPtr() {+      decRefCount();+    }++    SharedPtr& operator=(SharedObj* other_node) {+      if (node != other_node) {+        decRefCount();+        node = other_node;+        incRefCount();+      } else if (node != nullptr) {+        node->detached = false;       }-    #endif-    SharedObj();-    #ifdef DEBUG_SHARED_PTR-      std::string getDbgFile() {-        return file;+      return *this;+    }++    SharedPtr& operator=(const SharedPtr& obj) {+      return *this = obj.node;+    }++    // Prevents all SharedPtrs from freeing this node until it is assigned to another SharedPtr.+    SharedObj* detach() {+      if (node != nullptr) node->detached = true;+      #ifdef DEBUG_SHARED_PTR+      if (node->dbg) {+        std::cerr << "DETACHING NODE\n";       }-      size_t getDbgLine() {-        return line;+      #endif +      return node;+    }++    SharedObj* obj() const { return node; }+    SharedObj* operator->() const { return node; }+    bool isNull() const { return node == nullptr; }+    operator bool() const { return node != nullptr; }++   protected:+    SharedObj* node;+    void decRefCount() {+      if (node == nullptr) return;+      --node->refcount;+      #ifdef DEBUG_SHARED_PTR+      if (node->dbg) std::cerr << "- " << node << " X " << node->refcount << " (" << this << ") " << "\n";+      #endif+      if (node->refcount == 0 && !node->detached) {+        #ifdef DEBUG_SHARED_PTR+        if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";+        #endif+        delete node;       }-      void setDbg(bool dbg) {-        this->dbg = dbg;+      else if (node->refcount == 0) {+        #ifdef DEBUG_SHARED_PTR+        if (node->dbg) std::cerr << "NODE EVAEDED DELETE " << node << "\n";+        #endif       }-    #endif-    static void setTaint(bool val) {-      taint = val;     }-    virtual ~SharedObj();-    long getRefCount() {-      return refcounter;+    void incRefCount() {+      if (node == nullptr) return;+      node->detached = false;+      ++node->refcount;+      #ifdef DEBUG_SHARED_PTR+      if (node->dbg) std::cerr << "+ " << node << " X " << node->refcount << " (" << this << ") " << "\n";+      #endif     }   }; +  template <class T>+  class SharedImpl : private SharedPtr { -  class SharedPtr {-  protected:-    SharedObj* node;-  protected:-    void decRefCount();-    void incRefCount();   public:-    // the empty constructor-    SharedPtr()-    : node(NULL) {};-    // the create constructor-    SharedPtr(SharedObj* ptr);-    // the copy constructor-    SharedPtr(const SharedPtr& obj);-    // the move constructor-    SharedPtr(SharedPtr&& obj);-    // copy assignment operator-    SharedPtr& operator=(const SharedPtr& obj);-    // move assignment operator-    SharedPtr& operator=(SharedPtr&& obj);-    // pure virtual destructor-    virtual ~SharedPtr() = 0;-  public:-    SharedObj* obj () const {-      return node;-    };-    SharedObj* operator-> () const {-      return node;-    };-    bool isNull () {-      return node == NULL;-    };-    bool isNull () const {-      return node == NULL;-    };-    SharedObj* detach() const {-      if (node) {-        node->detached = true;-      }-      return node;-    };-    operator bool() const {-      return node != NULL;-    };+    SharedImpl() : SharedPtr(nullptr) {} -  };+    template <class U>+    SharedImpl(U* node) :+      SharedPtr(static_cast<T*>(node)) {} -  template < class T >-  class SharedImpl : private SharedPtr {-  public:-    SharedImpl()-    : SharedPtr(NULL) {};-    SharedImpl(T* node)-    : SharedPtr(node) {};-    template < class U >-    SharedImpl(SharedImpl<U> obj)-    : SharedPtr(static_cast<T*>(obj.ptr())) {}-    SharedImpl(T&& node)-    : SharedPtr(node) {};-    SharedImpl(const T& node)-    : SharedPtr(node) {};-    // the copy constructor-    SharedImpl(const SharedImpl<T>& impl)-    : SharedPtr(impl.node) {};-    // the move constructor-    SharedImpl(SharedImpl<T>&& impl)-    : SharedPtr(impl.node) {};-    // copy assignment operator-    SharedImpl<T>& operator=(const SharedImpl<T>& rhs) {-      if (node) decRefCount();-      node = rhs.node;-      incRefCount();-      return *this;-    }-    // move assignment operator-    SharedImpl<T>& operator=(SharedImpl<T>&& rhs) {-      // don't move our self-      if (this != &rhs) {-        if (node) decRefCount();-        node = std::move(rhs.node);-        rhs.node = NULL;-      }-      return *this;-    }-    ~SharedImpl() {};-  public:-    operator T*() const {-      return static_cast<T*>(this->obj());-    }-    operator T&() const {-      return *static_cast<T*>(this->obj());+    template <class U>+    SharedImpl(const SharedImpl<U>& impl) :+      SharedImpl(impl.ptr()) {}++    template <class U>+    SharedImpl<T>& operator=(U *rhs) {+      return static_cast<SharedImpl<T>&>(+        SharedPtr::operator=(static_cast<T*>(rhs)));     }-    T& operator* () const {-      return *static_cast<T*>(this->obj());-    };-    T* operator-> () const {-      return static_cast<T*>(this->obj());-    };-    T* ptr () const {-      return static_cast<T*>(this->obj());-    };-    T* detach() const {-      if (this->obj() == NULL) return NULL;-      return static_cast<T*>(SharedPtr::detach());++    template <class U>+    SharedImpl<T>& operator=(const SharedImpl<U>& rhs) {+      return static_cast<SharedImpl<T>&>(+        SharedPtr::operator=(static_cast<const SharedImpl<T>&>(rhs)));     }-    bool isNull() const {-      return this->obj() == NULL;++    operator std::string() const {+      if (node) return node->to_string();+      return "null";     }-    bool operator<(const T& rhs) const {-      return *this->ptr() < rhs;-    };-    operator bool() const {-      return this->obj() != NULL;-    };++    using SharedPtr::isNull;+    using SharedPtr::operator bool;+    operator T*() const { return static_cast<T*>(this->obj()); }+    operator T&() const { return *static_cast<T*>(this->obj()); }+    T& operator* () const { return *static_cast<T*>(this->obj()); };+    T* operator-> () const { return static_cast<T*>(this->obj()); };+    T* ptr () const { return static_cast<T*>(this->obj()); };+    T* detach() { return static_cast<T*>(SharedPtr::detach()); }+   }; -}+  // Comparison operators, based on:+  // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp++  template<class T1, class T2>+  bool operator==(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+    return x.ptr() == y.ptr();+  }++  template<class T1, class T2>+  bool operator!=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+    return x.ptr() != y.ptr();+  }++  template<class T1, class T2>+  bool operator<(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+    using CT = typename std::common_type<T1*, T2*>::type;+    return std::less<CT>()(x.get(), y.get());+  }++  template<class T1, class T2>+  bool operator<=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+    return !(y < x);+  }++  template<class T1, class T2>+  bool operator>(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+    return y < x;+  }++  template<class T1, class T2>+  bool operator>=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+    return !(x < y);+  }++  template <class T>+  bool operator==(const SharedImpl<T>& x, std::nullptr_t) noexcept {+    return x.isNull();+  }++  template <class T>+  bool operator==(std::nullptr_t, const SharedImpl<T>& x) noexcept {+    return x.isNull();+  }++  template <class T>+  bool operator!=(const SharedImpl<T>& x, std::nullptr_t) noexcept {+    return !x.isNull();+  }++  template <class T>+  bool operator!=(std::nullptr_t, const SharedImpl<T>& x) noexcept {+    return !x.isNull();+  }++  template <class T>+  bool operator<(const SharedImpl<T>& x, std::nullptr_t) {+    return std::less<T*>()(x.get(), nullptr);+  }++  template <class T>+  bool operator<(std::nullptr_t, const SharedImpl<T>& y) {+    return std::less<T*>()(nullptr, y.get());+  }++  template <class T>+  bool operator<=(const SharedImpl<T>& x, std::nullptr_t) {+    return !(nullptr < x);+  }++  template <class T>+  bool operator<=(std::nullptr_t, const SharedImpl<T>& y) {+    return !(y < nullptr);+  }++  template <class T>+  bool operator>(const SharedImpl<T>& x, std::nullptr_t) {+    return nullptr < x;+  }++  template <class T>+  bool operator>(std::nullptr_t, const SharedImpl<T>& y) {+    return y < nullptr;+  }++  template <class T>+  bool operator>=(const SharedImpl<T>& x, std::nullptr_t) {+    return !(x < nullptr);+  }++  template <class T>+  bool operator>=(std::nullptr_t, const SharedImpl<T>& y) {+    return !(nullptr < y);+  }++}  // namespace Sass  #endif
− libsass/src/node.cpp
@@ -1,319 +0,0 @@-#include "sass.hpp"-#include <vector>--#include "node.hpp"-#include "context.hpp"-#include "parser.hpp"--namespace Sass {---  Node Node::createCombinator(const Complex_Selector::Combinator& combinator) {-    NodeDequePtr null;-    return Node(COMBINATOR, combinator, NULL /*pSelector*/, null /*pCollection*/);-  }---  Node Node::createSelector(const Complex_Selector& pSelector) {-    NodeDequePtr null;--    Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(&pSelector);-    pStripped->tail(NULL);-    pStripped->combinator(Complex_Selector::ANCESTOR_OF);--    Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/);-    n.got_line_feed = pSelector.has_line_feed();-    return n;-  }---  Node Node::createCollection() {-    NodeDequePtr pEmptyCollection = std::make_shared<NodeDeque>();-    return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection);-  }---  Node Node::createCollection(const NodeDeque& values) {-    NodeDequePtr pShallowCopiedCollection = std::make_shared<NodeDeque>(values);-    return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pShallowCopiedCollection);-  }---  Node Node::createNil() {-    NodeDequePtr null;-    return Node(NIL, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, null /*pCollection*/);-  }---  Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector_Ptr pSelector, NodeDequePtr& pCollection)-  : got_line_feed(false), mType(type), mCombinator(combinator), mpSelector(pSelector), mpCollection(pCollection)-  { if (pSelector) got_line_feed = pSelector->has_line_feed(); }---  Node Node::klone() const {-    NodeDequePtr pNewCollection = std::make_shared<NodeDeque>();-    if (mpCollection) {-      for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) {-        Node& toClone = *iter;-        pNewCollection->push_back(toClone.klone());-      }-    }--    Node n(mType, mCombinator, mpSelector ? SASS_MEMORY_COPY(mpSelector) : NULL, pNewCollection);-    n.got_line_feed = got_line_feed;-    return n;-  }---  bool Node::contains(const Node& potentialChild) const {-    bool found = false;--    for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) {-      Node& toTest = *iter;--      if (toTest == potentialChild) {-        found = true;-        break;-      }-    }--    return found;-  }---  bool Node::operator==(const Node& rhs) const {-    if (this->type() != rhs.type()) {-      return false;-    }--    if (this->isCombinator()) {--      return this->combinator() == rhs.combinator();--    } else if (this->isNil()) {--      return true; // no state to check--    } else if (this->isSelector()){--      return *this->selector() == *rhs.selector();--    } else if (this->isCollection()) {--      if (this->collection()->size() != rhs.collection()->size()) {-        return false;-      }--      for (NodeDeque::iterator lhsIter = this->collection()->begin(), lhsIterEnd = this->collection()->end(),-           rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) {--        if (*lhsIter != *rhsIter) {-          return false;-        }--      }--      return true;--    }--    // We shouldn't get here.-    throw "Comparing unknown node types. A new type was probably added and this method wasn't implemented for it.";-  }---  void Node::plus(Node& rhs) {-    if (!this->isCollection() || !rhs.isCollection()) {-      throw "Both the current node and rhs must be collections.";-    }-    this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end());-  }--#ifdef DEBUG-  std::ostream& operator<<(std::ostream& os, const Node& node) {--    if (node.isCombinator()) {--      switch (node.combinator()) {-        case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;-        case Complex_Selector::PARENT_OF:   os << "\">\""; break;-        case Complex_Selector::PRECEDES:    os << "\"~\""; break;-        case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;-        case Complex_Selector::REFERENCE: os    << "\"/\""; break;-      }--    } else if (node.isNil()) {--      os << "nil";--    } else if (node.isSelector()){--      os << node.selector()->head()->to_string();--    } else if (node.isCollection()) {--      os << "[";--      for (NodeDeque::iterator iter = node.collection()->begin(), iterBegin = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {-        if (iter != iterBegin) {-          os << ", ";-        }--        os << (*iter);-      }--      os << "]";--    }--    return os;--  }-#endif---  Node complexSelectorToNode(Complex_Selector_Ptr pToConvert) {-    if (pToConvert == NULL) {-      return Node::createNil();-    }-    Node node = Node::createCollection();-    node.got_line_feed = pToConvert->has_line_feed();-    bool has_lf = pToConvert->has_line_feed();--    // unwrap the selector from parent ref-    if (pToConvert->head() && pToConvert->head()->has_parent_ref()) {-      Complex_Selector_Obj tail = pToConvert->tail();-      if (tail) tail->has_line_feed(pToConvert->has_line_feed());-      pToConvert = tail;-    }--    while (pToConvert) {--      bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference();--      // the first Complex_Selector may contain a dummy head pointer, skip it.-      if (pToConvert->head() && !empty_parent_ref) {-        node.collection()->push_back(Node::createSelector(*pToConvert));-        if (has_lf) node.collection()->back().got_line_feed = has_lf;-        if (pToConvert->head() || empty_parent_ref) {-          if (pToConvert->tail()) {-            pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());-          }-        }-        has_lf = false;-      }--      if (pToConvert->combinator() != Complex_Selector::ANCESTOR_OF) {-        node.collection()->push_back(Node::createCombinator(pToConvert->combinator()));-        if (has_lf) node.collection()->back().got_line_feed = has_lf;-        has_lf = false;-      }--      if (pToConvert && empty_parent_ref && pToConvert->tail()) {-        // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());-      }--      pToConvert = pToConvert->tail();-    }--    return node;-  }---  Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert) {-    if (toConvert.isNil()) {-      return NULL;-    }---    if (!toConvert.isCollection()) {-      throw "The node to convert to a Complex_Selector_Ptr must be a collection type or nil.";-    }---    NodeDeque& childNodes = *toConvert.collection();--    std::string noPath("");-    Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL);--    Complex_Selector_Obj pCurrent = pFirst;--    if (toConvert.isSelector()) pFirst->has_line_feed(toConvert.got_line_feed);-    if (toConvert.isCombinator()) pFirst->has_line_feed(toConvert.got_line_feed);--    for (NodeDeque::iterator childIter = childNodes.begin(), childIterEnd = childNodes.end(); childIter != childIterEnd; childIter++) {--      Node& child = *childIter;--      if (child.isSelector()) {-        // JMA - need to clone the selector, because they can end up getting shared across Node-        // collections, and can result in an infinite loop during the call to parentSuperselector()-        pCurrent->tail(SASS_MEMORY_COPY(child.selector()));-        // if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);-        pCurrent = pCurrent->tail();-      } else if (child.isCombinator()) {-        pCurrent->combinator(child.combinator());-        if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);--        // if the next node is also a combinator, create another Complex_Selector to hold it so it doesn't replace the current combinator-        if (childIter+1 != childIterEnd) {-          Node& nextNode = *(childIter+1);-          if (nextNode.isCombinator()) {-            pCurrent->tail(SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL));-            if (nextNode.got_line_feed) pCurrent->tail()->has_line_feed(nextNode.got_line_feed);-            pCurrent = pCurrent->tail();-          }-        }-      } else {-        throw "The node to convert's children must be only combinators or selectors.";-      }-    }--    // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass-    Compound_Selector_Ptr fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[NODE]"), 1);-    Parent_Selector_Ptr selectorRef = SASS_MEMORY_NEW(Parent_Selector, ParserState("[NODE]"));-    fakeHead->elements().push_back(selectorRef);-    if (toConvert.got_line_feed) pFirst->has_line_feed(toConvert.got_line_feed);-    // pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed);-    pFirst->head(fakeHead);-    return SASS_MEMORY_COPY(pFirst);-  }--  // A very naive trim function, which removes duplicates in a node-  // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs-  Node Node::naiveTrim(Node& seqses) {--    std::vector<Node*> res;-    std::vector<Complex_Selector_Obj> known;--    NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(),-                                seqsesIterEnd = seqses.collection()->rend();--    for (; seqsesIter != seqsesIterEnd; ++seqsesIter)-    {-      Node& seqs1 = *seqsesIter;-      if( seqs1.isSelector() ) {-        Complex_Selector_Obj sel = seqs1.selector();-        std::vector<Complex_Selector_Obj>::iterator it;-        bool found = false;-        for (it = known.begin(); it != known.end(); ++it) {-          if (**it == *sel) { found = true; break; }-        }-        if( !found ) {-          known.push_back(seqs1.selector());-          res.push_back(&seqs1);-        }-      } else {-        res.push_back(&seqs1);-      }-    }--    Node result = Node::createCollection();--    for (size_t i = res.size() - 1; i != std::string::npos; --i) {-      result.collection()->push_back(*res[i]);-    }--    return result;-  }-}
− libsass/src/node.hpp
@@ -1,118 +0,0 @@-#ifndef SASS_NODE_H-#define SASS_NODE_H--#include <deque>-#include <memory>--#include "ast.hpp"---namespace Sass {-----  class Context;--  /*-   There are a lot of stumbling blocks when trying to port the ruby extend code to C++. The biggest is the choice of-   data type. The ruby code will pretty seamlessly switch types between an Array<SimpleSequence or Op> (libsass'-   equivalent is the Complex_Selector) to a Sequence, which contains more metadata about the sequence than just the-   selector info. They also have the ability to have arbitrary nestings of arrays like [1, [2]], which is hard to-   implement using Array equivalents in C++ (like the deque or vector). They also have the ability to include nil-   in the arrays, like [1, nil, 3], which has potential semantic differences than an empty array [1, [], 3]. To be-   able to represent all of these as unique cases, we need to create a tree of variant objects. The tree nature allows-   the inconsistent nesting levels. The variant nature (while making some of the C++ code uglier) allows the code to-   more closely match the ruby code, which is a huge benefit when attempting to implement an complex algorithm like-   the Extend operator.--   Note that the current libsass data model also pairs the combinator with the Complex_Selector that follows it, but-   ruby sass has no such restriction, so we attempt to create a data structure that can handle them split apart.-   */--  class Node;-  typedef std::deque<Node> NodeDeque;-  typedef std::shared_ptr<NodeDeque> NodeDequePtr;--  class Node {-  public:-    enum TYPE {-      SELECTOR,-      COMBINATOR,-      COLLECTION,-      NIL-    };--    TYPE type() const { return mType; }-    bool isCombinator() const { return mType == COMBINATOR; }-    bool isSelector() const { return mType == SELECTOR; }-    bool isCollection() const { return mType == COLLECTION; }-    bool isNil() const { return mType == NIL; }-    bool got_line_feed;--    Complex_Selector::Combinator combinator() const { return mCombinator; }--    Complex_Selector_Obj selector() { return mpSelector; }-    Complex_Selector_Obj selector() const { return mpSelector; }--    NodeDequePtr collection() { return mpCollection; }-    const NodeDequePtr collection() const { return mpCollection; }--    static Node createCombinator(const Complex_Selector::Combinator& combinator);--    // This method will klone the selector, stripping off the tail and combinator-    static Node createSelector(const Complex_Selector& pSelector);--    static Node createCollection();-    static Node createCollection(const NodeDeque& values);--    static Node createNil();-    static Node naiveTrim(Node& seqses);--    Node klone() const;--    bool operator==(const Node& rhs) const;-    inline bool operator!=(const Node& rhs) const { return !(*this == rhs); }---    /*-    COLLECTION FUNCTIONS--    Most types don't need any helper methods (nil and combinator due to their simplicity and-    selector due to the fact that we leverage the non-node selector code on the Complex_Selector-    whereever possible). The following methods are intended to be called on Node objects whose-    type is COLLECTION only.-    */--    // rhs and this must be node collections. Shallow copy the nodes from rhs to the end of this.-    // This function DOES NOT remove the nodes from rhs.-    void plus(Node& rhs);--    // potentialChild must be a node collection of selectors/combinators. this must be a collection-    // of collections of nodes/combinators. This method checks if potentialChild is a child of this-    // Node.-    bool contains(const Node& potentialChild) const;--  private:-    // Private constructor; Use the static methods (like createCombinator and createSelector)-    // to instantiate this object. This is more expressive, and it allows us to break apart each-    // case into separate functions.-    Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector_Ptr pSelector, NodeDequePtr& pCollection);--    TYPE mType;--    // TODO: can we union these to save on memory?-    Complex_Selector::Combinator mCombinator;-    Complex_Selector_Obj mpSelector;-    NodeDequePtr mpCollection;-  };--#ifdef DEBUG-  std::ostream& operator<<(std::ostream& os, const Node& node);-#endif-  Node complexSelectorToNode(Complex_Selector_Ptr pToConvert);-  Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert);--}--#endif
libsass/src/operation.hpp view
@@ -1,171 +1,221 @@ #ifndef SASS_OPERATION_H #define SASS_OPERATION_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++// base classes to implement curiously recurring template pattern (CRTP)+// https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern++#include <typeinfo>+#include <stdexcept>+ #include "ast_fwd_decl.hpp"+#include "ast_def_macros.hpp"  namespace Sass { +  #define ATTACH_ABSTRACT_CRTP_PERFORM_METHODS()\+    virtual void perform(Operation<void>* op) = 0; \+    virtual Value* perform(Operation<Value*>* op) = 0; \+    virtual std::string perform(Operation<std::string>* op) = 0; \+    virtual AST_Node* perform(Operation<AST_Node*>* op) = 0; \+    virtual Selector* perform(Operation<Selector*>* op) = 0; \+    virtual Statement* perform(Operation<Statement*>* op) = 0; \+    virtual Expression* perform(Operation<Expression*>* op) = 0; \+    virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) = 0; \+    virtual Supports_Condition* perform(Operation<Supports_Condition*>* op) = 0; \++  // you must add operators to every class+  // ensures `this` of actual instance type+  // we therefore call the specific operator+  // they are virtual so most specific is used+  #define ATTACH_CRTP_PERFORM_METHODS()\+    virtual void perform(Operation<void>* op) override { return (*op)(this); } \+    virtual Value* perform(Operation<Value*>* op) override { return (*op)(this); } \+    virtual std::string perform(Operation<std::string>* op) override { return (*op)(this); } \+    virtual AST_Node* perform(Operation<AST_Node*>* op) override { return (*op)(this); } \+    virtual Selector* perform(Operation<Selector*>* op) override { return (*op)(this); } \+    virtual Statement* perform(Operation<Statement*>* op) override { return (*op)(this); } \+    virtual Expression* perform(Operation<Expression*>* op) override { return (*op)(this); } \+    virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) override { return (*op)(this); } \+    virtual Supports_Condition* perform(Operation<Supports_Condition*>* op) override { return (*op)(this); } \+   template<typename T>   class Operation {   public:-    virtual T operator()(AST_Node_Ptr x)               = 0;-    virtual ~Operation()                            { }+    virtual T operator()(AST_Node* x)               = 0;     // statements-    virtual T operator()(Block_Ptr x)                  = 0;-    virtual T operator()(Ruleset_Ptr x)                = 0;-    virtual T operator()(Bubble_Ptr x)                 = 0;-    virtual T operator()(Trace_Ptr x)                  = 0;-    virtual T operator()(Supports_Block_Ptr x)         = 0;-    virtual T operator()(Media_Block_Ptr x)            = 0;-    virtual T operator()(At_Root_Block_Ptr x)          = 0;-    virtual T operator()(Directive_Ptr x)              = 0;-    virtual T operator()(Keyframe_Rule_Ptr x)          = 0;-    virtual T operator()(Declaration_Ptr x)            = 0;-    virtual T operator()(Assignment_Ptr x)             = 0;-    virtual T operator()(Import_Ptr x)                 = 0;-    virtual T operator()(Import_Stub_Ptr x)            = 0;-    virtual T operator()(Warning_Ptr x)                = 0;-    virtual T operator()(Error_Ptr x)                  = 0;-    virtual T operator()(Debug_Ptr x)                  = 0;-    virtual T operator()(Comment_Ptr x)                = 0;-    virtual T operator()(If_Ptr x)                     = 0;-    virtual T operator()(For_Ptr x)                    = 0;-    virtual T operator()(Each_Ptr x)                   = 0;-    virtual T operator()(While_Ptr x)                  = 0;-    virtual T operator()(Return_Ptr x)                 = 0;-    virtual T operator()(Content_Ptr x)                = 0;-    virtual T operator()(Extension_Ptr x)              = 0;-    virtual T operator()(Definition_Ptr x)             = 0;-    virtual T operator()(Mixin_Call_Ptr x)             = 0;+    virtual T operator()(Block* x)                  = 0;+    virtual T operator()(Ruleset* x)                = 0;+    virtual T operator()(Bubble* x)                 = 0;+    virtual T operator()(Trace* x)                  = 0;+    virtual T operator()(Supports_Block* x)         = 0;+    virtual T operator()(MediaRule* x) = 0;+    virtual T operator()(CssMediaRule* x) = 0;+    virtual T operator()(CssMediaQuery* x) = 0;+    virtual T operator()(At_Root_Block* x)          = 0;+    virtual T operator()(Directive* x)              = 0;+    virtual T operator()(Keyframe_Rule* x)          = 0;+    virtual T operator()(Declaration* x)            = 0;+    virtual T operator()(Assignment* x)             = 0;+    virtual T operator()(Import* x)                 = 0;+    virtual T operator()(Import_Stub* x)            = 0;+    virtual T operator()(Warning* x)                = 0;+    virtual T operator()(Error* x)                  = 0;+    virtual T operator()(Debug* x)                  = 0;+    virtual T operator()(Comment* x)                = 0;+    virtual T operator()(If* x)                     = 0;+    virtual T operator()(For* x)                    = 0;+    virtual T operator()(Each* x)                   = 0;+    virtual T operator()(While* x)                  = 0;+    virtual T operator()(Return* x)                 = 0;+    virtual T operator()(Content* x)                = 0;+    virtual T operator()(ExtendRule* x)              = 0;+    virtual T operator()(Definition* x)             = 0;+    virtual T operator()(Mixin_Call* x)             = 0;     // expressions-    virtual T operator()(List_Ptr x)                   = 0;-    virtual T operator()(Map_Ptr x)                    = 0;-    virtual T operator()(Function_Ptr x)               = 0;-    virtual T operator()(Binary_Expression_Ptr x)      = 0;-    virtual T operator()(Unary_Expression_Ptr x)       = 0;-    virtual T operator()(Function_Call_Ptr x)          = 0;-    virtual T operator()(Function_Call_Schema_Ptr x)   = 0;-    virtual T operator()(Custom_Warning_Ptr x)         = 0;-    virtual T operator()(Custom_Error_Ptr x)           = 0;-    virtual T operator()(Variable_Ptr x)               = 0;-    virtual T operator()(Number_Ptr x)                 = 0;-    virtual T operator()(Color_Ptr x)                  = 0;-    virtual T operator()(Boolean_Ptr x)                = 0;-    virtual T operator()(String_Schema_Ptr x)          = 0;-    virtual T operator()(String_Quoted_Ptr x)          = 0;-    virtual T operator()(String_Constant_Ptr x)        = 0;-    virtual T operator()(Supports_Condition_Ptr x)     = 0;-    virtual T operator()(Supports_Operator_Ptr x)      = 0;-    virtual T operator()(Supports_Negation_Ptr x)      = 0;-    virtual T operator()(Supports_Declaration_Ptr x)   = 0;-    virtual T operator()(Supports_Interpolation_Ptr x) = 0;-    virtual T operator()(Media_Query_Ptr x)            = 0;-    virtual T operator()(Media_Query_Expression_Ptr x) = 0;-    virtual T operator()(At_Root_Query_Ptr x)          = 0;-    virtual T operator()(Null_Ptr x)                   = 0;-    virtual T operator()(Parent_Selector_Ptr x)        = 0;+    virtual T operator()(Null* x)                   = 0;+    virtual T operator()(List* x)                   = 0;+    virtual T operator()(Map* x)                    = 0;+    virtual T operator()(Function* x)               = 0;+    virtual T operator()(Binary_Expression* x)      = 0;+    virtual T operator()(Unary_Expression* x)       = 0;+    virtual T operator()(Function_Call* x)          = 0;+    virtual T operator()(Custom_Warning* x)         = 0;+    virtual T operator()(Custom_Error* x)           = 0;+    virtual T operator()(Variable* x)               = 0;+    virtual T operator()(Number* x)                 = 0;+    virtual T operator()(Color* x)                  = 0;+    virtual T operator()(Color_RGBA* x)             = 0;+    virtual T operator()(Color_HSLA* x)             = 0;+    virtual T operator()(Boolean* x)                = 0;+    virtual T operator()(String_Schema* x)          = 0;+    virtual T operator()(String_Quoted* x)          = 0;+    virtual T operator()(String_Constant* x)        = 0;+    virtual T operator()(Supports_Condition* x)     = 0;+    virtual T operator()(Supports_Operator* x)      = 0;+    virtual T operator()(Supports_Negation* x)      = 0;+    virtual T operator()(Supports_Declaration* x)   = 0;+    virtual T operator()(Supports_Interpolation* x) = 0;+    virtual T operator()(Media_Query* x) = 0;+    virtual T operator()(Media_Query_Expression* x) = 0;+    virtual T operator()(At_Root_Query* x)          = 0;+    virtual T operator()(Parent_Reference* x)        = 0;     // parameters and arguments-    virtual T operator()(Parameter_Ptr x)              = 0;-    virtual T operator()(Parameters_Ptr x)             = 0;-    virtual T operator()(Argument_Ptr x)               = 0;-    virtual T operator()(Arguments_Ptr x)              = 0;+    virtual T operator()(Parameter* x)              = 0;+    virtual T operator()(Parameters* x)             = 0;+    virtual T operator()(Argument* x)               = 0;+    virtual T operator()(Arguments* x)              = 0;     // selectors-    virtual T operator()(Selector_Schema_Ptr x)        = 0;-    virtual T operator()(Placeholder_Selector_Ptr x)   = 0;-    virtual T operator()(Element_Selector_Ptr x)       = 0;-    virtual T operator()(Class_Selector_Ptr x)         = 0;-    virtual T operator()(Id_Selector_Ptr x)            = 0;-    virtual T operator()(Attribute_Selector_Ptr x)     = 0;-    virtual T operator()(Pseudo_Selector_Ptr x)        = 0;-    virtual T operator()(Wrapped_Selector_Ptr x)       = 0;-    virtual T operator()(Compound_Selector_Ptr x)= 0;-    virtual T operator()(Complex_Selector_Ptr x)      = 0;-    virtual T operator()(Selector_List_Ptr x) = 0;+    virtual T operator()(Selector_Schema* x)        = 0;+    virtual T operator()(Placeholder_Selector* x)   = 0;+    virtual T operator()(Type_Selector* x)       = 0;+    virtual T operator()(Class_Selector* x)         = 0;+    virtual T operator()(Id_Selector* x)            = 0;+    virtual T operator()(Attribute_Selector* x)     = 0;+    virtual T operator()(Pseudo_Selector* x)        = 0;+    virtual T operator()(SelectorComponent* x) = 0;+    virtual T operator()(SelectorCombinator* x) = 0;+    virtual T operator()(CompoundSelector* x) = 0;+    virtual T operator()(ComplexSelector* x) = 0;+    virtual T operator()(SelectorList* x) = 0; -    template <typename U>-    T fallback(U x) { return T(); }   }; +  // example: Operation_CRTP<Expression*, Eval>+  // T is the base return type of all visitors+  // D is the class derived visitor class+  // normally you want to implement all operators   template <typename T, typename D>   class Operation_CRTP : public Operation<T> {   public:-    D& impl() { return static_cast<D&>(*this); }-  public:-    T operator()(AST_Node_Ptr x)               { return static_cast<D*>(this)->fallback(x); }+    T operator()(AST_Node* x)               { return static_cast<D*>(this)->fallback(x); }     // statements-    T operator()(Block_Ptr x)                  { return static_cast<D*>(this)->fallback(x); }-    T operator()(Ruleset_Ptr x)                { return static_cast<D*>(this)->fallback(x); }-    T operator()(Bubble_Ptr x)                 { return static_cast<D*>(this)->fallback(x); }-    T operator()(Trace_Ptr x)                  { return static_cast<D*>(this)->fallback(x); }-    T operator()(Supports_Block_Ptr x)         { return static_cast<D*>(this)->fallback(x); }-    T operator()(Media_Block_Ptr x)            { return static_cast<D*>(this)->fallback(x); }-    T operator()(At_Root_Block_Ptr x)          { return static_cast<D*>(this)->fallback(x); }-    T operator()(Directive_Ptr x)              { return static_cast<D*>(this)->fallback(x); }-    T operator()(Keyframe_Rule_Ptr x)          { return static_cast<D*>(this)->fallback(x); }-    T operator()(Declaration_Ptr x)            { return static_cast<D*>(this)->fallback(x); }-    T operator()(Assignment_Ptr x)             { return static_cast<D*>(this)->fallback(x); }-    T operator()(Import_Ptr x)                 { return static_cast<D*>(this)->fallback(x); }-    T operator()(Import_Stub_Ptr x)            { return static_cast<D*>(this)->fallback(x); }-    T operator()(Warning_Ptr x)                { return static_cast<D*>(this)->fallback(x); }-    T operator()(Error_Ptr x)                  { return static_cast<D*>(this)->fallback(x); }-    T operator()(Debug_Ptr x)                  { return static_cast<D*>(this)->fallback(x); }-    T operator()(Comment_Ptr x)                { return static_cast<D*>(this)->fallback(x); }-    T operator()(If_Ptr x)                     { return static_cast<D*>(this)->fallback(x); }-    T operator()(For_Ptr x)                    { return static_cast<D*>(this)->fallback(x); }-    T operator()(Each_Ptr x)                   { return static_cast<D*>(this)->fallback(x); }-    T operator()(While_Ptr x)                  { return static_cast<D*>(this)->fallback(x); }-    T operator()(Return_Ptr x)                 { return static_cast<D*>(this)->fallback(x); }-    T operator()(Content_Ptr x)                { return static_cast<D*>(this)->fallback(x); }-    T operator()(Extension_Ptr x)              { return static_cast<D*>(this)->fallback(x); }-    T operator()(Definition_Ptr x)             { return static_cast<D*>(this)->fallback(x); }-    T operator()(Mixin_Call_Ptr x)             { return static_cast<D*>(this)->fallback(x); }+    T operator()(Block* x)                  { return static_cast<D*>(this)->fallback(x); }+    T operator()(Ruleset* x)                { return static_cast<D*>(this)->fallback(x); }+    T operator()(Bubble* x)                 { return static_cast<D*>(this)->fallback(x); }+    T operator()(Trace* x)                  { return static_cast<D*>(this)->fallback(x); }+    T operator()(Supports_Block* x)         { return static_cast<D*>(this)->fallback(x); }+    T operator()(MediaRule* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(CssMediaRule* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(CssMediaQuery* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(At_Root_Block* x)          { return static_cast<D*>(this)->fallback(x); }+    T operator()(Directive* x)              { return static_cast<D*>(this)->fallback(x); }+    T operator()(Keyframe_Rule* x)          { return static_cast<D*>(this)->fallback(x); }+    T operator()(Declaration* x)            { return static_cast<D*>(this)->fallback(x); }+    T operator()(Assignment* x)             { return static_cast<D*>(this)->fallback(x); }+    T operator()(Import* x)                 { return static_cast<D*>(this)->fallback(x); }+    T operator()(Import_Stub* x)            { return static_cast<D*>(this)->fallback(x); }+    T operator()(Warning* x)                { return static_cast<D*>(this)->fallback(x); }+    T operator()(Error* x)                  { return static_cast<D*>(this)->fallback(x); }+    T operator()(Debug* x)                  { return static_cast<D*>(this)->fallback(x); }+    T operator()(Comment* x)                { return static_cast<D*>(this)->fallback(x); }+    T operator()(If* x)                     { return static_cast<D*>(this)->fallback(x); }+    T operator()(For* x)                    { return static_cast<D*>(this)->fallback(x); }+    T operator()(Each* x)                   { return static_cast<D*>(this)->fallback(x); }+    T operator()(While* x)                  { return static_cast<D*>(this)->fallback(x); }+    T operator()(Return* x)                 { return static_cast<D*>(this)->fallback(x); }+    T operator()(Content* x)                { return static_cast<D*>(this)->fallback(x); }+    T operator()(ExtendRule* x)              { return static_cast<D*>(this)->fallback(x); }+    T operator()(Definition* x)             { return static_cast<D*>(this)->fallback(x); }+    T operator()(Mixin_Call* x)             { return static_cast<D*>(this)->fallback(x); }     // expressions-    T operator()(List_Ptr x)                   { return static_cast<D*>(this)->fallback(x); }-    T operator()(Map_Ptr x)                    { return static_cast<D*>(this)->fallback(x); }-    T operator()(Function_Ptr x)               { return static_cast<D*>(this)->fallback(x); }-    T operator()(Binary_Expression_Ptr x)      { return static_cast<D*>(this)->fallback(x); }-    T operator()(Unary_Expression_Ptr x)       { return static_cast<D*>(this)->fallback(x); }-    T operator()(Function_Call_Ptr x)          { return static_cast<D*>(this)->fallback(x); }-    T operator()(Function_Call_Schema_Ptr x)   { return static_cast<D*>(this)->fallback(x); }-    T operator()(Custom_Warning_Ptr x)         { return static_cast<D*>(this)->fallback(x); }-    T operator()(Custom_Error_Ptr x)           { return static_cast<D*>(this)->fallback(x); }-    T operator()(Variable_Ptr x)               { return static_cast<D*>(this)->fallback(x); }-    T operator()(Number_Ptr x)                 { return static_cast<D*>(this)->fallback(x); }-    T operator()(Color_Ptr x)                  { return static_cast<D*>(this)->fallback(x); }-    T operator()(Boolean_Ptr x)                { return static_cast<D*>(this)->fallback(x); }-    T operator()(String_Schema_Ptr x)          { return static_cast<D*>(this)->fallback(x); }-    T operator()(String_Constant_Ptr x)        { return static_cast<D*>(this)->fallback(x); }-    T operator()(String_Quoted_Ptr x)          { return static_cast<D*>(this)->fallback(x); }-    T operator()(Supports_Condition_Ptr x)     { return static_cast<D*>(this)->fallback(x); }-    T operator()(Supports_Operator_Ptr x)      { return static_cast<D*>(this)->fallback(x); }-    T operator()(Supports_Negation_Ptr x)      { return static_cast<D*>(this)->fallback(x); }-    T operator()(Supports_Declaration_Ptr x)   { return static_cast<D*>(this)->fallback(x); }-    T operator()(Supports_Interpolation_Ptr x) { return static_cast<D*>(this)->fallback(x); }-    T operator()(Media_Query_Ptr x)            { return static_cast<D*>(this)->fallback(x); }-    T operator()(Media_Query_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }-    T operator()(At_Root_Query_Ptr x)          { return static_cast<D*>(this)->fallback(x); }-    T operator()(Null_Ptr x)                   { return static_cast<D*>(this)->fallback(x); }-    T operator()(Parent_Selector_Ptr x)        { return static_cast<D*>(this)->fallback(x); }+    T operator()(Null* x)                   { return static_cast<D*>(this)->fallback(x); }+    T operator()(List* x)                   { return static_cast<D*>(this)->fallback(x); }+    T operator()(Map* x)                    { return static_cast<D*>(this)->fallback(x); }+    T operator()(Function* x)               { return static_cast<D*>(this)->fallback(x); }+    T operator()(Binary_Expression* x)      { return static_cast<D*>(this)->fallback(x); }+    T operator()(Unary_Expression* x)       { return static_cast<D*>(this)->fallback(x); }+    T operator()(Function_Call* x)          { return static_cast<D*>(this)->fallback(x); }+    T operator()(Custom_Warning* x)         { return static_cast<D*>(this)->fallback(x); }+    T operator()(Custom_Error* x)           { return static_cast<D*>(this)->fallback(x); }+    T operator()(Variable* x)               { return static_cast<D*>(this)->fallback(x); }+    T operator()(Number* x)                 { return static_cast<D*>(this)->fallback(x); }+    T operator()(Color* x)                  { return static_cast<D*>(this)->fallback(x); }+    T operator()(Color_RGBA* x)             { return static_cast<D*>(this)->fallback(x); }+    T operator()(Color_HSLA* x)             { return static_cast<D*>(this)->fallback(x); }+    T operator()(Boolean* x)                { return static_cast<D*>(this)->fallback(x); }+    T operator()(String_Schema* x)          { return static_cast<D*>(this)->fallback(x); }+    T operator()(String_Constant* x)        { return static_cast<D*>(this)->fallback(x); }+    T operator()(String_Quoted* x)          { return static_cast<D*>(this)->fallback(x); }+    T operator()(Supports_Condition* x)     { return static_cast<D*>(this)->fallback(x); }+    T operator()(Supports_Operator* x)      { return static_cast<D*>(this)->fallback(x); }+    T operator()(Supports_Negation* x)      { return static_cast<D*>(this)->fallback(x); }+    T operator()(Supports_Declaration* x)   { return static_cast<D*>(this)->fallback(x); }+    T operator()(Supports_Interpolation* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(Media_Query* x)            { return static_cast<D*>(this)->fallback(x); }+    T operator()(Media_Query_Expression* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(At_Root_Query* x)          { return static_cast<D*>(this)->fallback(x); }+    T operator()(Parent_Reference* x)        { return static_cast<D*>(this)->fallback(x); }     // parameters and arguments-    T operator()(Parameter_Ptr x)              { return static_cast<D*>(this)->fallback(x); }-    T operator()(Parameters_Ptr x)             { return static_cast<D*>(this)->fallback(x); }-    T operator()(Argument_Ptr x)               { return static_cast<D*>(this)->fallback(x); }-    T operator()(Arguments_Ptr x)              { return static_cast<D*>(this)->fallback(x); }+    T operator()(Parameter* x)              { return static_cast<D*>(this)->fallback(x); }+    T operator()(Parameters* x)             { return static_cast<D*>(this)->fallback(x); }+    T operator()(Argument* x)               { return static_cast<D*>(this)->fallback(x); }+    T operator()(Arguments* x)              { return static_cast<D*>(this)->fallback(x); }     // selectors-    T operator()(Selector_Schema_Ptr x)        { return static_cast<D*>(this)->fallback(x); }-    T operator()(Placeholder_Selector_Ptr x)   { return static_cast<D*>(this)->fallback(x); }-    T operator()(Element_Selector_Ptr x)       { return static_cast<D*>(this)->fallback(x); }-    T operator()(Class_Selector_Ptr x)         { return static_cast<D*>(this)->fallback(x); }-    T operator()(Id_Selector_Ptr x)            { return static_cast<D*>(this)->fallback(x); }-    T operator()(Attribute_Selector_Ptr x)     { return static_cast<D*>(this)->fallback(x); }-    T operator()(Pseudo_Selector_Ptr x)        { return static_cast<D*>(this)->fallback(x); }-    T operator()(Wrapped_Selector_Ptr x)       { return static_cast<D*>(this)->fallback(x); }-    T operator()(Compound_Selector_Ptr x){ return static_cast<D*>(this)->fallback(x); }-    T operator()(Complex_Selector_Ptr x)      { return static_cast<D*>(this)->fallback(x); }-    T operator()(Selector_List_Ptr x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(Selector_Schema* x)        { return static_cast<D*>(this)->fallback(x); }+    T operator()(Placeholder_Selector* x)   { return static_cast<D*>(this)->fallback(x); }+    T operator()(Type_Selector* x)       { return static_cast<D*>(this)->fallback(x); }+    T operator()(Class_Selector* x)         { return static_cast<D*>(this)->fallback(x); }+    T operator()(Id_Selector* x)            { return static_cast<D*>(this)->fallback(x); }+    T operator()(Attribute_Selector* x)     { return static_cast<D*>(this)->fallback(x); }+    T operator()(Pseudo_Selector* x)        { return static_cast<D*>(this)->fallback(x); }+    T operator()(SelectorComponent* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(SelectorCombinator* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(CompoundSelector* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(ComplexSelector* x) { return static_cast<D*>(this)->fallback(x); }+    T operator()(SelectorList* x) { return static_cast<D*>(this)->fallback(x); } -    template <typename U>-    T fallback(U x)                         { return T(); }+    // fallback with specific type U+    // will be called if not overloaded+    template <typename U> inline T fallback(U x)+    {+      throw std::runtime_error(+        std::string(typeid(*this).name()) + ": CRTP not implemented for " + typeid(x).name());+    }+   };  }
libsass/src/operators.cpp view
@@ -1,4 +1,8 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"++#include <cmath> #include "operators.hpp"  namespace Sass {@@ -57,26 +61,21 @@     /* colour math deprecation warning */     void op_color_deprecation(enum Sass_OP op, std::string lsh, std::string rhs, const ParserState& pstate)     {-      std::string op_str(-        op == Sass_OP::ADD ? "plus" :-          op == Sass_OP::DIV ? "div" :-            op == Sass_OP::SUB ? "minus" :-              op == Sass_OP::MUL ? "times" : ""-      );--      std::string msg("The operation `" + lsh + " " + op_str + " " + rhs + "` is deprecated and will be an error in future versions.");-      std::string tail("Consider using Sass's color functions instead.\nhttp://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions");--      deprecated(msg, tail, false, pstate);+      deprecated(+        "The operation `" + lsh + " " + sass_op_to_name(op) + " " + rhs ++        "` is deprecated and will be an error in future versions.",+        "Consider using Sass's color functions instead.\n"+        "https://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions",+        /*with_column=*/false, pstate);     }      /* static function, throws OperationError, has no traces but optional pstate for returned value */-    Value_Ptr op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+    Value* op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)     {       enum Sass_OP op = operand.operand; -      String_Quoted_Ptr lqstr = Cast<String_Quoted>(&lhs);-      String_Quoted_Ptr rqstr = Cast<String_Quoted>(&rhs);+      String_Quoted* lqstr = Cast<String_Quoted>(&lhs);+      String_Quoted* rqstr = Cast<String_Quoted>(&rhs);        std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));       std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));@@ -120,8 +119,9 @@       return SASS_MEMORY_NEW(String_Constant, pstate, lstr + sep + rstr);     } +    /* ToDo: allow to operate also with hsla colors */     /* static function, throws OperationError, has no traces but optional pstate for returned value */-    Value_Ptr op_colors(enum Sass_OP op, const Color& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+    Value* op_colors(enum Sass_OP op, const Color_RGBA& lhs, const Color_RGBA& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)     {        if (lhs.a() != rhs.a()) {@@ -133,7 +133,7 @@        op_color_deprecation(op, lhs.to_string(), rhs.to_string(), pstate); -      return SASS_MEMORY_NEW(Color,+      return SASS_MEMORY_NEW(Color_RGBA,                              pstate,                              ops[op](lhs.r(), rhs.r()),                              ops[op](lhs.g(), rhs.g()),@@ -142,7 +142,7 @@     }      /* static function, throws OperationError, has no traces but optional pstate for returned value */-    Value_Ptr op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+    Value* op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)     {       double lval = lhs.value();       double rval = rhs.value();@@ -165,7 +165,7 @@         if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) {           if (lhs.numerators == rhs.numerators) {             if (lhs.denominators == rhs.denominators) {-              Number_Ptr v = SASS_MEMORY_COPY(&lhs);+              Number* v = SASS_MEMORY_COPY(&lhs);               v->value(ops[op](lval, rval));               return v;             }@@ -212,7 +212,7 @@     }      /* static function, throws OperationError, has no traces but optional pstate for returned value */-    Value_Ptr op_number_color(enum Sass_OP op, const Number& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+    Value* op_number_color(enum Sass_OP op, const Number& lhs, const Color_RGBA& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)     {       double lval = lhs.value(); @@ -220,7 +220,7 @@         case Sass_OP::ADD:         case Sass_OP::MUL: {           op_color_deprecation(op, lhs.to_string(), rhs.to_string(opt), pstate);-          return SASS_MEMORY_NEW(Color,+          return SASS_MEMORY_NEW(Color_RGBA,                                 pstate,                                 ops[op](lval, rhs.r()),                                 ops[op](lval, rhs.g()),@@ -243,7 +243,7 @@     }      /* static function, throws OperationError, has no traces but optional pstate for returned value */-    Value_Ptr op_color_number(enum Sass_OP op, const Color& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+    Value* op_color_number(enum Sass_OP op, const Color_RGBA& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)     {       double rval = rhs.value(); @@ -254,7 +254,7 @@        op_color_deprecation(op, lhs.to_string(), rhs.to_string(), pstate); -      return SASS_MEMORY_NEW(Color,+      return SASS_MEMORY_NEW(Color_RGBA,                             pstate,                             ops[op](lhs.r(), rval),                             ops[op](lhs.g(), rval),
libsass/src/operators.hpp view
@@ -17,11 +17,11 @@     bool lte(Expression_Obj, Expression_Obj);     bool gte(Expression_Obj, Expression_Obj);     // arithmetic for all the combinations that matter-    Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);-    Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);-    Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);-    Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);-    Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+    Value* op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+    Value* op_colors(enum Sass_OP, const Color_RGBA&, const Color_RGBA&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+    Value* op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+    Value* op_number_color(enum Sass_OP, const Number&, const Color_RGBA&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+    Value* op_color_number(enum Sass_OP, const Color_RGBA&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);    }; 
+ libsass/src/ordered_map.hpp view
@@ -0,0 +1,112 @@+#ifndef SASS_ORDERED_MAP_H+#define SASS_ORDERED_MAP_H++namespace Sass {++  // ##########################################################################+  // Very simple and limited container for insert ordered hash map.+  // Piggy-back implementation on std::unordered_map and std::vector+  // ##########################################################################+  template<+    class Key,+    class T,+    class Hash = std::hash<Key>,+    class KeyEqual = std::equal_to<Key>,+    class Allocator = std::allocator<std::pair<const Key, T>>+  >+  class ordered_map {++  private:++    using map_type = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>;+    using map_iterator = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>::iterator;+    using map_const_iterator = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>::const_iterator;++    // The main unordered map+    map_type _map;++    // Keep insertion order+    std::vector<Key> _keys;+    std::vector<T> _values;++    const KeyEqual _keyEqual;++  public:++    ordered_map() :+      _keyEqual(KeyEqual())+    {+    }++    ordered_map& operator= (const ordered_map& other) {+      _map = other._map;+      _keys = other._keys;+      _values = other._values;+      return *this;+    }++    std::pair<Key, T> front() {+      return std::make_pair(+        _keys.front(),+        _values.front()+      );+    }++    bool empty() const {+      return _keys.empty();+    }++    void insert(const Key& key, const T& val) {+      if (!hasKey(key)) {+        _values.push_back(val);+        _keys.push_back(key);+      }+      _map[key] = val;+    }++    bool hasKey(const Key& key) const {+      return _map.find(key) != _map.end();+    }++    bool erase(const Key& key) {+      _map.erase(key);+      // find the position in the array+      for (size_t i = 0; i < _keys.size(); i += 1) {+        if (_keyEqual(key, _keys[i])) {+          _keys.erase(_keys.begin() + i);+          _values.erase(_values.begin() + i);+          return true;+        }+      }+      return false;+    }++    const std::vector<Key>& keys() const { return _keys; }+    const std::vector<T>& values() const { return _values; }++    const T& get(const Key& key) {+      if (hasKey(key)) {+        return _map[key];+      }+      throw std::runtime_error("Key does not exist");+    }++    using iterator = typename std::vector<Key>::iterator;+    using const_iterator = typename std::vector<Key>::const_iterator;+    using reverse_iterator = typename std::vector<Key>::reverse_iterator;+    using const_reverse_iterator = typename std::vector<Key>::const_reverse_iterator;++    typename std::vector<Key>::iterator end() { return _keys.end(); }+    typename std::vector<Key>::iterator begin() { return _keys.begin(); }+    typename std::vector<Key>::reverse_iterator rend() { return _keys.rend(); }+    typename std::vector<Key>::reverse_iterator rbegin() { return _keys.rbegin(); }+    typename std::vector<Key>::const_iterator end() const { return _keys.end(); }+    typename std::vector<Key>::const_iterator begin() const { return _keys.begin(); }+    typename std::vector<Key>::const_reverse_iterator rend() const { return _keys.rend(); }+    typename std::vector<Key>::const_reverse_iterator rbegin() const { return _keys.rbegin(); }++  };++}++#endif
libsass/src/output.cpp view
@@ -1,6 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "output.hpp"+#include "util.hpp"  namespace Sass { @@ -12,12 +16,12 @@    Output::~Output() { } -  void Output::fallback_impl(AST_Node_Ptr n)+  void Output::fallback_impl(AST_Node* n)   {     return n->perform(this);   } -  void Output::operator()(Number_Ptr n)+  void Output::operator()(Number* n)   {     // check for a valid unit here     // includes result for reporting@@ -31,12 +35,12 @@     append_token(res, n);   } -  void Output::operator()(Import_Ptr imp)+  void Output::operator()(Import* imp)   {     top_nodes.push_back(imp);   } -  void Output::operator()(Map_Ptr m)+  void Output::operator()(Map* m)   {     // should be handle in check_expression     throw Exception::InvalidValue({}, *m);@@ -86,9 +90,8 @@    } -  void Output::operator()(Comment_Ptr c)+  void Output::operator()(Comment* c)   {-    std::string txt = c->text()->to_string(opt);     // if (indentation && txt == "/**/") return;     bool important = c->is_important();     if (output_style() != COMPRESSED || important) {@@ -108,15 +111,17 @@     }   } -  void Output::operator()(Ruleset_Ptr r)+  void Output::operator()(Ruleset* r)   {-    Selector_Obj s     = r->selector();-    Block_Obj    b     = r->block();+    Block_Obj b = r->block();+    SelectorListObj s = r->selector(); +    if (!s || s->empty()) return;+     // Filter out rulesets that aren't printable (process its children though)     if (!Util::isPrintable(r, output_style())) {       for (size_t i = 0, L = b->length(); i < L; ++i) {-        const Statement_Obj& stm = b->at(i);+        const Statement_Obj& stm = b->get(i);         if (Cast<Has_Block>(stm)) {           if (!Cast<Declaration>(stm)) {             stm->perform(this);@@ -126,7 +131,9 @@       return;     } -    if (output_style() == NESTED) indentation += r->tabs();+    if (output_style() == NESTED) {+      indentation += r->tabs();+    }     if (opt.source_comments) {       std::stringstream ss;       append_indentation();@@ -139,22 +146,22 @@     if (s) s->perform(this);     append_scope_opener(b);     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Obj stm = b->at(i);+      Statement_Obj stm = b->get(i);       bool bPrintExpression = true;       // Check print conditions-      if (Declaration_Ptr dec = Cast<Declaration>(stm)) {-        if (String_Constant_Ptr valConst = Cast<String_Constant>(dec->value())) {-          std::string val(valConst->value());-          if (String_Quoted_Ptr qstr = Cast<String_Quoted>(valConst)) {+      if (Declaration* dec = Cast<Declaration>(stm)) {+        if (const String_Constant* valConst = Cast<String_Constant>(dec->value())) {+          const std::string& val = valConst->value();+          if (const String_Quoted* qstr = Cast<const String_Quoted>(valConst)) {             if (!qstr->quote_mark() && val.empty()) {               bPrintExpression = false;             }           }         }-        else if (List_Ptr list = Cast<List>(dec->value())) {+        else if (List* list = Cast<List>(dec->value())) {           bool all_invisible = true;           for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {-            Expression_Ptr item = list->at(list_i);+            Expression* item = list->get(list_i);             if (!item->is_invisible()) all_invisible = false;           }           if (all_invisible && !list->is_bracketed()) bPrintExpression = false;@@ -169,7 +176,7 @@     append_scope_closer(b);    }-  void Output::operator()(Keyframe_Rule_Ptr r)+  void Output::operator()(Keyframe_Rule* r)   {     Block_Obj b = r->block();     Selector_Obj v = r->name();@@ -185,14 +192,14 @@      append_scope_opener();     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Obj stm = b->at(i);+      Statement_Obj stm = b->get(i);       stm->perform(this);       if (i < L - 1) append_special_linefeed();     }     append_scope_closer();   } -  void Output::operator()(Supports_Block_Ptr f)+  void Output::operator()(Supports_Block* f)   {     if (f->is_invisible()) return; @@ -202,7 +209,7 @@     // Filter out feature blocks that aren't printable (process its children though)     if (!Util::isPrintable(f, output_style())) {       for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement_Obj stm = b->at(i);+        Statement_Obj stm = b->get(i);         if (Cast<Has_Block>(stm)) {           stm->perform(this);         }@@ -218,7 +225,7 @@     append_scope_opener();      for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Obj stm = b->at(i);+      Statement_Obj stm = b->get(i);       stm->perform(this);       if (i < L - 1) append_special_linefeed();     }@@ -229,44 +236,24 @@    } -  void Output::operator()(Media_Block_Ptr m)+  void Output::operator()(CssMediaRule* rule)   {-    if (m->is_invisible()) return;--    Block_Obj b     = m->block();--    // Filter out media blocks that aren't printable (process its children though)-    if (!Util::isPrintable(m, output_style())) {-      for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement_Obj stm = b->at(i);-        if (Cast<Has_Block>(stm)) {-          stm->perform(this);-        }-      }-      return;-    }-    if (output_style() == NESTED) indentation += m->tabs();-    append_indentation();-    append_token("@media", m);-    append_mandatory_space();-    in_media_block = true;-    m->media_queries()->perform(this);-    in_media_block = false;-    append_scope_opener();--    for (size_t i = 0, L = b->length(); i < L; ++i) {-      if (b->at(i)) {-      Statement_Obj stm = b->at(i);-        stm->perform(this);-      }-      if (i < L - 1) append_special_linefeed();+    // Avoid null pointer exception+    if (rule == nullptr) return;+    // Skip empty/invisible rule+    if (rule->isInvisible()) return;+    // Avoid null pointer exception+    if (rule->block() == nullptr) return;+    // Skip empty/invisible rule+    if (rule->block()->isInvisible()) return;+    // Skip if block is empty/invisible+    if (Util::isPrintable(rule, output_style())) {+      // Let inspect do its magic+      Inspect::operator()(rule);     }--    if (output_style() == NESTED) indentation -= m->tabs();-    append_scope_closer();   } -  void Output::operator()(Directive_Ptr a)+  void Output::operator()(Directive* a)   {     std::string      kwd   = a->keyword();     Selector_Obj   s     = a->selector();@@ -301,7 +288,7 @@     bool format = kwd != "@font-face";;      for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement_Obj stm = b->at(i);+      Statement_Obj stm = b->get(i);       stm->perform(this);       if (i < L - 1 && format) append_special_linefeed();     }@@ -309,7 +296,7 @@     append_scope_closer();   } -  void Output::operator()(String_Quoted_Ptr s)+  void Output::operator()(String_Quoted* s)   {     if (s->quote_mark()) {       append_token(quote(s->value(), s->quote_mark()), s);@@ -320,12 +307,9 @@     }   } -  void Output::operator()(String_Constant_Ptr s)+  void Output::operator()(String_Constant* s)   {     std::string value(s->value());-    if (s->can_compress_whitespace() && output_style() == COMPRESSED) {-      value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());-    }     if (!in_comment && !in_custom_property) {       append_token(string_to_output(value), s);     } else {
libsass/src/output.hpp view
@@ -11,13 +11,6 @@ namespace Sass {   class Context; -  // Refactor to make it generic to find linefeed (look behind)-  inline bool ends_with(std::string const & value, std::string const & ending)-  {-    if (ending.size() > value.size()) return false;-    return std::equal(ending.rbegin(), ending.rend(), value.rbegin());-  }-   class Output : public Inspect {   protected:     using Inspect::operator();@@ -28,24 +21,24 @@    protected:     std::string charset;-    std::vector<AST_Node_Ptr> top_nodes;+    std::vector<AST_Node*> top_nodes;    public:     OutputBuffer get_buffer(void); -    virtual void operator()(Map_Ptr);-    virtual void operator()(Ruleset_Ptr);-    virtual void operator()(Supports_Block_Ptr);-    virtual void operator()(Media_Block_Ptr);-    virtual void operator()(Directive_Ptr);-    virtual void operator()(Keyframe_Rule_Ptr);-    virtual void operator()(Import_Ptr);-    virtual void operator()(Comment_Ptr);-    virtual void operator()(Number_Ptr);-    virtual void operator()(String_Quoted_Ptr);-    virtual void operator()(String_Constant_Ptr);+    virtual void operator()(Map*);+    virtual void operator()(Ruleset*);+    virtual void operator()(Supports_Block*);+    virtual void operator()(CssMediaRule*);+    virtual void operator()(Directive*);+    virtual void operator()(Keyframe_Rule*);+    virtual void operator()(Import*);+    virtual void operator()(Comment*);+    virtual void operator()(Number*);+    virtual void operator()(String_Quoted*);+    virtual void operator()(String_Constant*); -    void fallback_impl(AST_Node_Ptr n);+    void fallback_impl(AST_Node* n);    }; 
libsass/src/parser.cpp view
@@ -1,13 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "parser.hpp"-#include "file.hpp"-#include "inspect.hpp"-#include "constants.hpp"-#include "util.hpp"-#include "prelexer.hpp" #include "color_maps.hpp"-#include "sass/functions.h"-#include "error_handling.hpp"+#include "util_string.hpp"  // Notes about delayed: some ast nodes can have delayed evaluation so // they can preserve their original semantics if needed. This is most@@ -21,20 +18,16 @@ // Another case with delayed values are colors. In compressed mode // only processed values get compressed (other are left as written). -#include <cstdlib>-#include <iostream>-#include <vector>-#include <typeinfo>  namespace Sass {   using namespace Constants;   using namespace Prelexer; -  Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+  Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent)   {     pstate.offset.column = 0;     pstate.offset.line = 0;-    Parser p(ctx, pstate, traces);+    Parser p(ctx, pstate, traces, allow_parent);     p.source   = source ? source : beg;     p.position = beg ? beg : p.source;     p.end      = p.position + strlen(p.position);@@ -44,11 +37,11 @@     return p;   } -  Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+  Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent)   {     pstate.offset.column = 0;     pstate.offset.line = 0;-    Parser p(ctx, pstate, traces);+    Parser p(ctx, pstate, traces, allow_parent);     p.source   = source ? source : beg;     p.position = beg ? beg : p.source;     p.end      = end ? end : p.position + strlen(p.position);@@ -66,12 +59,11 @@       pstate.offset.line = 0;     } -  Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+  SelectorListObj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent)   {-    Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source);-    // ToDo: ruby sass errors on parent references+    Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source, allow_parent);     // ToDo: remap the source-map entries somehow-    return p.parse_selector_list(false);+    return p.parseSelectorList(false);   }    bool Parser::peek_newline(const char* start)@@ -258,16 +250,23 @@     else if (lex < kwd_extend >(true)) {       Lookahead lookahead = lookahead_for_include(position);       if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");-      Selector_List_Obj target;+      SelectorListObj target;       if (!lookahead.has_interpolants) {-        target = parse_selector_list(true);+        LOCAL_FLAG(allow_parent, false);+        auto selector = parseSelectorList(true);+        auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector);+        extender->isOptional(selector && selector->is_optional());+        block->append(extender);       }       else {-        target = SASS_MEMORY_NEW(Selector_List, pstate);-        target->schema(parse_selector_schema(lookahead.found, true));+        LOCAL_FLAG(allow_parent, false);+        auto selector = parse_selector_schema(lookahead.found, true);+        auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector);+        // A schema is not optional yet, check once it is evaluated+        // extender->isOptional(selector && selector->is_optional());+        block->append(extender);       } -      block->append(SASS_MEMORY_NEW(Extension, pstate, target));     }      // selector may contain interpolations which need delayed evaluation@@ -280,7 +279,7 @@     }      // parse multiple specific keyword directives-    else if (lex < kwd_media >(true)) { block->append(parse_media_block()); }+    else if (lex < kwd_media >(true)) { block->append(parseMediaRule()); }     else if (lex < kwd_at_root >(true)) { block->append(parse_at_root_block()); }     else if (lex < kwd_include_directive >(true)) { block->append(parse_include_directive()); }     else if (lex < kwd_content_directive >(true)) { block->append(parse_content_directive()); }@@ -291,9 +290,9 @@     // ignore the @charset directive for now     else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } +    else if (lex < exactly < else_kwd >>(true)) { error("Invalid CSS: @else must come after @if"); }+     // generic at keyword (keep last)-    else if (lex< re_special_directive >(true)) { block->append(parse_special_directive()); }-    else if (lex< re_prefixed_directive >(true)) { block->append(parse_prefixed_directive()); }     else if (lex< at_keyword >(true)) { block->append(parse_directive()); }      else if (is_root && stack.back() != Scope::AtRoot /* && block->is_root() */) {@@ -333,11 +332,11 @@     do {       while (lex< block_comment >());       if (lex< quoted_string >()) {-        to_import.push_back(std::pair<std::string,Function_Call_Obj>(std::string(lexed), 0));+        to_import.push_back(std::pair<std::string,Function_Call_Obj>(std::string(lexed), {}));       }       else if (lex< uri_prefix >()) {         Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);-        Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, "url", args);+        Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, std::string("url"), args);          if (lex< quoted_string >()) {           Expression_Obj quoted_url = parse_string();@@ -479,7 +478,7 @@       bool is_arglist = false;       bool is_keyword = false;       Expression_Obj val = parse_space_list();-      List_Ptr l = Cast<List>(val);+      List* l = Cast<List>(val);       if (lex_css< exactly< ellipsis > >()) {         if (val->concrete_type() == Expression::MAP || (            (l != NULL && l->separator() == SASS_HASH)@@ -527,10 +526,13 @@     // create the connector object (add parts later)     Ruleset_Obj ruleset = SASS_MEMORY_NEW(Ruleset, pstate);     // parse selector static or as schema to be evaluated later-    if (lookahead.parsable) ruleset->selector(parse_selector_list(false));+    if (lookahead.parsable) {+      ruleset->selector(parseSelectorList(false));+    }     else {-      Selector_List_Obj list = SASS_MEMORY_NEW(Selector_List, pstate);-      list->schema(parse_selector_schema(lookahead.position, false));+      SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate);+      auto sc = parse_selector_schema(lookahead.position, false);+      ruleset->schema(sc);       ruleset->selector(list);     }     // then parse the inner block@@ -556,15 +558,14 @@     lex< optional_spaces >();     const char* i = position;     // selector schema re-uses string schema implementation-    String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+    String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate);     // the selector schema is pretty much just a wrapper for the string schema     Selector_Schema_Obj selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema);     selector_schema->connect_parent(chroot == false);-    selector_schema->media_block(last_media_block);      // process until end     while (i < end_of_selector) {-      // try to parse mutliple interpolants+      // try to parse multiple interpolants       if (const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, end_of_selector)) {         // accumulate the preceding segment if the position has advanced         if (i < p) {@@ -645,224 +646,34 @@     // normalize underscores to hyphens     std::string name(Util::normalize_underscores(lexed));     // create the initial mixin call object-    Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, 0, 0);+    Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, {}, {}, {});     // parse mandatory arguments     call->arguments(parse_arguments());+    // parse using and optional block parameters+    bool has_parameters = lex< kwd_using >() != nullptr;++    if (has_parameters) {+      if (!peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \"(\", was ");+    } else {+      if (peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \";\", was ");+    }++    if (has_parameters) call->block_parameters(parse_parameters());+     // parse optional block     if (peek < exactly <'{'> >()) {       call->block(parse_block());     }+    else if (has_parameters)  {+      css_error("Invalid CSS", " after ", ": expected \"{\", was ");+    }     // return ast node     return call.detach();   }   // EO parse_include_directive -  // parse a list of complex selectors-  // this is the main entry point for most-  Selector_List_Obj Parser::parse_selector_list(bool chroot)-  {-    bool reloop;-    bool had_linefeed = false;-    NESTING_GUARD(nestings);-    Complex_Selector_Obj sel;-    Selector_List_Obj group = SASS_MEMORY_NEW(Selector_List, pstate);-    group->media_block(last_media_block); -    if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) {-      css_error("Invalid CSS", " after ", ": expected selector, was ");-    }--    do {-      reloop = false;--      had_linefeed = had_linefeed || peek_newline();--      if (peek_css< alternatives < class_char < selector_list_delims > > >())-        break; // in case there are superfluous commas at the end--      // now parse the complex selector-      sel = parse_complex_selector(chroot);--      if (!sel) return group.detach();--      sel->has_line_feed(had_linefeed);--      had_linefeed = false;--      while (peek_css< exactly<','> >())-      {-        lex< css_comments >(false);-        // consume everything up and including the comma separator-        reloop = lex< exactly<','> >() != 0;-        // remember line break (also between some commas)-        had_linefeed = had_linefeed || peek_newline();-        // remember line break (also between some commas)-      }-      group->append(sel);-    }-    while (reloop);-    while (lex_css< kwd_optional >()) {-      group->is_optional(true);-    }-    // update for end position-    group->update_pstate(pstate);-    if (sel) sel->last()->has_line_break(false);-    return group.detach();-  }-  // EO parse_selector_list--  // a complex selector combines a compound selector with another-  // complex selector, with one of four combinator operations.-  // the compound selector (head) is optional, since the combinator-  // can come first in the whole selector sequence (like `> DIV').-  Complex_Selector_Obj Parser::parse_complex_selector(bool chroot)-  {--    NESTING_GUARD(nestings);-    String_Obj reference = 0;-    lex < block_comment >();-    advanceToNextToken();-    Complex_Selector_Obj sel = SASS_MEMORY_NEW(Complex_Selector, pstate);--    if (peek < end_of_file >()) return 0;--    // parse the left hand side-    Compound_Selector_Obj lhs;-    // special case if it starts with combinator ([+~>])-    if (!peek_css< class_char < selector_combinator_ops > >()) {-      // parse the left hand side-      lhs = parse_compound_selector();-    }---    // parse combinator between lhs and rhs-    Complex_Selector::Combinator combinator = Complex_Selector::ANCESTOR_OF;-    if      (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO;-    else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES;-    else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF;-    else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) {-      // comments are allowed, but not spaces?-      combinator = Complex_Selector::REFERENCE;-      if (!lex < re_reference_combinator >()) return 0;-      reference = SASS_MEMORY_NEW(String_Constant, pstate, lexed);-      if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?-    }--    if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;--    // lex < block_comment >();-    sel->head(lhs);-    sel->combinator(combinator);-    sel->media_block(last_media_block);--    if (combinator == Complex_Selector::REFERENCE) sel->reference(reference);-    // has linfeed after combinator?-    sel->has_line_break(peek_newline());-    // sel->has_line_feed(has_line_feed);--    // check if we got the abort condition (ToDo: optimize)-    if (!peek_css< class_char < complex_selector_delims > >()) {-      // parse next selector in sequence-      sel->tail(parse_complex_selector(true));-    }--    // add a parent selector if we are not in a root-    // also skip adding parent ref if we only have refs-    if (!sel->has_parent_ref() && !chroot) {-      // create the objects to wrap parent selector reference-      Compound_Selector_Obj head = SASS_MEMORY_NEW(Compound_Selector, pstate);-      Parent_Selector_Ptr parent = SASS_MEMORY_NEW(Parent_Selector, pstate, false);-      parent->media_block(last_media_block);-      head->media_block(last_media_block);-      // add simple selector-      head->append(parent);-      // selector may not have any head yet-      if (!sel->head()) { sel->head(head); }-      // otherwise we need to create a new complex selector and set the old one as its tail-      else {-        sel = SASS_MEMORY_NEW(Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel);-        sel->media_block(last_media_block);-      }-      // peek for linefeed and remember result on head-      // if (peek_newline()) head->has_line_break(true);-    }--    sel->update_pstate(pstate);-    // complex selector-    return sel;-  }-  // EO parse_complex_selector--  // parse one compound selector, which is basically-  // a list of simple selectors (directly adjacent)-  // lex them exactly (without skipping white-space)-  Compound_Selector_Obj Parser::parse_compound_selector()-  {-    // init an empty compound selector wrapper-    Compound_Selector_Obj seq = SASS_MEMORY_NEW(Compound_Selector, pstate);-    seq->media_block(last_media_block);--    // skip initial white-space-    lex< css_whitespace >();--    // parse list-    while (true)-    {-      // remove all block comments (don't skip white-space)-      lex< delimited_by< slash_star, star_slash, false > >(false);-      // parse functional-      if (match < re_pseudo_selector >())-      {-        seq->append(parse_simple_selector());-      }-      // parse parent selector-      else if (lex< exactly<'&'> >(false))-      {-        // this produces a linefeed!?-        seq->has_parent_reference(true);-        seq->append(SASS_MEMORY_NEW(Parent_Selector, pstate));-        // parent selector only allowed at start-        // upcoming Sass may allow also trailing-        if (seq->length() > 1) {-          ParserState state(pstate);-          Simple_Selector_Obj cur = (*seq)[seq->length()-1];-          Simple_Selector_Obj prev = (*seq)[seq->length()-2];-          std::string sel(prev->to_string({ NESTED, 5 }));-          std::string found(cur->to_string({ NESTED, 5 }));-          if (lex < identifier >()) { found += std::string(lexed); }-          error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n"-            "\"" + found + "\" may only be used at the beginning of a compound selector.", state);-        }-      }-      // parse type selector-      else if (lex< re_type_selector >(false))-      {-        seq->append(SASS_MEMORY_NEW(Element_Selector, pstate, lexed));-      }-      // peek for abort conditions-      else if (peek< spaces >()) break;-      else if (peek< end_of_file >()) { break; }-      else if (peek_css < class_char < selector_combinator_ops > >()) break;-      else if (peek_css < class_char < complex_selector_delims > >()) break;-      // otherwise parse another simple selector-      else {-        Simple_Selector_Obj sel = parse_simple_selector();-        if (!sel) return 0;-        seq->append(sel);-      }-    }--    if (seq && !peek_css<alternatives<end_of_file,exactly<'{'>>>()) {-      seq->has_line_break(peek_newline());-    }--    // EO while true-    return seq;--  }-  // EO parse_compound_selector--  Simple_Selector_Obj Parser::parse_simple_selector()+  SimpleSelectorObj Parser::parse_simple_selector()   {     lex < css_comments >(false);     if (lex< class_name >()) {@@ -872,10 +683,10 @@       return SASS_MEMORY_NEW(Id_Selector, pstate, lexed);     }     else if (lex< alternatives < variable, number, static_reference_combinator > >()) {-      return SASS_MEMORY_NEW(Element_Selector, pstate, lexed);+      return SASS_MEMORY_NEW(Type_Selector, pstate, lexed);     }     else if (peek< pseudo_not >()) {-      return parse_negated_selector();+      return parse_negated_selector2();     }     else if (peek< re_pseudo_selector >()) {       return parse_pseudo_selector();@@ -887,86 +698,117 @@       return parse_attribute_selector();     }     else if (lex< placeholder >()) {-      Placeholder_Selector_Ptr sel = SASS_MEMORY_NEW(Placeholder_Selector, pstate, lexed);-      sel->media_block(last_media_block);-      return sel;+      return SASS_MEMORY_NEW(Placeholder_Selector, pstate, lexed);     }     else {       css_error("Invalid CSS", " after ", ": expected selector, was ");     }     // failed-    return 0;+    return {};   } -  Wrapped_Selector_Obj Parser::parse_negated_selector()+  Pseudo_Selector_Obj Parser::parse_negated_selector2()   {     lex< pseudo_not >();     std::string name(lexed);     ParserState nsource_position = pstate;-    Selector_List_Obj negated = parse_selector_list(true);+    SelectorListObj negated = parseSelectorList(true);     if (!lex< exactly<')'> >()) {       error("negated selector is missing ')'");     }     name.erase(name.size() - 1);-    return SASS_MEMORY_NEW(Wrapped_Selector, nsource_position, name, negated);++    Pseudo_Selector* sel = SASS_MEMORY_NEW(Pseudo_Selector, nsource_position, name.substr(1));+    sel->selector(negated);+    return sel;   } +  // Helper to clean binominal string+  bool BothAreSpaces(char lhs, char rhs) { return isspace(lhs) && isspace(rhs); }+   // a pseudo selector often starts with one or two colons   // it can contain more selectors inside parentheses-  Simple_Selector_Obj Parser::parse_pseudo_selector() {-    if (lex< sequence<-          optional < pseudo_prefix >,-          // we keep the space within the name, strange enough-          // ToDo: refactor output to schedule the space for it-          // or do we really want to keep the real white-space?-          sequence< identifier, optional < block_comment >, exactly<'('> >-        > >())-    {+  SimpleSelectorObj Parser::parse_pseudo_selector() { -      std::string name(lexed);-      name.erase(name.size() - 1);-      ParserState p = pstate;+    // Lex one or two colon characters+    if (lex<pseudo_prefix>()) {+      std::string colons(lexed);+      // Check if it is a pseudo element+      bool element = colons.size() == 2; -      // specially parse static stuff-      // ToDo: really everything static?-      if (peek_css <-            sequence <-              alternatives <-                static_value,-                binomial-              >,-              optional_css_whitespace,-              exactly<')'>-            >-          >()-      ) {-        lex_css< alternatives < static_value, binomial > >();-        String_Constant_Obj expr = SASS_MEMORY_NEW(String_Constant, pstate, lexed);-        if (lex_css< exactly<')'> >()) {-          expr->can_compress_whitespace(true);-          return SASS_MEMORY_NEW(Pseudo_Selector, p, name, expr);+      if (lex< sequence<+        // we keep the space within the name, strange enough+        // ToDo: refactor output to schedule the space for it+        // or do we really want to keep the real white-space?+        sequence< identifier, optional < block_comment >, exactly<'('> >+      > >())+      {++        std::string name(lexed);+        name.erase(name.size() - 1);+        ParserState p = pstate;++        // specially parse nth-child pseudo selectors+        if (lex_css < sequence < binomial, word_boundary >>()) {+          std::string parsed(lexed); // always compacting binominals (as dart-sass)+          parsed.erase(std::unique(parsed.begin(), parsed.end(), BothAreSpaces), parsed.end());+          String_Constant_Obj arg = SASS_MEMORY_NEW(String_Constant, pstate, parsed);+          Pseudo_Selector* pseudo = SASS_MEMORY_NEW(Pseudo_Selector, p, name, element);+          if (lex < sequence < css_whitespace, insensitive < of_kwd >>>(false)) {+            pseudo->selector(parseSelectorList(true));+          }+          pseudo->argument(arg);+          if (lex_css< exactly<')'> >()) {+            return pseudo;+          }         }-      }-      else if (Selector_List_Obj wrapped = parse_selector_list(true)) {-        if (wrapped && lex_css< exactly<')'> >()) {-          return SASS_MEMORY_NEW(Wrapped_Selector, p, name, wrapped);+        else {+          if (peek_css< exactly<')'>>()  && Util::equalsLiteral("nth-", name.substr(0, 4))) {+            css_error("Invalid CSS", " after ", ": expected An+B expression, was ");+          }++          std::string unvendored = Util::unvendor(name);++          if (unvendored == "not" || unvendored == "matches" || unvendored == "current"  || unvendored == "any" || unvendored == "has" || unvendored == "host" || unvendored == "host-context" || unvendored == "slotted") {+             if (SelectorListObj wrapped = parseSelectorList(true)) {+                if (wrapped && lex_css< exactly<')'> >()) {+                  Pseudo_Selector* pseudo = SASS_MEMORY_NEW(Pseudo_Selector, p, name, element);+                  pseudo->selector(wrapped);+                  return pseudo;+                }+              }+          } else {+            String_Schema_Obj arg = parse_css_variable_value();+            Pseudo_Selector* pseudo = SASS_MEMORY_NEW(Pseudo_Selector, p, name, element);+            pseudo->argument(arg);++            if (lex_css< exactly<')'> >()) {+              return pseudo;+            }+          }         }+       }+      // EO if pseudo selector -    }-    // EO if pseudo selector+      else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {+        return SASS_MEMORY_NEW(Pseudo_Selector, pstate, lexed, element);+      }+      else if (lex < pseudo_prefix >()) {+        css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");+      } -    else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {-      return SASS_MEMORY_NEW(Pseudo_Selector, pstate, lexed);     }-    else if(lex < pseudo_prefix >()) {-      css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");+    else {+      lex < identifier >(); // needed for error message?+      css_error("Invalid CSS", " after ", ": expected selector, was ");     } +     css_error("Invalid CSS", " after ", ": expected \")\", was ");      // unreachable statement-    return 0;+    return {};   }    const char* Parser::re_attr_sensitive_close(const char* src)@@ -985,11 +827,11 @@     if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector");     std::string name(lexed);     if (lex_css< re_attr_sensitive_close >()) {-      return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, 0);+      return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", {}, {});     }     else if (lex_css< re_attr_insensitive_close >()) {       char modifier = lexed.begin[0];-      return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, modifier);+      return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", {}, modifier);     }     if (!lex_css< alternatives< exact_match, class_match, dash_match,                                 prefix_match, suffix_match, substring_match > >()) {@@ -997,7 +839,7 @@     }     std::string matcher(lexed); -    String_Obj value = 0;+    String_Obj value;     if (lex_css< identifier >()) {       value = SASS_MEMORY_NEW(String_Constant, p, lexed);     }@@ -1016,11 +858,11 @@       return SASS_MEMORY_NEW(Attribute_Selector, p, name, matcher, value, modifier);     }     error("unterminated attribute selector for " + name);-    return NULL; // to satisfy compilers (error must not return)+    return {}; // to satisfy compilers (error must not return)   }    /* parse block comment and add to block */-  void Parser::parse_block_comments()+  void Parser::parse_block_comments(bool store)   {     Block_Obj block = block_stack.back(); @@ -1028,7 +870,7 @@       bool is_important = lexed.begin[2] == '!';       // flag on second param is to skip loosely over comments       String_Obj contents = parse_interpolated_chunk(lexed, true, false);-      block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));+      if (store) block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));     }   } @@ -1072,7 +914,7 @@       }       else {         value = parse_list(DELAYED);-        if (List_Ptr list = Cast<List>(value)) {+        if (List* list = Cast<List>(value)) {           if (!list->is_bracketed() && list->length() == 0 && !peek< exactly <'{'> >()) {             css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");           }@@ -1086,23 +928,6 @@     }   } -  // parse +/- and return false if negative-  // this is never hit via spec tests-  bool Parser::parse_number_prefix()-  {-    bool positive = true;-    while(true) {-      if (lex < block_comment >()) continue;-      if (lex < number_prefix >()) continue;-      if (lex < exactly < '-' > >()) {-        positive = !positive;-        continue;-      }-      break;-    }-    return positive;-  }-   Expression_Obj Parser::parse_map()   {     NESTING_GUARD(nestings);@@ -1375,7 +1200,7 @@     Expression_Obj lhs = parse_operators();     // if it's a singleton, return it (don't wrap it)     if (!(peek_css< exactly<'+'> >(position) ||-          // condition is a bit misterious, but some combinations should not be counted as operations+          // condition is a bit mysterious, but some combinations should not be counted as operations           (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||           (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||           peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position))@@ -1478,7 +1303,7 @@     }     else if (lex< identifier_schema >()) {       String_Obj string = parse_identifier_schema();-      if (String_Schema_Ptr schema = Cast<String_Schema>(string)) {+      if (String_Schema* schema = Cast<String_Schema>(string)) {         if (lex < exactly < '(' > >()) {           schema->append(parse_list());           lex < exactly < ')' > >();@@ -1493,32 +1318,25 @@       return parse_function_call();     }     else if (lex< exactly<'+'> >()) {-      Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());+      Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());       if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());       return ex;     }     else if (lex< exactly<'-'> >()) {-      Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());+      Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());       if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());       return ex;     }     else if (lex< exactly<'/'> >()) {-      Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor());+      Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor());       if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());       return ex;     }     else if (lex< sequence< kwd_not > >()) {-      Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());+      Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());       if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());       return ex;     }-    // this whole branch is never hit via spec tests-    else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {-      if (parse_number_prefix()) return parse_value(); // prefix is positive-      Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());-      if (ex->operand()) ex->is_delayed(ex->operand()->is_delayed());-      return ex;-    }     else {       return parse_value();     }@@ -1533,9 +1351,9 @@               (L > 2 && parsed.substr(0, 3) == "-0.") );   } -  Number_Ptr Parser::lexed_number(const ParserState& pstate, const std::string& parsed)+  Number* Parser::lexed_number(const ParserState& pstate, const std::string& parsed)   {-    Number_Ptr nr = SASS_MEMORY_NEW(Number,+    Number* nr = SASS_MEMORY_NEW(Number,                                     pstate,                                     sass_strtod(parsed.c_str()),                                     "",@@ -1545,9 +1363,9 @@     return nr;   } -  Number_Ptr Parser::lexed_percentage(const ParserState& pstate, const std::string& parsed)+  Number* Parser::lexed_percentage(const ParserState& pstate, const std::string& parsed)   {-    Number_Ptr nr = SASS_MEMORY_NEW(Number,+    Number* nr = SASS_MEMORY_NEW(Number,                                     pstate,                                     sass_strtod(parsed.c_str()),                                     "%",@@ -1557,7 +1375,7 @@     return nr;   } -  Number_Ptr Parser::lexed_dimension(const ParserState& pstate, const std::string& parsed)+  Number* Parser::lexed_dimension(const ParserState& pstate, const std::string& parsed)   {     size_t L = parsed.length();     size_t num_pos = parsed.find_first_not_of(" \n\r\t");@@ -1568,7 +1386,7 @@     }     if (unit_pos == std::string::npos) unit_pos = L;     const std::string& num = parsed.substr(num_pos, unit_pos - num_pos);-    Number_Ptr nr = SASS_MEMORY_NEW(Number,+    Number* nr = SASS_MEMORY_NEW(Number,                                     pstate,                                     sass_strtod(num.c_str()),                                     Token(number(parsed.c_str())),@@ -1578,9 +1396,9 @@     return nr;   } -  Value_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed)+  Value* Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed)   {-    Color_Ptr color = NULL;+    Color_RGBA* color = NULL;     if (parsed[0] != '#') {       return SASS_MEMORY_NEW(String_Quoted, pstate, parsed);     }@@ -1590,7 +1408,7 @@       std::string r(2, parsed[1]);       std::string g(2, parsed[2]);       std::string b(2, parsed[3]);-      color = SASS_MEMORY_NEW(Color,+      color = SASS_MEMORY_NEW(Color_RGBA,                                pstate,                                static_cast<double>(strtol(r.c_str(), NULL, 16)),                                static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1603,7 +1421,7 @@       std::string g(2, parsed[2]);       std::string b(2, parsed[3]);       std::string a(2, parsed[4]);-      color = SASS_MEMORY_NEW(Color,+      color = SASS_MEMORY_NEW(Color_RGBA,                                pstate,                                static_cast<double>(strtol(r.c_str(), NULL, 16)),                                static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1615,7 +1433,7 @@       std::string r(parsed.substr(1,2));       std::string g(parsed.substr(3,2));       std::string b(parsed.substr(5,2));-      color = SASS_MEMORY_NEW(Color,+      color = SASS_MEMORY_NEW(Color_RGBA,                                pstate,                                static_cast<double>(strtol(r.c_str(), NULL, 16)),                                static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1628,7 +1446,7 @@       std::string g(parsed.substr(3,2));       std::string b(parsed.substr(5,2));       std::string a(parsed.substr(7,2));-      color = SASS_MEMORY_NEW(Color,+      color = SASS_MEMORY_NEW(Color_RGBA,                                pstate,                                static_cast<double>(strtol(r.c_str(), NULL, 16)),                                static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1641,10 +1459,10 @@     return color;   } -  Value_Ptr Parser::color_or_string(const std::string& lexed) const+  Value* Parser::color_or_string(const std::string& lexed) const   {     if (auto color = name_to_color(lexed)) {-      auto c = SASS_MEMORY_NEW(Color, color);+      auto c = SASS_MEMORY_NEW(Color_RGBA, color);       c->is_delayed(true);       c->pstate(pstate);       c->disp(lexed);@@ -1663,7 +1481,7 @@       if (match< ampersand >()) {         warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate);       }-      return SASS_MEMORY_NEW(Parent_Selector, pstate); }+      return SASS_MEMORY_NEW(Parent_Reference, pstate); }      if (lex< kwd_important >())     { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); }@@ -1729,7 +1547,7 @@     css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");      // unreachable statement-    return 0;+    return {};   }    // this parses interpolation inside other strings@@ -1742,7 +1560,7 @@                     find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);      if (!p) {-      String_Quoted_Ptr str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, std::string(i, chunk.end), 0, false, false, true, css);+      String_Quoted* str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, std::string(i, chunk.end), 0, false, false, true, css);       if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');       return str_quoted;     }@@ -1786,71 +1604,63 @@     return schema.detach();   } -  String_Schema_Obj Parser::parse_css_variable_value(bool top_level)-  {-    String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);-    String_Schema_Obj tok;-    if (!(tok = parse_css_variable_value_token(top_level))) {-      return NULL;-    }--    schema->concat(tok);-    while ((tok = parse_css_variable_value_token(top_level))) {-      schema->concat(tok);-    }--    return schema.detach();-  }--  String_Schema_Obj Parser::parse_css_variable_value_token(bool top_level)+  String_Schema_Obj Parser::parse_css_variable_value()   {     String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);-    if (-      (top_level && lex< css_variable_top_level_value >(false)) ||-      (!top_level && lex< css_variable_value >(false))-    ) {-      Token str(lexed);-      schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));-    }-    else if (Expression_Obj tok = lex_interpolation()) {-      if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {-        schema->concat(s);-      } else {-        schema->append(tok);-      }-    }-    else if (lex< quoted_string >()) {-      Expression_Obj tok = parse_string();-      if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {-        schema->concat(s);-      } else {-        schema->append(tok);-      }-    }-    else {-      if (peek< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {-        if (lex< exactly<'('> >()) {-          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("(")));-          if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);-          if (!lex< exactly<')'> >()) css_error("Invalid CSS", " after ", ": expected \")\", was ");-          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(")")));+    std::vector<char> brackets;+    while (true) {+      if (+        (brackets.empty() && lex< css_variable_top_level_value >(false)) ||+        (!brackets.empty() && lex< css_variable_value >(false))+      ) {+        Token str(lexed);+        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));+      } else if (Expression_Obj tok = lex_interpolation()) {+        if (String_Schema* s = Cast<String_Schema>(tok)) {+          if (s->empty()) break;+          schema->concat(s);+        } else {+          schema->append(tok);         }-        else if (lex< exactly<'['> >()) {-          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("[")));-          if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);-          if (!lex< exactly<']'> >()) css_error("Invalid CSS", " after ", ": expected \"]\", was ");-          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("]")));+      } else if (lex< quoted_string >()) {+        Expression_Obj tok = parse_string();+        if (tok.isNull()) break;+        if (String_Schema* s = Cast<String_Schema>(tok)) {+          if (s->empty()) break;+          schema->concat(s);+        } else {+          schema->append(tok);         }-        else if (lex< exactly<'{'> >()) {-          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("{")));-          if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);-          if (!lex< exactly<'}'> >()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");-          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("}")));+      } else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {+        const char opening_bracket = *(position - 1);+        brackets.push_back(opening_bracket);+        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, opening_bracket)));+      } else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) {+        if (brackets.empty()) break;+        const char closing_bracket = *(match - 1);+        if (brackets.back() != Util::opening_bracket_for(closing_bracket)) {+          std::string message = ": expected \"";+          message += Util::closing_bracket_for(brackets.back());+          message += "\", was ";+          css_error("Invalid CSS", " after ", message);         }+        lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >();+        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, closing_bracket)));+        brackets.pop_back();+      } else {+        break;       }     } -    return schema->length() > 0 ? schema.detach() : NULL;+    if (!brackets.empty()) {+      std::string message = ": expected \"";+      message += Util::closing_bracket_for(brackets.back());+      message += "\", was ";+      css_error("Invalid CSS", " after ", message);+    }++    if (schema->empty()) error("Custom property values may not be empty.");+    return schema.detach();   }    Value_Obj Parser::parse_static_value()@@ -1883,7 +1693,7 @@       return SASS_MEMORY_NEW(String_Quoted, pstate, std::string(str.begin, str.end));     } -    String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+    String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate);     while (i < str.end) {       p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, str.end);       if (p) {@@ -1918,7 +1728,7 @@    String_Obj Parser::parse_ie_keyword_arg()   {-    String_Schema_Ptr kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3);+    String_Schema_Obj kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3);     if (lex< variable >()) {       kwd_arg->append(SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed)));     } else {@@ -2139,7 +1949,7 @@       uri = url_string->to_string({ NESTED, 5 });     } -    if (String_Schema_Ptr schema = Cast<String_Schema>(url_string)) {+    if (String_Schema* schema = Cast<String_Schema>(url_string)) {       String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate);       res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix));       res->append(schema);@@ -2166,7 +1976,7 @@       while (pp && peek< exactly< hash_lbrace > >(pp)) {         pp = sequence< interpolant, real_uri_value >(pp);       }-      if (!pp) return 0;+      if (!pp) return {};       position = pp;       return parse_interpolated_chunk(Token(p, position));     }@@ -2175,7 +1985,7 @@       return SASS_MEMORY_NEW(String_Constant, pstate, res);     } -    return 0;+    return {};   }    Function_Call_Obj Parser::parse_function_call()@@ -2191,18 +2001,21 @@     return SASS_MEMORY_NEW(Function_Call, call_pos, name, args);   } -  Function_Call_Schema_Obj Parser::parse_function_call_schema()+  Function_Call_Obj Parser::parse_function_call_schema()   {     String_Obj name = parse_identifier_schema();     ParserState source_position_of_call = pstate;     Arguments_Obj args = parse_arguments(); -    return SASS_MEMORY_NEW(Function_Call_Schema, source_position_of_call, name, args);+    return SASS_MEMORY_NEW(Function_Call, source_position_of_call, name, args);   }    Content_Obj Parser::parse_content_directive()   {-    return SASS_MEMORY_NEW(Content, pstate);+    ParserState call_pos = pstate;+    Arguments_Obj args = parse_arguments();++    return SASS_MEMORY_NEW(Content, call_pos, args);   }    If_Obj Parser::parse_if_directive(bool else_if)@@ -2212,7 +2025,7 @@     bool root = block_stack.back()->is_root();     Expression_Obj predicate = parse_list();     Block_Obj block = parse_block(root);-    Block_Obj alternative = NULL;+    Block_Obj alternative;      // only throw away comment if we parse a case     // we want all other comments to be parsed@@ -2297,7 +2110,7 @@     stack.push_back(Scope::Control);     bool root = block_stack.back()->is_root();     // create the initial while call object-    While_Obj call = SASS_MEMORY_NEW(While, pstate, 0, 0);+    While_Obj call = SASS_MEMORY_NEW(While, pstate, {}, {});     // parse mandatory predicate     Expression_Obj predicate = parse_list();     List_Obj l = Cast<List>(predicate);@@ -2313,20 +2126,107 @@     return call.detach();   } -  // EO parse_while_directive-  Media_Block_Obj Parser::parse_media_block()++  std::vector<CssMediaQuery_Obj> Parser::parseCssMediaQueries()   {-    stack.push_back(Scope::Media);-    Media_Block_Obj media_block = SASS_MEMORY_NEW(Media_Block, pstate, 0, 0);+    std::vector<CssMediaQuery_Obj> result;+    do {+      if (auto query = parseCssMediaQuery()) {+        result.push_back(query);+      }+    } while (lex<exactly<','>>());+    return result;+  } -    media_block->media_queries(parse_media_queries());+  std::string Parser::parseIdentifier()+  {+    if (lex < identifier >(false)) {+      return std::string(lexed);+    }+    return std::string();+  } -    Media_Block_Obj prev_media_block = last_media_block;-    last_media_block = media_block;-    media_block->block(parse_css_block());-    last_media_block = prev_media_block;+  CssMediaQuery_Obj Parser::parseCssMediaQuery()+  {+    CssMediaQuery_Obj result = SASS_MEMORY_NEW(CssMediaQuery, pstate);+    lex<css_comments>(false);++    // Check if any tokens are to parse+    if (!peek_css<exactly<'('>>()) {++      std::string token1(parseIdentifier());+      lex<css_comments>(false);++      if (token1.empty()) {+        return {};+      }++      std::string token2(parseIdentifier());+      lex<css_comments>(false);++      if (Util::equalsLiteral("and", token2)) {+        result->type(token1);+      }+      else {+        if (token2.empty()) {+          result->type(token1);+        }+        else {+          result->modifier(token1);+          result->type(token2);+        }++        if (lex < kwd_and >()) {+          lex<css_comments>(false);+        }+        else {+          return result;+        }++      }++    }++    std::vector<std::string> queries;++    do {+      lex<css_comments>(false);++      if (lex<exactly<'('>>()) {+        // In dart sass parser returns a pure string+        if (lex < skip_over_scopes < exactly < '(' >, exactly < ')' > > >()) {+          std::string decl("(" + std::string(lexed));+          queries.push_back(decl);+        }+        // Should be: parseDeclarationValue;+        if (!lex<exactly<')'>>()) {+          // Should we throw an error here?+        }+      }+    } while (lex < kwd_and >());++    result->features(queries);++    if (result->features().empty()) {+      if (result->type().empty()) {+        return {};+      }+    }++    return result;+  }+++  // EO parse_while_directive+  MediaRule_Obj Parser::parseMediaRule()+  {+    MediaRule_Obj rule = SASS_MEMORY_NEW(MediaRule, pstate);+    stack.push_back(Scope::Media);+    rule->schema(parse_media_queries());+    parse_block_comments(false);+    rule->block(parse_css_block());     stack.pop_back();-    return media_block.detach();+    return rule;   }    List_Obj Parser::parse_media_queries()@@ -2339,7 +2239,7 @@     return queries.detach();   } -  // Expression_Ptr Parser::parse_media_query()+  // Expression* Parser::parse_media_query()   Media_Query_Obj Parser::parse_media_query()   {     advanceToNextToken();@@ -2353,7 +2253,7 @@      while (lex_css < kwd_and >()) media_query->append(parse_media_expression());     if (lex < identifier_schema >()) {-      String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+      String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate);       schema->append(media_query->media_type());       schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));       schema->append(parse_identifier_schema());@@ -2370,7 +2270,7 @@   {     if (lex < identifier_schema >()) {       String_Obj ss = parse_identifier_schema();-      return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, 0, true);+      return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, {}, true);     }     if (!lex_css< exactly<'('> >()) {       error("media query expression must begin with '('");@@ -2380,7 +2280,7 @@       error("media feature required in media query expression");     }     feature = parse_expression();-    Expression_Obj expression = 0;+    Expression_Obj expression;     if (lex_css< exactly<':'> >()) {       expression = parse_list(DELAYED);     }@@ -2394,10 +2294,7 @@   // these are very similar to media blocks   Supports_Block_Obj Parser::parse_supports_directive()   {-    Supports_Condition_Obj cond = parse_supports_condition();-    if (!cond) {-      css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", false);-    }+    Supports_Condition_Obj cond = parse_supports_condition(/*top_level=*/true);     // create the ast node object for the support queries     Supports_Block_Obj query = SASS_MEMORY_NEW(Supports_Block, pstate, cond);     // additional block is mandatory@@ -2409,27 +2306,27 @@    // parse one query operation   // may encounter nested queries-  Supports_Condition_Obj Parser::parse_supports_condition()+  Supports_Condition_Obj Parser::parse_supports_condition(bool top_level)   {     lex < css_whitespace >();     Supports_Condition_Obj cond;     if ((cond = parse_supports_negation())) return cond;-    if ((cond = parse_supports_operator())) return cond;+    if ((cond = parse_supports_operator(top_level))) return cond;     if ((cond = parse_supports_interpolation())) return cond;     return cond;   }    Supports_Condition_Obj Parser::parse_supports_negation()   {-    if (!lex < kwd_not >()) return 0;-    Supports_Condition_Obj cond = parse_supports_condition_in_parens();+    if (!lex < kwd_not >()) return {};+    Supports_Condition_Obj cond = parse_supports_condition_in_parens(/*parens_required=*/true);     return SASS_MEMORY_NEW(Supports_Negation, pstate, cond);   } -  Supports_Condition_Obj Parser::parse_supports_operator()+  Supports_Condition_Obj Parser::parse_supports_operator(bool top_level)   {-    Supports_Condition_Obj cond = parse_supports_condition_in_parens();-    if (cond.isNull()) return 0;+    Supports_Condition_Obj cond = parse_supports_condition_in_parens(/*parens_required=*/top_level);+    if (cond.isNull()) return {};      while (true) {       Supports_Operator::Operand op = Supports_Operator::OR;@@ -2437,9 +2334,9 @@       else if(!lex < kwd_or >()) { break; }        lex < css_whitespace >();-      Supports_Condition_Obj right = parse_supports_condition_in_parens();+      Supports_Condition_Obj right = parse_supports_condition_in_parens(/*parens_required=*/true); -      // Supports_Condition_Ptr cc = SASS_MEMORY_NEW(Supports_Condition, *static_cast<Supports_Condition_Ptr>(cond));+      // Supports_Condition* cc = SASS_MEMORY_NEW(Supports_Condition, *static_cast<Supports_Condition*>(cond));       cond = SASS_MEMORY_NEW(Supports_Operator, pstate, cond, right, op);     }     return cond;@@ -2447,10 +2344,10 @@    Supports_Condition_Obj Parser::parse_supports_interpolation()   {-    if (!lex < interpolant >()) return 0;+    if (!lex < interpolant >()) return {};      String_Obj interp = parse_interpolated_chunk(lexed);-    if (!interp) return 0;+    if (!interp) return {};      return SASS_MEMORY_NEW(Supports_Interpolation, pstate, interp);   }@@ -2459,10 +2356,10 @@   // look like declarations their semantics differ significantly   Supports_Condition_Obj Parser::parse_supports_declaration()   {-    Supports_Condition_Ptr cond;+    Supports_Condition* cond;     // parse something declaration like     Expression_Obj feature = parse_expression();-    Expression_Obj expression = 0;+    Expression_Obj expression;     if (lex_css< exactly<':'> >()) {       expression = parse_list(DELAYED);     }@@ -2475,21 +2372,24 @@     return cond;   } -  Supports_Condition_Obj Parser::parse_supports_condition_in_parens()+  Supports_Condition_Obj Parser::parse_supports_condition_in_parens(bool parens_required)   {     Supports_Condition_Obj interp = parse_supports_interpolation();-    if (interp != 0) return interp;+    if (interp != nullptr) return interp; -    if (!lex < exactly <'('> >()) return 0;+    if (!lex < exactly <'('> >()) {+      if (parens_required) {+        css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", /*trim=*/false);+      } else {+        return {};+      }+    }     lex < css_whitespace >(); -    Supports_Condition_Obj cond = parse_supports_condition();-    if (cond != 0) {-      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");-    } else {-      cond = parse_supports_declaration();-      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");-    }+    Supports_Condition_Obj cond = parse_supports_condition(/*top_level=*/false);+    if (cond.isNull()) cond = parse_supports_declaration();+    if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");+     lex < css_whitespace >();     return cond;   }@@ -2498,7 +2398,7 @@   {     stack.push_back(Scope::AtRoot);     ParserState at_source_position = pstate;-    Block_Obj body = 0;+    Block_Obj body;     At_Root_Query_Obj expr;     Lookahead lookahead_result;     if (lex_css< exactly<'('> >()) {@@ -2545,68 +2445,6 @@     return cond;   } -  Directive_Obj Parser::parse_special_directive()-  {-    std::string kwd(lexed);--    if (lexed == "@else") error("Invalid CSS: @else must come after @if");--    // this whole branch is never hit via spec tests--    Directive_Ptr at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);-    Lookahead lookahead = lookahead_for_include(position);-    if (lookahead.found && !lookahead.has_interpolants) {-      at_rule->selector(parse_selector_list(false));-    }--    lex < css_comments >(false);--    if (lex < static_property >()) {-      at_rule->value(parse_interpolated_chunk(Token(lexed)));-    } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {-      at_rule->value(parse_list());-    }--    lex < css_comments >(false);--    if (peek< exactly<'{'> >()) {-      at_rule->block(parse_block());-    }--    return at_rule;-  }--  // this whole branch is never hit via spec tests-  Directive_Obj Parser::parse_prefixed_directive()-  {-    std::string kwd(lexed);--    if (lexed == "@else") error("Invalid CSS: @else must come after @if");--    Directive_Obj at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);-    Lookahead lookahead = lookahead_for_include(position);-    if (lookahead.found && !lookahead.has_interpolants) {-      at_rule->selector(parse_selector_list(false));-    }--    lex < css_comments >(false);--    if (lex < static_property >()) {-      at_rule->value(parse_interpolated_chunk(Token(lexed)));-    } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {-      at_rule->value(parse_list());-    }--    lex < css_comments >(false);--    if (peek< exactly<'{'> >()) {-      at_rule->block(parse_block());-    }--    return at_rule;-  }--   Directive_Obj Parser::parse_directive()   {     Directive_Obj directive = SASS_MEMORY_NEW(Directive, pstate, lexed);@@ -2624,7 +2462,7 @@     if (lex < interpolant >(true) != NULL) {       return parse_interpolated_chunk(lexed, true);     }-    return 0;+    return {};   }    Expression_Obj Parser::lex_interp_uri()@@ -2647,6 +2485,7 @@     lex <       one_plus <         alternatives <+          exactly <'>'>,           sequence <             exactly <'\\'>,             any_char@@ -2690,13 +2529,13 @@     if (match) {       return SASS_MEMORY_NEW(String_Constant, pstate, lexed);     }-    return NULL;+    return {};   }    Expression_Obj Parser::lex_almost_any_value_token()   {     Expression_Obj rv;-    if (*position == 0) return 0;+    if (*position == 0) return {};     if ((rv = lex_almost_any_value_chars())) return rv;     // if ((rv = lex_block_comment())) return rv;     // if ((rv = lex_single_line_comment())) return rv;@@ -2712,10 +2551,10 @@   {      String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);-    if (*position == 0) return 0;+    if (*position == 0) return {};     lex < spaces >(false);     Expression_Obj token = lex_almost_any_value_token();-    if (!token) return 0;+    if (!token) return {};     schema->append(token);     if (*position == 0) {       schema->rtrim();@@ -2813,7 +2652,7 @@       rv.error = q;       rv.position = q;       // check expected opening bracket-      // only after successfull matching+      // only after successful matching       if (peek < exactly<'{'> >(q)) rv.found = q;       // else if (peek < end_of_file >(q)) rv.found = q;       else if (peek < exactly<'('> >(q)) rv.found = q;@@ -2997,7 +2836,7 @@    Expression_Obj Parser::fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, std::vector<Operand>& ops, size_t i)   {-    if (String_Schema_Ptr schema = Cast<String_Schema>(base)) {+    if (String_Schema* schema = Cast<String_Schema>(base)) {       // return schema;       if (schema->has_interpolants()) {         if (i + 1 < operands.size() && (@@ -3020,7 +2859,7 @@     }      for (size_t S = operands.size(); i < S; ++i) {-      if (String_Schema_Ptr schema = Cast<String_Schema>(operands[i])) {+      if (String_Schema* schema = Cast<String_Schema>(operands[i])) {         if (schema->has_interpolants()) {           if (i + 1 < S) {             // this whole branch is never hit via spec tests@@ -3037,13 +2876,13 @@       } else {         base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]);       }-      Binary_Expression_Ptr b = Cast<Binary_Expression>(base.ptr());+      Binary_Expression* b = Cast<Binary_Expression>(base.ptr());       if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {         base->is_delayed(true);       }     }     // nested binary expression are never to be delayed-    if (Binary_Expression_Ptr b = Cast<Binary_Expression>(base)) {+    if (Binary_Expression* b = Cast<Binary_Expression>(base)) {       if (Cast<Binary_Expression>(b->left())) base->set_delayed(false);       if (Cast<Binary_Expression>(b->right())) base->set_delayed(false);     }@@ -3054,8 +2893,15 @@   {     Position p(pos.line ? pos : before_token);     ParserState pstate(path, source, p, Offset(0, 0));+    // `pstate.src` may not outlive stack unwind so we must copy it.+    // This is needed since we often parse dynamically generated code,+    // e.g. for interpolations, and we normally don't want to keep this+    // memory around after we parsed the AST tree successfully. Only on+    // errors we want to preserve them for better error reporting.+    char *src_copy = sass_copy_c_string(pstate.src);+    pstate.src = src_copy;     traces.push_back(Backtrace(pstate));-    throw Exception::InvalidSass(pstate, traces, msg);+    throw Exception::InvalidSass(pstate, traces, msg, src_copy);   }    void Parser::error(std::string msg)@@ -3078,7 +2924,7 @@     }     // backup position to last significant char     while (trim && last_pos > source && last_pos < end) {-      if (!Prelexer::is_space(*last_pos)) break;+      if (!Util::ascii_isspace(static_cast<unsigned char>(*last_pos))) break;       utf8::prior(last_pos, source);     } 
libsass/src/parser.hpp view
@@ -1,6 +1,10 @@ #ifndef SASS_PARSER_H #define SASS_PARSER_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include <string> #include <vector> @@ -38,7 +42,6 @@     Context& ctx;     std::vector<Block_Obj> block_stack;     std::vector<Scope> stack;-    Media_Block_Ptr last_media_block;     const char* source;     const char* position;     const char* end;@@ -48,23 +51,24 @@     Backtraces traces;     size_t indentation;     size_t nestings;+    bool allow_parent;      Token lexed; -    Parser(Context& ctx, const ParserState& pstate, Backtraces traces)-    : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),+    Parser(Context& ctx, const ParserState& pstate, Backtraces traces, bool allow_parent = true)+    : ParserState(pstate), ctx(ctx), block_stack(), stack(0),       source(0), position(0), end(0), before_token(pstate), after_token(pstate),-      pstate(pstate), traces(traces), indentation(0), nestings(0)-    { +      pstate(pstate), traces(traces), indentation(0), nestings(0), allow_parent(allow_parent)+    {       stack.push_back(Scope::Root);     }      // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));-    static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);-    static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);-    static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);+    static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = nullptr, bool allow_parent = true);+    static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = nullptr, bool allow_parent = true);+    static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = nullptr);     // special static parsers to convert strings into certain selectors-    static Selector_List_Obj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);+    static SelectorListObj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = nullptr, bool allow_parent = true);  #ifdef __clang__ @@ -184,7 +188,7 @@       // update after_token position for current token       after_token.add(it_before_token, it_after_token); -      // ToDo: could probably do this incremetal on original object (API wants offset?)+      // ToDo: could probably do this incremental on original object (API wants offset?)       pstate = ParserState(path, source, lexed, before_token, after_token - before_token);        // advance internal char iterator@@ -256,20 +260,20 @@     Argument_Obj parse_argument();     Assignment_Obj parse_assignment();     Ruleset_Obj parse_ruleset(Lookahead lookahead);-    Selector_List_Obj parse_selector_list(bool chroot);-    Complex_Selector_Obj parse_complex_selector(bool chroot);+    SelectorListObj parseSelectorList(bool chroot);+    ComplexSelectorObj parseComplexSelector(bool chroot);     Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot);-    Compound_Selector_Obj parse_compound_selector();-    Simple_Selector_Obj parse_simple_selector();-    Wrapped_Selector_Obj parse_negated_selector();-    Simple_Selector_Obj parse_pseudo_selector();+    CompoundSelectorObj parseCompoundSelector();+    SimpleSelectorObj parse_simple_selector();+    Pseudo_Selector_Obj parse_negated_selector2();+    Expression* parse_binominal();+    SimpleSelectorObj parse_pseudo_selector();     Attribute_Selector_Obj parse_attribute_selector();     Block_Obj parse_block(bool is_root = false);     Block_Obj parse_css_block(bool is_root = false);     bool parse_block_nodes(bool is_root = false);     bool parse_block_node(bool is_root = false); -    bool parse_number_prefix();     Declaration_Obj parse_declaration();     Expression_Obj parse_map();     Expression_Obj parse_bracket_list();@@ -285,14 +289,13 @@     Expression_Obj parse_value();     Function_Call_Obj parse_calc_function();     Function_Call_Obj parse_function_call();-    Function_Call_Schema_Obj parse_function_call_schema();+    Function_Call_Obj parse_function_call_schema();     String_Obj parse_url_function_string();     String_Obj parse_url_function_argument();     String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true);     String_Obj parse_string();     Value_Obj parse_static_value();-    String_Schema_Obj parse_css_variable_value(bool top_level = true);-    String_Schema_Obj parse_css_variable_value_token(bool top_level = true);+    String_Schema_Obj parse_css_variable_value();     String_Obj parse_ie_property();     String_Obj parse_ie_keyword_arg();     String_Schema_Obj parse_value_schema(const char* stop);@@ -301,31 +304,32 @@     For_Obj parse_for_directive();     Each_Obj parse_each_directive();     While_Obj parse_while_directive();+    MediaRule_Obj parseMediaRule();+    std::vector<CssMediaQuery_Obj> parseCssMediaQueries();+    std::string parseIdentifier();+    CssMediaQuery_Obj parseCssMediaQuery();     Return_Obj parse_return_directive();     Content_Obj parse_content_directive();     void parse_charset_directive();-    Media_Block_Obj parse_media_block();     List_Obj parse_media_queries();     Media_Query_Obj parse_media_query();     Media_Query_Expression_Obj parse_media_expression();     Supports_Block_Obj parse_supports_directive();-    Supports_Condition_Obj parse_supports_condition();+    Supports_Condition_Obj parse_supports_condition(bool top_level);     Supports_Condition_Obj parse_supports_negation();-    Supports_Condition_Obj parse_supports_operator();+    Supports_Condition_Obj parse_supports_operator(bool top_level);     Supports_Condition_Obj parse_supports_interpolation();     Supports_Condition_Obj parse_supports_declaration();-    Supports_Condition_Obj parse_supports_condition_in_parens();+    Supports_Condition_Obj parse_supports_condition_in_parens(bool parens_required);     At_Root_Block_Obj parse_at_root_block();     At_Root_Query_Obj parse_at_root_query();     String_Schema_Obj parse_almost_any_value();-    Directive_Obj parse_special_directive();-    Directive_Obj parse_prefixed_directive();     Directive_Obj parse_directive();     Warning_Obj parse_warning();     Error_Obj parse_error();     Debug_Obj parse_debug(); -    Value_Ptr color_or_string(const std::string& lexed) const;+    Value* color_or_string(const std::string& lexed) const;      // be more like ruby sass     Expression_Obj lex_almost_any_value_token();@@ -338,7 +342,7 @@     Token lex_variable();     Token lex_identifier(); -    void parse_block_comments();+    void parse_block_comments(bool store = true);      Lookahead lookahead_for_value(const char* start = 0);     Lookahead lookahead_for_selector(const char* start = 0);@@ -375,19 +379,19 @@           return SASS_MEMORY_NEW(String_Constant, pstate, lexed);         }       }-      return 0;+      return {};     }    public:-    static Number_Ptr lexed_number(const ParserState& pstate, const std::string& parsed);-    static Number_Ptr lexed_dimension(const ParserState& pstate, const std::string& parsed);-    static Number_Ptr lexed_percentage(const ParserState& pstate, const std::string& parsed);-    static Value_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed);+    static Number* lexed_number(const ParserState& pstate, const std::string& parsed);+    static Number* lexed_dimension(const ParserState& pstate, const std::string& parsed);+    static Number* lexed_percentage(const ParserState& pstate, const std::string& parsed);+    static Value* lexed_hex_color(const ParserState& pstate, const std::string& parsed);   private:-    Number_Ptr lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); };-    Number_Ptr lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); };-    Number_Ptr lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); };-    Value_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); };+    Number* lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); };+    Number* lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); };+    Number* lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); };+    Value* lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); };      static const char* re_attr_sensitive_close(const char* src);     static const char* re_attr_insensitive_close(const char* src);
+ libsass/src/parser_selectors.cpp view
@@ -0,0 +1,189 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "parser.hpp"++namespace Sass {++  using namespace Prelexer;+  using namespace Constants;++  ComplexSelectorObj Parser::parseComplexSelector(bool chroot)+  {++    NESTING_GUARD(nestings);++    lex < block_comment >();+    advanceToNextToken();++    ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate);++    if (peek < end_of_file >()) return sel;++    while (true) {++      lex < block_comment >();+      advanceToNextToken();++      // check for child (+) combinator+      if (lex < exactly < selector_combinator_child > >()) {+        sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::CHILD, peek_newline()));+      }+      // check for general sibling (~) combinator+      else if (lex < exactly < selector_combinator_general > >()) {+        sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::GENERAL, peek_newline()));+      }+      // check for adjecant sibling (+) combinator+      else if (lex < exactly < selector_combinator_adjacent > >()) {+        sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::ADJACENT, peek_newline()));+      }+      // check if we can parse a compound selector+      else if (CompoundSelectorObj compound = parseCompoundSelector()) {+        sel->append(compound);+      }+      else {+        break;+      }+    }++    if (sel->empty()) return {};++    // check if we parsed any parent references+    sel->chroots(sel->has_real_parent_ref() || chroot);++    sel->update_pstate(pstate);++    return sel;++  }++  SelectorListObj Parser::parseSelectorList(bool chroot)+  {++    bool reloop;+    bool had_linefeed = false;+    NESTING_GUARD(nestings);+    SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate);++    if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) {+      css_error("Invalid CSS", " after ", ": expected selector, was ");+    }++    do {+      reloop = false;++      had_linefeed = had_linefeed || peek_newline();++      if (peek_css< alternatives < class_char < selector_list_delims > > >())+        break; // in case there are superfluous commas at the end++      // now parse the complex selector+      ComplexSelectorObj complex = parseComplexSelector(chroot);+      if (complex.isNull()) return list.detach();+      complex->hasPreLineFeed(had_linefeed);++      had_linefeed = false;++      while (peek_css< exactly<','> >())+      {+        lex< css_comments >(false);+        // consume everything up and including the comma separator+        reloop = lex< exactly<','> >() != 0;+        // remember line break (also between some commas)+        had_linefeed = had_linefeed || peek_newline();+        // remember line break (also between some commas)+      }+      list->append(complex);++    } while (reloop);++    while (lex_css< kwd_optional >()) {+      list->is_optional(true);+    }++    // update for end position+    list->update_pstate(pstate);++    return list.detach();+  }++  // parse one compound selector, which is basically+  // a list of simple selectors (directly adjacent)+  // lex them exactly (without skipping white-space)+  CompoundSelectorObj Parser::parseCompoundSelector()+  {+    // init an empty compound selector wrapper+    CompoundSelectorObj seq = SASS_MEMORY_NEW(CompoundSelector, pstate);++    // skip initial white-space+    lex < block_comment >();+    advanceToNextToken();++    if (lex< exactly<'&'> >(false))+    {+      // ToDo: check the conditions and try to simplify flag passing+      if (!allow_parent) error("Parent selectors aren't allowed here.");+      // Create and append a new parent selector object+      seq->hasRealParent(true);+    }++    // parse list+    while (true)+    {+      // remove all block comments+      // leaves trailing white-space+      lex < block_comment >();+      // parse parent selector+      if (lex< exactly<'&'> >(false))+      {+        // parent selector only allowed at start+        // upcoming Sass may allow also trailing+        ParserState state(pstate);+        std::string found("&");+        if (lex < identifier >()) {+          found += std::string(lexed);+        }+        std::string sel(seq->hasRealParent() ? "&" : "");+        if (!seq->empty()) { sel = seq->last()->to_string({ NESTED, 5 }); }+        // ToDo: parser should throw parser exceptions+        error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n"+          "\"" + found + "\" may only be used at the beginning of a compound selector.", state);+      }+      // parse functional+      else if (match < re_functional >())+        {+          seq->append(parse_simple_selector());+        }++      // parse type selector+      else if (lex< re_type_selector >(false))+      {+        seq->append(SASS_MEMORY_NEW(Type_Selector, pstate, lexed));+      }+      // peek for abort conditions+      else if (peek< spaces >()) break;+      else if (peek< end_of_file >()) { break; }+      else if (peek_css < class_char < selector_combinator_ops > >()) break;+      else if (peek_css < class_char < complex_selector_delims > >()) break;+      // otherwise parse another simple selector+      else {+        SimpleSelectorObj sel = parse_simple_selector();+        if (!sel) return {};+        seq->append(sel);+      }+    }+    // EO while true++    if (seq && !peek_css<alternatives<end_of_file,exactly<'{'>>>()) {+      seq->hasPostLineBreak(peek_newline());+    }++    // We may have set hasRealParent+    if (seq && seq->empty() && !seq->hasRealParent()) return {};++    return seq;+  }+++}
− libsass/src/paths.hpp
@@ -1,71 +0,0 @@-#ifndef SASS_PATHS_H-#define SASS_PATHS_H--#include <string>-#include <vector>-#include <sstream>---template<typename T>-std::string vector_to_string(std::vector<T> v)-{-  std::stringstream buffer;-  buffer << "[";--  if (!v.empty())-  {  buffer << v[0]; }-  else-  { buffer << "]"; }--  if (v.size() == 1)-  { buffer << "]"; }-  else-  {-    for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i];-    buffer << "]";-  }--  return buffer.str();-}--namespace Sass {---  template<typename T>-  std::vector<std::vector<T> > paths(std::vector<std::vector<T> > strata, size_t from_end = 0)-  {-    if (strata.empty()) {-      return std::vector<std::vector<T> >();-    }--    size_t end = strata.size() - from_end;-    if (end <= 1) {-      std::vector<std::vector<T> > starting_points;-      starting_points.reserve(strata[0].size());-      for (size_t i = 0, S = strata[0].size(); i < S; ++i) {-        std::vector<T> starting_point;-        starting_point.push_back(strata[0][i]);-        starting_points.push_back(starting_point);-      }-      return starting_points;-    }--    std::vector<std::vector<T> > up_to_here = paths(strata, from_end + 1);-    std::vector<T>          here       = strata[end-1];--    std::vector<std::vector<T> > branches;-    branches.reserve(up_to_here.size() * here.size());-    for (size_t i = 0, S1 = up_to_here.size(); i < S1; ++i) {-      for (size_t j = 0, S2 = here.size(); j < S2; ++j) {-        std::vector<T> branch = up_to_here[i];-        branch.push_back(here[j]);-        branches.push_back(branch);-      }-    }--    return branches;-  }--}--#endif
+ libsass/src/permutate.hpp view
@@ -0,0 +1,140 @@+#ifndef SASS_PATHS_H+#define SASS_PATHS_H++#include <vector>++namespace Sass {++  // Returns a list of all possible paths through the given lists.+  //+  // For example, given `[[1, 2], [3, 4], [5]]`, this returns:+  //+  // ```+  // [[1, 3, 5],+  //  [2, 3, 5],+  //  [1, 4, 5],+  //  [2, 4, 5]]+  // ```+  // +  // Note: called `paths` in dart-sass+  template <class T>+  std::vector<std::vector<T>> permutate(+    const std::vector<std::vector<T>>& in)+  {++    size_t L = in.size(), n = 0;++    // Exit early if any entry is empty+    for (size_t i = 0; i < L; i += 1) {+      if (in[i].size() == 0) return {};+    }++    size_t* state = new size_t[L + 1];+    std::vector<std::vector<T>> out;++    // First initialize all states for every permutation group+    for (size_t i = 0; i < L; i += 1) {+      state[i] = in[i].size() - 1;+    }+    while (true) {+      std::vector<T> perm;+      // Create one permutation for state+      for (size_t i = 0; i < L; i += 1) {+        perm.push_back(in.at(i).at(in[i].size() - state[i] - 1));+      }+      // Current group finished+      if (state[n] == 0) {+        // Find position of next decrement+        while (n < L && state[++n] == 0) {}++        if (n == L) {+          out.push_back(perm);+          break;+        }++        state[n] -= 1;++        for (size_t p = 0; p < n; p += 1) {+          state[p] = in[p].size() - 1;+        }++        // Restart from front+        n = 0;++      }+      else {+        state[n] -= 1;+      }+      out.push_back(perm);+    }++    delete[] state;+    return out;+  }+  // EO permutate++  // ToDo: this variant is used in resolve_parent_refs+  template <class T>+  std::vector<std::vector<T>>+    permutateAlt(const std::vector<std::vector<T>>& in) {++    size_t L = in.size();+    size_t n = in.size() - 1;+    size_t* state = new size_t[L];+    std::vector< std::vector<T>> out;++    // First initialize all states for every permutation group+    for (size_t i = 0; i < L; i += 1) {+      if (in[i].size() == 0) return {};+      state[i] = in[i].size() - 1;+    }++    while (true) {+      /*+      // std::cerr << "PERM: ";+      for (size_t p = 0; p < L; p++)+      { // std::cerr << state[p] << " "; }+      // std::cerr << "\n";+      */+      std::vector<T> perm;+      // Create one permutation for state+      for (size_t i = 0; i < L; i += 1) {+        perm.push_back(in.at(i).at(in[i].size() - state[i] - 1));+      }+      // Current group finished+      if (state[n] == 0) {+        // Find position of next decrement+        while (n > 0 && state[--n] == 0)+        {++        }+        // Check for end condition+        if (state[n] != 0) {+          // Decrease next on the left side+          state[n] -= 1;+          // Reset all counters to the right+          for (size_t p = n + 1; p < L; p += 1) {+            state[p] = in[p].size() - 1;+          }+          // Restart from end+          n = L - 1;+        }+        else {+          out.push_back(perm);+          break;+        }+      }+      else {+        state[n] -= 1;+      }+      out.push_back(perm);+    }++    delete[] state;+    return out;+  }+  // EO permutateAlt++}++#endif
libsass/src/plugins.cpp view
@@ -1,7 +1,11 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include "output.hpp" #include "plugins.hpp"+#include "util.hpp"  #ifdef _WIN32 #include <windows.h>@@ -154,7 +158,7 @@           }         }       }-      catch (utf8::invalid_utf8)+      catch (utf8::invalid_utf8&)       {         // report the error to the console (should not happen)         // implementors should make sure to provide valid utf8
libsass/src/position.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "position.hpp"  namespace Sass {
libsass/src/position.hpp view
@@ -86,7 +86,7 @@      size_t length()    const { return end - begin; }     std::string ws_before() const { return std::string(prefix, begin); }-    const std::string to_string() const { return std::string(begin, end); }+    std::string to_string() const { return std::string(begin, end); }     std::string time_wspace() const {       std::string str(to_string());       std::string whitespaces(" \t\f\v\n\r");
libsass/src/prelexer.cpp view
@@ -1,8 +1,11 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <cctype>+ #include <iostream> #include <iomanip> #include "util.hpp"+#include "util_string.hpp" #include "position.hpp" #include "prelexer.hpp" #include "constants.hpp"@@ -260,7 +263,7 @@       >(src);     } -    // Match a line comment (/.*?(?=\n|\r\n?|\Z)/.+    // Match a line comment (/.*?(?=\n|\r\n?|\f|\Z)/.     const char* line_comment(const char* src)     {       return sequence<@@ -333,7 +336,7 @@       return alternatives<                unicode_seq,                alpha,-               unicode,+               nonascii,                exactly<'-'>,                exactly<'_'>,                NONASCII,@@ -348,7 +351,7 @@       return alternatives<                unicode_seq,                alnum,-               unicode,+               nonascii,                exactly<'-'>,                exactly<'_'>,                NONASCII,@@ -382,7 +385,7 @@     {       return alternatives <                alpha,-               unicode,+               nonascii,                escape_seq,                exactly<'_'>              >(src);@@ -392,7 +395,7 @@     {       return alternatives <                alnum,-               unicode,+               nonascii,                escape_seq,                exactly<'_'>              >(src);@@ -1260,6 +1263,9 @@     const char* kwd_lte(const char* src) {       return exactly<lte>(src);     }+    const char* kwd_using(const char* src) {+      return keyword<using_kwd>(src);+    }      // match specific IE syntax     const char* ie_progid(const char* src) {@@ -1394,7 +1400,7 @@     }*/      const char* H(const char* src) {-      return std::isxdigit(*src) ? src+1 : 0;+      return Util::ascii_isxdigit(static_cast<unsigned char>(*src)) ? src+1 : 0;     }      const char* W(const char* src) {
libsass/src/prelexer.hpp view
@@ -25,6 +25,7 @@     const char* kwd_gte(const char* src);     const char* kwd_lt(const char* src);     const char* kwd_lte(const char* src);+    const char* kwd_using(const char* src);      // Match standard control chars     const char* kwd_at(const char* src);@@ -64,17 +65,16 @@       size_t level = 0;       bool in_squote = false;       bool in_dquote = false;-      // bool in_braces = false;--      while (*src) {--        // check for abort condition-        if (end && src >= end) break;+      bool in_backslash_escape = false; +      while ((end == nullptr || src < end) && *src != '\0') {         // has escaped sequence?-        if (*src == '\\') {-          ++ src; // skip this (and next)+        if (in_backslash_escape) {+          in_backslash_escape = false;         }+        else if (*src == '\\') {+          in_backslash_escape = true;+        }         else if (*src == '"') {           in_dquote = ! in_dquote;         }@@ -119,7 +119,7 @@     // first start/opener must be consumed already!     template<prelexer start, prelexer stop>     const char* skip_over_scopes(const char* src) {-      return skip_over_scopes<start, stop>(src, 0);+      return skip_over_scopes<start, stop>(src, nullptr);     }      // Match a sequence of characters delimited by the supplied chars.
libsass/src/remove_placeholders.cpp view
@@ -1,84 +1,86 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+#include "ast.hpp"+ #include "remove_placeholders.hpp"-#include "context.hpp"-#include "inspect.hpp"-#include <iostream>  namespace Sass {      Remove_Placeholders::Remove_Placeholders()     { } -    void Remove_Placeholders::operator()(Block_Ptr b) {-        for (size_t i = 0, L = b->length(); i < L; ++i) {-            Statement_Ptr st = b->at(i);-            st->perform(this);-        }+    void Remove_Placeholders::operator()(Block* b) {+      for (size_t i = 0, L = b->length(); i < L; ++i) {+        if (b->get(i)) b->get(i)->perform(this);+      }     } -    Selector_List_Ptr Remove_Placeholders::remove_placeholders(Selector_List_Ptr sl)+    void Remove_Placeholders::remove_placeholders(SimpleSelector* simple)     {-      Selector_List_Ptr new_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());--      for (size_t i = 0, L = sl->length(); i < L; ++i) {-          if (!sl->at(i)->contains_placeholder()) {-              new_sl->append(sl->at(i));-          }+      if (Pseudo_Selector * pseudo = simple->getPseudoSelector()) {+        if (pseudo->selector()) remove_placeholders(pseudo->selector());       }--      return new_sl;-     } --    void Remove_Placeholders::operator()(Ruleset_Ptr r) {-        // Create a new selector group without placeholders-        Selector_List_Obj sl = Cast<Selector_List>(r->selector());+    void Remove_Placeholders::remove_placeholders(CompoundSelector* compound)+    {+      for (size_t i = 0, L = compound->length(); i < L; ++i) {+        if (compound->get(i)) remove_placeholders(compound->get(i));+      }+      listEraseItemIf(compound->elements(), listIsEmpty<SimpleSelector>);+    } -        if (sl) {-          // Set the new placeholder selector list-          r->selector(remove_placeholders(sl));-          // Remove placeholders in wrapped selectors-          for (Complex_Selector_Obj cs : sl->elements()) {-            while (cs) {-              if (cs->head()) {-                for (Simple_Selector_Obj& ss : cs->head()->elements()) {-                  if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {-                    if (Selector_List_Ptr wsl = Cast<Selector_List>(ws->selector())) {-                      Selector_List_Ptr clean = remove_placeholders(wsl);-                      // also clean superflous parent selectors-                      // probably not really the correct place-                      clean->remove_parent_selectors();-                      ws->selector(clean);-                    }-                  }-                }-              }-              cs = cs->tail();-            }+    void Remove_Placeholders::remove_placeholders(ComplexSelector* complex)+    {+      if (complex->has_placeholder()) {+        complex->clear(); // remove all+      }+      else {+        for (size_t i = 0, L = complex->length(); i < L; ++i) {+          if (CompoundSelector * compound = complex->get(i)->getCompound()) {+            if (compound) remove_placeholders(compound);           }         }+        listEraseItemIf(complex->elements(), listIsEmpty<SelectorComponent>);+      }+    } -        // Iterate into child blocks-        Block_Obj b = r->block();+    SelectorList* Remove_Placeholders::remove_placeholders(SelectorList* sl)+    {+      for (size_t i = 0, L = sl->length(); i < L; ++i) {+        if (sl->get(i)) remove_placeholders(sl->get(i));+      }+      listEraseItemIf(sl->elements(), listIsEmpty<ComplexSelector>);+      return sl;+    } -        for (size_t i = 0, L = b->length(); i < L; ++i) {-            if (b->at(i)) {-                Statement_Obj st = b->at(i);-                st->perform(this);-            }-        }+    void Remove_Placeholders::operator()(CssMediaRule* rule)+    {+      if (rule->block()) operator()(rule->block());     } -    void Remove_Placeholders::operator()(Media_Block_Ptr m) {-        operator()(m->block());+    void Remove_Placeholders::operator()(Ruleset* r)+    {+      if (SelectorListObj sl = r->selector()) {+        // Set the new placeholder selector list+        r->selector((remove_placeholders(sl)));+      }+      // Iterate into child blocks+      Block_Obj b = r->block();+      for (size_t i = 0, L = b->length(); i < L; ++i) {+        if (b->get(i)) { b->get(i)->perform(this); }+      }     }-    void Remove_Placeholders::operator()(Supports_Block_Ptr m) {-        operator()(m->block());++    void Remove_Placeholders::operator()(Supports_Block* m)+    {+      if (m->block()) operator()(m->block());     } -    void Remove_Placeholders::operator()(Directive_Ptr a) {-        if (a->block()) a->block()->perform(this);+    void Remove_Placeholders::operator()(Directive* a)+    {+      if (a->block()) a->block()->perform(this);     }  }
libsass/src/remove_placeholders.hpp view
@@ -1,34 +1,36 @@ #ifndef SASS_REMOVE_PLACEHOLDERS_H #define SASS_REMOVE_PLACEHOLDERS_H -#pragma once--#include "ast.hpp"+#include "ast_fwd_decl.hpp" #include "operation.hpp"  namespace Sass { +  class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> { -    class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> {+  public: -        void fallback_impl(AST_Node_Ptr n) {}+    SelectorList* remove_placeholders(SelectorList*);+    void remove_placeholders(SimpleSelector* simple);+    void remove_placeholders(CompoundSelector* complex);+    void remove_placeholders(ComplexSelector* complex); -    public:-      Selector_List_Ptr remove_placeholders(Selector_List_Ptr); -    public:-        Remove_Placeholders();-        ~Remove_Placeholders() { }+  public:+    Remove_Placeholders();+    ~Remove_Placeholders() { } -        void operator()(Block_Ptr);-        void operator()(Ruleset_Ptr);-        void operator()(Media_Block_Ptr);-        void operator()(Supports_Block_Ptr);-        void operator()(Directive_Ptr);+    void operator()(Block*);+    void operator()(Ruleset*);+    void operator()(CssMediaRule*);+    void operator()(Supports_Block*);+    void operator()(Directive*); -        template <typename U>-        void fallback(U x) { return fallback_impl(x); }-    };+    // ignore missed types+    template <typename U>+    void fallback(U x) {}++  };  } 
libsass/src/sass.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstdlib> #include <cstring> #include <vector>@@ -7,6 +10,7 @@ #include "sass.h" #include "file.hpp" #include "util.hpp"+#include "context.hpp" #include "sass_context.hpp" #include "sass_functions.hpp" 
libsass/src/sass.hpp view
@@ -14,11 +14,17 @@ // aplies to MSVC and MinGW #ifdef _WIN32 // we do not want the ERROR macro-# define NOGDI+# ifndef NOGDI+#  define NOGDI+# endif // we do not want the min/max macro-# define NOMINMAX+# ifndef NOMINMAX+#  define NOMINMAX+# endif // we do not want the IN/OUT macro-# define _NO_W32_PSEUDO_MODIFIERS+# ifndef _NO_W32_PSEUDO_MODIFIERS+#  define _NO_W32_PSEUDO_MODIFIERS+# endif #endif  @@ -59,6 +65,7 @@   // only used internal to trigger ruby inspect behavior   const static Sass_Output_Style INSPECT = SASS_STYLE_INSPECT;   const static Sass_Output_Style TO_SASS = SASS_STYLE_TO_SASS;+  const static Sass_Output_Style TO_CSS = SASS_STYLE_TO_CSS;    // helper to aid dreaded MSVC debug mode   // see implementation for more details@@ -90,13 +97,10 @@   // Precision for fractional numbers   int precision; -  // Do not compress colors in selectors-  bool in_selector;-   // initialization list (constructor with defaults)   Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED,-                       int precision = 5, bool in_selector = false)-  : output_style(style), precision(precision), in_selector(in_selector)+                       int precision = 10)+  : output_style(style), precision(precision)   { }  };@@ -125,7 +129,7 @@    // initialization list (constructor with defaults)   Sass_Output_Options(Sass_Output_Style style = Sass::NESTED,-                      int precision = 5,+                      int precision = 10,                       const char* indent = "  ",                       const char* linefeed = "\n",                       bool source_comments = false)
libsass/src/sass2scss.cpp view
@@ -154,6 +154,21 @@  	} +	static size_t findFirstCharacter (std::string& sass, size_t pos)+	{+		return sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos);+	}++	static size_t findLastCharacter (std::string& sass, size_t pos)+	{+		return sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, pos);+	}++	static bool isUrl (std::string& sass, size_t pos)+	{+		return sass[pos] == 'u' && sass[pos+1] == 'r' && sass[pos+2] == 'l' && sass[pos+3] == '(';+	}+ 	// check if there is some char data 	// will ignore everything in comments 	static bool hasCharData (std::string& sass)@@ -452,7 +467,7 @@ 		// right trim input 		sass = rtrim(sass); -		// get postion of first meaningfull character in string+		// get position of first meaningfull character in string 		size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);  		// special case for final run@@ -542,7 +557,7 @@ 				// default to a selector 				// change back if property found 				converter.selector = true;-				// get postion of first whitespace char+				// get position of first whitespace char 				size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left); 				// assertion check for valid result 				if (pos_wspace != std::string::npos)@@ -565,7 +580,7 @@ 							// assertion for valid result 							if (pos_colon != std::string::npos) 							{-								// ... after the first word (skip begining colons)+								// ... after the first word (skip beginning colons) 								pos_colon = sass.find_first_of(":", pos_colon); 								// it is a selector if there was no colon found 								converter.selector = pos_colon == std::string::npos;@@ -587,6 +602,7 @@ 				sass.substr(pos_left, 5) == "@warn" || 				sass.substr(pos_left, 6) == "@debug" || 				sass.substr(pos_left, 6) == "@error" ||+				sass.substr(pos_left, 6) == "@value" || 				sass.substr(pos_left, 8) == "@charset" || 				sass.substr(pos_left, 10) == "@namespace" 			) { sass = indent + sass.substr(pos_left); }@@ -606,23 +622,38 @@ 			{ 				// get positions for the actual import url 				size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7);-				size_t pos_quote = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);-				// leave proper urls untouched-				if (sass.substr(pos_quote, 4) != "url(")-				{-					// check if the url appears to be already quoted-					if (sass.substr(pos_quote, 1) != "\"" && sass.substr(pos_quote, 1) != "\'")-					{-						// get position of the last char on the line-						size_t pos_end = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);-						// assertion check for valid result-						if (pos_end != std::string::npos)-						{-							// add quotes around the full line after the import statement-							sass = sass.substr(0, pos_quote) + "\"" + sass.substr(pos_quote, pos_end - pos_quote + 1) + "\"";+				size_t pos = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);+				size_t start = pos;+				bool in_dqstr = false;+				bool in_sqstr = false;+				bool is_escaped = false;+				do {+					if (is_escaped) {+						is_escaped = false;+					}+					else if (sass[pos] == '\\') {+						is_escaped = true;+					}+					else if (sass[pos] == '"') {+						if (!in_sqstr) in_dqstr = ! in_dqstr;+					}+					else if (sass[pos] == '\'') {+						if (!in_dqstr) in_sqstr = ! in_sqstr;+					}+					else if (in_dqstr || in_sqstr) {+						// skip over quoted stuff+					}+					else if (sass[pos] == ',' || sass[pos] == 0) {+						if (sass[start] != '"' && sass[start] != '\'' && !isUrl(sass, start)) {+							size_t end = findLastCharacter(sass, pos - 1) + 1;+							sass = sass.replace(end, 0, "\"");+							sass = sass.replace(start, 0, "\"");+							pos += 2; 						}+						start = findFirstCharacter(sass, pos + 1); 					} 				}+				while (sass[pos++] != 0);  			} 			else if (@@ -727,7 +758,7 @@ 				scss += flush(sass, converter); 			} -			// get postion of last meaningfull char+			// get position of last meaningfull char 			size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);  			// check for invalid result
libsass/src/sass_context.cpp view
@@ -1,20 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <cstring>-#include <stdexcept>-#include <sstream>-#include <string>-#include <vector>--#include "sass.h" #include "ast.hpp"-#include "file.hpp"-#include "json.hpp"-#include "util.hpp"-#include "context.hpp"-#include "sass_context.hpp"+ #include "sass_functions.hpp"-#include "ast_fwd_decl.hpp"-#include "error_handling.hpp"+#include "json.hpp"  #define LFEED "\n" @@ -28,6 +18,24 @@     return json_mkstring(str.c_str());   } +  static void handle_string_error(Sass_Context* c_ctx, const std::string& msg, int severety)+  {+    std::stringstream msg_stream;+    JsonNode* json_err = json_mkobject();+    msg_stream << "Internal Error: " << msg << std::endl;+    json_append_member(json_err, "status", json_mknumber(severety));+    json_append_member(json_err, "message", json_mkstring(msg.c_str()));+    json_append_member(json_err, "formatted", json_mkstream(msg_stream));+    try { c_ctx->error_json = json_stringify(json_err, "  "); }+    catch (...) {}+    c_ctx->error_message = sass_copy_string(msg_stream.str());+    c_ctx->error_text = sass_copy_c_string(msg.c_str());+    c_ctx->error_status = severety;+    c_ctx->output_string = 0;+    c_ctx->source_map_string = 0;+    json_delete(json_err);+  }+   static int handle_error(Sass_Context* c_ctx) {     try {       throw;@@ -67,23 +75,23 @@       }        // now create the code trace (ToDo: maybe have util functions?)-      if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) {+      if (e.pstate.line != std::string::npos &&+          e.pstate.column != std::string::npos &&+          e.pstate.src != nullptr) {         size_t lines = e.pstate.line;-        const char* line_beg = e.pstate.src;         // scan through src until target line         // move line_beg pointer to line start-        while (line_beg && *line_beg && lines != 0) {+        const char* line_beg;+        for (line_beg = e.pstate.src; *line_beg != '\0'; ++line_beg) {+          if (lines == 0) break;           if (*line_beg == '\n') --lines;-          utf8::unchecked::next(line_beg);          }-        const char* line_end = line_beg;         // move line_end before next newline character-        while (line_end && *line_end && *line_end != '\n') {-          if (*line_end == '\n') break;-          if (*line_end == '\r') break;-          utf8::unchecked::next(line_end); +        const char* line_end;+        for (line_end = line_beg; *line_end != '\0'; ++line_end) {+          if (*line_end == '\n' || *line_end == '\r') break;         }-        if (line_end && *line_end != 0) ++ line_end;+        if (*line_end != '\0') ++line_end;         size_t line_len = line_end - line_beg;         size_t move_in = 0; size_t shorten = 0;         size_t left_chars = 42; size_t max_chars = 76;@@ -107,96 +115,34 @@       json_append_member(json_err, "message", json_mkstring(e.what()));       json_append_member(json_err, "formatted", json_mkstream(msg_stream));       try { c_ctx->error_json = json_stringify(json_err, "  "); }-      catch (...) {}+      catch (...) {} // silently ignore this error?       c_ctx->error_message = sass_copy_string(msg_stream.str());       c_ctx->error_text = sass_copy_c_string(e.what());       c_ctx->error_status = 1;       c_ctx->error_file = sass_copy_c_string(e.pstate.path);       c_ctx->error_line = e.pstate.line + 1;       c_ctx->error_column = e.pstate.column + 1;-      c_ctx->error_src = e.pstate.src;+      c_ctx->error_src = sass_copy_c_string(e.pstate.src);       c_ctx->output_string = 0;       c_ctx->source_map_string = 0;       json_delete(json_err);     }     catch (std::bad_alloc& ba) {       std::stringstream msg_stream;-      JsonNode* json_err = json_mkobject();-      msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;-      json_append_member(json_err, "status", json_mknumber(2));-      json_append_member(json_err, "message", json_mkstring(ba.what()));-      json_append_member(json_err, "formatted", json_mkstream(msg_stream));-      try { c_ctx->error_json = json_stringify(json_err, "  "); }-      catch (...) {}-      c_ctx->error_message = sass_copy_string(msg_stream.str());-      c_ctx->error_text = sass_copy_c_string(ba.what());-      c_ctx->error_status = 2;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-      json_delete(json_err);+      msg_stream << "Unable to allocate memory: " << ba.what();+      handle_string_error(c_ctx, msg_stream.str(), 2);     }     catch (std::exception& e) {-      std::stringstream msg_stream;-      JsonNode* json_err = json_mkobject();-      msg_stream << "Internal Error: " << e.what() << std::endl;-      json_append_member(json_err, "status", json_mknumber(3));-      json_append_member(json_err, "message", json_mkstring(e.what()));-      json_append_member(json_err, "formatted", json_mkstream(msg_stream));-      try { c_ctx->error_json = json_stringify(json_err, "  "); }-      catch (...) {}-      c_ctx->error_message = sass_copy_string(msg_stream.str());-      c_ctx->error_text = sass_copy_c_string(e.what());-      c_ctx->error_status = 3;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-      json_delete(json_err);+      handle_string_error(c_ctx, e.what(), 3);     }     catch (std::string& e) {-      std::stringstream msg_stream;-      JsonNode* json_err = json_mkobject();-      msg_stream << "Internal Error: " << e << std::endl;-      json_append_member(json_err, "status", json_mknumber(4));-      json_append_member(json_err, "message", json_mkstring(e.c_str()));-      json_append_member(json_err, "formatted", json_mkstream(msg_stream));-      try { c_ctx->error_json = json_stringify(json_err, "  "); }-      catch (...) {}-      c_ctx->error_message = sass_copy_string(msg_stream.str());-      c_ctx->error_text = sass_copy_c_string(e.c_str());-      c_ctx->error_status = 4;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-      json_delete(json_err);+      handle_string_error(c_ctx, e, 4);     }     catch (const char* e) {-      std::stringstream msg_stream;-      JsonNode* json_err = json_mkobject();-      msg_stream << "Internal Error: " << e << std::endl;-      json_append_member(json_err, "status", json_mknumber(4));-      json_append_member(json_err, "message", json_mkstring(e));-      json_append_member(json_err, "formatted", json_mkstream(msg_stream));-      try { c_ctx->error_json = json_stringify(json_err, "  "); }-      catch (...) {}-      c_ctx->error_message = sass_copy_string(msg_stream.str());-      c_ctx->error_text = sass_copy_c_string(e);-      c_ctx->error_status = 4;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-      json_delete(json_err);+      handle_string_error(c_ctx, e, 4);     }     catch (...) {-      std::stringstream msg_stream;-      JsonNode* json_err = json_mkobject();-      msg_stream << "Unknown error occurred" << std::endl;-      json_append_member(json_err, "status", json_mknumber(5));-      json_append_member(json_err, "message", json_mkstring("unknown"));-      try { c_ctx->error_json = json_stringify(json_err, "  "); }-      catch (...) {}-      c_ctx->error_message = sass_copy_string(msg_stream.str());-      c_ctx->error_text = sass_copy_c_string("unknown");-      c_ctx->error_status = 5;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-      json_delete(json_err);+      handle_string_error(c_ctx, "unknown", 5);     }     return c_ctx->error_status;   }@@ -212,7 +158,7 @@   {      // assert valid pointer-    if (compiler == 0) return 0;+    if (compiler == 0) return {};     // The cpp context must be set by now     Context* cpp_ctx = compiler->cpp_ctx;     Sass_Context* c_ctx = compiler->c_ctx;@@ -233,7 +179,7 @@       // dispatch parse call       Block_Obj root(cpp_ctx->parse());       // abort on errors-      if (!root) return 0;+      if (!root) return {};        // skip all prefixed files? (ToDo: check srcmap)       // IMO source-maps should point to headers already@@ -253,7 +199,7 @@     catch (...) { handle_errors(c_ctx); }      // error-    return 0;+    return {};    } @@ -331,8 +277,8 @@       c_ctx->error_message = 0;       c_ctx->error_status = 0;       // reset error position-      c_ctx->error_src = 0;       c_ctx->error_file = 0;+      c_ctx->error_src = 0;       c_ctx->error_line = std::string::npos;       c_ctx->error_column = std::string::npos; @@ -381,7 +327,7 @@    inline void init_options (struct Sass_Options* options)   {-    options->precision = 5;+    options->precision = 10;     options->indent = "  ";     options->linefeed = LFEED;   }@@ -396,7 +342,9 @@    Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path)   {-    SharedObj::setTaint(true); // needed for static colors+    #ifdef DEBUG_SHARED_PTR+    SharedObj::setTaint(true);+    #endif     struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(1, sizeof(struct Sass_File_Context));     if (ctx == 0) { std::cerr << "Error allocating memory for file context" << std::endl; return 0; }     ctx->type = SASS_CONTEXT_FILE;@@ -413,6 +361,9 @@    Sass_Data_Context* ADDCALL sass_make_data_context(char* source_string)   {+    #ifdef DEBUG_SHARED_PTR+    SharedObj::setTaint(true);+    #endif     struct Sass_Data_Context* ctx = (struct Sass_Data_Context*) calloc(1, sizeof(struct Sass_Data_Context));     if (ctx == 0) { std::cerr << "Error allocating memory for data context" << std::endl; return 0; }     ctx->type = SASS_CONTEXT_DATA;@@ -448,7 +399,7 @@       return data_ctx->error_status;     try {       if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); }-      // empty source string is a valid case, even if not really usefull (different than with file context)+      // empty source string is a valid case, even if not really useful (different than with file context)       // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); }     }     catch (...) { return handle_errors(data_ctx) | 1; }@@ -530,7 +481,7 @@   static void sass_clear_options (struct Sass_Options* options)   {     if (options == 0) return;-    // Deallocate custom functions, headers and importes+    // Deallocate custom functions, headers and imports     sass_delete_function_list(options->c_functions);     sass_delete_importer_list(options->c_importers);     sass_delete_importer_list(options->c_headers);@@ -591,6 +542,7 @@     if (ctx->error_text)        free(ctx->error_text);     if (ctx->error_json)        free(ctx->error_json);     if (ctx->error_file)        free(ctx->error_file);+    if (ctx->error_src)         free(ctx->error_src);     free_string_array(ctx->included_files);     // play safe and reset properties     ctx->output_string = 0;@@ -599,6 +551,7 @@     ctx->error_text = 0;     ctx->error_json = 0;     ctx->error_file = 0;+    ctx->error_src = 0;     ctx->included_files = 0;     // debug leaked memory     #ifdef DEBUG_SHARED_PTR@@ -617,7 +570,7 @@     if (cpp_ctx) delete(cpp_ctx);     compiler->cpp_ctx = NULL;     compiler->c_ctx = NULL;-    compiler->root = NULL;+    compiler->root = {};     free(compiler);   } @@ -653,7 +606,7 @@   void ADDCALL sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); }   void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); } -  // Getters for Sass_Compiler options (get conected sass context)+  // Getters for Sass_Compiler options (get connected sass context)   enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler) { return compiler->state; }   struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; }   struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler) { return compiler->c_ctx; }@@ -697,9 +650,9 @@   IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_message);   IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_text);   IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_file);+  IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src);   IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_line);   IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_column);-  IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src);   IMPLEMENT_SASS_CONTEXT_GETTER(const char*, output_string);   IMPLEMENT_SASS_CONTEXT_GETTER(const char*, source_map_string);   IMPLEMENT_SASS_CONTEXT_GETTER(char**, included_files);@@ -709,6 +662,7 @@   IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_message);   IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_text);   IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_file);+  IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_src);   IMPLEMENT_SASS_CONTEXT_TAKER(char*, output_string);   IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string);   IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files);
libsass/src/sass_context.hpp view
@@ -90,7 +90,7 @@   char* error_file;   size_t error_line;   size_t error_column;-  const char* error_src;+  char* error_src;    // report imported files   char** included_files;
libsass/src/sass_functions.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstring> #include "util.hpp" #include "context.hpp"@@ -98,7 +101,7 @@   }    // Creator for a single import entry returned by the custom importer inside the list-  // We take ownership of the memory for source and srcmap (freed when context is destroyd)+  // We take ownership of the memory for source and srcmap (freed when context is destroyed)   Sass_Import_Entry ADDCALL sass_make_import(const char* imp_path, const char* abs_path, char* source, char* srcmap)   {     Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import));@@ -167,21 +170,21 @@    // Getters and Setters for environments (lexical, local and global)   union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame env, const char* name) {-    Expression_Ptr ex = Cast<Expression>((*env->frame)[name]);+    Expression* ex = Cast<Expression>((*env->frame)[name]);     return ex != NULL ? ast_node_to_sass_value(ex) : NULL;   }   void ADDCALL sass_env_set_lexical (Sass_Env_Frame env, const char* name, union Sass_Value* val) {     (*env->frame)[name] = sass_value_to_ast_node(val);   }   union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame env, const char* name) {-    Expression_Ptr ex = Cast<Expression>(env->frame->get_local(name));+    Expression* ex = Cast<Expression>(env->frame->get_local(name));     return ex != NULL ? ast_node_to_sass_value(ex) : NULL;   }   void ADDCALL sass_env_set_local (Sass_Env_Frame env, const char* name, union Sass_Value* val) {     env->frame->set_local(name, sass_value_to_ast_node(val));   }   union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame env, const char* name) {-    Expression_Ptr ex = Cast<Expression>(env->frame->get_global(name));+    Expression* ex = Cast<Expression>(env->frame->get_global(name));     return ex != NULL ? ast_node_to_sass_value(ex) : NULL;   }   void ADDCALL sass_env_set_global (Sass_Env_Frame env, const char* name, union Sass_Value* val) {
libsass/src/sass_functions.hpp view
@@ -3,7 +3,7 @@  #include "sass.h" #include "environment.hpp"-#include "functions.hpp"+#include "fn_utils.hpp"  // Struct to hold custom function callback struct Sass_Function {
− libsass/src/sass_util.cpp
@@ -1,149 +0,0 @@-#include "sass.hpp"-#include "node.hpp"--namespace Sass {---  /*-    # This is the equivalent of ruby's Sass::Util.paths.-    #-    # Return an array of all possible paths through the given arrays.-    #-    # @param arrs [NodeCollection<NodeCollection<Node>>]-    # @return [NodeCollection<NodeCollection<Node>>]-    #-    # @example-    #   paths([[1, 2], [3, 4], [5]]) #=>-    #     # [[1, 3, 5],-    #     #  [2, 3, 5],-    #     #  [1, 4, 5],-    #     #  [2, 4, 5]]--   The following is the modified version of the ruby code that was more portable to C++. You-   should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.--    def paths(arrs)-        // I changed the inject and maps to an iterative approach to make it easier to implement in C++-      loopStart = [[]]--      for arr in arrs do-        permutations = []-        for e in arr do-          for path in loopStart do-            permutations.push(path + [e])-          end-        end-        loopStart = permutations-      end-    end-  */-  Node paths(const Node& arrs) {--    Node loopStart = Node::createCollection();-    loopStart.collection()->push_back(Node::createCollection());--    for (NodeDeque::iterator arrsIter = arrs.collection()->begin(), arrsEndIter = arrs.collection()->end();-    	arrsIter != arrsEndIter; ++arrsIter) {--      Node& arr = *arrsIter;--      Node permutations = Node::createCollection();--      for (NodeDeque::iterator arrIter = arr.collection()->begin(), arrIterEnd = arr.collection()->end();-      	arrIter != arrIterEnd; ++arrIter) {--        Node& e = *arrIter;--        for (NodeDeque::iterator loopStartIter = loopStart.collection()->begin(), loopStartIterEnd = loopStart.collection()->end();-          loopStartIter != loopStartIterEnd; ++loopStartIter) {--          Node& path = *loopStartIter;--          Node newPermutation = Node::createCollection();-          newPermutation.got_line_feed = arr.got_line_feed;-          newPermutation.plus(path);-          newPermutation.collection()->push_back(e);--          permutations.collection()->push_back(newPermutation);-        }-      }--      loopStart = permutations;-    }--    return loopStart;-  }---  /*-  This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten.-  Sass::Util.flatten requires the number of levels to flatten, while-  [].flatten doesn't and will flatten the entire array. This function-  supports both.--  # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened-  #-  # @param arr [NodeCollection] The array to flatten-  # @param n [int] The number of levels to flatten-  # @return [NodeCollection] The flattened array--  The following is the modified version of the ruby code that was more portable to C++. You-  should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.--  def flatten(arr, n = -1)-    if n != -1 and n == 0 then-      return arr-    end--    flattened = []--    for e in arr do-      if e.is_a?(Array) then-        flattened.concat(flatten(e, n - 1))-      else-        flattened << e-      end-    end--    return flattened-  end-  */-  Node flatten(Node& arr, int n) {-    if (n != -1 && n == 0) {-      return arr;-    }--    Node flattened = Node::createCollection();-    if (arr.got_line_feed) flattened.got_line_feed = true;--    for (NodeDeque::iterator iter = arr.collection()->begin(), iterEnd = arr.collection()->end();-    	iter != iterEnd; iter++) {-    	Node& e = *iter;--      // e has the lf set-      if (e.isCollection()) {--      	// e.collection().got_line_feed = e.got_line_feed;-      	Node recurseFlattened = flatten(e, n - 1);--      	if(e.got_line_feed) {-      		 flattened.got_line_feed = e.got_line_feed;-      	  recurseFlattened.got_line_feed = e.got_line_feed;-      	}--      	for(auto i : (*recurseFlattened.collection())) {-          if (recurseFlattened.got_line_feed) {--            i.got_line_feed = true;-          }-          flattened.collection()->push_back(i);-      	}--      } else {-      	flattened.collection()->push_back(e);-      }-    }--    return flattened;-  }-}
− libsass/src/sass_util.hpp
@@ -1,256 +0,0 @@-#ifndef SASS_SASS_UTIL_H-#define SASS_SASS_UTIL_H--#include "ast.hpp"-#include "node.hpp"-#include "debug.hpp"--namespace Sass {-----  /*-   This is for ports of functions in the Sass:Util module.-   */---  /*-    # Return a Node collection of all possible paths through the given Node collection of Node collections.-    #-    # @param arrs [NodeCollection<NodeCollection<Node>>]-    # @return [NodeCollection<NodeCollection<Node>>]-    #-    # @example-    #   paths([[1, 2], [3, 4], [5]]) #=>-    #     # [[1, 3, 5],-    #     #  [2, 3, 5],-    #     #  [1, 4, 5],-    #     #  [2, 4, 5]]-  */-  Node paths(const Node& arrs);---  /*-  This class is a default implementation of a Node comparator that can be passed to the lcs function below.-  It uses operator== for equality comparision. It then returns one if the Nodes are equal.-  */-  class DefaultLcsComparator {-  public:-    bool operator()(const Node& one, const Node& two, Node& out) const {-      // TODO: Is this the correct C++ interpretation?-      // block ||= proc {|a, b| a == b && a}-      if (one == two) {-        out = one;-        return true;-      }--      return false;-    }-  };---  typedef std::vector<std::vector<int> > LCSTable;---  /*-  This is the equivalent of ruby's Sass::Util.lcs_backtrace.--  # Computes a single longest common subsequence for arrays x and y.-  # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS-  */-  template<typename ComparatorType>-  Node lcs_backtrace(const LCSTable& c, const Node& x, const Node& y, int i, int j, const ComparatorType& comparator) {-    DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)--    if (i == 0 || j == 0) {-      DEBUG_PRINTLN(LCS, "RETURNING EMPTY")-      return Node::createCollection();-    }--    NodeDeque& xChildren = *(x.collection());-    NodeDeque& yChildren = *(y.collection());--    Node compareOut = Node::createNil();-    if (comparator(xChildren[i], yChildren[j], compareOut)) {-      DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")-      Node result = lcs_backtrace(c, x, y, i - 1, j - 1, comparator);-      result.collection()->push_back(compareOut);-      return result;-    }--    if (c[i][j - 1] > c[i - 1][j]) {-      DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")-      return lcs_backtrace(c, x, y, i, j - 1, comparator);-    }--    DEBUG_PRINTLN(LCS, "FINAL RETURN")-    return lcs_backtrace(c, x, y, i - 1, j, comparator);-  }---  /*-  This is the equivalent of ruby's Sass::Util.lcs_table.--  # Calculates the memoization table for the Least Common Subsequence algorithm.-  # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS-  */-  template<typename ComparatorType>-  void lcs_table(const Node& x, const Node& y, const ComparatorType& comparator, LCSTable& out) {-    DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)--    NodeDeque& xChildren = *(x.collection());-    NodeDeque& yChildren = *(y.collection());--    LCSTable c(xChildren.size(), std::vector<int>(yChildren.size()));--    // These shouldn't be necessary since the vector will be initialized to 0 already.-    // x.size.times {|i| c[i][0] = 0}-    // y.size.times {|j| c[0][j] = 0}--    for (size_t i = 1; i < xChildren.size(); i++) {-      for (size_t j = 1; j < yChildren.size(); j++) {-        Node compareOut = Node::createNil();--        if (comparator(xChildren[i], yChildren[j], compareOut)) {-          c[i][j] = c[i - 1][j - 1] + 1;-        } else {-          c[i][j] = std::max(c[i][j - 1], c[i - 1][j]);-        }-      }-    }--    out = c;-  }---  /*-  This is the equivalent of ruby's Sass::Util.lcs.--  # Computes a single longest common subsequence for `x` and `y`.-  # If there are more than one longest common subsequences,-  # the one returned is that which starts first in `x`.--  # @param x [NodeCollection]-  # @param y [NodeCollection]-  # @comparator An equality check between elements of `x` and `y`.-  # @return [NodeCollection] The LCS--  http://en.wikipedia.org/wiki/Longest_common_subsequence_problem-  */-  template<typename ComparatorType>-  Node lcs(Node& x, Node& y, const ComparatorType& comparator) {-    DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)--    Node newX = Node::createCollection();-    newX.collection()->push_back(Node::createNil());-    newX.plus(x);--    Node newY = Node::createCollection();-    newY.collection()->push_back(Node::createNil());-    newY.plus(y);--    LCSTable table;-    lcs_table(newX, newY, comparator, table);--    return lcs_backtrace(table, newX, newY, static_cast<int>(newX.collection()->size()) - 1, static_cast<int>(newY.collection()->size()) - 1, comparator);-  }---  /*-  This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten.-  Sass::Util.flatten requires the number of levels to flatten, while-  [].flatten doesn't and will flatten the entire array. This function-  supports both.--  # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened-  #-  # @param arr [NodeCollection] The array to flatten-  # @param n [int] The number of levels to flatten-  # @return [NodeCollection] The flattened array-  */-  Node flatten(Node& arr, int n = -1);---  /*-  This is the equivalent of ruby's Sass::Util.group_by_to_a.--  # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed-  # order. Unlike [#hash_to_a], the resulting order isn't sorted key order;-  # instead, it's the same order as `#group_by` has under Ruby 1.9 (key-  # appearance order).-  #-  # @param enum [Enumerable]-  # @return [Array<[Object, Array]>] An array of pairs.--  TODO: update @param and @return once I know what those are.--  The following is the modified version of the ruby code that was more portable to C++. You-  should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.--    def group_by_to_a(enum, &block)-      order = {}--      arr = []--      grouped = {}--      for e in enum do-        key = block[e]-        unless order.include?(key)-          order[key] = order.size-        end--        if not grouped.has_key?(key) then-          grouped[key] = [e]-        else-          grouped[key].push(e)-        end-      end--      grouped.each do |key, vals|-        arr[order[key]] = [key, vals]-      end--      arr-    end--  */-  template<typename EnumType, typename KeyType, typename KeyFunctorType>-  void group_by_to_a(std::vector<EnumType>& enumeration, KeyFunctorType& keyFunc, std::vector<std::pair<KeyType, std::vector<EnumType> > >& arr /*out*/) {--    std::map<unsigned int, KeyType> order;--    std::map<size_t, std::vector<EnumType> > grouped;--    for (typename std::vector<EnumType>::iterator enumIter = enumeration.begin(), enumIterEnd = enumeration.end(); enumIter != enumIterEnd; enumIter++) {-      EnumType& e = *enumIter;--      KeyType key = keyFunc(e);--      if (grouped.find(key->hash()) == grouped.end()) {-        order.insert(std::make_pair((unsigned int)order.size(), key));--        std::vector<EnumType> newCollection;-        newCollection.push_back(e);-        grouped.insert(std::make_pair(key->hash(), newCollection));-      } else {-        std::vector<EnumType>& collection = grouped.at(key->hash());-        collection.push_back(e);-      }-    }--    for (unsigned int index = 0; index < order.size(); index++) {-      KeyType& key = order.at(index);-      std::vector<EnumType>& values = grouped.at(key->hash());--      std::pair<KeyType, std::vector<EnumType> > grouping = std::make_pair(key, values);--      arr.push_back(grouping);-    }-  }---}--#endif
libsass/src/sass_values.cpp view
@@ -1,9 +1,11 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstdlib> #include <cstring> #include "util.hpp" #include "eval.hpp"-#include "values.hpp" #include "operators.hpp" #include "sass/values.h" #include "sass_values.hpp"@@ -290,7 +292,7 @@   union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b)   { -    Sass::Value_Ptr rv;+    Sass::Value_Obj rv;      try { @@ -312,28 +314,34 @@       }        if (sass_value_is_number(a) && sass_value_is_number(b)) {-        Number_Ptr_Const l_n = Cast<Number>(lhs);-        Number_Ptr_Const r_n = Cast<Number>(rhs);+        const Number* l_n = Cast<Number>(lhs);+        const Number* r_n = Cast<Number>(rhs);         rv = Operators::op_numbers(op, *l_n, *r_n, options, l_n->pstate());       }       else if (sass_value_is_number(a) && sass_value_is_color(a)) {-        Number_Ptr_Const l_n = Cast<Number>(lhs);-        Color_Ptr_Const r_c = Cast<Color>(rhs);+        const Number* l_n = Cast<Number>(lhs);+        // Direct HSLA operations are not supported+        // All color maths will be deprecated anyway+        Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA();         rv = Operators::op_number_color(op, *l_n, *r_c, options, l_n->pstate());       }       else if (sass_value_is_color(a) && sass_value_is_number(b)) {-        Color_Ptr_Const l_c = Cast<Color>(lhs);-        Number_Ptr_Const r_n = Cast<Number>(rhs);+        // Direct HSLA operations are not supported+        // All color maths will be deprecated anyway+        Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+        const Number* r_n = Cast<Number>(rhs);         rv = Operators::op_color_number(op, *l_c, *r_n, options, l_c->pstate());       }       else if (sass_value_is_color(a) && sass_value_is_color(b)) {-        Color_Ptr_Const l_c = Cast<Color>(lhs);-        Color_Ptr_Const r_c = Cast<Color>(rhs);+        // Direct HSLA operations are not supported+        // All color maths will be deprecated anyway+        Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+        Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA();         rv = Operators::op_colors(op, *l_c, *r_c, options, l_c->pstate());       }       else /* convert other stuff to string and apply operation */ {-        Value_Ptr l_v = Cast<Value>(lhs);-        Value_Ptr r_v = Cast<Value>(rhs);+        Value* l_v = Cast<Value>(lhs);+        Value* r_v = Cast<Value>(rhs);         rv = Operators::op_strings(op, *l_v, *r_v, options, l_v->pstate());       } @@ -341,8 +349,7 @@       if (!rv) return sass_make_error("invalid return value");        // convert result back to ast node-      return ast_node_to_sass_value(rv);-+      return ast_node_to_sass_value(rv.ptr());     }      // simply pass the error message back to the caller for now
libsass/src/source_map.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <string> #include <sstream> #include <iostream>@@ -170,12 +173,12 @@     current_position += offset;   } -  void SourceMap::add_open_mapping(const AST_Node_Ptr node)+  void SourceMap::add_open_mapping(const AST_Node* node)   {     mappings.push_back(Mapping(node->pstate(), current_position));   } -  void SourceMap::add_close_mapping(const AST_Node_Ptr node)+  void SourceMap::add_close_mapping(const AST_Node* node)   {     mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position));   }
libsass/src/source_map.hpp view
@@ -28,8 +28,8 @@     void prepend(const Offset& offset);     void append(const OutputBuffer& out);     void prepend(const OutputBuffer& out);-    void add_open_mapping(const AST_Node_Ptr node);-    void add_close_mapping(const AST_Node_Ptr node);+    void add_open_mapping(const AST_Node* node);+    void add_close_mapping(const AST_Node* node);      std::string render_srcmap(Context &ctx);     ParserState remap(const ParserState& pstate);
+ libsass/src/stylesheet.cpp view
@@ -0,0 +1,22 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "stylesheet.hpp"++namespace Sass {++  // Constructor+  Sass::StyleSheet::StyleSheet(const Resource& res, Block_Obj root) :+    Resource(res),+    root(root)+  {+  }++  StyleSheet::StyleSheet(const StyleSheet& sheet) :+    Resource(sheet),+    root(sheet.root)+  {+  }++}
+ libsass/src/stylesheet.hpp view
@@ -0,0 +1,57 @@+#ifndef SASS_STYLESHEET_H+#define SASS_STYLESHEET_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast_fwd_decl.hpp"+#include "extender.hpp"+#include "file.hpp"++namespace Sass {++  // parsed stylesheet from loaded resource+  // this should be a `Module` for sass 4.0+  class StyleSheet : public Resource {+    public:++      // The canonical URL for this module's source file. This may be `null`+      // if the module was loaded from a string without a URL provided.+      // Uri get url;++      // Modules that this module uses.+      // List<Module> get upstream;++      // The module's variables.+      // Map<String, Value> get variables;++      // The module's functions. Implementations must ensure+      // that each [Callable] is stored under its own name.+      // Map<String, Callable> get functions;++      // The module's mixins. Implementations must ensure that+      // each [Callable] is stored under its own name.+      // Map<String, Callable> get mixins;++      // The extensions defined in this module, which is also able to update+      // [css]'s style rules in-place based on downstream extensions.+      // Extender extender;++      // The module's CSS tree.+      Block_Obj root;++    public:++      // default argument constructor+      StyleSheet(const Resource& res, Block_Obj root);++      // Copy constructor+      StyleSheet(const StyleSheet& res);++  };+++}++#endif
− libsass/src/subset_map.cpp
@@ -1,55 +0,0 @@-#include "sass.hpp"-#include "ast.hpp"-#include "subset_map.hpp"--namespace Sass {--  void Subset_Map::put(const Compound_Selector_Obj& sel, const SubSetMapPair& value)-  {-    if (sel->empty()) throw std::runtime_error("internal error: subset map keys may not be empty");-    size_t index = values_.size();-    values_.push_back(value);-    for (size_t i = 0, S = sel->length(); i < S; ++i)-    {-      hash_[(*sel)[i]].push_back(std::make_pair(sel, index));-    }-  }--  std::vector<SubSetMapPair> Subset_Map::get_kv(const Compound_Selector_Obj& sel)-  {-    SimpleSelectorDict dict(sel->begin(), sel->end()); // XXX Set-    std::vector<size_t> indices;-    for (size_t i = 0, S = sel->length(); i < S; ++i) {-      if (!hash_.count((*sel)[i])) {-        continue;-      }-      const std::vector<std::pair<Compound_Selector_Obj, size_t> >& subsets = hash_[(*sel)[i]];-      for (const std::pair<Compound_Selector_Obj, size_t>& item : subsets) {-        bool include = true;-        for (const Simple_Selector_Obj& it : item.first->elements()) {-          auto found = dict.find(it);-          if (found == dict.end()) {-            include = false;-            break;-          }-        }-        if (include) indices.push_back(item.second);-      }-    }-    sort(indices.begin(), indices.end());-    std::vector<size_t>::iterator indices_end = unique(indices.begin(), indices.end());-    indices.resize(distance(indices.begin(), indices_end));--    std::vector<SubSetMapPair> results;-    for (size_t i = 0, S = indices.size(); i < S; ++i) {-      results.push_back(values_[indices[i]]);-    }-    return results;-  }--  std::vector<SubSetMapPair> Subset_Map::get_v(const Compound_Selector_Obj& sel)-  {-    return get_kv(sel);-  }--}
− libsass/src/subset_map.hpp
@@ -1,76 +0,0 @@-#ifndef SASS_SUBSET_MAP_H-#define SASS_SUBSET_MAP_H--#include <map>-#include <set>-#include <vector>-#include <algorithm>-#include <iterator>--#include "ast_fwd_decl.hpp"---// #include <iostream>-// #include <sstream>-// template<typename T>-// std::string vector_to_string(std::vector<T> v)-// {-//   std::stringstream buffer;-//   buffer << "[";--//   if (!v.empty())-//   {  buffer << v[0]; }-//   else-//   { buffer << "]"; }--//   if (v.size() == 1)-//   { buffer << "]"; }-//   else-//   {-//     for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i];-//     buffer << "]";-//   }--//   return buffer.str();-// }--// template<typename T>-// std::string set_to_string(set<T> v)-// {-//   std::stringstream buffer;-//   buffer << "[";-//   typename std::set<T>::iterator i = v.begin();-//   if (!v.empty())-//   {  buffer << *i; }-//   else-//   { buffer << "]"; }--//   if (v.size() == 1)-//   { buffer << "]"; }-//   else-//   {-//     for (++i; i != v.end(); ++i) buffer << ", " << *i;-//     buffer << "]";-//   }--//   return buffer.str();-// }--namespace Sass {--  class Subset_Map {-  private:-    std::vector<SubSetMapPair> values_;-    std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> >, OrderNodes > hash_;-  public:-    void put(const Compound_Selector_Obj& sel, const SubSetMapPair& value);-    std::vector<SubSetMapPair> get_kv(const Compound_Selector_Obj& s);-    std::vector<SubSetMapPair> get_v(const Compound_Selector_Obj& s);-    bool empty() { return values_.empty(); }-    void clear() { values_.clear(); hash_.clear(); }-    const std::vector<SubSetMapPair> values(void) { return values_; }-  };--}--#endif
− libsass/src/to_c.cpp
@@ -1,74 +0,0 @@-#include "sass.hpp"-#include "to_c.hpp"-#include "ast.hpp"--namespace Sass {--  union Sass_Value* To_C::fallback_impl(AST_Node_Ptr n)-  { return sass_make_error("unknown type for C-API"); }--  union Sass_Value* To_C::operator()(Boolean_Ptr b)-  { return sass_make_boolean(b->value()); }--  union Sass_Value* To_C::operator()(Number_Ptr n)-  { return sass_make_number(n->value(), n->unit().c_str()); }--  union Sass_Value* To_C::operator()(Custom_Warning_Ptr w)-  { return sass_make_warning(w->message().c_str()); }--  union Sass_Value* To_C::operator()(Custom_Error_Ptr e)-  { return sass_make_error(e->message().c_str()); }--  union Sass_Value* To_C::operator()(Color_Ptr c)-  { return sass_make_color(c->r(), c->g(), c->b(), c->a()); }--  union Sass_Value* To_C::operator()(String_Constant_Ptr s)-  {-    if (s->quote_mark()) {-      return sass_make_qstring(s->value().c_str());-    } else {-      return sass_make_string(s->value().c_str());-    }-  }--  union Sass_Value* To_C::operator()(String_Quoted_Ptr s)-  { return sass_make_qstring(s->value().c_str()); }--  union Sass_Value* To_C::operator()(List_Ptr l)-  {-    union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed());-    for (size_t i = 0, L = l->length(); i < L; ++i) {-      sass_list_set_value(v, i, (*l)[i]->perform(this));-    }-    return v;-  }--  union Sass_Value* To_C::operator()(Map_Ptr m)-  {-    union Sass_Value* v = sass_make_map(m->length());-    int i = 0;-    for (auto key : m->keys()) {-      sass_map_set_key(v, i, key->perform(this));-      sass_map_set_value(v, i, m->at(key)->perform(this));-      i++;-    }-    return v;-  }--  union Sass_Value* To_C::operator()(Arguments_Ptr a)-  {-    union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false);-    for (size_t i = 0, L = a->length(); i < L; ++i) {-      sass_list_set_value(v, i, (*a)[i]->perform(this));-    }-    return v;-  }--  union Sass_Value* To_C::operator()(Argument_Ptr a)-  { return a->value()->perform(this); }--  // not strictly necessary because of the fallback-  union Sass_Value* To_C::operator()(Null_Ptr n)-  { return sass_make_null(); }--};
− libsass/src/to_c.hpp
@@ -1,39 +0,0 @@-#ifndef SASS_TO_C_H-#define SASS_TO_C_H--#include "ast_fwd_decl.hpp"-#include "operation.hpp"-#include "sass/values.h"--namespace Sass {--  class To_C : public Operation_CRTP<union Sass_Value*, To_C> {-    // override this to define a catch-all-    union Sass_Value* fallback_impl(AST_Node_Ptr n);--  public:--    To_C() { }-    ~To_C() { }--    union Sass_Value* operator()(Boolean_Ptr);-    union Sass_Value* operator()(Number_Ptr);-    union Sass_Value* operator()(Color_Ptr);-    union Sass_Value* operator()(String_Constant_Ptr);-    union Sass_Value* operator()(String_Quoted_Ptr);-    union Sass_Value* operator()(Custom_Warning_Ptr);-    union Sass_Value* operator()(Custom_Error_Ptr);-    union Sass_Value* operator()(List_Ptr);-    union Sass_Value* operator()(Map_Ptr);-    union Sass_Value* operator()(Null_Ptr);-    union Sass_Value* operator()(Arguments_Ptr);-    union Sass_Value* operator()(Argument_Ptr);--    // dispatch to fallback implementation-    union Sass_Value* fallback(AST_Node_Ptr x)-    { return fallback_impl(x); }-  };--}--#endif
libsass/src/to_value.cpp view
@@ -1,60 +1,62 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "to_value.hpp"  namespace Sass { -  Value_Ptr To_Value::fallback_impl(AST_Node_Ptr n)-  {-    // throw a runtime error if this happens-    // we want a well defined set of possible nodes-    throw std::runtime_error("invalid node for to_value");-  }-   // Custom_Error is a valid value-  Value_Ptr To_Value::operator()(Custom_Error_Ptr e)+  Value* To_Value::operator()(Custom_Error* e)   {     return e;   }    // Custom_Warning is a valid value-  Value_Ptr To_Value::operator()(Custom_Warning_Ptr w)+  Value* To_Value::operator()(Custom_Warning* w)   {     return w;   }    // Boolean is a valid value-  Value_Ptr To_Value::operator()(Boolean_Ptr b)+  Value* To_Value::operator()(Boolean* b)   {     return b;   }    // Number is a valid value-  Value_Ptr To_Value::operator()(Number_Ptr n)+  Value* To_Value::operator()(Number* n)   {     return n;   }    // Color is a valid value-  Value_Ptr To_Value::operator()(Color_Ptr c)+  Value* To_Value::operator()(Color_RGBA* c)   {     return c;   } +  // Color is a valid value+  Value* To_Value::operator()(Color_HSLA* c)+  {+    return c;+  }+   // String_Constant is a valid value-  Value_Ptr To_Value::operator()(String_Constant_Ptr s)+  Value* To_Value::operator()(String_Constant* s)   {     return s;   }    // String_Quoted is a valid value-  Value_Ptr To_Value::operator()(String_Quoted_Ptr s)+  Value* To_Value::operator()(String_Quoted* s)   {     return s;   }    // List is a valid value-  Value_Ptr To_Value::operator()(List_Ptr l)+  Value* To_Value::operator()(List* l)   {     List_Obj ll = SASS_MEMORY_NEW(List,                                l->pstate(),@@ -69,32 +71,32 @@   }    // Map is a valid value-  Value_Ptr To_Value::operator()(Map_Ptr m)+  Value* To_Value::operator()(Map* m)   {     return m;   }    // Null is a valid value-  Value_Ptr To_Value::operator()(Null_Ptr n)+  Value* To_Value::operator()(Null* n)   {     return n;   }    // Function is a valid value-  Value_Ptr To_Value::operator()(Function_Ptr n)+  Value* To_Value::operator()(Function* n)   {     return n;   }    // Argument returns its value-  Value_Ptr To_Value::operator()(Argument_Ptr arg)+  Value* To_Value::operator()(Argument* arg)   {     if (!arg->name().empty()) return 0;     return arg->value()->perform(this);   } -  // Selector_List is converted to a string-  Value_Ptr To_Value::operator()(Selector_List_Ptr s)+  // SelectorList is converted to a string+  Value* To_Value::operator()(SelectorList* s)   {     return SASS_MEMORY_NEW(String_Quoted,                            s->pstate(),@@ -102,7 +104,7 @@   }    // Binary_Expression is converted to a string-  Value_Ptr To_Value::operator()(Binary_Expression_Ptr s)+  Value* To_Value::operator()(Binary_Expression* s)   {     return SASS_MEMORY_NEW(String_Quoted,                            s->pstate(),
libsass/src/to_value.hpp view
@@ -7,9 +7,7 @@  namespace Sass { -  class To_Value : public Operation_CRTP<Value_Ptr, To_Value> {--    Value_Ptr fallback_impl(AST_Node_Ptr n);+  class To_Value : public Operation_CRTP<Value*, To_Value> {    private: @@ -21,28 +19,26 @@     : ctx(ctx)     { }     ~To_Value() { }-    using Operation<Value_Ptr>::operator();+    using Operation<Value*>::operator(); -    Value_Ptr operator()(Argument_Ptr);-    Value_Ptr operator()(Boolean_Ptr);-    Value_Ptr operator()(Number_Ptr);-    Value_Ptr operator()(Color_Ptr);-    Value_Ptr operator()(String_Constant_Ptr);-    Value_Ptr operator()(String_Quoted_Ptr);-    Value_Ptr operator()(Custom_Warning_Ptr);-    Value_Ptr operator()(Custom_Error_Ptr);-    Value_Ptr operator()(List_Ptr);-    Value_Ptr operator()(Map_Ptr);-    Value_Ptr operator()(Null_Ptr);-    Value_Ptr operator()(Function_Ptr);+    Value* operator()(Argument*);+    Value* operator()(Boolean*);+    Value* operator()(Number*);+    Value* operator()(Color_RGBA*);+    Value* operator()(Color_HSLA*);+    Value* operator()(String_Constant*);+    Value* operator()(String_Quoted*);+    Value* operator()(Custom_Warning*);+    Value* operator()(Custom_Error*);+    Value* operator()(List*);+    Value* operator()(Map*);+    Value* operator()(Null*);+    Value* operator()(Function*);      // convert to string via `To_String`-    Value_Ptr operator()(Selector_List_Ptr);-    Value_Ptr operator()(Binary_Expression_Ptr);+    Value* operator()(SelectorList*);+    Value* operator()(Binary_Expression*); -    // fallback throws error-    template <typename U>-    Value_Ptr fallback(U x) { return fallback_impl(x); }   };  }
libsass/src/units.cpp view
@@ -1,5 +1,7 @@ #include "sass.hpp"+#include <map> #include <stdexcept>+#include <algorithm> #include "units.hpp" #include "error_handling.hpp" @@ -266,6 +268,10 @@     return (numerators == rhs.numerators) &&            (denominators == rhs.denominators);   }+  bool Units::operator!= (const Units& rhs) const+  {+    return ! (*this == rhs);+  }    double Units::normalize()   {@@ -324,7 +330,7 @@     // has the advantage that they will be pre-sorted     std::map<std::string, int> exponents; -    // initialize by summing up occurences in unit vectors+    // initialize by summing up occurrences in unit vectors     // this will already cancel out equivalent units (e.q. px/px)     for (size_t i = 0; i < iL; i ++) exponents[numerators[i]] += 1;     for (size_t n = 0; n < nL; n ++) exponents[denominators[n]] -= 1;@@ -390,7 +396,7 @@            denominators.size() == 0;   } -  // this does not cover all cases (multiple prefered units)+  // this does not cover all cases (multiple preferred units)   double Units::convert_factor(const Units& r) const   { @@ -461,7 +467,7 @@       {         // get and increment afterwards         const std::string r_den = *(r_den_it);-        // get possible converstion factor for units+        // get possible conversion factor for units         double conversion = conversion_factor(l_den, r_den);         // skip incompatible denominator         if (conversion == 0) {
libsass/src/units.hpp view
@@ -83,6 +83,7 @@     // 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;   };
libsass/src/utf8/checked.h view
@@ -1,4 +1,4 @@-// Copyright 2006 Nemanja Trifunovic+// Copyright 2006-2016 Nemanja Trifunovic  /* Permission is hereby granted, free of charge, to any person or organization@@ -41,7 +41,7 @@     class invalid_code_point : public exception {         uint32_t cp;     public:-        invalid_code_point(uint32_t cp) : cp(cp) {}+        invalid_code_point(uint32_t codepoint) : cp(codepoint) {}         virtual const char* what() const throw() { return "Invalid code point"; }         uint32_t code_point() const {return cp;}     };@@ -107,7 +107,9 @@                         *out++ = *it;                     break;                 case internal::NOT_ENOUGH_ROOM:-                    throw not_enough_room();+                    out = utf8::append (replacement, out);+                    start = end;+                    break;                 case internal::INVALID_LEAD:                     out = utf8::append (replacement, out);                     ++start;@@ -194,10 +196,10 @@     }      template <typename octet_iterator, typename distance_type>-    void retreat (octet_iterator& it, distance_type n, octet_iterator start)+    void retreat (octet_iterator& it, distance_type n, octet_iterator end)     {         for (distance_type i = 0; i < n; ++i)-            utf8::prior(it, start);+            utf8::prior(it, end);     }      template <typename octet_iterator>@@ -240,7 +242,7 @@     template <typename u16bit_iterator, typename octet_iterator>     u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)     {-        while (start != end) {+        while (start < end) {             uint32_t cp = utf8::next(start, end);             if (cp > 0xffff) { //make a surrogate pair                 *result++ = static_cast<uint16_t>((cp >> 10)   + internal::LEAD_OFFSET);@@ -264,7 +266,7 @@     template <typename octet_iterator, typename u32bit_iterator>     u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)     {-        while (start != end)+        while (start < end)             (*result++) = utf8::next(start, end);          return result;@@ -279,9 +281,9 @@       public:       iterator () {}       explicit iterator (const octet_iterator& octet_it,-                         const octet_iterator& range_start,-                         const octet_iterator& range_end) :-               it(octet_it), range_start(range_start), range_end(range_end)+                         const octet_iterator& rangestart,+                         const octet_iterator& rangeend) :+               it(octet_it), range_start(rangestart), range_end(rangeend)       {           if (it < range_start || it > range_end)               throw std::out_of_range("Invalid utf-8 iterator position");
libsass/src/utf8/core.h view
@@ -222,6 +222,9 @@     template <typename octet_iterator>     utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)     {+        if (it == end)+            return NOT_ENOUGH_ROOM;+         // Save the original value of it so we can go back in case of failure         // Of course, it does not make much sense with i.e. stream iterators         octet_iterator original_it = it;
libsass/src/utf8_string.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <string> #include <vector> #include <cstdlib>
libsass/src/util.cpp view
@@ -2,6 +2,7 @@ #include "sass.h" #include "ast.hpp" #include "util.hpp"+#include "util_string.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "constants.hpp"@@ -26,8 +27,8 @@     #endif      // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93-    if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);-    else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val);+    if (std::fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);+    else if (std::fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val);     // work around some compiler issue     // cygwin has it not defined in std     using namespace std;@@ -163,42 +164,64 @@     std::replace(str.begin(), str.end(), '\n', ' ');   } -  // bell characters are replaced with spaces-  // also eats spaces after line-feeds (ltrim)+  // 1. Removes whitespace after newlines.+  // 2. Replaces newlines with spaces.+  //+  // This method only considers LF and CRLF as newlines.   std::string string_to_output(const std::string& str)   {-    std::string out("");-    bool lf = false;-    for (auto i : str) {-      if (i == '\n') {-        out += ' ';-        lf = true;-      } else if (!(lf && isspace(i))) {-        out += i;-        lf = false;+    std::string result;+    result.reserve(str.size());+    std::size_t pos = 0;+    while (true) {+      const std::size_t newline = str.find_first_of("\n\r", pos);+      if (newline == std::string::npos) break;+      result.append(str, pos, newline - pos);+      if (str[newline] == '\r') {+        if (str[newline + 1] == '\n') {+          pos = newline + 2;+        } else {+          // CR without LF: append as-is and continue.+          result += '\r';+          pos = newline + 1;+          continue;+        }+      } else {+        pos = newline + 1;       }+      result += ' ';+      const std::size_t non_space = str.find_first_not_of(" \f\n\r\t\v", pos);+      if (non_space != std::string::npos) {+        pos = non_space;+      }     }-    return out;+    result.append(str, pos, std::string::npos);+    return result;   }    std::string escape_string(const std::string& str)   {-    std::string out("");-    for (auto i : str) {-      if (i == '\n') {-        out += "\\n";-      } else if (i == '\r') {-        out += "\\r";-      } else if (i == '\t') {-        out += "\\t";-      } else {-        out += i;+    std::string out;+    out.reserve(str.size());+    for (char c : str) {+      switch (c) {+        case '\n':+          out.append("\\n");+          break;+        case '\r':+          out.append("\\r");+          break;+        case '\f':+          out.append("\\f");+          break;+        default:+          out += c;       }     }     return out;   } -  std::string comment_to_string(const std::string& text)+  std::string comment_to_compact_string(const std::string& text)   {     std::string str = "";     size_t has = 0;@@ -207,7 +230,6 @@     for (auto i : text) {       if (clean) {         if (i == '\n') { has = 0; }-        else if (i == '\r') { has = 0; }         else if (i == '\t') { ++ has; }         else if (i == ' ') { ++ has; }         else if (i == '*') {}@@ -219,8 +241,6 @@         }       } else if (i == '\n') {         clean = true;-      } else if (i == '\r') {-        clean = true;       } else {         str += i;       }@@ -270,7 +290,7 @@          // parse as many sequence chars as possible         // ToDo: Check if ruby aborts after possible max-        while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;+        while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len;          if (len > 1) { @@ -356,7 +376,7 @@          // parse as many sequence chars as possible         // ToDo: Check if ruby aborts after possible max-        while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;+        while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len;          // hex string?         if (keep_utf8_sequences) {@@ -508,42 +528,15 @@   }    namespace Util {-    using std::string; -    std::string rtrim(const std::string &str) {-      std::string trimmed = str;-      size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");-      if (pos_ws != std::string::npos)-      { trimmed.erase(pos_ws + 1); }-      else { trimmed.clear(); }-      return trimmed;-    }--    std::string normalize_underscores(const std::string& str) {-      std::string normalized = str;-      for(size_t i = 0, L = normalized.length(); i < L; ++i) {-        if(normalized[i] == '_') {-          normalized[i] = '-';-        }-      }-      return normalized;-    }--    std::string normalize_decimals(const std::string& str) {-      std::string prefix = "0";-      std::string normalized = str;--      return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;-    }--    bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style) {+    bool isPrintable(Ruleset* r, Sass_Output_Style style) {       if (r == NULL) {         return false;       }        Block_Obj b = r->block(); -      Selector_List_Ptr sl = Cast<Selector_List>(r->selector());+      SelectorList* sl = r->selector();       bool hasSelectors = sl ? sl->length() > 0 : false;        if (!hasSelectors) {@@ -556,14 +549,14 @@         Statement_Obj stm = b->at(i);         if (Cast<Directive>(stm)) {           return true;-        } else if (Declaration_Ptr d = Cast<Declaration>(stm)) {+        } else if (Declaration* d = Cast<Declaration>(stm)) {           return isPrintable(d, style);-        } else if (Has_Block_Ptr p = Cast<Has_Block>(stm)) {+        } else if (Has_Block* p = Cast<Has_Block>(stm)) {           Block_Obj pChildBlock = p->block();           if (isPrintable(pChildBlock, style)) {             hasPrintableChildBlocks = true;           }-        } else if (Comment_Ptr c = Cast<Comment>(stm)) {+        } else if (Comment* c = Cast<Comment>(stm)) {           // keep for uncompressed           if (style != COMPRESSED) {             hasDeclarations = true;@@ -584,17 +577,17 @@       return false;     } -    bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style)+    bool isPrintable(String_Constant* s, Sass_Output_Style style)     {       return ! s->value().empty();     } -    bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style)+    bool isPrintable(String_Quoted* s, Sass_Output_Style style)     {       return true;     } -    bool isPrintable(Declaration_Ptr d, Sass_Output_Style style)+    bool isPrintable(Declaration* d, Sass_Output_Style style)     {       Expression_Obj val = d->value();       if (String_Quoted_Obj sq = Cast<String_Quoted>(val)) return isPrintable(sq.ptr(), style);@@ -602,7 +595,7 @@       return true;     } -    bool isPrintable(Supports_Block_Ptr f, Sass_Output_Style style) {+    bool isPrintable(Supports_Block* f, Sass_Output_Style style) {       if (f == NULL) {         return false;       }@@ -616,7 +609,7 @@         if (Cast<Declaration>(stm) || Cast<Directive>(stm)) {           hasDeclarations = true;         }-        else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+        else if (Has_Block* b = Cast<Has_Block>(stm)) {           Block_Obj pChildBlock = b->block();           if (!b->is_invisible()) {             if (isPrintable(pChildBlock, style)) {@@ -633,36 +626,37 @@       return false;     } -    bool isPrintable(Media_Block_Ptr m, Sass_Output_Style style)+    bool isPrintable(CssMediaRule* m, Sass_Output_Style style)     {-      if (m == 0) return false;+      if (m == nullptr) return false;       Block_Obj b = m->block();-      if (b == 0) return false;+      if (b == nullptr) return false;+      if (m->empty()) return false;       for (size_t i = 0, L = b->length(); i < L; ++i) {         Statement_Obj stm = b->at(i);         if (Cast<Directive>(stm)) return true;         else if (Cast<Declaration>(stm)) return true;-        else if (Comment_Ptr c = Cast<Comment>(stm)) {+        else if (Comment* c = Cast<Comment>(stm)) {           if (isPrintable(c, style)) {             return true;           }         }-        else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) {+        else if (Ruleset* r = Cast<Ruleset>(stm)) {           if (isPrintable(r, style)) {             return true;           }         }-        else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) {+        else if (Supports_Block* f = Cast<Supports_Block>(stm)) {           if (isPrintable(f, style)) {             return true;           }         }-        else if (Media_Block_Ptr mb = Cast<Media_Block>(stm)) {+        else if (CssMediaRule* mb = Cast<CssMediaRule>(stm)) {           if (isPrintable(mb, style)) {             return true;           }         }-        else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+        else if (Has_Block* b = Cast<Has_Block>(stm)) {           if (isPrintable(b->block(), style)) {             return true;           }@@ -671,7 +665,7 @@       return false;     } -    bool isPrintable(Comment_Ptr c, Sass_Output_Style style)+    bool isPrintable(Comment* c, Sass_Output_Style style)     {       // keep for uncompressed       if (style != COMPRESSED) {@@ -695,27 +689,27 @@         if (Cast<Declaration>(stm) || Cast<Directive>(stm)) {           return true;         }-        else if (Comment_Ptr c = Cast<Comment>(stm)) {+        else if (Comment* c = Cast<Comment>(stm)) {           if (isPrintable(c, style)) {             return true;           }         }-        else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) {+        else if (Ruleset* r = Cast<Ruleset>(stm)) {           if (isPrintable(r, style)) {             return true;           }         }-        else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) {+        else if (Supports_Block* f = Cast<Supports_Block>(stm)) {           if (isPrintable(f, style)) {             return true;           }         }-        else if (Media_Block_Ptr m = Cast<Media_Block>(stm)) {+        else if (CssMediaRule * m = Cast<CssMediaRule>(stm)) {           if (isPrintable(m, style)) {             return true;           }         }-        else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+        else if (Has_Block* b = Cast<Has_Block>(stm)) {           if (isPrintable(b->block(), style)) {             return true;           }@@ -723,10 +717,6 @@       }        return false;-    }--    bool isAscii(const char chr) {-      return unsigned(chr) < 128;     }    }
libsass/src/util.hpp view
@@ -1,17 +1,35 @@ #ifndef SASS_UTIL_H #define SASS_UTIL_H -#include <vector>-#include <string>-#include <assert.h>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "sass/base.h" #include "ast_fwd_decl.hpp" +#include <cmath>+#include <cstring>+#include <vector>+#include <string>+#include <assert.h>+ #define SASS_ASSERT(cond, msg) assert(cond && msg)  namespace Sass { +  template <typename T>+  T clip(const T& n, const T& lower, const T& upper) {+    return std::max(lower, std::min(n, upper));+  }++  template <typename T>+  T absmod(const T& n, const T& r) {+    T m = std::fmod(n, r);+    if (m < 0.0) m += r;+    return m;+  }+   double round(double val, size_t precision = 0);   double sass_strtod(const char* str);   const char* safe_str(const char *, const char* = "");@@ -20,7 +38,7 @@   std::string read_css_string(const std::string& str, bool css = true);   std::string evacuate_escapes(const std::string& str);   std::string string_to_output(const std::string& str);-  std::string comment_to_string(const std::string& text);+  std::string comment_to_compact_string(const std::string& text);   std::string read_hex_escapes(const std::string& str);   std::string escape_string(const std::string& str);   void newline_to_space(std::string& str);@@ -34,22 +52,53 @@    bool peek_linefeed(const char* start); -  namespace Util {+  // Returns true iff `elements` ⊆ `container`.+  template <typename C, typename T>+  bool contains_all(C container, T elements) {+    for (const auto &el : elements) {+      if (container.find(el) == container.end()) return false;+    }+    return true;+  } -    std::string rtrim(const std::string& str);+  // C++20 `starts_with` equivalent.+  // See https://en.cppreference.com/w/cpp/string/basic_string/starts_with+  inline bool starts_with(const std::string& str, const char* prefix, size_t prefix_len) {+    return str.compare(0, prefix_len, prefix) == 0;+  } -    std::string normalize_underscores(const std::string& str);-    std::string normalize_decimals(const std::string& str);+  inline bool starts_with(const std::string& str, const char* prefix) {+    return starts_with(str, prefix, std::strlen(prefix));+  } -    bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style = NESTED);-    bool isPrintable(Supports_Block_Ptr r, Sass_Output_Style style = NESTED);-    bool isPrintable(Media_Block_Ptr r, Sass_Output_Style style = NESTED);-    bool isPrintable(Comment_Ptr b, Sass_Output_Style style = NESTED);+  // C++20 `ends_with` equivalent.+  // See https://en.cppreference.com/w/cpp/string/basic_string/ends_with+  inline bool ends_with(const std::string& str, const std::string& suffix) {+    return suffix.size() <= str.size() && std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());+  }++  inline bool ends_with(const std::string& str, const char* suffix, size_t suffix_len) {+    if (suffix_len > str.size()) return false;+    const char* suffix_it = suffix + suffix_len;+    const char* str_it = str.c_str() + str.size();+    while (suffix_it != suffix) if (*(--suffix_it) != *(--str_it)) return false;+    return true;+  }++  inline bool ends_with(const std::string& str, const char* suffix) {+    return ends_with(str, suffix, std::strlen(suffix));+  }++  namespace Util {++    bool isPrintable(Ruleset* r, Sass_Output_Style style = NESTED);+    bool isPrintable(Supports_Block* r, Sass_Output_Style style = NESTED);+    bool isPrintable(CssMediaRule* r, Sass_Output_Style style = NESTED);+    bool isPrintable(Comment* b, Sass_Output_Style style = NESTED);     bool isPrintable(Block_Obj b, Sass_Output_Style style = NESTED);-    bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style = NESTED);-    bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style = NESTED);-    bool isPrintable(Declaration_Ptr d, Sass_Output_Style style = NESTED);-    bool isAscii(const char chr);+    bool isPrintable(String_Constant* s, Sass_Output_Style style = NESTED);+    bool isPrintable(String_Quoted* s, Sass_Output_Style style = NESTED);+    bool isPrintable(Declaration* d, Sass_Output_Style style = NESTED);    } }
+ libsass/src/util_string.cpp view
@@ -0,0 +1,125 @@+#include "util_string.hpp"++#include <iostream>+#include <algorithm>++namespace Sass {+  namespace Util {++    // ##########################################################################+    // Special case insensitive string matcher. We can optimize+    // the more general compare case quite a bit by requiring+    // consumers to obey some rules (lowercase and no space).+    // - `literal` must only contain lower case ascii characters+    // there is one edge case where this could give false positives+    // test could contain a (non-ascii) char exactly 32 below literal+    // ##########################################################################+    bool equalsLiteral(const char* lit, const std::string& test) {+      // Work directly on characters+      const char* src = test.c_str();+      // There is a small chance that the search string+      // Is longer than the rest of the string to look at+      while (*lit && (*src == *lit || *src + 32 == *lit)) {+        ++src, ++lit;+      }+      // True if literal is at end+      // If not test was too long+      return *lit == 0;+    }++    void ascii_str_tolower(std::string* s) {+      for (auto& ch : *s) {+        ch = ascii_tolower(static_cast<unsigned char>(ch));+      }+    }++    void ascii_str_toupper(std::string* s) {+      for (auto& ch : *s) {+        ch = ascii_toupper(static_cast<unsigned char>(ch));+      }+    }++    std::string rtrim(std::string str) {+      auto it = std::find_if_not(str.rbegin(), str.rend(), ascii_isspace);+      str.erase(str.rend() - it);+      return str;+    }++    // ###########################################################################+    // Returns [name] without a vendor prefix.+    // If [name] has no vendor prefix, it's returned as-is.+    // ###########################################################################+    std::string unvendor(const std::string& name)+    {+      if (name.size() < 2) return name;+      if (name[0] != '-') return name;+      if (name[1] == '-') return name;+      for (size_t i = 2; i < name.size(); i++) {+        if (name[i] == '-') return name.substr(i + 1);+      }+      return name;+    }+    // EO unvendor++    std::string normalize_newlines(const std::string& str) {+      std::string result;+      result.reserve(str.size());+      std::size_t pos = 0;+      while (true) {+        const std::size_t newline = str.find_first_of("\n\f\r", pos);+        if (newline == std::string::npos) break;+        result.append(str, pos, newline - pos);+        result += '\n';+        if (str[newline] == '\r' && str[newline + 1] == '\n') {+          pos = newline + 2;+        }+        else {+          pos = newline + 1;+        }+      }+      result.append(str, pos, std::string::npos);+      return result;+    }++    std::string normalize_underscores(const std::string& str) {+      std::string normalized = str;+      std::replace(normalized.begin(), normalized.end(), '_', '-');+      return normalized;+    }++    std::string normalize_decimals(const std::string& str) {+      std::string normalized;+      if (!str.empty() && str[0] == '.') {+        normalized.reserve(str.size() + 1);+        normalized += '0';+        normalized += str;+      }+      else {+        normalized = str;+      }+      return normalized;+    }++    char opening_bracket_for(char closing_bracket) {+      switch (closing_bracket) {+      case ')': return '(';+      case ']': return '[';+      case '}': return '{';+      default: return '\0';+      }+    }++    char closing_bracket_for(char opening_bracket) {+      switch (opening_bracket) {+      case '(': return ')';+      case '[': return ']';+      case '{': return '}';+      default: return '\0';+      }+    }++  }+  // namespace Util++}+// namespace Sass
+ libsass/src/util_string.hpp view
@@ -0,0 +1,72 @@+#ifndef SASS_UTIL_STRING_H+#define SASS_UTIL_STRING_H++#include <string>++namespace Sass {+  namespace Util {++    // ##########################################################################+    // Special case insensitive string matcher. We can optimize+    // the more general compare case quite a bit by requiring+    // consumers to obey some rules (lowercase and no space).+    // - `literal` must only contain lower case ascii characters+    // there is one edge case where this could give false positives+    // test could contain a (non-ascii) char exactly 32 below literal+    // ##########################################################################+    bool equalsLiteral(const char* lit, const std::string& test);++    // ###########################################################################+    // Returns [name] without a vendor prefix.+    // If [name] has no vendor prefix, it's returned as-is.+    // ###########################################################################+    std::string unvendor(const std::string& name);++    std::string rtrim(std::string str);+    std::string normalize_newlines(const std::string& str);+    std::string normalize_underscores(const std::string& str);+    std::string normalize_decimals(const std::string& str);+    char opening_bracket_for(char closing_bracket);+    char closing_bracket_for(char opening_bracket);++    // Locale-independent ASCII character routines.++    inline bool ascii_isalpha(unsigned char c) {+      return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');+    }++    inline bool ascii_isdigit(unsigned char c) {+      return (c >= '0' && c <= '9');+    }++    inline bool ascii_isalnum(unsigned char c) {+      return ascii_isalpha(c) || ascii_isdigit(c);+    }++    inline bool ascii_isascii(unsigned char c) { return c < 128; }++    inline bool ascii_isxdigit(unsigned char c) {+      return ascii_isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');+    }++    inline bool ascii_isspace(unsigned char c) {+      return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n';+    }++    inline char ascii_tolower(unsigned char c) {+      if (c >= 'A' && c <= 'Z') return c + 32;+      return c;+    }++    void ascii_str_tolower(std::string* s);++    inline char ascii_toupper(unsigned char c) {+      if (c >= 'a' && c <= 'z') return c - 32;+      return c;+    }++    void ascii_str_toupper(std::string* s);++  }  // namespace Sass+}  // namespace Util+#endif  // SASS_UTIL_STRING_H
libsass/src/values.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "sass.h" #include "values.hpp" @@ -7,21 +10,26 @@ namespace Sass {    // convert value from C++ side to C-API-  union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val)+  union Sass_Value* ast_node_to_sass_value (const Expression* val)   {     if (val->concrete_type() == Expression::NUMBER)     {-      Number_Ptr_Const res = Cast<Number>(val);+      const Number* res = Cast<Number>(val);       return sass_make_number(res->value(), res->unit().c_str());     }     else if (val->concrete_type() == Expression::COLOR)     {-      Color_Ptr_Const col = Cast<Color>(val);-      return sass_make_color(col->r(), col->g(), col->b(), col->a());+      if (const Color_RGBA* rgba = Cast<Color_RGBA>(val)) {+        return sass_make_color(rgba->r(), rgba->g(), rgba->b(), rgba->a());+      } else {+        // ToDo: allow to also use HSLA colors!!+        Color_RGBA_Obj col = Cast<Color>(val)->copyAsRGBA();+        return sass_make_color(col->r(), col->g(), col->b(), col->a());+      }     }     else if (val->concrete_type() == Expression::LIST)     {-      List_Ptr_Const l = Cast<List>(val);+      const List* l = Cast<List>(val);       union Sass_Value* list = sass_make_list(l->size(), l->separator(), l->is_bracketed());       for (size_t i = 0, L = l->length(); i < L; ++i) {         Expression_Obj obj = l->at(i);@@ -32,7 +40,7 @@     }     else if (val->concrete_type() == Expression::MAP)     {-      Map_Ptr_Const m = Cast<Map>(val);+      const Map* m = Cast<Map>(val);       union Sass_Value* map = sass_make_map(m->length());       size_t i = 0; for (Expression_Obj key : m->keys()) {         sass_map_set_key(map, i, ast_node_to_sass_value(key));@@ -47,16 +55,16 @@     }     else if (val->concrete_type() == Expression::BOOLEAN)     {-      Boolean_Ptr_Const res = Cast<Boolean>(val);+      const Boolean* res = Cast<Boolean>(val);       return sass_make_boolean(res->value());     }     else if (val->concrete_type() == Expression::STRING)     {-      if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(val))+      if (const String_Quoted* qstr = Cast<String_Quoted>(val))       {         return sass_make_qstring(qstr->value().c_str());       }-      else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(val))+      else if (const String_Constant* cstr = Cast<String_Constant>(val))       {         return sass_make_string(cstr->value().c_str());       }@@ -65,7 +73,7 @@   }    // convert value from C-API to C++ side-  Value_Ptr sass_value_to_ast_node (const union Sass_Value* val)+  Value* sass_value_to_ast_node (const union Sass_Value* val)   {     switch (sass_value_get_tag(val)) {       case SASS_NUMBER:@@ -78,7 +86,8 @@                                ParserState("[C-VALUE]"),                                sass_boolean_get_value(val));       case SASS_COLOR:-        return SASS_MEMORY_NEW(Color,+        // ToDo: allow to also use HSLA colors!!+        return SASS_MEMORY_NEW(Color_RGBA,                                ParserState("[C-VALUE]"),                                sass_color_get_r(val),                                sass_color_get_g(val),@@ -94,7 +103,7 @@                                  ParserState("[C-VALUE]"),                                  sass_string_get_value(val));       case SASS_LIST: {-        List_Ptr l = SASS_MEMORY_NEW(List,+        List* l = SASS_MEMORY_NEW(List,                                   ParserState("[C-VALUE]"),                                   sass_list_get_length(val),                                   sass_list_get_separator(val));@@ -105,7 +114,7 @@         return l;       }       case SASS_MAP: {-        Map_Ptr m = SASS_MEMORY_NEW(Map, ParserState("[C-VALUE]"));+        Map* m = SASS_MEMORY_NEW(Map, ParserState("[C-VALUE]"));         for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) {           *m << std::make_pair(             sass_value_to_ast_node(sass_map_get_key(val, i)),
libsass/src/values.hpp view
@@ -5,8 +5,8 @@  namespace Sass { -  union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val);-  Value_Ptr sass_value_to_ast_node (const union Sass_Value* val);+  union Sass_Value* ast_node_to_sass_value (const Expression* val);+  Value* sass_value_to_ast_node (const union Sass_Value* val);  } #endif
tests/Main.hs view
@@ -35,7 +35,7 @@ #ifndef EXTERNAL_LIBSASS     it "should report correct version" $ do         str <- peekCString libsass_version-        str `shouldBe` "3.5.5"+        str `shouldBe` "3.6.3" #endif      it "should support quoted strings" $ withCString "sample" $ \cstr -> do