diff --git a/Bindings/Libsass/Context.hs b/Bindings/Libsass/Context.hs
--- a/Bindings/Libsass/Context.hs
+++ b/Bindings/Libsass/Context.hs
@@ -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
diff --git a/Bindings/Libsass/Types.hs b/Bindings/Libsass/Types.hs
--- a/Bindings/Libsass/Types.hs
+++ b/Bindings/Libsass/Types.hs
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/hlibsass.cabal b/hlibsass.cabal
--- a/hlibsass.cabal
+++ b/hlibsass.cabal
@@ -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>
diff --git a/libsass/Makefile b/libsass/Makefile
--- a/libsass/Makefile
+++ b/libsass/Makefile
@@ -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:
diff --git a/libsass/Makefile.conf b/libsass/Makefile.conf
--- a/libsass/Makefile.conf
+++ b/libsass/Makefile.conf
@@ -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 \
diff --git a/libsass/include/sass/base.h b/libsass/include/sass/base.h
--- a/libsass/include/sass/base.h
+++ b/libsass/include/sass/base.h
@@ -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
diff --git a/libsass/include/sass/context.h b/libsass/include/sass/context.h
--- a/libsass/include/sass/context.h
+++ b/libsass/include/sass/context.h
@@ -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);
diff --git a/libsass/src/GNUmakefile.am b/libsass/src/GNUmakefile.am
--- a/libsass/src/GNUmakefile.am
+++ b/libsass/src/GNUmakefile.am
@@ -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 \
diff --git a/libsass/src/ast.cpp b/libsass/src/ast.cpp
--- a/libsass/src/ast.cpp
+++ b/libsass/src/ast.cpp
@@ -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);
+
+  /////////////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////////////
+
 }
diff --git a/libsass/src/ast.hpp b/libsass/src/ast.hpp
--- a/libsass/src/ast.hpp
+++ b/libsass/src/ast.hpp
@@ -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
 
diff --git a/libsass/src/ast2c.cpp b/libsass/src/ast2c.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast2c.cpp
@@ -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(); }
+
+};
diff --git a/libsass/src/ast2c.hpp b/libsass/src/ast2c.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast2c.hpp
@@ -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
diff --git a/libsass/src/ast_def_macros.hpp b/libsass/src/ast_def_macros.hpp
--- a/libsass/src/ast_def_macros.hpp
+++ b/libsass/src/ast_def_macros.hpp
@@ -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
diff --git a/libsass/src/ast_fwd_decl.cpp b/libsass/src/ast_fwd_decl.cpp
--- a/libsass/src/ast_fwd_decl.cpp
+++ b/libsass/src/ast_fwd_decl.cpp
@@ -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)
 
 }
diff --git a/libsass/src/ast_fwd_decl.hpp b/libsass/src/ast_fwd_decl.hpp
--- a/libsass/src/ast_fwd_decl.hpp
+++ b/libsass/src/ast_fwd_decl.hpp
@@ -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)
 
 }
 
diff --git a/libsass/src/ast_helpers.hpp b/libsass/src/ast_helpers.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_helpers.hpp
@@ -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
diff --git a/libsass/src/ast_sel_cmp.cpp b/libsass/src/ast_sel_cmp.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_sel_cmp.cpp
@@ -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; }
+  }
+
+  /*#########################################################################*/
+  /*#########################################################################*/
+
+}
diff --git a/libsass/src/ast_sel_super.cpp b/libsass/src/ast_sel_super.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_sel_super.cpp
@@ -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());
+  }
+
+  // ##########################################################################
+  // ##########################################################################
+
+}
diff --git a/libsass/src/ast_sel_unify.cpp b/libsass/src/ast_sel_unify.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_sel_unify.cpp
@@ -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*)
+
+  // ##########################################################################
+  // ##########################################################################
+
+}
diff --git a/libsass/src/ast_sel_weave.cpp b/libsass/src/ast_sel_weave.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_sel_weave.cpp
@@ -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
+
+  // ##########################################################################
+  // ##########################################################################
+
+}
diff --git a/libsass/src/ast_selectors.cpp b/libsass/src/ast_selectors.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_selectors.cpp
@@ -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);
+
+}
diff --git a/libsass/src/ast_selectors.hpp b/libsass/src/ast_selectors.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_selectors.hpp
@@ -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
diff --git a/libsass/src/ast_supports.cpp b/libsass/src/ast_supports.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_supports.cpp
@@ -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);
+
+  /////////////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////////////
+
+}
diff --git a/libsass/src/ast_supports.hpp b/libsass/src/ast_supports.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_supports.hpp
@@ -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
diff --git a/libsass/src/ast_values.cpp b/libsass/src/ast_values.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_values.cpp
@@ -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);
+
+  /////////////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////////////
+
+}
diff --git a/libsass/src/ast_values.hpp b/libsass/src/ast_values.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ast_values.hpp
@@ -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
diff --git a/libsass/src/backtrace.cpp b/libsass/src/backtrace.cpp
--- a/libsass/src/backtrace.cpp
+++ b/libsass/src/backtrace.cpp
@@ -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;
       }
 
diff --git a/libsass/src/base64vlq.cpp b/libsass/src/base64vlq.cpp
--- a/libsass/src/base64vlq.cpp
+++ b/libsass/src/base64vlq.cpp
@@ -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 {
diff --git a/libsass/src/bind.cpp b/libsass/src/bind.cpp
--- a/libsass/src/bind.cpp
+++ b/libsass/src/bind.cpp
@@ -1,6 +1,7 @@
 #include "sass.hpp"
 #include "bind.hpp"
 #include "ast.hpp"
+#include "backtrace.hpp"
 #include "context.hpp"
 #include "expand.hpp"
 #include "eval.hpp"
@@ -10,7 +11,7 @@
 
 namespace Sass {
 
-  void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval)
+  void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces)
   {
     std::string callee(type + " " + name);
 
@@ -54,7 +55,7 @@
         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);
         }
       }
     }
diff --git a/libsass/src/bind.hpp b/libsass/src/bind.hpp
--- a/libsass/src/bind.hpp
+++ b/libsass/src/bind.hpp
@@ -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
diff --git a/libsass/src/c2ast.cpp b/libsass/src/c2ast.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/c2ast.cpp
@@ -0,0 +1,64 @@
+#include "ast.hpp"
+#include "units.hpp"
+#include "position.hpp"
+#include "backtrace.hpp"
+#include "sass/values.h"
+#include "ast_fwd_decl.hpp"
+#include "error_handling.hpp"
+
+namespace Sass {
+
+  Value* 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;
+  }
+
+}
diff --git a/libsass/src/c2ast.hpp b/libsass/src/c2ast.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/c2ast.hpp
@@ -0,0 +1,14 @@
+#ifndef SASS_C2AST_H
+#define SASS_C2AST_H
+
+#include "position.hpp"
+#include "backtrace.hpp"
+#include "ast_fwd_decl.hpp"
+
+namespace Sass {
+
+  Value* c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate);
+
+}
+
+#endif
diff --git a/libsass/src/cencode.c b/libsass/src/cencode.c
--- a/libsass/src/cencode.c
+++ b/libsass/src/cencode.c
@@ -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)
 			{
diff --git a/libsass/src/check_nesting.cpp b/libsass/src/check_nesting.cpp
--- a/libsass/src/check_nesting.cpp
+++ b/libsass/src/check_nesting.cpp
@@ -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);
   }
 }
diff --git a/libsass/src/check_nesting.hpp b/libsass/src/check_nesting.hpp
--- a/libsass/src/check_nesting.hpp
+++ b/libsass/src/check_nesting.hpp
@@ -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*);
   };
 
 }
diff --git a/libsass/src/color_maps.cpp b/libsass/src/color_maps.cpp
--- a/libsass/src/color_maps.cpp
+++ b/libsass/src/color_maps.cpp
@@ -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
diff --git a/libsass/src/color_maps.hpp b/libsass/src/color_maps.hpp
--- a/libsass/src/color_maps.hpp
+++ b/libsass/src/color_maps.hpp
@@ -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);
 
 }
diff --git a/libsass/src/constants.cpp b/libsass/src/constants.cpp
--- a/libsass/src/constants.cpp
+++ b/libsass/src/constants.cpp
@@ -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
diff --git a/libsass/src/constants.hpp b/libsass/src/constants.hpp
--- a/libsass/src/constants.hpp
+++ b/libsass/src/constants.hpp
@@ -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
diff --git a/libsass/src/context.cpp b/libsass/src/context.cpp
--- a/libsass/src/context.cpp
+++ b/libsass/src/context.cpp
@@ -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;
   }
diff --git a/libsass/src/context.hpp b/libsass/src/context.hpp
--- a/libsass/src/context.hpp
+++ b/libsass/src/context.hpp
@@ -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);
 
diff --git a/libsass/src/cssize.cpp b/libsass/src/cssize.cpp
--- a/libsass/src/cssize.cpp
+++ b/libsass/src/cssize.cpp
@@ -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;
-  }
 }
diff --git a/libsass/src/cssize.hpp b/libsass/src/cssize.hpp
--- a/libsass/src/cssize.hpp
+++ b/libsass/src/cssize.hpp
@@ -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*);
   };
 
 }
diff --git a/libsass/src/dart_helpers.hpp b/libsass/src/dart_helpers.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/dart_helpers.hpp
@@ -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
diff --git a/libsass/src/debugger.hpp b/libsass/src/debugger.hpp
--- a/libsass/src/debugger.hpp
+++ b/libsass/src/debugger.hpp
@@ -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
diff --git a/libsass/src/emitter.cpp b/libsass/src/emitter.cpp
--- a/libsass/src/emitter.cpp
+++ b/libsass/src/emitter.cpp
@@ -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;
diff --git a/libsass/src/emitter.hpp b/libsass/src/emitter.hpp
--- a/libsass/src/emitter.hpp
+++ b/libsass/src/emitter.hpp
@@ -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);
diff --git a/libsass/src/environment.cpp b/libsass/src/environment.cpp
--- a/libsass/src/environment.cpp
+++ b/libsass/src/environment.cpp
@@ -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;
       }
diff --git a/libsass/src/environment.hpp b/libsass/src/environment.hpp
--- a/libsass/src/environment.hpp
+++ b/libsass/src/environment.hpp
@@ -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;
 
 }
 
diff --git a/libsass/src/error_handling.cpp b/libsass/src/error_handling.cpp
--- a/libsass/src/error_handling.cpp
+++ b/libsass/src/error_handling.cpp
@@ -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)
+    {
+
+    }
+    
 
   }
 
diff --git a/libsass/src/error_handling.hpp b/libsass/src/error_handling.hpp
--- a/libsass/src/error_handling.hpp
+++ b/libsass/src/error_handling.hpp
@@ -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() {};
     };
 
   }
diff --git a/libsass/src/eval.cpp b/libsass/src/eval.cpp
--- a/libsass/src/eval.cpp
+++ b/libsass/src/eval.cpp
@@ -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;
   };
 
 }
diff --git a/libsass/src/eval.hpp b/libsass/src/eval.hpp
--- a/libsass/src/eval.hpp
+++ b/libsass/src/eval.hpp
@@ -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]"));
 
 }
 
diff --git a/libsass/src/eval_selectors.cpp b/libsass/src/eval_selectors.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/eval_selectors.cpp
@@ -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;
+  }
+}
diff --git a/libsass/src/expand.cpp b/libsass/src/expand.cpp
--- a/libsass/src/expand.cpp
+++ b/libsass/src/expand.cpp
@@ -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);
     }
diff --git a/libsass/src/expand.hpp b/libsass/src/expand.hpp
--- a/libsass/src/expand.hpp
+++ b/libsass/src/expand.hpp
@@ -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*);
+
   };
 
 }
diff --git a/libsass/src/extend.cpp b/libsass/src/extend.cpp
deleted file mode 100644
--- a/libsass/src/extend.cpp
+++ /dev/null
@@ -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();
-  }
-}
diff --git a/libsass/src/extend.hpp b/libsass/src/extend.hpp
deleted file mode 100644
--- a/libsass/src/extend.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/extender.cpp b/libsass/src/extender.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/extender.cpp
@@ -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
+
+}
diff --git a/libsass/src/extender.hpp b/libsass/src/extender.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/extender.hpp
@@ -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
diff --git a/libsass/src/extension.cpp b/libsass/src/extension.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/extension.cpp
@@ -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);
+
+  }
+
+  // ##########################################################################
+  // ##########################################################################
+
+}
diff --git a/libsass/src/extension.hpp b/libsass/src/extension.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/extension.hpp
@@ -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
diff --git a/libsass/src/file.cpp b/libsass/src/file.cpp
--- a/libsass/src/file.cpp
+++ b/libsass/src/file.cpp
@@ -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
diff --git a/libsass/src/file.hpp b/libsass/src/file.hpp
--- a/libsass/src/file.hpp
+++ b/libsass/src/file.hpp
@@ -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)
       { }
   };
 
diff --git a/libsass/src/fn_colors.cpp b/libsass/src/fn_colors.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_colors.cpp
@@ -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);
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_colors.hpp b/libsass/src/fn_colors.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_colors.hpp
@@ -0,0 +1,85 @@
+#ifndef SASS_FN_COLORS_H
+#define SASS_FN_COLORS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    // macros for common ranges (u mean unsigned or upper, r for full range)
+    #define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double
+    #define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double
+    #define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double
+    #define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double
+    #define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double
+    #define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double
+
+    // macros for color related inputs (rbg and alpha/opacity values)
+    #define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double
+    #define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double
+
+    extern Signature rgb_sig;
+    extern Signature rgba_4_sig;
+    extern Signature rgba_2_sig;
+    extern Signature red_sig;
+    extern Signature green_sig;
+    extern Signature blue_sig;
+    extern Signature mix_sig;
+    extern Signature hsl_sig;
+    extern Signature hsla_sig;
+    extern Signature hue_sig;
+    extern Signature saturation_sig;
+    extern Signature lightness_sig;
+    extern Signature adjust_hue_sig;
+    extern Signature lighten_sig;
+    extern Signature darken_sig;
+    extern Signature saturate_sig;
+    extern Signature desaturate_sig;
+    extern Signature grayscale_sig;
+    extern Signature complement_sig;
+    extern Signature invert_sig;
+    extern Signature alpha_sig;
+    extern Signature opacity_sig;
+    extern Signature opacify_sig;
+    extern Signature fade_in_sig;
+    extern Signature transparentize_sig;
+    extern Signature fade_out_sig;
+    extern Signature adjust_color_sig;
+    extern Signature scale_color_sig;
+    extern Signature change_color_sig;
+    extern Signature ie_hex_str_sig;
+
+    BUILT_IN(rgb);
+    BUILT_IN(rgba_4);
+    BUILT_IN(rgba_2);
+    BUILT_IN(red);
+    BUILT_IN(green);
+    BUILT_IN(blue);
+    BUILT_IN(mix);
+    BUILT_IN(hsl);
+    BUILT_IN(hsla);
+    BUILT_IN(hue);
+    BUILT_IN(saturation);
+    BUILT_IN(lightness);
+    BUILT_IN(adjust_hue);
+    BUILT_IN(lighten);
+    BUILT_IN(darken);
+    BUILT_IN(saturate);
+    BUILT_IN(desaturate);
+    BUILT_IN(grayscale);
+    BUILT_IN(complement);
+    BUILT_IN(invert);
+    BUILT_IN(alpha);
+    BUILT_IN(opacify);
+    BUILT_IN(transparentize);
+    BUILT_IN(adjust_color);
+    BUILT_IN(scale_color);
+    BUILT_IN(change_color);
+    BUILT_IN(ie_hex_str);
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_lists.cpp b/libsass/src/fn_lists.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_lists.cpp
@@ -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());
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_lists.hpp b/libsass/src/fn_lists.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_lists.hpp
@@ -0,0 +1,34 @@
+#ifndef SASS_FN_LISTS_H
+#define SASS_FN_LISTS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    extern Signature length_sig;
+    extern Signature nth_sig;
+    extern Signature index_sig;
+    extern Signature join_sig;
+    extern Signature append_sig;
+    extern Signature zip_sig;
+    extern Signature list_separator_sig;
+    extern Signature is_bracketed_sig;
+    extern Signature keywords_sig;
+
+    BUILT_IN(length);
+    BUILT_IN(nth);
+    BUILT_IN(index);
+    BUILT_IN(join);
+    BUILT_IN(append);
+    BUILT_IN(zip);
+    BUILT_IN(list_separator);
+    BUILT_IN(is_bracketed);
+    BUILT_IN(keywords);
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_maps.cpp b/libsass/src/fn_maps.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_maps.cpp
@@ -0,0 +1,94 @@
+#include "operators.hpp"
+#include "fn_utils.hpp"
+#include "fn_maps.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    /////////////////
+    // MAP FUNCTIONS
+    /////////////////
+
+    Signature map_get_sig = "map-get($map, $key)";
+    BUILT_IN(map_get)
+    {
+      // leaks for "map-get((), foo)" if not Obj
+      // investigate why this is (unexpected)
+      Map_Obj m = ARGM("$map", Map);
+      Expression_Obj v = ARG("$key", Expression);
+      try {
+        Value_Obj val = m->at(v);
+        if (!val) return SASS_MEMORY_NEW(Null, pstate);
+        val->set_delayed(false);
+        return val.detach();
+      } catch (const std::out_of_range&) {
+        return SASS_MEMORY_NEW(Null, pstate);
+      }
+      catch (...) { throw; }
+    }
+
+    Signature map_has_key_sig = "map-has-key($map, $key)";
+    BUILT_IN(map_has_key)
+    {
+      Map_Obj m = ARGM("$map", Map);
+      Expression_Obj v = ARG("$key", Expression);
+      return SASS_MEMORY_NEW(Boolean, pstate, m->has(v));
+    }
+
+    Signature map_keys_sig = "map-keys($map)";
+    BUILT_IN(map_keys)
+    {
+      Map_Obj m = ARGM("$map", Map);
+      List* 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;
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_maps.hpp b/libsass/src/fn_maps.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_maps.hpp
@@ -0,0 +1,30 @@
+#ifndef SASS_FN_MAPS_H
+#define SASS_FN_MAPS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    #define ARGM(argname, argtype) get_arg_m(argname, env, sig, pstate, traces)
+
+    extern Signature map_get_sig;
+    extern Signature map_merge_sig;
+    extern Signature map_remove_sig;
+    extern Signature map_keys_sig;
+    extern Signature map_values_sig;
+    extern Signature map_has_key_sig;
+
+    BUILT_IN(map_get);
+    BUILT_IN(map_merge);
+    BUILT_IN(map_remove);
+    BUILT_IN(map_keys);
+    BUILT_IN(map_values);
+    BUILT_IN(map_has_key);
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_miscs.cpp b/libsass/src/fn_miscs.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_miscs.cpp
@@ -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);
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_miscs.hpp b/libsass/src/fn_miscs.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_miscs.hpp
@@ -0,0 +1,40 @@
+#ifndef SASS_FN_MISCS_H
+#define SASS_FN_MISCS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    extern Signature type_of_sig;
+    extern Signature variable_exists_sig;
+    extern Signature global_variable_exists_sig;
+    extern Signature function_exists_sig;
+    extern Signature mixin_exists_sig;
+    extern Signature feature_exists_sig;
+    extern Signature call_sig;
+    extern Signature not_sig;
+    extern Signature if_sig;
+    extern Signature set_nth_sig;
+    extern Signature content_exists_sig;
+    extern Signature get_function_sig;
+
+    BUILT_IN(type_of);
+    BUILT_IN(variable_exists);
+    BUILT_IN(global_variable_exists);
+    BUILT_IN(function_exists);
+    BUILT_IN(mixin_exists);
+    BUILT_IN(feature_exists);
+    BUILT_IN(call);
+    BUILT_IN(sass_not);
+    BUILT_IN(sass_if);
+    BUILT_IN(set_nth);
+    BUILT_IN(content_exists);
+    BUILT_IN(get_function);
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_numbers.cpp b/libsass/src/fn_numbers.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_numbers.cpp
@@ -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);
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_numbers.hpp b/libsass/src/fn_numbers.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_numbers.hpp
@@ -0,0 +1,45 @@
+#ifndef SASS_FN_NUMBERS_H
+#define SASS_FN_NUMBERS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    // return a number object (copied since we want to have reduced units)
+    #define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy
+
+    extern Signature percentage_sig;
+    extern Signature round_sig;
+    extern Signature ceil_sig;
+    extern Signature floor_sig;
+    extern Signature abs_sig;
+    extern Signature min_sig;
+    extern Signature max_sig;
+    extern Signature inspect_sig;
+    extern Signature random_sig;
+    extern Signature unique_id_sig;
+    extern Signature unit_sig;
+    extern Signature unitless_sig;
+    extern Signature comparable_sig;
+
+    BUILT_IN(percentage);
+    BUILT_IN(round);
+    BUILT_IN(ceil);
+    BUILT_IN(floor);
+    BUILT_IN(abs);
+    BUILT_IN(min);
+    BUILT_IN(max);
+    BUILT_IN(inspect);
+    BUILT_IN(random);
+    BUILT_IN(unique_id);
+    BUILT_IN(unit);
+    BUILT_IN(unitless);
+    BUILT_IN(comparable);
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_selectors.cpp b/libsass/src/fn_selectors.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_selectors.cpp
@@ -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);
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_selectors.hpp b/libsass/src/fn_selectors.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_selectors.hpp
@@ -0,0 +1,35 @@
+#ifndef SASS_FN_SELECTORS_H
+#define SASS_FN_SELECTORS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    #define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, traces, ctx)
+    #define ARGSELS(argname) get_arg_sels(argname, env, sig, pstate, traces, ctx)
+
+    BUILT_IN(selector_nest);
+    BUILT_IN(selector_append);
+    BUILT_IN(selector_extend);
+    BUILT_IN(selector_replace);
+    BUILT_IN(selector_unify);
+    BUILT_IN(is_superselector);
+    BUILT_IN(simple_selectors);
+    BUILT_IN(selector_parse);
+
+    extern Signature selector_nest_sig;
+    extern Signature selector_append_sig;
+    extern Signature selector_extend_sig;
+    extern Signature selector_replace_sig;
+    extern Signature selector_unify_sig;
+    extern Signature is_superselector_sig;
+    extern Signature simple_selectors_sig;
+    extern Signature selector_parse_sig;
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_strings.cpp b/libsass/src/fn_strings.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_strings.cpp
@@ -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);
+      }
+    }
+
+  }
+
+}
diff --git a/libsass/src/fn_strings.hpp b/libsass/src/fn_strings.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_strings.hpp
@@ -0,0 +1,34 @@
+#ifndef SASS_FN_STRINGS_H
+#define SASS_FN_STRINGS_H
+
+#include "fn_utils.hpp"
+
+namespace Sass {
+
+  namespace Functions {
+
+    extern Signature unquote_sig;
+    extern Signature quote_sig;
+    extern Signature str_length_sig;
+    extern Signature str_insert_sig;
+    extern Signature str_index_sig;
+    extern Signature str_slice_sig;
+    extern Signature to_upper_case_sig;
+    extern Signature to_lower_case_sig;
+    extern Signature length_sig;
+
+    BUILT_IN(sass_unquote);
+    BUILT_IN(sass_quote);
+    BUILT_IN(str_length);
+    BUILT_IN(str_insert);
+    BUILT_IN(str_index);
+    BUILT_IN(str_slice);
+    BUILT_IN(to_upper_case);
+    BUILT_IN(to_lower_case);
+    BUILT_IN(length);
+
+  }
+
+}
+
+#endif
diff --git a/libsass/src/fn_utils.cpp b/libsass/src/fn_utils.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_utils.cpp
@@ -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();
+    }
+
+
+  }
+
+}
diff --git a/libsass/src/fn_utils.hpp b/libsass/src/fn_utils.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/fn_utils.hpp
@@ -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
diff --git a/libsass/src/functions.cpp b/libsass/src/functions.cpp
deleted file mode 100644
--- a/libsass/src/functions.cpp
+++ /dev/null
@@ -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);
-    }
-  }
-}
diff --git a/libsass/src/functions.hpp b/libsass/src/functions.hpp
deleted file mode 100644
--- a/libsass/src/functions.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/inspect.cpp b/libsass/src/inspect.cpp
--- a/libsass/src/inspect.cpp
+++ b/libsass/src/inspect.cpp
@@ -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();
+      }
+    }
   }
 
 }
diff --git a/libsass/src/inspect.hpp b/libsass/src/inspect.hpp
--- a/libsass/src/inspect.hpp
+++ b/libsass/src/inspect.hpp
@@ -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)); }
   };
 
 }
diff --git a/libsass/src/json.cpp b/libsass/src/json.cpp
--- a/libsass/src/json.cpp
+++ b/libsass/src/json.cpp
@@ -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;
   }
diff --git a/libsass/src/lexer.cpp b/libsass/src/lexer.cpp
--- a/libsass/src/lexer.cpp
+++ b/libsass/src/lexer.cpp
@@ -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/)
diff --git a/libsass/src/lexer.hpp b/libsass/src/lexer.hpp
--- a/libsass/src/lexer.hpp
+++ b/libsass/src/lexer.hpp
@@ -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);
 
diff --git a/libsass/src/listize.cpp b/libsass/src/listize.cpp
--- a/libsass/src/listize.cpp
+++ b/libsass/src/listize.cpp
@@ -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);
   }
 
 }
diff --git a/libsass/src/listize.hpp b/libsass/src/listize.hpp
--- a/libsass/src/listize.hpp
+++ b/libsass/src/listize.hpp
@@ -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); }
   };
 
 }
diff --git a/libsass/src/memory/SharedPtr.cpp b/libsass/src/memory/SharedPtr.cpp
--- a/libsass/src/memory/SharedPtr.cpp
+++ b/libsass/src/memory/SharedPtr.cpp
@@ -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();
-  }
-
 }
diff --git a/libsass/src/memory/SharedPtr.hpp b/libsass/src/memory/SharedPtr.hpp
--- a/libsass/src/memory/SharedPtr.hpp
+++ b/libsass/src/memory/SharedPtr.hpp
@@ -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
diff --git a/libsass/src/node.cpp b/libsass/src/node.cpp
deleted file mode 100644
--- a/libsass/src/node.cpp
+++ /dev/null
@@ -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;
-  }
-}
diff --git a/libsass/src/node.hpp b/libsass/src/node.hpp
deleted file mode 100644
--- a/libsass/src/node.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/operation.hpp b/libsass/src/operation.hpp
--- a/libsass/src/operation.hpp
+++ b/libsass/src/operation.hpp
@@ -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());
+    }
+
   };
 
 }
diff --git a/libsass/src/operators.cpp b/libsass/src/operators.cpp
--- a/libsass/src/operators.cpp
+++ b/libsass/src/operators.cpp
@@ -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),
diff --git a/libsass/src/operators.hpp b/libsass/src/operators.hpp
--- a/libsass/src/operators.hpp
+++ b/libsass/src/operators.hpp
@@ -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);
 
   };
 
diff --git a/libsass/src/ordered_map.hpp b/libsass/src/ordered_map.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/ordered_map.hpp
@@ -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
diff --git a/libsass/src/output.cpp b/libsass/src/output.cpp
--- a/libsass/src/output.cpp
+++ b/libsass/src/output.cpp
@@ -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 {
diff --git a/libsass/src/output.hpp b/libsass/src/output.hpp
--- a/libsass/src/output.hpp
+++ b/libsass/src/output.hpp
@@ -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);
 
   };
 
diff --git a/libsass/src/parser.cpp b/libsass/src/parser.cpp
--- a/libsass/src/parser.cpp
+++ b/libsass/src/parser.cpp
@@ -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);
     }
 
diff --git a/libsass/src/parser.hpp b/libsass/src/parser.hpp
--- a/libsass/src/parser.hpp
+++ b/libsass/src/parser.hpp
@@ -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);
diff --git a/libsass/src/parser_selectors.cpp b/libsass/src/parser_selectors.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/parser_selectors.cpp
@@ -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;
+  }
+
+
+}
diff --git a/libsass/src/paths.hpp b/libsass/src/paths.hpp
deleted file mode 100644
--- a/libsass/src/paths.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/permutate.hpp b/libsass/src/permutate.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/permutate.hpp
@@ -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
diff --git a/libsass/src/plugins.cpp b/libsass/src/plugins.cpp
--- a/libsass/src/plugins.cpp
+++ b/libsass/src/plugins.cpp
@@ -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
diff --git a/libsass/src/position.cpp b/libsass/src/position.cpp
--- a/libsass/src/position.cpp
+++ b/libsass/src/position.cpp
@@ -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 {
diff --git a/libsass/src/position.hpp b/libsass/src/position.hpp
--- a/libsass/src/position.hpp
+++ b/libsass/src/position.hpp
@@ -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");
diff --git a/libsass/src/prelexer.cpp b/libsass/src/prelexer.cpp
--- a/libsass/src/prelexer.cpp
+++ b/libsass/src/prelexer.cpp
@@ -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) {
diff --git a/libsass/src/prelexer.hpp b/libsass/src/prelexer.hpp
--- a/libsass/src/prelexer.hpp
+++ b/libsass/src/prelexer.hpp
@@ -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.
diff --git a/libsass/src/remove_placeholders.cpp b/libsass/src/remove_placeholders.cpp
--- a/libsass/src/remove_placeholders.cpp
+++ b/libsass/src/remove_placeholders.cpp
@@ -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);
     }
 
 }
diff --git a/libsass/src/remove_placeholders.hpp b/libsass/src/remove_placeholders.hpp
--- a/libsass/src/remove_placeholders.hpp
+++ b/libsass/src/remove_placeholders.hpp
@@ -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) {}
+
+  };
 
 }
 
diff --git a/libsass/src/sass.cpp b/libsass/src/sass.cpp
--- a/libsass/src/sass.cpp
+++ b/libsass/src/sass.cpp
@@ -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"
 
diff --git a/libsass/src/sass.hpp b/libsass/src/sass.hpp
--- a/libsass/src/sass.hpp
+++ b/libsass/src/sass.hpp
@@ -14,11 +14,17 @@
 // aplies to MSVC and MinGW
 #ifdef _WIN32
 // we do not want the ERROR macro
-# define NOGDI
+# ifndef NOGDI
+#  define NOGDI
+# endif
 // we do not want the min/max macro
-# define NOMINMAX
+# ifndef NOMINMAX
+#  define NOMINMAX
+# endif
 // we do not want the IN/OUT macro
-# define _NO_W32_PSEUDO_MODIFIERS
+# ifndef _NO_W32_PSEUDO_MODIFIERS
+#  define _NO_W32_PSEUDO_MODIFIERS
+# endif
 #endif
 
 
@@ -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)
diff --git a/libsass/src/sass2scss.cpp b/libsass/src/sass2scss.cpp
--- a/libsass/src/sass2scss.cpp
+++ b/libsass/src/sass2scss.cpp
@@ -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
diff --git a/libsass/src/sass_context.cpp b/libsass/src/sass_context.cpp
--- a/libsass/src/sass_context.cpp
+++ b/libsass/src/sass_context.cpp
@@ -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);
diff --git a/libsass/src/sass_context.hpp b/libsass/src/sass_context.hpp
--- a/libsass/src/sass_context.hpp
+++ b/libsass/src/sass_context.hpp
@@ -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;
diff --git a/libsass/src/sass_functions.cpp b/libsass/src/sass_functions.cpp
--- a/libsass/src/sass_functions.cpp
+++ b/libsass/src/sass_functions.cpp
@@ -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) {
diff --git a/libsass/src/sass_functions.hpp b/libsass/src/sass_functions.hpp
--- a/libsass/src/sass_functions.hpp
+++ b/libsass/src/sass_functions.hpp
@@ -3,7 +3,7 @@
 
 #include "sass.h"
 #include "environment.hpp"
-#include "functions.hpp"
+#include "fn_utils.hpp"
 
 // Struct to hold custom function callback
 struct Sass_Function {
diff --git a/libsass/src/sass_util.cpp b/libsass/src/sass_util.cpp
deleted file mode 100644
--- a/libsass/src/sass_util.cpp
+++ /dev/null
@@ -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;
-  }
-}
diff --git a/libsass/src/sass_util.hpp b/libsass/src/sass_util.hpp
deleted file mode 100644
--- a/libsass/src/sass_util.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/sass_values.cpp b/libsass/src/sass_values.cpp
--- a/libsass/src/sass_values.cpp
+++ b/libsass/src/sass_values.cpp
@@ -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
diff --git a/libsass/src/source_map.cpp b/libsass/src/source_map.cpp
--- a/libsass/src/source_map.cpp
+++ b/libsass/src/source_map.cpp
@@ -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));
   }
diff --git a/libsass/src/source_map.hpp b/libsass/src/source_map.hpp
--- a/libsass/src/source_map.hpp
+++ b/libsass/src/source_map.hpp
@@ -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);
diff --git a/libsass/src/stylesheet.cpp b/libsass/src/stylesheet.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/stylesheet.cpp
@@ -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)
+  {
+  }
+
+}
diff --git a/libsass/src/stylesheet.hpp b/libsass/src/stylesheet.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/stylesheet.hpp
@@ -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
diff --git a/libsass/src/subset_map.cpp b/libsass/src/subset_map.cpp
deleted file mode 100644
--- a/libsass/src/subset_map.cpp
+++ /dev/null
@@ -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);
-  }
-
-}
diff --git a/libsass/src/subset_map.hpp b/libsass/src/subset_map.hpp
deleted file mode 100644
--- a/libsass/src/subset_map.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/to_c.cpp b/libsass/src/to_c.cpp
deleted file mode 100644
--- a/libsass/src/to_c.cpp
+++ /dev/null
@@ -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(); }
-
-};
diff --git a/libsass/src/to_c.hpp b/libsass/src/to_c.hpp
deleted file mode 100644
--- a/libsass/src/to_c.hpp
+++ /dev/null
@@ -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
diff --git a/libsass/src/to_value.cpp b/libsass/src/to_value.cpp
--- a/libsass/src/to_value.cpp
+++ b/libsass/src/to_value.cpp
@@ -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(),
diff --git a/libsass/src/to_value.hpp b/libsass/src/to_value.hpp
--- a/libsass/src/to_value.hpp
+++ b/libsass/src/to_value.hpp
@@ -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); }
   };
 
 }
diff --git a/libsass/src/units.cpp b/libsass/src/units.cpp
--- a/libsass/src/units.cpp
+++ b/libsass/src/units.cpp
@@ -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) {
diff --git a/libsass/src/units.hpp b/libsass/src/units.hpp
--- a/libsass/src/units.hpp
+++ b/libsass/src/units.hpp
@@ -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;
   };
diff --git a/libsass/src/utf8/checked.h b/libsass/src/utf8/checked.h
--- a/libsass/src/utf8/checked.h
+++ b/libsass/src/utf8/checked.h
@@ -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");
diff --git a/libsass/src/utf8/core.h b/libsass/src/utf8/core.h
--- a/libsass/src/utf8/core.h
+++ b/libsass/src/utf8/core.h
@@ -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;
diff --git a/libsass/src/utf8_string.cpp b/libsass/src/utf8_string.cpp
--- a/libsass/src/utf8_string.cpp
+++ b/libsass/src/utf8_string.cpp
@@ -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>
diff --git a/libsass/src/util.cpp b/libsass/src/util.cpp
--- a/libsass/src/util.cpp
+++ b/libsass/src/util.cpp
@@ -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;
     }
 
   }
diff --git a/libsass/src/util.hpp b/libsass/src/util.hpp
--- a/libsass/src/util.hpp
+++ b/libsass/src/util.hpp
@@ -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);
 
   }
 }
diff --git a/libsass/src/util_string.cpp b/libsass/src/util_string.cpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/util_string.cpp
@@ -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
diff --git a/libsass/src/util_string.hpp b/libsass/src/util_string.hpp
new file mode 100644
--- /dev/null
+++ b/libsass/src/util_string.hpp
@@ -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
diff --git a/libsass/src/values.cpp b/libsass/src/values.cpp
--- a/libsass/src/values.cpp
+++ b/libsass/src/values.cpp
@@ -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)),
diff --git a/libsass/src/values.hpp b/libsass/src/values.hpp
--- a/libsass/src/values.hpp
+++ b/libsass/src/values.hpp
@@ -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
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -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
