packages feed

hlibsass 0.1.5.2 → 0.1.6.0

raw patch · 108 files changed

+16609/−12791 lines, 108 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Bindings.Libsass: libsass_version :: CString
+ Bindings.Libsass.Base: libsass_language_version :: CString
+ Bindings.Libsass.Base: libsass_version :: CString
+ Bindings.Libsass.Base: sass_alloc_memory :: CSize -> IO (Ptr ())
+ Bindings.Libsass.Base: sass_copy_c_string :: CString -> IO CString
+ Bindings.Libsass.Base: sass_free_memory :: Ptr () -> IO ()
+ Bindings.Libsass.Base: sass_resolve_file :: CString -> Ptr CString -> IO CString
+ Bindings.Libsass.Base: sass_string_quote :: CString -> CChar -> IO CString
+ Bindings.Libsass.Base: sass_string_unquote :: CString -> IO CString
+ Bindings.Libsass.Context: sass_compiler_get_options :: Ptr SassCompiler -> IO (Ptr SassOptions)
+ Bindings.Libsass.Context: sass_delete_options :: Ptr SassOptions -> IO ()
+ Bindings.Libsass.Context: sass_option_get_source_map_file_urls :: Ptr SassOptions -> IO Bool
+ Bindings.Libsass.Context: sass_option_set_source_map_file_urls :: Ptr SassOptions -> Bool -> IO ()
+ Bindings.Libsass.Types: SassSeparatorHash :: SassSeparator
+ Bindings.Libsass.Types: SassStyleInspect :: SassOutputStyle
+ Bindings.Libsass.Types: SassStyleToSass :: SassOutputStyle

Files

Bindings/Libsass.hs view
@@ -5,15 +5,12 @@ {-# LANGUAGE ForeignFunctionInterface #-} module Bindings.Libsass   (-    libsass_version-  , module Libsass+    module Libsass   ) where +import           Bindings.Libsass.Types     as Libsass+import           Bindings.Libsass.Base      as Libsass import           Bindings.Libsass.Context   as Libsass import           Bindings.Libsass.Functions as Libsass-import           Bindings.Libsass.Types     as Libsass-import           Bindings.Libsass.Wrappers  as Libsass import           Bindings.Libsass.Values    as Libsass-import           Foreign.C.String--foreign import ccall unsafe "sass/base.h" libsass_version :: CString+import           Bindings.Libsass.Wrappers  as Libsass
+ Bindings/Libsass/Base.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Bindings.Libsass.Base where++import           Foreign+import           Foreign.C++foreign import ccall unsafe "sass/base.h" sass_alloc_memory+    :: CSize+    -> IO (Ptr ())++foreign import ccall unsafe "sass/base.h" sass_copy_c_string+    :: CString+    -> IO CString++foreign import ccall unsafe "sass/base.h" sass_free_memory+    :: Ptr ()+    -> IO ()++foreign import ccall unsafe "sass/base.h" sass_string_quote+    :: CString+    -> CChar+    -> IO CString++foreign import ccall unsafe "sass/base.h" sass_string_unquote+    :: CString+    -> IO CString++foreign import ccall unsafe "sass/base.h" sass_resolve_file+    :: CString+    -> Ptr CString+    -> IO CString++foreign import ccall unsafe "sass/base.h" libsass_version+    :: CString++foreign import ccall unsafe "sass/base.h" libsass_language_version+    :: CString
Bindings/Libsass/Context.hs view
@@ -3,8 +3,7 @@  import           Bindings.Libsass.Types import           Foreign-import           Foreign.C.String-import           Foreign.C.Types+import           Foreign.C  foreign import ccall unsafe "sass/context.h" sass_make_options     :: IO (Ptr SassOptions)@@ -50,6 +49,10 @@     :: Ptr SassCompiler     -> IO () +foreign import ccall unsafe "sass/context.h" sass_delete_options+    :: Ptr SassOptions+    -> IO ()+ foreign import ccall unsafe "sass/context.h" sass_delete_file_context     :: Ptr SassFileContext     -> IO ()@@ -108,6 +111,10 @@     :: Ptr SassOptions     -> IO Bool +foreign import ccall unsafe "sass/context.h" sass_option_get_source_map_file_urls+    :: Ptr SassOptions+    -> IO Bool+ foreign import ccall unsafe "sass/context.h" sass_option_get_omit_source_map_url     :: Ptr SassOptions     -> IO Bool@@ -185,6 +192,11 @@     -> Bool     -> IO () +foreign import ccall unsafe "sass/context.h" sass_option_set_source_map_file_urls+    :: Ptr SassOptions+    -> Bool+    -> IO ()+ foreign import ccall unsafe "sass/context.h" sass_option_set_omit_source_map_url     :: Ptr SassOptions     -> Bool@@ -333,6 +345,10 @@ foreign import ccall unsafe "sass/context.h" sass_compiler_get_context     :: Ptr SassCompiler     -> IO (Ptr SassContext)++foreign import ccall unsafe "sass/context.h" sass_compiler_get_options+    :: Ptr SassCompiler+    -> IO (Ptr SassOptions)  foreign import ccall unsafe "sass/context.h" sass_compiler_get_import_stack_size     :: Ptr SassCompiler
Bindings/Libsass/Types.hs view
@@ -2,15 +2,17 @@ module Bindings.Libsass.Types where  import           Foreign-import           Foreign.C.String+import           Foreign.C --- From sass.h+-- From sass/base.h  -- | Defines output style of compiled CSS. data SassOutputStyle = SassStyleNested                      | SassStyleExpanded                      | SassStyleCompact                      | SassStyleCompressed+                     | SassStyleInspect -- ^ Marked as internal+                     | SassStyleToSass -- ^ Marked as internal                      deriving (Eq, Show)  instance Enum SassOutputStyle where@@ -18,14 +20,18 @@     fromEnum SassStyleExpanded   = 1     fromEnum SassStyleCompact    = 2     fromEnum SassStyleCompressed = 3+    fromEnum SassStyleInspect    = 4+    fromEnum SassStyleToSass     = 5      toEnum 0 = SassStyleNested     toEnum 1 = SassStyleExpanded     toEnum 2 = SassStyleCompact     toEnum 3 = SassStyleCompressed+    toEnum 4 = SassStyleInspect+    toEnum 5 = SassStyleToSass     toEnum u = error ("SassOutputStyle.toEnum: Cannot match " ++ show u) --- From sass_context.h+-- From sass/context.h  data SassCompiler data SassOptions@@ -48,7 +54,7 @@     toEnum 2 = SassCompilerExecuted     toEnum u = error ("SassCompilerState.toEnum: Cannot match " ++ show u) --- From sass_functions.h+-- From sass/functions.h  data SassImport data SassImporter@@ -76,7 +82,7 @@   -> IO (Ptr SassValue) type SassFunctionFn = FunPtr SassFunctionFnType --- From sass_values.h+-- From sass/values.h  data SassValue @@ -113,20 +119,23 @@     toEnum 8 = SassWarning     toEnum u = error ("SassTag.toEnum: Cannot match " ++ show u) --- ^ Separator used in Sass lists.+-- | Separator used in Sass lists. data SassSeparator = SassSeparatorComma                    | SassSeparatorSpace+                   | SassSeparatorHash -- ^ Marked as internal                    deriving (Eq, Show)  instance Enum SassSeparator where     fromEnum SassSeparatorComma = 0-    fromEnum SassSeparatorSpace  = 1+    fromEnum SassSeparatorSpace = 1+    fromEnum SassSeparatorHash  = 2      toEnum 0 = SassSeparatorComma     toEnum 1 = SassSeparatorSpace+    toEnum 2 = SassSeparatorHash     toEnum u = error ("SassSeparator.toEnum: Cannot match " ++ show u) --- ^ Operator used to combine two 'SassValue's.+-- | Operator used to combine two 'SassValue's. data SassOp = SassAnd             | SassOr             | SassEq
Bindings/Libsass/Values.hs view
@@ -5,6 +5,70 @@ import           Foreign import           Foreign.C +foreign import ccall unsafe "sass/values.h" sass_make_null+    :: IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_boolean+    :: Bool+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_string+    :: CString+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_qstring+    :: CString+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_number+    :: CDouble+    -> CString+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_color+    :: CDouble+    -> CDouble+    -> CDouble+    -> CDouble+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_list+    :: CSize+    -> CInt -- ^ 'SassSeparator'+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_map+    :: CSize+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_error+    :: CString+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_warning+    :: CString+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_delete_value+    :: Ptr SassValue+    -> IO ()++foreign import ccall unsafe "sass/values.h" sass_clone_value+    :: Ptr SassValue+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_value_op+    :: CInt -- ^ 'SassOp'+    -> Ptr SassValue+    -> Ptr SassValue+    -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_value_stringify+    :: Ptr SassValue+    -> Bool+    -> CInt+    -> IO (Ptr SassValue)+ foreign import ccall unsafe "sass/values.h" sass_value_get_tag     :: Ptr SassValue     -> IO CInt -- ^ Returns 'SassTag'@@ -193,67 +257,3 @@     :: Ptr SassValue     -> CString     -> IO ()--foreign import ccall unsafe "sass/values.h" sass_make_null-    :: IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_boolean-    :: Bool-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_string-    :: CString-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_qstring-    :: CString-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_number-    :: CDouble-    -> CString-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_color-    :: CDouble-    -> CDouble-    -> CDouble-    -> CDouble-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_list-    :: CSize-    -> CInt -- ^ 'SassSeparator'-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_map-    :: CSize-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_error-    :: CString-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_warning-    :: CString-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_delete_value-    :: Ptr SassValue-    -> IO ()--foreign import ccall unsafe "sass/values.h" sass_clone_value-    :: Ptr SassValue-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_value_stringify-    :: Ptr SassValue-    -> Bool-    -> CInt-    -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_value_op-    :: CInt -- ^ 'SassOp'-    -> Ptr SassValue-    -> Ptr SassValue-    -> IO (Ptr SassValue)
CHANGELOG.md view
@@ -1,6 +1,13 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.1.6.0] - 2017-03-25+### Fixed+- `tools/libpath.sh` recognizes `stack`++### Changed+- Update to Libsass 3.4.3+ ## [0.1.5.2] - 2017-03-19 ### Fixed - Build process for dependant libraries works with Cabal 1.24 & Stack 1.4@@ -63,6 +70,7 @@ - Bindings to Libsass C API - Basic tests +[0.1.6.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.5.2...v0.1.6.0 [0.1.5.2]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.5.0...v0.1.5.2 [0.1.5.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.4.0...v0.1.5.0 [0.1.4.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.3.0...v0.1.4.0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015 Jakub Fijałkowski+Copyright (c) 2015-2017 Jakub Fijałkowski  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
hlibsass.cabal view
@@ -1,5 +1,5 @@ name:                hlibsass-version:             0.1.5.2+version:             0.1.6.0 license:             MIT license-file:        LICENSE author:              Jakub Fijałkowski <fiolek94@gmail.com>@@ -26,6 +26,8 @@   libsass/src/b64/*.h   libsass/src/utf8/*.h   libsass/src/support/*.pc.in+  libsass/src/memory/*.hpp+  libsass/src/memory/*.cpp   libsass/include/*.h   libsass/include/sass/*.h   libsass/include/sass/*.h.in@@ -54,6 +56,7 @@ library   exposed-modules:       Bindings.Libsass+    , Bindings.Libsass.Base     , Bindings.Libsass.Types     , Bindings.Libsass.Wrappers     , Bindings.Libsass.Context@@ -64,10 +67,12 @@   default-language:    Haskell2010   default-extensions:  ForeignFunctionInterface   ghc-options:         -Wall-  if flag(externalLibsass)-    extra-libraries:   sass-  else-    extra-libraries:   sass, stdc+++  extra-libraries:     sass+  if !flag(externalLibsass) && !flag(sharedLibsass)+    if os(darwin)+      extra-libraries: c+++    else+      extra-libraries: stdc++  test-suite tests   hs-source-dirs:      tests
libsass/LICENSE view
@@ -1,5 +1,5 @@ -Copyright (C) 2012 by Hampton Catlin+Copyright (C) 2012-2016 by the Sass Open Source Foundation  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in
libsass/Makefile view
@@ -1,3 +1,4 @@+OS       ?= $(shell uname -s) CC       ?= gcc CXX      ?= g++ RM       ?= rm -f@@ -5,6 +6,11 @@ MKDIR    ?= mkdir RMDIR    ?= rmdir WINDRES  ?= windres+# Solaris/Illumos flavors+# ginstall from coreutils+ifeq ($(OS),SunOS)+INSTALL  ?= ginstall+endif INSTALL  ?= install CFLAGS   ?= -Wall CXXFLAGS ?= -Wall@@ -20,14 +26,14 @@ ifneq (,$(findstring /cygdrive/,$(PATH))) 	UNAME := Cygwin else-	ifneq (,$(findstring WINDOWS,$(PATH)))+	ifneq (,$(findstring Windows_NT,$(OS))) 		UNAME := Windows 	else 		ifneq (,$(findstring mingw32,$(MAKE)))-			UNAME := MinGW+			UNAME := Windows 		else 			ifneq (,$(findstring MINGW32,$(shell uname -s)))-				UNAME = MinGW+				UNAME = Windows 			else 				UNAME := $(shell uname -s) 			endif@@ -57,7 +63,7 @@ endif  # enable mandatory flag-ifeq (MinGW,$(UNAME))+ifeq (Windows,$(UNAME)) 	ifneq ($(BUILD),shared) 		STATIC_ALL     ?= 1 	endif@@ -117,25 +123,33 @@ 	LDFLAGS += -stdlib=libc++ endif -ifneq (MinGW,$(UNAME))+ifneq (Windows,$(UNAME)) 	ifneq (FreeBSD,$(UNAME))-		LDFLAGS += -ldl-		LDLIBS += -ldl+		ifneq (OpenBSD,$(UNAME))+			LDFLAGS += -ldl+			LDLIBS += -ldl+		endif 	endif endif  ifneq ($(BUILD),shared)-	BUILD = static+	BUILD := static endif+ifeq ($(DEBUG),1)+	BUILD := debug-$(BUILD)+endif -ifeq (,$(PREFIX))-	ifeq (,$(TRAVIS_BUILD_DIR))-		PREFIX = /usr/local+ifeq (,$(TRAVIS_BUILD_DIR))+	ifeq ($(OS),SunOS)+		PREFIX ?= /opt/local 	else-		PREFIX = $(TRAVIS_BUILD_DIR)+		PREFIX ?= /usr/local 	endif+else+	PREFIX ?= $(TRAVIS_BUILD_DIR) endif + SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec@@ -145,7 +159,7 @@ LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so -ifeq (MinGW,$(UNAME))+ifeq (Windows,$(UNAME)) 	ifeq (shared,$(BUILD)) 		CFLAGS     += -D ADD_EXPORTS 		CXXFLAGS   += -D ADD_EXPORTS@@ -159,9 +173,6 @@ 	endif endif -ifeq (MinGW,$(UNAME))-	SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe-endif ifeq (Windows,$(UNAME)) 	SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe endif@@ -171,7 +182,7 @@ RESOURCES = STATICLIB = lib/libsass.a SHAREDLIB = lib/libsass.so-ifeq (MinGW,$(UNAME))+ifeq (Windows,$(UNAME)) 	RESOURCES += res/resource.rc 	SHAREDLIB  = lib/libsass.dll 	ifeq (shared,$(BUILD))@@ -294,13 +305,16 @@ 	$(SASSC_BIN) -v  test: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)  test_build: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) -test_issues: $(SASSC_BIN)-	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues+test_full: $(SASSC_BIN)+	$(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass --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.4 -c $(SASSC_BIN) --impl libsass --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)  clean-objects: lib 	-$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la
libsass/Makefile.conf view
@@ -1,47 +1,52 @@ # this is merely a common Makefile multiple implementers can use+# bigger files (in terms of compile time) tend to go to the top,+# so they don't end up as the last compile unit when compiling+# in parallel. But we also want to mix them a little too avoid+# heavy RAM usage peaks. Other than that the order is arbitrary. + SOURCES = \ 	ast.cpp \-	base64vlq.cpp \-	bind.cpp \-	color_maps.cpp \-	constants.cpp \+	node.cpp \ 	context.cpp \-	cssize.cpp \-	emitter.cpp \+	constants.cpp \+	functions.cpp \+	color_maps.cpp \ 	environment.cpp \-	error_handling.cpp \-	eval.cpp \-	expand.cpp \-	extend.cpp \+	bind.cpp \ 	file.cpp \-	functions.cpp \-	inspect.cpp \+	util.cpp \ 	json.cpp \-	lexer.cpp \-	listize.cpp \-	memory_manager.cpp \-	node.cpp \-	output.cpp \-	parser.cpp \+	units.cpp \+	values.cpp \ 	plugins.cpp \ 	position.cpp \+	lexer.cpp \+	parser.cpp \ 	prelexer.cpp \+	eval.cpp \+	expand.cpp \+	listize.cpp \+	cssize.cpp \+	extend.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 \-	sass_interface.cpp \ 	sass2scss.cpp \-	source_map.cpp \ 	to_c.cpp \-	to_string.cpp \ 	to_value.cpp \-	units.cpp \+	source_map.cpp \+	subset_map.cpp \+	error_handling.cpp \+	memory/SharedPtr.cpp \ 	utf8_string.cpp \-	values.cpp \-	util.cpp+	base64vlq.cpp  CSOURCES = cencode.c
libsass/VERSION view
@@ -1,1 +1,1 @@-3.3.2+3.4.3
libsass/include/sass/base.h view
@@ -1,6 +1,8 @@ #ifndef SASS_BASE_H #define SASS_BASE_H +// #define DEBUG_SHARED_PTR+ #ifdef _MSC_VER   #pragma warning(disable : 4503)   #ifndef _SCL_SECURE_NO_WARNINGS@@ -56,9 +58,19 @@   SASS_STYLE_NESTED,   SASS_STYLE_EXPANDED,   SASS_STYLE_COMPACT,-  SASS_STYLE_COMPRESSED+  SASS_STYLE_COMPRESSED,+  // only used internaly+  SASS_STYLE_INSPECT,+  SASS_STYLE_TO_SASS }; +// to allocate buffer to be filled+ADDAPI void* ADDCALL sass_alloc_memory(size_t size);+// to allocate a buffer from existing string+ADDAPI char* ADDCALL sass_copy_c_string(const char* str);+// to free overtaken memory when done+ADDAPI void ADDCALL sass_free_memory(void* ptr);+ // Some convenient string helper function ADDAPI char* ADDCALL sass_string_quote (const char* str, const char quote_mark); ADDAPI char* ADDCALL sass_string_unquote (const char* str);@@ -66,8 +78,12 @@ // Resolve a file via the given include paths in the include char* array ADDAPI char* ADDCALL sass_resolve_file (const char* path, const char* incs[]); -// Get compiled libsass version+// Implemented sass language version+// Hardcoded version 3.4 for time being ADDAPI const char* ADDCALL libsass_version(void);++// Get compiled libsass language+ADDAPI const char* ADDCALL libsass_language_version(void);  #ifdef __cplusplus } // __cplusplus defined.
libsass/include/sass/context.h view
@@ -50,6 +50,7 @@ // Release all memory allocated with the compiler // This does _not_ include any contexts or options ADDAPI void ADDCALL sass_delete_compiler(struct Sass_Compiler* compiler);+ADDAPI void ADDCALL sass_delete_options(struct Sass_Options* options);  // Release all memory allocated and also ourself ADDAPI void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx);@@ -73,6 +74,7 @@ ADDAPI bool ADDCALL sass_option_get_source_comments (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_embed (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_contents (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_source_map_file_urls (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_omit_source_map_url (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_is_indented_syntax_src (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_indent (struct Sass_Options* options);@@ -93,6 +95,7 @@ ADDAPI void ADDCALL sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); ADDAPI void ADDCALL sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); ADDAPI void ADDCALL sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents);+ADDAPI void ADDCALL sass_option_set_source_map_file_urls (struct Sass_Options* options, bool source_map_file_urls); ADDAPI void ADDCALL sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); ADDAPI void ADDCALL sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); ADDAPI void ADDCALL sass_option_set_indent (struct Sass_Options* options, const char* indent);
− libsass/include/sass/interface.h
@@ -1,105 +0,0 @@-#ifndef SASS_C_INTERFACE_H-#define SASS_C_INTERFACE_H--// the API in this header has been deprecated-// please use the new API from sass/context.h--#include <stddef.h>-#include <stdbool.h>-#include <sass/base.h>--#ifdef __cplusplus-extern "C" {-#endif---// Please ensure there are no null values.-// Thar be dragons.-struct sass_options {-  // Output style for the generated css code-  // A value from above SASS_STYLE_* constants-  int output_style;-  // If you want inline source comments-  bool source_comments;-  // Path to source map file-  // Enables the source map generating-  // Used to create sourceMappingUrl-  const char* source_map_file;-  // Disable sourceMappingUrl in css output-  bool omit_source_map_url;-  // embed sourceMappingUrl as data uri-  bool source_map_embed;-  // embed include contents in maps-  bool source_map_contents;-  // Pass-through as sourceRoot property-  const char* source_map_root;-  // Treat source_string as sass (as opposed to scss)-  bool is_indented_syntax_src;-  // Colon-separated list of paths-  // Semicolon-separated on Windows-  const char* include_paths;-  const char* plugin_paths;-  // String to be used for indentation-  const char* indent;-  // String to be used to for line feeds-  const char* linefeed;-  // Precision for outputting fractional numbers-  int precision;-};--struct sass_context {-  const char* input_path;-  const char* output_path;-  char* source_string;-  char* output_string;-  char* source_map_string;-  struct sass_options options;-  int error_status;-  char* error_message;-  Sass_Function_List c_functions;-  char** included_files;-  int num_included_files;-};--struct sass_file_context {-  const char* input_path;-  const char* output_path;-  char* output_string;-  char* source_map_string;-  struct sass_options options;-  int error_status;-  char* error_message;-  Sass_Function_List c_functions;-  char** included_files;-  int num_included_files;-};--struct sass_folder_context {-  const char* search_path;-  const char* output_path;-  struct sass_options options;-  int error_status;-  char* error_message;-  Sass_Function_List c_functions;-  char** included_files;-  int num_included_files;-};--struct sass_context* sass_new_context (void);-struct sass_file_context* sass_new_file_context (void);-struct sass_folder_context* sass_new_folder_context (void);--void sass_free_context (struct sass_context* ctx);-void sass_free_file_context (struct sass_file_context* ctx);-void sass_free_folder_context(struct sass_folder_context* ctx);--int sass_compile (struct sass_context* ctx);-int sass_compile_file (struct sass_file_context* ctx);-int sass_compile_folder (struct sass_folder_context* ctx);---#ifdef __cplusplus-}-#endif--#endif
libsass/include/sass/values.h view
@@ -29,7 +29,10 @@ // Tags for denoting Sass list separators enum Sass_Separator {   SASS_COMMA,-  SASS_SPACE+  SASS_SPACE,+  // only used internally to represent a hash map before evaluation+  // otherwise we would be too early to check for duplicate keys+  SASS_HASH };  // Value Operators@@ -40,6 +43,32 @@   NUM_OPS                    // so we know how big to make the op table }; +// Creator functions for all value types+ADDAPI union Sass_Value* ADDCALL sass_make_null    (void);+ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);+ADDAPI union Sass_Value* ADDCALL sass_make_string  (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_number  (double val, const char* unit);+ADDAPI union Sass_Value* ADDCALL sass_make_color   (double r, double g, double b, double a);+ADDAPI union Sass_Value* ADDCALL sass_make_list    (size_t len, enum Sass_Separator sep);+ADDAPI union Sass_Value* ADDCALL sass_make_map     (size_t len);+ADDAPI union Sass_Value* ADDCALL sass_make_error   (const char* msg);+ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);++// Generic destructor function for all types+// Will release memory of all associated Sass_Values+// Means we will delete recursively for lists and maps+ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);++// Make a deep cloned copy of the given sass value+ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);++// Execute an operation for two Sass_Values and return the result as a Sass_Value too+ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b);++// Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING)+ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision);+ // Return the sass tag for a generic sass value // Check is needed before accessing specific values! ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);@@ -106,33 +135,6 @@ // Getters and setters for Sass_Warning ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v); ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg);--// Creator functions for all value types-ADDAPI union Sass_Value* ADDCALL sass_make_null    (void);-ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);-ADDAPI union Sass_Value* ADDCALL sass_make_string  (const char* val);-ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);-ADDAPI union Sass_Value* ADDCALL sass_make_number  (double val, const char* unit);-ADDAPI union Sass_Value* ADDCALL sass_make_color   (double r, double g, double b, double a);-ADDAPI union Sass_Value* ADDCALL sass_make_list    (size_t len, enum Sass_Separator sep);-ADDAPI union Sass_Value* ADDCALL sass_make_map     (size_t len);-ADDAPI union Sass_Value* ADDCALL sass_make_error   (const char* msg);-ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);--// Generic destructor function for all types-// Will release memory of all associated Sass_Values-// Means we will delete recursively for lists and maps-ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);--// Make a deep cloned copy of the given sass value-ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);--// Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING)-ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision);--// Execute an operation for two Sass_Values and return the result as a Sass_Value too-ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b);-  #ifdef __cplusplus } // __cplusplus defined.
libsass/include/sass/version.h view
@@ -5,4 +5,8 @@ #define LIBSASS_VERSION "[NA]" #endif +#ifndef LIBSASS_LANGUAGE_VERSION+#define LIBSASS_LANGUAGE_VERSION "3.4"+#endif+ #endif
libsass/include/sass/version.h.in view
@@ -5,4 +5,8 @@ #define LIBSASS_VERSION "@PACKAGE_VERSION@" #endif +#ifndef LIBSASS_LANGUAGE_VERSION+#define LIBSASS_LANGUAGE_VERSION "3.4"+#endif+ #endif
libsass/include/sass2scss.h view
@@ -37,7 +37,7 @@ #ifndef SASS2SCSS_VERSION // Hardcode once the file is copied from // https://github.com/mgreter/sass2scss-#define SASS2SCSS_VERSION "1.0.5"+#define SASS2SCSS_VERSION "1.1.0" #endif  // add namespace for c++
libsass/src/ast.cpp view
@@ -1,2134 +1,2451 @@-#include "ast.hpp"-#include "context.hpp"-#include "node.hpp"-#include "extend.hpp"-#include "to_string.hpp"-#include "color_maps.hpp"-#include <set>-#include <iomanip>-#include <algorithm>-#include <iostream>--namespace Sass {--  static Null sass_null(Sass::Null(ParserState("null")));--  bool Supports_Operator::needs_parens(Supports_Condition* cond) const {-    return dynamic_cast<Supports_Negation*>(cond) ||-          (dynamic_cast<Supports_Operator*>(cond) &&-           dynamic_cast<Supports_Operator*>(cond)->operand() != operand());-  }--  bool Supports_Negation::needs_parens(Supports_Condition* cond) const {-    return dynamic_cast<Supports_Negation*>(cond) ||-          dynamic_cast<Supports_Operator*>(cond);-  }--  void AST_Node::update_pstate(const ParserState& pstate)-  {-    pstate_.offset += pstate - pstate_ + pstate.offset;-  }--  inline bool is_ns_eq(const std::string& l, const std::string& r)-  {-    if (l.empty() && r.empty()) return true;-    else if (l.empty() && r == "*") return true;-    else if (r.empty() && l == "*") return true;-    else return l == r;-  }----  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* l = (*this)[i];-      Simple_Selector* 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()-  {-    return has_parent_reference();-  }--  bool Complex_Selector::has_parent_ref()-  {-    return (head() && head()->has_parent_ref()) ||-           (tail() && tail()->has_parent_ref());-  }--  bool Complex_Selector::operator< (const Complex_Selector& rhs) const-  {-    // const iterators for tails-    const Complex_Selector* l = this;-    const Complex_Selector* r = &rhs;-    Compound_Selector* l_h = l ? l->head() : 0;-    Compound_Selector* r_h = r ? r->head() : 0;-    // process all tails-    while (true)-    {-      // skip empty ancestor first-      if (l && l->is_empty_ancestor())-      {-        l = l->tail();-        l_h = l ? l->head() : 0;-        continue;-      }-      // skip empty ancestor first-      if (r && r->is_empty_ancestor())-      {-        r = r->tail();-        r_h = r ? r->head() : 0;-        continue;-      }-      // 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 = l ? l->head() : 0;-        r_h = r ? r->head() : 0;-      }-      // 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 = l ? l->head() : 0;-        r_h = r ? r->head() : 0;-      }-      // heads are not equal-      else return *l_h < *r_h;-    }-    return true;-  }--  bool Complex_Selector::operator== (const Complex_Selector& rhs) const-  {-    // const iterators for tails-    const Complex_Selector* l = this;-    const Complex_Selector* r = &rhs;-    Compound_Selector* l_h = l ? l->head() : 0;-    Compound_Selector* r_h = r ? r->head() : 0;-    // process all tails-    while (true)-    {-      // skip empty ancestor first-      if (l && l->is_empty_ancestor())-      {-        l = l->tail();-        l_h = l ? l->head() : 0;-        continue;-      }-      // skip empty ancestor first-      if (r && r->is_empty_ancestor())-      {-        r = r->tail();-        r_h = r ? r->head() : 0;-        continue;-      }-      // 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 = l ? l->head() : 0;-        r_h = r ? r->head() : 0;-      }-      // fail if only one is null-      else if (!r_h) return !l_h;-      else if (!l_h) return !r_h;-      // 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 heads-        l_h = l ? l->head() : 0;-        r_h = r ? r->head() : 0;-      }-      // abort-      else break;-    }-    // unreachable-    return false;-  }--  Compound_Selector* Compound_Selector::unify_with(Compound_Selector* rhs, Context& ctx)-  {-    Compound_Selector* unified = rhs;-    for (size_t i = 0, L = length(); i < L; ++i)-    {-      if (!unified) break;-      unified = (*this)[i]->unify_with(unified, ctx);-    }-    return unified;-  }--  bool Simple_Selector::operator== (const Simple_Selector& rhs) const-  {-    const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);-    const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);-    if (ll && rr) return *ll == *rr;--    if (is_ns_eq(ns(), rhs.ns()))-    { return name() == rhs.name(); }-    return ns() == rhs.ns();-  }--  bool Simple_Selector::operator< (const Simple_Selector& rhs) const-  {-    const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);-    const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);-    if (ll && rr) return *ll < *rr;--    if (is_ns_eq(ns(), rhs.ns()))-    { 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 (const Selector_List* ls = dynamic_cast<const Selector_List*>(&rhs)) { return *this == *ls; }-    else if (const Complex_Selector* ls = dynamic_cast<const Complex_Selector*>(&rhs)) { return *this == *ls; }-    else if (const Compound_Selector* ls = dynamic_cast<const Compound_Selector*>(&rhs)) { return *this == *ls; }-    // 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 (const List* ls = dynamic_cast<const List*>(&rhs)) { return *this == *ls; }-    if (const Selector* ls = dynamic_cast<const 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*> l_lst = this->elements();-    std::vector<Complex_Selector*> r_lst = rhs.elements();-    std::sort(l_lst.begin(), l_lst.end(), cmp_complex_selector());-    std::sort(r_lst.begin(), r_lst.end(), cmp_complex_selector());-    // 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* l = l_lst[i];-      Complex_Selector* 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;-    }-    // no mismatch-    return true;-  }--  Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx)-  {-    To_String to_string(&ctx);-    for (size_t i = 0, L = rhs->length(); i < L; ++i)-    { if (perform(&to_string) == (*rhs)[i]->perform(&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))-    {-      for (i = 0, L = rhs->length(); i < L; ++i)-      {-        if ((typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) && (*rhs)[L-1]->is_pseudo_element())-        { found = true; break; }-      }-    }-    else-    {-      for (i = 0, L = rhs->length(); i < L; ++i)-      {-        if (typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector))-        { found = true; break; }-      }-    }-    if (!found)-    {-      Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);-      (*cpy) << this;-      return cpy;-    }-    Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());-    for (size_t j = 0; j < i; ++j)-    { (*cpy) << (*rhs)[j]; }-    (*cpy) << this;-    for (size_t j = i; j < L; ++j)-    { (*cpy) << (*rhs)[j]; }-    return cpy;-  }--  Simple_Selector* Type_Selector::unify_with(Simple_Selector* rhs, Context& ctx)-  {-    // 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())-      {-        // creaty the copy inside (avoid unnecessary copies)-        Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this);-        // overwrite the name if star is given as name-        if (ts->name() == "*") { ts->name(rhs->name()); }-        // now overwrite the namespace name and flag-        ts->ns(rhs->ns()); ts->has_ns(rhs->has_ns());-        // return copy-        return ts;-      }-    }-    // namespace may changed, check the name now-    // overwrite star (but not with another star)-    if (name() == "*" && rhs->name() != "*")-    {-      // creaty the copy inside (avoid unnecessary copies)-      Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this);-      // simply set the new name-      ts->name(rhs->name());-      // return copy-      return ts;-    }-    // return original-    return this;-  }--  Compound_Selector* Type_Selector::unify_with(Compound_Selector* rhs, Context& ctx)-  {-    // TODO: handle namespaces--    // if the rhs is empty, just return a copy of this-    if (rhs->length() == 0) {-      Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());-      (*cpy) << this;-      return cpy;-    }--    Simple_Selector* rhs_0 = (*rhs)[0];-    // otherwise, this is a tag name-    if (name() == "*")-    {-      if (typeid(*rhs_0) == typeid(Type_Selector))-      {-        // if rhs is universal, just return this tagname + rhs's qualifiers-        Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);-        Type_Selector* ts = static_cast<Type_Selector*>(rhs_0);-        (*cpy)[0] = this->unify_with(ts, ctx);-        return cpy;-      }-      else if (dynamic_cast<Selector_Qualifier*>(rhs_0)) {-        // qualifier is `.class`, so we can prefix with `ns|*.class`-        Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());-        if (has_ns() && !rhs_0->has_ns()) {-          if (ns() != "*") (*cpy) << this;-        }-        for (size_t i = 0, L = rhs->length(); i < L; ++i)-        { (*cpy) << (*rhs)[i]; }-        return cpy;-      }---      return rhs;-    }--    if (typeid(*rhs_0) == typeid(Type_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-      Compound_Selector* copy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);-      (*copy)[0] = this->unify_with(rhs_0, ctx);-      return copy;--    }-    // else it's a tag name and a bunch of qualifiers -- just append them-    Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());-    if (name() != "*") (*cpy) << this;-    (*cpy) += rhs;-    return cpy;-  }--  Compound_Selector* Selector_Qualifier::unify_with(Compound_Selector* rhs, Context& ctx)-  {-    if (name()[0] == '#')-    {-      for (size_t i = 0, L = rhs->length(); i < L; ++i)-      {-        Simple_Selector* rhs_i = (*rhs)[i];-        if (typeid(*rhs_i) == typeid(Selector_Qualifier) &&-            static_cast<Selector_Qualifier*>(rhs_i)->name()[0] == '#' &&-            static_cast<Selector_Qualifier*>(rhs_i)->name() != name())-          return 0;-      }-    }-    rhs->has_line_break(has_line_break());-    return Simple_Selector::unify_with(rhs, ctx);-  }--  Compound_Selector* Pseudo_Selector::unify_with(Compound_Selector* rhs, Context& ctx)-  {-    if (is_pseudo_element())-    {-      for (size_t i = 0, L = rhs->length(); i < L; ++i)-      {-        Simple_Selector* rhs_i = (*rhs)[i];-        if (typeid(*rhs_i) == typeid(Pseudo_Selector) &&-            static_cast<Pseudo_Selector*>(rhs_i)->is_pseudo_element() &&-            static_cast<Pseudo_Selector*>(rhs_i)->name() != name())-        { return 0; }-      }-    }-    return Simple_Selector::unify_with(rhs, ctx);-  }--  bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const-  {-    if (is_ns_eq(ns(), rhs.ns())) {-      if (name() == rhs.name()) {-        if (matcher() == rhs.matcher()) {-          return value() < rhs.value();-        } else { return matcher() < rhs.matcher(); }-      } else { return name() < rhs.name(); }-    }-    else return false;-  }--  bool Attribute_Selector::operator< (const Simple_Selector& rhs) const-  {-    if (const Attribute_Selector* w = dynamic_cast<const Attribute_Selector*>(&rhs))-    {-      return *this < *w;-    }-    if (is_ns_eq(ns(), rhs.ns()))-    { return name() < rhs.name(); }-    return ns() < rhs.ns();-  }--  bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const-  {-    if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())-    { return matcher() == rhs.matcher() && value() == rhs.value(); }-    else return false;-  }--  bool Attribute_Selector::operator== (const Simple_Selector& rhs) const-  {-    if (const Attribute_Selector* w = dynamic_cast<const Attribute_Selector*>(&rhs))-    {-      return *this == *w;-    }-    if (is_ns_eq(ns(), rhs.ns()))-    { return name() == rhs.name(); }-    return ns() == rhs.ns();-  }--  bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const-  {-    if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())-    { return *(selector()) == *(rhs.selector()); }-    else return false;-  }--  bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const-  {-    if (const Wrapped_Selector* w = dynamic_cast<const Wrapped_Selector*>(&rhs))-    {-      return *this == *w;-    }-    if (is_ns_eq(ns(), rhs.ns()))-    { return name() == rhs.name(); }-    return ns() == rhs.ns();-  }--  bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub)-  {-    if (this->name() != sub->name()) return false;-    if (this->name() == ":current") return false;-    if (Selector_List* rhs_list = dynamic_cast<Selector_List*>(sub->selector())) {-      if (Selector_List* lhs_list = dynamic_cast<Selector_List*>(selector())) {-        return lhs_list->is_superselector_of(rhs_list);-      }-      error("is_superselector expected a Selector_List", sub->pstate());-    } else {-      error("is_superselector expected a Selector_List", sub->pstate());-    }-    return false;-  }--  bool Compound_Selector::is_superselector_of(Selector_List* rhs, std::string wrapped)-  {-    for (Complex_Selector* item : rhs->elements()) {-      if (is_superselector_of(item, wrapped)) return true;-    }-    return false;-  }--  bool Compound_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapped)-  {-    if (rhs->head()) return is_superselector_of(rhs->head(), wrapped);-    return false;-  }--  bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)-  {-    To_String to_string;--    Compound_Selector* lhs = this;-    Simple_Selector* lbase = lhs->base();-    Simple_Selector* 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]->perform(&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]->perform(&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;-    }--    std::set<std::string> lset, rset;--    if (lbase && rbase)-    {-      if (lbase->perform(&to_string) == rbase->perform(&to_string)) {-        for (size_t i = 1, L = length(); i < L; ++i)-        { lset.insert((*this)[i]->perform(&to_string)); }-        for (size_t i = 1, L = rhs->length(); i < L; ++i)-        { rset.insert((*rhs)[i]->perform(&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* lhs = (*this)[i];-      // very special case for wrapped matches selector-      if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(lhs)) {-        if (wrapped->name() == ":not") {-          if (Selector_List* not_list = dynamic_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") {-          lhs = wrapped->selector();-          if (Selector_List* list = dynamic_cast<Selector_List*>(wrapped->selector())) {-            if (Compound_Selector* comp = dynamic_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* rhs_sel = rhs->elements().size() > i ? (*rhs)[i] : 0;-        if (Wrapped_Selector* wrapped_r = dynamic_cast<Wrapped_Selector*>(rhs_sel)) {-          if (wrapped->name() == wrapped_r->name()) {-          if (wrapped->is_superselector_of(wrapped_r)) {-             continue;-             rset.insert(lhs->perform(&to_string));--          }}-        }-      }-      // match from here on as strings-      lset.insert(lhs->perform(&to_string));-    }--    for (size_t n = 0, nL = rhs->length(); n < nL; ++n)-    {-      auto r = (*rhs)[n];-      if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(r)) {-        if (wrapped->name() == ":not") {-          if (Selector_List* ls = dynamic_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* ls = dynamic_cast<Selector_List*>(wrapped->selector())) {-            ls->remove_parent_selectors();-            return (is_superselector_of(ls, wrapped->name()));-          }-        }-      }-      rset.insert(r->perform(&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* Compound_Selector::to_complex(Memory_Manager& mem)-  {-    // create an intermediate complex selector-    return SASS_MEMORY_NEW(mem, Complex_Selector,-                           pstate(),-                           Complex_Selector::ANCESTOR_OF,-                           this,-                           0);-  }--  Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx)-  {--    // get last tails (on the right side)-    Complex_Selector* l_last = this->last();-    Complex_Selector* 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* l_last_head = l_last->head();-    Compound_Selector* 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* unified = r_last_head->unify_with(l_last_head, ctx);--    // 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, ctx);-    Node rhsNode = complexSelectorToNode(other, ctx);--    // overwrite universal base-    if (!is_universal)-    {-      // create some temporaries to convert to node-      Complex_Selector* fake = unified->to_complex(ctx.mem);-      Node unified_node = complexSelectorToNode(fake, ctx);-      // add to permutate the list?-      rhsNode.plus(unified_node);-    }--    // do some magic we inherit from node and extend-    Node node = Extend::subweave(lhsNode, rhsNode, ctx);-    Selector_List* result = SASS_MEMORY_NEW(ctx.mem, 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) << nodeToComplexSelector(Node::naiveTrim(*it, ctx), ctx); }--    // only return if list has some entries-    return result->length() ? result : 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*> l_lst = this->elements();-    std::vector<Simple_Selector*> r_lst = rhs.elements();-    std::sort(l_lst.begin(), l_lst.end(), cmp_simple_selector());-    std::sort(r_lst.begin(), r_lst.end(), cmp_simple_selector());-    // 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* l = l_lst[i];-      Simple_Selector* 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;-    }-    // no mismatch-    return true;-  }--  bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const {-    return *pLeft < *pRight;-  }--  bool Complex_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)-  {-    return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);-  }--  bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapping)-  {-    Complex_Selector* lhs = this;-    To_String to_string;-    // check for selectors with leading or trailing combinators-    if (!lhs->head() || !rhs->head())-    { return false; }-    const Complex_Selector* l_innermost = lhs->innermost();-    if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)-    { return false; }-    const Complex_Selector* 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* lhs_tail = lhs->tail();-      Complex_Selector* 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() && lhs_tail->head()) {-        if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;-      }-    }--    bool found = false;-    Complex_Selector* 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());-    }-    else-    {-      return lhs->tail()->is_superselector_of(marker->tail());-    }-    // catch-all-    return false;-  }--  size_t Complex_Selector::length() const-  {-    // TODO: make this iterative-    if (!tail()) return 1;-    return 1 + tail()->length();-  }--  Complex_Selector* Complex_Selector::context(Context& ctx)-  {-    if (!tail()) return 0;-    if (!head()) return tail()->context(ctx);-    Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate(), combinator(), head(), tail()->context(ctx));-    cpy->media_block(media_block());-    return cpy;-  }--  // 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(Context& ctx, Complex_Selector* ss)-  {--    Complex_Selector* t = ss->tail();-    Combinator c = ss->combinator();-    String* r = ss->reference();-    Compound_Selector* 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) {-        error("Invalid parent selector", pstate_);-      } else if (last()->head_ && last()->head_->length()) {-        Compound_Selector* rh = last()->head();-        size_t i = 0, L = h->length();-        if (dynamic_cast<Type_Selector*>(h->first())) {-          if (Selector_Qualifier* sq = dynamic_cast<Selector_Qualifier*>(rh->last())) {-            Selector_Qualifier* sqs = new Selector_Qualifier(*sq);-            sqs->name(sqs->name() + (*h)[0]->name());-            (*rh)[rh->length()-1] = sqs;-            for (i = 1; i < L; ++i) *rh << (*h)[i];-          } else if (Type_Selector* ts = dynamic_cast<Type_Selector*>(rh->last())) {-            Type_Selector* tss = new Type_Selector(*ts);-            tss->name(tss->name() + (*h)[0]->name());-            (*rh)[rh->length()-1] = tss;-            for (i = 1; i < L; ++i) *rh << (*h)[i];-          } else if (Selector_Placeholder* ps = dynamic_cast<Selector_Placeholder*>(rh->last())) {-            Selector_Placeholder* pss = new Selector_Placeholder(*ps);-            pss->name(pss->name() + (*h)[0]->name());-            (*rh)[rh->length()-1] = pss;-            for (i = 1; i < L; ++i) *rh << (*h)[i];-          } else {-            *last()->head_ += h;-          }-        } else {-          *last()->head_ += h;-        }-      } else {-        *last()->head_ += h;-      }-    } else {-      // std::cerr << "has no or empty head\n";-    }--    if (last()) {-      if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {-        Complex_Selector* inter = SASS_MEMORY_NEW(ctx.mem, 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* Selector_List::parentize(Selector_List* ps, Context& ctx)-  {-    Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());-    for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {-      Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());-      *list << (*ps)[pi];-      for (size_t si = 0, sL = this->length(); si < sL; ++si) {-        *ss += (*this)[si]->parentize(list, ctx);-      }-    }-    return ss;-  }--  Selector_List* Complex_Selector::parentize(Selector_List* parents, Context& ctx)-  {--    Complex_Selector* tail = this->tail();-    Compound_Selector* head = this->head();--    // first parentize the tail (which may return an expanded list)-    Selector_List* tails = tail ? tail->parentize(parents, ctx) : 0;--    if (head && head->length() > 0) {--      // we have a parent selector in a simple compound list-      // mix parent complex selector into the compound list-      if (dynamic_cast<Parent_Selector*>((*head)[0])) {-        if (parents && parents->length()) {-          Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());-          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* t = (*tails)[n];-                Complex_Selector* parent = (*parents)[i];-                Complex_Selector* s = parent->cloneFully(ctx);-                Complex_Selector* ss = this->clone(ctx);-                ss->tail(t ? t->clone(ctx) : 0);-                Compound_Selector* h = head_->clone(ctx);-                if (h->length()) h->erase(h->begin());-                ss->head(h->length() ? h : 0);-                s->append(ctx, ss);-                *retval << 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* parent = (*parents)[i];-              Complex_Selector* s = parent->cloneFully(ctx);-              Complex_Selector* ss = this->clone(ctx);-              // 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) {-                throw Exception::InvalidParent(parent, ss);-              }-              ss->tail(tail ? tail->clone(ctx) : 0);-              Compound_Selector* h = head_->clone(ctx);-              if (h->length()) h->erase(h->begin());-              ss->head(h->length() ? h : 0);-              // \/ IMO ruby sass bug \/-              ss->has_line_feed(false);-              s->append(ctx, ss);-              *retval << s;-            }-          }-          return retval;-        }-        // have no parent but some tails-        else {-          Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());-          if (tails && tails->length() > 0) {-            for (size_t n = 0, nL = tails->length(); n < nL; ++n) {-              Complex_Selector* cpy = this->clone(ctx);-              cpy->tail((*tails)[n]->cloneFully(ctx));-              cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate()));-              for (size_t i = 1, L = this->head()->length(); i < L; ++i)-                *cpy->head() << (*this->head())[i];-              if (!cpy->head()->length()) cpy->head(0);-              *retval << cpy->skip_empty_reference();-            }-          }-          // have no parent nor tails-          else {-            Complex_Selector* cpy = this->clone(ctx);-            cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate()));-            for (size_t i = 1, L = this->head()->length(); i < L; ++i)-              *cpy->head() << (*this->head())[i];-            if (!cpy->head()->length()) cpy->head(0);-            *retval << cpy->skip_empty_reference();-          }-          return retval;-        }-      }-      // no parent selector in head-      else {-        return this->tails(ctx, tails);-      }--    }-    // has no head-    else {-      return this->tails(ctx, tails);-    }--    // unreachable-    return 0;-  }--  Selector_List* Complex_Selector::tails(Context& ctx, Selector_List* tails)-  {-    Selector_List* rv = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate_);-    if (tails && tails->length()) {-      for (size_t i = 0, iL = tails->length(); i < iL; ++i) {-        Complex_Selector* pr = this->clone(ctx);-        pr->tail((*tails)[i]);-        *rv << pr;-      }-    }-    else {-      *rv << this;-    }-    return rv;-  }--  // return the last tail that is defined-  Complex_Selector* Complex_Selector::first()-  {-    // declare variables used in loop-    Complex_Selector* cur = this->tail_;-    const Compound_Selector* head = head_;-    // processing loop-    while (cur)-    {-      // get the head-      head = cur->head_;-      // check for single parent ref-      if (head && head->length() == 1)-      {-        // abort (and return) if it is not a parent selector-        if (!dynamic_cast<Parent_Selector*>((*head)[0])) break;-      }-      // advance to next-      cur = cur->tail_;-    }-    // result-    return cur;-  }--  // return the last tail that is defined-  const Complex_Selector* Complex_Selector::first() const-  {-    // declare variables used in loop-    const Complex_Selector* cur = this->tail_;-    const Compound_Selector* head = head_;-    // processing loop-    while (cur)-    {-      // get the head-      head = cur->head_;-      // check for single parent ref-      if (head && head->length() == 1)-      {-        // abort (and return) if it is not a parent selector-        if (!dynamic_cast<Parent_Selector*>((*head)[0])) break;-      }-      // advance to next-      cur = cur->tail_;-    }-    // result-    return cur;-  }--  // return the last tail that is defined-  Complex_Selector* Complex_Selector::last()-  {-    // ToDo: implement with a while loop-    return tail_? tail_->last() : this;-  }--  // return the last tail that is defined-  const Complex_Selector* Complex_Selector::last() const-  {-    // ToDo: implement with a while loop-    return tail_? tail_->last() : this;-  }---  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* val, Combinator c)-  {-    if (!tail())-    { tail(val); combinator(c); }-    else-    { tail()->set_innermost(val, c); }-  }--  Complex_Selector* Complex_Selector::clone(Context& ctx) const-  {-    Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);-    cpy->media_block(this->media_block());-    if (tail()) cpy->tail(tail()->clone(ctx));-    return cpy;-  }--  Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const-  {-    Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);--    if (head()) {-      cpy->head(head()->clone(ctx));-    }--    if (tail()) {-      cpy->tail(tail()->cloneFully(ctx));-    }--    return cpy;-  }--  Compound_Selector* Compound_Selector::clone(Context& ctx) const-  {-    Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this);-    cpy->media_block(this->media_block());-    return cpy;-  }--  Selector_List* Selector_List::clone(Context& ctx) const-  {-    Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this);-    cpy->media_block(this->media_block());-    return cpy;-  }--  Selector_List* Selector_List::cloneFully(Context& ctx) const-  {-    Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());-    for (size_t i = 0, L = length(); i < L; ++i) {-      *cpy << (*this)[i]->cloneFully(ctx);-    }-    return cpy;-  }--  /* not used anymore - remove?-  Selector_Placeholder* Selector::find_placeholder()-  {-    return 0;-  }*/--  // 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() && (*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());-        }-      }-    }-  }--  void Selector_List::adjust_after_pushing(Complex_Selector* 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 *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 *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 *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 *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* Selector_List::unify_with(Selector_List* rhs, Context& ctx) {-    std::vector<Complex_Selector*> 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* seq1 = (*this)[lhs_i];-      for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {-        Complex_Selector* seq2 = (*rhs)[rhs_i];--        Selector_List* result = seq1->unify_with(seq2, ctx);-        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* final_result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());-    for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {-      *final_result << *itr;-    }-    return final_result;-  }--  void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) {-    To_String to_string;--    Selector_List* extender = this;-    for (auto complex_sel : extendee->elements()) {-      Complex_Selector* c = complex_sel;---      // Ignore any parent selectors, until we find the first non Selector_Reference head-      Compound_Selector* compound_sel = c->head();-      Complex_Selector* pIter = complex_sel;-      while (pIter) {-        Compound_Selector* pHead = pIter->head();-        if (pHead && dynamic_cast<Parent_Selector*>(pHead->elements()[0]) == NULL) {-          compound_sel = pHead;-          break;-        }--        pIter = pIter->tail();-      }--      if (!pIter->head() || pIter->tail()) {-        error("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->to_str_vec(), std::make_pair((*extender)[i], compound_sel));-      }-    }-  };--  std::vector<std::string> Compound_Selector::to_str_vec()-  {-    To_String to_string;-    std::vector<std::string> result;-    result.reserve(length());-    for (size_t i = 0, L = length(); i < L; ++i)-    { result.push_back((*this)[i]->perform(&to_string)); }-    return result;-  }--  Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx)-  {-    To_String to_string(&ctx);-    Compound_Selector* result = SASS_MEMORY_NEW(ctx.mem, 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]->perform(&to_string));-      for (size_t j = 0, M = rhs->length(); j < M; ++j)-      {-        if (thisSelector == (*rhs)[j]->perform(&to_string))-        {-          found = true;-          break;-        }-      }-      if (!found) (*result) << (*this)[i];-    }--    return result;-  }--  void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx)-  {-    for (SourcesSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) {-      this->sources_.insert((*iterator)->clone(ctx));-    }-  }--  void Arguments::adjust_after_pushing(Argument* a)-  {-    if (!a->name().empty()) {-      if (/* has_rest_argument_ || */ has_keyword_argument_) {-        error("named arguments must precede variable-length argument", a->pstate());-      }-      has_named_arguments_ = true;-    }-    else if (a->is_rest_argument()) {-      if (has_rest_argument_) {-        error("functions and mixins may only be called with one variable-length argument", a->pstate());-      }-      if (has_keyword_argument_) {-        error("only keyword arguments may follow variable arguments", a->pstate());-      }-      has_rest_argument_ = true;-    }-    else if (a->is_keyword_argument()) {-      if (has_keyword_argument_) {-        error("functions and mixins may only be called with one keyword argument", a->pstate());-      }-      has_keyword_argument_ = true;-    }-    else {-      if (has_rest_argument_) {-        error("ordinal arguments must precede variable-length arguments", a->pstate());-      }-      if (has_named_arguments_) {-        error("ordinal arguments must precede named arguments", a->pstate());-      }-    }-  }--  bool Ruleset::is_invisible() const {-    Selector_List* sl = static_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) {-      if (!(*block())[i]->is_invisible()) return false;-    }-    return true;-  }--  Number::Number(ParserState pstate, double val, std::string u, bool zero)-  : Value(pstate),-    value_(val),-    zero_(zero),-    numerator_units_(std::vector<std::string>()),-    denominator_units_(std::vector<std::string>()),-    hash_(0)-  {-    size_t l = 0, r = 0;-    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) numerator_units_.push_back(unit);-          else denominator_units_.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;-        l = r + 1;-      }-    }-    concrete_type(NUMBER);-  }--  std::string Number::unit() const-  {-    std::string u;-    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {-      if (i) u += '*';-      u += numerator_units_[i];-    }-    if (!denominator_units_.empty()) u += '/';-    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {-      if (i) u += '*';-      u += denominator_units_[i];-    }-    return u;-  }--  bool Number::is_unitless()-  { return numerator_units_.empty() && denominator_units_.empty(); }--  void Number::normalize(const std::string& prefered, bool strict)-  {--    // first make sure same units cancel each other out-    // it seems that a map table will fit nicely to do this-    // we basically construct exponents for each unit-    // has the advantage that they will be pre-sorted-    std::map<std::string, int> exponents;--    // initialize by summing up occurences in unit vectors-    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];-    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];--    // the final conversion factor-    double factor = 1;--    // get the first entry of numerators-    // forward it when entry is converted-    std::vector<std::string>::iterator nom_it = numerator_units_.begin();-    std::vector<std::string>::iterator nom_end = numerator_units_.end();-    std::vector<std::string>::iterator denom_it = denominator_units_.begin();-    std::vector<std::string>::iterator denom_end = denominator_units_.end();--    // main normalization loop-    // should be close to optimal-    while (denom_it != denom_end)-    {-      // get and increment afterwards-      const std::string denom = *(denom_it ++);-      // skip already canceled out unit-      if (exponents[denom] >= 0) continue;-      // skip all units we don't know how to convert-      if (string_to_unit(denom) == UNKNOWN) continue;-      // now search for nominator-      while (nom_it != nom_end)-      {-        // get and increment afterwards-        const std::string nom = *(nom_it ++);-        // skip already canceled out unit-        if (exponents[nom] <= 0) continue;-        // skip all units we don't know how to convert-        if (string_to_unit(nom) == UNKNOWN) continue;-        // we now have two convertable units-        // add factor for current conversion-        factor *= conversion_factor(nom, denom, strict);-        // update nominator/denominator exponent-        -- exponents[nom]; ++ exponents[denom];-        // inner loop done-        break;-      }-    }--    // now we can build up the new unit arrays-    numerator_units_.clear();-    denominator_units_.clear();--    // build them by iterating over the exponents-    for (auto exp : exponents)-    {-      // maybe there is more effecient way to push-      // the same item multiple times to a vector?-      for(size_t i = 0, S = abs(exp.second); i < S; ++i)-      {-        // opted to have these switches in the inner loop-        // makes it more readable and should not cost much-        if (!exp.first.empty()) {-          if (exp.second < 0) denominator_units_.push_back(exp.first);-          else if (exp.second > 0) numerator_units_.push_back(exp.first);-        }-      }-    }--    // apply factor to value_-    // best precision this way-    value_ *= factor;--    // maybe convert to other unit-    // easier implemented on its own-    try { convert(prefered, strict); }-    catch (incompatibleUnits& err)-    { error(err.what(), pstate()); }-    catch (...) { throw; }--  }--  void Number::convert(const std::string& prefered, bool strict)-  {-    // abort if unit is empty-    if (prefered.empty()) return;--    // first make sure same units cancel each other out-    // it seems that a map table will fit nicely to do this-    // we basically construct exponents for each unit-    // has the advantage that they will be pre-sorted-    std::map<std::string, int> exponents;--    // initialize by summing up occurences in unit vectors-    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];-    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];--    // the final conversion factor-    double factor = 1;--    std::vector<std::string>::iterator denom_it = denominator_units_.begin();-    std::vector<std::string>::iterator denom_end = denominator_units_.end();--    // main normalization loop-    // should be close to optimal-    while (denom_it != denom_end)-    {-      // get and increment afterwards-      const std::string denom = *(denom_it ++);-      // check if conversion is needed-      if (denom == prefered) continue;-      // skip already canceled out unit-      if (exponents[denom] >= 0) continue;-      // skip all units we don't know how to convert-      if (string_to_unit(denom) == UNKNOWN) continue;-      // we now have two convertable units-      // add factor for current conversion-      factor *= conversion_factor(denom, prefered, strict);-      // update nominator/denominator exponent-      ++ exponents[denom]; -- exponents[prefered];-    }--    std::vector<std::string>::iterator nom_it = numerator_units_.begin();-    std::vector<std::string>::iterator nom_end = numerator_units_.end();--    // now search for nominator-    while (nom_it != nom_end)-    {-      // get and increment afterwards-      const std::string nom = *(nom_it ++);-      // check if conversion is needed-      if (nom == prefered) continue;-      // skip already canceled out unit-      if (exponents[nom] <= 0) continue;-      // skip all units we don't know how to convert-      if (string_to_unit(nom) == UNKNOWN) continue;-      // we now have two convertable units-      // add factor for current conversion-      factor *= conversion_factor(nom, prefered, strict);-      // update nominator/denominator exponent-      -- exponents[nom]; ++ exponents[prefered];-    }--    // now we can build up the new unit arrays-    numerator_units_.clear();-    denominator_units_.clear();--    // build them by iterating over the exponents-    for (auto exp : exponents)-    {-      // maybe there is more effecient way to push-      // the same item multiple times to a vector?-      for(size_t i = 0, S = abs(exp.second); i < S; ++i)-      {-        // opted to have these switches in the inner loop-        // makes it more readable and should not cost much-        if (!exp.first.empty()) {-          if (exp.second < 0) denominator_units_.push_back(exp.first);-          else if (exp.second > 0) numerator_units_.push_back(exp.first);-        }-      }-    }--    // apply factor to value_-    // best precision this way-    value_ *= factor;--  }--  // useful for making one number compatible with another-  std::string Number::find_convertible_unit() const-  {-    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {-      std::string u(numerator_units_[i]);-      if (string_to_unit(u) != UNKNOWN) return u;-    }-    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {-      std::string u(denominator_units_[i]);-      if (string_to_unit(u) != UNKNOWN) return u;-    }-    return std::string();-  }--  bool Custom_Warning::operator== (const Expression& rhs) const-  {-    if (const Custom_Warning* r = dynamic_cast<const Custom_Warning*>(&rhs)) {-      return message() == r->message();-    }-    return false;-  }--  bool Custom_Error::operator== (const Expression& rhs) const-  {-    if (const Custom_Error* r = dynamic_cast<const Custom_Error*>(&rhs)) {-      return message() == r->message();-    }-    return false;-  }--  bool Number::operator== (const Expression& rhs) const-  {-    if (const Number* r = dynamic_cast<const Number*>(&rhs)) {-      return (numerator_units_ == r->numerator_units_) &&-             (denominator_units_ == r->denominator_units_) &&-             std::fabs(value() - r->value()) < NUMBER_EPSILON;-    }-    return false;-  }--  bool Number::operator< (const Number& rhs) const-  {-    Number tmp_r(rhs);-    tmp_r.normalize(find_convertible_unit());-    std::string l_unit(unit());-    std::string r_unit(tmp_r.unit());-    if (!l_unit.empty() && !r_unit.empty() && unit() != tmp_r.unit()) {-      error("cannot compare numbers with incompatible units", pstate());-    }-    return value() < tmp_r.value();-  }--  bool String_Quoted::operator== (const Expression& rhs) const-  {-    if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(&rhs)) {-      return (value() == qstr->value());-    } else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(&rhs)) {-      return (value() == cstr->value());-    }-    return false;-  }--  bool String_Constant::operator== (const Expression& rhs) const-  {-    if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(&rhs)) {-      return (value() == qstr->value());-    } else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(&rhs)) {-      return (value() == cstr->value());-    }-    return false;-  }--  bool String_Schema::operator== (const Expression& rhs) const-  {-    if (const String_Schema* r = dynamic_cast<const String_Schema*>(&rhs)) {-      if (length() != r->length()) return false;-      for (size_t i = 0, L = length(); i < L; ++i) {-        Expression* rv = (*r)[i];-        Expression* 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 (const Boolean* r = dynamic_cast<const Boolean*>(&rhs)) {-      return (value() == r->value());-    }-    return false;-  }--  bool Color::operator== (const Expression& rhs) const-  {-    if (const Color* r = dynamic_cast<const 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 (const List* r = dynamic_cast<const List*>(&rhs)) {-      if (length() != r->length()) return false;-      if (separator() != r->separator()) return false;-      for (size_t i = 0, L = length(); i < L; ++i) {-        Expression* rv = (*r)[i];-        Expression* lv = (*this)[i];-        if (!lv || !rv) return false;-        if (!(*lv == *rv)) return false;-      }-      return true;-    }-    return false;-  }--  bool Map::operator== (const Expression& rhs) const-  {-    if (const Map* r = dynamic_cast<const Map*>(&rhs)) {-      if (length() != r->length()) return false;-      for (auto key : keys()) {-        Expression* lv = at(key);-        Expression* 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;-  }--  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) {-      if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {-        if (!arg->name().empty()) return i;-      }-    }-    return length();-  }--  Expression* Hashed::at(Expression* k) const-  {-    if (elements_.count(k))-    { return elements_.at(k); }-    else { return &sass_null; }-  }--  std::string Map::to_string(bool compressed, int precision) const-  {-    std::string res("");-    if (empty()) return res;-    if (is_invisible()) return res;-    bool items_output = false;-    for (auto key : keys()) {-      if (key->is_invisible()) continue;-      if (at(key)->is_invisible()) continue;-      if (items_output) res += compressed ? "," : ", ";-      Value* v_key = dynamic_cast<Value*>(key);-      Value* v_val = dynamic_cast<Value*>(at(key));-      if (v_key) res += v_key->to_string(compressed, precision);-      res += compressed ? ":" : ": ";-      if (v_val) res += v_val->to_string(compressed, precision);-      items_output = true;-    }-    return res;-  }--  std::string List::to_string(bool compressed, int precision) const-  {-    std::string res("");-    if (empty()) return res;-    if (is_invisible()) return res;-    bool items_output = false;-    std::string sep = separator() == SASS_COMMA ? "," : " ";-    if (!compressed && sep == ",") sep += " ";-    for (size_t i = 0, L = size(); i < L; ++i) {-      Expression* item = (*this)[i];-      if (item->is_invisible()) continue;-      if (items_output) res += sep;-      if (Value* v_val = dynamic_cast<Value*>(item))-      { res += v_val->to_string(compressed, precision); }-      items_output = true;-    }-    return res;-  }--  std::string String_Schema::to_string(bool compressed, int precision) const-  {-    std::string res("");-    for (size_t i = 0, L = length(); i < L; ++i) {-      if ((*this)[i]->is_interpolant()) res += "#{";-      if (Value* val = dynamic_cast<Value*>((*this)[i]))-      { res += val->to_string(compressed, precision); }-      if ((*this)[i]->is_interpolant()) res += "}";-    }-    return res;-  }--  std::string Null::to_string(bool compressed, int precision) const-  {-    return "null";-  }--  std::string Boolean::to_string(bool compressed, int precision) const-  {-    return value_ ? "true" : "false";-  }--  // helper function for serializing colors-  template <size_t range>-  static double cap_channel(double c) {-    if      (c > range) return range;-    else if (c < 0)     return 0;-    else                return c;-  }--  std::string Color::to_string(bool compressed, int precision) const-  {-    std::stringstream ss;--    // original color name-    // maybe an unknown token-    std::string name = disp();--    // resolved color-    std::string res_name = name;--    double r = Sass::round(cap_channel<0xff>(r_));-    double g = Sass::round(cap_channel<0xff>(g_));-    double b = Sass::round(cap_channel<0xff>(b_));-    double a = cap_channel<1>   (a_);--    // get color from given name (if one was given at all)-    if (name != "" && name_to_color(name)) {-      const Color* n = name_to_color(name);-      r = Sass::round(cap_channel<0xff>(n->r()));-      g = Sass::round(cap_channel<0xff>(n->g()));-      b = Sass::round(cap_channel<0xff>(n->b()));-      a = cap_channel<1>   (n->a());-    }-    // otherwise get the possible resolved color name-    else {-      double numval = r * 0x10000 + g * 0x100 + b;-      if (color_to_name(numval))-        res_name = color_to_name(numval);-    }--    std::stringstream hexlet;-    hexlet << '#' << std::setw(1) << std::setfill('0');-    // create a short color hexlet if there is any need for it-    if (compressed && is_color_doublet(r, g, b) && a == 1) {-      hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);-      hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);-      hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);-    } else {-      hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);-      hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);-      hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);-    }--    if (compressed && !this->is_delayed()) name = "";--    // retain the originally specified color definition if unchanged-    if (name != "") {-      ss << name;-    }-    else if (r == 0 && g == 0 && b == 0 && a == 0) {-        ss << "transparent";-    }-    else if (a >= 1) {-      if (res_name != "") {-        if (compressed && hexlet.str().size() < res_name.size()) {-          ss << hexlet.str();-        } else {-          ss << res_name;-        }-      }-      else {-        ss << hexlet.str();-      }-    }-    else {-      ss << "rgba(";-      ss << static_cast<unsigned long>(r) << ",";-      if (!compressed) ss << " ";-      ss << static_cast<unsigned long>(g) << ",";-      if (!compressed) ss << " ";-      ss << static_cast<unsigned long>(b) << ",";-      if (!compressed) ss << " ";-      ss << a << ')';-    }--    return ss.str();--  }--  std::string Number::to_string(bool compressed, int precision) const-  {--    std::string res;--    // 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 << 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, precision));-      ss << std::fixed << 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;--    // reset stream for another run-    ss.clear(); ss.str(std::string());--    // take a shortcut for integers-    if (is_int)-    {-      ss.precision(0);-      ss << std::fixed << value_;-      res = std::string(ss.str());-    }-    // process floats-    else-    {-      // do we have have too much precision?-      if (pos_fract < precision + pos_point)-      { precision = (int)(pos_fract - pos_point); }-      // round value again-      ss.precision(precision);-      ss << std::fixed << value_;-      res = std::string(ss.str());-      // maybe we truncated up to decimal point-      size_t pos = res.find_last_not_of("0");-      bool at_dec_point = res[pos] == '.' ||-                          res[pos] == ',';-      // don't leave a blank point-      if (at_dec_point) ++ pos;-      res.resize (pos + 1);-    }--    // some final cosmetics-    if (res == "-0.0") res.erase(0, 1);-    else if (res == "-0") res.erase(0, 1);-    else if (res == "") res = "0";--    // add unit now-    res += unit();--    // and return-    return res;--  }--  std::string String_Quoted::to_string(bool compressed, int precision) const-  {-    return quote_mark_ ? quote(value_, quote_mark_, true) : value_;-  }--  std::string String_Constant::to_string(bool compressed, int precision) const-  {-    return quote_mark_ ? quote(value_, quote_mark_, true) : value_;-  }--  std::string Custom_Error::to_string(bool compressed, int precision) const-  {-    return message();-  }-  std::string Custom_Warning::to_string(bool compressed, int precision) const-  {-    return message();-  }--  std::string Selector_List::to_string(bool compressed, int precision) const-  {-    std::string str("");-    auto end = this->end();-    auto start = this->begin();-    while (start < end && *start) {-      Complex_Selector* sel = *start;-      if (!str.empty()) str += ", ";-      str += sel->to_string(compressed, precision);-      ++ start;-    }-    return str;-  }--  std::string Compound_Selector::to_string(bool compressed, int precision) const-  {-    std::string str("");-    auto end = this->end();-    auto start = this->begin();-    while (start < end && *start) {-      Simple_Selector* sel = *start;-      str += sel->to_string(compressed, precision);-      ++ start;-    }-    return str;-  }--  std::string Complex_Selector::to_string(bool compressed, int precision) const-  {-    // first render head and tail if they are available-    std::string str_head(head() ? head()->to_string(compressed, precision) : "");-    std::string str_tail(tail() ? tail()->to_string(compressed, precision) : "");-    std::string str_ref(reference() ? reference()->to_string(compressed, precision) : "");-    // combinator in between-    std::string str_op("");-    // use a switch statement-    switch (combinator()) {-      case ANCESTOR_OF: str_op = " "; break;-      case PARENT_OF:   str_op = ">"; break;-      case PRECEDES:    str_op = "~"; break;-      case ADJACENT_TO: str_op = "+"; break;-      case REFERENCE:   str_op = "/" + str_ref + "/"; break;-    }-    // prettify for non ancestors-    if (combinator() != ANCESTOR_OF) {-      // no spaces needed for compressed-      if (compressed == false) {-        // make sure we add some spaces where needed-        if (str_tail != "") str_op += " ";-        if (str_head != "") str_head += " ";-      }-    }-    // is ancestor with no tail-    else if (str_tail == "") {-      str_op = ""; // superflous-    }-    // now build the final result-    return str_head + str_op + str_tail;-  }--  std::string Selector_Schema::to_string(bool compressed, int precision) const-  {-    return contents()->to_string(compressed, precision);-  }--  std::string Parent_Selector::to_string(bool compressed, int precision) const-  {-    return "&";-  }--  std::string Attribute_Selector::to_string(bool compressed, int precision) const-  {-    std::string val(value() ? value()->to_string(compressed, precision) : "");-    return "[" + this->ns_name() + this->matcher() + val + "]";-  }--  std::string Wrapped_Selector::to_string(bool compressed, int precision) const-  {-    // first render the-    std::string main(this->Simple_Selector::to_string(compressed, precision));-    std::string wrapped(selector() ? selector()->to_string(compressed, precision) : "");-    // now build the final result-    return main + "(" + wrapped + ")";-  }--  //////////////////////////////////////////////////////////////////////////////////////////-  // Additional method on Lists to retrieve values directly or from an encompassed Argument.-  //////////////////////////////////////////////////////////////////////////////////////////-  Expression* List::value_at_index(size_t i) {-    if (is_arglist_) {-      if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {-        return arg->value();-      } else {-        return (*this)[i];-      }-    } else {-      return (*this)[i];-    }-  }--}-+#include "sass.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "node.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 Supports_Operator::needs_parens(Supports_Condition_Obj cond) const {+    if (Supports_Operator_Obj op = SASS_MEMORY_CAST(Supports_Operator, cond)) {+      return op->operand() != operand();+    }+    return SASS_MEMORY_CAST(Supports_Negation, cond) != NULL;+  }++  bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const {+    return SASS_MEMORY_CAST(Supports_Negation, cond) ||+           SASS_MEMORY_CAST(Supports_Operator, cond);+  }++  size_t HashExpression::operator() (Expression_Obj ex) const {+    return ex ? ex->hash() : 0;+  }++  size_t HashSimpleSelector::operator() (Simple_Selector_Obj ex) const {+    return ex ? ex->hash() : 0;+  }+++  bool CompareExpression::operator()(const Expression_Obj& lhs, const Expression_Obj& rhs) const {+    return lhs && rhs && lhs->eq(*rhs);+  }++  bool CompareSimpleSelector::operator()(Simple_Selector_Obj lhs, Simple_Selector_Obj rhs) const {+    return &lhs && &rhs && *lhs == *rhs;+  }++  std::string & str_ltrim(std::string & str)+  {+    auto it2 =  std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );+    str.erase( str.begin() , it2);+    return str;+  }++  std::string & str_rtrim(std::string & str)+  {+    auto it1 =  std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );+    str.erase( it1.base() , str.end() );+    return str;+  }++  void String_Constant::rtrim()+  {+    value_ = str_rtrim(value_);+  }+  void String_Constant::ltrim()+  {+    value_ = str_ltrim(value_);+  }+  void String_Constant::trim()+  {+    rtrim();+    ltrim();+  }++  void String_Schema::rtrim()+  {+    if (!empty()) {+      if (String_Ptr str = SASS_MEMORY_CAST(String, last())) str->rtrim();+    }+  }+  void String_Schema::ltrim()+  {+    if (!empty()) {+      if (String_Ptr str = SASS_MEMORY_CAST(String, first())) str->ltrim();+    }+  }+  void String_Schema::trim()+  {+    rtrim();+    ltrim();+  }++  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());+    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;+  }++  void AST_Node::set_pstate_offset(const Offset& offset)+  {+    pstate_.offset = offset;+  }++  inline bool is_ns_eq(const std::string& l, const std::string& r)+  {+    if (l.empty() && r.empty()) return true;+    else if (l.empty() && r == "*") return true;+    else if (r.empty() && l == "*") return true;+    else return l == r;+  }++++  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()+  {+    for (Simple_Selector_Obj s : *this) {+      if (s && s->has_parent_ref()) return true;+    }+    return false;+  }++  bool Compound_Selector::has_real_parent_ref()+  {+    for (Simple_Selector_Obj s : *this) {+      if (s && s->has_real_parent_ref()) return true;+    }+    return false;+  }++  bool Complex_Selector::has_parent_ref()+  {+    return (head() && head()->has_parent_ref()) ||+           (tail() && tail()->has_parent_ref());+  }++  bool Complex_Selector::has_real_parent_ref()+  {+    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 = l ? &l->head() : 0;+    Compound_Selector_Ptr r_h = r ? &r->head() : 0;+    // process all tails+    while (true)+    {+      // skip empty ancestor first+      if (l && l->is_empty_ancestor())+      {+        l = &l->tail();+        l_h = l ? &l->head() : 0;+        continue;+      }+      // skip empty ancestor first+      if (r && r->is_empty_ancestor())+      {+        r = &r->tail();+        r_h = r ? &r->head() : 0;+        continue;+      }+      // 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 = l ? &l->head() : 0;+        r_h = r ? &r->head() : 0;+      }+      // 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 = l ? &l->head() : 0;+        r_h = r ? &r->head() : 0;+      }+      // heads are not equal+      else return *l_h < *r_h;+    }+    return true;+  }++  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 = l ? &l->head() : 0;+    Compound_Selector_Ptr r_h = r ? &r->head() : 0;+    // process all tails+    while (true)+    {+      // skip empty ancestor first+      if (l && l->is_empty_ancestor())+      {+        l = &l->tail();+        l_h = l ? &l->head() : 0;+        continue;+      }+      // skip empty ancestor first+      if (r && r->is_empty_ancestor())+      {+        r = &r->tail();+        r_h = r ? &r->head() : 0;+        continue;+      }+      // 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 = l ? &l->head() : 0;+        r_h = r ? &r->head() : 0;+      }+      // 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))+      {+        // 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 = l ? &l->head() : 0;+        r_h = r ? &r->head() : 0;+      }+      // abort+      else break;+    }+    // unreachable+    return false;+  }++  Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+  {+    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, ctx);+    }+    return unified.detach();+  }++  bool Selector::operator== (const Selector& rhs) const+  {+    if (Selector_List_Ptr_Const sl = dynamic_cast<Selector_List_Ptr_Const>(this)) return *sl == rhs;+    if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(this)) return *sp == rhs;+    throw std::runtime_error("invalid selector base classes to compare");+    return false;+  }++  bool Selector::operator< (const Selector& rhs) const+  {+    if (Selector_List_Ptr_Const sl = dynamic_cast<Selector_List_Ptr_Const>(this)) return *sl < rhs;+    if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(this)) return *sp < rhs;+    throw std::runtime_error("invalid selector base classes to compare");+    return false;+  }++  bool Simple_Selector::operator== (const Selector& rhs) const+  {+    if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(&rhs)) return *this == *sp;+    return false;+  }++  bool Simple_Selector::operator< (const Selector& rhs) const+  {+    if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(&rhs)) return *this < *sp;+    return false;+  }++  bool Simple_Selector::operator== (const Simple_Selector& rhs) const+  {+    Simple_Type type = simple_type();+    // dynamic cast is a bottleneck - use concrete type as types are final+    if (type == PSEUDO_SEL /* Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this) */) {+      return *static_cast<Pseudo_Selector_Ptr_Const>(this) == rhs;+    }+    else if (type == WRAPPED_SEL /* Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this) */) {+      return *static_cast<Wrapped_Selector_Ptr_Const>(this) == rhs;+    }+    else if (type == ATTR_SEL /* Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this) */) {+      return *static_cast<Attribute_Selector_Ptr_Const>(this) == rhs;+    }+    else if (name_ == rhs.name_)+    { return is_ns_eq(ns_, rhs.ns_); }+    else return false;+  }++  bool Simple_Selector::operator< (const Simple_Selector& rhs) const+  {+    Simple_Type type = simple_type();+    // dynamic cast is a bottleneck - use concrete type as types are final+    if (type == PSEUDO_SEL /* Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this) */) {+      return *static_cast<Pseudo_Selector_Ptr_Const>(this) < rhs;+    }+    else if (type == WRAPPED_SEL /* Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this) */) {+      return *static_cast<Wrapped_Selector_Ptr_Const>(this) < rhs;+    }+    else if (type == ATTR_SEL /* Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this) */) {+      return *static_cast<Attribute_Selector_Ptr_Const>(this) < rhs;+    }+    if (is_ns_eq(ns_, rhs.ns_))+    { 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 ls = dynamic_cast<Selector_List_Ptr_Const>(&rhs)) { return *this == *ls; }+    else if (Complex_Selector_Ptr_Const ls = dynamic_cast<Complex_Selector_Ptr_Const>(&rhs)) { return *this == *ls; }+    else if (Compound_Selector_Ptr_Const ls = dynamic_cast<Compound_Selector_Ptr_Const>(&rhs)) { return *this == *ls; }+    // 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 = dynamic_cast<List_Ptr_Const>(&rhs)) { return *this == *ls; }+    if (Selector_Ptr_Const ls = dynamic_cast<Selector_Ptr_Const>(&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(), cmp_complex_selector());+    std::sort(r_lst.begin(), r_lst.end(), cmp_complex_selector());+    // 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;+    }+    // no mismatch+    return true;+  }++  bool Selector_List::operator< (const Selector& rhs) const+  {+    if (Selector_List_Ptr_Const sp = dynamic_cast<Selector_List_Ptr_Const>(&rhs)) return *this < *sp;+    return false;+  }++  bool Selector_List::operator< (const Selector_List& rhs) const+  {+    if (this->length() != rhs.length()) return false;+    for (size_t i = 0; i < rhs.length(); i ++) {+      if (!(*at(i) < *rhs.at(i))) return false;+    }+    return true;+  }++  Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+  {+    for (size_t i = 0, L = rhs->length(); i < L; ++i)+    { if (to_string(ctx.c_options) == rhs->at(i)->to_string(ctx.c_options)) 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))+    {+      for (i = 0, L = rhs->length(); i < L; ++i)+      {+        if ((SASS_MEMORY_CAST(Pseudo_Selector, (*rhs)[i]) || SASS_MEMORY_CAST(Wrapped_Selector, (*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element())+        { found = true; break; }+      }+    }+    else+    {+      for (i = 0, L = rhs->length(); i < L; ++i)+      {+        if (SASS_MEMORY_CAST(Pseudo_Selector, (*rhs)[i]) || SASS_MEMORY_CAST(Wrapped_Selector, (*rhs)[i]))+        { found = true; break; }+      }+    }+    if (!found)+    {+      rhs->append(this);+      return rhs;+    }+    rhs->elements().insert(rhs->elements().begin() + i, this);+    return rhs;+  }++  Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs, Context& ctx)+  {+    // 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, Context& ctx)+  {+    // 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 = SASS_MEMORY_CAST_PTR(Element_Selector, rhs_0);+        rhs->at(0) = this->unify_with(ts, ctx);+        return rhs;+      }+      else if (SASS_MEMORY_CAST_PTR(Class_Selector, rhs_0) || SASS_MEMORY_CAST_PTR(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, ctx);+      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, Context& ctx)+  {+    rhs->has_line_break(has_line_break());+    return Simple_Selector::unify_with(rhs, ctx);+  }++  Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+  {+    for (size_t i = 0, L = rhs->length(); i < L; ++i)+    {+      if (Id_Selector_Ptr sel = SASS_MEMORY_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, ctx);+  }++  Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+  {+    if (is_pseudo_element())+    {+      for (size_t i = 0, L = rhs->length(); i < L; ++i)+      {+        if (Pseudo_Selector_Ptr sel = SASS_MEMORY_CAST(Pseudo_Selector, rhs->at(i))) {+          if (sel->is_pseudo_element() && sel->name() != name()) return 0;+        }+      }+    }+    return Simple_Selector::unify_with(rhs, ctx);+  }++  bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const+  {+    if (is_ns_eq(ns(), rhs.ns())) {+      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 true;+          }+          if (!no_lhs_val && !no_rhs_val) {+            return *value() < *rhs.value();+          }+        } else { return matcher() < rhs.matcher(); }+      } else { return name() < rhs.name(); }+    }+    return false;+  }++  bool Attribute_Selector::operator< (const Simple_Selector& rhs) const+  {+    if (Attribute_Selector_Ptr_Const w = dynamic_cast<Attribute_Selector_Ptr_Const>(&rhs))+    {+      return *this < *w;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { 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(ns(), rhs.ns()));+    }+    // both are defined, evaluate+    if (no_lhs_val == no_rhs_val) {+      return (name() == rhs.name())+        && (matcher() == rhs.matcher())+        && (is_ns_eq(ns(), rhs.ns()))+        && (*value() == *rhs.value());+    }+    // not equal+    return false;++  }++  bool Attribute_Selector::operator== (const Simple_Selector& rhs) const+  {+    if (Attribute_Selector_Ptr_Const w = dynamic_cast<Attribute_Selector_Ptr_Const>(&rhs))+    {+      return *this == *w;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { return name() == rhs.name(); }+    return ns() == rhs.ns();+  }++  bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const+  {+    if (is_ns_eq(ns(), rhs.ns()) && 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 == rhs_ex;+    }+    else return false;+  }++  bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const+  {+    if (Pseudo_Selector_Ptr_Const w = dynamic_cast<Pseudo_Selector_Ptr_Const>(&rhs))+    {+      return *this == *w;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { return name() == rhs.name(); }+    return ns() == rhs.ns();+  }++  bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const+  {+    if (is_ns_eq(ns(), rhs.ns()) && 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 < rhs_ex;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { return name() < rhs.name(); }+    return ns() < rhs.ns();+  }++  bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const+  {+    if (Pseudo_Selector_Ptr_Const w = dynamic_cast<Pseudo_Selector_Ptr_Const>(&rhs))+    {+      return *this < *w;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { return name() < rhs.name(); }+    return ns() < rhs.ns();+  }++  bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const+  {+    if (is_ns_eq(ns(), rhs.ns()) && 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 = dynamic_cast<Wrapped_Selector_Ptr_Const>(&rhs))+    {+      return *this == *w;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { return name() == rhs.name(); }+    return ns() == rhs.ns();+  }++  bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const+  {+    if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+    { return *(selector()) < *(rhs.selector()); }+    if (is_ns_eq(ns(), rhs.ns()))+    { return name() < rhs.name(); }+    return ns() < rhs.ns();+  }++  bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const+  {+    if (Wrapped_Selector_Ptr_Const w = dynamic_cast<Wrapped_Selector_Ptr_Const>(&rhs))+    {+      return *this < *w;+    }+    if (is_ns_eq(ns(), rhs.ns()))+    { 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 = SASS_MEMORY_CAST(Selector_List, sub->selector())) {+      if (Selector_List_Obj lhs_list = SASS_MEMORY_CAST(Selector_List, selector())) {+        return lhs_list->is_superselector_of(rhs_list);+      }+      error("is_superselector expected a Selector_List", sub->pstate());+    } else {+      error("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;+    }++    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 lhs = &(*this)[i];+      // very special case for wrapped matches selector+      if (Wrapped_Selector_Obj wrapped = SASS_MEMORY_CAST(Wrapped_Selector, lhs)) {+        if (wrapped->name() == ":not") {+          if (Selector_List_Obj not_list = SASS_MEMORY_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") {+          lhs = wrapped->selector();+          if (Selector_List_Obj list = SASS_MEMORY_CAST(Selector_List, wrapped->selector())) {+            if (Compound_Selector_Obj comp = SASS_MEMORY_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 = rhs->elements().size() > i ? &(*rhs)[i] : 0;+        if (Wrapped_Selector_Ptr wrapped_r = dynamic_cast<Wrapped_Selector_Ptr>(rhs_sel)) {+          if (wrapped->name() == wrapped_r->name()) {+          if (wrapped->is_superselector_of(wrapped_r)) {+             continue;+             rset.insert(lhs->to_string());++          }}+        }+      }+      // match from here on as strings+      lset.insert(lhs->to_string());+    }++    for (size_t n = 0, nL = rhs->length(); n < nL; ++n)+    {+      Selector_Obj r = &(*rhs)[n];+      if (Wrapped_Selector_Obj wrapped = SASS_MEMORY_CAST(Wrapped_Selector, r)) {+        if (wrapped->name() == ":not") {+          if (Selector_List_Obj ls = SASS_MEMORY_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 = SASS_MEMORY_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, Context& ctx)+  {++    // 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, ctx);++    // 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, ctx);+    Node rhsNode = complexSelectorToNode(other, ctx);++    // 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, ctx);+      // add to permutate the list?+      rhsNode.plus(unified_node);+    }++    // do some magic we inherit from node and extend+    Node node = Extend::subweave(lhsNode, rhsNode, ctx);+    Selector_List_Ptr 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, ctx), ctx)); }++    // only return if list has some entries+    return result->length() ? result : 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(), cmp_simple_selector());+    std::sort(r_lst.begin(), r_lst.end(), cmp_simple_selector());+    // 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;+    }+    // no mismatch+    return true;+  }++  bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector_Obj& pLeft, const Complex_Selector_Obj& pRight) const {+    return *pLeft < *pRight;+  }++  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());+    }+    else+    {+      return lhs->tail()->is_superselector_of(&marker->tail());+    }+    // catch-all+    return false;+  }++  size_t Complex_Selector::length() const+  {+    // TODO: make this iterative+    if (!tail()) return 1;+    return 1 + tail()->length();+  }++  Complex_Selector_Obj Complex_Selector::context(Context& ctx)+  {+    if (!tail()) return 0;+    if (!head()) return tail()->context(ctx);+    Complex_Selector_Obj cpy = SASS_MEMORY_NEW(Complex_Selector, pstate(), combinator(), head(), tail()->context(ctx));+    cpy->media_block(media_block());+    return cpy;+  }++  // 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(Context& ctx, Complex_Selector_Obj ss)+  {++    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) {+        error("Invalid parent selector", pstate_);+      } else if (last()->head_ && last()->head_->length()) {+        Compound_Selector_Obj rh = last()->head();+        size_t i = 0, L = h->length();+        if (SASS_MEMORY_CAST(Element_Selector, h->first())) {+          if (Class_Selector_Ptr sq = SASS_MEMORY_CAST(Class_Selector, rh->last())) {+            Class_Selector_Ptr sqs = SASS_MEMORY_COPY(sq);+            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 sq = SASS_MEMORY_CAST(Id_Selector, rh->last())) {+            Id_Selector_Ptr sqs = SASS_MEMORY_COPY(sq);+            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 = SASS_MEMORY_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 = SASS_MEMORY_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 {+        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_Ptr Selector_List::resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, 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(ctx, pstack, implicit_parent);+        ss->concat(&rv);+      }+    }+    return ss;+  }++  Selector_List_Ptr Complex_Selector::resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, 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(ctx, pstack, 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 (SASS_MEMORY_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) : 0);+                Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);+                // remove parent selector from sequence+                if (h->length()) h->erase(h->begin());+                ss->head(h->length() ? &h : 0);+                // 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(ctx, ss);+                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) {+                throw Exception::InvalidParent(&parent, &ss);+              }+              ss->tail(tail ? SASS_MEMORY_CLONE(tail) : 0);+              Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);+              // remove parent selector from sequence+              if (h->length()) h->erase(h->begin());+              ss->head(h->length() ? &h : 0);+              // \/ 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(ctx, &ss);+              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(ctx, &tails);+      }++      for (Simple_Selector_Obj ss : head->elements()) {+        if (Wrapped_Selector_Ptr ws = SASS_MEMORY_CAST(Wrapped_Selector, ss)) {+          if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, ws->selector())) {+            if (parents) ws->selector(sl->resolve_parent_refs(ctx, pstack, implicit_parent));+          }+        }+      }++      return retval.detach();++    }+    // has no head+    else {+      return this->tails(ctx, &tails);+    }++    // unreachable+    return 0;+  }++  Selector_List_Ptr Complex_Selector::tails(Context& ctx, 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 || !SASS_MEMORY_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()+  {+    // ToDo: implement with a while loop+    return tail_? tail_->last() : this;+  }++  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());+        }+      }+    }+  }++  bool Selector_List::has_parent_ref()+  {+    for (Complex_Selector_Obj s : elements()) {+      if (s && s->has_parent_ref()) return true;+    }+    return false;+  }++  bool Selector_List::has_real_parent_ref()+  {+    for (Complex_Selector_Obj s : elements()) {+      if (s && s->has_real_parent_ref()) return true;+    }+    return false;+  }++  bool Selector_Schema::has_parent_ref()+  {+    if (String_Schema_Obj schema = SASS_MEMORY_CAST(String_Schema, contents())) {+      return schema->length() > 0 && SASS_MEMORY_CAST(Parent_Selector, schema->at(0)) != NULL;+    }+    return false;+  }++  bool Selector_Schema::has_real_parent_ref()+  {+    if (String_Schema_Obj schema = SASS_MEMORY_CAST(String_Schema, contents())) {+      Parent_Selector_Obj p = SASS_MEMORY_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, Context& ctx) {+    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, ctx);+        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, Context& ctx, 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 && SASS_MEMORY_CAST(Parent_Selector, pHead->elements()[0]) == NULL) {+          compound_sel = pHead;+          break;+        }++        pIter = pIter->tail();+      }++      if (!pIter->head() || pIter->tail()) {+        error("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));+      }+    }+  };++  std::vector<std::string> Compound_Selector::to_str_vec()+  {+    std::vector<std::string> result(length());+    for (size_t i = 0, L = length(); i < L; ++i)+    { result.push_back((*this)[i]->to_string()); }+    return result;+  }++  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, Context& ctx)+  {+    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(ctx.c_options));+      for (size_t j = 0, M = rhs->length(); j < M; ++j)+      {+        if (thisSelector == (*rhs)[j]->to_string(ctx.c_options))+        {+          found = true;+          break;+        }+      }+      if (!found) result->append(&(*this)[i]);+    }++    return result;+  }++  void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx)+  {+    for (SourcesSet::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_rest_argument_ || */ has_keyword_argument_) {+        error("named arguments must precede variable-length argument", a->pstate());+      }+      has_named_arguments_ = true;+    }+    else if (a->is_rest_argument()) {+      if (has_rest_argument_) {+        error("functions and mixins may only be called with one variable-length argument", a->pstate());+      }+      if (has_keyword_argument_) {+        error("only keyword arguments may follow variable arguments", a->pstate());+      }+      has_rest_argument_ = true;+    }+    else if (a->is_keyword_argument()) {+      if (has_keyword_argument_) {+        error("functions and mixins may only be called with one keyword argument", a->pstate());+      }+      has_keyword_argument_ = true;+    }+    else {+      if (has_rest_argument_) {+        error("ordinal arguments must precede variable-length arguments", a->pstate());+      }+      if (has_named_arguments_) {+        error("ordinal arguments must precede named arguments", a->pstate());+      }+    }+  }++  bool Ruleset::is_invisible() const {+    if (Selector_List_Ptr sl = SASS_MEMORY_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),+    value_(val),+    zero_(zero),+    numerator_units_(std::vector<std::string>()),+    denominator_units_(std::vector<std::string>()),+    hash_(0)+  {+    size_t l = 0, r = 0;+    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) numerator_units_.push_back(unit);+          else denominator_units_.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;+        l = r + 1;+      }+    }+    concrete_type(NUMBER);+  }++  std::string Number::unit() const+  {+    std::string u;+    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+      if (i) u += '*';+      u += numerator_units_[i];+    }+    if (!denominator_units_.empty()) u += '/';+    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+      if (i) u += '*';+      u += denominator_units_[i];+    }+    return u;+  }++  bool Number::is_valid_css_unit() const+  {+    return numerator_units().size() <= 1 &&+           denominator_units().size() == 0;+  }++  bool Number::is_unitless() const+  { return numerator_units_.empty() && denominator_units_.empty(); }++  void Number::normalize(const std::string& prefered, bool strict)+  {++    // first make sure same units cancel each other out+    // it seems that a map table will fit nicely to do this+    // we basically construct exponents for each unit+    // has the advantage that they will be pre-sorted+    std::map<std::string, int> exponents;++    // initialize by summing up occurences in unit vectors+    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];+    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];++    // the final conversion factor+    double factor = 1;++    // get the first entry of numerators+    // forward it when entry is converted+    std::vector<std::string>::iterator nom_it = numerator_units_.begin();+    std::vector<std::string>::iterator nom_end = numerator_units_.end();+    std::vector<std::string>::iterator denom_it = denominator_units_.begin();+    std::vector<std::string>::iterator denom_end = denominator_units_.end();++    // main normalization loop+    // should be close to optimal+    while (denom_it != denom_end)+    {+      // get and increment afterwards+      const std::string denom = *(denom_it ++);+      // skip already canceled out unit+      if (exponents[denom] >= 0) continue;+      // skip all units we don't know how to convert+      if (string_to_unit(denom) == UNKNOWN) continue;+      // now search for nominator+      while (nom_it != nom_end)+      {+        // get and increment afterwards+        const std::string nom = *(nom_it ++);+        // skip already canceled out unit+        if (exponents[nom] <= 0) continue;+        // skip all units we don't know how to convert+        if (string_to_unit(nom) == UNKNOWN) continue;+        // we now have two convertable units+        // add factor for current conversion+        factor *= conversion_factor(nom, denom, strict);+        // update nominator/denominator exponent+        -- exponents[nom]; ++ exponents[denom];+        // inner loop done+        break;+      }+    }++    // now we can build up the new unit arrays+    numerator_units_.clear();+    denominator_units_.clear();++    // build them by iterating over the exponents+    for (auto exp : exponents)+    {+      // maybe there is more effecient way to push+      // the same item multiple times to a vector?+      for(size_t i = 0, S = abs(exp.second); i < S; ++i)+      {+        // opted to have these switches in the inner loop+        // makes it more readable and should not cost much+        if (!exp.first.empty()) {+          if (exp.second < 0) denominator_units_.push_back(exp.first);+          else if (exp.second > 0) numerator_units_.push_back(exp.first);+        }+      }+    }++    // apply factor to value_+    // best precision this way+    value_ *= factor;++    // maybe convert to other unit+    // easier implemented on its own+    try { convert(prefered, strict); }+    catch (incompatibleUnits& err)+    { error(err.what(), pstate()); }+    catch (...) { throw; }++  }++  // this does not cover all cases (multiple prefered units)+  double Number::convert_factor(const Number& n) const+  {++    // first make sure same units cancel each other out+    // it seems that a map table will fit nicely to do this+    // we basically construct exponents for each unit class+    // std::map<std::string, int> exponents;+    // initialize by summing up occurences in unit vectors+    // for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[unit_to_class(numerator_units_[i])];+    // for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[unit_to_class(denominator_units_[i])];++    std::vector<std::string> l_miss_nums(0);+    std::vector<std::string> l_miss_dens(0);+    // create copy since we need these for state keeping+    std::vector<std::string> r_nums(n.numerator_units_);+    std::vector<std::string> r_dens(n.denominator_units_);++    std::vector<std::string>::const_iterator l_num_it = numerator_units_.begin();+    std::vector<std::string>::const_iterator l_num_end = numerator_units_.end();++    bool l_unitless = is_unitless();+    bool r_unitless = n.is_unitless();++    // overall conversion+    double factor = 1;++    // process all left numerators+    while (l_num_it != l_num_end)+    {+      // get and increment afterwards+      const std::string l_num = *(l_num_it ++);++      std::vector<std::string>::iterator r_num_it = r_nums.begin();+      std::vector<std::string>::iterator r_num_end = r_nums.end();++      bool found = false;+      // search for compatible numerator+      while (r_num_it != r_num_end)+      {+        // get and increment afterwards+        const std::string r_num = *(r_num_it);+        // get possible converstion factor for units+        double conversion = conversion_factor(l_num, r_num, false);+        // skip incompatible numerator+        if (conversion == 0) {+          ++ r_num_it;+          continue;+        }+        // apply to global factor+        factor *= conversion;+        // remove item from vector+        r_nums.erase(r_num_it);+        // found numerator+        found = true;+        break;+      }+      // maybe we did not find any+      // left numerator is leftover+      if (!found) l_miss_nums.push_back(l_num);+    }++    std::vector<std::string>::const_iterator l_den_it = denominator_units_.begin();+    std::vector<std::string>::const_iterator l_den_end = denominator_units_.end();++    // process all left denominators+    while (l_den_it != l_den_end)+    {+      // get and increment afterwards+      const std::string l_den = *(l_den_it ++);++      std::vector<std::string>::iterator r_den_it = r_dens.begin();+      std::vector<std::string>::iterator r_den_end = r_dens.end();++      bool found = false;+      // search for compatible denominator+      while (r_den_it != r_den_end)+      {+        // get and increment afterwards+        const std::string r_den = *(r_den_it);+        // get possible converstion factor for units+        double conversion = conversion_factor(l_den, r_den, false);+        // skip incompatible denominator+        if (conversion == 0) {+          ++ r_den_it;+          continue;+        }+        // apply to global factor+        factor *= conversion;+        // remove item from vector+        r_dens.erase(r_den_it);+        // found denominator+        found = true;+        break;+      }+      // maybe we did not find any+      // left denominator is leftover+      if (!found) l_miss_dens.push_back(l_den);+    }++    // check left-overs (ToDo: might cancel out)+    if (l_miss_nums.size() > 0 && !r_unitless) {+      throw Exception::IncompatibleUnits(n, *this);+    }+    if (l_miss_dens.size() > 0 && !r_unitless) {+      throw Exception::IncompatibleUnits(n, *this);+    }+    if (r_nums.size() > 0 && !l_unitless) {+      throw Exception::IncompatibleUnits(n, *this);+    }+    if (r_dens.size() > 0 && !l_unitless) {+      throw Exception::IncompatibleUnits(n, *this);+    }++    return factor;+  }++  // this does not cover all cases (multiple prefered units)+  bool Number::convert(const std::string& prefered, bool strict)+  {+    // no conversion if unit is empty+    if (prefered.empty()) return true;++    // first make sure same units cancel each other out+    // it seems that a map table will fit nicely to do this+    // we basically construct exponents for each unit+    // has the advantage that they will be pre-sorted+    std::map<std::string, int> exponents;++    // initialize by summing up occurences in unit vectors+    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];+    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];++    // the final conversion factor+    double factor = 1;++    std::vector<std::string>::iterator denom_it = denominator_units_.begin();+    std::vector<std::string>::iterator denom_end = denominator_units_.end();++    // main normalization loop+    // should be close to optimal+    while (denom_it != denom_end)+    {+      // get and increment afterwards+      const std::string denom = *(denom_it ++);+      // check if conversion is needed+      if (denom == prefered) continue;+      // skip already canceled out unit+      if (exponents[denom] >= 0) continue;+      // skip all units we don't know how to convert+      if (string_to_unit(denom) == UNKNOWN) continue;+      // we now have two convertable units+      // add factor for current conversion+      factor *= conversion_factor(denom, prefered, strict);+      // update nominator/denominator exponent+      ++ exponents[denom]; -- exponents[prefered];+    }++    std::vector<std::string>::iterator nom_it = numerator_units_.begin();+    std::vector<std::string>::iterator nom_end = numerator_units_.end();++    // now search for nominator+    while (nom_it != nom_end)+    {+      // get and increment afterwards+      const std::string nom = *(nom_it ++);+      // check if conversion is needed+      if (nom == prefered) continue;+      // skip already canceled out unit+      if (exponents[nom] <= 0) continue;+      // skip all units we don't know how to convert+      if (string_to_unit(nom) == UNKNOWN) continue;+      // we now have two convertable units+      // add factor for current conversion+      factor *= conversion_factor(nom, prefered, strict);+      // update nominator/denominator exponent+      -- exponents[nom]; ++ exponents[prefered];+    }++    // now we can build up the new unit arrays+    numerator_units_.clear();+    denominator_units_.clear();++    // build them by iterating over the exponents+    for (auto exp : exponents)+    {+      // maybe there is more effecient way to push+      // the same item multiple times to a vector?+      for(size_t i = 0, S = abs(exp.second); i < S; ++i)+      {+        // opted to have these switches in the inner loop+        // makes it more readable and should not cost much+        if (!exp.first.empty()) {+          if (exp.second < 0) denominator_units_.push_back(exp.first);+          else if (exp.second > 0) numerator_units_.push_back(exp.first);+        }+      }+    }++    // apply factor to value_+    // best precision this way+    value_ *= factor;++    // success?+    return true;++  }++  // useful for making one number compatible with another+  std::string Number::find_convertible_unit() const+  {+    for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+      std::string u(numerator_units_[i]);+      if (string_to_unit(u) != UNKNOWN) return u;+    }+    for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+      std::string u(denominator_units_[i]);+      if (string_to_unit(u) != UNKNOWN) return u;+    }+    return std::string();+  }++  bool Custom_Warning::operator== (const Expression& rhs) const+  {+    if (Custom_Warning_Ptr_Const r = dynamic_cast<Custom_Warning_Ptr_Const>(&rhs)) {+      return message() == r->message();+    }+    return false;+  }++  bool Custom_Error::operator== (const Expression& rhs) const+  {+    if (Custom_Error_Ptr_Const r = dynamic_cast<Custom_Error_Ptr_Const>(&rhs)) {+      return message() == r->message();+    }+    return false;+  }++  bool Number::eq (const Expression& rhs) const+  {+    if (Number_Ptr_Const r = dynamic_cast<Number_Ptr_Const>(&rhs)) {+      size_t lhs_units = numerator_units_.size() + denominator_units_.size();+      size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size();+      if (!lhs_units && !rhs_units) {+        return std::fabs(value() - r->value()) < NUMBER_EPSILON;+      }+      return (numerator_units_ == r->numerator_units_) &&+             (denominator_units_ == r->denominator_units_) &&+             std::fabs(value() - r->value()) < NUMBER_EPSILON;+    }+    return false;+  }++  bool Number::operator== (const Expression& rhs) const+  {+    if (Number_Ptr_Const r = dynamic_cast<Number_Ptr_Const>(&rhs)) {+      size_t lhs_units = numerator_units_.size() + denominator_units_.size();+      size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size();+      // unitless and only having one unit seems equivalent (will change in future)+      if (!lhs_units || !rhs_units) {+        return std::fabs(value() - r->value()) < NUMBER_EPSILON;+      }+      return (numerator_units_ == r->numerator_units_) &&+             (denominator_units_ == r->denominator_units_) &&+             std::fabs(value() - r->value()) < NUMBER_EPSILON;+    }+    return false;+  }++  bool Number::operator< (const Number& rhs) const+  {+    size_t lhs_units = numerator_units_.size() + denominator_units_.size();+    size_t rhs_units = rhs.numerator_units_.size() + rhs.denominator_units_.size();+    // unitless and only having one unit seems equivalent (will change in future)+    if (!lhs_units || !rhs_units) {+      return value() < rhs.value();+    }++    Number tmp_r(&rhs); // copy+    tmp_r.normalize(find_convertible_unit());+    std::string l_unit(unit());+    std::string r_unit(tmp_r.unit());+    if (unit() != tmp_r.unit()) {+      error("cannot compare numbers with incompatible units", pstate());+    }+    return value() < tmp_r.value();+  }++  bool String_Quoted::operator== (const Expression& rhs) const+  {+    if (String_Quoted_Ptr_Const qstr = dynamic_cast<String_Quoted_Ptr_Const>(&rhs)) {+      return (value() == qstr->value());+    } else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(&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 = dynamic_cast<String_Quoted_Ptr_Const>(&rhs)) {+      return (value() == qstr->value());+    } else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(&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 = dynamic_cast<String_Schema_Ptr_Const>(&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 = dynamic_cast<Boolean_Ptr_Const>(&rhs)) {+      return (value() == r->value());+    }+    return false;+  }++  bool Color::operator== (const Expression& rhs) const+  {+    if (Color_Ptr_Const r = dynamic_cast<Color_Ptr_Const>(&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 = dynamic_cast<List_Ptr_Const>(&rhs)) {+      if (length() != r->length()) return false;+      if (separator() != r->separator()) 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 = dynamic_cast<Map_Ptr_Const>(&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;+  }++  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 = dynamic_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());+  }++  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();+  }++  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_, '*');+  }++  //////////////////////////////////////////////////////////////////////////////////////////+  // 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* arg = dynamic_cast<Argument*>(&obj)) {+        return arg->value();+      } else {+        return &obj;+      }+    } else {+      return &obj;+    }+  }++  //////////////////////////////////////////////////////////////////////////////////////////+  // Convert map to (key, value) list.+  //////////////////////////////////////////////////////////////////////////////////////////+  List_Obj Map::to_list(Context& ctx, 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(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(Textual);+  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);++}
libsass/src/ast.hpp view
@@ -9,2332 +9,3076 @@ #include <iostream> #include <typeinfo> #include <algorithm>-#include <unordered_map>-#include "sass/base.h"--#ifdef __clang__--/*- * There are some overloads used here that trigger the clang overload- * hiding warning. Specifically:- *- * Type type() which hides string type() from Expression- *- * and- *- * Block* block() which hides virtual Block* block() from Statement- *- */--#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 "to_string.hpp"-#include "source_map.hpp"--#include "sass.h"--namespace Sass {--  const double NUMBER_EPSILON = 0.00000000000001;--  // 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 Memory_Object {-    ADD_PROPERTY(ParserState, pstate)-  public:-    AST_Node(ParserState pstate)-    : pstate_(pstate)-    { }-    virtual ~AST_Node() = 0;-    // virtual Block* block() { return 0; }-  public:-    void update_pstate(const ParserState& pstate);-  public:-    Offset off() { return pstate(); }-    Position pos() { return pstate(); }-    ATTACH_OPERATIONS()-  };-  inline AST_Node::~AST_Node() { }---  //////////////////////////////////////////////////////////////////////-  // 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,-      C_WARNING,-      C_ERROR,-      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(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_(d),-      is_interpolant_(i),-      concrete_type_(ct)-    { }-    virtual operator bool() { return true; }-    virtual ~Expression() { }-    virtual std::string type() { 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 operator== (const Expression& rhs) const { return false; }-    virtual void set_delayed(bool delayed) { is_delayed(delayed); }-    virtual size_t hash() { return 0; }-  };--  //////////////////////////////////////////////////////////////////////-  // 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)-    { }-    virtual bool operator== (const Expression& rhs) const = 0;-    virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;-  };-}--/////////////////////////////////////////////////////////////////////////////////////-// Hash method specializations for std::unordered_map to work with Sass::Expression-/////////////////////////////////////////////////////////////////////////////////////--namespace std {-  template<>-  struct hash<Sass::Expression*>-  {-    size_t operator()(Sass::Expression* s) const-    {-      return s->hash();-    }-  };-  template<>-  struct equal_to<Sass::Expression*>-  {-    bool operator()( Sass::Expression* lhs,  Sass::Expression* 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>())-    { elements_.reserve(s); }-    virtual ~Vectorized() = 0;-    size_t length() const   { return elements_.size(); }-    bool empty() const      { return elements_.empty(); }-    T last()                { return elements_.back(); }-    T first()               { return elements_.front(); }-    T& operator[](size_t i) { return elements_[i]; }-    virtual const T& at(size_t i) const { return elements_.at(i); }-    const T& operator[](size_t i) const { return elements_[i]; }-    Vectorized& operator<<(T element)-    {-      if (!element) return *this;-      reset_hash();-      elements_.push_back(element);-      adjust_after_pushing(element);-      return *this;-    }-    Vectorized& operator+=(Vectorized* v)-    {-      for (size_t i = 0, L = v->length(); i < L; ++i) *this << (*v)[i];-      return *this;-    }-    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_; }--    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:-    std::unordered_map<Expression*, Expression*> elements_;-    std::vector<Expression*> list_;-  protected:-    size_t hash_;-    Expression* duplicate_key_;-    void reset_hash() { hash_ = 0; }-    void reset_duplicate_key() { duplicate_key_ = 0; }-    virtual void adjust_after_pushing(std::pair<Expression*, Expression*> p) { }-  public:-    Hashed(size_t s = 0) : elements_(std::unordered_map<Expression*, Expression*>(s)), list_(std::vector<Expression*>())-    { elements_.reserve(s); list_.reserve(s); reset_duplicate_key(); }-    virtual ~Hashed();-    size_t length() const                  { return list_.size(); }-    bool empty() const                     { return list_.empty(); }-    bool has(Expression* k) const          { return elements_.count(k) == 1; }-    Expression* at(Expression* k) const;-    bool has_duplicate_key() const         { return duplicate_key_ != 0; }-    Expression* get_duplicate_key() const  { return duplicate_key_; }-    const std::unordered_map<Expression*, Expression*> elements() { return elements_; }-    Hashed& operator<<(std::pair<Expression*, Expression*> 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 std::unordered_map<Expression*, Expression*>& pairs() const { return elements_; }-    const std::vector<Expression*>& keys() const { return list_; }--    std::unordered_map<Expression*, Expression*>::iterator end() { return elements_.end(); }-    std::unordered_map<Expression*, Expression*>::iterator begin() { return elements_.begin(); }-    std::unordered_map<Expression*, Expression*>::const_iterator end() const { return elements_.end(); }-    std::unordered_map<Expression*, Expression*>::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(Block*, block)-    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)-     { }-    virtual ~Statement() = 0;-    // needed for rearranging nested rulesets during CSS emission-    virtual bool   is_hoistable() { return false; }-    virtual bool   is_invisible() const { return false; }-    virtual bool   bubbles() { return false; }-    virtual Block* block()  { return 0; }-    virtual bool has_content()-    {-      return statement_type_ == CONTENT;-    }-  };-  inline Statement::~Statement() { }--  ////////////////////////-  // Blocks of statements.-  ////////////////////////-  class Block : public Statement, public Vectorized<Statement*> {-    ADD_PROPERTY(bool, is_root)-    ADD_PROPERTY(bool, is_at_root);-    // needed for properly formatted CSS emission-    ADD_PROPERTY(bool, has_hoistable)-    ADD_PROPERTY(bool, has_non_hoistable)-  protected:-    void adjust_after_pushing(Statement* s)-    {-      if (s->is_hoistable()) has_hoistable_     = true;-      else                   has_non_hoistable_ = true;-    }-  public:-    Block(ParserState pstate, size_t s = 0, bool r = false)-    : Statement(pstate),-      Vectorized<Statement*>(s),-      is_root_(r),-      is_at_root_(false),-      has_hoistable_(false),-      has_non_hoistable_(false)-    { }-    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();-    }-    Block* block() { return this; }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Abstract base class for statements that contain blocks of statements.-  ////////////////////////////////////////////////////////////////////////-  class Has_Block : public Statement {-    ADD_PROPERTY(Block*, block)-  public:-    Has_Block(ParserState pstate, Block* b)-    : Statement(pstate), block_(b)-    { }-    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*, selector)-    ADD_PROPERTY(bool, at_root);-    ADD_PROPERTY(bool, is_root);-  public:-    Ruleset(ParserState pstate, Selector* s = 0, Block* b = 0)-    : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false)-    { statement_type(RULESET); }-    bool is_invisible() const;-    // nested rulesets need to be hoisted out of their enclosing blocks-    bool is_hoistable() { return true; }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////-  // Nested declaration sets (i.e., namespaced properties).-  /////////////////////////////////////////////////////////-  class Propset : public Has_Block {-    ADD_PROPERTY(String*, property_fragment)-  public:-    Propset(ParserState pstate, String* pf, Block* b = 0)-    : Has_Block(pstate, b), property_fragment_(pf)-    { }-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Bubble.-  /////////////////-  class Bubble : public Statement {-    ADD_PROPERTY(Statement*, node)-    ADD_PROPERTY(bool, group_end)-  public:-    Bubble(ParserState pstate, Statement* n, Statement* g = 0, size_t t = 0)-    : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0)-    { }-    bool bubbles() { return true; }-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Media queries.-  /////////////////-  class Media_Block : public Has_Block {-    ADD_PROPERTY(List*, media_queries)-  public:-    Media_Block(ParserState pstate, List* mqs, Block* b)-    : Has_Block(pstate, b), media_queries_(mqs)-    { statement_type(MEDIA); }-    Media_Block(ParserState pstate, List* mqs, Block* b, Selector* s)-    : Has_Block(pstate, b), media_queries_(mqs)-    { statement_type(MEDIA); }-    bool bubbles() { return true; }-    bool is_hoistable() { return true; }-    bool is_invisible() const;-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // At-rules -- arbitrary directives beginning with "@" that may have an-  // optional statement block.-  ///////////////////////////////////////////////////////////////////////-  class At_Rule : public Has_Block {-    ADD_PROPERTY(std::string, keyword)-    ADD_PROPERTY(Selector*, selector)-    ADD_PROPERTY(Expression*, value)-  public:-    At_Rule(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0)-    : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // 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_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // Keyframe-rules -- the child blocks of "@keyframes" nodes.-  ///////////////////////////////////////////////////////////////////////-  class Keyframe_Rule : public Has_Block {-    ADD_PROPERTY(Selector*, selector)-  public:-    Keyframe_Rule(ParserState pstate, Block* b)-    : Has_Block(pstate, b), selector_(0)-    { statement_type(KEYFRAMERULE); }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Declarations -- style rules consisting of a property name and values.-  ////////////////////////////////////////////////////////////////////////-  class Declaration : public Statement {-    ADD_PROPERTY(String*, property)-    ADD_PROPERTY(Expression*, value)-    ADD_PROPERTY(bool, is_important)-    ADD_PROPERTY(bool, is_indented)-  public:-    Declaration(ParserState pstate,-                String* prop, Expression* val, bool i = false)-    : Statement(pstate), property_(prop), value_(val), is_important_(i), is_indented_(false)-    { statement_type(DECLARATION); }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////-  // Assignments -- variable and value.-  /////////////////////////////////////-  class Assignment : public Statement {-    ADD_PROPERTY(std::string, variable)-    ADD_PROPERTY(Expression*, value)-    ADD_PROPERTY(bool, is_default)-    ADD_PROPERTY(bool, is_global)-  public:-    Assignment(ParserState pstate,-               std::string var, Expression* 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); }-    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*> urls_;-    std::vector<Include>     incs_;-    ADD_PROPERTY(List*,      media_queries);-  public:-    Import(ParserState pstate)-    : Statement(pstate),-      urls_(std::vector<Expression*>()),-      incs_(std::vector<Include>()),-      media_queries_(0)-    { statement_type(IMPORT); }-    std::vector<Expression*>& urls() { return urls_; }-    std::vector<Include>& incs() { return incs_; }-    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); }-    ATTACH_OPERATIONS()-  };--  //////////////////////////////-  // The Sass `@warn` directive.-  //////////////////////////////-  class Warning : public Statement {-    ADD_PROPERTY(Expression*, message)-  public:-    Warning(ParserState pstate, Expression* msg)-    : Statement(pstate), message_(msg)-    { statement_type(WARNING); }-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////-  // The Sass `@error` directive.-  ///////////////////////////////-  class Error : public Statement {-    ADD_PROPERTY(Expression*, message)-  public:-    Error(ParserState pstate, Expression* msg)-    : Statement(pstate), message_(msg)-    { statement_type(ERROR); }-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////-  // The Sass `@debug` directive.-  ///////////////////////////////-  class Debug : public Statement {-    ADD_PROPERTY(Expression*, value)-  public:-    Debug(ParserState pstate, Expression* val)-    : Statement(pstate), value_(val)-    { statement_type(DEBUGSTMT); }-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////-  // CSS comments. These may be interpolated.-  ///////////////////////////////////////////-  class Comment : public Statement {-    ADD_PROPERTY(String*, text)-    ADD_PROPERTY(bool, is_important)-  public:-    Comment(ParserState pstate, String* txt, bool is_important)-    : Statement(pstate), text_(txt), is_important_(is_important)-    { statement_type(COMMENT); }-    virtual bool is_invisible() const-    { return is_important() == false; }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////-  // The Sass `@if` control directive.-  ////////////////////////////////////-  class If : public Has_Block {-    ADD_PROPERTY(Expression*, predicate)-    ADD_PROPERTY(Block*, alternative)-  public:-    If(ParserState pstate, Expression* pred, Block* con, Block* alt = 0)-    : Has_Block(pstate, con), predicate_(pred), alternative_(alt)-    { statement_type(IF); }-    virtual bool has_content()-    {-      return Has_Block::has_content() || (alternative_ && alternative_->has_content());-    }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////-  // The Sass `@for` control directive.-  /////////////////////////////////////-  class For : public Has_Block {-    ADD_PROPERTY(std::string, variable)-    ADD_PROPERTY(Expression*, lower_bound)-    ADD_PROPERTY(Expression*, upper_bound)-    ADD_PROPERTY(bool, is_inclusive)-  public:-    For(ParserState pstate,-        std::string var, Expression* lo, Expression* hi, Block* b, bool inc)-    : Has_Block(pstate, b),-      variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)-    { statement_type(FOR); }-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////-  // The Sass `@each` control directive.-  //////////////////////////////////////-  class Each : public Has_Block {-    ADD_PROPERTY(std::vector<std::string>, variables)-    ADD_PROPERTY(Expression*, list)-  public:-    Each(ParserState pstate, std::vector<std::string> vars, Expression* lst, Block* b)-    : Has_Block(pstate, b), variables_(vars), list_(lst)-    { statement_type(EACH); }-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////-  // The Sass `@while` control directive.-  ///////////////////////////////////////-  class While : public Has_Block {-    ADD_PROPERTY(Expression*, predicate)-  public:-    While(ParserState pstate, Expression* pred, Block* b)-    : Has_Block(pstate, b), predicate_(pred)-    { statement_type(WHILE); }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////-  // The @return directive for use inside SassScript functions.-  /////////////////////////////////////////////////////////////-  class Return : public Statement {-    ADD_PROPERTY(Expression*, value)-  public:-    Return(ParserState pstate, Expression* val)-    : Statement(pstate), value_(val)-    { statement_type(RETURN); }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////-  // The Sass `@extend` directive.-  ////////////////////////////////-  class Extension : public Statement {-    ADD_PROPERTY(Selector*, selector)-  public:-    Extension(ParserState pstate, Selector* s)-    : Statement(pstate), selector_(s)-    { statement_type(EXTEND); }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////////////-  // Definitions for both mixins and functions. The two cases are distinguished-  // by a type tag.-  /////////////////////////////////////////////////////////////////////////////-  struct Backtrace;-  typedef Environment<AST_Node*> Env;-  typedef const char* Signature;-  typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);-  typedef const char* Signature;-  class Definition : public Has_Block {-  public:-    enum Type { MIXIN, FUNCTION };-    ADD_PROPERTY(std::string, name)-    ADD_PROPERTY(Parameters*, 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* params,-               Block* 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* 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* 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_OPERATIONS()-  };--  //////////////////////////////////////-  // Mixin calls (i.e., `@include ...`).-  //////////////////////////////////////-  class Mixin_Call : public Has_Block {-    ADD_PROPERTY(std::string, name)-    ADD_PROPERTY(Arguments*, arguments)-  public:-    Mixin_Call(ParserState pstate, std::string n, Arguments* args, Block* b = 0)-    : Has_Block(pstate, b), name_(n), arguments_(args)-    { }-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////-  // The @content directive for mixin content blocks.-  ///////////////////////////////////////////////////-  class Content : public Statement {-  public:-    Content(ParserState pstate) : Statement(pstate)-    { statement_type(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*> {-    void adjust_after_pushing(Expression* e) { is_expanded(false); }-  private:-    ADD_PROPERTY(enum Sass_Separator, separator)-    ADD_PROPERTY(bool, is_arglist)-  public:-    List(ParserState pstate,-         size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false)-    : Value(pstate),-      Vectorized<Expression*>(size),-      separator_(sep), is_arglist_(argl)-    { concrete_type(LIST); }-    std::string type() { return is_arglist_ ? "arglist" : "list"; }-    static std::string type_name() { return "list"; }-    const char* sep_string(bool compressed = false) const {-      return separator() == SASS_COMMA ?-        (compressed ? "," : ", ") : " ";-    }-    bool is_invisible() const { return empty(); }-    Expression* 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());-        for (size_t i = 0, L = length(); i < L; ++i)-          hash_combine(hash_, (elements()[i])->hash());-      }-      return hash_;-    }--    virtual void set_delayed(bool delayed)-    {-      for (size_t i = 0, L = length(); i < L; ++i)-        (elements()[i])->set_delayed(delayed);-      is_delayed(delayed);-    }--    virtual bool operator== (const Expression& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////////////////////////-  // Key value paris.-  ///////////////////////////////////////////////////////////////////////-  class Map : public Value, public Hashed {-    void adjust_after_pushing(std::pair<Expression*, Expression*> p) { is_expanded(false); }-  public:-    Map(ParserState pstate,-         size_t size = 0)-    : Value(pstate),-      Hashed(size)-    { concrete_type(MAP); }-    std::string type() { return "map"; }-    static std::string type_name() { return "map"; }-    bool is_invisible() const { return empty(); }--    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;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////////////////////////////////////////-  // Binary expressions. Represents logical, relational, and arithmetic-  // operations. Templatized to avoid large switch statements and repetitive-  // subclassing.-  //////////////////////////////////////////////////////////////////////////-  class Binary_Expression : public Expression {-  private:-    ADD_HASHED(enum Sass_OP, type)-    ADD_HASHED(Expression*, left)-    ADD_HASHED(Expression*, right)-    size_t hash_;-  public:-    Binary_Expression(ParserState pstate,-                      enum Sass_OP t, Expression* lhs, Expression* rhs)-    : Expression(pstate), type_(t), left_(lhs), right_(rhs), hash_(0)-    { }-    const std::string type_name() {-      switch (type_) {-        case AND: return "and"; break;-        case OR: return "or"; break;-        case EQ: return "eq"; break;-        case NEQ: return "neq"; break;-        case GT: return "gt"; break;-        case GTE: return "gte"; break;-        case LT: return "lt"; break;-        case LTE: return "lte"; break;-        case ADD: return "add"; break;-        case SUB: return "sub"; break;-        case MUL: return "mul"; break;-        case DIV: return "div"; break;-        case MOD: return "mod"; break;-        case NUM_OPS: return "num_ops"; break;-        default: return "invalid"; break;-      }-    }-    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-      {-        const Binary_Expression* m = dynamic_cast<const 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>()(type_);-        hash_combine(hash_, left()->hash());-        hash_combine(hash_, right()->hash());-      }-      return hash_;-    }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////////-  // Arithmetic negation (logical negation is just an ordinary function call).-  ////////////////////////////////////////////////////////////////////////////-  class Unary_Expression : public Expression {-  public:-    enum Type { PLUS, MINUS, NOT };-  private:-    ADD_HASHED(Type, type)-    ADD_HASHED(Expression*, operand)-    size_t hash_;-  public:-    Unary_Expression(ParserState pstate, Type t, Expression* o)-    : Expression(pstate), type_(t), operand_(o), hash_(0)-    { }-    const std::string type_name() {-      switch (type_) {-        case PLUS: return "plus"; break;-        case MINUS: return "minus"; break;-        case NOT: return "not"; break;-        default: return "invalid"; break;-      }-    }-    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        const Unary_Expression* m = dynamic_cast<const 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>()(type_);-        hash_combine(hash_, operand()->hash());-      };-      return hash_;-    }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////-  // Individual argument objects for mixin and function calls.-  ////////////////////////////////////////////////////////////-  class Argument : public Expression {-    ADD_HASHED(Expression*, value)-    ADD_HASHED(std::string, name)-    ADD_PROPERTY(bool, is_rest_argument)-    ADD_PROPERTY(bool, is_keyword_argument)-    size_t hash_;-  public:-    Argument(ParserState pstate, Expression* 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_) {-        error("variable-length argument may not be passed by name", pstate);-      }-    }--    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        const Argument* m = dynamic_cast<const 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_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*> {-    ADD_PROPERTY(bool, has_named_arguments)-    ADD_PROPERTY(bool, has_rest_argument)-    ADD_PROPERTY(bool, has_keyword_argument)-  protected:-    void adjust_after_pushing(Argument* a);-  public:-    Arguments(ParserState pstate)-    : Expression(pstate),-      Vectorized<Argument*>(),-      has_named_arguments_(false),-      has_rest_argument_(false),-      has_keyword_argument_(false)-    { }-    ATTACH_OPERATIONS()-  };--  //////////////////-  // Function calls.-  //////////////////-  class Function_Call : public Expression {-    ADD_HASHED(std::string, name)-    ADD_HASHED(Arguments*, arguments)-    ADD_PROPERTY(void*, cookie)-    size_t hash_;-  public:-    Function_Call(ParserState pstate, std::string n, Arguments* args, void* cookie)-    : Expression(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0)-    { concrete_type(STRING); }-    Function_Call(ParserState pstate, std::string n, Arguments* args)-    : Expression(pstate), name_(n), arguments_(args), cookie_(0), hash_(0)-    { concrete_type(STRING); }--    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        const Function_Call* m = dynamic_cast<const 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_OPERATIONS()-  };--  /////////////////////////-  // Function call schemas.-  /////////////////////////-  class Function_Call_Schema : public Expression {-    ADD_PROPERTY(String*, name)-    ADD_PROPERTY(Arguments*, arguments)-  public:-    Function_Call_Schema(ParserState pstate, String* n, Arguments* args)-    : Expression(pstate), name_(n), arguments_(args)-    { concrete_type(STRING); }-    ATTACH_OPERATIONS()-  };--  ///////////////////////-  // Variable references.-  ///////////////////////-  class Variable : public Expression {-    ADD_PROPERTY(std::string, name)-  public:-    Variable(ParserState pstate, std::string n)-    : Expression(pstate), name_(n)-    { }--    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        const Variable* e = dynamic_cast<const 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_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////////-  // Textual (i.e., unevaluated) numeric data. Variants are distinguished with-  // a type tag.-  ////////////////////////////////////////////////////////////////////////////-  class Textual : public Expression {-  public:-    enum Type { NUMBER, PERCENTAGE, DIMENSION, HEX };-  private:-    ADD_HASHED(Type, type)-    ADD_HASHED(std::string, value)-    size_t hash_;-  public:-    Textual(ParserState pstate, Type t, std::string val)-    : Expression(pstate, true), type_(t), value_(val),-      hash_(0)-    { }--    virtual bool operator==(const Expression& rhs) const-    {-      try-      {-        const Textual* e = dynamic_cast<const Textual*>(&rhs);-        return e && value() == e->value() && type() == e->type();-      }-      catch (std::bad_cast&)-      {-        return false;-      }-      catch (...) { throw; }-    }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<std::string>()(value_);-        hash_combine(hash_, std::hash<int>()(type_));-      }-      return hash_;-    }--    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////-  // Numbers, percentages, dimensions, and colors.-  ////////////////////////////////////////////////-  class Number : public Value {-    ADD_HASHED(double, value)-    ADD_PROPERTY(bool, zero)-    std::vector<std::string> numerator_units_;-    std::vector<std::string> denominator_units_;-    size_t hash_;-  public:-    Number(ParserState pstate, double val, std::string u = "", bool zero = true);-    bool            zero()              { return zero_; }-    std::vector<std::string>& numerator_units()   { return numerator_units_; }-    std::vector<std::string>& denominator_units() { return denominator_units_; }-    const std::vector<std::string>& numerator_units() const   { return numerator_units_; }-    const std::vector<std::string>& denominator_units() const { return denominator_units_; }-    std::string type() { return "number"; }-    static std::string type_name() { return "number"; }-    std::string unit() const;--    bool is_unitless();-    void convert(const std::string& unit = "", bool strict = false);-    void normalize(const std::string& unit = "", bool strict = false);-    // useful for making one number compatible with another-    std::string find_convertible_unit() const;--    virtual size_t hash()-    {-      if (hash_ == 0) {-        hash_ = std::hash<double>()(value_);-        for (const auto numerator : numerator_units())-          hash_combine(hash_, std::hash<std::string>()(numerator));-        for (const auto denominator : denominator_units())-          hash_combine(hash_, std::hash<std::string>()(denominator));-      }-      return hash_;-    }--    virtual bool operator< (const Number& rhs) const;-    virtual bool operator== (const Expression& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    ATTACH_OPERATIONS()-  };--  //////////-  // Colors.-  //////////-  class Color : public Value {-    ADD_HASHED(double, r)-    ADD_HASHED(double, g)-    ADD_HASHED(double, b)-    ADD_HASHED(double, a)-    ADD_PROPERTY(bool, sixtuplet)-    ADD_PROPERTY(std::string, disp)-    size_t hash_;-  public:-    Color(ParserState pstate, double r, double g, double b, double a = 1, bool sixtuplet = true, const std::string disp = "")-    : Value(pstate), r_(r), g_(g), b_(b), a_(a), sixtuplet_(sixtuplet), disp_(disp),-      hash_(0)-    { concrete_type(COLOR); }-    std::string type() { 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;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    ATTACH_OPERATIONS()-  };--  //////////////////////////////-  // Errors from Sass_Values.-  //////////////////////////////-  class Custom_Error : public Value {-    ADD_PROPERTY(std::string, message)-  public:-    Custom_Error(ParserState pstate, std::string msg)-    : Value(pstate), message_(msg)-    { concrete_type(C_ERROR); }-    virtual bool operator== (const Expression& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  //////////////////////////////-  // Warnings from Sass_Values.-  //////////////////////////////-  class Custom_Warning : public Value {-    ADD_PROPERTY(std::string, message)-  public:-    Custom_Warning(ParserState pstate, std::string msg)-    : Value(pstate), message_(msg)-    { concrete_type(C_WARNING); }-    virtual bool operator== (const Expression& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  ////////////-  // Booleans.-  ////////////-  class Boolean : public Value {-    ADD_HASHED(bool, value)-    size_t hash_;-  public:-    Boolean(ParserState pstate, bool val)-    : Value(pstate), value_(val),-      hash_(0)-    { concrete_type(BOOLEAN); }-    virtual operator bool() { return value_; }-    std::string type() { 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;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////////////////////////-  // Abstract base class for Sass string values. Includes interpolated and-  // "flat" strings.-  ////////////////////////////////////////////////////////////////////////-  class String : public Value {-    ADD_PROPERTY(bool, sass_fix_1291)-  public:-    String(ParserState pstate, bool delayed = false, bool sass_fix_1291 = false)-    : Value(pstate, delayed), sass_fix_1291_(sass_fix_1291)-    { concrete_type(STRING); }-    static std::string type_name() { return "string"; }-    virtual ~String() = 0;-    virtual bool operator==(const Expression& rhs) const = 0;-    virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;-    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*> {-    ADD_PROPERTY(bool, has_interpolants)-    size_t hash_;-  public:-    String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)-    : String(pstate), Vectorized<Expression*>(size), has_interpolants_(has_interpolants), hash_(0)-    { concrete_type(STRING); }-    std::string type() { return "string"; }-    static std::string type_name() { return "string"; }--    virtual size_t hash()-    {-      if (hash_ == 0) {-        for (auto string : elements())-          hash_combine(hash_, string->hash());-      }-      return hash_;-    }--    virtual bool operator==(const Expression& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    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)-    ADD_HASHED(std::string, value)-  protected:-    size_t hash_;-  public:-    String_Constant(ParserState pstate, std::string val)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)-    { }-    String_Constant(ParserState pstate, const char* beg)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0)-    { }-    String_Constant(ParserState pstate, const char* beg, const char* end)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0)-    { }-    String_Constant(ParserState pstate, const Token& tok)-    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)-    { }-    std::string type() { return "string"; }-    static std::string type_name() { return "string"; }--    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 to_string(bool compressed = false, int precision = 5) const;--    // static char auto_quote() { return '*'; }-    static char double_quote() { return '"'; }-    static char single_quote() { return '\''; }--    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)-    : String_Constant(pstate, val)-    {-      value_ = unquote(value_, &quote_mark_, keep_utf8_escapes);-      if (q && quote_mark_) quote_mark_ = q;-    }-    virtual bool operator==(const Expression& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  /////////////////-  // Media queries.-  /////////////////-  class Media_Query : public Expression,-                      public Vectorized<Media_Query_Expression*> {-    ADD_PROPERTY(String*, media_type)-    ADD_PROPERTY(bool, is_negated)-    ADD_PROPERTY(bool, is_restricted)-  public:-    Media_Query(ParserState pstate,-                String* t = 0, size_t s = 0, bool n = false, bool r = false)-    : Expression(pstate), Vectorized<Media_Query_Expression*>(s),-      media_type_(t), is_negated_(n), is_restricted_(r)-    { }-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////////-  // Media expressions (for use inside media queries).-  ////////////////////////////////////////////////////-  class Media_Query_Expression : public Expression {-    ADD_PROPERTY(Expression*, feature)-    ADD_PROPERTY(Expression*, value)-    ADD_PROPERTY(bool, is_interpolated)-  public:-    Media_Query_Expression(ParserState pstate,-                           Expression* f, Expression* v, bool i = false)-    : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)-    { }-    ATTACH_OPERATIONS()-  };--  ////////////////////-  // `@supports` rule.-  ////////////////////-  class Supports_Block : public Has_Block {-    ADD_PROPERTY(Supports_Condition*, condition)-  public:-    Supports_Block(ParserState pstate, Supports_Condition* condition, Block* block = 0)-    : Has_Block(pstate, block), condition_(condition)-    { statement_type(SUPPORTS); }-    bool is_hoistable() { return true; }-    bool bubbles() { return true; }-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////////////////////-  // The abstract superclass of all Supports conditions.-  //////////////////////////////////////////////////////-  class Supports_Condition : public Expression {-  public:-    Supports_Condition(ParserState pstate)-    : Expression(pstate)-    { }-    virtual bool needs_parens(Supports_Condition* cond) const { return false; }-    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*, left);-    ADD_PROPERTY(Supports_Condition*, right);-    ADD_PROPERTY(Operand, operand);-  public:-    Supports_Operator(ParserState pstate, Supports_Condition* l, Supports_Condition* r, Operand o)-    : Supports_Condition(pstate), left_(l), right_(r), operand_(o)-    { }-    virtual bool needs_parens(Supports_Condition* cond) const;-    ATTACH_OPERATIONS()-  };--  //////////////////////////////////////////-  // A negation condition (`not CONDITION`).-  //////////////////////////////////////////-  class Supports_Negation : public Supports_Condition {-  private:-    ADD_PROPERTY(Supports_Condition*, condition);-  public:-    Supports_Negation(ParserState pstate, Supports_Condition* c)-    : Supports_Condition(pstate), condition_(c)-    { }-    virtual bool needs_parens(Supports_Condition* cond) const;-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////-  // A declaration condition (e.g. `(feature: value)`).-  /////////////////////////////////////////////////////-  class Supports_Declaration : public Supports_Condition {-  private:-    ADD_PROPERTY(Expression*, feature);-    ADD_PROPERTY(Expression*, value);-  public:-    Supports_Declaration(ParserState pstate, Expression* f, Expression* v)-    : Supports_Condition(pstate), feature_(f), value_(v)-    { }-    virtual bool needs_parens(Supports_Condition* cond) const { return false; }-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////-  // An interpolation condition (e.g. `#{$var}`).-  ///////////////////////////////////////////////-  class Supports_Interpolation : public Supports_Condition {-  private:-    ADD_PROPERTY(Expression*, value);-  public:-    Supports_Interpolation(ParserState pstate, Expression* v)-    : Supports_Condition(pstate), value_(v)-    { }-    virtual bool needs_parens(Supports_Condition* cond) const { return false; }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////-  // At root expressions (for use inside @at-root).-  /////////////////////////////////////////////////-  class At_Root_Expression : public Expression {-  private:-    ADD_PROPERTY(String*, feature)-    ADD_PROPERTY(Expression*, value)-    ADD_PROPERTY(bool, is_interpolated)-  public:-    At_Root_Expression(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false)-    : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)-    { }-    bool exclude(std::string str)-    {-      To_String to_string;-      bool with = feature() && unquote(feature()->perform(&to_string)).compare("with") == 0;-      List* l = static_cast<List*>(value());-      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]->perform(&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]->perform(&to_string));-          if (v.compare("all") == 0 || v == str) return true;-        }-        return false;-      }-    }-    ATTACH_OPERATIONS()-  };--  ///////////-  // At-root.-  ///////////-  class At_Root_Block : public Has_Block {-    ADD_PROPERTY(At_Root_Expression*, expression)-  public:-    At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Expression* e = 0)-    : Has_Block(pstate, b), expression_(e)-    { statement_type(ATROOT); }-    bool is_hoistable() { return true; }-    bool bubbles() { return true; }-    bool exclude_node(Statement* s) {-      if (s->statement_type() == Statement::DIRECTIVE)-      {-        return expression()->exclude(static_cast<At_Rule*>(s)->keyword().erase(0, 1));-      }-      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 (static_cast<At_Rule*>(s)->is_keyframes())-      {-        return expression()->exclude("keyframes");-      }-      return false;-    }-    ATTACH_OPERATIONS()-  };--  //////////////////-  // The null value.-  //////////////////-  class Null : public Value {-  public:-    Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); }-    std::string type() { 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;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;--    ATTACH_OPERATIONS()-  };--  /////////////////////////////////-  // Thunks for delayed evaluation.-  /////////////////////////////////-  class Thunk : public Expression {-    ADD_PROPERTY(Expression*, expression)-    ADD_PROPERTY(Env*, environment)-  public:-    Thunk(ParserState pstate, Expression* exp, Env* env = 0)-    : Expression(pstate), expression_(exp), environment_(env)-    { }-  };--  /////////////////////////////////////////////////////////-  // Individual parameter objects for mixins and functions.-  /////////////////////////////////////////////////////////-  class Parameter : public AST_Node {-    ADD_PROPERTY(std::string, name)-    ADD_PROPERTY(Expression*, default_value)-    ADD_PROPERTY(bool, is_rest_parameter)-  public:-    Parameter(ParserState pstate,-              std::string n, Expression* def = 0, bool rest = false)-    : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)-    {-      if (default_value_ && is_rest_parameter_) {-        error("variable-length parameter may not have a default value", pstate);-      }-    }-    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*> {-    ADD_PROPERTY(bool, has_optional_parameters)-    ADD_PROPERTY(bool, has_rest_parameter)-  protected:-    void adjust_after_pushing(Parameter* p)-    {-      if (p->default_value()) {-        if (has_rest_parameter_) {-          error("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_) {-          error("functions and mixins cannot have more than one variable-length parameter", p->pstate());-        }-        has_rest_parameter_ = true;-      }-      else {-        if (has_rest_parameter_) {-          error("required parameters must precede variable-length parameters", p->pstate());-        }-        if (has_optional_parameters_) {-          error("required parameters must precede optional parameters", p->pstate());-        }-      }-    }-  public:-    Parameters(ParserState pstate)-    : AST_Node(pstate),-      Vectorized<Parameter*>(),-      has_optional_parameters_(false),-      has_rest_parameter_(false)-    { }-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////-  // Abstract base class for CSS selectors.-  /////////////////////////////////////////-  class Selector : public Expression {-    ADD_PROPERTY(bool, has_reference)-    ADD_PROPERTY(bool, has_placeholder)-    // 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-    ADD_PROPERTY(Media_Block*, media_block)-  public:-    Selector(ParserState pstate, bool r = false, bool h = false)-    : Expression(pstate),-      has_reference_(r),-      has_placeholder_(h),-      has_line_feed_(false),-      has_line_break_(false),-      is_optional_(false),-      media_block_(0)-    { concrete_type(SELECTOR); }-    virtual ~Selector() = 0;-    virtual unsigned long specificity() {-      return Constants::Specificity_Universal;-    }-    virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;-  };-  inline Selector::~Selector() { }--  /////////////////////////////////////////////////////////////////////////-  // Interpolated selectors -- the interpolated String will be expanded and-  // re-parsed into a normal selector class.-  /////////////////////////////////////////////////////////////////////////-  class Selector_Schema : public Selector {-    ADD_PROPERTY(String*, contents)-    ADD_PROPERTY(bool, at_root);-  public:-    Selector_Schema(ParserState pstate, String* c)-    : Selector(pstate), contents_(c), at_root_(false)-    { }-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////-  // Abstract base class for simple selectors.-  ////////////////////////////////////////////-  class Simple_Selector : public Selector {-    ADD_PROPERTY(std::string, ns);-    ADD_PROPERTY(std::string, name)-    ADD_PROPERTY(bool, has_ns)-  public:-    Simple_Selector(ParserState pstate, std::string n = "")-    : Selector(pstate), ns_(""), name_(n), has_ns_(false)-    {-      size_t pos = n.find('|');-      // found some namespace-      if (pos != std::string::npos) {-        has_ns_ = true;-        ns_ = n.substr(0, pos);-        name_ = n.substr(pos + 1);-      }-    }-    virtual std::string ns_name() const-    {-      std::string name("");-      if (has_ns_)-        name += ns_ + "|";-      return name + name_;-    }-    // 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 ~Simple_Selector() = 0;-    virtual Compound_Selector* unify_with(Compound_Selector*, Context&);-    virtual bool has_parent_ref() { return false; };-    virtual bool is_pseudo_element() { return false; }-    virtual bool is_pseudo_class() { return false; }--    virtual bool is_superselector_of(Compound_Selector* sub) { return false; }--    bool operator==(const Simple_Selector& rhs) const;-    inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); }--    bool operator<(const Simple_Selector& rhs) const;-    // default implementation should work for most of the simple selectors (otherwise overload)-    virtual std::string to_string(bool compressed = false, int precision = 5) const { return this->ns_name(); };-    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 {-  public:-    Parent_Selector(ParserState pstate)-    : Simple_Selector(pstate, "&")-    { has_reference(true); }-    virtual bool has_parent_ref() { return true; };-    virtual unsigned long specificity()-    {-      return 0;-    }-    std::string type() { return "selector"; }-    static std::string type_name() { return "selector"; }-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////////-  // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.-  /////////////////////////////////////////////////////////////////////////-  class Selector_Placeholder : public Simple_Selector {-  public:-    Selector_Placeholder(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { has_placeholder(true); }-    // virtual Selector_Placeholder* find_placeholder();-    virtual ~Selector_Placeholder() {};-    ATTACH_OPERATIONS()-  };--  /////////////////////////////////////////////////////////////////////-  // Type selectors (and the universal selector) -- e.g., div, span, *.-  /////////////////////////////////////////////////////////////////////-  class Type_Selector : public Simple_Selector {-  public:-    Type_Selector(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { }-    virtual unsigned long specificity()-    {-      // ToDo: What is the specificity of the star selector?-      if (name() == "*") return Constants::Specificity_Universal;-      else               return Constants::Specificity_Type;-    }-    virtual Simple_Selector* unify_with(Simple_Selector*, Context&);-    virtual Compound_Selector* unify_with(Compound_Selector*, Context&);-    ATTACH_OPERATIONS()-  };--  ////////////////////////////////////////////////-  // Selector qualifiers -- i.e., classes and ids.-  ////////////////////////////////////////////////-  class Selector_Qualifier : public Simple_Selector {-  public:-    Selector_Qualifier(ParserState pstate, std::string n)-    : Simple_Selector(pstate, n)-    { }-    virtual unsigned long specificity()-    {-      if (name()[0] == '#') return Constants::Specificity_ID;-      if (name()[0] == '.') return Constants::Specificity_Class;-      else                  return Constants::Specificity_Type;-    }-    virtual Compound_Selector* unify_with(Compound_Selector*, Context&);-    ATTACH_OPERATIONS()-  };--  ///////////////////////////////////////////////////-  // Attribute selectors -- e.g., [src*=".jpg"], etc.-  ///////////////////////////////////////////////////-  class Attribute_Selector : public Simple_Selector {-    ADD_PROPERTY(std::string, matcher)-    ADD_PROPERTY(String*, value) // might be interpolated-  public:-    Attribute_Selector(ParserState pstate, std::string n, std::string m, String* v)-    : Simple_Selector(pstate, n), matcher_(m), value_(v)-    { }-    virtual unsigned long specificity()-    {-      return Constants::Specificity_Attr;-    }-    bool operator==(const Simple_Selector& rhs) const;-    bool operator==(const Attribute_Selector& rhs) const;-    bool operator<(const Simple_Selector& rhs) const;-    bool operator<(const Attribute_Selector& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    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";-  }--  class Pseudo_Selector : public Simple_Selector {-    ADD_PROPERTY(String*, expression)-  public:-    Pseudo_Selector(ParserState pstate, std::string n, String* expr = 0)-    : Simple_Selector(pstate, n), expression_(expr)-    { }--    // A pseudo-class always consists of a "colon" (:) followed by the name-    // of the pseudo-class and optionally by a value between parentheses.-    virtual bool is_pseudo_class()-    {-      return (name_[0] == ':' && name_[1] != ':')-             && ! is_pseudo_class_element(name_);-    }--    // 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()-    {-      return (name_[0] == ':' && name_[1] == ':')-             || is_pseudo_class_element(name_);-    }-    virtual unsigned long specificity()-    {-      if (is_pseudo_element())-        return Constants::Specificity_Type;-      return Constants::Specificity_Pseudo;-    }-    virtual Compound_Selector* unify_with(Compound_Selector*, Context&);-    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*, selector)-  public:-    Wrapped_Selector(ParserState pstate, std::string n, Selector* sel)-    : Simple_Selector(pstate, n), selector_(sel)-    { }-    virtual bool is_superselector_of(Wrapped_Selector* sub);-    // Selectors inside the negation pseudo-class are counted like any-    // other, but the negation itself does not count as a pseudo-class.-    virtual unsigned long specificity()-    {-      return selector_ ? selector_->specificity() : 0;-    }-    bool operator==(const Simple_Selector& rhs) const;-    bool operator==(const Wrapped_Selector& rhs) const;-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  struct Complex_Selector_Pointer_Compare {-    bool operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const;-  };--  ////////////////////////////////////////////////////////////////////////////-  // Simple selector sequences. Maintains flags indicating whether it contains-  // any parent references or placeholders, to simplify expansion.-  ////////////////////////////////////////////////////////////////////////////-  typedef std::set<Complex_Selector*, Complex_Selector_Pointer_Compare> SourcesSet;-  class Compound_Selector : public Selector, public Vectorized<Simple_Selector*> {-  private:-    SourcesSet sources_;-    ADD_PROPERTY(bool, has_parent_reference);-  protected:-    void adjust_after_pushing(Simple_Selector* 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*>(s),-      has_parent_reference_(false)-    { }-    bool contains_placeholder() {-      for (size_t i = 0, L = length(); i < L; ++i) {-        if ((*this)[i]->has_placeholder()) return true;-      }-      return false;-    };--    bool is_universal() const-    {-      return length() == 1 && (*this)[0]->is_universal();-    }--    Complex_Selector* to_complex(Memory_Manager& mem);-    Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx);-    // virtual Selector_Placeholder* find_placeholder();-    virtual bool has_parent_ref();-    Simple_Selector* base()-    {-      // Implement non-const in terms of const. Safe to const_cast since this method is non-const-      return const_cast<Simple_Selector*>(static_cast<const Compound_Selector*>(this)->base());-    }-    const Simple_Selector* base() const {-      if (length() == 0) return 0;-      if (typeid(*(*this)[0]) == typeid(Type_Selector))-        return (*this)[0];-//      else cerr << "SERIOUSELY " << "\n";-      return 0;-    }-    virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapped = "");-    virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapped = "");-    virtual bool is_superselector_of(Selector_List* sub, std::string wrapped = "");-    virtual unsigned long specificity()-    {-      int sum = 0;-      for (size_t i = 0, L = length(); i < L; ++i)-      { sum += (*this)[i]->specificity(); }-      return sum;-    }--    bool is_empty_reference()-    {-      return length() == 1 &&-             typeid(*(*this)[0]) == typeid(Parent_Selector);-    }-    std::vector<std::string> to_str_vec(); // sometimes need to convert to a flat "by-value" data structure--    bool operator<(const Compound_Selector& rhs) const;--    bool operator==(const Compound_Selector& rhs) const;-    inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); }--    SourcesSet& sources() { return sources_; }-    void clearSources() { sources_.clear(); }-    void mergeSources(SourcesSet& sources, Context& ctx);--    Compound_Selector* clone(Context&) const; // does not clone the Simple_Selector*s--    Compound_Selector* minus(Compound_Selector* rhs, Context& ctx);-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    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:-    ADD_PROPERTY(Combinator, combinator)-    ADD_PROPERTY(Compound_Selector*, head)-    ADD_PROPERTY(Complex_Selector*, tail)-    ADD_PROPERTY(String*, 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* h = 0,-                     Complex_Selector* t = 0,-                     String* r = 0)-    : Selector(pstate),-      combinator_(c),-      head_(h), tail_(t),-      reference_(r)-    {-      if ((h && h->has_reference())   || (t && t->has_reference()))   has_reference(true);-      if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true);-    }-    virtual bool has_parent_ref();--    Complex_Selector* 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;-    }--    Complex_Selector* context(Context&);---    // front returns the first real tail-    // skips over parent and empty ones-    const Complex_Selector* first() const;--    // last returns the last real tail-    const Complex_Selector* last() const;--    Selector_List* tails(Context& ctx, Selector_List* tails);--    // unconstant accessors-    Complex_Selector* first();-    Complex_Selector* last();--    // some shortcuts that should be removed-    const Complex_Selector* innermost() const { return last(); };-    Complex_Selector* innermost() { return last(); };--    size_t length() const;-    Selector_List* parentize(Selector_List* parents, Context& ctx);-    virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");-    virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");-    virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");-    // virtual Selector_Placeholder* find_placeholder();-    Selector_List* unify_with(Complex_Selector* rhs, Context& ctx);-    Combinator clear_innermost();-    void append(Context&, Complex_Selector*);-    void set_innermost(Complex_Selector*, Combinator);-    virtual unsigned long specificity() const-    {-      int sum = 0;-      if (head()) sum += head()->specificity();-      if (tail()) sum += tail()->specificity();-      return sum;-    }-    bool operator<(const Complex_Selector& rhs) const;-    bool operator==(const Complex_Selector& rhs) const;-    inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }-    SourcesSet sources()-    {-      //s = Set.new-      //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}-      //s--      SourcesSet srcs;--      Compound_Selector* pHead = head();-      Complex_Selector*  pTail = tail();--      if (pHead) {-        SourcesSet& headSources = pHead->sources();-        srcs.insert(headSources.begin(), headSources.end());-      }--      if (pTail) {-        SourcesSet tailSources = pTail->sources();-        srcs.insert(tailSources.begin(), tailSources.end());-      }--      return srcs;-    }-    void addSources(SourcesSet& sources, Context& ctx) {-      // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}-      Complex_Selector* pIter = this;-      while (pIter) {-        Compound_Selector* pHead = pIter->head();--        if (pHead) {-          pHead->mergeSources(sources, ctx);-        }--        pIter = pIter->tail();-      }-    }-    void clearSources() {-      Complex_Selector* pIter = this;-      while (pIter) {-        Compound_Selector* pHead = pIter->head();--        if (pHead) {-          pHead->clearSources();-        }--        pIter = pIter->tail();-      }-    }-    Complex_Selector* clone(Context&) const;      // does not clone Compound_Selector*s-    Complex_Selector* cloneFully(Context&) const; // clones Compound_Selector*s-    // std::vector<Compound_Selector*> to_vector();-    virtual std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  typedef std::deque<Complex_Selector*> ComplexSelectorDeque;-  typedef Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;--  ///////////////////////////////////-  // Comma-separated selector groups.-  ///////////////////////////////////-  class Selector_List : public Selector, public Vectorized<Complex_Selector*> {-    ADD_PROPERTY(std::vector<std::string>, wspace)-  protected:-    void adjust_after_pushing(Complex_Selector* c);-  public:-    Selector_List(ParserState pstate, size_t s = 0)-    : Selector(pstate), Vectorized<Complex_Selector*>(s), wspace_(0)-    { }-    std::string type() { return "list"; }-    // remove parent selector references-    // basically unwraps parsed selectors-    void remove_parent_selectors();-    // virtual Selector_Placeholder* find_placeholder();-    Selector_List* parentize(Selector_List* parents, Context& ctx);-    virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");-    virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");-    virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");-    Selector_List* unify_with(Selector_List*, Context&);-    void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&);-    virtual unsigned long specificity()-    {-      unsigned long sum = 0;-      unsigned long specificity = 0;-      for (size_t i = 0, L = length(); i < L; ++i)-      {-        specificity = (*this)[i]->specificity();-        if (sum < specificity) sum = specificity;-      }-      return sum;-    }-    Selector_List* clone(Context&) const;      // does not clone Compound_Selector*s-    Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s-    virtual bool operator==(const Selector& 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 std::string to_string(bool compressed = false, int precision = 5) const;-    ATTACH_OPERATIONS()-  };--  template<typename SelectorType>-  bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) {-    // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector-    // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but-    // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting-    // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the-    // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the-    // members of a SimpleSequence (Compound_Selector)).-    //-    // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for-    // use with Compound_Selector and Complex_Selector objects.-    if (simpleSelectorOrderDependent) {-      return !(one < two) && !(two < one);-    } else {-      return one == two;-    }-  }--  // compare function for sorting and probably other other uses-  struct cmp_complex_selector { inline bool operator() (const Complex_Selector* l, const Complex_Selector* r) { return (*l < *r); } };-  struct cmp_compound_selector { inline bool operator() (const Compound_Selector* l, const Compound_Selector* r) { return (*l < *r); } };-  struct cmp_simple_selector { inline bool operator() (const Simple_Selector* l, const Simple_Selector* r) { return (*l < *r); } };+#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;++  // 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 std::string to_string(Sass_Inspect_Options opt) const;+    virtual std::string to_string() const;+    virtual void cloneChildren() {};+  public:+    void update_pstate(const ParserState& pstate);+    void set_pstate_offset(const Offset& offset);+  public:+    Offset off() { return pstate(); }+    Position pos() { return pstate(); }+    ATTACH_OPERATIONS()+  };+  inline AST_Node::~AST_Node() { }+++  //////////////////////////////////////////////////////////////////////+  // 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,+      C_WARNING,+      C_ERROR,+      FUNCTION,+      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() { 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>())+    { elements_.reserve(s); list_.reserve(s); reset_duplicate_key(); }+    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)+    ADD_PROPERTY(bool, is_at_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),+      is_at_root_(false)+    { }+    Block(const Block* ptr)+    : Statement(ptr),+      Vectorized<Statement_Obj>(*ptr),+      is_root_(ptr->is_root_),+      is_at_root_(ptr->is_at_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_Obj, selector)+    ADD_PROPERTY(bool, at_root);+    ADD_PROPERTY(bool, is_root);+  public:+    Ruleset(ParserState pstate, Selector_Obj s = 0, Block_Obj b = 0)+    : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false)+    { statement_type(RULESET); }+    Ruleset(const Ruleset* ptr)+    : Has_Block(ptr),+      selector_(ptr->selector_),+      at_root_(ptr->at_root_),+      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_PROPERTY(std::string, name)+  public:+    Trace(ParserState pstate, std::string n, Block_Obj b = 0)+    : Has_Block(pstate, b), name_(n)+    { }+    Trace(const Trace* ptr)+    : Has_Block(ptr),+      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(ParserState pstate, List_Obj mqs, Block_Obj b, Selector_Obj s)+    : 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_PROPERTY(std::string, keyword)+    ADD_PROPERTY(Selector_Obj, selector)+    ADD_PROPERTY(Expression_Obj, value)+  public:+    Directive(ParserState pstate, std::string kwd, Selector_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_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_indented)+  public:+    Declaration(ParserState pstate,+                String_Obj prop, Expression_Obj val, bool i = false, Block_Obj b = 0)+    : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), 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_indented_(ptr->is_indented_)+    { statement_type(DECLARATION); }+    ATTACH_AST_OPERATIONS(Declaration)+    ATTACH_OPERATIONS()+  };++  /////////////////////////////////////+  // Assignments -- variable and value.+  /////////////////////////////////////+  class Assignment : public Statement {+    ADD_PROPERTY(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_PROPERTY(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_Obj, selector)+  public:+    Extension(ParserState pstate, Selector_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 Environment<AST_Node_Obj> Env;+  typedef const char* Signature;+  typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*, std::vector<Selector_List_Obj>);+  class Definition : public Has_Block {+  public:+    enum Type { MIXIN, FUNCTION };+    ADD_PROPERTY(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_PROPERTY(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_Obj, media_block)+  public:+    Content(ParserState pstate) : Statement(pstate)+    { statement_type(CONTENT); }+    Content(const Content* ptr) : Statement(ptr)+    { 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, from_selector)+  public:+    List(ParserState pstate,+         size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false)+    : Value(pstate),+      Vectorized<Expression_Obj>(size),+      separator_(sep),+      is_arglist_(argl),+      from_selector_(false)+    { concrete_type(LIST); }+    List(const List* ptr)+    : Value(ptr),+      Vectorized<Expression_Obj>(*ptr),+      separator_(ptr->separator_),+      is_arglist_(ptr->is_arglist_),+      from_selector_(ptr->from_selector_)+    { concrete_type(LIST); }+    std::string type() { 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(); }+    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());+        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() { return "map"; }+    static std::string type_name() { return "map"; }+    bool is_invisible() const { return empty(); }+    List_Obj to_list(Context& ctx, 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"; break;+      case OR: return "or"; break;+      case EQ: return "eq"; break;+      case NEQ: return "neq"; break;+      case GT: return "gt"; break;+      case GTE: return "gte"; break;+      case LT: return "lt"; break;+      case LTE: return "lte"; break;+      case ADD: return "plus"; break;+      case SUB: return "sub"; break;+      case MUL: return "times"; break;+      case DIV: return "div"; break;+      case MOD: return "mod"; break;+      // this is only used internally!+      case NUM_OPS: return "[OPS]"; break;+      default: return "invalid"; break;+    }+  }++  //////////////////////////////////////////////////////////////////////////+  // Binary expressions. Represents logical, relational, and arithmetic+  // operations. Templatized to avoid large switch statements and repetitive+  // subclassing.+  //////////////////////////////////////////////////////////////////////////+  class Binary_Expression : public PreValue {+  private:+    ADD_HASHED(Operand, op)+    ADD_HASHED(Expression_Obj, left)+    ADD_HASHED(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() {+      switch (type()) {+        case AND: return "and"; break;+        case OR: return "or"; break;+        case EQ: return "eq"; break;+        case NEQ: return "neq"; break;+        case GT: return "gt"; break;+        case GTE: return "gte"; break;+        case LT: return "lt"; break;+        case LTE: return "lte"; break;+        case ADD: return "add"; break;+        case SUB: return "sub"; break;+        case MUL: return "mul"; break;+        case DIV: return "div"; break;+        case MOD: return "mod"; break;+        // this is only used internally!+        case NUM_OPS: return "[OPS]"; break;+        default: return "invalid"; break;+      }+    }+    const std::string separator() {+      switch (type()) {+        case AND: return "&&"; break;+        case OR: return "||"; break;+        case EQ: return "=="; break;+        case NEQ: return "!="; break;+        case GT: return ">"; break;+        case GTE: return ">="; break;+        case LT: return "<"; break;+        case LTE: return "<="; break;+        case ADD: return "+"; break;+        case SUB: return "-"; break;+        case MUL: return "*"; break;+        case DIV: return "/"; break;+        case MOD: return "%"; break;+        // this is only used internally!+        case NUM_OPS: return "[OPS]"; break;+        default: return "invalid"; break;+      }+    }+    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 = dynamic_cast<Binary_Expression_Ptr_Const>(&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>()(type());+        hash_combine(hash_, left()->hash());+        hash_combine(hash_, right()->hash());+      }+      return hash_;+    }+    enum Sass_OP type() 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 };+  private:+    ADD_HASHED(Type, type)+    ADD_HASHED(Expression_Obj, operand)+    size_t hash_;+  public:+    Unary_Expression(ParserState pstate, Type t, Expression_Obj o)+    : Expression(pstate), type_(t), operand_(o), hash_(0)+    { }+    Unary_Expression(const Unary_Expression* ptr)+    : Expression(ptr),+      type_(ptr->type_),+      operand_(ptr->operand_),+      hash_(ptr->hash_)+    { }+    const std::string type_name() {+      switch (type_) {+        case PLUS: return "plus"; break;+        case MINUS: return "minus"; break;+        case NOT: return "not"; break;+        default: return "invalid"; break;+      }+    }+    virtual bool operator==(const Expression& rhs) const+    {+      try+      {+        Unary_Expression_Ptr_Const m = dynamic_cast<Unary_Expression_Ptr_Const>(&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>()(type_);+        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 {+    ADD_HASHED(Expression_Obj, value)+    ADD_HASHED(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_) {+        error("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_) {+        error("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 = dynamic_cast<Argument_Ptr_Const>(&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 calls.+  //////////////////+  class Function_Call : public PreValue {+    ADD_HASHED(std::string, name)+    ADD_HASHED(Arguments_Obj, arguments)+    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), via_call_(false), cookie_(cookie), 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_),+      via_call_(ptr->via_call_),+      cookie_(ptr->cookie_),+      hash_(ptr->hash_)+    { concrete_type(FUNCTION); }++    virtual bool operator==(const Expression& rhs) const+    {+      try+      {+        Function_Call_Ptr_Const m = dynamic_cast<Function_Call_Ptr_Const>(&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_PROPERTY(std::string, name)+  public:+    Variable(ParserState pstate, std::string n)+    : PreValue(pstate), name_(n)+    { }+    Variable(const Variable* ptr)+    : PreValue(ptr), name_(ptr->name_)+    { }++    virtual bool operator==(const Expression& rhs) const+    {+      try+      {+        Variable_Ptr_Const e = dynamic_cast<Variable_Ptr_Const>(&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()+  };++  ////////////////////////////////////////////////////////////////////////////+  // Textual (i.e., unevaluated) numeric data. Variants are distinguished with+  // a type tag.+  ////////////////////////////////////////////////////////////////////////////+  class Textual : public Expression {+  public:+    enum Type { NUMBER, PERCENTAGE, DIMENSION, HEX };+  private:+    ADD_HASHED(Type, type)+    ADD_HASHED(std::string, value)+    size_t hash_;+  public:+    Textual(ParserState pstate, Type t, std::string val)+    : Expression(pstate, DELAYED), type_(t), value_(val),+      hash_(0)+    { }+    Textual(const Textual* ptr)+    : Expression(ptr),+      type_(ptr->type_),+      value_(ptr->value_),+      hash_(ptr->hash_)+    { }++    virtual bool operator==(const Expression& rhs) const+    {+      try+      {+        Textual_Ptr_Const e = dynamic_cast<Textual_Ptr_Const>(&rhs);+        return e && value() == e->value() && type() == e->type();+      }+      catch (std::bad_cast&)+      {+        return false;+      }+      catch (...) { throw; }+    }++    virtual size_t hash()+    {+      if (hash_ == 0) {+        hash_ = std::hash<std::string>()(value_);+        hash_combine(hash_, std::hash<int>()(type_));+      }+      return hash_;+    }++    ATTACH_AST_OPERATIONS(Textual)+    ATTACH_OPERATIONS()+  };++  ////////////////////////////////////////////////+  // Numbers, percentages, dimensions, and colors.+  ////////////////////////////////////////////////+  class Number : public Value {+    ADD_HASHED(double, value)+    ADD_PROPERTY(bool, zero)+    std::vector<std::string> numerator_units_;+    std::vector<std::string> denominator_units_;+    size_t hash_;+  public:+    Number(ParserState pstate, double val, std::string u = "", bool zero = true);++    Number(const Number* ptr)+    : Value(ptr),+      value_(ptr->value_), zero_(ptr->zero_),+      numerator_units_(ptr->numerator_units_),+      denominator_units_(ptr->denominator_units_),+      hash_(ptr->hash_)+    { concrete_type(NUMBER); }++    bool zero() { return zero_; }+    bool is_valid_css_unit() const;+    std::vector<std::string>& numerator_units()   { return numerator_units_; }+    std::vector<std::string>& denominator_units() { return denominator_units_; }+    const std::vector<std::string>& numerator_units() const   { return numerator_units_; }+    const std::vector<std::string>& denominator_units() const { return denominator_units_; }+    std::string type() { return "number"; }+    static std::string type_name() { return "number"; }+    std::string unit() const;++    bool is_unitless() const;+    double convert_factor(const Number&) const;+    bool convert(const std::string& unit = "", bool strict = false);+    void normalize(const std::string& unit = "", bool strict = false);+    // useful for making one number compatible with another+    std::string find_convertible_unit() const;++    virtual size_t hash()+    {+      if (hash_ == 0) {+        hash_ = std::hash<double>()(value_);+        for (const auto numerator : numerator_units())+          hash_combine(hash_, std::hash<std::string>()(numerator));+        for (const auto denominator : denominator_units())+          hash_combine(hash_, std::hash<std::string>()(denominator));+      }+      return hash_;+    }++    virtual bool operator< (const Number& rhs) const;+    virtual bool operator== (const Expression& rhs) const;+    virtual bool eq(const Expression& rhs) const;+    ATTACH_AST_OPERATIONS(Number)+    ATTACH_OPERATIONS()+  };++  //////////+  // Colors.+  //////////+  class Color : public Value {+    ADD_HASHED(double, r)+    ADD_HASHED(double, g)+    ADD_HASHED(double, b)+    ADD_HASHED(double, a)+    ADD_PROPERTY(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() { 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_PROPERTY(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_PROPERTY(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 {+    ADD_HASHED(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() { 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 void ltrim() = 0;+    virtual void trim() = 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, has_interpolants)+    size_t hash_;+  public:+    String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)+    : String(pstate), Vectorized<Expression_Obj>(size), hash_(0)+    { concrete_type(STRING); }+    String_Schema(const String_Schema* ptr)+    : String(ptr),+      Vectorized<Expression_Obj>(*ptr),+      hash_(ptr->hash_)+    { concrete_type(STRING); }++    std::string type() { 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 void ltrim();+    virtual void trim();++    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)+    ADD_HASHED(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)+    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)+    { }+    String_Constant(ParserState pstate, const char* beg)+    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0)+    { }+    String_Constant(ParserState pstate, const char* beg, const char* end)+    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0)+    { }+    String_Constant(ParserState pstate, const Token& tok)+    : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)+    { }+    std::string type() { return "string"; }+    static std::string type_name() { return "string"; }+    virtual bool is_invisible() const;+    virtual void rtrim();+    virtual void ltrim();+    virtual void trim();++    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)+    : String_Constant(pstate, val)+    {+      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 = SASS_MEMORY_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 = SASS_MEMORY_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() { 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_PROPERTY(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)+    {+      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_)+    {+      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_) {+          error("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_) {+          error("functions and mixins cannot have more than one variable-length parameter", p->pstate());+        }+        has_rest_parameter_ = true;+      }+      else {+        if (has_rest_parameter_) {+          error("required parameters must precede variable-length parameters", p->pstate());+        }+        if (has_optional_parameters_) {+          error("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, bool r = false, bool h = false)+    : Expression(pstate),+      // has_reference_(r),+      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() {+      return 0;+    }+    virtual void set_media_block(Media_Block_Ptr mb) {+      media_block(mb);+    }+    virtual bool has_parent_ref() {+      return false;+    }+    virtual bool has_real_parent_ref() {+      return false;+    }+    // dispatch to correct handlers+    virtual bool operator<(const Selector& rhs) const;+    virtual bool operator==(const Selector& rhs) const;+    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 Selector {+    ADD_PROPERTY(String_Obj, contents)+    ADD_PROPERTY(bool, at_root);+  public:+    Selector_Schema(ParserState pstate, String_Obj c)+    : Selector(pstate), contents_(c), at_root_(false)+    { }+    Selector_Schema(const Selector_Schema* ptr)+    : Selector(ptr),+      contents_(ptr->contents_),+      at_root_(ptr->at_root_)+    { }+    virtual bool has_parent_ref();+    virtual bool has_real_parent_ref();+    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_PROPERTY(std::string, ns)+    ADD_PROPERTY(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 bool unique() const+    {+      return false;+    }+    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 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, Context&);+    virtual bool has_parent_ref() { return false; };+    virtual bool has_real_parent_ref() { return false; };+    virtual bool is_pseudo_element() { return false; }+    virtual bool is_pseudo_class() { 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() { return real(); };+    virtual bool has_parent_ref() { return true; };+    virtual bool has_real_parent_ref() { return is_real_parent_ref(); };+    virtual unsigned long specificity()+    {+      return 0;+    }+    std::string type() { 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()+    {+      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()+    {+      if (name() == "*") return 0;+      else               return Constants::Specificity_Element;+    }+    virtual Simple_Selector_Ptr unify_with(Simple_Selector_Ptr, Context&);+    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+    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 bool unique() const+    {+      return false;+    }+    virtual unsigned long specificity()+    {+      return Constants::Specificity_Class;+    }+    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+    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 bool unique() const+    {+      return true;+    }+    virtual unsigned long specificity()+    {+      return Constants::Specificity_ID;+    }+    virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+    ATTACH_AST_OPERATIONS(Id_Selector)+    ATTACH_OPERATIONS()+  };++  ///////////////////////////////////////////////////+  // Attribute selectors -- e.g., [src*=".jpg"], etc.+  ///////////////////////////////////////////////////+  class Attribute_Selector : public Simple_Selector {+    ADD_PROPERTY(std::string, matcher)+    // this cannot be changed to obj atm!!!!!!????!!!!!!!+    ADD_PROPERTY(String_Obj, value) // might be interpolated+  public:+    Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v)+    : Simple_Selector(pstate, n), matcher_(m), value_(v)+    { simple_type(ATTR_SEL); }+    Attribute_Selector(const Attribute_Selector* ptr)+    : Simple_Selector(ptr),+      matcher_(ptr->matcher_),+      value_(ptr->value_)+    { 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()+    {+      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-class always consists of a "colon" (:) followed by the name+    // of the pseudo-class and optionally by a value between parentheses.+    virtual bool is_pseudo_class()+    {+      return (name_[0] == ':' && name_[1] != ':')+             && ! is_pseudo_class_element(name_);+    }++    // 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()+    {+      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()+    {+      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, Context&);+    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_Obj, selector)+  public:+    Wrapped_Selector(ParserState pstate, std::string n, Selector_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()+    {+      if (hash_ == 0) {+        hash_combine(hash_, Simple_Selector::hash());+        if (selector_) hash_combine(hash_, selector_->hash());+      }+      return hash_;+    }+    virtual bool has_parent_ref() {+      // if (has_reference()) return true;+      if (!selector()) return false;+      return selector()->has_parent_ref();+    }+    virtual bool has_real_parent_ref() {+      // if (has_reference()) return true;+      if (!selector()) return false;+      return selector()->has_real_parent_ref();+    }+    virtual unsigned long specificity()+    {+      return selector_ ? selector_->specificity() : 0;+    }+    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()+  };++  struct Complex_Selector_Pointer_Compare {+    bool operator() (const Complex_Selector_Obj& pLeft, const Complex_Selector_Obj& pRight) const;+  };++  ////////////////////////////////////////////////////////////////////////////+  // Simple selector sequences. Maintains flags indicating whether it contains+  // any parent references or placeholders, to simplify expansion.+  ////////////////////////////////////////////////////////////////////////////+  typedef std::set<Complex_Selector_Obj, Complex_Selector_Pointer_Compare> SourcesSet;+  class Compound_Selector : public Selector, public Vectorized<Simple_Selector_Obj> {+  private:+    SourcesSet 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, Context& ctx);+    // virtual Placeholder_Selector_Ptr find_placeholder();+    virtual bool has_parent_ref();+    virtual bool has_real_parent_ref();+    Simple_Selector_Ptr base()+    {+      // Implement non-const in terms of const. Safe to const_cast since this method is non-const+      return const_cast<Simple_Selector_Ptr>(static_cast<Compound_Selector_Ptr_Const>(this)->base());+    }+    Simple_Selector_Ptr_Const base() const {+      if (length() == 0) return 0;+      // ToDo: why is this needed?+      if (SASS_MEMORY_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()+    {+      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 &&+             SASS_MEMORY_CAST(Parent_Selector, (*this)[0]);+    }+    std::vector<Subset_Map_Key> to_str_vec(); // sometimes need to convert to a flat "by-value" data structure++    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); }++    SourcesSet& sources() { return sources_; }+    void clearSources() { sources_.clear(); }+    void mergeSources(SourcesSet& sources, Context& ctx);++    Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs, Context& ctx);+    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:+    ADD_PROPERTY(Combinator, combinator)+    ADD_PROPERTY(Compound_Selector_Obj, head)+    ADD_PROPERTY(Complex_Selector_Obj, tail)+    ADD_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();+    virtual bool has_real_parent_ref();++    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;+    }++    Complex_Selector_Obj context(Context&);+++    Selector_List_Ptr tails(Context& ctx, 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(Context& ctx, std::vector<Selector_List_Obj>& pstack, 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, Context& ctx);+    Combinator clear_innermost();+    void append(Context&, Complex_Selector_Obj);+    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 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); }+    SourcesSet sources()+    {+      //s = Set.new+      //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}+      //s++      SourcesSet srcs;++      Compound_Selector_Obj pHead = head();+      Complex_Selector_Obj  pTail = tail();++      if (pHead) {+        SourcesSet& headSources = pHead->sources();+        srcs.insert(headSources.begin(), headSources.end());+      }++      if (pTail) {+        SourcesSet tailSources = pTail->sources();+        srcs.insert(tailSources.begin(), tailSources.end());+      }++      return srcs;+    }+    void addSources(SourcesSet& sources, Context& ctx) {+      // 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, ctx);+        }++        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()+  };++  typedef std::deque<Complex_Selector_Obj> ComplexSelectorDeque;++  ///////////////////////////////////+  // Comma-separated selector groups.+  ///////////////////////////////////+  class Selector_List : public Selector, public Vectorized<Complex_Selector_Obj> {+    ADD_PROPERTY(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), wspace_(0)+    { }+    Selector_List(const Selector_List* ptr)+    : Selector(ptr),+      Vectorized<Complex_Selector_Obj>(*ptr),+      wspace_(ptr->wspace_)+    { }+    std::string type() { return "list"; }+    // remove parent selector references+    // basically unwraps parsed selectors+    virtual bool has_parent_ref();+    virtual bool has_real_parent_ref();+    void remove_parent_selectors();+    Selector_List_Ptr resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, 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, Context&);+    void populate_extends(Selector_List_Obj, Context&, Subset_Map&);+    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()+    {+      unsigned long sum = 0;+      unsigned long specificity = 0;+      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 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()+  };++  template<typename SelectorType>+  bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) {+    // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector+    // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but+    // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting+    // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the+    // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the+    // members of a SimpleSequence (Compound_Selector)).+    //+    // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for+    // use with Compound_Selector and Complex_Selector objects.+    if (simpleSelectorOrderDependent) {+      return !(one < two) && !(two < one);+    } else {+      return one == two;+    }+  }++  // 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); } };  } 
libsass/src/ast_def_macros.hpp view
@@ -28,13 +28,13 @@  #define ATTACH_OPERATIONS()\ virtual void perform(Operation<void>* op) { (*op)(this); }\-virtual AST_Node* perform(Operation<AST_Node*>* op) { return (*op)(this); }\-virtual Statement* perform(Operation<Statement*>* op) { return (*op)(this); }\-virtual Expression* perform(Operation<Expression*>* op) { return (*op)(this); }\-virtual Selector* perform(Operation<Selector*>* op) { return (*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* perform(Operation<Value*>* op) { return (*op)(this); }+virtual Value_Ptr perform(Operation<Value_Ptr>* op) { return (*op)(this); }  #define ADD_PROPERTY(type, name)\ protected:\
− libsass/src/ast_factory.hpp
@@ -1,93 +0,0 @@-#ifndef SASS_AST_FACTORY_H-#define SASS_AST_FACTORY_H--#include <vector>--#include "ast.hpp"--namespace Sass {--  class AST_Factory {-    std::vector<AST_Node*> nodes;-  public:-    // statements-    Block* new_Block(std::string p, size_t l, size_t s = 0, bool r = false);-    Ruleset* new_Ruleset(std::string p, size_t l, Selector* s, Block* b);-    Propset* new_Propset(std::string p, size_t l, String* pf, Block* b);-    Supports_Query* new_Supports_Query(std::string p, size_t l, Supports_Query* f, Block* b);-    Media_Query* new_Media_Query(std::string p, size_t l, List* q, Block* b);-    At_Root_Block* new_At_Root_Block(std::string p, size_t l, Selector* sel, Block* b);-    At_Rule* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b);-    Keyframe_Rule* new_Keyframe_Rule(std::string p, size_t l, Block* b);-    Declaration* new_Declaration(std::string p, size_t l, String* prop, List* vals);-    Assignment* new_Assignment(std::string p, size_t l, std::string var, Expression* val, bool guarded = false);-    Import<Function_Call*>* new_CSS_Import(std::string p, size_t l, Function_Call* loc);-    Import<String*>* new_SASS_Import(std::string p, size_t l, String* loc);-    Custom_Warning* new_Custom_Warning(std::string msg, size_t l, std::string msg);-    Custom_Error* new_Custom_Error(std::string p, size_t l, std::string msg);-    Warning* new_Warning(std::string p, size_t l, Expression* msg);-    Error* new_Error(std::string p, size_t l, Expression* msg);-    Debug* new_Debug(std::string p, size_t l, Expression* val);-    Comment* new_Comment(std::string p, size_t l, String* txt);-    If* new_If(std::string p, size_t l, Expression* pred, Block* con, Block* alt = 0);-    For* new_For(std::string p, size_t l, std::string var, Expression* lo, Expression* hi, Block* b, bool inc);-    Each* new_Each(std::string p, size_t l, std::vector<std::string> vars, Expression* lst, Block* b);-    While* new_While(std::string p, size_t l, Expression* pred, Block* b);-    Extension* new_Extension(std::string p, size_t l, Selector* s);-    Definition<MIXIN>* new_Mixin_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b);-    Definition<FUNCTION>* new_Function_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b);-    Mixin_Call* new_Mixin_Call(std::string p, size_t l, std::string n, Arguments* args, Block* b = 0);-    // expressions-    List* new_List(std::string p, size_t l, size_t size = 0, enum Sass_Separator sep = List::space, bool argl = false);-    Map* new_Map(std::string p, size_t l, size_t size = 0);-    Binary_Expression<AND>* new_And(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<OR>* new_Or(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<EQ>* new_Eq(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<NEQ>* new_Neq(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<GT>* new_Gt(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<GTE>* new_Gte(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<LT>* new_Lt(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<LTE>* new_Lte(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<ADD>* new_Add(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<SUB>* new_Sub(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<MUL>* new_Mul(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Binary_Expression<DIV>* new_Div(std::string p, size_t l, Expression* lhs, Expression* rhs);-    Negation* new_Negation(std::string p, size_t l, Expression* o);-    Function_Call* new_Function_Call(std::string p, size_t l, String* n, Arguments* args);-    Variable* new_Variable(std::string p, size_t l, std::string n);-    Textual<NUMBER>* new_Textual_Number(std::string p, size_t l, std::string val);-    Textual<PERCENTAGE>* new_Textual_Percentage(std::string p, size_t l, std::string val);-    Textual<DIMENSION>* new_Textual_Dimension(std::string p, size_t l, std::string val);-    Textual<HEX>* new_Textual_Hex(std::string p, size_t l, std::string val);-    Number* new_Number(std::string p, size_t l, double val);-    Percentage* new_Percentage(std::string p, size_t l, double val);-    Dimension* new_Dimension(std::string p, size_t l, double val, std::string unit);-    Color* new_Color(std::string p, size_t l, double r, double g, double b, double a = 1, std::string disp = "");-    Boolean* new_Boolean(std::string p, size_t l, bool val);-    String_Schema* new_String_Schema(std::string p, size_t l, size_t size = 0);-    String_Constant* new_String_Constant(std::string p, size_t l, std::string val);-    String_Constant* new_String_Constant(std::string p, size_t l, const char* beg);-    String_Constant* new_String_Constant(std::string p, size_t l, const char* beg, const char* end);-    Supports_Condition* new_Supports_Condition(std::string p, size_t l, String* f, Expression* v);-    Media_Expression* new_Media_Expression(std::string p, size_t l, String* f, Expression* v);-    Parent_Selector* new_Parent_Selector(std::string p, size_t l, Selector* s);-    // parameters and arguments-    Parameter* new_Parameter(std::string p, size_t l, std::string n, Expression* def = 0, bool rest = false);-    Parameters* new_Parameters(std::string p, size_t l);-    Argument* new_Argument(std::string p, size_t l, Expression* val, std::string n = "", bool rest = false);-    Arguments* new_Arguments(std::string p, size_t l);-    // selectors-    Selector_Schema* new_Selector_Schema(std::string p, size_t l, String* c);-    Attribute_Selector* new_Attribute_Selector(std::string p, size_t l, std::string n, std::string m, String* v);-    Simple_Selector* new_Simple_Selector(std::string p, size_t l, std::string c);-    Reference_Selector* new_Reference_Selector(std::string p, size_t l);-    Placeholder_Selector* new_Placeholder_Selector(std::string p, size_t l, std::string n);-    Pseudo_Selector* new_Pseudo_Selector(std::string p, size_t l, std::string n, Expression* expr = 0);-    Wrapped_Selector* new_Wrapped_Selector(std::string p, size_t l, std::string n, Simple_Base* sel);-    Compound_Selector* new_Compound_Selector(std::string p, size_t l, size_t s = 0);-    Complex_Selector* new_Complex_Selector(std::string p, size_t l, Complex_Selector::Combinator c, Complex_Selector* ctx, Compound_Selector* sel);-    Selector_List* new_Selector_List(std::string p, size_t l, size_t s = 0);-  };-}--#endif
libsass/src/ast_fwd_decl.hpp view
@@ -1,90 +1,389 @@ #ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H +#include <vector>+#include <iostream>+#include <algorithm>+#include <unordered_map>+#include <unordered_set>+#include "memory/SharedPtr.hpp"+ ///////////////////////////////////////////// // Forward declarations for the AST visitors. ///////////////////////////////////////////// namespace Sass {    class AST_Node;-  // statements-  class Statement;+  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 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;-  class Propset;+  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 Supports_Block;-  class At_Rule;+  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;-  class Declaration;+  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 Definition;-  class Mixin_Call;-  // expressions-  class Value;-  class Expression;+  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 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 Textual;+  typedef Textual* Textual_Ptr;+  typedef Textual const* Textual_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 Boolean;-  class String_Schema;+  typedef Boolean* Boolean_Ptr;+  typedef Boolean const* Boolean_Ptr_Const;   class String;+  typedef String* String_Ptr;+  typedef String const* String_Ptr_Const;++  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;-  class At_Root_Expression;+  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;-  // parameters and arguments+  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;-  // selectors+  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;-  class Selector_Placeholder;-  class Type_Selector;-  class Selector_Qualifier;+  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 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; +   // common classes   class Context;+  class Expand;+  class Eval;++  // 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++  IMPL_MEM_OBJ(AST_Node);+  IMPL_MEM_OBJ(Statement);+  IMPL_MEM_OBJ(Block);+  IMPL_MEM_OBJ(Ruleset);+  IMPL_MEM_OBJ(Bubble);+  IMPL_MEM_OBJ(Trace);+  IMPL_MEM_OBJ(Media_Block);+  IMPL_MEM_OBJ(Supports_Block);+  IMPL_MEM_OBJ(Directive);+  IMPL_MEM_OBJ(Keyframe_Rule);+  IMPL_MEM_OBJ(At_Root_Block);+  IMPL_MEM_OBJ(Declaration);+  IMPL_MEM_OBJ(Assignment);+  IMPL_MEM_OBJ(Import);+  IMPL_MEM_OBJ(Import_Stub);+  IMPL_MEM_OBJ(Warning);+  IMPL_MEM_OBJ(Error);+  IMPL_MEM_OBJ(Debug);+  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(Definition);+  IMPL_MEM_OBJ(Mixin_Call);+  IMPL_MEM_OBJ(Value);+  IMPL_MEM_OBJ(Expression);+  IMPL_MEM_OBJ(List);+  IMPL_MEM_OBJ(Map);+  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(Textual);+  IMPL_MEM_OBJ(Number);+  IMPL_MEM_OBJ(Color);+  IMPL_MEM_OBJ(Boolean);+  IMPL_MEM_OBJ(String_Schema);+  IMPL_MEM_OBJ(String);+  IMPL_MEM_OBJ(String_Constant);+  IMPL_MEM_OBJ(String_Quoted);+  IMPL_MEM_OBJ(Media_Query);+  IMPL_MEM_OBJ(Media_Query_Expression);+  IMPL_MEM_OBJ(Supports_Condition);+  IMPL_MEM_OBJ(Supports_Operator);+  IMPL_MEM_OBJ(Supports_Negation);+  IMPL_MEM_OBJ(Supports_Declaration);+  IMPL_MEM_OBJ(Supports_Interpolation);+  IMPL_MEM_OBJ(At_Root_Query);+  IMPL_MEM_OBJ(Null);+  IMPL_MEM_OBJ(Parent_Selector);+  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(Placeholder_Selector);+  IMPL_MEM_OBJ(Element_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);+++  struct HashExpression {+    size_t operator() (Expression_Obj ex) const;+  };+  struct CompareExpression {+    bool operator()(const Expression_Obj& lhs, const Expression_Obj& rhs) const;+  };++  struct HashSimpleSelector {+    size_t operator() (Simple_Selector_Obj ex) const;+  };++  struct CompareSimpleSelector {+    bool operator()(Simple_Selector_Obj lhs, Simple_Selector_Obj rhs) const;+  };++  typedef std::unordered_map<+    Expression_Obj, // key+    Expression_Obj, // value+    HashExpression, // hasher+    CompareExpression // compare+  > ExpressionMap;+  typedef std::unordered_set<+    Expression_Obj, // value+    HashExpression, // hasher+    CompareExpression // compare+  > ExpressionSet;++  typedef std::string Subset_Map_Key;+  typedef std::vector<size_t> Subset_Map_Arr;+  typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> Subset_Map_Val;  } 
libsass/src/base64vlq.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "base64vlq.hpp"  namespace Sass {
libsass/src/bind.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "bind.hpp" #include "ast.hpp" #include "context.hpp"@@ -5,22 +6,20 @@ #include <map> #include <iostream> #include <sstream>-#include "to_string.hpp"  namespace Sass { -  void bind(std::string type, std::string name, Parameters* ps, Arguments* as, Context* ctx, Env* env, Eval* eval)+  void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval)   {     std::string callee(type + " " + name);-    Listize listize(*ctx);-    std::map<std::string, Parameter*> param_map; +    std::map<std::string, Parameter_Obj> param_map;+     for (size_t i = 0, L = as->length(); i < L; ++i) {-      if (auto str = dynamic_cast<String_Quoted*>((*as)[i]->value())) {+      if (auto str = SASS_MEMORY_CAST(String_Quoted, (*as)[i]->value())) {         // force optional quotes (only if needed)         if (str->quote_mark()) {           str->quote_mark('*');-          str->is_delayed(true);         }       }     }@@ -28,7 +27,7 @@     // Set up a map to ensure named arguments refer to actual parameters. Also     // eval each default value left-to-right, wrt env, populating env as we go.     for (size_t i = 0, L = ps->length(); i < L; ++i) {-      Parameter*  p = (*ps)[i];+      Parameter_Obj  p = ps->at(i);       param_map[p->name()] = p;       // if (p->default_value()) {       //   env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env));@@ -39,11 +38,11 @@     size_t ip = 0, LP = ps->length();     size_t ia = 0, LA = as->length();     while (ia < LA) {-      Argument* a = (*as)[ia];+      Argument_Obj a = as->at(ia);       if (ip >= LP) {         // skip empty rest arguments         if (a->is_rest_argument()) {-          if (List* l = dynamic_cast<List*>(a->value())) {+          if (List_Obj l = SASS_MEMORY_CAST(List, a->value())) {             if (l->length() == 0) {               ++ ia; continue;             }@@ -54,43 +53,37 @@         msg << " for `" << name << "'";         return error(msg.str(), as->pstate());       }-      Parameter* p = (*ps)[ip];+      Parameter_Obj p = ps->at(ip);        // If the current parameter is the rest parameter, process and break the loop       if (p->is_rest_parameter()) {         // The next argument by coincidence provides a rest argument         if (a->is_rest_argument()) {+           // We should always get a list for rest arguments-          if (List* rest = dynamic_cast<List*>(a->value())) {-            // arg contains a list-            List* args = rest;-            // make sure it's an arglist-            if (rest->is_arglist()) {-              // can pass it through as it was-              env->local_frame()[p->name()] = args;-            }-            // create a new list and wrap each item as an argument-            // otherwise we will not be able to fetch it again-            else {+          if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) {               // create a new list object for wrapped items-              List* arglist = SASS_MEMORY_NEW(ctx->mem, List,+              List_Ptr arglist = SASS_MEMORY_NEW(List,                                               p->pstate(),                                               0,                                               rest->separator(),                                               true);               // wrap each item from list as an argument-              for (Expression* item : rest->elements()) {-                (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,-                                              item->pstate(),-                                              item,-                                              "",-                                              false,-                                              false);+              for (Expression_Obj item : rest->elements()) {+                if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, item)) {+                  arglist->append(SASS_MEMORY_COPY(arg)); // copy+                } else {+                  arglist->append(SASS_MEMORY_NEW(Argument,+                                                  item->pstate(),+                                                  &item,+                                                  "",+                                                  false,+                                                  false));+                }               }               // assign new arglist to environment               env->local_frame()[p->name()] = arglist;             }-          }           // invalid state           else {             throw std::runtime_error("invalid state");@@ -98,23 +91,23 @@         } else if (a->is_keyword_argument()) {            // expand keyword arguments into their parameters-          List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), 0, SASS_COMMA, true);+          List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true);           env->local_frame()[p->name()] = arglist;-          Map* argmap = static_cast<Map*>(a->value());+          Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value());           for (auto key : argmap->keys()) {-            std::string name = unquote(static_cast<String_Constant*>(key)->value());-            (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,-                                          key->pstate(),-                                          argmap->at(key),-                                          "$" + name,-                                          false,-                                          false);+            std::string name = unquote(SASS_MEMORY_CAST(String_Constant, key)->value());+            arglist->append(SASS_MEMORY_NEW(Argument,+                                            key->pstate(),+                                            argmap->at(key),+                                            "$" + name,+                                            false,+                                            false));           }          } else {            // create a new list object for wrapped items-          List* arglist = SASS_MEMORY_NEW(ctx->mem, List,+          List_Obj arglist = SASS_MEMORY_NEW(List,                                           p->pstate(),                                           0,                                           SASS_COMMA,@@ -124,52 +117,45 @@             // get and post inc             a = (*as)[ia++];             // maybe we have another list as argument-            List* ls = dynamic_cast<List*>(a->value());+            List_Obj ls = SASS_MEMORY_CAST(List, a->value());             // skip any list completely if empty             if (ls && ls->empty() && a->is_rest_argument()) continue;-            // flatten all nested arglists-            if (ls && ls->is_arglist()) {-              for (size_t i = 0, L = ls->size(); i < L; ++i) {-                // already have a wrapped argument-                if (Argument* arg = dynamic_cast<Argument*>((*ls)[i])) {-                  (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);-                }-                // wrap all other value types into Argument-                else {-                  (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,-                                                (*ls)[i]->pstate(),-                                                (*ls)[i],++            Expression_Obj value = a->value();+            if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, value)) {+              arglist->append(&arg);+            }+            // check if we have rest argument+            else if (a->is_rest_argument()) {+              // preserve the list separator from rest args+              if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) {+                arglist->separator(rest->separator());++                for (size_t i = 0, L = rest->size(); i < L; ++i) {+                  Expression_Obj obj = rest->at(i);+                  arglist->append(SASS_MEMORY_NEW(Argument,+                                                obj->pstate(),+                                                &obj,                                                 "",                                                 false,-                                                false);+                                                false));                 }               }-            }-            // already have a wrapped argument-            else if (Argument* arg = dynamic_cast<Argument*>(a->value())) {-              (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);+              // no more arguments+              break;             }             // wrap all other value types into Argument             else {-              (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,+              arglist->append(SASS_MEMORY_NEW(Argument,                                             a->pstate(),                                             a->value(),                                             a->name(),                                             false,-                                            false);-            }-            // check if we have rest argument-            if (a->is_rest_argument()) {-              // preserve the list separator from rest args-              if (List* rest = dynamic_cast<List*>(a->value())) {-                arglist->separator(rest->separator());-              }-              // no more arguments-              break;+                                            false));             }           }           // assign new arglist to environment-          env->local_frame()[p->name()] = arglist;+          env->local_frame()[p->name()] = &arglist;         }         // consumed parameter         ++ip;@@ -180,25 +166,30 @@       // If the current argument is the rest argument, extract a value for processing       else if (a->is_rest_argument()) {         // normal param and rest arg-        List* arglist = static_cast<List*>(a->value());+        List_Obj arglist = SASS_MEMORY_CAST(List, a->value());         // empty rest arg - treat all args as default values         if (!arglist->length()) {           break;         } else {-          if (arglist->length() + ia > LP && !ps->has_rest_parameter()) {-            int arg_count = (arglist->length() + LA - 1);+          if (arglist->length() > LP - ip && !ps->has_rest_parameter()) {+            size_t arg_count = (arglist->length() + LA - 1);             std::stringstream msg;             msg << callee << " takes " << LP;             msg << (LP == 1 ? " argument" : " arguments");             msg << " but " << arg_count;             msg << (arg_count == 1 ? " was passed" : " were passed.");             deprecated_bind(msg.str(), as->pstate());++            while (arglist->length() > LP - ip) {+              arglist->elements().erase(arglist->elements().end() - 1);+            }           }         }         // otherwise move one of the rest args into the param, converting to argument if necessary-        if (!(a = dynamic_cast<Argument*>((*arglist)[0]))) {-          Expression* a_to_convert = (*arglist)[0];-          a = SASS_MEMORY_NEW(ctx->mem, Argument,+        Expression_Obj obj = arglist->at(0);+        if (!(a = SASS_MEMORY_CAST(Argument, obj))) {+          Expression_Ptr a_to_convert = &obj;+          a = SASS_MEMORY_NEW(Argument,                               a_to_convert->pstate(),                               a_to_convert,                               "",@@ -209,18 +200,19 @@         if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) {           ++ia;         }+       } else if (a->is_keyword_argument()) {-        Map* argmap = static_cast<Map*>(a->value());+        Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value());          for (auto key : argmap->keys()) {-          std::string name = "$" + unquote(static_cast<String_Constant*>(key)->value());+          std::string name = "$" + unquote(SASS_MEMORY_CAST(String_Constant, key)->value());            if (!param_map.count(name)) {             std::stringstream msg;             msg << callee << " has no parameter named " << name;             error(msg.str(), a->pstate());           }-          env->local_frame()[name] = argmap->at(key);+          env->local_frame()[name] = &argmap->at(&key);         }         ++ia;         continue;@@ -236,7 +228,7 @@           error(msg.str(), a->pstate());         }         // ordinal arg -- bind it to the next param-        env->local_frame()[p->name()] = a->value();+        env->local_frame()[p->name()] = &a->value();         ++ip;       }       else {@@ -258,7 +250,7 @@               << "provided more than once in call to " << callee;           error(msg.str(), a->pstate());         }-        env->local_frame()[a->name()] = a->value();+        env->local_frame()[a->name()] = &a->value();       }     }     // EO while ia@@ -267,29 +259,25 @@     // That's only okay if they have default values, or were already bound by     // named arguments, or if it's a single rest-param.     for (size_t i = ip; i < LP; ++i) {-      To_String to_string(ctx);-      Parameter* leftover = (*ps)[i];+      Parameter_Obj leftover = ps->at(i);       // cerr << "env for default params:" << endl;       // env->print();       // cerr << "********" << endl;       if (!env->has_local(leftover->name())) {         if (leftover->is_rest_parameter()) {-          env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(ctx->mem, List,+          env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(List,                                                                    leftover->pstate(),                                                                    0,                                                                    SASS_COMMA,                                                                    true);         }         else if (leftover->default_value()) {-          Expression* dv = leftover->default_value()->perform(eval);+          Expression_Ptr dv = leftover->default_value()->perform(eval);           env->local_frame()[leftover->name()] = dv;         }         else {           // param is unbound and has no default value -- error-          std::stringstream msg;-          msg << "required parameter " << leftover->name()-              << " is missing in call to " << callee;-          error(msg.str(), as->pstate());+          throw Exception::MissingArgument(as->pstate(), name, leftover->name(), type);         }       }     }
libsass/src/bind.hpp view
@@ -3,16 +3,12 @@  #include <string> #include "environment.hpp"+#include "ast_fwd_decl.hpp"  namespace Sass {-  class   AST_Node;-  class   Parameters;-  class   Arguments;-  class   Context;-  class   Eval;-  typedef Environment<AST_Node*> Env;+  typedef Environment<AST_Node_Obj> Env; -  void bind(std::string type, std::string name, Parameters*, Arguments*, Context*, Env*, Eval*);+  void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Context*, Env*, Eval*); }  #endif
+ libsass/src/check_nesting.cpp view
@@ -0,0 +1,376 @@+#include "sass.hpp"+#include <vector>++#include "check_nesting.hpp"++namespace Sass {++  CheckNesting::CheckNesting()+  : parents(std::vector<Statement_Ptr>()),+    parent(0),+    current_mixin_definition(0)+  { }++  Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)+  {+    Statement_Ptr old_parent = this->parent;++    if (At_Root_Block_Ptr root = SASS_MEMORY_CAST_PTR(At_Root_Block, parent)) {+      std::vector<Statement_Ptr> old_parents = this->parents;+      std::vector<Statement_Ptr> new_parents;++      for (size_t i = 0, L = this->parents.size(); i < L; i++) {+        Statement_Ptr p = this->parents.at(i);+        if (!root->exclude_node(p)) {+          new_parents.push_back(p);+        }+      }+      this->parents = new_parents;++      for (size_t i = this->parents.size(); i > 0; i--) {+        Statement_Ptr p = 0;+        Statement_Ptr gp = 0;+        if (i > 0) p = this->parents.at(i - 1);+        if (i > 1) gp = this->parents.at(i - 2);++        if (!this->is_transparent_parent(p, gp)) {+          this->parent = p;+          break;+        }+      }++      At_Root_Block_Ptr ar = SASS_MEMORY_CAST_PTR(At_Root_Block, parent);+      Statement_Ptr ret = this->visit_children(&ar->block());++      this->parent = old_parent;+      this->parents = old_parents;++      return ret;+    }++    if (!this->is_transparent_parent(parent, old_parent)) {+      this->parent = parent;+    }++    this->parents.push_back(parent);++    Block_Ptr b = SASS_MEMORY_CAST_PTR(Block, parent);++    if (!b) {+      if (Has_Block_Ptr bb = SASS_MEMORY_CAST(Has_Block, *parent)) {+        b = &bb->block();+      }+    }++    if (b) {+      for (auto n : b->elements()) {+        n->perform(this);+      }+    }+    this->parent = old_parent;+    this->parents.pop_back();++    return b;+  }+++  Statement_Ptr CheckNesting::operator()(Block_Ptr b)+  {+    return this->visit_children(b);+  }++  Statement_Ptr CheckNesting::operator()(Definition_Ptr n)+  {+    if (!this->should_visit(n)) return NULL;+    if (!is_mixin(n)) {+      visit_children(n);+      return n;+    }++    Definition_Ptr old_mixin_definition = this->current_mixin_definition;+    this->current_mixin_definition = n;++    visit_children(n);++    this->current_mixin_definition = old_mixin_definition;++    return n;+  }++  Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)+  {+    Block_Ptr b1 = SASS_MEMORY_CAST_PTR(Block, s);+    Has_Block_Ptr b2 = SASS_MEMORY_CAST_PTR(Has_Block, s);+    return b1 || b2 ? visit_children(s) : s;+  }++  bool CheckNesting::should_visit(Statement_Ptr node)+  {+    if (!this->parent) return true;++    if (SASS_MEMORY_CAST_PTR(Content, node))+    { this->invalid_content_parent(this->parent); }++    if (is_charset(node))+    { this->invalid_charset_parent(this->parent); }++    if (SASS_MEMORY_CAST_PTR(Extension, node))+    { this->invalid_extend_parent(this->parent); }++    // if (SASS_MEMORY_CAST(Import, node))+    // { this->invalid_import_parent(this->parent); }++    if (this->is_mixin(node))+    { this->invalid_mixin_definition_parent(this->parent); }++    if (this->is_function(node))+    { this->invalid_function_parent(this->parent); }++    if (this->is_function(this->parent))+    { this->invalid_function_child(node); }++    if (SASS_MEMORY_CAST_PTR(Declaration, node))+    { this->invalid_prop_parent(this->parent); }++    if (SASS_MEMORY_CAST_PTR(Declaration, this->parent))+    { this->invalid_prop_child(node); }++    if (SASS_MEMORY_CAST_PTR(Return, node))+    { this->invalid_return_parent(this->parent); }++    return true;+  }++  void CheckNesting::invalid_content_parent(Statement_Ptr parent)+  {+    if (!this->current_mixin_definition) {+      throw Exception::InvalidSass(+        parent->pstate(),+        "@content may only be used within a mixin."+      );+    }+  }++  void CheckNesting::invalid_charset_parent(Statement_Ptr parent)+  {+    if (!(+        is_root_node(parent)+    )) {+      throw Exception::InvalidSass(+        parent->pstate(),+        "@charset may only be used at the root of a document."+      );+    }+  }++  void CheckNesting::invalid_extend_parent(Statement_Ptr parent)+  {+    if (!(+        SASS_MEMORY_CAST_PTR(Ruleset, parent) ||+        SASS_MEMORY_CAST_PTR(Mixin_Call, parent) ||+        is_mixin(parent)+    )) {+      throw Exception::InvalidSass(+        parent->pstate(),+        "Extend directives may only be used within rules."+      );+    }+  }++  // void CheckNesting::invalid_import_parent(Statement_Ptr parent)+  // {+  //   for (auto pp : this->parents) {+  //     if (+  //         SASS_MEMORY_CAST(Each, pp) ||+  //         SASS_MEMORY_CAST(For, pp) ||+  //         SASS_MEMORY_CAST(If, pp) ||+  //         SASS_MEMORY_CAST(While, pp) ||+  //         SASS_MEMORY_CAST(Trace, pp) ||+  //         SASS_MEMORY_CAST(Mixin_Call, pp) ||+  //         is_mixin(pp)+  //     ) {+  //       throw Exception::InvalidSass(+  //         parent->pstate(),+  //         "Import directives may not be defined within control directives or other mixins."+  //       );+  //     }+  //   }++  //   if (this->is_root_node(parent)) {+  //     return;+  //   }++  //   if (false/*n.css_import?*/) {+  //     throw Exception::InvalidSass(+  //       parent->pstate(),+  //       "CSS import directives may only be used at the root of a document."+  //     );+  //   }+  // }++  void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent)+  {+    for (Statement_Ptr pp : this->parents) {+      if (+          SASS_MEMORY_CAST_PTR(Each, pp) ||+          SASS_MEMORY_CAST_PTR(For, pp) ||+          SASS_MEMORY_CAST_PTR(If, pp) ||+          SASS_MEMORY_CAST_PTR(While, pp) ||+          SASS_MEMORY_CAST_PTR(Trace, pp) ||+          SASS_MEMORY_CAST_PTR(Mixin_Call, pp) ||+          is_mixin(pp)+      ) {+        throw Exception::InvalidSass(+          parent->pstate(),+          "Mixins may not be defined within control directives or other mixins."+        );+      }+    }+  }++  void CheckNesting::invalid_function_parent(Statement_Ptr parent)+  {+    for (Statement_Ptr pp : this->parents) {+      if (+          SASS_MEMORY_CAST_PTR(Each, pp) ||+          SASS_MEMORY_CAST_PTR(For, pp) ||+          SASS_MEMORY_CAST_PTR(If, pp) ||+          SASS_MEMORY_CAST_PTR(While, pp) ||+          SASS_MEMORY_CAST_PTR(Trace, pp) ||+          SASS_MEMORY_CAST_PTR(Mixin_Call, pp) ||+          is_mixin(pp)+      ) {+        throw Exception::InvalidSass(+          parent->pstate(),+          "Functions may not be defined within control directives or other mixins."+        );+      }+    }+  }++  void CheckNesting::invalid_function_child(Statement_Ptr child)+  {+    if (!(+        SASS_MEMORY_CAST_PTR(Each, child) ||+        SASS_MEMORY_CAST_PTR(For, child) ||+        SASS_MEMORY_CAST_PTR(If, child) ||+        SASS_MEMORY_CAST_PTR(While, child) ||+        SASS_MEMORY_CAST_PTR(Trace, child) ||+        SASS_MEMORY_CAST_PTR(Comment, child) ||+        SASS_MEMORY_CAST_PTR(Debug, child) ||+        SASS_MEMORY_CAST_PTR(Return, child) ||+        SASS_MEMORY_CAST_PTR(Variable, child) ||+        // Ruby Sass doesn't distinguish variables and assignments+        SASS_MEMORY_CAST_PTR(Assignment, child) ||+        SASS_MEMORY_CAST_PTR(Warning, child) ||+        SASS_MEMORY_CAST_PTR(Error, child)+    )) {+      throw Exception::InvalidSass(+        child->pstate(),+        "Functions can only contain variable declarations and control directives."+      );+    }+  }++  void CheckNesting::invalid_prop_child(Statement_Ptr child)+  {+    if (!(+        SASS_MEMORY_CAST_PTR(Each, child) ||+        SASS_MEMORY_CAST_PTR(For, child) ||+        SASS_MEMORY_CAST_PTR(If, child) ||+        SASS_MEMORY_CAST_PTR(While, child) ||+        SASS_MEMORY_CAST_PTR(Trace, child) ||+        SASS_MEMORY_CAST_PTR(Comment, child) ||+        SASS_MEMORY_CAST_PTR(Declaration, child) ||+        SASS_MEMORY_CAST_PTR(Mixin_Call, child)+    )) {+      throw Exception::InvalidSass(+        child->pstate(),+        "Illegal nesting: Only properties may be nested beneath properties."+      );+    }+  }++  void CheckNesting::invalid_prop_parent(Statement_Ptr parent)+  {+    if (!(+        is_mixin(parent) ||+        is_directive_node(parent) ||+        SASS_MEMORY_CAST_PTR(Ruleset, parent) ||+        SASS_MEMORY_CAST_PTR(Keyframe_Rule, parent) ||+        SASS_MEMORY_CAST_PTR(Declaration, parent) ||+        SASS_MEMORY_CAST_PTR(Mixin_Call, parent)+    )) {+      throw Exception::InvalidSass(+        parent->pstate(),+        "Properties are only allowed within rules, directives, mixin includes, or other properties."+      );+    }+  }++  void CheckNesting::invalid_return_parent(Statement_Ptr parent)+  {+    if (!this->is_function(parent)) {+      throw Exception::InvalidSass(+        parent->pstate(),+        "@return may only be used within a function."+      );+    }+  }++  bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)+  {+    bool parent_bubbles = parent && parent->bubbles();++    bool valid_bubble_node = parent_bubbles &&+                             !is_root_node(grandparent) &&+                             !is_at_root_node(grandparent);++    return SASS_MEMORY_CAST_PTR(Import, parent) ||+           SASS_MEMORY_CAST_PTR(Each, parent) ||+           SASS_MEMORY_CAST_PTR(For, parent) ||+           SASS_MEMORY_CAST_PTR(If, parent) ||+           SASS_MEMORY_CAST_PTR(While, parent) ||+           SASS_MEMORY_CAST_PTR(Trace, parent) ||+           valid_bubble_node;+  }++  bool CheckNesting::is_charset(Statement_Ptr n)+  {+    Directive_Ptr d = SASS_MEMORY_CAST_PTR(Directive, n);+    return d && d->keyword() == "charset";+  }++  bool CheckNesting::is_mixin(Statement_Ptr n)+  {+    Definition_Ptr def = SASS_MEMORY_CAST_PTR(Definition, n);+    return def && def->type() == Definition::MIXIN;+  }++  bool CheckNesting::is_function(Statement_Ptr n)+  {+    Definition_Ptr def = SASS_MEMORY_CAST_PTR(Definition, n);+    return def && def->type() == Definition::FUNCTION;+  }++  bool CheckNesting::is_root_node(Statement_Ptr n)+  {+    if (SASS_MEMORY_CAST_PTR(Ruleset, n)) return false;++    Block_Ptr b = SASS_MEMORY_CAST_PTR(Block, n);+    return b && b->is_root();+  }++  bool CheckNesting::is_at_root_node(Statement_Ptr n)+  {+    return SASS_MEMORY_CAST_PTR(At_Root_Block, n) != NULL;+  }++  bool CheckNesting::is_directive_node(Statement_Ptr n)+  {+    return SASS_MEMORY_CAST_PTR(Directive, n) ||+           SASS_MEMORY_CAST_PTR(Import, n) ||+           SASS_MEMORY_CAST_PTR(Media_Block, n) ||+           SASS_MEMORY_CAST_PTR(Supports_Block, n);+  }+}
+ libsass/src/check_nesting.hpp view
@@ -0,0 +1,64 @@+#ifndef SASS_CHECK_NESTING_H+#define SASS_CHECK_NESTING_H++#include "ast.hpp"+#include "operation.hpp"++namespace Sass {++  typedef Environment<AST_Node_Obj> Env;++  class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> {++    std::vector<Statement_Ptr>  parents;+    Statement_Ptr               parent;+    Definition_Ptr              current_mixin_definition;++    Statement_Ptr fallback_impl(Statement_Ptr);+    Statement_Ptr before(Statement_Ptr);+    Statement_Ptr visit_children(Statement_Ptr);++  public:+    CheckNesting();+    ~CheckNesting() { }++    Statement_Ptr operator()(Block_Ptr);+    Statement_Ptr operator()(Definition_Ptr);++    template <typename U>+    Statement_Ptr fallback(U x) {+      Statement_Ptr n = SASS_MEMORY_CAST_PTR(Statement, x);+      if (this->should_visit(n)) {+        return fallback_impl(n);+      }+      return NULL;+    }++  private:+    void invalid_content_parent(Statement_Ptr);+    void invalid_charset_parent(Statement_Ptr);+    void invalid_extend_parent(Statement_Ptr);+    // void invalid_import_parent(Statement_Ptr);+    void invalid_mixin_definition_parent(Statement_Ptr);+    void invalid_function_parent(Statement_Ptr);++    void invalid_function_child(Statement_Ptr);+    void invalid_prop_child(Statement_Ptr);+    void invalid_prop_parent(Statement_Ptr);+    void invalid_return_parent(Statement_Ptr);++    bool is_transparent_parent(Statement_Ptr, Statement_Ptr);++    bool should_visit(Statement_Ptr);++    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);+  };++}++#endif
libsass/src/color_maps.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "ast.hpp" #include "color_maps.hpp" @@ -451,7 +452,7 @@     { 102 * 0x10000 +  51 * 0x100 + 153, ColorNames::rebeccapurple }   }; -  const std::map<const char*, const Color*, map_cmp_str> names_to_colors+  const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors   {     { ColorNames::aliceblue, &Colors::aliceblue },     { ColorNames::antiquewhite, &Colors::antiquewhite },@@ -604,7 +605,7 @@     { ColorNames::transparent, &Colors::transparent }   }; -  const Color* name_to_color(const char* key)+  Color_Ptr_Const name_to_color(const char* key)   {     auto p = names_to_colors.find(key);     if (p != names_to_colors.end()) {@@ -613,7 +614,7 @@     return 0;   } -  const Color* name_to_color(const std::string& key)+  Color_Ptr_Const name_to_color(const std::string& key)   {     return name_to_color(key.c_str());   }
libsass/src/color_maps.hpp view
@@ -320,10 +320,10 @@   }    extern const std::map<const int, const char*> colors_to_names;-  extern const std::map<const char*, const Color*, map_cmp_str> names_to_colors;+  extern const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors; -  extern const Color* name_to_color(const char*);-  extern const Color* name_to_color(const std::string&);+  extern Color_Ptr_Const name_to_color(const char*);+  extern Color_Ptr_Const name_to_color(const std::string&);   extern const char* color_to_name(const int);   extern const char* color_to_name(const Color&);   extern const char* color_to_name(const double);
libsass/src/constants.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "constants.hpp"  namespace Sass {@@ -9,12 +10,13 @@     // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity     // https://github.com/sass/sass/issues/1495#issuecomment-61189114     extern const unsigned long Specificity_Star = 0;-    extern const unsigned long Specificity_Universal = 1 << 0;-    extern const unsigned long Specificity_Type = 1 << 8;-    extern const unsigned long Specificity_Class = 1 << 16;-    extern const unsigned long Specificity_Attr = 1 << 16;-    extern const unsigned long Specificity_Pseudo = 1 << 16;-    extern const unsigned long Specificity_ID = 1 << 24;+    extern const unsigned long Specificity_Universal = 0;+    extern const unsigned long Specificity_Element = 1;+    extern const unsigned long Specificity_Base = 1000;+    extern const unsigned long Specificity_Class = 1000;+    extern const unsigned long Specificity_Attr = 1000;+    extern const unsigned long Specificity_Pseudo = 1000;+    extern const unsigned long Specificity_ID = 1000000;      // sass keywords     extern const char at_root_kwd[]       = "@at-root";@@ -78,7 +80,7 @@     extern const char keyframes_kwd[]    = "keyframes";     extern const char only_kwd[]         = "only";     extern const char rgb_kwd[]          = "rgb(";-    extern const char url_kwd[]          = "url(";+    extern const char url_kwd[]          = "url";     // extern const char url_prefix_kwd[]   = "url-prefix(";     extern const char important_kwd[]    = "important";     extern const char pseudo_not_kwd[]   = ":not(";@@ -86,11 +88,10 @@     extern const char odd_kwd[]          = "odd";     extern const char progid_kwd[]       = "progid";     extern const char expression_kwd[]   = "expression";-    extern const char calc_kwd[]         = "calc(";-    extern const char moz_calc_kwd[]     = "-moz-calc(";-    extern const char webkit_calc_kwd[]  = "-webkit-calc(";-    extern const char ms_calc_kwd[]      = "-ms-calc(";+    extern const char calc_fn_kwd[]      = "calc"; +    extern const char almost_any_value_class[] = "\"'#!;{}";+     // css selector keywords     extern const char sel_deep_kwd[] = "/deep/"; @@ -124,6 +125,7 @@     extern const char rbrace[]          = "}";     extern const char rparen[]          = ")";     extern const char sign_chars[]      = "-+";+    extern const char op_chars[]        = "-+";     extern const char hyphen[]          = "-";     extern const char ellipsis[]        = "...";     // extern const char url_space_chars[] = " \t\r\n\f";
libsass/src/constants.hpp view
@@ -11,7 +11,8 @@     // The following list of selectors is by increasing specificity:     extern const unsigned long Specificity_Star;     extern const unsigned long Specificity_Universal;-    extern const unsigned long Specificity_Type;+    extern const unsigned long Specificity_Element;+    extern const unsigned long Specificity_Base;     extern const unsigned long Specificity_Class;     extern const unsigned long Specificity_Attr;     extern const unsigned long Specificity_Pseudo;@@ -81,18 +82,17 @@     extern const char rgb_kwd[];     extern const char url_kwd[];     // extern const char url_prefix_kwd[];-    extern const char image_url_kwd[];     extern const char important_kwd[];     extern const char pseudo_not_kwd[];     extern const char even_kwd[];     extern const char odd_kwd[];     extern const char progid_kwd[];     extern const char expression_kwd[];-    extern const char calc_kwd[];-    extern const char moz_calc_kwd[];-    extern const char webkit_calc_kwd[];-    extern const char ms_calc_kwd[];+    extern const char calc_fn_kwd[]; +    // char classes for "regular expressions"+    extern const char almost_any_value_class[];+     // css selector keywords     extern const char sel_deep_kwd[]; @@ -126,6 +126,7 @@     extern const char rbrace[];     extern const char rparen[];     extern const char sign_chars[];+    extern const char op_chars[];     extern const char hyphen[];     extern const char ellipsis[];     // extern const char url_space_chars[];
libsass/src/context.cpp view
@@ -1,9 +1,4 @@-#ifdef _WIN32-#define PATH_SEP ';'-#else-#define PATH_SEP ':'-#endif-+#include "sass.hpp" #include <string> #include <cstdlib> #include <cstring>@@ -23,11 +18,13 @@ #include "output.hpp" #include "expand.hpp" #include "eval.hpp"+#include "check_nesting.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"@@ -62,13 +59,13 @@     return safe_path == "" ? "stdout" : safe_path;   } -  Context::Context(struct Sass_Context* c_ctx)+  Context::Context(struct Sass_Context& c_ctx)   : CWD(File::get_cwd()),+    c_options(c_ctx),     entry_path(""),     head_imports(0),-    mem(Memory_Manager()),     plugins(),-    emitter(this),+    emitter(c_options),      strings(),     resources(),@@ -76,19 +73,17 @@     subset_map(),     import_stack(), -    c_options               (c_ctx),-     c_headers               (std::vector<Sass_Importer_Entry>()),     c_importers             (std::vector<Sass_Importer_Entry>()),     c_functions             (std::vector<Sass_Function_Entry>()), -    indent                  (safe_str(c_options->indent, "  ")),-    linefeed                (safe_str(c_options->linefeed, "\n")),+    indent                  (safe_str(c_options.indent, "  ")),+    linefeed                (safe_str(c_options.linefeed, "\n")), -    input_path              (make_canonical_path(safe_input(c_options->input_path))),-    output_path             (make_canonical_path(safe_output(c_options->output_path, input_path))),-    source_map_file         (make_canonical_path(safe_str(c_options->source_map_file, ""))),-    source_map_root         (make_canonical_path(safe_str(c_options->source_map_root, "")))+    input_path              (make_canonical_path(safe_input(c_options.input_path))),+    output_path             (make_canonical_path(safe_output(c_options.output_path, input_path))),+    source_map_file         (make_canonical_path(safe_str(c_options.source_map_file, ""))),+    source_map_root         (make_canonical_path(safe_str(c_options.source_map_root, "")))    { @@ -96,10 +91,10 @@     include_paths.push_back(CWD);      // collect more paths from different options-    collect_include_paths(sass_option_get_include_path(c_options));-    // collect_include_paths(initializers.include_paths_array());-    collect_plugin_paths(sass_option_get_plugin_path(c_options));-    // collect_plugin_paths(initializers.plugin_paths_array());+    collect_include_paths(c_options.include_path);+    collect_include_paths(c_options.include_paths);+    collect_plugin_paths(c_options.plugin_path);+    collect_plugin_paths(c_options.plugin_paths);      // load plugins and register custom behaviors     for(auto plug : plugin_paths) plugins.load_plugins(plug);@@ -150,6 +145,7 @@     }     // clear inner structures (vectors) and input source     resources.clear(); import_stack.clear();+    subset_map.clear(), sheets.clear();   }    Data_Context::~Data_Context()@@ -167,7 +163,6 @@    void Context::collect_include_paths(const char* paths_str)   {-     if (paths_str) {       const char* beg = paths_str;       const char* end = Prelexer::find_first<PATH_SEP>(beg);@@ -190,17 +185,17 @@     }   } -  void Context::collect_include_paths(const char** paths_array)+  void Context::collect_include_paths(string_list* paths_array)   {-    if (!paths_array) return;-    for (size_t i = 0; paths_array[i]; i++) {-      collect_include_paths(paths_array[i]);+    while (paths_array)+    {+      collect_include_paths(paths_array->string);+      paths_array = paths_array->next;     }   }    void Context::collect_plugin_paths(const char* paths_str)   {-     if (paths_str) {       const char* beg = paths_str;       const char* end = Prelexer::find_first<PATH_SEP>(beg);@@ -223,15 +218,15 @@     }   } -  void Context::collect_plugin_paths(const char** paths_array)+  void Context::collect_plugin_paths(string_list* paths_array)   {-    if (!paths_array) return;-    for (size_t i = 0; paths_array[i]; i++) {-      collect_plugin_paths(paths_array[i]);+    while (paths_array)+    {+      collect_plugin_paths(paths_array->string);+      paths_array = paths_array->next;     }   } -   // resolve the imp_path in base_path or include_paths   // looks for alternatives and returns a list from one directory   std::vector<Include> Context::find_includes(const Importer& import)@@ -254,7 +249,7 @@    // register include with resolved path and its content   // memory of the resources will be freed by us on exit-  void Context::register_resource(const Include& inc, const Resource& res)+  void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate)   {      // do not parse same resource twice@@ -294,22 +289,40 @@     const char* contents = resources[idx].contents;     // keep a copy of the path around (for parserstates)     // ToDo: we clean it, but still not very elegant!?-    strings.push_back(sass_strdup(inc.abs_path.c_str()));+    strings.push_back(sass_copy_c_string(inc.abs_path.c_str()));     // create the initial parser state from resource     ParserState pstate(strings.back(), contents, idx);++    // check existing import stack for possible recursion+    for (size_t i = 0; i < import_stack.size() - 2; ++i) {+      auto parent = import_stack[i];+      if (std::strcmp(parent->abs_path, import->abs_path) == 0) {+        std::string stack("An @import loop has been found:");+        for (size_t n = 1; n < i + 2; ++n) {+          stack += "\n    " + std::string(import_stack[n]->imp_path) ++            " imports " + std::string(import_stack[n+1]->imp_path);+        }+        // implement error throw directly until we+        // decided how to handle full stack traces+        ParserState state = prstate ? *prstate : pstate;+        throw Exception::InvalidSyntax(state, stack, &import_stack);+        // error(stack, prstate ? *prstate : pstate, import_stack);+      }+    }+     // create a parser instance from the given c_str buffer     Parser p(Parser::from_c_str(contents, *this, pstate));     // do not yet dispose these buffers     sass_import_take_source(import);     sass_import_take_srcmap(import);     // then parse the root block-    Block* root = p.parse();+    Block_Obj root = p.parse();     // delete memory of current stack frame     sass_delete_import(import_stack.back());     // remove current stack frame     import_stack.pop_back();     // create key/value pair for ast node-    std::pair<const std::string, const StyleSheet>+    std::pair<const std::string, StyleSheet>       ast_pair(inc.abs_path, { res, root });     // register resulting resource     sheets.insert(ast_pair);@@ -338,13 +351,14 @@      // process the resolved entry     else if (resolved.size() == 1) {+      bool use_cache = c_importers.size() == 0;       // use cache for the resource loading-      if (sheets.count(resolved[0].abs_path)) return resolved[0];+      if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0];       // try to read the content of the resolved file entry       // the memory buffer returned must be freed by us!       if (char* contents = read_file(resolved[0].abs_path)) {         // register the newly resolved file resource-        register_resource(resolved[0], { contents, 0 });+        register_resource(resolved[0], { contents, 0 }, &pstate);         // return resolved entry         return resolved[0];       }@@ -355,7 +369,7 @@    } -  void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) {+  void Context::import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path) {      ParserState pstate(imp->pstate());     std::string imp_path(unquote(load_path));@@ -365,30 +379,27 @@     if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {        protocol = std::string(imp_path.c_str(), proto - 3);-      // std::cerr << "==================== " << protocol << "\n";-      if (protocol.compare("file") && true) {--      }+      // if (protocol.compare("file") && true) { }     }      // add urls (protocol other than file) and urls without procotol to `urls` member     // ToDo: if ctx_path is already a file resource, we should not add it here?-    if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {-      imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path));+    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* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path));-      Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc);-      Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate);-      (*loc_args) << loc_arg;-      Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args);+      String_Constant_Ptr 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);       imp->urls().push_back(new_url);     }     else {       const Importer importer(imp_path, ctx_path);       Include include(load_import(importer, pstate));       if (include.abs_path.empty()) {-        error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate);+        error("File to import not found or unreadable: " + imp_path + ".\nParent style sheet: " + ctx_path, pstate);       }       imp->incs().push_back(include);     }@@ -397,7 +408,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* 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_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one)   {     // unique counter     size_t count = 0;@@ -426,14 +437,14 @@           // query data from the current include           Sass_Import_Entry include = *it_includes;           char* source = sass_import_take_source(include);-          char* srcmap = sass_import_take_source(include);+          char* srcmap = sass_import_take_srcmap(include);           size_t line = sass_import_get_error_line(include);           size_t column = sass_import_get_error_column(include);           const char *abs_path = sass_import_get_abs_path(include);           // handle error message passed back from custom importer           // it may (or may not) override the line and column info           if (const char* err_message = sass_import_get_error_message(include)) {-            if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });+            if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate);             if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);             else error(err_message, ParserState(ctx_path, source, Position(line, column)));           }@@ -447,7 +458,7 @@             // attach information to AST node             imp->incs().push_back(include);             // register the resource buffers-            register_resource(include, { source, srcmap });+            register_resource(include, { source, srcmap }, &pstate);           }           // only a path was retuned           // try to load it like normal@@ -483,7 +494,7 @@   void register_c_functions(Context&, Env* env, Sass_Function_List);   void register_c_function(Context&, Env* env, Sass_Function_Entry); -  char* Context::render(Block* root)+  char* Context::render(Block_Obj root)   {     // check for valid block     if (!root) return 0;@@ -494,9 +505,9 @@     // get the resulting buffer from stream     OutputBuffer emitted = emitter.get_buffer();     // should we append a source map url?-    if (!c_options->omit_source_map_url) {+    if (!c_options.omit_source_map_url) {       // generate an embeded source map-      if (c_options->source_map_embed) {+      if (c_options.source_map_embed) {         emitted.buffer += linefeed;         emitted.buffer += format_embedded_source_map();       }@@ -508,27 +519,27 @@     }     // create a copy of the resulting buffer string     // this must be freed or taken over by implementor-    return sass_strdup(emitted.buffer.c_str());+    return sass_copy_c_string(emitted.buffer.c_str());   } -  void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate)+  void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, ParserState pstate)   {     // create a custom import to resolve headers-    Import* imp = SASS_MEMORY_NEW(mem, Import, pstate);+    Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);     // dispatch headers which will add custom functions     // custom headers are added to the import instance-    call_headers(entry_path, ctx_path, pstate, imp);+    call_headers(entry_path, ctx_path, pstate, &imp);     // increase head count to skip later     head_imports += resources.size() - 1;     // add the statement if we have urls-    if (!imp->urls().empty()) (*root) << imp;+    if (!imp->urls().empty()) root->append(&imp);     // process all other resources (add Import_Stub nodes)     for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {-      (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]);+      root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));     }   } -  Block* File_Context::parse()+  Block_Obj File_Context::parse()   {      // check if entry file is given@@ -551,7 +562,7 @@     }      // abort early if no content could be loaded (various reasons)-    if (!contents) throw "File to read not found or unreadable: " + input_path;+    if (!contents) throw std::runtime_error("File to read not found or unreadable: " + input_path);      // store entry path     entry_path = abs_path;@@ -574,14 +585,14 @@    } -  Block* Data_Context::parse()+  Block_Obj Data_Context::parse()   {      // check if source string is given     if (!source_c_str) return 0;      // convert indented sass syntax-    if(c_options->is_indented_syntax_src) {+    if(c_options.is_indented_syntax_src) {       // call sass2scss to convert the string       char * converted = sass2scss(source_c_str,         // preserve the structure as much as possible@@ -595,7 +606,7 @@      // ToDo: this may be resolved via custom importers     std::string abs_path(rel2abs(entry_path));-    char* abs_path_c_str = sass_strdup(abs_path.c_str());+    char* abs_path_c_str = sass_copy_c_string(abs_path.c_str());     strings.push_back(abs_path_c_str);      // create entry only for the import stack@@ -618,15 +629,14 @@     // parse root block from includes-  Block* Context::compile()+  Block_Obj Context::compile()   {     // abort if there is no data     if (resources.size() == 0) return 0;     // get root block from the first style sheet-    Block* root = sheets.at(entry_path).root;+    Block_Obj root = sheets.at(entry_path).root;     // abort on invalid root-    if (root == 0) return 0;-+    if (root.isNull()) return 0;     Env global; // create root environment     // register built-in functions on env     register_built_in_functions(*this, &global);@@ -638,16 +648,21 @@     // create crtp visitor objects     Expand expand(*this, &global, &backtrace);     Cssize cssize(*this, &backtrace);+    CheckNesting check_nesting;+    // check nesting+    check_nesting(&root);     // expand and eval the tree-    root = root->perform(&expand)->block();+    root = expand(&root);+    // check nesting+    check_nesting(&root);     // merge and bubble certain rules-    root = root->perform(&cssize)->block();+    root = cssize(&root);     // should we extend something?     if (!subset_map.empty()) {       // create crtp visitor object       Extend extend(*this, subset_map);       // extend tree nodes-      root->perform(&extend);+      extend(&root);     }      // clean up by removing empty placeholders@@ -682,7 +697,7 @@     if (source_map_file == "") return 0;     char* result = 0;     std::string map = emitter.render_srcmap(*this);-    result = sass_strdup(map.c_str());+    result = sass_copy_c_string(map.c_str());     return result;   } @@ -703,14 +718,14 @@    void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)   {-    Definition* def = make_native_function(sig, f, ctx);+    Definition_Ptr 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* def = make_native_function(sig, f, ctx);+    Definition_Ptr def = make_native_function(sig, f, ctx);     std::stringstream ss;     ss << def->name() << "[f]" << arity;     def->environment(env);@@ -719,7 +734,7 @@    void register_overload_stub(Context& ctx, std::string name, Env* env)   {-    Definition* stub = SASS_MEMORY_NEW(ctx.mem, Definition,+    Definition_Ptr stub = SASS_MEMORY_NEW(Definition,                                        ParserState("[built-in function]"),                                        0,                                        name,@@ -840,7 +855,7 @@   }   void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)   {-    Definition* def = make_c_function(descr, ctx);+    Definition_Ptr def = make_c_function(descr, ctx);     def->environment(env);     (*env)[def->name() + "[f]"] = def;   }
libsass/src/context.hpp view
@@ -10,7 +10,6 @@  #include "ast_fwd_decl.hpp" #include "kwd_arg_macros.hpp"-#include "memory_manager.hpp" #include "ast_fwd_decl.hpp" #include "sass_context.hpp" #include "environment.hpp"@@ -27,20 +26,20 @@    class Context {   public:-    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)+    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)     { 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* imp)+    bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr 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* 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_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);    public:     const std::string CWD;+    struct Sass_Options& c_options;     std::string entry_path;     size_t head_imports;-    Memory_Manager mem;     Plugins plugins;     Output emitter; @@ -48,12 +47,11 @@     // these are guaranteed to be freed     std::vector<char*> strings;     std::vector<Resource> resources;-    std::map<const std::string, const StyleSheet> sheets;-    Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > subset_map;+    std::map<const std::string, StyleSheet> sheets;+    Subset_Map subset_map;     std::vector<Sass_Import_Entry> import_stack;      struct Sass_Compiler* c_compiler;-    struct Sass_Options* c_options;      // absolute paths to includes     std::vector<std::string> included_files;@@ -68,7 +66,7 @@   -    void apply_custom_headers(Block* root, const char* path, ParserState pstate);+    void apply_custom_headers(Block_Obj root, const char* path, ParserState pstate);      std::vector<Sass_Importer_Entry> c_headers;     std::vector<Sass_Importer_Entry> c_importers;@@ -86,24 +84,24 @@     const std::string source_map_root; // path for sourceRoot property (pass-through)      virtual ~Context();-    Context(struct Sass_Context*);-    virtual Block* parse() = 0;-    virtual Block* compile();-    virtual char* render(Block* root);+    Context(struct Sass_Context&);+    virtual Block_Obj parse() = 0;+    virtual Block_Obj compile();+    virtual char* render(Block_Obj root);     virtual char* render_srcmap(); -    void register_resource(const Include&, const Resource&);+    void register_resource(const Include&, const Resource&, ParserState* = 0);     std::vector<Include> find_includes(const Importer& import);     Include load_import(const Importer&, ParserState pstate); -    Sass_Output_Style output_style() { return c_options->output_style; };+    Sass_Output_Style output_style() { return c_options.output_style; };     std::vector<std::string> get_included_files(bool skip = false, size_t headers = 0);    private:     void collect_plugin_paths(const char* paths_str);-    void collect_plugin_paths(const char** paths_array);+    void collect_plugin_paths(string_list* paths_array);     void collect_include_paths(const char* paths_str);-    void collect_include_paths(const char** paths_array);+    void collect_include_paths(string_list* paths_array);     std::string format_embedded_source_map();     std::string format_source_mapping_url(const std::string& out_path); @@ -119,27 +117,27 @@    class File_Context : public Context {   public:-    File_Context(struct Sass_File_Context* ctx)+    File_Context(struct Sass_File_Context& ctx)     : Context(ctx)     { }     virtual ~File_Context();-    virtual Block* parse();+    virtual Block_Obj parse();   };    class Data_Context : public Context {   public:     char* source_c_str;     char* srcmap_c_str;-    Data_Context(struct Sass_Data_Context* ctx)+    Data_Context(struct Sass_Data_Context& ctx)     : Context(ctx)     {-      source_c_str       = ctx->source_string;-      srcmap_c_str       = ctx->srcmap_string;-      ctx->source_string = 0; // passed away-      ctx->srcmap_string = 0; // passed away+      source_c_str       = ctx.source_string;+      srcmap_c_str       = ctx.srcmap_string;+      ctx.source_string = 0; // passed away+      ctx.srcmap_string = 0; // passed away     }     virtual ~Data_Context();-    virtual Block* parse();+    virtual Block_Obj parse();   };  }
libsass/src/cssize.cpp view
@@ -1,9 +1,9 @@+#include "sass.hpp" #include <iostream> #include <typeinfo> #include <vector>  #include "cssize.hpp"-#include "to_string.hpp" #include "context.hpp" #include "backtrace.hpp" @@ -11,157 +11,224 @@    Cssize::Cssize(Context& ctx, Backtrace* bt)   : ctx(ctx),-    block_stack(std::vector<Block*>()),-    p_stack(std::vector<Statement*>()),+    block_stack(std::vector<Block_Ptr>()),+    p_stack(std::vector<Statement_Ptr>()),     backtrace(bt)-  {  }+  { } -  Statement* Cssize::parent()+  Statement_Ptr Cssize::parent()   {     return p_stack.size() ? p_stack.back() : block_stack.front();   } -  Statement* Cssize::operator()(Block* b)+  Block_Ptr Cssize::operator()(Block_Ptr b)   {-    Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, b->pstate(), b->length(), b->is_root());+    Block_Ptr bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root());     // bb->tabs(b->tabs());     block_stack.push_back(bb);-    append_block(b);+    append_block(b, bb);     block_stack.pop_back();     return bb;   } -  Statement* Cssize::operator()(At_Rule* r)+  Statement_Ptr Cssize::operator()(Trace_Ptr t)   {+    return t->block()->perform(this);+  }++  Statement_Ptr Cssize::operator()(Declaration_Ptr d)+  {+    String_Obj property = SASS_MEMORY_CAST(String, d->property());++    if (Declaration_Ptr dd = dynamic_cast<Declaration_Ptr>(parent())) {+      String_Obj parent_property = SASS_MEMORY_CAST(String, dd->property());+      property = SASS_MEMORY_NEW(String_Constant,+                                 d->property()->pstate(),+                                 parent_property->to_string() + "-" + property->to_string());+      if (!dd->value()) {+        d->tabs(dd->tabs() + 1);+      }+    }++    Declaration_Obj dd = SASS_MEMORY_NEW(Declaration,+                                      d->pstate(),+                                      property,+                                      d->value(),+                                      d->is_important());+    dd->is_indented(d->is_indented());+    dd->tabs(d->tabs());++    p_stack.push_back(&dd);+    Block_Obj bb = d->block() ? operator()(&d->block()) : NULL;+    p_stack.pop_back();++    if (bb && bb->length()) {+      if (dd->value() && !dd->value()->is_invisible()) {+        bb->unshift(&dd);+      }+      return bb.detach();+    }+    else if (dd->value() && !dd->value()->is_invisible()) {+      return dd.detach();+    }++    return 0;+  }++  Statement_Ptr Cssize::operator()(Directive_Ptr r)+  {     if (!r->block() || !r->block()->length()) return r;      if (parent()->statement_type() == Statement::RULESET)     {-      return (r->is_keyframes()) ? SASS_MEMORY_NEW(ctx.mem, Bubble, r->pstate(), r) : bubble(r);+      return (r->is_keyframes()) ? SASS_MEMORY_NEW(Bubble, r->pstate(), r) : bubble(r);     }      p_stack.push_back(r);-    At_Rule* rr = SASS_MEMORY_NEW(ctx.mem, At_Rule,+    Directive_Obj rr = SASS_MEMORY_NEW(Directive,                                   r->pstate(),                                   r->keyword(),                                   r->selector(),-                                  r->block() ? r->block()->perform(this)->block() : 0);+                                  r->block() ? operator()(&r->block()) : 0);     if (r->value()) rr->value(r->value());     p_stack.pop_back();      bool directive_exists = false;     size_t L = rr->block() ? rr->block()->length() : 0;     for (size_t i = 0; i < L && !directive_exists; ++i) {-      Statement* s = (*r->block())[i];+      Statement_Obj s = r->block()->at(i);       if (s->statement_type() != Statement::BUBBLE) directive_exists = true;       else {-        s = static_cast<Bubble*>(s)->node();+        Bubble_Obj s_obj = SASS_MEMORY_CAST(Bubble, s);+        s = s_obj->node();         if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false;-        else directive_exists = (static_cast<At_Rule*>(s)->keyword() == rr->keyword());+        else directive_exists = (static_cast<Directive_Ptr>(&s)->keyword() == rr->keyword());       }      } -    Block* result = SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate());+    Block_Ptr result = SASS_MEMORY_NEW(Block, rr->pstate());     if (!(directive_exists || rr->is_keyframes()))     {-      At_Rule* empty_node = static_cast<At_Rule*>(rr);-      empty_node->block(SASS_MEMORY_NEW(ctx.mem, Block, rr->block() ? rr->block()->pstate() : rr->pstate()));-      *result << empty_node;+      Directive_Ptr empty_node = SASS_MEMORY_CAST(Directive, rr);+      empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate()));+      result->append(empty_node);     } -    Statement* ss = debubble(rr->block() ? rr->block() : SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate()), rr);-    for (size_t i = 0, L = ss->block()->length(); i < L; ++i) {-      *result << (*ss->block())[i];+    Block_Obj ss = debubble(rr->block() ? &rr->block() : SASS_MEMORY_NEW(Block, rr->pstate()), &rr);+    for (size_t i = 0, L = ss->length(); i < L; ++i) {+      result->append(ss->at(i));     }      return result;   } -  Statement* Cssize::operator()(Keyframe_Rule* r)+  Statement_Ptr Cssize::operator()(Keyframe_Rule_Ptr r)   {     if (!r->block() || !r->block()->length()) return r; -    Keyframe_Rule* rr = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule,+    Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule,                                         r->pstate(),-                                        r->block()->perform(this)->block());-    if (r->selector()) rr->selector(r->selector());+                                        operator()(&r->block()));+    if (&r->name()) rr->name(r->name()); -    return debubble(rr->block(), rr)->block();+    return debubble(&rr->block(), &rr);   } -  Statement* Cssize::operator()(Ruleset* r)+  Statement_Ptr Cssize::operator()(Ruleset_Ptr r)   {     p_stack.push_back(r);-    Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset,+    // 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());+    // this should protect us (at least a bit) from our mess+    // fixing this properly is harder that it should be ...+    if (dynamic_cast<Statement_Ptr>(bb) == NULL) {+      error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate());+    }+    Ruleset_Obj rr = SASS_MEMORY_NEW(Ruleset,                                   r->pstate(),                                   r->selector(),-                                  r->block()->perform(this)->block());+                                  bb);++    rr->is_root(r->is_root());     // rr->tabs(r->block()->tabs());     p_stack.pop_back(); -    Block* props = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());-    Block* rules = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());+    if (!rr->block()) {+      error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate());+    }++    Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());+    Block_Ptr rules = SASS_MEMORY_NEW(Block, rr->block()->pstate());     for (size_t i = 0, L = rr->block()->length(); i < L; i++)     {-      Statement* s = (*rr->block())[i];-      if (bubblable(s)) *rules << s;-      if (!bubblable(s)) *props << s;+      Statement_Ptr s = &rr->block()->at(i);+      if (bubblable(s)) rules->append(s);+      if (!bubblable(s)) props->append(s);     }      if (props->length())     {-      Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());-      *bb += props;+      Block_Obj bb = SASS_MEMORY_NEW(Block, rr->block()->pstate());+      bb->concat(&props);       rr->block(bb);        for (size_t i = 0, L = rules->length(); i < L; i++)       {-        (*rules)[i]->tabs((*rules)[i]->tabs() + 1);+        Statement_Ptr stm = &rules->at(i);+        stm->tabs(stm->tabs() + 1);       } -      rules->unshift(rr);+      rules->unshift(&rr);     } -    rules = debubble(rules)->block();+    Block_Ptr ptr = rules;+    rules = debubble(rules);+    void* lp = ptr;+    void* rp = rules;+    if (lp != rp) {+      Block_Obj obj = ptr;+    }      if (!(!rules->length() ||-          !bubblable(rules->last()) ||+          !bubblable(&rules->last()) ||           parent()->statement_type() == Statement::RULESET))     {       rules->last()->group_end(true);     }-     return rules;   } -  Statement* Cssize::operator()(Null* m)+  Statement_Ptr Cssize::operator()(Null_Ptr m)   {     return 0;   } -  Statement* Cssize::operator()(Media_Block* m)+  Statement_Ptr Cssize::operator()(Media_Block_Ptr m)   {     if (parent()->statement_type() == Statement::RULESET)     { return bubble(m); }      if (parent()->statement_type() == Statement::MEDIA)-    { return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m); }+    { return SASS_MEMORY_NEW(Bubble, m->pstate(), m); }      p_stack.push_back(m); -    Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+    Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block,                                       m->pstate(),-                                      m->media_queries(),-                                      m->block()->perform(this)->block());+                                      &m->media_queries(),+                                      operator()(&m->block()));     mm->tabs(m->tabs());      p_stack.pop_back(); -    return debubble(mm->block(), mm)->block();+    return debubble(&mm->block(), &mm);   } -  Statement* Cssize::operator()(Supports_Block* m)+  Statement_Ptr Cssize::operator()(Supports_Block_Ptr m)   {     if (!m->block()->length())     { return m; }@@ -171,339 +238,302 @@      p_stack.push_back(m); -    Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+    Supports_Block_Obj mm = SASS_MEMORY_NEW(Supports_Block,                                        m->pstate(),-                                       m->condition(),-                                       m->block()->perform(this)->block());+                                       &m->condition(),+                                       operator()(&m->block()));     mm->tabs(m->tabs());      p_stack.pop_back(); -    return debubble(mm->block(), mm)->block();+    return debubble(&mm->block(), &mm);   } -  Statement* Cssize::operator()(At_Root_Block* m)+  Statement_Ptr Cssize::operator()(At_Root_Block_Ptr m)   {     bool tmp = false;     for (size_t i = 0, L = p_stack.size(); i < L; ++i) {-      Statement* s = p_stack[i];+      Statement_Ptr s = p_stack[i];       tmp |= m->exclude_node(s);     }      if (!tmp)     {-      Block* bb = m->block()->perform(this)->block();+      Block_Ptr bb = operator()(&m->block());       for (size_t i = 0, L = bb->length(); i < L; ++i) {         // (bb->elements())[i]->tabs(m->tabs());-        if (bubblable((*bb)[i])) (*bb)[i]->tabs((*bb)[i]->tabs() + m->tabs());+        Statement_Obj stm = bb->at(i);+        if (bubblable(&stm)) stm->tabs(stm->tabs() + m->tabs());       }-      if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end());+      if (bb->length() && bubblable(&bb->last())) bb->last()->group_end(m->group_end());       return bb;     }      if (m->exclude_node(parent()))     {-      return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m);+      return SASS_MEMORY_NEW(Bubble, m->pstate(), m);     }      return bubble(m);   } -  Statement* Cssize::bubble(At_Rule* m)+  Statement_Ptr Cssize::bubble(Directive_Ptr m)   {-    Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate());-    Has_Block* new_rule = static_cast<Has_Block*>(shallow_copy(this->parent()));+    Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+    Has_Block_Obj new_rule = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(this->parent()));     new_rule->block(bb);     new_rule->tabs(this->parent()->tabs());--    size_t L = m->block() ? m->block()->length() : 0;-    for (size_t i = 0; i < L; ++i) {-      *new_rule->block() << (*m->block())[i];-    }+    new_rule->block()->concat(&m->block()); -    Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block() ? m->block()->pstate() : m->pstate());-    *wrapper_block << new_rule;-    At_Rule* mm = SASS_MEMORY_NEW(ctx.mem, At_Rule,+    Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate());+    wrapper_block->append(&new_rule);+    Directive_Obj mm = SASS_MEMORY_NEW(Directive,                                   m->pstate(),                                   m->keyword(),                                   m->selector(),                                   wrapper_block);     if (m->value()) mm->value(m->value()); -    Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+    Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), &mm);     return bubble;   } -  Statement* Cssize::bubble(At_Root_Block* m)+  Statement_Ptr Cssize::bubble(At_Root_Block_Ptr m)   {-    Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate());-    Has_Block* new_rule = static_cast<Has_Block*>(shallow_copy(this->parent()));+    Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+    Has_Block_Obj new_rule = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(this->parent()));     new_rule->block(bb);     new_rule->tabs(this->parent()->tabs());--    for (size_t i = 0, L = m->block()->length(); i < L; ++i) {-      *new_rule->block() << (*m->block())[i];-    }+    new_rule->block()->concat(&m->block()); -    Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());-    *wrapper_block << new_rule;-    At_Root_Block* mm = SASS_MEMORY_NEW(ctx.mem, At_Root_Block,+    Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+    wrapper_block->append(&new_rule);+    At_Root_Block_Ptr mm = SASS_MEMORY_NEW(At_Root_Block,                                         m->pstate(),                                         wrapper_block,                                         m->expression());-    Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+    Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);     return bubble;   } -  Statement* Cssize::bubble(Supports_Block* m)+  Statement_Ptr Cssize::bubble(Supports_Block_Ptr m)   {-    Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));+    Ruleset_Obj parent = static_cast<Ruleset_Ptr>(SASS_MEMORY_COPY(this->parent())); -    Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate());-    Ruleset* new_rule = SASS_MEMORY_NEW(ctx.mem, Ruleset,+    Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+    Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,                                         parent->pstate(),                                         parent->selector(),                                         bb);     new_rule->tabs(parent->tabs());--    for (size_t i = 0, L = m->block()->length(); i < L; ++i) {-      *new_rule->block() << (*m->block())[i];-    }+    new_rule->block()->concat(&m->block()); -    Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());-    *wrapper_block << new_rule;-    Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+    Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+    wrapper_block->append(new_rule);+    Supports_Block_Ptr mm = SASS_MEMORY_NEW(Supports_Block,                                        m->pstate(),-                                       m->condition(),+                                       &m->condition(),                                        wrapper_block);      mm->tabs(m->tabs()); -    Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+    Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);     return bubble;   } -  Statement* Cssize::bubble(Media_Block* m)+  Statement_Ptr Cssize::bubble(Media_Block_Ptr m)   {-    Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));+    Ruleset_Obj parent = static_cast<Ruleset_Ptr>(SASS_MEMORY_COPY(this->parent())); -    Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate());-    Ruleset* new_rule = SASS_MEMORY_NEW(ctx.mem, Ruleset,+    Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+    Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,                                         parent->pstate(),                                         parent->selector(),                                         bb);     new_rule->tabs(parent->tabs());--    for (size_t i = 0, L = m->block()->length(); i < L; ++i) {-      *new_rule->block() << (*m->block())[i];-    }+    new_rule->block()->concat(&m->block()); -    Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());-    *wrapper_block << new_rule;-    Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+    Block_Ptr 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(),+                                      &m->media_queries(),                                       wrapper_block,                                       0);      mm->tabs(m->tabs()); -    Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);--    return bubble;+    return SASS_MEMORY_NEW(Bubble, mm->pstate(), &mm);   } -  bool Cssize::bubblable(Statement* s)+  bool Cssize::bubblable(Statement_Ptr s)   {-    return s->statement_type() == Statement::RULESET || s->bubbles();+    return dynamic_cast<Ruleset_Ptr>(s) || s->bubbles();   } -  Statement* Cssize::flatten(Statement* s)+  Block_Ptr Cssize::flatten(Block_Ptr b)   {-    Block* bb = s->block();-    Block* result = SASS_MEMORY_NEW(ctx.mem, Block, bb->pstate(), 0, bb->is_root());-    for (size_t i = 0, L = bb->length(); i < L; ++i) {-      Statement* ss = (*bb)[i];-      if (ss->block()) {-        ss = flatten(ss);-        for (size_t j = 0, K = ss->block()->length(); j < K; ++j) {-          *result << (*ss->block())[j];+    Block_Ptr 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 = SASS_MEMORY_CAST_PTR(Block, ss)) {+        Block_Obj bs = flatten(bb);+        for (size_t j = 0, K = bs->length(); j < K; ++j) {+          result->append(bs->at(j));         }       }       else {-        *result << ss;+        result->append(ss);       }     }     return result;   } -  std::vector<std::pair<bool, Block*>> Cssize::slice_by_bubble(Statement* b)+  std::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block_Ptr b)   {-    std::vector<std::pair<bool, Block*>> results;-    for (size_t i = 0, L = b->block()->length(); i < L; ++i) {-      Statement* value = (*b->block())[i];-      bool key = value->statement_type() == Statement::BUBBLE;+    std::vector<std::pair<bool, Block_Obj>> results; +    for (size_t i = 0, L = b->length(); i < L; ++i) {+      Statement_Obj value = b->at(i);+      bool key = dynamic_cast<Bubble_Ptr>(&value) != NULL;+       if (!results.empty() && results.back().first == key)       {-        Block* wrapper_block = results.back().second;-        *wrapper_block << value;+        Block_Obj wrapper_block = results.back().second;+        wrapper_block->append(value);       }       else       {-        Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, value->pstate());-        *wrapper_block << value;+        Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());+        wrapper_block->append(value);         results.push_back(std::make_pair(key, wrapper_block));       }     }     return results;   } -  Statement* Cssize::shallow_copy(Statement* s)-  {-    switch (s->statement_type())-    {-      case Statement::RULESET:-        return SASS_MEMORY_NEW(ctx.mem, Ruleset, *static_cast<Ruleset*>(s));-      case Statement::MEDIA:-        return SASS_MEMORY_NEW(ctx.mem, Media_Block, *static_cast<Media_Block*>(s));-      case Statement::BUBBLE:-        return SASS_MEMORY_NEW(ctx.mem, Bubble, *static_cast<Bubble*>(s));-      case Statement::DIRECTIVE:-        return SASS_MEMORY_NEW(ctx.mem, At_Rule, *static_cast<At_Rule*>(s));-      case Statement::SUPPORTS:-        return SASS_MEMORY_NEW(ctx.mem, Supports_Block, *static_cast<Supports_Block*>(s));-      case Statement::ATROOT:-        return SASS_MEMORY_NEW(ctx.mem, At_Root_Block, *static_cast<At_Root_Block*>(s));-      case Statement::KEYFRAMERULE:-        return SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, *static_cast<Keyframe_Rule*>(s));-      case Statement::NONE:-      default:-        error("unknown internal error; please contact the LibSass maintainers", s->pstate(), backtrace);-        String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), std::string("`CSSize` can't clone ") + typeid(*s).name());-        return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg);-    }-  }--  Statement* Cssize::debubble(Block* children, Statement* parent)+  Block_Ptr Cssize::debubble(Block_Ptr children, Statement_Ptr parent)   {-    Has_Block* previous_parent = 0;-    std::vector<std::pair<bool, Block*>> baz = slice_by_bubble(children);-    Block* result = SASS_MEMORY_NEW(ctx.mem, Block, children->pstate());+    Has_Block_Obj previous_parent = 0;+    std::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children);+    Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate());      for (size_t i = 0, L = baz.size(); i < L; ++i) {       bool is_bubble = baz[i].first;-      Block* slice = baz[i].second;+      Block_Obj slice = baz[i].second;        if (!is_bubble) {         if (!parent) {-          *result << slice;+          result->append(&slice);         }         else if (previous_parent) {-          *previous_parent->block() += slice;+          previous_parent->block()->concat(&slice);         }         else {-          previous_parent = static_cast<Has_Block*>(shallow_copy(parent));+          previous_parent = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(parent));+          previous_parent->block(slice);           previous_parent->tabs(parent->tabs()); -          Has_Block* new_parent = static_cast<Has_Block*>(shallow_copy(parent));-          new_parent->block(slice);-          new_parent->tabs(parent->tabs());--          *result << new_parent;+          result->append(&previous_parent);         }         continue;       } -      Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block,-                                             children->block()->pstate(),-                                             children->block()->length(),-                                             children->block()->is_root());-       for (size_t j = 0, K = slice->length(); j < K; ++j)       {-        Statement* ss = 0;-        Bubble* b = static_cast<Bubble*>((*slice)[j]);-+        Statement_Ptr ss = NULL;+        Statement_Obj stm = slice->at(j);+        // this has to go now here (too bad)+        Bubble_Obj node = SASS_MEMORY_CAST(Bubble, stm);+        Media_Block_Ptr m1 = NULL;+        Media_Block_Ptr m2 = NULL;+        if (parent) m1 = SASS_MEMORY_CAST(Media_Block, *parent);+        if (node) m2 = SASS_MEMORY_CAST(Media_Block, node->node());         if (!parent ||             parent->statement_type() != Statement::MEDIA ||-            b->node()->statement_type() != Statement::MEDIA ||-            static_cast<Media_Block*>(b->node())->media_queries() == static_cast<Media_Block*>(parent)->media_queries())+            node->node()->statement_type() != Statement::MEDIA ||+            (m1 && m2 && &m1->media_queries() == &m2->media_queries())+          )         {-          ss = b->node();+          ss = &node->node();         }         else         {-          List* mq = merge_media_queries(static_cast<Media_Block*>(b->node()), static_cast<Media_Block*>(parent));+          List_Obj mq = merge_media_queries(static_cast<Media_Block_Ptr>(&node->node()), static_cast<Media_Block_Ptr>(parent));           if (!mq->length()) continue;-          static_cast<Media_Block*>(b->node())->media_queries(mq);-          ss = b->node();+          static_cast<Media_Block_Ptr>(&node->node())->media_queries(mq);+          ss = &node->node();         }          if (!ss) continue; -        ss->tabs(ss->tabs() + b->tabs());-        ss->group_end(b->group_end());+        ss->tabs(ss->tabs() + node->tabs());+        ss->group_end(node->group_end());          if (!ss) continue; -        Block* bb = SASS_MEMORY_NEW(ctx.mem, Block,-                                    children->block()->pstate(),-                                    children->block()->length(),-                                    children->block()->is_root());-        *bb << ss->perform(this);-        Statement* wrapper = flatten(bb);-        *wrapper_block << wrapper;+        Block_Obj bb = SASS_MEMORY_NEW(Block,+                                    children->pstate(),+                                    children->length(),+                                    children->is_root());+        bb->append(ss->perform(this)); -        if (wrapper->block()->length()) {-          previous_parent = 0;+        Block_Obj wrapper_block = SASS_MEMORY_NEW(Block,+                                              children->pstate(),+                                              children->length(),+                                              children->is_root());++        Block_Ptr wrapper = flatten(&bb);+        wrapper_block->append(wrapper);++        if (wrapper->length()) {+          previous_parent = NULL;         }-      } -      if (wrapper_block) {-        *result << flatten(wrapper_block);+        if (wrapper_block) {+          result->append(&wrapper_block);+        }       }     } -    return flatten(result);+    return flatten(&result);   } -  Statement* Cssize::fallback_impl(AST_Node* n)+  Statement_Ptr Cssize::fallback_impl(AST_Node_Ptr n)   {-    return static_cast<Statement*>(n);+    return static_cast<Statement_Ptr>(n);   } -  void Cssize::append_block(Block* b)+  void Cssize::append_block(Block_Ptr b, Block_Ptr cur)   {-    Block* current_block = block_stack.back();-     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* ith = (*b)[i]->perform(this);-      if (ith && ith->block()) {-        for (size_t j = 0, K = ith->block()->length(); j < K; ++j) {-          *current_block << (*ith->block())[j];+      Statement_Obj ith = b->at(i)->perform(this);+      if (Block_Ptr bb = SASS_MEMORY_CAST(Block, ith)) {+        for (size_t j = 0, K = bb->length(); j < K; ++j) {+          cur->append(bb->at(j));         }       }       else if (ith) {-        *current_block << ith;+        cur->append(ith);       }     }   } -  List* Cssize::merge_media_queries(Media_Block* m1, Media_Block* m2)+  List_Ptr Cssize::merge_media_queries(Media_Block_Ptr m1, Media_Block_Ptr m2)   {-    List* qq = SASS_MEMORY_NEW(ctx.mem, List,+    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++) {-        Media_Query* mq1 = static_cast<Media_Query*>((*m1->media_queries())[i]);-        Media_Query* mq2 = static_cast<Media_Query*>((*m2->media_queries())[j]);-        Media_Query* mq = merge_media_query(mq1, mq2);--        if (mq) *qq << mq;+        Expression_Obj l1 = m1->media_queries()->at(i);+        Expression_Obj l2 = m2->media_queries()->at(j);+        Media_Query_Ptr mq1 = SASS_MEMORY_CAST(Media_Query, l1);+        Media_Query_Ptr mq2 = SASS_MEMORY_CAST(Media_Query, l2);+        Media_Query_Ptr mq = merge_media_query(mq1, mq2);+        if (mq) qq->append(mq);       }     } @@ -511,17 +541,16 @@   }  -  Media_Query* Cssize::merge_media_query(Media_Query* mq1, Media_Query* mq2)+  Media_Query_Ptr Cssize::merge_media_query(Media_Query_Ptr mq1, Media_Query_Ptr mq2)   {-    To_String to_string(&ctx);      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()->perform(&to_string) : "";+    std::string t1 = &mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : "";     std::string m2 = std::string(mq2->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");-    std::string t2 = mq2->media_type() ? mq2->media_type()->perform(&to_string) : "";+    std::string t2 = &mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : "";       if (t1.empty()) t1 = t2;@@ -548,18 +577,19 @@       mod = m1.empty() ? m2 : m1;     } -    Media_Query* mm = SASS_MEMORY_NEW(ctx.mem, Media_Query,+    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(ctx.mem, String_Quoted, mq1->pstate(), type));+      mm->media_type(SASS_MEMORY_NEW(String_Quoted, mq1->pstate(), type));     } -    *mm += mq2;-    *mm += mq1;+    mm->concat(mq2);+    mm->concat(mq1);+     return mm;   } }
libsass/src/cssize.hpp view
@@ -8,69 +8,69 @@  namespace Sass { -  typedef Environment<AST_Node*> Env;+  typedef Environment<AST_Node_Obj> Env;   struct Backtrace; -  class Cssize : public Operation_CRTP<Statement*, Cssize> {+  class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> { -    Context&                 ctx;-    std::vector<Block*>      block_stack;-    std::vector<Statement*>  p_stack;-    Backtrace*               backtrace;+    Context&                    ctx;+    std::vector<Block_Ptr>         block_stack;+    std::vector<Statement_Ptr>     p_stack;+    Backtrace*                  backtrace; -    Statement* fallback_impl(AST_Node* n);+    Statement_Ptr fallback_impl(AST_Node_Ptr n);    public:     Cssize(Context&, Backtrace*);-    virtual ~Cssize() { }+    ~Cssize() { } -    using Operation<Statement*>::operator();+    Selector_List_Ptr selector(); -    Statement* operator()(Block*);-    Statement* operator()(Ruleset*);-    // Statement* operator()(Propset*);-    // Statement* operator()(Bubble*);-    Statement* operator()(Media_Block*);-    Statement* operator()(Supports_Block*);-    Statement* operator()(At_Root_Block*);-    Statement* operator()(At_Rule*);-    Statement* operator()(Keyframe_Rule*);-    // 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()(Extension*);-    // Statement* operator()(Definition*);-    // Statement* operator()(Mixin_Call*);-    // Statement* operator()(Content*);-    Statement* operator()(Null*);+    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* parent();-    std::vector<std::pair<bool, Block*>> slice_by_bubble(Statement*);-    Statement* bubble(At_Rule*);-    Statement* bubble(At_Root_Block*);-    Statement* bubble(Media_Block*);-    Statement* bubble(Supports_Block*);-    Statement* shallow_copy(Statement*);-    Statement* debubble(Block* children, Statement* parent = 0);-    Statement* flatten(Statement*);-    bool bubblable(Statement*);+    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); -    List* merge_media_queries(Media_Block*, Media_Block*);-    Media_Query* merge_media_query(Media_Query*, Media_Query*);+    Block_Ptr debubble(Block_Ptr children, Statement_Ptr parent = 0);+    Block_Ptr flatten(Block_Ptr);+    bool bubblable(Statement_Ptr); +    List_Ptr merge_media_queries(Media_Block_Ptr, Media_Block_Ptr);+    Media_Query_Ptr merge_media_query(Media_Query_Ptr, Media_Query_Ptr);+     template <typename U>-    Statement* fallback(U x) { return fallback_impl(x); }+    Statement_Ptr fallback(U x) { return fallback_impl(x); } -    void append_block(Block*);+    void append_block(Block_Ptr, Block_Ptr);   };  }
libsass/src/debugger.hpp view
@@ -8,13 +8,13 @@  using namespace Sass; -inline void debug_ast(AST_Node* node, std::string ind = "", Env* env = 0);+inline void debug_ast(AST_Node_Ptr node, std::string ind = "", Env* env = 0);  inline void debug_sources_set(SourcesSet& set, std::string ind = "") {   if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";   for(auto const &pair : set) {-    debug_ast(pair, ind + "");+    debug_ast(&pair, ind + "");     // debug_ast(set[pair], ind + "first: ");   }   if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";@@ -44,7 +44,7 @@   return is.str(); } -inline std::string pstate_source_position(AST_Node* node)+inline std::string pstate_source_position(AST_Node_Ptr node) {   std::stringstream str;   Position start(node->pstate());@@ -52,58 +52,82 @@   str << (start.file == std::string::npos ? -1 : start.file)     << "@[" << start.line << ":" << start.column << "]"     << "-[" << end.line << ":" << end.column << "]";+#ifdef DEBUG_SHARED_PTR+      str << "x" << node->getRefCount() << ""+      << " " << node->getDbgFile()+      << "@" << node->getDbgLine();+#endif   return str.str(); } -inline void debug_ast(AST_Node* node, std::string ind, Env* env)+inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) {   if (node == 0) return;   if (ind == "") std::cerr << "####################################################################\n";-  if (dynamic_cast<Bubble*>(node)) {-    Bubble* bubble = dynamic_cast<Bubble*>(node);+  if (dynamic_cast<Bubble_Ptr>(node)) {+    Bubble_Ptr bubble = dynamic_cast<Bubble_Ptr>(node);     std::cerr << ind << "Bubble " << bubble;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << bubble->tabs();     std::cerr << std::endl;-  } else if (dynamic_cast<At_Root_Block*>(node)) {-    At_Root_Block* root_block = dynamic_cast<At_Root_Block*>(node);+    debug_ast(&bubble->node(), ind + " ", env);+  } else if (dynamic_cast<Trace_Ptr>(node)) {+    Trace_Ptr trace = dynamic_cast<Trace_Ptr>(node);+    std::cerr << ind << "Trace " << trace;+    std::cerr << " (" << pstate_source_position(node) << ")"+    << " [name:" << trace->name() << "]"+    << std::endl;+    debug_ast(&trace->block(), ind + " ", env);+  } else if (dynamic_cast<At_Root_Block_Ptr>(node)) {+    At_Root_Block_Ptr root_block = dynamic_cast<At_Root_Block_Ptr>(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 (dynamic_cast<Selector_List*>(node)) {-    Selector_List* selector = dynamic_cast<Selector_List*>(node);+    debug_ast(&root_block->expression(), ind + ":", env);+    debug_ast(&root_block->block(), ind + " ", env);+  } else if (dynamic_cast<Selector_List_Ptr>(node)) {+    Selector_List_Ptr selector = dynamic_cast<Selector_List_Ptr>(node);     std::cerr << ind << "Selector_List " << 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 << std::endl; -    for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }+    for(const Complex_Selector_Obj& i : selector->elements()) { debug_ast(&i, ind + " ", env); } -//  } else if (dynamic_cast<Expression*>(node)) {-//    Expression* expression = dynamic_cast<Expression*>(node);+//  } else if (dynamic_cast<Expression_Ptr>(node)) {+//    Expression_Ptr expression = dynamic_cast<Expression_Ptr>(node); //    std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl; -  } else if (dynamic_cast<Parent_Selector*>(node)) {-    Parent_Selector* selector = dynamic_cast<Parent_Selector*>(node);+  } else if (dynamic_cast<Parent_Selector_Ptr>(node)) {+    Parent_Selector_Ptr selector = dynamic_cast<Parent_Selector_Ptr>(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); -  } else if (dynamic_cast<Complex_Selector*>(node)) {-    Complex_Selector* selector = dynamic_cast<Complex_Selector*>(node);+  } else if (dynamic_cast<Complex_Selector_Ptr>(node)) {+    Complex_Selector_Ptr selector = dynamic_cast<Complex_Selector_Ptr>(node);     std::cerr << ind << "Complex_Selector " << 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]": " -")       << " -- ";@@ -117,64 +141,104 @@       }       // 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);+    debug_ast(&selector->head(), ind + " " /* + "[" + del + "]" */, env);     if (selector->tail()) {-      debug_ast(selector->tail(), ind + "{" + del + "}", env);+      debug_ast(&selector->tail(), ind + "{" + del + "}", env);     } else if(del != " ") {       std::cerr << ind << " |" << del << "| {trailing op}" << std::endl;     }     SourcesSet set = selector->sources();     // debug_sources_set(set, ind + "  @--> ");-  } else if (dynamic_cast<Compound_Selector*>(node)) {-    Compound_Selector* selector = dynamic_cast<Compound_Selector*>(node);+  } else if (dynamic_cast<Compound_Selector_Ptr>(node)) {+    Compound_Selector_Ptr selector = dynamic_cast<Compound_Selector_Ptr>(node);     std::cerr << ind << "Compound_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";+    std::cerr << " <" << selector->hash() << ">";     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(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Propset*>(node)) {-    Propset* selector = dynamic_cast<Propset*>(node);-    std::cerr << ind << "Propset " << selector;-    std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " " << selector->tabs() << std::endl;-    if (selector->block()) for(auto i : selector->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Wrapped_Selector*>(node)) {-    Wrapped_Selector* selector = dynamic_cast<Wrapped_Selector*>(node);+    for(const Simple_Selector_Obj& i : selector->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Wrapped_Selector_Ptr>(node)) {+    Wrapped_Selector_Ptr selector = dynamic_cast<Wrapped_Selector_Ptr>(node);     std::cerr << ind << "Wrapped_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;-    debug_ast(selector->selector(), ind + " () ", env);-  } else if (dynamic_cast<Pseudo_Selector*>(node)) {-    Pseudo_Selector* selector = dynamic_cast<Pseudo_Selector*>(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);+  } else if (dynamic_cast<Pseudo_Selector_Ptr>(node)) {+    Pseudo_Selector_Ptr selector = dynamic_cast<Pseudo_Selector_Ptr>(node);     std::cerr << ind << "Pseudo_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;-    debug_ast(selector->expression(), ind + " <= ", env);-  } else if (dynamic_cast<Attribute_Selector*>(node)) {-    Attribute_Selector* selector = dynamic_cast<Attribute_Selector*>(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->expression(), ind + " <= ", env);+  } else if (dynamic_cast<Attribute_Selector_Ptr>(node)) {+    Attribute_Selector_Ptr selector = dynamic_cast<Attribute_Selector_Ptr>(node);     std::cerr << ind << "Attribute_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;-    debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);-  } else if (dynamic_cast<Selector_Qualifier*>(node)) {-    Selector_Qualifier* selector = dynamic_cast<Selector_Qualifier*>(node);-    std::cerr << ind << "Selector_Qualifier " << selector;+    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 (dynamic_cast<Class_Selector_Ptr>(node)) {+    Class_Selector_Ptr selector = dynamic_cast<Class_Selector_Ptr>(node);+    std::cerr << ind << "Class_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;-  } else if (dynamic_cast<Type_Selector*>(node)) {-    Type_Selector* selector = dynamic_cast<Type_Selector*>(node);-    std::cerr << ind << "Type_Selector " << selector;+    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 (dynamic_cast<Id_Selector_Ptr>(node)) {+    Id_Selector_Ptr selector = dynamic_cast<Id_Selector_Ptr>(node);+    std::cerr << ind << "Id_Selector " << selector;     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") <<-      " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;-  } else if (dynamic_cast<Selector_Placeholder*>(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 (dynamic_cast<Element_Selector_Ptr>(node)) {+    Element_Selector_Ptr selector = dynamic_cast<Element_Selector_Ptr>(node);+    std::cerr << ind << "Element_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 (dynamic_cast<Placeholder_Selector_Ptr>(node)) { -    Selector_Placeholder* selector = dynamic_cast<Selector_Placeholder*>(node);-    std::cerr << ind << "Selector_Placeholder [" << selector->ns_name() << "] " << selector+    Placeholder_Selector_Ptr selector = dynamic_cast<Placeholder_Selector_Ptr>(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]": " -")@@ -187,8 +251,8 @@     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; -  } else if (dynamic_cast<Selector_Schema*>(node)) {-    Selector_Schema* selector = dynamic_cast<Selector_Schema*>(node);+  } else if (dynamic_cast<Selector_Schema_Ptr>(node)) {+    Selector_Schema_Ptr selector = dynamic_cast<Selector_Schema_Ptr>(node);     std::cerr << ind << "Selector_Schema " << selector;     std::cerr << " (" << pstate_source_position(node) << ")"       << (selector->at_root() && selector->at_root() ? " [@ROOT]" : "")@@ -197,183 +261,196 @@       << (selector->has_line_feed() ? " [line-feed]": " -")     << std::endl; -    debug_ast(selector->contents(), ind + " ");+    debug_ast(&selector->contents(), ind + " ");     // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } -  } else if (dynamic_cast<Selector*>(node)) {-    Selector* selector = dynamic_cast<Selector*>(node);+  } else if (dynamic_cast<Selector_Ptr>(node)) {+    Selector_Ptr selector = dynamic_cast<Selector_Ptr>(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::endl; -  } else if (dynamic_cast<Media_Query_Expression*>(node)) {-    Media_Query_Expression* block = dynamic_cast<Media_Query_Expression*>(node);+  } else if (dynamic_cast<Media_Query_Expression_Ptr>(node)) {+    Media_Query_Expression_Ptr block = dynamic_cast<Media_Query_Expression_Ptr>(node);     std::cerr << ind << "Media_Query_Expression " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -")     << std::endl;-    debug_ast(block->feature(), ind + " feature) ");-    debug_ast(block->value(), ind + " value) ");+    debug_ast(&block->feature(), ind + " feature) ");+    debug_ast(&block->value(), ind + " value) "); -  } else if (dynamic_cast<Media_Query*>(node)) {-    Media_Query* block = dynamic_cast<Media_Query*>(node);+  } else if (dynamic_cast<Media_Query_Ptr>(node)) {+    Media_Query_Ptr block = dynamic_cast<Media_Query_Ptr>(node);     std::cerr << ind << "Media_Query " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << (block->is_negated() ? " [is_negated]": " -")       << (block->is_restricted() ? " [is_restricted]": " -")     << std::endl;-    debug_ast(block->media_type(), ind + " ");-    for(auto i : block->elements()) { debug_ast(i, ind + " ", env); }+    debug_ast(&block->media_type(), ind + " ");+    for(const auto& i : block->elements()) { debug_ast(&i, ind + " ", env); } -  } else if (dynamic_cast<Media_Block*>(node)) {-    Media_Block* block = dynamic_cast<Media_Block*>(node);+  } else if (dynamic_cast<Media_Block_Ptr>(node)) {+    Media_Block_Ptr block = dynamic_cast<Media_Block_Ptr>(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(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Supports_Block*>(node)) {-    Supports_Block* block = dynamic_cast<Supports_Block*>(node);+    debug_ast(&block->media_queries(), ind + " =@ ");+    if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Supports_Block_Ptr>(node)) {+    Supports_Block_Ptr block = dynamic_cast<Supports_Block_Ptr>(node);     std::cerr << ind << "Supports_Block " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->condition(), ind + " =@ ");-  } else if (dynamic_cast<Supports_Operator*>(node)) {-    Supports_Operator* block = dynamic_cast<Supports_Operator*>(node);+    debug_ast(&block->condition(), ind + " =@ ");+    debug_ast(&block->block(), ind + " <>");+  } else if (dynamic_cast<Supports_Operator_Ptr>(node)) {+    Supports_Operator_Ptr block = dynamic_cast<Supports_Operator_Ptr>(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 (dynamic_cast<Supports_Negation*>(node)) {-    Supports_Negation* block = dynamic_cast<Supports_Negation*>(node);+    debug_ast(&block->left(), ind + " left) ");+    debug_ast(&block->right(), ind + " right) ");+  } else if (dynamic_cast<Supports_Negation_Ptr>(node)) {+    Supports_Negation_Ptr block = dynamic_cast<Supports_Negation_Ptr>(node);     std::cerr << ind << "Supports_Negation " << block;     std::cerr << " (" << pstate_source_position(node) << ")"     << std::endl;-    debug_ast(block->condition(), ind + " condition) ");-  } else if (dynamic_cast<Supports_Declaration*>(node)) {-    Supports_Declaration* block = dynamic_cast<Supports_Declaration*>(node);+    debug_ast(&block->condition(), ind + " condition) ");+  } else if (dynamic_cast<At_Root_Query_Ptr>(node)) {+    At_Root_Query_Ptr block = dynamic_cast<At_Root_Query_Ptr>(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 (dynamic_cast<Supports_Declaration_Ptr>(node)) {+    Supports_Declaration_Ptr block = dynamic_cast<Supports_Declaration_Ptr>(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 (dynamic_cast<Block*>(node)) {-    Block* root_block = dynamic_cast<Block*>(node);+    debug_ast(&block->feature(), ind + " feature) ");+    debug_ast(&block->value(), ind + " value) ");+  } else if (dynamic_cast<Block_Ptr>(node)) {+    Block_Ptr root_block = dynamic_cast<Block_Ptr>(node);     std::cerr << ind << "Block " << root_block;     std::cerr << " (" << pstate_source_position(node) << ")";     if (root_block->is_root()) std::cerr << " [root]";     std::cerr << " " << root_block->tabs() << std::endl;-    if (root_block->block()) for(auto i : root_block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Warning*>(node)) {-    Warning* block = dynamic_cast<Warning*>(node);+    for(const Statement_Obj& i : root_block->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Warning_Ptr>(node)) {+    Warning_Ptr block = dynamic_cast<Warning_Ptr>(node);     std::cerr << ind << "Warning " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-  } else if (dynamic_cast<Error*>(node)) {-    Error* block = dynamic_cast<Error*>(node);+    debug_ast(&block->message(), ind + " : ");+  } else if (dynamic_cast<Error_Ptr>(node)) {+    Error_Ptr block = dynamic_cast<Error_Ptr>(node);     std::cerr << ind << "Error " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-  } else if (dynamic_cast<Debug*>(node)) {-    Debug* block = dynamic_cast<Debug*>(node);+  } else if (dynamic_cast<Debug_Ptr>(node)) {+    Debug_Ptr block = dynamic_cast<Debug_Ptr>(node);     std::cerr << ind << "Debug " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-  } else if (dynamic_cast<Comment*>(node)) {-    Comment* block = dynamic_cast<Comment*>(node);+    debug_ast(&block->value(), ind + " ");+  } else if (dynamic_cast<Comment_Ptr>(node)) {+    Comment_Ptr block = dynamic_cast<Comment_Ptr>(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 (dynamic_cast<If*>(node)) {-    If* block = dynamic_cast<If*>(node);+    debug_ast(&block->text(), ind + "// ", env);+  } else if (dynamic_cast<If_Ptr>(node)) {+    If_Ptr block = dynamic_cast<If_Ptr>(node);     std::cerr << ind << "If " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->predicate(), ind + " = ");-    debug_ast(block->block(), ind + " <>");-    debug_ast(block->alternative(), ind + " ><");-  } else if (dynamic_cast<Return*>(node)) {-    Return* block = dynamic_cast<Return*>(node);+    debug_ast(&block->predicate(), ind + " = ");+    debug_ast(&block->block(), ind + " <>");+    debug_ast(&block->alternative(), ind + " ><");+  } else if (dynamic_cast<Return_Ptr>(node)) {+    Return_Ptr block = dynamic_cast<Return_Ptr>(node);     std::cerr << ind << "Return " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-  } else if (dynamic_cast<Extension*>(node)) {-    Extension* block = dynamic_cast<Extension*>(node);+  } else if (dynamic_cast<Extension_Ptr>(node)) {+    Extension_Ptr block = dynamic_cast<Extension_Ptr>(node);     std::cerr << ind << "Extension " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->selector(), ind + "-> ", env);-  } else if (dynamic_cast<Content*>(node)) {-    Content* block = dynamic_cast<Content*>(node);+    debug_ast(&block->selector(), ind + "-> ", env);+  } else if (dynamic_cast<Content_Ptr>(node)) {+    Content_Ptr block = dynamic_cast<Content_Ptr>(node);     std::cerr << ind << "Content " << block;     std::cerr << " (" << pstate_source_position(node) << ")";+    std::cerr << " [@media:" << block->media_block() << "]";     std::cerr << " " << block->tabs() << std::endl;-  } else if (dynamic_cast<Import_Stub*>(node)) {-    Import_Stub* block = dynamic_cast<Import_Stub*>(node);+  } else if (dynamic_cast<Import_Stub_Ptr>(node)) {+    Import_Stub_Ptr block = dynamic_cast<Import_Stub_Ptr>(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 (dynamic_cast<Import*>(node)) {-    Import* block = dynamic_cast<Import*>(node);+  } else if (dynamic_cast<Import_Ptr>(node)) {+    Import_Ptr block = dynamic_cast<Import_Ptr>(node);     std::cerr << ind << "Import " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->media_queries(), ind + " @ ");     // std::vector<std::string>         files_;-    for (auto imp : block->urls()) debug_ast(imp, "@ ", env);-  } else if (dynamic_cast<Assignment*>(node)) {-    Assignment* block = dynamic_cast<Assignment*>(node);+    for (auto imp : block->urls()) debug_ast(&imp, ind + "@: ", env);+    debug_ast(&block->import_queries(), ind + "@@ ");+  } else if (dynamic_cast<Assignment_Ptr>(node)) {+    Assignment_Ptr block = dynamic_cast<Assignment_Ptr>(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 (dynamic_cast<Declaration*>(node)) {-    Declaration* block = dynamic_cast<Declaration*>(node);+    debug_ast(&block->value(), ind + "=", env);+  } else if (dynamic_cast<Declaration_Ptr>(node)) {+    Declaration_Ptr block = dynamic_cast<Declaration_Ptr>(node);     std::cerr << ind << "Declaration " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->property(), ind + " prop: ", env);-    debug_ast(block->value(), ind + " value: ", env);-  } else if (dynamic_cast<Keyframe_Rule*>(node)) {-    Keyframe_Rule* has_block = dynamic_cast<Keyframe_Rule*>(node);+    debug_ast(&block->property(), ind + " prop: ", env);+    debug_ast(&block->value(), ind + " value: ", env);+    debug_ast(&block->block(), ind + " ", env);+  } else if (dynamic_cast<Keyframe_Rule_Ptr>(node)) {+    Keyframe_Rule_Ptr has_block = dynamic_cast<Keyframe_Rule_Ptr>(node);     std::cerr << ind << "Keyframe_Rule " << has_block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << has_block->tabs() << std::endl;-    if (has_block->selector()) debug_ast(has_block->selector(), ind + "@");-    if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<At_Rule*>(node)) {-    At_Rule* block = dynamic_cast<At_Rule*>(node);-    std::cerr << ind << "At_Rule " << block;+    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 (dynamic_cast<Directive_Ptr>(node)) {+    Directive_Ptr block = dynamic_cast<Directive_Ptr>(node);+    std::cerr << ind << "Directive " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl;-    debug_ast(block->selector(), ind + "~", env);-    debug_ast(block->value(), ind + "+", env);-    if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Each*>(node)) {-    Each* block = dynamic_cast<Each*>(node);+    debug_ast(&block->selector(), ind + "~", env);+    debug_ast(&block->value(), ind + "+", env);+    if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Each_Ptr>(node)) {+    Each_Ptr block = dynamic_cast<Each_Ptr>(node);     std::cerr << ind << "Each " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<For*>(node)) {-    For* block = dynamic_cast<For*>(node);+    if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<For_Ptr>(node)) {+    For_Ptr block = dynamic_cast<For_Ptr>(node);     std::cerr << ind << "For " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<While*>(node)) {-    While* block = dynamic_cast<While*>(node);+    if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<While_Ptr>(node)) {+    While_Ptr block = dynamic_cast<While_Ptr>(node);     std::cerr << ind << "While " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << block->tabs() << std::endl;-    if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Definition*>(node)) {-    Definition* block = dynamic_cast<Definition*>(node);+    if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Definition_Ptr>(node)) {+    Definition_Ptr block = dynamic_cast<Definition_Ptr>(node);     std::cerr << ind << "Definition " << block;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [name: " << block->name() << "] ";@@ -382,62 +459,73 @@     // std::cerr << " [signature: " << block->signature() << "] ";     std::cerr << " [native: " << block->native_function() << "] ";     std::cerr << " " << block->tabs() << std::endl;-    debug_ast(block->parameters(), ind + " params: ", env);-    if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Mixin_Call*>(node)) {-    Mixin_Call* block = dynamic_cast<Mixin_Call*>(node);+    debug_ast(&block->parameters(), ind + " params: ", env);+    if (block->block()) debug_ast(&block->block(), ind + " ", env);+  } else if (dynamic_cast<Mixin_Call_Ptr>(node)) {+    Mixin_Call_Ptr block = dynamic_cast<Mixin_Call_Ptr>(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: ");-    if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (Ruleset* ruleset = dynamic_cast<Ruleset*>(node)) {+    debug_ast(&block->arguments(), ind + " args: ");+    if (block->block()) debug_ast(&block->block(), ind + " ", env);+  } else if (Ruleset_Ptr ruleset = dynamic_cast<Ruleset_Ptr>(node)) {     std::cerr << ind << "Ruleset " << ruleset;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [indent: " << ruleset->tabs() << "]";+    std::cerr << (ruleset->is_invisible() ? " [INVISIBLE]" : "");     std::cerr << (ruleset->at_root() ? " [@ROOT]" : "");     std::cerr << (ruleset->is_root() ? " [root]" : "");     std::cerr << std::endl;-    debug_ast(ruleset->selector(), ind + ">");-    debug_ast(ruleset->block(), ind + " ");-  } else if (dynamic_cast<Block*>(node)) {-    Block* block = dynamic_cast<Block*>(node);+    debug_ast(&ruleset->selector(), ind + ">");+    debug_ast(&ruleset->block(), ind + " ");+  } else if (dynamic_cast<Block_Ptr>(node)) {+    Block_Ptr block = dynamic_cast<Block_Ptr>(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(auto i : block->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Textual*>(node)) {-    Textual* expression = dynamic_cast<Textual*>(node);-    std::cerr << ind << "Textual ";+    for(const Statement_Obj& i : block->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Textual_Ptr>(node)) {+    Textual_Ptr expression = dynamic_cast<Textual_Ptr>(node);+    std::cerr << ind << "Textual " << expression;+    std::cerr << " (" << pstate_source_position(node) << ")";     if (expression->type() == Textual::NUMBER) std::cerr << " [NUMBER]";     else if (expression->type() == Textual::PERCENTAGE) std::cerr << " [PERCENTAGE]";     else if (expression->type() == Textual::DIMENSION) std::cerr << " [DIMENSION]";     else if (expression->type() == Textual::HEX) std::cerr << " [HEX]";-    std::cerr << expression << " [" << expression->value() << "]";+    std::cerr << " [" << expression->value() << "]";+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     if (expression->is_delayed()) std::cerr << " [delayed]";     std::cerr << std::endl;-  } else if (dynamic_cast<Variable*>(node)) {-    Variable* expression = dynamic_cast<Variable*>(node);+  } else if (dynamic_cast<Variable_Ptr>(node)) {+    Variable_Ptr expression = dynamic_cast<Variable_Ptr>(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(static_cast<Expression*>((*env)[name]), ind + " -> ", env);-  } else if (dynamic_cast<Function_Call_Schema*>(node)) {-    Function_Call_Schema* expression = dynamic_cast<Function_Call_Schema*>(node);+    if (env && env->has(name)) debug_ast(SASS_MEMORY_CAST(Expression, (*env)[name]), ind + " -> ", env);+  } else if (dynamic_cast<Function_Call_Schema_Ptr>(node)) {+    Function_Call_Schema_Ptr expression = dynamic_cast<Function_Call_Schema_Ptr>(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 (dynamic_cast<Function_Call*>(node)) {-    Function_Call* expression = dynamic_cast<Function_Call*>(node);+    debug_ast(&expression->name(), ind + "name: ", env);+    debug_ast(&expression->arguments(), ind + " args: ", env);+  } else if (dynamic_cast<Function_Call_Ptr>(node)) {+    Function_Call_Ptr expression = dynamic_cast<Function_Call_Ptr>(node);     std::cerr << ind << "Function_Call " << expression;+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";-    std::cerr << " [" << expression->name() << "]" << std::endl;-    debug_ast(expression->arguments(), ind + " args: ", env);-  } else if (dynamic_cast<Arguments*>(node)) {-    Arguments* expression = dynamic_cast<Arguments*>(node);+    std::cerr << " [" << expression->name() << "]";+    if (expression->is_delayed()) std::cerr << " [delayed]";+    if (expression->is_interpolant()) std::cerr << " [interpolant]";+    std::cerr << std::endl;+    debug_ast(&expression->arguments(), ind + " args: ", env);+  } else if (dynamic_cast<Arguments_Ptr>(node)) {+    Arguments_Ptr expression = dynamic_cast<Arguments_Ptr>(node);     std::cerr << ind << "Arguments " << expression;     if (expression->is_delayed()) std::cerr << " [delayed]";     std::cerr << " (" << pstate_source_position(node) << ")";@@ -445,100 +533,120 @@     if (expression->has_rest_argument()) std::cerr << " [has_rest_argument]";     if (expression->has_keyword_argument()) std::cerr << " [has_keyword_argument]";     std::cerr << std::endl;-    for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Argument*>(node)) {-    Argument* expression = dynamic_cast<Argument*>(node);+    for(const Argument_Obj& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Argument_Ptr>(node)) {+    Argument_Ptr expression = dynamic_cast<Argument_Ptr>(node);     std::cerr << ind << "Argument " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << expression->value() << "]";     std::cerr << " [name: " << expression->name() << "] ";     std::cerr << " [rest: " << expression->is_rest_argument() << "] ";     std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl;-    debug_ast(expression->value(), ind + " value: ", env);-  } else if (dynamic_cast<Parameters*>(node)) {-    Parameters* expression = dynamic_cast<Parameters*>(node);+    debug_ast(&expression->value(), ind + " value: ", env);+  } else if (dynamic_cast<Parameters_Ptr>(node)) {+    Parameters_Ptr expression = dynamic_cast<Parameters_Ptr>(node);     std::cerr << ind << "Parameters " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] ";     std::cerr << " [has_rest: " << expression->has_rest_parameter() << "] ";     std::cerr << std::endl;-    for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Parameter*>(node)) {-    Parameter* expression = dynamic_cast<Parameter*>(node);+    for(const Parameter_Obj& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Parameter_Ptr>(node)) {+    Parameter_Ptr expression = dynamic_cast<Parameter_Ptr>(node);     std::cerr << ind << "Parameter " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [name: " << expression->name() << "] ";     std::cerr << " [default: " << expression->default_value() << "] ";     std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl;-  } else if (dynamic_cast<Unary_Expression*>(node)) {-    Unary_Expression* expression = dynamic_cast<Unary_Expression*>(node);+  } else if (dynamic_cast<Unary_Expression_Ptr>(node)) {+    Unary_Expression_Ptr expression = dynamic_cast<Unary_Expression_Ptr>(node);     std::cerr << ind << "Unary_Expression " << expression;+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";+    std::cerr << " [delayed: " << expression->is_delayed() << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << expression->type() << "]" << std::endl;-    debug_ast(expression->operand(), ind + " operand: ", env);-  } else if (dynamic_cast<Binary_Expression*>(node)) {-    Binary_Expression* expression = dynamic_cast<Binary_Expression*>(node);+    debug_ast(&expression->operand(), ind + " operand: ", env);+  } else if (dynamic_cast<Binary_Expression_Ptr>(node)) {+    Binary_Expression_Ptr expression = dynamic_cast<Binary_Expression_Ptr>(node);     std::cerr << ind << "Binary_Expression " << expression;+    if (expression->is_interpolant()) std::cerr << " [is interpolant] ";+    if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";+    if (expression->is_right_interpolant()) std::cerr << " [right interpolant] ";     std::cerr << " [delayed: " << expression->is_delayed() << "] ";+    std::cerr << " [ws_before: " << expression->op().ws_before << "] ";+    std::cerr << " [ws_after: " << expression->op().ws_after << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << expression->type_name() << "]" << std::endl;-    debug_ast(expression->left(), ind + " left:  ", env);-    debug_ast(expression->right(), ind + " right: ", env);-  } else if (dynamic_cast<Map*>(node)) {-    Map* expression = dynamic_cast<Map*>(node);+    debug_ast(&expression->left(), ind + " left:  ", env);+    debug_ast(&expression->right(), ind + " right: ", env);+  } else if (dynamic_cast<Map_Ptr>(node)) {+    Map_Ptr expression = dynamic_cast<Map_Ptr>(node);     std::cerr << ind << "Map " << expression;+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [Hashed]" << std::endl;-    for (auto i : expression->elements()) {-      debug_ast(i.first, ind + " key: ");-      debug_ast(i.second, ind + " val: ");+    for (const auto& i : expression->elements()) {+      debug_ast(&i.first, ind + " key: ");+      debug_ast(&i.second, ind + " val: ");     }-  } else if (dynamic_cast<List*>(node)) {-    List* expression = dynamic_cast<List*>(node);+  } else if (dynamic_cast<List_Ptr>(node)) {+    List_Ptr expression = dynamic_cast<List_Ptr>(node);     std::cerr << ind << "List " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " (" << expression->length() << ") " <<-      (expression->separator() == SASS_COMMA ? "Comma " : "Space ") <<+      (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map" : "Space ") <<       " [delayed: " << expression->is_delayed() << "] " <<       " [interpolant: " << expression->is_interpolant() << "] " <<+      " [listized: " << expression->from_selector() << "] " <<       " [arglist: " << expression->is_arglist() << "] " <<       " [hash: " << expression->hash() << "] " <<       std::endl;-    for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Content*>(node)) {-    Content* expression = dynamic_cast<Content*>(node);+    for(const auto& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Content_Ptr>(node)) {+    Content_Ptr expression = dynamic_cast<Content_Ptr>(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 (dynamic_cast<Boolean*>(node)) {-    Boolean* expression = dynamic_cast<Boolean*>(node);+  } else if (dynamic_cast<Boolean_Ptr>(node)) {+    Boolean_Ptr expression = dynamic_cast<Boolean_Ptr>(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 (dynamic_cast<Color*>(node)) {-    Color* expression = dynamic_cast<Color*>(node);+  } else if (dynamic_cast<Color_Ptr>(node)) {+    Color_Ptr expression = dynamic_cast<Color_Ptr>(node);     std::cerr << ind << "Color " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";+    std::cerr << " [delayed: " << expression->is_delayed() << "] ";+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " [" << expression->r() << ":"  << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;-  } else if (dynamic_cast<Number*>(node)) {-    Number* expression = dynamic_cast<Number*>(node);+  } else if (dynamic_cast<Number_Ptr>(node)) {+    Number_Ptr expression = dynamic_cast<Number_Ptr>(node);     std::cerr << ind << "Number " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";+    std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";     std::cerr << " [" << expression->value() << expression->unit() << "]" <<       " [hash: " << expression->hash() << "] " <<       std::endl;-  } else if (dynamic_cast<String_Quoted*>(node)) {-    String_Quoted* expression = dynamic_cast<String_Quoted*>(node);+  } else if (dynamic_cast<Null_Ptr>(node)) {+    Null_Ptr expression = dynamic_cast<Null_Ptr>(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 (dynamic_cast<String_Quoted_Ptr>(node)) {+    String_Quoted_Ptr expression = dynamic_cast<String_Quoted_Ptr>(node);     std::cerr << ind << "String_Quoted " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << prettyprint(expression->value()) << "]";     if (expression->is_delayed()) std::cerr << " [delayed]";-    if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]";     if (expression->is_interpolant()) std::cerr << " [interpolant]";     if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;-  } else if (dynamic_cast<String_Constant*>(node)) {-    String_Constant* expression = dynamic_cast<String_Constant*>(node);+  } else if (dynamic_cast<String_Constant_Ptr>(node)) {+    String_Constant_Ptr expression = dynamic_cast<String_Constant_Ptr>(node);     std::cerr << ind << "String_Constant " << expression;     if (expression->concrete_type()) {       std::cerr << " " << expression->concrete_type();@@ -546,28 +654,30 @@     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " [" << prettyprint(expression->value()) << "]";     if (expression->is_delayed()) std::cerr << " [delayed]";-    if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]";     if (expression->is_interpolant()) std::cerr << " [interpolant]";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;-  } else if (dynamic_cast<String_Schema*>(node)) {-    String_Schema* expression = dynamic_cast<String_Schema*>(node);+  } else if (dynamic_cast<String_Schema_Ptr>(node)) {+    String_Schema_Ptr expression = dynamic_cast<String_Schema_Ptr>(node);     std::cerr << ind << "String_Schema " << expression;+    std::cerr << " (" << pstate_source_position(expression) << ")";     std::cerr << " " << expression->concrete_type();+    std::cerr << " (" << pstate_source_position(node) << ")";     if (expression->is_delayed()) std::cerr << " [delayed]";-    if (expression->has_interpolants()) std::cerr << " [has_interpolants]";-    if (expression->is_interpolant()) std::cerr << " [interpolant]";+    if (expression->is_interpolant()) std::cerr << " [is interpolant]";+    if (expression->has_interpolant()) std::cerr << " [has interpolant]";+    if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";+    if (expression->is_right_interpolant()) std::cerr << " [right interpolant] ";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;-    for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<String*>(node)) {-    String* expression = dynamic_cast<String*>(node);+    for(const auto& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<String_Ptr>(node)) {+    String_Ptr expression = dynamic_cast<String_Ptr>(node);     std::cerr << ind << "String " << expression;     std::cerr << " " << expression->concrete_type();     std::cerr << " (" << pstate_source_position(node) << ")";-    if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]";     if (expression->is_interpolant()) std::cerr << " [interpolant]";     std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;-  } else if (dynamic_cast<Expression*>(node)) {-    Expression* expression = dynamic_cast<Expression*>(node);+  } else if (dynamic_cast<Expression_Ptr>(node)) {+    Expression_Ptr expression = dynamic_cast<Expression_Ptr>(node);     std::cerr << ind << "Expression " << expression;     std::cerr << " (" << pstate_source_position(node) << ")";     switch (expression->concrete_type()) {@@ -582,17 +692,18 @@       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;     }     std::cerr << std::endl;-  } else if (dynamic_cast<Has_Block*>(node)) {-    Has_Block* has_block = dynamic_cast<Has_Block*>(node);+  } else if (dynamic_cast<Has_Block_Ptr>(node)) {+    Has_Block_Ptr has_block = dynamic_cast<Has_Block_Ptr>(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(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }-  } else if (dynamic_cast<Statement*>(node)) {-    Statement* statement = dynamic_cast<Statement*>(node);+    if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(&i, ind + " ", env); }+  } else if (dynamic_cast<Statement_Ptr>(node)) {+    Statement_Ptr statement = dynamic_cast<Statement_Ptr>(node);     std::cerr << ind << "Statement " << statement;     std::cerr << " (" << pstate_source_position(node) << ")";     std::cerr << " " << statement->tabs() << std::endl;@@ -624,7 +735,7 @@     std::cerr << node << " ";     if (node->got_line_feed) std::cerr << "[LF] ";     std::cerr << std::endl;-    debug_ast(node->selector(), ind + "  ");+    debug_ast(&node->selector(), ind + "  ");   } else if (node->isCollection()) {     std::cerr << ind;     std::cerr << "Collection ";@@ -650,35 +761,36 @@   if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; } -inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0)+/*+inline void debug_ast(const AST_Node_Ptr node, std::string ind = "", Env* env = 0) {-  debug_ast(const_cast<AST_Node*>(node), ind, env);+  debug_ast(const_cast<AST_Node_Ptr>(node), ind, env); }-+*/ inline void debug_node(const Node* node, std::string ind = "") {   debug_node(const_cast<Node*>(node), ind); } -inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, std::string 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: ");+  for(auto const &it : map.values()) {+    debug_ast(&it.first, ind + "first: ");+    debug_ast(&it.second, ind + "second: ");   }   if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; } -typedef std::pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> ExtensionPair; typedef std::vector<ExtensionPair> SubsetMapEntries;  inline void debug_subset_entries(SubsetMapEntries* 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: ");+    debug_ast(&pair.first, ind + "first: ");+    debug_ast(&pair.second, ind + "second: ");   }   if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; }
libsass/src/emitter.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "util.hpp" #include "context.hpp" #include "output.hpp"@@ -6,20 +7,20 @@  namespace Sass { -  Emitter::Emitter(Context* ctx)+  Emitter::Emitter(struct Sass_Output_Options& opt)   : wbuf(),-    ctx(ctx),+    opt(opt),     indentation(0),     scheduled_space(0),     scheduled_linefeed(0),     scheduled_delimiter(false),+    scheduled_mapping(0),     in_comment(false),     in_wrapped(false),     in_media_block(false),     in_declaration(false),     in_space_array(false),-    in_comma_array(false),-    in_debug(false)+    in_comma_array(false)   { }    // return buffer as string@@ -28,9 +29,9 @@     return wbuf.buffer;   } -  Sass_Output_Style Emitter::output_style(void)+  Sass_Output_Style Emitter::output_style(void) const   {-    return ctx ? ctx->output_style() : SASS_STYLE_COMPRESSED;+    return opt.output_style;   }    // PROXY METHODS FOR SOURCE MAPS@@ -44,9 +45,11 @@   void Emitter::set_filename(const std::string& str)   { wbuf.smap.file = str; } -  void Emitter::add_open_mapping(AST_Node* node)+  void Emitter::schedule_mapping(const AST_Node_Ptr node)+  { scheduled_mapping = node; }+  void Emitter::add_open_mapping(const AST_Node_Ptr node)   { wbuf.smap.add_open_mapping(node); }-  void Emitter::add_close_mapping(AST_Node* node)+  void Emitter::add_close_mapping(const AST_Node_Ptr node)   { wbuf.smap.add_close_mapping(node); }   ParserState Emitter::remap(const ParserState& pstate)   { return wbuf.smap.remap(pstate); }@@ -54,9 +57,11 @@   // MAIN BUFFER MANIPULATION    // add outstanding delimiter-  void Emitter::finalize(void)+  void Emitter::finalize(bool final)   {     scheduled_space = 0;+    if (output_style() == SASS_STYLE_COMPRESSED)+      if (final) scheduled_delimiter = false;     if (scheduled_linefeed)       scheduled_linefeed = 1;     flush_schedules();@@ -70,7 +75,7 @@       std::string linefeeds = "";        for (size_t i = 0; i < scheduled_linefeed; i++)-        linefeeds += ctx ? ctx->linefeed : "\n";+        linefeeds += opt.linefeed;       scheduled_space = 0;       scheduled_linefeed = 0;       append_string(linefeeds);@@ -103,10 +108,11 @@   // append some text or token to the buffer   void Emitter::append_string(const std::string& text)   {+     // write space/lf     flush_schedules(); -    if (in_comment && output_style() == SASS_STYLE_COMPACT) {+    if (in_comment && output_style() == COMPACT) {       // unescape comment nodes       std::string out = comment_to_string(text);       // add to buffer@@ -133,10 +139,16 @@    // 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, AST_Node* node)+  void Emitter::append_token(const std::string& text, const AST_Node_Ptr node)   {     flush_schedules();     add_open_mapping(node);+    // hotfix for browser issues+    // this is pretty ugly indeed+    if (scheduled_mapping) {+      add_open_mapping(scheduled_mapping);+      scheduled_mapping = 0;+    }     append_string(text);     add_close_mapping(node);   }@@ -145,27 +157,27 @@    void Emitter::append_indentation()   {-    if (output_style() == SASS_STYLE_COMPRESSED) return;-    if (output_style() == SASS_STYLE_COMPACT) return;+    if (output_style() == COMPRESSED) return;+    if (output_style() == COMPACT) return;     if (in_declaration && in_comma_array) return;     if (scheduled_linefeed && indentation)       scheduled_linefeed = 1;     std::string indent = "";     for (size_t i = 0; i < indentation; i++)-      indent += ctx ? ctx->indent : "  ";+      indent += opt.indent;     append_string(indent);   }    void Emitter::append_delimiter()   {     scheduled_delimiter = true;-    if (output_style() == SASS_STYLE_COMPACT) {+    if (output_style() == COMPACT) {       if (indentation == 0) {         append_mandatory_linefeed();       } else {         append_mandatory_space();       }-    } else if (output_style() != SASS_STYLE_COMPRESSED) {+    } else if (output_style() != COMPRESSED) {       append_optional_linefeed();     }   }@@ -191,8 +203,8 @@    void Emitter::append_optional_space()   {-    if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && buffer().size()) {-      char lst = buffer().at(buffer().length() - 1);+    if ((output_style() != COMPRESSED) && buffer().size()) {+      unsigned char lst = buffer().at(buffer().length() - 1);       if (!isspace(lst) || scheduled_delimiter) {         append_mandatory_space();       }@@ -201,17 +213,17 @@    void Emitter::append_special_linefeed()   {-    if (output_style() == SASS_STYLE_COMPACT) {+    if (output_style() == COMPACT) {       append_mandatory_linefeed();       for (size_t p = 0; p < indentation; p++)-        append_string(ctx ? ctx->indent : "  ");+        append_string(opt.indent);     }   }    void Emitter::append_optional_linefeed()   {     if (in_declaration && in_comma_array) return;-    if (output_style() == SASS_STYLE_COMPACT) {+    if (output_style() == COMPACT) {       append_mandatory_space();     } else {       append_mandatory_linefeed();@@ -220,14 +232,14 @@    void Emitter::append_mandatory_linefeed()   {-    if (output_style() != SASS_STYLE_COMPRESSED) {+    if (output_style() != COMPRESSED) {       scheduled_linefeed = 1;       scheduled_space = 0;       // flush_schedules();     }   } -  void Emitter::append_scope_opener(AST_Node* node)+  void Emitter::append_scope_opener(AST_Node_Ptr node)   {     scheduled_linefeed = 0;     append_optional_space();@@ -238,13 +250,13 @@     // append_optional_space();     ++ indentation;   }-  void Emitter::append_scope_closer(AST_Node* node)+  void Emitter::append_scope_closer(AST_Node_Ptr node)   {     -- indentation;     scheduled_linefeed = 0;-    if (output_style() == SASS_STYLE_COMPRESSED)+    if (output_style() == COMPRESSED)       scheduled_delimiter = false;-    if (output_style() == SASS_STYLE_EXPANDED) {+    if (output_style() == EXPANDED) {       append_optional_linefeed();       append_indentation();     } else {@@ -254,7 +266,7 @@     if (node) add_close_mapping(node);     append_optional_linefeed();     if (indentation != 0) return;-    if (output_style() != SASS_STYLE_COMPRESSED)+    if (output_style() != COMPRESSED)       scheduled_linefeed = 2;   } 
libsass/src/emitter.hpp view
@@ -2,6 +2,7 @@ #define SASS_EMITTER_H  #include <string>+#include "sass.hpp" #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp"@@ -12,7 +13,7 @@   class Emitter {      public:-      Emitter(Context* ctx);+      Emitter(struct Sass_Output_Options& opt);       virtual ~Emitter() { }      protected:@@ -24,17 +25,19 @@       // proxy methods for source maps       void add_source_index(size_t idx);       void set_filename(const std::string& str);-      void add_open_mapping(AST_Node* node);-      void add_close_mapping(AST_Node* node);+      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);       std::string render_srcmap(Context &ctx);       ParserState remap(const ParserState& pstate);      public:-      Context* ctx;+      struct Sass_Output_Options& opt;       size_t indentation;       size_t scheduled_space;       size_t scheduled_linefeed;       bool scheduled_delimiter;+      AST_Node_Ptr scheduled_mapping;      public:       // output strings different in comments@@ -48,16 +51,14 @@       // nested lists need parentheses       bool in_space_array;       bool in_comma_array;-      // list separators don't get compressed in @debug-      bool in_debug;      public:       // return buffer as std::string       std::string get_buffer(void);       // flush scheduled space/linefeed-      Sass_Output_Style output_style(void);+      Sass_Output_Style output_style(void) const;       // add outstanding linefeed-      void finalize(void);+      void finalize(bool final = true);       // flush scheduled space/linefeed       void flush_schedules(void);       // prepend some text or token to the buffer@@ -69,7 +70,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, AST_Node* node);+      void append_token(const std::string& text, const AST_Node_Ptr node);      public: // syntax sugar       void append_indentation();@@ -78,8 +79,8 @@       void append_special_linefeed(void);       void append_optional_linefeed(void);       void append_mandatory_linefeed(void);-      void append_scope_opener(AST_Node* node = 0);-      void append_scope_closer(AST_Node* node = 0);+      void append_scope_opener(AST_Node_Ptr node = 0);+      void append_scope_closer(AST_Node_Ptr node = 0);       void append_comma_separator(void);       void append_colon_separator(void);       void append_delimiter(void);
libsass/src/environment.cpp view
@@ -1,14 +1,24 @@+#include "sass.hpp" #include "ast.hpp" #include "environment.hpp"  namespace Sass {    template <typename T>-  Environment<T>::Environment() : local_frame_(std::map<std::string, T>()), parent_(0) { }+  Environment<T>::Environment(bool is_shadow)+  : local_frame_(std::map<std::string, T>()),+    parent_(0), is_shadow_(false)+  { }   template <typename T>-  Environment<T>::Environment(Environment<T>* env) : local_frame_(std::map<std::string, T>()), parent_(env) { }+  Environment<T>::Environment(Environment<T>* env, bool is_shadow)+  : local_frame_(std::map<std::string, T>()),+    parent_(env), is_shadow_(is_shadow)+  { }   template <typename T>-  Environment<T>::Environment(Environment<T>& env) : local_frame_(std::map<std::string, T>()), parent_(&env) { }+  Environment<T>::Environment(Environment<T>& env, bool is_shadow)+  : local_frame_(std::map<std::string, T>()),+    parent_(&env), is_shadow_(is_shadow)+  { }    // link parent to create a stack   template <typename T>@@ -118,12 +128,13 @@   template <typename T>   void Environment<T>::set_lexical(const std::string& key, T val)   {-    auto cur = this;-    while (cur->is_lexical()) {+    auto cur = this; bool shadow = false;+    while (cur->is_lexical() || shadow) {       if (cur->has_local(key)) {         cur->set_local(key, val);         return;       }+      shadow = cur->is_shadow();       cur = cur->parent_;     }     set_local(key, val);@@ -168,8 +179,8 @@     for (typename std::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* val = dynamic_cast<Value*>(i->second))-        { std::cerr << " : " << val->to_string(true, 5); }+        if (Value_Ptr val = SASS_MEMORY_CAST_PTR(Value, i->second))+        { std::cerr << " : " << val->to_string(); }         std::cerr << std::endl;       }     }@@ -178,7 +189,7 @@   #endif    // compile implementation for AST_Node-  template class Environment<AST_Node*>;+  template class Environment<AST_Node_Obj>;  } 
libsass/src/environment.hpp view
@@ -6,7 +6,6 @@  #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp"-#include "memory_manager.hpp"  namespace Sass { @@ -15,12 +14,12 @@     // TODO: test with map     std::map<std::string, T> local_frame_;     ADD_PROPERTY(Environment*, parent)+    ADD_PROPERTY(bool, is_shadow)    public:-    Memory_Manager mem;-    Environment();-    Environment(Environment* env);-    Environment(Environment& env);+    Environment(bool is_shadow = false);+    Environment(Environment* env, bool is_shadow = false);+    Environment(Environment& env, bool is_shadow = false);      // link parent to create a stack     void link(Environment& env);@@ -87,6 +86,7 @@     #endif    };+ }  #endif
libsass/src/error_handling.cpp view
@@ -1,7 +1,7 @@+#include "sass.hpp" #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp"-#include "to_string.hpp" #include "error_handling.hpp"  #include <iostream>@@ -10,43 +10,133 @@    namespace Exception { -    Base::Base(ParserState pstate, std::string msg)-    : std::runtime_error(msg),-      msg(msg), pstate(pstate)+    Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)+    : std::runtime_error(msg), msg(msg),+      prefix("Error"), pstate(pstate),+      import_stack(import_stack)     { } -    const char* Base::what() const throw()-    {-      return msg.c_str();-    }-     InvalidSass::InvalidSass(ParserState pstate, std::string msg)     : Base(pstate, msg)     { }  -    InvalidParent::InvalidParent(Selector* parent, Selector* selector)+    InvalidParent::InvalidParent(Selector_Ptr parent, Selector_Ptr selector)     : Base(selector->pstate()), parent(parent), selector(selector)     {       msg = "Invalid parent selector for \"";-      msg += selector->to_string(false);+      msg += selector->to_string(Sass_Inspect_Options());       msg += "\": \"";-      msg += parent->to_string(false);;+      msg += parent->to_string(Sass_Inspect_Options());       msg += "\"";     } -    InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value)+    InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value)     : Base(pstate), fn(fn), arg(arg), type(type), value(value)     {       msg  = arg + ": \"";-      msg += value->to_string(true, 5);+      if (value) msg += value->to_string(Sass_Inspect_Options());       msg += "\" is not a " + type;       msg += " for `" + fn + "'";     } -    InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg)-    : Base(pstate, msg)+    MissingArgument::MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype)+    : Base(pstate), fn(fn), arg(arg), fntype(fntype)+    {+      msg  = fntype + " " + fn;+      msg += " is missing argument ";+      msg += arg + ".";+    }++    InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)+    : Base(pstate, msg, import_stack)     { }++    UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)+    : lhs(lhs), rhs(rhs), op(op)+    {+      msg  = def_op_msg + ": \"";+      msg += lhs->to_string({ NESTED, 5 });+      msg += " " + op + " ";+      msg += rhs->to_string({ TO_SASS, 5 });+      msg += "\".";+    }++    InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)+    : UndefinedOperation(lhs, rhs, op)+    {+      msg  = def_op_null_msg + ": \"";+      msg += lhs->inspect();+      msg += " " + op + " ";+      msg += rhs->inspect();+      msg += "\".";+    }++    ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)+    : lhs(lhs), rhs(rhs)+    {+      msg  = "divided by 0";+    }++    DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org)+    : Base(org.pstate()), dup(dup), org(org)+    {+      msg  = "Duplicate key ";+      msg += dup.get_duplicate_key()->inspect();+      msg += " in map (";+      msg += org.inspect();+      msg += ").";+    }++    TypeMismatch::TypeMismatch(const Expression& var, const std::string type)+    : Base(var.pstate()), var(var), type(type)+    {+      msg  = var.to_string();+      msg += " is not an ";+      msg += type;+      msg += ".";+    }++    InvalidValue::InvalidValue(const Expression& val)+    : Base(val.pstate()), val(val)+    {+      msg  = val.to_string();+      msg += " isn't a valid CSS value.";+    }++    StackError::StackError(const AST_Node& node)+    : Base(node.pstate()), node(node)+    {+      msg  = "stack level too deep";+    }++    IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs)+    : lhs(lhs), rhs(rhs)+    {+      msg  = "Incompatible units: '";+      msg += rhs.unit();+      msg += "' and '";+      msg += lhs.unit();+      msg += "'.";+    }++    AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)+    : lhs(lhs), rhs(rhs), op(op)+    {+      msg  = "Alpha channels must be equal: ";+      msg += lhs->to_string({ NESTED, 5 });+      msg += " " + op + " ";+      msg += rhs->to_string({ NESTED, 5 });+      msg += ".";+    }+++    SassValueError::SassValueError(ParserState pstate, OperationError& err)+    : Base(pstate, err.what())+    {+      msg = err.what();+      prefix = err.errtype();+    }    } 
libsass/src/error_handling.hpp view
@@ -12,16 +12,21 @@    namespace Exception { -    const std::string def_msg = "Invalid sass";+    const std::string def_msg = "Invalid sass detected";+    const std::string def_op_msg = "Undefined operation";+    const std::string def_op_null_msg = "Invalid null operation";      class Base : public std::runtime_error {       protected:         std::string msg;+        std::string prefix;       public:         ParserState pstate;+        std::vector<Sass_Import_Entry>* import_stack;       public:-        Base(ParserState pstate, std::string msg = def_msg);-        virtual const char* what() const throw();+        Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0);+        virtual const char* errtype() const { return prefix.c_str(); }+        virtual const char* what() const throw() { return msg.c_str(); }         virtual ~Base() throw() {};     }; @@ -33,28 +38,143 @@      class InvalidParent : public Base {       protected:-        Selector* parent;-        Selector* selector;+        Selector_Ptr parent;+        Selector_Ptr selector;       public:-        InvalidParent(Selector* parent, Selector* selector);+        InvalidParent(Selector_Ptr parent, Selector_Ptr selector);         virtual ~InvalidParent() throw() {};     }; +    class MissingArgument : public Base {+      protected:+        std::string fn;+        std::string arg;+        std::string fntype;+      public:+        MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype);+        virtual ~MissingArgument() throw() {};+    };+     class InvalidArgumentType : public Base {       protected:         std::string fn;         std::string arg;         std::string type;-        const Value* value;+        const Value_Ptr value;       public:-        InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value = 0);+        InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0);         virtual ~InvalidArgumentType() throw() {};     };      class InvalidSyntax : public Base {       public:-        InvalidSyntax(ParserState pstate, std::string msg);+        InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0);         virtual ~InvalidSyntax() throw() {};+    };++    /* common virtual base class (has no pstate) */+    class OperationError : public std::runtime_error {+      protected:+        std::string msg;+      public:+        OperationError(std::string msg = def_op_msg)+        : std::runtime_error(msg), msg(msg)+        {};+      public:+        virtual const char* errtype() const { return "Error"; }+        virtual const char* what() const throw() { return msg.c_str(); }+        virtual ~OperationError() throw() {};+    };++    class ZeroDivisionError : public OperationError {+      protected:+        const Expression& lhs;+        const Expression& rhs;+      public:+        ZeroDivisionError(const Expression& lhs, const Expression& rhs);+        virtual const char* errtype() const { return "ZeroDivisionError"; }+        virtual ~ZeroDivisionError() throw() {};+    };++    class DuplicateKeyError : public Base {+      protected:+        const Map& dup;+        const Expression& org;+      public:+        DuplicateKeyError(const Map& dup, const Expression& org);+        virtual const char* errtype() const { return "Error"; }+        virtual ~DuplicateKeyError() throw() {};+    };++    class TypeMismatch : public Base {+      protected:+        const Expression& var;+        const std::string type;+      public:+        TypeMismatch(const Expression& var, const std::string type);+        virtual const char* errtype() const { return "Error"; }+        virtual ~TypeMismatch() throw() {};+    };++    class InvalidValue : public Base {+      protected:+        const Expression& val;+      public:+        InvalidValue(const Expression& val);+        virtual const char* errtype() const { return "Error"; }+        virtual ~InvalidValue() throw() {};+    };++    class StackError : public Base {+      protected:+        const AST_Node& node;+      public:+        StackError(const AST_Node& node);+        virtual const char* errtype() const { return "SystemStackError"; }+        virtual ~StackError() throw() {};+    };++    class IncompatibleUnits : public OperationError {+      protected:+        const Number& lhs;+        const Number& rhs;+      public:+        IncompatibleUnits(const Number& lhs, const Number& rhs);+        virtual ~IncompatibleUnits() throw() {};+    };++    class UndefinedOperation : public OperationError {+      protected:+        Expression_Ptr_Const lhs;+        Expression_Ptr_Const rhs;+        const std::string op;+      public:+        UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& 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, const std::string& op);+        virtual ~InvalidNullOperation() throw() {};+    };++    class AlphaChannelsNotEqual : public OperationError {+      protected:+        Expression_Ptr_Const lhs;+        Expression_Ptr_Const rhs;+        const std::string op;+      public:+        AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+        // virtual const char* errtype() const { return "Error"; }+        virtual ~AlphaChannelsNotEqual() throw() {};+    };++    class SassValueError : public Base {+      public:+        SassValueError(ParserState pstate, OperationError& err);+        virtual ~SassValueError() throw() {};     };    }
libsass/src/eval.cpp view
@@ -1,1530 +1,1712 @@-#include <cstdlib>-#include <cmath>-#include <iostream>-#include <sstream>-#include <iomanip>-#include <typeinfo>--#include "file.hpp"-#include "eval.hpp"-#include "ast.hpp"-#include "bind.hpp"-#include "util.hpp"-#include "to_string.hpp"-#include "inspect.hpp"-#include "environment.hpp"-#include "position.hpp"-#include "sass/values.h"-#include "to_value.hpp"-#include "to_c.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "lexer.hpp"-#include "prelexer.hpp"-#include "parser.hpp"-#include "expand.hpp"-#include "color_maps.hpp"--namespace Sass {--  inline double add(double x, double y) { return x + y; }-  inline double sub(double x, double y) { return x - y; }-  inline double mul(double x, double y) { return x * y; }-  inline double div(double x, double y) { return x / y; } // x/0 checked by caller-  inline double mod(double x, double y) { return std::abs(std::fmod(x, y)); } // x/0 checked by caller-  typedef double (*bop)(double, double);-  bop ops[Sass_OP::NUM_OPS] = {-    0, 0, // and, or-    0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte-    add, sub, mul, div, mod-  };--  Eval::Eval(Expand& exp)-  : exp(exp),-    ctx(exp.ctx),-    listize(exp.ctx)-  { }-  Eval::~Eval() { }--  Context& Eval::context()-  {-    return ctx;-  }--  Env* Eval::environment()-  {-    return exp.environment();-  }--  Selector_List* Eval::selector()-  {-    return exp.selector();-  }--  Backtrace* Eval::backtrace()-  {-    return exp.backtrace();-  }--  Expression* Eval::operator()(Block* b)-  {-    Expression* val = 0;-    for (size_t i = 0, L = b->length(); i < L; ++i) {-      val = (*b)[i]->perform(this);-      if (val) return val;-    }-    return val;-  }--  Expression* Eval::operator()(Assignment* a)-  {-    Env* env = exp.environment();-    std::string var(a->variable());-    if (a->is_global()) {-      if (a->is_default()) {-        if (env->has_global(var)) {-          Expression* e = dynamic_cast<Expression*>(env->get_global(var));-          if (!e || e->concrete_type() == Expression::NULL_VAL) {-            env->set_global(var, a->value()->perform(this));-          }-        }-        else {-          env->set_global(var, a->value()->perform(this));-        }-      }-      else {-        env->set_global(var, a->value()->perform(this));-      }-    }-    else if (a->is_default()) {-      if (env->has_lexical(var)) {-        auto cur = env;-        while (cur && cur->is_lexical()) {-          if (cur->has_local(var)) {-            if (AST_Node* node = cur->get_local(var)) {-              Expression* e = dynamic_cast<Expression*>(node);-              if (!e || e->concrete_type() == Expression::NULL_VAL) {-                cur->set_local(var, a->value()->perform(this));-              }-            }-            else {-              throw std::runtime_error("Env not in sync");-            }-            return 0;-          }-          cur = cur->parent();-        }-        throw std::runtime_error("Env not in sync");-      }-      else if (env->has_global(var)) {-        if (AST_Node* node = env->get_global(var)) {-          Expression* e = dynamic_cast<Expression*>(node);-          if (!e || e->concrete_type() == Expression::NULL_VAL) {-            env->set_global(var, a->value()->perform(this));-          }-        }-      }-      else if (env->is_lexical()) {-        env->set_local(var, a->value()->perform(this));-      }-      else {-        env->set_local(var, a->value()->perform(this));-      }-    }-    else {-      env->set_lexical(var, a->value()->perform(this));-    }-    return 0;-  }--  Expression* Eval::operator()(If* i)-  {-    Expression* rv = 0;-    Env env(exp.environment());-    exp.env_stack.push_back(&env);-    if (*i->predicate()->perform(this)) {-      rv = i->block()->perform(this);-    }-    else {-      Block* alt = i->alternative();-      if (alt) rv = alt->perform(this);-    }-    exp.env_stack.pop_back();-    return rv;-  }--  // For does not create a new env scope-  // But iteration vars are reset afterwards-  Expression* Eval::operator()(For* f)-  {-    std::string variable(f->variable());-    Expression* low = f->lower_bound()->perform(this);-    if (low->concrete_type() != Expression::NUMBER) {-      error("lower bound of `@for` directive must be numeric", low->pstate());-    }-    Expression* high = f->upper_bound()->perform(this);-    if (high->concrete_type() != Expression::NUMBER) {-      error("upper bound of `@for` directive must be numeric", high->pstate());-    }-    Number* sass_start = static_cast<Number*>(low);-    Number* sass_end = static_cast<Number*>(high);-    // check if units are valid for sequence-    if (sass_start->unit() != sass_end->unit()) {-      std::stringstream msg; msg << "Incompatible units: '"-        << sass_start->unit() << "' and '"-        << sass_end->unit() << "'.";-      error(msg.str(), low->pstate(), backtrace());-    }-    double start = sass_start->value();-    double end = sass_end->value();-    // only create iterator once in this environment-    Env* env = exp.environment();-    Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());-    AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;-    env->set_local(variable, it);-    Block* body = f->block();-    Expression* val = 0;-    if (start < end) {-      if (f->is_inclusive()) ++end;-      for (double i = start;-           i < end;-           ++i) {-        it->value(i);-        env->set_local(variable, it);-        val = body->perform(this);-        if (val) break;-      }-    } else {-      if (f->is_inclusive()) --end;-      for (double i = start;-           i > end;-           --i) {-        it->value(i);-        env->set_local(variable, it);-        val = body->perform(this);-        if (val) break;-      }-    }-    // restore original environment-    if (!old_var) env->del_local(variable);-    else env->set_local(variable, old_var);-    return val;-  }--  // Eval does not create a new env scope-  // But iteration vars are reset afterwards-  Expression* Eval::operator()(Each* e)-  {-    std::vector<std::string> variables(e->variables());-    Expression* expr = e->list()->perform(this);-    Env* env = exp.environment();-    List* list = 0;-    Map* map = 0;-    if (expr->concrete_type() == Expression::MAP) {-      map = static_cast<Map*>(expr);-    }-    else if (expr->concrete_type() != Expression::LIST) {-      list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);-      *list << expr;-    }-    else {-      list = static_cast<List*>(expr);-    }-    // remember variables and then reset them-    std::vector<AST_Node*> old_vars(variables.size());-    for (size_t i = 0, L = variables.size(); i < L; ++i) {-      old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;-      env->set_local(variables[i], 0);-    }-    Block* body = e->block();-    Expression* val = 0;--    if (map) {-      for (auto key : map->keys()) {-        Expression* value = map->at(key);--        if (variables.size() == 1) {-          List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE);-          *variable << key;-          *variable << value;-          env->set_local(variables[0], variable);-        } else {-          env->set_local(variables[0], key);-          env->set_local(variables[1], value);-        }--        val = body->perform(this);-        if (val) break;-      }-    }-    else {-      for (size_t i = 0, L = list->length(); i < L; ++i) {-        Expression* e = (*list)[i];-        // unwrap value if the expression is an argument-        if (Argument* arg = dynamic_cast<Argument*>(e)) e = arg->value();-        // check if we got passed a list of args (investigate)-        if (List* scalars = dynamic_cast<List*>(e)) {-          if (variables.size() == 1) {-            Expression* var = scalars;-            env->set_local(variables[0], var);-          } else {-            for (size_t j = 0, K = variables.size(); j < K; ++j) {-              Expression* res = j >= scalars->length()-                ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())-                : (*scalars)[j];-              env->set_local(variables[j], res);-            }-          }-        } else {-          if (variables.size() > 0) {-            env->set_local(variables[0], e);-            for (size_t j = 1, K = variables.size(); j < K; ++j) {-              Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());-              env->set_local(variables[j], res);-            }-          }-        }-        val = body->perform(this);-        if (val) break;-      }-    }-    // restore original environment-    for (size_t j = 0, K = variables.size(); j < K; ++j) {-      if(!old_vars[j]) env->del_local(variables[j]);-      else env->set_local(variables[j], old_vars[j]);-    }-    return val;-  }--  Expression* Eval::operator()(While* w)-  {-    Expression* pred = w->predicate();-    Block* body = w->block();-    while (*pred->perform(this)) {-      Expression* val = body->perform(this);-      if (val) return val;-    }-    return 0;-  }--  Expression* Eval::operator()(Return* r)-  {-    return r->value()->perform(this);-  }--  Expression* Eval::operator()(Warning* w)-  {-    Expression* message = w->message()->perform(this);-    To_String to_string(&ctx);-    Env* env = exp.environment();--    // try to use generic function-    if (env->has("@warn[f]")) {--      Definition* def = static_cast<Definition*>((*env)["@warn[f]"]);-      // Block*          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;-      union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);-      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);-      sass_delete_value(c_args);-      sass_delete_value(c_val);-      return 0;--    }--    std::string result(unquote(message->perform(&to_string)));-    Backtrace top(backtrace(), w->pstate(), "");-    std::cerr << "WARNING: " << result;-    std::cerr << top.to_string(true);-    std::cerr << std::endl << std::endl;-    return 0;-  }--  Expression* Eval::operator()(Error* e)-  {-    Expression* message = e->message()->perform(this);-    To_String to_string(&ctx);-    Env* env = exp.environment();--    // try to use generic function-    if (env->has("@error[f]")) {--      Definition* def = static_cast<Definition*>((*env)["@error[f]"]);-      // Block*          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;-      union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);-      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);-      sass_delete_value(c_args);-      sass_delete_value(c_val);-      return 0;--    }--    std::string result(unquote(message->perform(&to_string)));-    error(result, e->pstate());-    return 0;-  }--  Expression* Eval::operator()(Debug* d)-  {-    Expression* message = d->value()->perform(this);-    To_String to_string(&ctx, false, true);-    Env* env = exp.environment();--    // try to use generic function-    if (env->has("@debug[f]")) {--      Definition* def = static_cast<Definition*>((*env)["@debug[f]"]);-      // Block*          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;-      union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);-      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);-      sass_delete_value(c_args);-      sass_delete_value(c_val);-      return 0;--    }--    std::string cwd(ctx.cwd());-    std::string result(unquote(message->perform(&to_string)));-    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));--    std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;-    std::cerr << std::endl;-    return 0;-  }--  Expression* Eval::operator()(List* l)-  {-    if (l->is_expanded()) return l;-    List* ll = SASS_MEMORY_NEW(ctx.mem, List,-                               l->pstate(),-                               l->length(),-                               l->separator(),-                               l->is_arglist());-    for (size_t i = 0, L = l->length(); i < L; ++i) {-      *ll << (*l)[i]->perform(this);-    }-    ll->is_expanded(true);-    return ll;-  }--  Expression* Eval::operator()(Map* m)-  {-    if (m->is_expanded()) return m;--    // make sure we're not starting with duplicate keys.-    // the duplicate key state will have been set in the parser phase.-    if (m->has_duplicate_key()) {-      To_String to_string(&ctx);-      error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());-    }--    Map* mm = SASS_MEMORY_NEW(ctx.mem, Map,-                                m->pstate(),-                                m->length());-    for (auto key : m->keys()) {-      Expression* ex_key = key->perform(this);-      Expression* ex_val = m->at(key)->perform(this);-      *mm << std::make_pair(ex_key, ex_val);-    }--    // check the evaluated keys aren't duplicates.-    if (mm->has_duplicate_key()) {-      To_String to_string(&ctx);-      error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", mm->pstate());-    }--    mm->is_expanded(true);-    return mm;-  }--  Expression* Eval::operator()(Binary_Expression* b)-  {-    enum Sass_OP op_type = b->type();-    // don't eval delayed expressions (the '/' when used as a separator)-    if (op_type == Sass_OP::DIV && b->is_delayed()) return b;-    b->is_delayed(false);-    // if one of the operands is a '/' then make sure it's evaluated-    Expression* lhs = b->left()->perform(this);-    lhs->is_delayed(false);-    while (typeid(*lhs) == typeid(Binary_Expression)) {-      Binary_Expression* lhs_ex = static_cast<Binary_Expression*>(lhs);-      if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break;-      lhs = Eval::operator()(lhs_ex);-    }--    switch (op_type) {-      case Sass_OP::AND:-        return *lhs ? b->right()->perform(this) : lhs;-        break;--      case Sass_OP::OR:-        return *lhs ? lhs : b->right()->perform(this);-        break;--      default:-        break;-    }-    // not a logical connective, so go ahead and eval the rhs-    Expression* rhs = b->right()->perform(this);-    // maybe fully evaluate structure-    if (op_type == Sass_OP::EQ ||-        op_type == Sass_OP::NEQ ||-        op_type == Sass_OP::GT ||-        op_type == Sass_OP::GTE ||-        op_type == Sass_OP::LT ||-        op_type == Sass_OP::LTE)-    {-      rhs->is_expanded(false);-      rhs->set_delayed(false);-      rhs = rhs->perform(this);-    }-    else-    {-      // rhs->set_delayed(false);-      // rhs = rhs->perform(this);-    }--    // upgrade string to number if possible (issue #948)-    if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL) {-      if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {-        std::string value(str->value());-        const char* start = value.c_str();-        if (Prelexer::sequence < Prelexer::number >(start) != 0) {-          rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value());-          rhs->is_delayed(false); rhs = rhs->perform(this);-        }-      }-    }--    // see if it's a relational expression-    switch(op_type) {-      case Sass_OP::EQ:  return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs));-      case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs));-      case Sass_OP::GT:  return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs) && !eq(lhs, rhs));-      case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs));-      case Sass_OP::LT:  return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs));-      case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs) || eq(lhs, rhs));--      default:                     break;-    }---    Expression::Concrete_Type l_type = lhs->concrete_type();-    Expression::Concrete_Type r_type = rhs->concrete_type();--    // Is one of the operands an interpolant?-    String_Schema* s1 = dynamic_cast<String_Schema*>(b->left());-    String_Schema* s2 = dynamic_cast<String_Schema*>(b->right());--    if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants())) {-      std::string sep;-      switch (op_type) {-        case Sass_OP::SUB: sep = "-"; break;-        case Sass_OP::DIV: sep = "/"; break;-        case Sass_OP::ADD: sep = "+"; break;-        case Sass_OP::MUL: sep = "*"; break;-        default:                      break;-      }--      // If possible upgrade LHS to a number-      if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB) {-        if (String_Constant* str = dynamic_cast<String_Constant*>(lhs)) {-          std::string value(str->value());-          const char* start = value.c_str();-          if (Prelexer::sequence < Prelexer::number >(start) != 0) {-            lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value());-            lhs->is_delayed(false); lhs = lhs->perform(this);-          }-        }-        if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {-          std::string value(str->value());-          const char* start = value.c_str();-          if (Prelexer::sequence < Prelexer::number >(start) != 0) {-            rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value());-            rhs->is_delayed(false); rhs = rhs->perform(this);-          }-        }-      }--      To_Value to_value(ctx, ctx.mem);-      Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));-      Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));-      Expression::Concrete_Type l_type = lhs->concrete_type();-      Expression::Concrete_Type r_type = rhs->concrete_type();--      if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {-        return SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(),-          v_l->to_string() + " " + sep + " " + v_r->to_string());-      }-    }--    // ToDo: throw error in op functions-    // ToDo: then catch and re-throw them-    ParserState pstate(b->pstate());-    int precision = (int)ctx.c_options->precision;-    bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED;-    if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {-      const Number* l_n = dynamic_cast<const Number*>(lhs);-      const Number* r_n = dynamic_cast<const Number*>(rhs);-      return op_numbers(ctx.mem, op_type, *l_n, *r_n, compressed, precision, &pstate);-    }-    if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {-      const Number* l_n = dynamic_cast<const Number*>(lhs);-      const Color* r_c = dynamic_cast<const Color*>(rhs);-      return op_number_color(ctx.mem, op_type, *l_n, *r_c, compressed, precision, &pstate);-    }-    if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {-      const Color* l_c = dynamic_cast<const Color*>(lhs);-      const Number* r_n = dynamic_cast<const Number*>(rhs);-      return op_color_number(ctx.mem, op_type, *l_c, *r_n, compressed, precision, &pstate);-    }-    if (l_type == Expression::COLOR && r_type == Expression::COLOR) {-      const Color* l_c = dynamic_cast<const Color*>(lhs);-      const Color* r_c = dynamic_cast<const Color*>(rhs);-      return op_colors(ctx.mem, op_type, *l_c, *r_c, compressed, precision, &pstate);-    }--    To_Value to_value(ctx, ctx.mem);-    Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));-    Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));-    Value* ex = op_strings(ctx.mem, op_type, *v_l, *v_r, compressed, precision, &pstate);-    if (String_Constant* str = dynamic_cast<String_Constant*>(ex))-    {-      if (str->concrete_type() != Expression::STRING) return ex;-      String_Constant* lstr = dynamic_cast<String_Constant*>(lhs);-      String_Constant* rstr = dynamic_cast<String_Constant*>(rhs);-      if (String_Constant* org = lstr ? lstr : rstr)-      { str->quote_mark(org->quote_mark()); }-    }-    return ex;--  }--  Expression* Eval::operator()(Unary_Expression* u)-  {-    Expression* operand = u->operand()->perform(this);-    if (u->type() == Unary_Expression::NOT) {-      Boolean* result = SASS_MEMORY_NEW(ctx.mem, Boolean, u->pstate(), (bool)*operand);-      result->value(!result->value());-      return result;-    }-    else if (operand->concrete_type() == Expression::NUMBER) {-      Number* result = SASS_MEMORY_NEW(ctx.mem, Number, *static_cast<Number*>(operand));-      result->value(u->type() == Unary_Expression::MINUS-                    ? -result->value()-                    :  result->value());-      return result;-    }-    else {-      To_String to_string(&ctx);-      // Special cases: +/- variables which evaluate to null ouput just +/-,-      // but +/- null itself outputs the string-      if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) {-        u->operand(SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), ""));-      }-      else u->operand(operand);-      String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted,-                                                  u->pstate(),-                                                  u->perform(&to_string));-      return result;-    }-    // unreachable-    return u;-  }--  Expression* Eval::operator()(Function_Call* c)-  {-    if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {-        std::ostringstream stm;-        stm << "Stack depth exceeded max of " << Constants::MaxCallStack;-        error(stm.str(), c->pstate(), backtrace());-    }-    std::string name(Util::normalize_underscores(c->name()));-    std::string full_name(name + "[f]");-    Arguments* args = c->arguments();-    if (full_name != "if[f]") {-      args = static_cast<Arguments*>(args->perform(this));-    }--    Env* env = environment();-    if (!env->has(full_name)) {-      if (!env->has("*[f]")) {-        // just pass it through as a literal-        Function_Call* lit = SASS_MEMORY_NEW(ctx.mem, Function_Call,-                                             c->pstate(),-                                             c->name(),-                                             args);-        To_String to_string(&ctx);-        if (args->has_named_arguments()) {-          error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());-        }-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted,-                               c->pstate(),-                               lit->perform(&to_string));-      } else {-        // call generic function-        full_name = "*[f]";-      }-    }--    Definition* def = static_cast<Definition*>((*env)[full_name]);--    if (def->is_overload_stub()) {-      std::stringstream ss;-      ss << full_name-         << args->length();-      full_name = ss.str();-      std::string resolved_name(full_name);-      if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());-      def = static_cast<Definition*>((*env)[resolved_name]);-    }--    Expression*     result = c;-    Block*          body   = def->block();-    Native_Function func   = def->native_function();-    Sass_Function_Entry c_function = def->c_function();--    Parameters* params = def->parameters();-    Env fn_env(def->environment());-    exp.env_stack.push_back(&fn_env);--    if (func || body) {-      bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);-      Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");-      exp.backtrace_stack.push_back(&here);-      // if it's user-defined, eval the body-      if (body) result = body->perform(this);-      // if it's native, invoke the underlying CPP function-      else result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace());-      if (!result) error(std::string("Function ") + c->name() + " did not return a value", c->pstate());-      exp.backtrace_stack.pop_back();-    }--    // else if it's a user-defined c function-    // convert call into C-API compatible form-    else if (c_function) {-      Sass_Function_Fn c_func = sass_function_get_function(c_function);-      if (full_name == "*[f]") {-        String_Quoted *str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, c->pstate(), c->name());-        Arguments* new_args = SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate());-        *new_args << SASS_MEMORY_NEW(ctx.mem, Argument, c->pstate(), str);-        *new_args += args;-        args = new_args;-      }--      // populates env with default values for params-      std::string ff(c->name());-      bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);--      Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");-      exp.backtrace_stack.push_back(&here);--      To_C to_c;-      union Sass_Value* c_args = sass_make_list(params[0].length(), SASS_COMMA);-      for(size_t i = 0; i < params[0].length(); i++) {-        std::string key = params[0][i]->name();-        AST_Node* node = fn_env.get_local(key);-        Expression* arg = static_cast<Expression*>(node);-        sass_list_set_value(c_args, i, arg->perform(&to_c));-      }-      union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_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(), backtrace());-      } 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(), backtrace());-      }-      result = cval_to_astnode(ctx.mem, c_val, ctx, backtrace(), c->pstate());--      exp.backtrace_stack.pop_back();-      sass_delete_value(c_args);-      if (c_val != c_args)-        sass_delete_value(c_val);-    }--    // link back to function definition-    // only do this for custom functions-    if (result->pstate().file == std::string::npos)-      result->pstate(c->pstate());--    result->is_delayed(result->concrete_type() == Expression::STRING);-    if (!result->is_delayed()) result = result->perform(this);-    exp.env_stack.pop_back();-    return result;-  }--  Expression* Eval::operator()(Function_Call_Schema* s)-  {-    Expression* evaluated_name = s->name()->perform(this);-    Expression* evaluated_args = s->arguments()->perform(this);-    String_Schema* ss = SASS_MEMORY_NEW(ctx.mem, String_Schema, s->pstate(), 2);-    (*ss) << evaluated_name << evaluated_args;-    return ss->perform(this);-  }--  Expression* Eval::operator()(Variable* v)-  {-    To_String to_string(&ctx);-    std::string name(v->name());-    Expression* value = 0;-    Env* env = environment();-    if (env->has(name)) value = static_cast<Expression*>((*env)[name]);-    else error("Undefined variable: \"" + v->name() + "\".", v->pstate());-    // std::cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << std::endl;-    if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value();--    // behave according to as ruby sass (add leading zero)-    if (value->concrete_type() == Expression::NUMBER) {-      value = SASS_MEMORY_NEW(ctx.mem, Number, *static_cast<Number*>(value));-      static_cast<Number*>(value)->zero(true);-    }-    else if (value->concrete_type() == Expression::STRING) {-      if (auto str = dynamic_cast<String_Quoted*>(value)) {-        value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *str);-      } else if (auto str = dynamic_cast<String_Constant*>(value)) {-        value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->perform(&to_string));-      }-    }-    else if (value->concrete_type() == Expression::LIST) {-      value = SASS_MEMORY_NEW(ctx.mem, List, *static_cast<List*>(value));-    }-    else if (value->concrete_type() == Expression::MAP) {-      value = SASS_MEMORY_NEW(ctx.mem, Map, *static_cast<Map*>(value));-    }-    else if (value->concrete_type() == Expression::BOOLEAN) {-      value = SASS_MEMORY_NEW(ctx.mem, Boolean, *static_cast<Boolean*>(value));-    }-    else if (value->concrete_type() == Expression::COLOR) {-      value = SASS_MEMORY_NEW(ctx.mem, Color, *static_cast<Color*>(value));-    }-    else if (value->concrete_type() == Expression::NULL_VAL) {-      value = SASS_MEMORY_NEW(ctx.mem, Null, value->pstate());-    }-    else if (value->concrete_type() == Expression::SELECTOR) {-      value = value->perform(this); // ->perform(&listize);-    }--    // std::cerr << "\ttype is now: " << typeid(*value).name() << std::endl << std::endl;-    return value;-  }--  Expression* Eval::operator()(Textual* t)-  {-    using Prelexer::number;-    Expression* result = 0;-    bool zero = !( t->value().substr(0, 1) == "." ||-                   t->value().substr(0, 2) == "-." );--    const std::string& text = t->value();-    size_t num_pos = text.find_first_not_of(" \n\r\t");-    if (num_pos == std::string::npos) num_pos = text.length();-    size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);-    if (unit_pos == std::string::npos) unit_pos = text.length();-    const std::string& num = text.substr(num_pos, unit_pos - num_pos);--    switch (t->type())-    {-      case Textual::NUMBER:-        result = SASS_MEMORY_NEW(ctx.mem, Number,-                                 t->pstate(),-                                 sass_atof(num.c_str()),-                                 "",-                                 zero);-        break;-      case Textual::PERCENTAGE:-        result = SASS_MEMORY_NEW(ctx.mem, Number,-                                 t->pstate(),-                                 sass_atof(num.c_str()),-                                 "%",-                                 zero);-        break;-      case Textual::DIMENSION:-        result = SASS_MEMORY_NEW(ctx.mem, Number,-                                 t->pstate(),-                                 sass_atof(num.c_str()),-                                 Token(number(text.c_str())),-                                 zero);-        break;-      case Textual::HEX: {-        if (t->value().substr(0, 1) != "#") {-          result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, t->pstate(), t->value());-          break;-        }-        std::string hext(t->value().substr(1)); // chop off the '#'-        if (hext.length() == 6) {-          std::string r(hext.substr(0,2));-          std::string g(hext.substr(2,2));-          std::string b(hext.substr(4,2));-          result = SASS_MEMORY_NEW(ctx.mem, Color,-                                   t->pstate(),-                                   static_cast<double>(strtol(r.c_str(), NULL, 16)),-                                   static_cast<double>(strtol(g.c_str(), NULL, 16)),-                                   static_cast<double>(strtol(b.c_str(), NULL, 16)),-                                   1, true,-                                   t->value());-        }-        else {-          result = SASS_MEMORY_NEW(ctx.mem, Color,-                                   t->pstate(),-                                   static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),-                                   static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),-                                   static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),-                                   1, false,-                                   t->value());-        }-      } break;-    }-    return result;-  }--  Expression* Eval::operator()(Number* n)-  {-    return n;-  }--  Expression* Eval::operator()(Boolean* b)-  {-    return b;-  }--  char is_quoted(std::string str)-  {-    size_t len = str.length();-    if (len < 2) return 0;-    if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) {-      return str[0];-    }-    else {-      return 0;-    }-  }--  std::string Eval::interpolation(Expression* s, bool into_quotes) {-    Env* env = environment();-    if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) {-      if (str_quoted->quote_mark()) {-        if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) {-          return evacuate_escapes(str_quoted->value());-        } else {-          return string_escape(quote(str_quoted->value(), str_quoted->quote_mark()));-        }-      } else {-        return evacuate_escapes(str_quoted->value());-      }-    } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {-      if (into_quotes && !str_constant->is_interpolant()) return str_constant->value();-      return evacuate_escapes(str_constant->value());-    } else if (dynamic_cast<Parent_Selector*>(s)) {-      To_String to_string(&ctx);-      Expression* sel = s->perform(this);-      return evacuate_quotes(sel ? sel->perform(&to_string) : "");--    } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) {-      // To_String to_string(&ctx);-      // return evacuate_quotes(str_schema->perform(&to_string));--      std::string res = "";-      for(auto i : str_schema->elements())-        res += (interpolation(i));-      //ToDo: do this in one step-      auto esc = evacuate_escapes(res);-      auto unq = unquote(esc);-      if (unq == esc) {-        return string_to_output(res);-      } else {-        return evacuate_quotes(unq);-      }-    } else if (List* list = dynamic_cast<List*>(s)) {-      std::string acc = ""; // ToDo: different output styles-      std::string sep = list->separator() == SASS_COMMA ? "," : " ";-      if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " ";-      bool initial = false;-      for(auto item : list->elements()) {-        if (item->concrete_type() != Expression::NULL_VAL) {-          if (initial) acc += sep;-          acc += interpolation(item);-          initial = true;-        }-      }-      return evacuate_quotes(acc);-    } else if (Variable* var = dynamic_cast<Variable*>(s)) {-      std::string name(var->name());-      if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate());-      Expression* value = static_cast<Expression*>((*env)[name]);-      return evacuate_quotes(interpolation(value));-    } else if (dynamic_cast<Binary_Expression*>(s)) {-      Expression* ex = s->perform(this);-      // avoid recursive calls if same object gets returned-      // since we will call interpolate again for the result-      if (ex == s) {-        To_String to_string(&ctx);-        return evacuate_quotes(s->perform(&to_string));-      }-      return evacuate_quotes(interpolation(ex));-    } else if (dynamic_cast<Function_Call*>(s)) {-      Expression* ex = s->perform(this);-      return evacuate_quotes(unquote(interpolation(ex)));-    } else if (dynamic_cast<Unary_Expression*>(s)) {-      Expression* ex = s->perform(this);-      return evacuate_quotes(interpolation(ex));-    } else if (dynamic_cast<Map*>(s)) {-      To_String to_string(&ctx);-      std::string dbg(s->perform(&to_string));-      error(dbg + " isn't a valid CSS value.", s->pstate());-      return dbg;-    } else {-      To_String to_string(&ctx);-      return evacuate_quotes(s->perform(&to_string));-    }-  }--  Expression* Eval::operator()(String_Schema* s)-  {-    std::string acc;-    bool into_quotes = false;-    size_t L = s->length();-    if (L > 1) {-      if (String_Constant* l = dynamic_cast<String_Constant*>((*s)[0])) {-        if (String_Constant* r = dynamic_cast<String_Constant*>((*s)[L - 1])) {-          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;-        }-      }-    }-    for (size_t i = 0; i < L; ++i) {-      // really a very special fix, but this is the logic I got from-      // analyzing the ruby sass behavior and it actually seems to work-      // https://github.com/sass/libsass/issues/1333-      if (i == 0 && L > 1 && dynamic_cast<Function_Call*>((*s)[i])) {-        Expression* ex = (*s)[i]->perform(this);-        if (auto sq = dynamic_cast<String_Quoted*>(ex)) {-          if (sq->is_delayed() && ! s->has_interpolants()) {-            acc += string_escape(quote(sq->value(), sq->quote_mark()));-          } else {-            acc += interpolation((*s)[i], into_quotes);-          }-        } else if (ex) {-          acc += interpolation((*s)[i], into_quotes);-        }-      } else if ((*s)[i]) {-        acc += interpolation((*s)[i], into_quotes);-      }-    }-    String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), acc);-    if (!str->quote_mark()) {-      str->value(string_unescape(str->value()));-    } else if (str->quote_mark()) {-      str->quote_mark('*');-    }-    str->is_delayed(true);-    return str;-  }--  Expression* Eval::operator()(String_Constant* s)-  {-    if (!s->is_delayed() && name_to_color(s->value())) {-      Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *name_to_color(s->value()));-      c->pstate(s->pstate());-      c->disp(s->value());-      return c;-    }-    return s;-  }--  Expression* Eval::operator()(String_Quoted* s)-  {-    return s;-  }--  Expression* Eval::operator()(Supports_Operator* c)-  {-    Expression* left = c->left()->perform(this);-    Expression* right = c->right()->perform(this);-    Supports_Operator* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Operator,-                                 c->pstate(),-                                 static_cast<Supports_Condition*>(left),-                                 static_cast<Supports_Condition*>(right),-                                 c->operand());-    return cc;-  }--  Expression* Eval::operator()(Supports_Negation* c)-  {-    Expression* condition = c->condition()->perform(this);-    Supports_Negation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Negation,-                                 c->pstate(),-                                 static_cast<Supports_Condition*>(condition));-    return cc;-  }--  Expression* Eval::operator()(Supports_Declaration* c)-  {-    Expression* feature = c->feature()->perform(this);-    Expression* value = c->value()->perform(this);-    Supports_Declaration* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,-                              c->pstate(),-                              feature,-                              value);-    return cc;-  }--  Expression* Eval::operator()(Supports_Interpolation* c)-  {-    Expression* value = c->value()->perform(this);-    Supports_Interpolation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation,-                            c->pstate(),-                            value);-    return cc;-  }--  Expression* Eval::operator()(At_Root_Expression* e)-  {-    Expression* feature = e->feature();-    feature = (feature ? feature->perform(this) : 0);-    Expression* value = e->value();-    value = (value ? value->perform(this) : 0);-    Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression,-                                     e->pstate(),-                                     static_cast<String*>(feature),-                                     value,-                                     e->is_interpolated());-    return ee;-  }--  Expression* Eval::operator()(Media_Query* q)-  {-    To_String to_string(&ctx);-    String* t = q->media_type();-    t = static_cast<String*>(t ? t->perform(this) : 0);-    Media_Query* qq = SASS_MEMORY_NEW(ctx.mem, Media_Query,-                                      q->pstate(),-                                      t,-                                      q->length(),-                                      q->is_negated(),-                                      q->is_restricted());-    for (size_t i = 0, L = q->length(); i < L; ++i) {-      *qq << static_cast<Media_Query_Expression*>((*q)[i]->perform(this));-    }-    return qq;-  }--  Expression* Eval::operator()(Media_Query_Expression* e)-  {-    Expression* feature = e->feature();-    feature = (feature ? feature->perform(this) : 0);-    if (feature && dynamic_cast<String_Quoted*>(feature)) {-      feature = SASS_MEMORY_NEW(ctx.mem, String_Quoted,-                                  feature->pstate(),-                                  dynamic_cast<String_Quoted*>(feature)->value());-    }-    Expression* value = e->value();-    value = (value ? value->perform(this) : 0);-    if (value && dynamic_cast<String_Quoted*>(value)) {-      value = SASS_MEMORY_NEW(ctx.mem, String_Quoted,-                                value->pstate(),-                                dynamic_cast<String_Quoted*>(value)->value());-    }-    return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression,-                           e->pstate(),-                           feature,-                           value,-                           e->is_interpolated());-  }--  Expression* Eval::operator()(Null* n)-  {-    return n;-  }--  Expression* Eval::operator()(Argument* a)-  {-    Expression* val = a->value();-    val->is_delayed(false);-    val = val->perform(this);-    val->is_delayed(false);--    bool is_rest_argument = a->is_rest_argument();-    bool is_keyword_argument = a->is_keyword_argument();--    if (a->is_rest_argument()) {-      if (val->concrete_type() == Expression::MAP) {-        is_rest_argument = false;-        is_keyword_argument = true;-      }-      else if(val->concrete_type() != Expression::LIST) {-        List* wrapper = SASS_MEMORY_NEW(ctx.mem, List,-                                        val->pstate(),-                                        0,-                                        SASS_COMMA,-                                        true);-        *wrapper << val;-        val = wrapper;-      }-    }-    return SASS_MEMORY_NEW(ctx.mem, Argument,-                           a->pstate(),-                           val,-                           a->name(),-                           is_rest_argument,-                           is_keyword_argument);-  }--  Expression* Eval::operator()(Arguments* a)-  {-    Arguments* aa = SASS_MEMORY_NEW(ctx.mem, Arguments, a->pstate());-    for (size_t i = 0, L = a->length(); i < L; ++i) {-      *aa << static_cast<Argument*>((*a)[i]->perform(this));-    }-    return aa;-  }--  Expression* Eval::operator()(Comment* c)-  {-    return 0;-  }--  inline Expression* Eval::fallback_impl(AST_Node* n)-  {-    return static_cast<Expression*>(n);-  }--  // All the binary helpers.--  bool Eval::eq(Expression* lhs, Expression* rhs)-  {-    // use compare operator from ast node-    return lhs && rhs && *lhs == *rhs;-  }--  bool Eval::lt(Expression* lhs, Expression* rhs)-  {-    Number* l = dynamic_cast<Number*>(lhs);-    Number* r = dynamic_cast<Number*>(rhs);-    if (!l) error("may only compare numbers", lhs->pstate());-    if (!r) error("may only compare numbers", rhs->pstate());-    // use compare operator from ast node-    return *l < *r;-  }--  Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, bool compressed, int precision, ParserState* pstate)-  {-    double lv = l.value();-    double rv = r.value();-    if (op == Sass_OP::DIV && !rv) {-      return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), "Infinity");-    }-    if (op == Sass_OP::MOD && !rv) {-      error("division by zero", pstate ? *pstate : r.pstate());-    }--    Number tmp(r);-    bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;-    tmp.normalize(l.find_convertible_unit(), strict);-    std::string l_unit(l.unit());-    std::string r_unit(tmp.unit());-    if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() &&-        (op == Sass_OP::ADD || op == Sass_OP::SUB)) {-      error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", pstate ? *pstate : r.pstate());-    }-    Number* v = SASS_MEMORY_NEW(mem, Number, l);-    v->pstate(pstate ? *pstate : l.pstate());-    if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {-      v->numerator_units() = r.numerator_units();-      v->denominator_units() = r.denominator_units();-    }--    if (op == Sass_OP::MUL) {-      v->value(ops[op](lv, rv));-      for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {-        v->numerator_units().push_back(r.numerator_units()[i]);-      }-      for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {-        v->denominator_units().push_back(r.denominator_units()[i]);-      }-    }-    else if (op == Sass_OP::DIV) {-      v->value(ops[op](lv, rv));-      for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {-        v->denominator_units().push_back(r.numerator_units()[i]);-      }-      for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {-        v->numerator_units().push_back(r.denominator_units()[i]);-      }-    } else {-      v->value(ops[op](lv, tmp.value()));-    }-    v->normalize();-    return v;-  }--  Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, bool compressed, int precision, ParserState* pstate)-  {-    Color r(rh);-    r.disp("");-    double lv = l.value();-    switch (op) {-      case Sass_OP::ADD:-      case Sass_OP::MUL: {-        return SASS_MEMORY_NEW(mem, Color,-                               pstate ? *pstate : l.pstate(),-                               ops[op](lv, r.r()),-                               ops[op](lv, r.g()),-                               ops[op](lv, r.b()),-                               r.a());-      } break;-      case Sass_OP::SUB:-      case Sass_OP::DIV: {-        std::string sep(op == Sass_OP::SUB ? "-" : "/");-        std::string color(r.to_string(compressed||!r.sixtuplet(), precision));-        return SASS_MEMORY_NEW(mem, String_Quoted,-                               pstate ? *pstate : l.pstate(),-                               l.to_string(compressed, precision)-                               + sep-                               + color);-      } break;-      case Sass_OP::MOD: {-        error("cannot divide a number by a color", pstate ? *pstate : r.pstate());-      } break;-      default: break; // caller should ensure that we don't get here-    }-    // unreachable-    return SASS_MEMORY_NEW(mem, Color, rh);-  }--  Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, bool compressed, int precision, ParserState* pstate)-  {-    double rv = r.value();-    if (op == Sass_OP::DIV && !rv) error("division by zero", pstate ? *pstate : r.pstate());-    return SASS_MEMORY_NEW(mem, Color,-                           pstate ? *pstate : l.pstate(),-                           ops[op](l.r(), rv),-                           ops[op](l.g(), rv),-                           ops[op](l.b(), rv),-                           l.a());-  }--  Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, bool compressed, int precision, ParserState* pstate)-  {-    if (l.a() != r.a()) {-      error("alpha channels must be equal when combining colors", pstate ? *pstate : r.pstate());-    }-    if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {-      error("division by zero", pstate ? *pstate : r.pstate());-    }-    return SASS_MEMORY_NEW(mem, Color,-                           pstate ? *pstate : l.pstate(),-                           ops[op](l.r(), r.r()),-                           ops[op](l.g(), r.g()),-                           ops[op](l.b(), r.b()),-                           l.a());-  }--  Value* Eval::op_strings(Memory_Manager& mem, enum Sass_OP op, Value& lhs, Value& rhs, bool compressed, int precision, ParserState* pstate)-  {-    Expression::Concrete_Type ltype = lhs.concrete_type();-    Expression::Concrete_Type rtype = rhs.concrete_type();--    String_Quoted* lqstr = dynamic_cast<String_Quoted*>(&lhs);-    String_Quoted* rqstr = dynamic_cast<String_Quoted*>(&rhs);--    std::string lstr(lqstr ? lqstr->value() : lhs.to_string(compressed, precision));-    std::string rstr(rqstr ? rqstr->value() : rhs.to_string(compressed, precision));--    bool l_str_quoted = ((Sass::String*)&lhs) && ((Sass::String*)&lhs)->sass_fix_1291();-    bool r_str_quoted = ((Sass::String*)&rhs) && ((Sass::String*)&rhs)->sass_fix_1291();-    bool l_str_color = ltype == Expression::STRING && name_to_color(lstr) && !l_str_quoted;-    bool r_str_color = rtype == Expression::STRING && name_to_color(rstr) && !r_str_quoted;--    if (l_str_color && r_str_color) {-      const Color* c_l = name_to_color(lstr);-      const Color* c_r = name_to_color(rstr);-      return op_colors(mem, op,*c_l, *c_r, compressed, precision);-    }-    else if (l_str_color && rtype == Expression::COLOR) {-      const Color* c_l = name_to_color(lstr);-      const Color* c_r = dynamic_cast<const Color*>(&rhs);-      return op_colors(mem, op, *c_l, *c_r, compressed, precision);-    }-    else if (ltype == Expression::COLOR && r_str_color) {-      const Color* c_l = dynamic_cast<const Color*>(&lhs);-      const Color* c_r = name_to_color(rstr);-      return op_colors(mem, op, *c_l, *c_r, compressed, precision);-    }-    else if (l_str_color && rtype == Expression::NUMBER) {-      const Color* c_l = name_to_color(lstr);-      const Number* n_r = dynamic_cast<const Number*>(&rhs);-      return op_color_number(mem, op, *c_l, *n_r, compressed, precision);-    }-    else if (ltype == Expression::NUMBER && r_str_color) {-      const Number* n_l = dynamic_cast<const Number*>(&lhs);-      const Color* c_r = name_to_color(rstr);-      return op_number_color(mem, op, *n_l, *c_r, compressed, precision);-    }-    if (op == Sass_OP::MUL) error("invalid operands for multiplication", lhs.pstate());-    if (op == Sass_OP::MOD) error("invalid operands for modulo", lhs.pstate());-    std::string sep;-    switch (op) {-      case Sass_OP::SUB: sep = "-"; break;-      case Sass_OP::DIV: sep = "/"; break;-      default:                      break;-    }-    if (ltype == Expression::NULL_VAL) error("Invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs.pstate());-    if (rtype == Expression::NULL_VAL) error("Invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", rhs.pstate());--    if (ltype == Expression::NUMBER && sep == "/" && rtype == Expression::STRING)-    {-      return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(),-        lhs.to_string() + sep + rhs.to_string());-    }--    if ( (ltype == Expression::STRING || sep == "") &&-         (sep != "/" || !rqstr || !rqstr->quote_mark())-    ) {-      char quote_mark = 0;-      std::string unq(unquote(lstr + sep + rstr, &quote_mark, true));-      if (quote_mark && quote_mark != '*') {-        return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), quote_mark + unq + quote_mark);-      }-      return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + sep + rstr);-    }-    return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + quote(rstr));-  }--  Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate)-  {-    using std::strlen;-    using std::strcpy;-    Expression* e = 0;-    switch (sass_value_get_tag(v)) {-      case SASS_BOOLEAN: {-        e = SASS_MEMORY_NEW(mem, Boolean, pstate, !!sass_boolean_get_value(v));-      } break;-      case SASS_NUMBER: {-        e = SASS_MEMORY_NEW(mem, Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));-      } break;-      case SASS_COLOR: {-        e = SASS_MEMORY_NEW(mem, 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(mem, String_Quoted, pstate, sass_string_get_value(v));-        else {-          e = SASS_MEMORY_NEW(mem, String_Constant, pstate, sass_string_get_value(v));-        }-      } break;-      case SASS_LIST: {-        List* l = SASS_MEMORY_NEW(mem, 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 << cval_to_astnode(mem, sass_list_get_value(v, i), ctx, backtrace, pstate);-        }-        e = l;-      } break;-      case SASS_MAP: {-        Map* m = SASS_MEMORY_NEW(mem, Map, pstate);-        for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {-          *m << std::make_pair(-            cval_to_astnode(mem, sass_map_get_key(v, i), ctx, backtrace, pstate),-            cval_to_astnode(mem, sass_map_get_value(v, i), ctx, backtrace, pstate));-        }-        e = m;-      } break;-      case SASS_NULL: {-        e = SASS_MEMORY_NEW(mem, Null, pstate);-      } break;-      case SASS_ERROR: {-        error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace);-      } break;-      case SASS_WARNING: {-        error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);-      } break;-    }-    return e;-  }--  Selector_List* Eval::operator()(Selector_List* s)-  {-    std::vector<Selector_List*> rv;-    Selector_List* sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate());-    sl->media_block(s->media_block());-    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 << (*rv[i])[round];-          abort = false;-        }-      }-      if (abort) {-        round = std::string::npos;-      } else {-        ++ round;-      }--    }-    return sl;-  }---  Selector_List* Eval::operator()(Complex_Selector* s)-  {-    return s->parentize(selector(), ctx);--  }--  Attribute_Selector* Eval::operator()(Attribute_Selector* s)-  {-    String* attr = s->value();-    if (attr) { attr = static_cast<String*>(attr->perform(this)); }-    Attribute_Selector* ss = SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, *s);-    ss->value(attr);-    return ss;-  }--  Selector_List* Eval::operator()(Selector_Schema* s)-  {-    To_String to_string;-    // the parser will look for a brace to end the selector-    std::string result_str(s->contents()->perform(this)->perform(&to_string));-    result_str = unquote(Util::rtrim(result_str)) + "{";-    Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());-    return operator()(p.parse_selector_list(exp.block_stack.back()->is_root()));-  }--  Expression* Eval::operator()(Parent_Selector* p)-  {-    Selector_List* pr = selector();-    if (pr) {-      exp.selector_stack.pop_back();-      pr = operator()(pr);-      exp.selector_stack.push_back(pr);-      return pr;-    } else {-      return SASS_MEMORY_NEW(ctx.mem, Null, p->pstate());+#include "sass.hpp"+#include <cstdlib>+#include <cmath>+#include <iostream>+#include <sstream>+#include <iomanip>+#include <typeinfo>++#include "file.hpp"+#include "eval.hpp"+#include "ast.hpp"+#include "bind.hpp"+#include "util.hpp"+#include "inspect.hpp"+#include "environment.hpp"+#include "position.hpp"+#include "sass/values.h"+#include "to_value.hpp"+#include "to_c.hpp"+#include "context.hpp"+#include "backtrace.hpp"+#include "lexer.hpp"+#include "prelexer.hpp"+#include "parser.hpp"+#include "expand.hpp"+#include "color_maps.hpp"++namespace Sass {++  inline double add(double x, double y) { return x + y; }+  inline double sub(double x, double y) { return x - y; }+  inline double mul(double x, double y) { return x * y; }+  inline double div(double x, double y) { return x / y; } // x/0 checked by caller+  inline double mod(double x, double y) { // x/0 checked by caller+    if ((x > 0 && y < 0) || (x < 0 && y > 0)) {+      double ret = std::fmod(x, y);+      return ret ? ret + y : ret;+    } else {+      return std::fmod(x, y);+    }+  }+  typedef double (*bop)(double, double);+  bop ops[Sass_OP::NUM_OPS] = {+    0, 0, // and, or+    0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte+    add, sub, mul, div, mod+  };++  Eval::Eval(Expand& exp)+  : exp(exp),+    ctx(exp.ctx),+    force(false),+    is_in_comment(false)+  { }+  Eval::~Eval() { }++  Env* Eval::environment()+  {+    return exp.environment();+  }++  Selector_List_Obj Eval::selector()+  {+    return exp.selector();+  }++  Backtrace* Eval::backtrace()+  {+    return exp.backtrace();+  }++  Expression_Ptr Eval::operator()(Block_Ptr b)+  {+    Expression_Ptr val = 0;+    for (size_t i = 0, L = b->length(); i < L; ++i) {+      val = b->at(i)->perform(this);+      if (val) return val;+    }+    return val;+  }++  Expression_Ptr Eval::operator()(Assignment_Ptr a)+  {+    Env* env = exp.environment();+    std::string var(a->variable());+    if (a->is_global()) {+      if (a->is_default()) {+        if (env->has_global(var)) {+          Expression_Ptr e = SASS_MEMORY_CAST(Expression, env->get_global(var));+          if (!e || e->concrete_type() == Expression::NULL_VAL) {+            env->set_global(var, a->value()->perform(this));+          }+        }+        else {+          env->set_global(var, a->value()->perform(this));+        }+      }+      else {+        env->set_global(var, a->value()->perform(this));+      }+    }+    else if (a->is_default()) {+      if (env->has_lexical(var)) {+        auto cur = env;+        while (cur && cur->is_lexical()) {+          if (cur->has_local(var)) {+            if (AST_Node_Obj node = cur->get_local(var)) {+              Expression_Ptr e = SASS_MEMORY_CAST(Expression, node);+              if (!e || e->concrete_type() == Expression::NULL_VAL) {+                cur->set_local(var, a->value()->perform(this));+              }+            }+            else {+              throw std::runtime_error("Env not in sync");+            }+            return 0;+          }+          cur = cur->parent();+        }+        throw std::runtime_error("Env not in sync");+      }+      else if (env->has_global(var)) {+        if (AST_Node_Obj node = env->get_global(var)) {+          Expression_Ptr e = SASS_MEMORY_CAST(Expression, node);+          if (!e || e->concrete_type() == Expression::NULL_VAL) {+            env->set_global(var, a->value()->perform(this));+          }+        }+      }+      else if (env->is_lexical()) {+        env->set_local(var, a->value()->perform(this));+      }+      else {+        env->set_local(var, a->value()->perform(this));+      }+    }+    else {+      env->set_lexical(var, a->value()->perform(this));+    }+    return 0;+  }++  Expression_Ptr Eval::operator()(If_Ptr i)+  {+    Expression_Obj rv = 0;+    Env env(exp.environment());+    exp.env_stack.push_back(&env);+    Expression_Obj cond = i->predicate()->perform(this);+    if (!cond->is_false()) {+      rv = i->block()->perform(this);+    }+    else {+      Block_Obj alt = i->alternative();+      if (alt) rv = alt->perform(this);+    }+    exp.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)+  {+    std::string variable(f->variable());+    Expression_Obj low = f->lower_bound()->perform(this);+    if (low->concrete_type() != Expression::NUMBER) {+      throw Exception::TypeMismatch(*low, "integer");+    }+    Expression_Obj high = f->upper_bound()->perform(this);+    if (high->concrete_type() != Expression::NUMBER) {+      throw Exception::TypeMismatch(*high, "integer");+    }+    Number_Obj sass_start = SASS_MEMORY_CAST(Number, low);+    Number_Obj sass_end = SASS_MEMORY_CAST(Number, high);+    // check if units are valid for sequence+    if (sass_start->unit() != sass_end->unit()) {+      std::stringstream msg; msg << "Incompatible units: '"+        << sass_end->unit() << "' and '"+        << sass_start->unit() << "'.";+      error(msg.str(), low->pstate(), backtrace());+    }+    double start = sass_start->value();+    double end = sass_end->value();+    // only create iterator once in this environment+    Env env(environment(), true);+    exp.env_stack.push_back(&env);+    Number_Ptr it = SASS_MEMORY_NEW(Number, low->pstate(), start, sass_end->unit());+    env.set_local(variable, it);+    Block_Obj body = f->block();+    Expression_Ptr val = 0;+    if (start < end) {+      if (f->is_inclusive()) ++end;+      for (double i = start;+           i < end;+           ++i) {+        it->value(i);+        env.set_local(variable, it);+        val = body->perform(this);+        if (val) break;+      }+    } else {+      if (f->is_inclusive()) --end;+      for (double i = start;+           i > end;+           --i) {+        it->value(i);+        env.set_local(variable, it);+        val = body->perform(this);+        if (val) break;+      }+    }+    exp.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)+  {+    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;+    if (expr->concrete_type() == Expression::MAP) {+      map = SASS_MEMORY_CAST(Map, expr);+    }+    else if (Selector_List_Ptr ls = SASS_MEMORY_CAST(Selector_List, expr)) {+      Listize listize;+      Expression_Obj rv = ls->perform(&listize);+      list = dynamic_cast<List_Ptr>(&rv);+    }+    else if (expr->concrete_type() != Expression::LIST) {+      list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);+      list->append(expr);+    }+    else {+      list = SASS_MEMORY_CAST(List, expr);+    }++    Block_Obj body = e->block();+    Expression_Obj val = 0;++    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);+          variable->append(key);+          variable->append(value);+          env.set_local(variables[0], variable);+        } else {+          env.set_local(variables[0], &key);+          env.set_local(variables[1], &value);+        }++        val = body->perform(this);+        if (val) break;+      }+    }+    else {+      if (list->length() == 1 && SASS_MEMORY_CAST(Selector_List, list)) {+        list = SASS_MEMORY_CAST(List, list);+      }+      for (size_t i = 0, L = list->length(); i < L; ++i) {+        Expression_Ptr e = &list->at(i);+        // unwrap value if the expression is an argument+        if (Argument_Ptr arg = SASS_MEMORY_CAST_PTR(Argument, e)) e = &arg->value();+        // check if we got passed a list of args (investigate)+        if (List_Ptr scalars = SASS_MEMORY_CAST_PTR(List, e)) {+          if (variables.size() == 1) {+            Expression_Ptr 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()+                ? SASS_MEMORY_NEW(Null, expr->pstate())+                : &scalars->at(j);+              env.set_local(variables[j], res);+            }+          }+        } else {+          if (variables.size() > 0) {+            env.set_local(variables.at(0), e);+            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());+              env.set_local(variables[j], res);+            }+          }+        }+        val = body->perform(this);+        if (val) break;+      }+    }+    exp.env_stack.pop_back();+    return val.detach();+  }++  Expression_Ptr Eval::operator()(While_Ptr w)+  {+    Expression_Obj pred = w->predicate();+    Block_Obj body = w->block();+    Env env(environment(), true);+    exp.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();+        return val.detach();+      }+      cond = pred->perform(this);+    }+    exp.env_stack.pop_back();+    return 0;+  }++  Expression_Ptr Eval::operator()(Return_Ptr r)+  {+    return r->value()->perform(this);+  }++  Expression_Ptr Eval::operator()(Warning_Ptr w)+  {+    Sass_Output_Style outstyle = ctx.c_options.output_style;+    ctx.c_options.output_style = NESTED;+    Expression_Obj message = w->message()->perform(this);+    Env* env = exp.environment();++    // try to use generic function+    if (env->has("@warn[f]")) {++      Definition_Ptr def = SASS_MEMORY_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;+      union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+      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;+      sass_delete_value(c_args);+      sass_delete_value(c_val);+      return 0;++    }++    std::string result(unquote(message->to_sass()));+    Backtrace top(backtrace(), w->pstate(), "");+    std::cerr << "WARNING: " << result;+    std::cerr << top.to_string();+    std::cerr << std::endl << std::endl;+    ctx.c_options.output_style = outstyle;+    return 0;+  }++  Expression_Ptr Eval::operator()(Error_Ptr e)+  {+    Sass_Output_Style outstyle = ctx.c_options.output_style;+    ctx.c_options.output_style = NESTED;+    Expression_Obj message = e->message()->perform(this);+    Env* env = exp.environment();++    // try to use generic function+    if (env->has("@error[f]")) {++      Definition_Ptr def = SASS_MEMORY_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;+      union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+      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;+      sass_delete_value(c_args);+      sass_delete_value(c_val);+      return 0;++    }++    std::string result(unquote(message->to_sass()));+    ctx.c_options.output_style = outstyle;+    error(result, e->pstate());+    return 0;+  }++  Expression_Ptr Eval::operator()(Debug_Ptr d)+  {+    Sass_Output_Style outstyle = ctx.c_options.output_style;+    ctx.c_options.output_style = NESTED;+    Expression_Obj message = d->value()->perform(this);+    Env* env = exp.environment();++    // try to use generic function+    if (env->has("@debug[f]")) {++      Definition_Ptr def = SASS_MEMORY_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;+      union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+      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;+      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 output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));+    ctx.c_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)+  {+    // special case for unevaluated map+    if (l->separator() == SASS_HASH) {+      Map_Obj lm = SASS_MEMORY_NEW(Map,+                                l->pstate(),+                                l->length() / 2);+      for (size_t i = 0, L = l->length(); i < L; i += 2)+      {+        Expression_Ptr key = (*l)[i+0]->perform(this);+        Expression_Ptr val = (*l)[i+1]->perform(this);+        // make sure the color key never displays its real name+        key->is_delayed(true); // verified+        *lm << std::make_pair(key, val);+      }+      if (lm->has_duplicate_key()) {+        throw Exception::DuplicateKeyError(*lm, *l);+      }++      lm->is_interpolant(l->is_interpolant());+      return lm->perform(this);+    }+    // check if we should expand it+    if (l->is_expanded()) return l;+    // regular case for unevaluated lists+    List_Obj ll = SASS_MEMORY_NEW(List,+                               l->pstate(),+                               l->length(),+                               l->separator(),+                               l->is_arglist());+    for (size_t i = 0, L = l->length(); i < L; ++i) {+      ll->append((*l)[i]->perform(this));+    }+    ll->is_interpolant(l->is_interpolant());+    ll->from_selector(l->from_selector());+    ll->is_expanded(true);+    return ll.detach();+  }++  Expression_Ptr Eval::operator()(Map_Ptr m)+  {+    if (m->is_expanded()) return m;++    // make sure we're not starting with duplicate keys.+    // the duplicate key state will have been set in the parser phase.+    if (m->has_duplicate_key()) {+      throw Exception::DuplicateKeyError(*m, *m);+    }++    Map_Obj mm = SASS_MEMORY_NEW(Map,+                                m->pstate(),+                                m->length());+    for (auto key : m->keys()) {+      Expression_Ptr ex_key = key->perform(this);+      Expression_Ptr ex_val = m->at(key)->perform(this);+      *mm << std::make_pair(ex_key, ex_val);+    }++    // check the evaluated keys aren't duplicates.+    if (mm->has_duplicate_key()) {+      throw Exception::DuplicateKeyError(*mm, *m);+    }++    mm->is_expanded(true);+    return mm.detach();+  }++  Expression_Ptr Eval::operator()(Binary_Expression_Ptr b_in)+  {++    String_Schema_Obj ret_schema;+    Binary_Expression_Obj b = b_in;+    enum Sass_OP op_type = b->type();++    // only the last item will be used to eval the binary expression+    if (String_Schema_Ptr s_l = SASS_MEMORY_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(),+                                                    b->op(), &s_l->last(), b->right());+        bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // unverified+        for (size_t i = 0; i < s_l->length() - 1; ++i) {+          ret_schema->append(s_l->at(i)->perform(this));+        }+        ret_schema->append(bin_ex->perform(this));+        return ret_schema->perform(this);+      }+    }+    if (String_Schema_Ptr s_r = SASS_MEMORY_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());+        Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),+                                                    b->op(), b->left(), &s_r->first());+        bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // verified+        ret_schema->append(bin_ex->perform(this));+        for (size_t i = 1; i < s_r->length(); ++i) {+          ret_schema->append(s_r->at(i)->perform(this));+        }+        return ret_schema->perform(this);+      }+    }++    // don't eval delayed expressions (the '/' when used as a separator)+    if (!force && op_type == Sass_OP::DIV && b->is_delayed()) {+      b->right(b->right()->perform(this));+      b->left(b->left()->perform(this));+      return b.detach();+    }++    Expression_Obj lhs = b->left();+    Expression_Obj rhs = b->right();++    // fully evaluate their values+    if (op_type == Sass_OP::EQ ||+        op_type == Sass_OP::NEQ ||+        op_type == Sass_OP::GT ||+        op_type == Sass_OP::GTE ||+        op_type == Sass_OP::LT ||+        op_type == Sass_OP::LTE)+    {+      LOCAL_FLAG(force, true);+      lhs->is_expanded(false);+      lhs->set_delayed(false);+      lhs = lhs->perform(this);+      rhs->is_expanded(false);+      rhs->set_delayed(false);+      rhs = rhs->perform(this);+    }+    else {+      lhs = lhs->perform(this);+    }++    Binary_Expression_Obj u3 = b;+    switch (op_type) {+      case Sass_OP::AND: {+        return *lhs ? b->right()->perform(this) : lhs.detach();+      } break;++      case Sass_OP::OR: {+        return *lhs ? lhs.detach() : b->right()->perform(this);+      } break;++      default:+        break;+    }+    // not a logical connective, so go ahead and eval the rhs+    rhs = rhs->perform(this);+    AST_Node_Obj lu = &lhs;+    AST_Node_Obj ru = &rhs;++    Expression::Concrete_Type l_type = lhs->concrete_type();+    Expression::Concrete_Type r_type = rhs->concrete_type();++    // Is one of the operands an interpolant?+    String_Schema_Obj s1 = SASS_MEMORY_CAST(String_Schema, b->left());+    String_Schema_Obj s2 = SASS_MEMORY_CAST(String_Schema, b->right());+    Binary_Expression_Obj b1 = SASS_MEMORY_CAST(Binary_Expression, b->left());+    Binary_Expression_Obj b2 = SASS_MEMORY_CAST(Binary_Expression, b->right());++    bool schema_op = false;++    bool force_delay = (s2 && s2->is_left_interpolant()) ||+                       (s1 && s1->is_right_interpolant()) ||+                       (b1 && b1->is_right_interpolant()) ||+                       (b2 && b2->is_left_interpolant());++    if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay)+    {+      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 = SASS_MEMORY_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) {+            Textual_Obj l = SASS_MEMORY_NEW(Textual, b->pstate(), Textual::DIMENSION, str->value());+            lhs = l->perform(this);+          }+        }+        // If possible upgrade RHS to a number (for string to number compare)+        if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, rhs)) {+          std::string value(str->value());+          const char* start = value.c_str();+          if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) {+            Textual_Obj r = SASS_MEMORY_NEW(Textual, b->pstate(), Textual::DIMENSION, str->value());+            rhs = r->perform(this);+          }+        }+      }++      To_Value to_value(ctx);+      Value_Obj v_l = dynamic_cast<Value_Ptr>(lhs->perform(&to_value));+      Value_Obj v_r = dynamic_cast<Value_Ptr>(rhs->perform(&to_value));+      l_type = lhs->concrete_type();+      r_type = rhs->concrete_type();++      if (s2 && s2->has_interpolants() && s2->length()) {+        Textual_Obj front = SASS_MEMORY_CAST(Textual, s2->elements().front());+        if (front && !front->is_interpolant())+        {+          // XXX: this is never hit via spec tests+          schema_op = true;+          rhs = front->perform(this);+        }+      }++      if (force_delay) {+        std::string str("");+        str += v_l->to_string(ctx.c_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);+        val->is_interpolant(b->left()->has_interpolant());+        return val;+      }+    }++    // see if it's a relational expression+    try {+      switch(op_type) {+        case Sass_OP::EQ:  return SASS_MEMORY_NEW(Boolean, b->pstate(), eq(lhs, rhs));+        case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), !eq(lhs, rhs));+        case Sass_OP::GT:  return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs));+        case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gte"));+        case Sass_OP::LT:  return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lt"));+        case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs));+        default:                     break;+      }+    }+    catch (Exception::OperationError& err)+    {+      // throw Exception::Base(b->pstate(), err.what());+      throw Exception::SassValueError(b->pstate(), err);+    }++    l_type = lhs->concrete_type();+    r_type = rhs->concrete_type();++    // ToDo: throw error in op functions+    // ToDo: then catch and re-throw them+    Expression_Obj rv = 0;+    try {+      ParserState pstate(b->pstate());+      if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {+        Number_Ptr l_n = SASS_MEMORY_CAST(Number, lhs);+        Number_Ptr r_n = SASS_MEMORY_CAST(Number, rhs);+        rv = op_numbers(op_type, *l_n, *r_n, ctx.c_options, &pstate);+      }+      else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {+        Number_Ptr l_n = SASS_MEMORY_CAST(Number, lhs);+        Color_Ptr r_c = SASS_MEMORY_CAST(Color, rhs);+        rv = op_number_color(op_type, *l_n, *r_c, ctx.c_options, &pstate);+      }+      else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {+        Color_Ptr l_c = SASS_MEMORY_CAST(Color, lhs);+        Number_Ptr r_n = SASS_MEMORY_CAST(Number, rhs);+        rv = op_color_number(op_type, *l_c, *r_n, ctx.c_options, &pstate);+      }+      else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {+        Color_Ptr l_c = SASS_MEMORY_CAST(Color, lhs);+        Color_Ptr r_c = SASS_MEMORY_CAST(Color, rhs);+        rv = op_colors(op_type, *l_c, *r_c, ctx.c_options, &pstate);+      }+      else {+        To_Value to_value(ctx);+        // this will leak if perform does not return a value!+        Value_Obj v_l = SASS_MEMORY_CAST_PTR(Value, lhs->perform(&to_value));+        Value_Obj v_r = SASS_MEMORY_CAST_PTR(Value, rhs->perform(&to_value));+        bool interpolant = b->is_right_interpolant() ||+                           b->is_left_interpolant() ||+                           b->is_interpolant();+        if (op_type == Sass_OP::SUB) interpolant = false;+        // if (op_type == Sass_OP::DIV) interpolant = true;+        // check for type violations+        if (l_type == Expression::MAP) {+          throw Exception::InvalidValue(*v_l);+        }+        if (r_type == Expression::MAP) {+          throw Exception::InvalidValue(*v_r);+        }+        Value_Ptr ex = op_strings(b->op(), *v_l, *v_r, ctx.c_options, &pstate, !interpolant); // pass true to compress+        if (String_Constant_Ptr str = dynamic_cast<String_Constant_Ptr>(ex))+        {+          if (str->concrete_type() == Expression::STRING)+          {+            String_Constant_Ptr lstr = SASS_MEMORY_CAST(String_Constant, lhs);+            String_Constant_Ptr rstr = SASS_MEMORY_CAST(String_Constant, rhs);+            if (op_type != Sass_OP::SUB) {+              if (String_Constant_Ptr org = lstr ? lstr : rstr)+              { str->quote_mark(org->quote_mark()); }+            }+          }+        }+        ex->is_interpolant(b->is_interpolant());+        rv = ex;+      }+    }+    catch (Exception::OperationError& err)+    {+      // throw Exception::Base(b->pstate(), err.what());+      throw Exception::SassValueError(b->pstate(), err);+    }++    if (rv) {+      if (schema_op) {+        // XXX: this is never hit via spec tests+        (*s2)[0] = rv;+        rv = s2->perform(this);+      }+    }++    return rv.detach();++  }++  Expression_Ptr Eval::operator()(Unary_Expression_Ptr u)+  {+    Expression_Obj operand = u->operand()->perform(this);+    if (u->type() == Unary_Expression::NOT) {+      Boolean_Ptr result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);+      result->value(!result->value());+      return result;+    }+    else if (operand->concrete_type() == Expression::NUMBER) {+      Number_Ptr result = SASS_MEMORY_NEW(Number, static_cast<Number_Ptr>(&operand));+      result->value(u->type() == Unary_Expression::MINUS+                    ? -result->value()+                    :  result->value());+      return result;+    }+    else {+      // Special cases: +/- variables which evaluate to null ouput just +/-,+      // but +/- null itself outputs the string+      if (operand->concrete_type() == Expression::NULL_VAL && SASS_MEMORY_CAST(Variable, u->operand())) {+        u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), ""));+      }+      // Never apply unary opertions on colors @see #2140+      else if (operand->concrete_type() == Expression::COLOR) {+        Color_Ptr c = dynamic_cast<Color_Ptr>(&operand);++        // Use the color name if this was eval with one+        if (c->disp().length() > 0) {+          operand = SASS_MEMORY_NEW(String_Constant, operand->pstate(), c->disp());+          u->operand(operand);+        }+      }+      else {+        u->operand(operand);+      }++      String_Constant_Ptr result = SASS_MEMORY_NEW(String_Quoted,+                                                  u->pstate(),+                                                  u->inspect());+      return result;+    }+    // unreachable+    return u;+  }++  Expression_Ptr Eval::operator()(Function_Call_Ptr c)+  {+    if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {+        // XXX: this is never hit via spec tests+        std::ostringstream stm;+        stm << "Stack depth exceeded max of " << Constants::MaxCallStack;+        error(stm.str(), c->pstate(), backtrace());+    }+    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();++    Env* env = environment();+    if (!env->has(full_name) || (!c->via_call() && Prelexer::re_special_fun(name.c_str()))) {+      if (!env->has("*[f]")) {+        for (Argument_Obj arg : args->elements()) {+          if (List_Obj ls = SASS_MEMORY_CAST(List, arg->value())) {+            if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate());+          }+        }+        args = SASS_MEMORY_CAST_PTR(Arguments, args->perform(this));+        Function_Call_Obj lit = SASS_MEMORY_NEW(Function_Call,+                                             c->pstate(),+                                             c->name(),+                                             &args);+        if (args->has_named_arguments()) {+          error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());+        }+        String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted,+                                             c->pstate(),+                                             lit->to_string(ctx.c_options));+        str->is_interpolant(c->is_interpolant());+        return str;+      } else {+        // call generic function+        full_name = "*[f]";+      }+    }++    // further delay for calls+    if (full_name != "call[f]") {+      args->set_delayed(false); // verified+    }+    if (full_name != "if[f]") {+      args = SASS_MEMORY_CAST_PTR(Arguments, args->perform(this));+    }+    Definition_Ptr def = SASS_MEMORY_CAST(Definition, (*env)[full_name]);++    if (def->is_overload_stub()) {+      std::stringstream ss;+      size_t L = args->length();+      // account for rest arguments+      if (args->has_rest_argument() && args->length() > 0) {+        // get the rest arguments list+        List_Ptr rest = SASS_MEMORY_CAST(List, args->last()->value());+        // arguments before rest argument plus rest+        if (rest) L += rest->length() - 1;+      }+      ss << full_name << L;+      full_name = ss.str();+      std::string resolved_name(full_name);+      if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());+      def = SASS_MEMORY_CAST(Definition, (*env)[resolved_name]);+    }++    Expression_Obj     result = c;+    Block_Obj          body   = def->block();+    Native_Function func   = def->native_function();+    Sass_Function_Entry c_function = def->c_function();++    Parameters_Obj params = def->parameters();+    Env fn_env(def->environment());+    exp.env_stack.push_back(&fn_env);++    if (func || body) {+      bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+      Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");+      exp.backtrace_stack.push_back(&here);+      // eval the body if user-defined or special, invoke underlying CPP function if native+      if (body /* && !Prelexer::re_special_fun(name.c_str()) */) {+        result = body->perform(this);+      }+      else if (func) {+        result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace(), exp.selector_stack);+      }+      if (!result) {+        error(std::string("Function ") + c->name() + " did not return a value", c->pstate());+      }+      exp.backtrace_stack.pop_back();+    }++    // else if it's a user-defined c function+    // convert call into C-API compatible form+    else if (c_function) {+      Sass_Function_Fn c_func = sass_function_get_function(c_function);+      if (full_name == "*[f]") {+        String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), c->name());+        Arguments_Obj new_args = SASS_MEMORY_NEW(Arguments, c->pstate());+        new_args->append(SASS_MEMORY_NEW(Argument, c->pstate(), &str));+        new_args->concat(&args);+        args = new_args;+      }++      // populates env with default values for params+      std::string ff(c->name());+      bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);++      Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");+      exp.backtrace_stack.push_back(&here);++      To_C to_c;+      union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA);+      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 = SASS_MEMORY_CAST(Expression, node);+        sass_list_set_value(c_args, i, arg->perform(&to_c));+      }+      union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_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(), backtrace());+      } 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(), backtrace());+      }+      result = cval_to_astnode(c_val, backtrace(), c->pstate());++      exp.backtrace_stack.pop_back();+      sass_delete_value(c_args);+      if (c_val != c_args)+        sass_delete_value(c_val);+    }++    // link back to function definition+    // only do this for custom functions+    if (result->pstate().file == std::string::npos)+      result->pstate(c->pstate());++    result = result->perform(this);+    result->is_interpolant(c->is_interpolant());+    exp.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)+  {+    std::string name(v->name());+    Expression_Obj value = 0;+    Env* env = environment();+    if (env->has(name)) {+      value = SASS_MEMORY_CAST(Expression, (*env)[name]);+    }+    else error("Undefined variable: \"" + v->name() + "\".", v->pstate());+    if (typeid(*value) == typeid(Argument)) {+      value = SASS_MEMORY_CAST(Argument, value)->value();+    }++    // behave according to as ruby sass (add leading zero)+    if (Number_Ptr nr = SASS_MEMORY_CAST(Number, value)) {+      nr->zero(true);+    }++    value->is_interpolant(v->is_interpolant());+    if (force) value->is_expanded(false);+    value->set_delayed(false); // verified+    value = value->perform(this);+    if(!force) (*env)[name] = &value;+    return value.detach();+  }++  Expression_Ptr Eval::operator()(Textual_Ptr t)+  {+    using Prelexer::number;+    Expression_Obj result = 0;+    size_t L = t->value().length();+    bool zero = !( (L > 0 && t->value().substr(0, 1) == ".") ||+                   (L > 1 && t->value().substr(0, 2) == "0.") ||+                   (L > 1 && t->value().substr(0, 2) == "-.")  ||+                   (L > 2 && t->value().substr(0, 3) == "-0.")+                 );++    const std::string& text = t->value();+    size_t num_pos = text.find_first_not_of(" \n\r\t");+    if (num_pos == std::string::npos) num_pos = text.length();+    size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);+    if (unit_pos == std::string::npos) unit_pos = text.length();+    const std::string& num = text.substr(num_pos, unit_pos - num_pos);++    switch (t->type())+    {+      case Textual::NUMBER:+        result = SASS_MEMORY_NEW(Number,+                                 t->pstate(),+                                 sass_atof(num.c_str()),+                                 "",+                                 zero);+        break;+      case Textual::PERCENTAGE:+        result = SASS_MEMORY_NEW(Number,+                                 t->pstate(),+                                 sass_atof(num.c_str()),+                                 "%",+                                 true);+        break;+      case Textual::DIMENSION:+        result = SASS_MEMORY_NEW(Number,+                                 t->pstate(),+                                 sass_atof(num.c_str()),+                                 Token(number(text.c_str())),+                                 zero);+        break;+      case Textual::HEX: {+        if (t->value().substr(0, 1) != "#") {+          result = SASS_MEMORY_NEW(String_Quoted, t->pstate(), t->value());+          break;+        }+        std::string hext(t->value().substr(1)); // chop off the '#'+        if (hext.length() == 6) {+          std::string r(hext.substr(0,2));+          std::string g(hext.substr(2,2));+          std::string b(hext.substr(4,2));+          result = SASS_MEMORY_NEW(Color,+                                   t->pstate(),+                                   static_cast<double>(strtol(r.c_str(), NULL, 16)),+                                   static_cast<double>(strtol(g.c_str(), NULL, 16)),+                                   static_cast<double>(strtol(b.c_str(), NULL, 16)),+                                   1, // alpha channel+                                   t->value());+        }+        else {+          result = SASS_MEMORY_NEW(Color,+                                   t->pstate(),+                                   static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),+                                   static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),+                                   static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),+                                   1, // alpha channel+                                   t->value());+        }+      } break;+    }+    result->is_interpolant(t->is_interpolant());+    return result.detach();+  }++  Expression_Ptr Eval::operator()(Color_Ptr c)+  {+    return c;+  }++  Expression_Ptr Eval::operator()(Number_Ptr n)+  {+    return n;+  }++  Expression_Ptr Eval::operator()(Boolean_Ptr b)+  {+    return b;+  }++  void Eval::interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl) {++    bool needs_closing_brace = false;++    if (Arguments_Ptr args = SASS_MEMORY_CAST(Arguments, ex)) {+      List_Ptr ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA);+      for(auto arg : args->elements()) {+        ll->append(arg->value());+      }+      ll->is_interpolant(args->is_interpolant());+      needs_closing_brace = true;+      res += "(";+      ex = ll;+    }+    if (Number_Ptr nr = SASS_MEMORY_CAST(Number, ex)) {+      if (!nr->is_valid_css_unit()) {+        throw Exception::InvalidValue(*nr);+      }+    }+    if (Argument_Ptr arg = SASS_MEMORY_CAST(Argument, ex)) {+      ex = &arg->value();+    }+    if (String_Quoted_Ptr sq = SASS_MEMORY_CAST(String_Quoted, ex)) {+      if (was_itpl) {+        bool was_interpolant = ex->is_interpolant();+        ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value());+        ex->is_interpolant(was_interpolant);+      }+    }++    if (SASS_MEMORY_CAST(Null, ex)) { return; }++    // parent selector needs another go+    if (SASS_MEMORY_CAST(Parent_Selector, ex)) {+      // XXX: this is never hit via spec tests+      ex = ex->perform(this);+    }++    if (List_Ptr l = SASS_MEMORY_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() && dynamic_cast<Null_Ptr>((*l)[0])) { res += ""; }+      for(Expression_Obj item : *l) {+        item->is_interpolant(l->is_interpolant());+        std::string rl(""); interpolation(ctx, rl, &item, into_quotes, l->is_interpolant());+        bool is_null = dynamic_cast<Null_Ptr>(&item) != 0; // rl != ""+        if (!is_null) ll->append(SASS_MEMORY_NEW(String_Quoted, item->pstate(), rl));+      }+      // Check indicates that we probably should not get a list+      // 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));+        newline_to_space(str); // replace directly+        res += str; // append to result string+      } else {+        res += (ll->to_string(ctx.c_options));+      }+      ll->is_interpolant(l->is_interpolant());+    }++    // Value+    // Textual+    // Function_Call+    // 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) : "");+      } else {+        res += ex ? ex->to_string(ctx.c_options) : "";+      }+    }++    if (needs_closing_brace) res += ")";++  }++  Expression_Ptr Eval::operator()(String_Schema_Ptr s)+  {+    size_t L = s->length();+    bool into_quotes = false;+    if (L > 1) {+      if (!SASS_MEMORY_CAST(String_Quoted, (*s)[0]) && !SASS_MEMORY_CAST(String_Quoted, (*s)[L - 1])) {+      if (String_Constant_Ptr l = SASS_MEMORY_CAST(String_Constant, (*s)[0])) {+        if (String_Constant_Ptr r = SASS_MEMORY_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;+          }+        }+      }+      }+    }+    bool was_quoted = false;+    bool was_interpolant = false;+    std::string res("");+    for (size_t i = 0; i < L; ++i) {+      bool is_quoted = SASS_MEMORY_CAST(String_Quoted, (*s)[i]) != NULL;+      if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; }+      else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; }+      Expression_Obj ex = (*s)[i]->perform(this);+      interpolation(ctx, res, ex, into_quotes, ex->is_interpolant());+      was_quoted = SASS_MEMORY_CAST(String_Quoted, (*s)[i]) != NULL;+      was_interpolant = (*s)[i]->is_interpolant();++    }+    if (!s->is_interpolant()) {+      if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(Null, s->pstate());+      return SASS_MEMORY_NEW(String_Constant, s->pstate(), res);+    }+    // 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);+    // if (s->is_interpolant()) str->quote_mark(0);+    // String_Constant_Ptr 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());+    return str.detach();+  }+++  Expression_Ptr Eval::operator()(String_Constant_Ptr s)+  {+    if (!s->is_delayed() && name_to_color(s->value())) {+      Color_Ptr c = SASS_MEMORY_COPY(name_to_color(s->value())); // copy+      c->pstate(s->pstate());+      c->disp(s->value());+      c->is_delayed(true);+      return c;+    }+    return s;+  }++  Expression_Ptr Eval::operator()(String_Quoted_Ptr s)+  {+    String_Quoted_Ptr 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_Ptr left = c->left()->perform(this);+    Expression_Ptr right = c->right()->perform(this);+    Supports_Operator_Ptr cc = SASS_MEMORY_NEW(Supports_Operator,+                                 c->pstate(),+                                 dynamic_cast<Supports_Condition_Ptr>(left),+                                 dynamic_cast<Supports_Condition_Ptr>(right),+                                 c->operand());+    return cc;+  }++  Expression_Ptr Eval::operator()(Supports_Negation_Ptr c)+  {+    Expression_Ptr condition = c->condition()->perform(this);+    Supports_Negation_Ptr cc = SASS_MEMORY_NEW(Supports_Negation,+                                 c->pstate(),+                                 dynamic_cast<Supports_Condition_Ptr>(condition));+    return cc;+  }++  Expression_Ptr Eval::operator()(Supports_Declaration_Ptr c)+  {+    Expression_Ptr feature = c->feature()->perform(this);+    Expression_Ptr value = c->value()->perform(this);+    Supports_Declaration_Ptr cc = SASS_MEMORY_NEW(Supports_Declaration,+                              c->pstate(),+                              feature,+                              value);+    return cc;+  }++  Expression_Ptr Eval::operator()(Supports_Interpolation_Ptr c)+  {+    Expression_Ptr value = c->value()->perform(this);+    Supports_Interpolation_Ptr cc = SASS_MEMORY_NEW(Supports_Interpolation,+                            c->pstate(),+                            value);+    return cc;+  }++  Expression_Ptr Eval::operator()(At_Root_Query_Ptr 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,+                                     e->pstate(),+                                     dynamic_cast<String_Ptr>(&feature),+                                     value);+    return ee;+  }++  Expression_Ptr Eval::operator()(Media_Query_Ptr q)+  {+    String_Obj t = q->media_type();+    t = static_cast<String_Ptr>(&t ? t->perform(this) : 0);+    Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query,+                                      q->pstate(),+                                      &t,+                                      q->length(),+                                      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)));+    }+    return qq.detach();+  }++  Expression_Ptr Eval::operator()(Media_Query_Expression_Ptr e)+  {+    Expression_Obj feature = e->feature();+    feature = (feature ? feature->perform(this) : 0);+    if (feature && SASS_MEMORY_CAST(String_Quoted, feature)) {+      feature = SASS_MEMORY_NEW(String_Quoted,+                                  feature->pstate(),+                                  SASS_MEMORY_CAST(String_Quoted, feature)->value());+    }+    Expression_Obj value = e->value();+    value = (value ? value->perform(this) : 0);+    if (value && SASS_MEMORY_CAST(String_Quoted, value)) {+      // XXX: this is never hit via spec tests+      value = SASS_MEMORY_NEW(String_Quoted,+                                value->pstate(),+                                SASS_MEMORY_CAST(String_Quoted, value)->value());+    }+    return SASS_MEMORY_NEW(Media_Query_Expression,+                           e->pstate(),+                           feature,+                           value,+                           e->is_interpolated());+  }++  Expression_Ptr Eval::operator()(Null_Ptr n)+  {+    return n;+  }++  Expression_Ptr Eval::operator()(Argument_Ptr a)+  {+    Expression_Obj val = a->value()->perform(this);+    bool is_rest_argument = a->is_rest_argument();+    bool is_keyword_argument = a->is_keyword_argument();++    if (a->is_rest_argument()) {+      if (val->concrete_type() == Expression::MAP) {+        is_rest_argument = false;+        is_keyword_argument = true;+      }+      else if(val->concrete_type() != Expression::LIST) {+        List_Obj wrapper = SASS_MEMORY_NEW(List,+                                        val->pstate(),+                                        0,+                                        SASS_COMMA,+                                        true);+        wrapper->append(val);+        val = &wrapper;+      }+    }+    return SASS_MEMORY_NEW(Argument,+                           a->pstate(),+                           val,+                           a->name(),+                           is_rest_argument,+                           is_keyword_argument);+  }++  Expression_Ptr Eval::operator()(Arguments_Ptr 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 = SASS_MEMORY_CAST(Argument, rv);+      if (!(arg->is_rest_argument() || arg->is_keyword_argument())) {+        aa->append(arg);+      }+    }++    if (a->has_rest_argument()) {+      Expression_Obj rest = a->get_rest_argument()->perform(this);+      Expression_Obj splat = SASS_MEMORY_CAST(Argument, rest)->value()->perform(this);++      Sass_Separator separator = SASS_COMMA;+      List_Ptr ls = SASS_MEMORY_CAST(List, splat);+      Map_Ptr ms = SASS_MEMORY_CAST(Map, splat);++      List_Obj arglist = SASS_MEMORY_NEW(List,+                                      splat->pstate(),+                                      0,+                                      ls ? ls->separator() : separator,+                                      true);++      if (ls && ls->is_arglist()) {+        arglist->concat(ls);+      } else if (ms) {+        aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), ms, "", false, true));+      } else if (ls) {+        arglist->concat(ls);+      } else {+        arglist->append(splat);+      }+      if (arglist->length()) {+        aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), &arglist, "", true));+      }+    }++    if (a->has_keyword_argument()) {+      Expression_Obj rv = a->get_keyword_argument()->perform(this);+      Argument_Ptr rvarg = SASS_MEMORY_CAST(Argument, rv);+      Expression_Obj kwarg = rvarg->value()->perform(this);++      aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "", false, true));+    }+    return aa.detach();+  }++  Expression_Ptr Eval::operator()(Comment_Ptr c)+  {+    return 0;+  }++  inline Expression_Ptr Eval::fallback_impl(AST_Node_Ptr n)+  {+    return static_cast<Expression_Ptr>(n);+  }++  // All the binary helpers.++  bool Eval::eq(Expression_Obj lhs, Expression_Obj rhs)+  {+    // use compare operator from ast node+    return lhs && rhs && *lhs == *rhs;+  }++  bool Eval::lt(Expression_Obj lhs, Expression_Obj rhs, std::string op)+  {+    Number_Obj l = SASS_MEMORY_CAST(Number, lhs);+    Number_Obj r = SASS_MEMORY_CAST(Number, rhs);+    // use compare operator from ast node+    if (!l || !r) throw Exception::UndefinedOperation(&lhs, &rhs, op);+    // use compare operator from ast node+    return *l < *r;+  }++  Value_Ptr Eval::op_numbers(enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)+  {+    double lv = l.value();+    double rv = r.value();+    if (op == Sass_OP::DIV && rv == 0) {+      // XXX: this is never hit via spec tests+      return SASS_MEMORY_NEW(String_Quoted, pstate ? *pstate : l.pstate(), lv ? "Infinity" : "NaN");+    }+    if (op == Sass_OP::MOD && !rv) {+      // XXX: this is never hit via spec tests+      throw Exception::ZeroDivisionError(l, r);+    }++    Number tmp(&r); // copy+    bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;+    tmp.normalize(l.find_convertible_unit(), strict);+    std::string l_unit(l.unit());+    std::string r_unit(tmp.unit());+    Number_Obj v = SASS_MEMORY_COPY(&l); // copy+    v->pstate(pstate ? *pstate : l.pstate());+    if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {+      v->numerator_units() = r.numerator_units();+      v->denominator_units() = r.denominator_units();+    }++    if (op == Sass_OP::MUL) {+      v->value(ops[op](lv, rv));+      for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {+        v->numerator_units().push_back(r.numerator_units()[i]);+      }+      for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {+        v->denominator_units().push_back(r.denominator_units()[i]);+      }+    }+    else if (op == Sass_OP::DIV) {+      v->value(ops[op](lv, rv));+      for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {+        v->denominator_units().push_back(r.numerator_units()[i]);+      }+      for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {+        v->numerator_units().push_back(r.denominator_units()[i]);+      }+    } else {+      v->value(ops[op](lv, r.value() * r.convert_factor(l)));+      // v->normalize();+      return v.detach();++      v->value(ops[op](lv, tmp.value()));+    }+    v->normalize();+    return v.detach();+  }++  Value_Ptr Eval::op_number_color(enum Sass_OP op, const Number& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)+  {+    double lv = l.value();+    switch (op) {+      case Sass_OP::ADD:+      case Sass_OP::MUL: {+        return SASS_MEMORY_NEW(Color,+                               pstate ? *pstate : l.pstate(),+                               ops[op](lv, r.r()),+                               ops[op](lv, r.g()),+                               ops[op](lv, r.b()),+                               r.a());+      } break;+      case Sass_OP::SUB:+      case Sass_OP::DIV: {+        std::string sep(op == Sass_OP::SUB ? "-" : "/");+        std::string color(r.to_string(opt));+        return SASS_MEMORY_NEW(String_Quoted,+                               pstate ? *pstate : l.pstate(),+                               l.to_string(opt)+                               + sep+                               + color);+      } break;+      case Sass_OP::MOD: {+        throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op));+      } break;+      default: break; // caller should ensure that we don't get here+    }+    // unreachable+    return NULL;+  }++  Value_Ptr Eval::op_color_number(enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)+  {+    double rv = r.value();+    if (op == Sass_OP::DIV && !rv) {+      // comparison of Fixnum with Float failed?+      throw Exception::ZeroDivisionError(l, r);+    }+    return SASS_MEMORY_NEW(Color,+                           pstate ? *pstate : l.pstate(),+                           ops[op](l.r(), rv),+                           ops[op](l.g(), rv),+                           ops[op](l.b(), rv),+                           l.a());+  }++  Value_Ptr Eval::op_colors(enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)+  {+    if (l.a() != r.a()) {+      throw Exception::AlphaChannelsNotEqual(&l, &r, "+");+    }+    if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {+      // comparison of Fixnum with Float failed?+      throw Exception::ZeroDivisionError(l, r);+    }+    return SASS_MEMORY_NEW(Color,+                           pstate ? *pstate : l.pstate(),+                           ops[op](l.r(), r.r()),+                           ops[op](l.g(), r.g()),+                           ops[op](l.b(), r.b()),+                           l.a());+  }++  Value_Ptr Eval::op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, ParserState* pstate, bool delayed)+  {+    Expression::Concrete_Type ltype = lhs.concrete_type();+    Expression::Concrete_Type rtype = rhs.concrete_type();+    enum Sass_OP op = operand.operand;++    String_Quoted_Ptr lqstr = dynamic_cast<String_Quoted_Ptr>(&lhs);+    String_Quoted_Ptr rqstr = dynamic_cast<String_Quoted_Ptr>(&rhs);++    std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));+    std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));++    if (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));+    if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));+    if (op == Sass_OP::MOD) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));+    if (op == Sass_OP::MUL) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));+    std::string sep;+    switch (op) {+      case Sass_OP::SUB: sep = "-"; break;+      case Sass_OP::DIV: sep = "/"; break;+      case Sass_OP::MUL: sep = "*"; break;+      case Sass_OP::MOD: sep = "%"; break;+      case Sass_OP::EQ:  sep = "=="; break;+      case Sass_OP::NEQ:  sep = "!="; break;+      case Sass_OP::LT:  sep = "<"; break;+      case Sass_OP::GT:  sep = ">"; break;+      case Sass_OP::LTE:  sep = "<="; break;+      case Sass_OP::GTE:  sep = ">="; break;+      default:                      break;+    }++    if ( (sep == "") /* &&+         (sep != "/" || !rqstr || !rqstr->quote_mark()) */+    ) {+      // create a new string that might be quoted on output (but do not unquote what we pass)+      return SASS_MEMORY_NEW(String_Quoted, pstate ? *pstate : lhs.pstate(), lstr + rstr, 0, false, true);+    }++    if (sep != "" && !delayed) {+      if (operand.ws_before) sep = " " + sep;+      if (operand.ws_after) sep = sep + " ";+    }++    if (op == Sass_OP::SUB || op == Sass_OP::DIV) {+      if (lqstr && lqstr->quote_mark()) lstr = quote(lstr);+      if (rqstr && rqstr->quote_mark()) rstr = quote(rstr);+    }++    return SASS_MEMORY_NEW(String_Constant, pstate ? *pstate : lhs.pstate(), lstr + sep + rstr);+  }++  Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, 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), backtrace, pstate));+        }+        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), backtrace, pstate),+            cval_to_astnode(sass_map_get_value(v, i), backtrace, 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, backtrace);+      } break;+      case SASS_WARNING: {+        error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);+      } 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;+    return s->resolve_parent_refs(ctx, exp.selector_stack, implicit_parent);+  }++  // XXX: this is never hit via spec tests+  Attribute_Selector_Ptr Eval::operator()(Attribute_Selector_Ptr s)+  {+    String_Obj attr = s->value();+    if (attr) { attr = static_cast<String_Ptr>(attr->perform(this)); }+    Attribute_Selector_Ptr ss = SASS_MEMORY_COPY(s);+    ss->value(attr);+    return ss;+  }++  Selector_List_Ptr Eval::operator()(Selector_Schema_Ptr s)+  {+    // the parser will look for a brace to end the selector+    Expression_Obj sel = s->contents()->perform(this);+    std::string result_str(sel->to_string(ctx.c_options));+    result_str = unquote(Util::rtrim(result_str)) + "\n{";+    Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());+    p.last_media_block = s->media_block();+    Selector_List_Obj sl = p.parse_selector_list(exp.block_stack.back()->is_root());+    if (s->has_parent_ref()) sl->remove_parent_selectors();+    return operator()(&sl);+  }++  Expression_Ptr Eval::operator()(Parent_Selector_Ptr 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();+    } else {+      return SASS_MEMORY_NEW(Null, p->pstate());     }   } 
libsass/src/eval.hpp view
@@ -1,106 +1,108 @@ #ifndef SASS_EVAL_H #define SASS_EVAL_H +#include "ast.hpp" #include "context.hpp" #include "listize.hpp" #include "operation.hpp"+#include "environment.hpp"  namespace Sass {    class Expand;   class Context;-  class Listize; -  class Eval : public Operation_CRTP<Expression*, Eval> {+  class Eval : public Operation_CRTP<Expression_Ptr, Eval> {     private:-    Expression* fallback_impl(AST_Node* n);+    Expression_Ptr fallback_impl(AST_Node_Ptr n);     public:     Expand&  exp;     Context& ctx;-    Listize  listize;     Eval(Expand& exp);-    virtual ~Eval();+    ~Eval(); +    bool force;+    bool is_in_comment;+     Env* environment();-    Context& context();-    Selector_List* selector();     Backtrace* backtrace();--    using Operation<Expression*>::operator();+    Selector_List_Obj selector();      // for evaluating function bodies-    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()(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()(List*);-    Expression* operator()(Map*);-    Expression* operator()(Binary_Expression*);-    Expression* operator()(Unary_Expression*);-    Expression* operator()(Function_Call*);-    Expression* operator()(Function_Call_Schema*);-    Expression* operator()(Variable*);-    Expression* operator()(Textual*);-    Expression* operator()(Number*);-    Expression* operator()(Boolean*);-    Expression* operator()(String_Schema*);-    Expression* operator()(String_Quoted*);-    Expression* operator()(String_Constant*);-    // Expression* operator()(Selector_List*);-    Expression* operator()(Media_Query*);-    Expression* operator()(Media_Query_Expression*);-    Expression* operator()(At_Root_Expression*);-    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*);+    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()(Textual_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);+    Expression_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);      // these will return selectors-    Selector_List* operator()(Selector_List*);-    Selector_List* operator()(Complex_Selector*);-    Attribute_Selector* operator()(Attribute_Selector*);+    Selector_List_Ptr operator()(Selector_List_Ptr);+    Selector_List_Ptr operator()(Complex_Selector_Ptr);+    Attribute_Selector_Ptr operator()(Attribute_Selector_Ptr);     // they don't have any specific implementatio (yet)-    Type_Selector* operator()(Type_Selector* s) { return s; };-    Pseudo_Selector* operator()(Pseudo_Selector* s) { return s; };-    Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; };-    Selector_Qualifier* operator()(Selector_Qualifier* s) { return s; };-    Selector_Placeholder* operator()(Selector_Placeholder* s) { return s; };+    Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; };+    Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; };+    Wrapped_Selector_Ptr operator()(Wrapped_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; };     // actual evaluated selectors-    Selector_List* operator()(Selector_Schema*);-    Expression* operator()(Parent_Selector*);+    Selector_List_Ptr operator()(Selector_Schema_Ptr);+    Expression_Ptr operator()(Parent_Selector_Ptr);      template <typename U>-    Expression* fallback(U x) { return fallback_impl(x); }+    Expression_Ptr fallback(U x) { return fallback_impl(x); }      // -- only need to define two comparisons, and the rest can be implemented in terms of them-    static bool eq(Expression*, Expression*);-    static bool lt(Expression*, Expression*);+    static bool eq(Expression_Obj, Expression_Obj);+    static bool lt(Expression_Obj, Expression_Obj, std::string op);     // -- arithmetic on the combinations that matter-    static Value* op_numbers(Memory_Manager&, enum Sass_OP, const Number&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0);-    static Value* op_number_color(Memory_Manager&, enum Sass_OP, const Number&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0);-    static Value* op_color_number(Memory_Manager&, enum Sass_OP, const Color&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0);-    static Value* op_colors(Memory_Manager&, enum Sass_OP, const Color&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0);-    static Value* op_strings(Memory_Manager&, enum Sass_OP, Value&, Value&, bool compressed = false, int precision = 5, ParserState* pstate = 0);+    static Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+    static Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+    static Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+    static Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+    static Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, ParserState* pstate = 0, bool interpolant = false);    private:-    std::string interpolation(Expression* s, bool into_quotes = false);+    void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false);    }; -  Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate = ParserState("[AST]"));+  Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate = ParserState("[AST]"));  } 
libsass/src/expand.cpp view
@@ -1,36 +1,41 @@-#ifdef _MSC_VER-#pragma warning(disable : 4503)-#endif-+#include "sass.hpp" #include <iostream> #include <typeinfo> +#include "ast.hpp" #include "expand.hpp" #include "bind.hpp" #include "eval.hpp"-#include "to_string.hpp" #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp"  namespace Sass { -  Expand::Expand(Context& ctx, Env* env, Backtrace* bt)+  // simple endless recursion protection+  const size_t maxRecursion = 500;++  Expand::Expand(Context& ctx, Env* env, Backtrace* bt, std::vector<Selector_List_Obj>* stack)   : ctx(ctx),     eval(Eval(*this)),+    recursions(0),+    in_keyframes(false),+    at_root_without_rule(false),+    old_at_root_without_rule(false),     env_stack(std::vector<Env*>()),-    block_stack(std::vector<Block*>()),-    property_stack(std::vector<String*>()),-    selector_stack(std::vector<Selector_List*>()),-    backtrace_stack(std::vector<Backtrace*>()),-    in_keyframes(false)+    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>()),+    backtrace_stack(std::vector<Backtrace*>())   {     env_stack.push_back(0);     env_stack.push_back(env);     block_stack.push_back(0);-    // import_stack.push_back(0);-    property_stack.push_back(0);-    selector_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);     backtrace_stack.push_back(0);     backtrace_stack.push_back(bt);   }@@ -47,7 +52,7 @@     return 0;   } -  Selector_List* Expand::selector()+  Selector_List_Obj Expand::selector()   {     if (selector_stack.size() > 0)       return selector_stack.back();@@ -62,161 +67,167 @@   }    // blocks create new variable scopes-  Statement* Expand::operator()(Block* b)+  Block_Ptr Expand::operator()(Block_Ptr b)   {     // create new local environment     // set the current env as parent     Env env(environment());     // copy the block object (add items later)-    Block* bb = SASS_MEMORY_NEW(ctx.mem, Block,+    Block_Obj bb = SASS_MEMORY_NEW(Block,                                 b->pstate(),                                 b->length(),                                 b->is_root());     // setup block and env stack-    this->block_stack.push_back(bb);+    this->block_stack.push_back(&bb);     this->env_stack.push_back(&env);     // operate on block+    // this may throw up!     this->append_block(b);     // revert block and env stack     this->block_stack.pop_back();     this->env_stack.pop_back();     // return copy-    return bb;+    return bb.detach();   } -  Statement* Expand::operator()(Ruleset* r)+  Statement_Ptr Expand::operator()(Ruleset_Ptr r)   {-    // reset when leaving scope+    LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule);      if (in_keyframes) {-      Keyframe_Rule* k = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, r->pstate(), r->block()->perform(this)->block());+      Block_Ptr bb = operator()(&r->block());+      Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);       if (r->selector()) {         selector_stack.push_back(0);-        k->selector(static_cast<Selector_List*>(r->selector()->perform(&eval)));+        k->name(SASS_MEMORY_CAST_PTR(Selector_List, r->selector()->perform(&eval)));         selector_stack.pop_back();       }-      return k;+      return k.detach();     } +    // reset when leaving scope+    LOCAL_FLAG(at_root_without_rule, false);+     // do some special checks for the base level rules     if (r->is_root()) {-      if (Selector_List* selector_list = dynamic_cast<Selector_List*>(r->selector())) {-        for (Complex_Selector* complex_selector : selector_list->elements()) {-          Complex_Selector* tail = complex_selector;+      if (Selector_List_Ptr selector_list = SASS_MEMORY_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* header : tail->head()->elements()) {-              if (dynamic_cast<Parent_Selector*>(header) == NULL) continue; // skip all others-              To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string));+            if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {+              if (SASS_MEMORY_CAST(Parent_Selector, header) == NULL) continue; // skip all others+              std::string sel_str(complex_selector->to_string(ctx.c_options));               error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace());             }-            tail = tail->tail();+            tail = &tail->tail();           }         }       }     } -    Expression* ex = r->selector()->perform(&eval);-    Selector_List* sel = dynamic_cast<Selector_List*>(ex);+    Expression_Obj ex = 0;+    if (r->selector()) ex = r->selector()->perform(&eval);+    Selector_List_Obj sel = SASS_MEMORY_CAST(Selector_List, ex);     if (sel == 0) throw std::runtime_error("Expanded null selector"); -    selector_stack.push_back(sel);-    Block* blk = r->block()->perform(this)->block();-    Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset,+    if (sel->length() == 0 || sel->has_parent_ref()) {+      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;+      }+      if (sel->has_real_parent_ref() && !has_parent_selector) {+        error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), backtrace());+      }+    }++    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;+    if (r->block()) blk = operator()(&r->block());+    Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset,                                   r->pstate(),-                                  sel,+                                  &sel,                                   blk);     selector_stack.pop_back();-    rr->tabs(r->tabs());--    return rr;-  }--  Statement* Expand::operator()(Propset* p)-  {-    property_stack.push_back(p->property_fragment());-    Block* expanded_block = p->block()->perform(this)->block();--    for (size_t i = 0, L = expanded_block->length(); i < L; ++i) {-      Statement* stm = (*expanded_block)[i];-      if (Declaration* dec = static_cast<Declaration*>(stm)) {-        String_Schema* combined_prop = SASS_MEMORY_NEW(ctx.mem, String_Schema, p->pstate());-        if (!property_stack.empty()) {-          *combined_prop << property_stack.back()->perform(&eval)-                         << SASS_MEMORY_NEW(ctx.mem, String_Quoted,-             p->pstate(), "-")-             << dec->property(); // TODO: eval the prop into a string constant-        }-        else {-          *combined_prop << dec->property();-        }-        dec->property(combined_prop);-        *block_stack.back() << dec;-      }-      else if (typeid(*stm) == typeid(Comment)) {-        // drop comments in propsets-      }-      else {-        error("contents of namespaced properties must result in style declarations only", stm->pstate(), backtrace());-      }+    if (block_stack.back()->is_root()) {+      env_stack.pop_back();     } -    property_stack.pop_back();+    rr->is_root(r->is_root());+    rr->tabs(r->tabs()); -    return 0;+    return rr;   } -  Statement* Expand::operator()(Supports_Block* f)+  Statement_Ptr Expand::operator()(Supports_Block_Ptr f)   {-    Expression* condition = f->condition()->perform(&eval);-    Supports_Block* ff = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+    Expression_Obj condition = f->condition()->perform(&eval);+    Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block,                                        f->pstate(),-                                       static_cast<Supports_Condition*>(condition),-                                       f->block()->perform(this)->block());-    return ff;+                                       SASS_MEMORY_CAST(Supports_Condition, condition),+                                       operator()(&f->block()));+    return ff.detach();   } -  Statement* Expand::operator()(Media_Block* m)+  Statement_Ptr Expand::operator()(Media_Block_Ptr m)   {-    To_String to_string(&ctx);-    Expression* mq = m->media_queries()->perform(&eval);-    mq = Parser::from_c_str(mq->perform(&to_string).c_str(), ctx, mq->pstate()).parse_media_queries();-    Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+    media_block_stack.push_back(m);+    Expression_Obj mq = m->media_queries()->perform(&eval);+    std::string str_mq(mq->to_string(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, mq->pstate()));+    mq = &p.parse_media_queries(); // re-assign now+    List_Obj ls = SASS_MEMORY_CAST_PTR(List, mq->perform(&eval));+    Block_Obj blk = operator()(&m->block());+    Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block,                                       m->pstate(),-                                      static_cast<List*>(mq),-                                      m->block()->perform(this)->block(),+                                      ls,+                                      blk,                                       0);+    media_block_stack.pop_back();     mm->tabs(m->tabs());     return mm;   } -  Statement* Expand::operator()(At_Root_Block* a)+  Statement_Ptr Expand::operator()(At_Root_Block_Ptr a)   {-    Block* ab = a->block();-    // if (ab) ab->is_root(true);-    Expression* ae = a->expression();+    Block_Obj ab = a->block();+    Expression_Obj ae = &a->expression();+     if (ae) ae = ae->perform(&eval);-    else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, a->pstate());-    Block* bb = ab ? ab->perform(this)->block() : 0;-    At_Root_Block* aa = SASS_MEMORY_NEW(ctx.mem, At_Root_Block,+    else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());++    LOCAL_FLAG(at_root_without_rule, true);+    LOCAL_FLAG(in_keyframes, false);++                                       ;++    Block_Obj bb = ab ? operator()(&ab) : NULL;+    At_Root_Block_Obj aa = SASS_MEMORY_NEW(At_Root_Block,                                         a->pstate(),                                         bb,-                                        static_cast<At_Root_Expression*>(ae));-    // aa->block()->is_root(true);-    return aa;+                                        SASS_MEMORY_CAST(At_Root_Query, ae));+    return aa.detach();   } -  Statement* Expand::operator()(At_Rule* a)+  Statement_Ptr Expand::operator()(Directive_Ptr a)   {     LOCAL_FLAG(in_keyframes, a->is_keyframes());-    Block* ab = a->block();-    Selector* as = a->selector();-    Expression* av = a->value();+    Block_Ptr ab = &a->block();+    Selector_Ptr as = &a->selector();+    Expression_Ptr av = &a->value();     selector_stack.push_back(0);     if (av) av = av->perform(&eval);-    if (as) as = dynamic_cast<Selector*>(as->perform(&eval));+    if (as) as = SASS_MEMORY_CAST_PTR(Selector, as->perform(&eval));     selector_stack.pop_back();-    Block* bb = ab ? ab->perform(this)->block() : 0;-    At_Rule* aa = SASS_MEMORY_NEW(ctx.mem, At_Rule,+    Block_Ptr bb = ab ? operator()(ab) : NULL;+    Directive_Ptr aa = SASS_MEMORY_NEW(Directive,                                   a->pstate(),                                   a->keyword(),                                   as,@@ -225,29 +236,40 @@     return aa;   } -  Statement* Expand::operator()(Declaration* d)+  Statement_Ptr Expand::operator()(Declaration_Ptr d)   {-    String* old_p = d->property();-    String* new_p = static_cast<String*>(old_p->perform(&eval));-    Expression* value = d->value()->perform(&eval);-    if (!value || (value->is_invisible() && !d->is_important())) return 0;-    Declaration* decl = SASS_MEMORY_NEW(ctx.mem, Declaration,+    Block_Obj ab = d->block();+    String_Obj old_p = d->property();+    Expression_Obj prop = old_p->perform(&eval);+    String_Obj new_p = SASS_MEMORY_CAST(String, prop);+    // we might get a color back+    if (!new_p) {+      std::string str(prop->to_string(ctx.c_options));+      new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str);+    }+    Expression_Obj value = d->value()->perform(&eval);+    Block_Obj bb = ab ? operator()(&ab) : NULL;+    if (!bb) {+      if (!value || (value->is_invisible() && !d->is_important())) return 0;+    }+    Declaration_Ptr decl = SASS_MEMORY_NEW(Declaration,                                         d->pstate(),                                         new_p,                                         value,-                                        d->is_important());+                                        d->is_important(),+                                        bb);     decl->tabs(d->tabs());     return decl;   } -  Statement* Expand::operator()(Assignment* a)+  Statement_Ptr Expand::operator()(Assignment_Ptr a)   {     Env* env = environment();     std::string var(a->variable());     if (a->is_global()) {       if (a->is_default()) {         if (env->has_global(var)) {-          Expression* e = dynamic_cast<Expression*>(env->get_global(var));+          Expression_Obj e = SASS_MEMORY_CAST(Expression, env->get_global(var));           if (!e || e->concrete_type() == Expression::NULL_VAL) {             env->set_global(var, a->value()->perform(&eval));           }@@ -265,8 +287,8 @@         auto cur = env;         while (cur && cur->is_lexical()) {           if (cur->has_local(var)) {-            if (AST_Node* node = cur->get_local(var)) {-              Expression* e = dynamic_cast<Expression*>(node);+            if (AST_Node_Obj node = cur->get_local(var)) {+              Expression_Obj e = SASS_MEMORY_CAST(Expression, node);               if (!e || e->concrete_type() == Expression::NULL_VAL) {                 cur->set_local(var, a->value()->perform(&eval));               }@@ -281,8 +303,8 @@         throw std::runtime_error("Env not in sync");       }       else if (env->has_global(var)) {-        if (AST_Node* node = env->get_global(var)) {-          Expression* e = dynamic_cast<Expression*>(node);+        if (AST_Node_Obj node = env->get_global(var)) {+          Expression_Obj e = SASS_MEMORY_CAST(Expression, node);           if (!e || e->concrete_type() == Expression::NULL_VAL) {             env->set_global(var, a->value()->perform(&eval));           }@@ -301,23 +323,28 @@     return 0;   } -  Statement* Expand::operator()(Import* imp)+  Statement_Ptr Expand::operator()(Import_Ptr imp)   {-    Import* result = SASS_MEMORY_NEW(ctx.mem, Import, imp->pstate());-    if (imp->media_queries() && imp->media_queries()->size()) {-      Expression* ex = imp->media_queries()->perform(&eval);-      result->media_queries(dynamic_cast<List*>(ex));+    Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate());+    if (imp->import_queries() && imp->import_queries()->size()) {+      Expression_Obj ex = imp->import_queries()->perform(&eval);+      result->import_queries(SASS_MEMORY_CAST(List, ex));     }     for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {       result->urls().push_back(imp->urls()[i]->perform(&eval));     }     // all resources have been dropped for Input_Stubs     // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {}-    return result;+    return result.detach();   } -  Statement* Expand::operator()(Import_Stub* i)+  Statement_Ptr Expand::operator()(Import_Stub_Ptr i)   {+    // get parent node from call stack+    AST_Node_Obj parent = call_stack.back();+    if (SASS_MEMORY_CAST(Block, parent) == NULL) {+      error("Import directives may not be used within control directives or mixins.", i->pstate());+    }     // we don't seem to need that actually afterall     Sass_Import_Entry import = sass_make_import(       i->imp_path().c_str(),@@ -326,66 +353,75 @@     );     ctx.import_stack.push_back(import);     const std::string& abs_path(i->resource().abs_path);-    append_block(ctx.sheets.at(abs_path).root);+    append_block(&ctx.sheets.at(abs_path).root);     sass_delete_import(ctx.import_stack.back());     ctx.import_stack.pop_back();     return 0;   } -  Statement* Expand::operator()(Warning* w)+  Statement_Ptr Expand::operator()(Warning_Ptr w)   {     // eval handles this too, because warnings may occur in functions     w->perform(&eval);     return 0;   } -  Statement* Expand::operator()(Error* e)+  Statement_Ptr Expand::operator()(Error_Ptr e)   {     // eval handles this too, because errors may occur in functions     e->perform(&eval);     return 0;   } -  Statement* Expand::operator()(Debug* d)+  Statement_Ptr Expand::operator()(Debug_Ptr d)   {     // eval handles this too, because warnings may occur in functions     d->perform(&eval);     return 0;   } -  Statement* Expand::operator()(Comment* c)+  Statement_Ptr Expand::operator()(Comment_Ptr c)   {+    eval.is_in_comment = true;+    Comment_Ptr rv = SASS_MEMORY_NEW(Comment, c->pstate(), SASS_MEMORY_CAST_PTR(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 SASS_MEMORY_NEW(ctx.mem, Comment, c->pstate(), static_cast<String*>(c->text()->perform(&eval)), c->is_important());+    return rv;   } -  Statement* Expand::operator()(If* i)+  Statement_Ptr Expand::operator()(If_Ptr i)   {-    if (*i->predicate()->perform(&eval)) {-      append_block(i->block());+    Env env(environment(), true);+    env_stack.push_back(&env);+    call_stack.push_back(i);+    Expression_Obj rv = i->predicate()->perform(&eval);+    if (*rv) {+      append_block(&i->block());     }     else {-      Block* alt = i->alternative();+      Block_Ptr alt = &i->alternative();       if (alt) append_block(alt);     }+    call_stack.pop_back();+    env_stack.pop_back();     return 0;   }    // For does not create a new env scope   // But iteration vars are reset afterwards-  Statement* Expand::operator()(For* f)+  Statement_Ptr Expand::operator()(For_Ptr f)   {     std::string variable(f->variable());-    Expression* low = f->lower_bound()->perform(&eval);+    Expression_Obj low = f->lower_bound()->perform(&eval);     if (low->concrete_type() != Expression::NUMBER) {-      error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace());+      throw Exception::TypeMismatch(*low, "integer");     }-    Expression* high = f->upper_bound()->perform(&eval);+    Expression_Obj high = f->upper_bound()->perform(&eval);     if (high->concrete_type() != Expression::NUMBER) {-      error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace());+      throw Exception::TypeMismatch(*high, "integer");     }-    Number* sass_start = static_cast<Number*>(low);-    Number* sass_end = static_cast<Number*>(high);+    Number_Obj sass_start = SASS_MEMORY_CAST(Number, low);+    Number_Obj sass_end = SASS_MEMORY_CAST(Number, high);     // check if units are valid for sequence     if (sass_start->unit() != sass_end->unit()) {       std::stringstream msg; msg << "Incompatible units: '"@@ -396,18 +432,20 @@     double start = sass_start->value();     double end = sass_end->value();     // only create iterator once in this environment-    Env* env = environment();-    Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());-    AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;-    env->set_local(variable, it);-    Block* body = f->block();+    Env env(environment(), true);+    env_stack.push_back(&env);+    call_stack.push_back(f);+    Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), start, sass_end->unit());+    env.set_local(variable, &it);+    Block_Ptr body = &f->block();     if (start < end) {       if (f->is_inclusive()) ++end;       for (double i = start;            i < end;            ++i) {+        it = SASS_MEMORY_COPY(it);         it->value(i);-        env->set_local(variable, it);+        env.set_local(variable, &it);         append_block(body);       }     } else {@@ -415,131 +453,136 @@       for (double i = start;            i > end;            --i) {+        it = SASS_MEMORY_COPY(it);         it->value(i);-        env->set_local(variable, it);+        env.set_local(variable, &it);         append_block(body);       }     }-    // restore original environment-    if (!old_var) env->del_local(variable);-    else env->set_local(variable, old_var);+    call_stack.pop_back();+    env_stack.pop_back();     return 0;   }    // Eval does not create a new env scope   // But iteration vars are reset afterwards-  Statement* Expand::operator()(Each* e)+  Statement_Ptr Expand::operator()(Each_Ptr e)   {     std::vector<std::string> variables(e->variables());-    Expression* expr = e->list()->perform(&eval);-    List* list = 0;-    Map* map = 0;+    Expression_Obj expr = e->list()->perform(&eval);+    List_Obj list = 0;+    Map_Obj map;     if (expr->concrete_type() == Expression::MAP) {-      map = static_cast<Map*>(expr);+      map = SASS_MEMORY_CAST(Map, expr);     }+    else if (Selector_List_Ptr ls = SASS_MEMORY_CAST(Selector_List, expr)) {+      Listize listize;+      Expression_Obj rv = ls->perform(&listize);+      list = SASS_MEMORY_CAST(List, rv);+    }     else if (expr->concrete_type() != Expression::LIST) {-      list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);-      *list << expr;+      list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);+      list->append(expr);     }     else {-      list = static_cast<List*>(expr);+      list = SASS_MEMORY_CAST(List, expr);     }     // remember variables and then reset them-    Env* env = environment();-    std::vector<AST_Node*> old_vars(variables.size());-    for (size_t i = 0, L = variables.size(); i < L; ++i) {-      old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;-      env->set_local(variables[i], 0);-    }-    Block* body = e->block();+    Env env(environment(), true);+    env_stack.push_back(&env);+    call_stack.push_back(e);+    Block_Ptr body = &e->block();      if (map) {       for (auto key : map->keys()) {-        Expression* k = key->perform(&eval);-        Expression* v = map->at(key)->perform(&eval);+        Expression_Obj k = key->perform(&eval);+        Expression_Obj v = map->at(key)->perform(&eval);          if (variables.size() == 1) {-          List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE);-          *variable << k;-          *variable << v;-          env->set_local(variables[0], variable);+          List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);+          variable->append(k);+          variable->append(v);+          env.set_local(variables[0], &variable);         } else {-          env->set_local(variables[0], k);-          env->set_local(variables[1], v);+          env.set_local(variables[0], &k);+          env.set_local(variables[1], &v);         }         append_block(body);       }     }     else {-      bool arglist = list->is_arglist();+      // bool arglist = list->is_arglist();+      if (list->length() == 1 && SASS_MEMORY_CAST(Selector_List, list)) {+        list = SASS_MEMORY_CAST(List, list);+      }       for (size_t i = 0, L = list->length(); i < L; ++i) {-        Expression* e = (*list)[i];+        Expression_Obj e = list->at(i);         // unwrap value if the expression is an argument-        if (Argument* arg = dynamic_cast<Argument*>(e)) e = arg->value();+        if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, e)) e = arg->value();         // check if we got passed a list of args (investigate)-        if (List* scalars = dynamic_cast<List*>(e)) {+        if (List_Obj scalars = SASS_MEMORY_CAST(List, e)) {           if (variables.size() == 1) {-            Expression* var = scalars;-            if (arglist) var = (*scalars)[0];-            env->set_local(variables[0], var);+            List_Obj var = scalars;+            // if (arglist) var = (*scalars)[0];+            env.set_local(variables[0], &var);           } else {             for (size_t j = 0, K = variables.size(); j < K; ++j) {-              Expression* res = j >= scalars->length()-                ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())+              Expression_Obj res = j >= scalars->length()+                ? SASS_MEMORY_NEW(Null, expr->pstate())                 : (*scalars)[j]->perform(&eval);-              env->set_local(variables[j], res);+              env.set_local(variables[j], &res);             }           }         } else {           if (variables.size() > 0) {-            env->set_local(variables[0], e);+            env.set_local(variables.at(0), &e);             for (size_t j = 1, K = variables.size(); j < K; ++j) {-              Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());-              env->set_local(variables[j], res);+              Expression_Obj res = SASS_MEMORY_NEW(Null, expr->pstate());+              env.set_local(variables[j], &res);             }           }         }         append_block(body);       }     }-    // restore original environment-    for (size_t j = 0, K = variables.size(); j < K; ++j) {-      if(!old_vars[j]) env->del_local(variables[j]);-      else env->set_local(variables[j], old_vars[j]);-    }+    call_stack.pop_back();+    env_stack.pop_back();     return 0;   } -  Statement* Expand::operator()(While* w)+  Statement_Ptr Expand::operator()(While_Ptr w)   {-    Expression* pred = w->predicate();-    Block* body = w->block();-    while (*pred->perform(&eval)) {+    Expression_Obj pred = w->predicate();+    Block_Ptr body = &w->block();+    Env env(environment(), true);+    env_stack.push_back(&env);+    call_stack.push_back(w);+    Expression_Obj cond = pred->perform(&eval);+    while (*&cond) {       append_block(body);+      cond = pred->perform(&eval);     }+    call_stack.pop_back();+    env_stack.pop_back();     return 0;   } -  Statement* Expand::operator()(Return* r)+  Statement_Ptr Expand::operator()(Return_Ptr r)   {     error("@return may only be used within a function", r->pstate(), backtrace());     return 0;   } -  Statement* Expand::operator()(Extension* e)-  {-    To_String to_string(&ctx);-    Selector_List* extender = dynamic_cast<Selector_List*>(selector());-    if (!extender) return 0;-    selector_stack.push_back(0); -    if (Selector_List* selector_list = dynamic_cast<Selector_List*>(e->selector())) {-      for (Complex_Selector* complex_selector : selector_list->elements()) {-        Complex_Selector* tail = complex_selector;+  void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {++    if (Selector_List_Obj sl = SASS_MEMORY_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* header : tail->head()->elements()) {-            if (dynamic_cast<Parent_Selector*>(header) == NULL) continue; // skip all others-            To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string));+          if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {+            if (SASS_MEMORY_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(), backtrace());           }           tail = tail->tail();@@ -547,52 +590,82 @@       }     } -    Selector_List* contextualized = dynamic_cast<Selector_List*>(e->selector()->perform(&eval));-    if (contextualized == NULL) return 0;++    Selector_List_Obj contextualized = SASS_MEMORY_CAST_PTR(Selector_List, s->perform(&eval));+    if (contextualized == false) return;     for (auto complex_sel : contextualized->elements()) {-      Complex_Selector* c = complex_sel;+      Complex_Selector_Obj c = complex_sel;       if (!c->head() || c->tail()) {-        To_String to_string(&ctx); std::string sel_str(contextualized->perform(&to_string));+        std::string sel_str(contextualized->to_string(ctx.c_options));         error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace());       }-      Compound_Selector* placeholder = c->head();-      placeholder->is_optional(e->selector()->is_optional());+      Compound_Selector_Obj placeholder = c->head();+      if (contextualized->is_optional()) placeholder->is_optional(true);       for (size_t i = 0, L = extender->length(); i < L; ++i) {-        Complex_Selector* sel = (*extender)[i];+        Complex_Selector_Obj sel = (*extender)[i];         if (!(sel->head() && sel->head()->length() > 0 &&-            dynamic_cast<Parent_Selector*>((*sel->head())[0])))+            SASS_MEMORY_CAST(Parent_Selector, (*sel->head())[0])))         {-          Compound_Selector* hh = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, (*extender)[i]->pstate());+          Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate());           hh->media_block((*extender)[i]->media_block());-          Complex_Selector* ssel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, (*extender)[i]->pstate());+          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* ps = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, (*extender)[i]->pstate());+          Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate());           ps->media_block((*extender)[i]->media_block());-          *hh << ps;+          hh->append(&ps);           ssel->tail(sel);           ssel->head(hh);           sel = ssel;         }         // if (c->has_line_feed()) sel->has_line_feed(true);-        ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder));+        ctx.subset_map.put(placeholder, std::make_pair(sel, placeholder));       }     } -    selector_stack.pop_back();+  } +  Statement* Expand::operator()(Extension_Ptr e)+  {+    if (Selector_List_Obj extender = SASS_MEMORY_CAST(Selector_List, selector())) {+      Selector_Obj s = e->selector();+      Selector_List_Obj sl = NULL;+      // check if we already have a valid selector list+      if ((sl = SASS_MEMORY_CAST(Selector_List, s))) {}+      // convert selector schema to a selector list+      else if (Selector_Schema_Obj schema = SASS_MEMORY_CAST(Selector_Schema, s)) {+        if (schema->has_real_parent_ref()) {+          sl = eval(&schema);+        } else {+          selector_stack.push_back(0);+          sl = eval(&schema);+          sl->remove_parent_selectors();+          selector_stack.pop_back();+        }+      }+      // abort on invalid selector+      if (sl.isNull()) return NULL;+      for (Complex_Selector_Obj cs : sl->elements()) {+        if (!cs.isNull() && !cs->head().isNull()) {+          cs->head()->media_block(media_block_stack.back());+        }+      }+      selector_stack.push_back(0);+      expand_selector_list(&sl, extender);+      selector_stack.pop_back();+    }     return 0;   } -  Statement* Expand::operator()(Definition* d)+  Statement_Ptr Expand::operator()(Definition_Ptr d)   {     Env* env = environment();-    Definition* dd = SASS_MEMORY_NEW(ctx.mem, Definition, *d);+    Definition_Obj dd = SASS_MEMORY_COPY(d);     env->local_frame()[d->name() +-                        (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;+                        (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = &dd;      if (d->type() == Definition::FUNCTION && (-      d->name() == "calc"       ||+      Prelexer::calc_fn_call(d->name().c_str()) ||       d->name() == "element"    ||       d->name() == "expression" ||       d->name() == "url"@@ -604,86 +677,113 @@       );     } -     // set the static link so we can have lexical scoping     dd->environment(env);     return 0;   } -  Statement* Expand::operator()(Mixin_Call* c)+  Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)   {++    if (recursions > maxRecursion) {+      throw Exception::StackError(*c);+    }++    recursions ++;+     Env* env = environment();     std::string full_name(c->name() + "[m]");     if (!env->has(full_name)) {       error("no mixin named " + c->name(), c->pstate(), backtrace());     }-    Definition* def = static_cast<Definition*>((*env)[full_name]);-    Block* body = def->block();-    Parameters* params = def->parameters();+    Definition_Obj def = SASS_MEMORY_CAST(Definition, (*env)[full_name]);+    Block_Obj body = def->block();+    Parameters_Obj params = def->parameters();      if (c->block() && c->name() != "@content" && !body->has_content()) {       error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace());     }-    Arguments* args = static_cast<Arguments*>(c->arguments()-                                               ->perform(&eval));+    Expression_Obj rv = c->arguments()->perform(&eval);+    Arguments_Obj args = SASS_MEMORY_CAST(Arguments, rv);     Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`");     backtrace_stack.push_back(&new_bt);     Env new_env(def->environment());     env_stack.push_back(&new_env);     if (c->block()) {       // represent mixin content blocks as thunks/closures-      Definition* thunk = SASS_MEMORY_NEW(ctx.mem, Definition,+      Definition_Obj thunk = SASS_MEMORY_NEW(Definition,                                           c->pstate(),                                           "@content",-                                          SASS_MEMORY_NEW(ctx.mem, Parameters, c->pstate()),+                                          SASS_MEMORY_NEW(Parameters, c->pstate()),                                           c->block(),                                           Definition::MIXIN);       thunk->environment(env);-      new_env.local_frame()["@content[m]"] = thunk;+      new_env.local_frame()["@content[m]"] = &thunk;     }      bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);-    append_block(body);-    backtrace_stack.pop_back();++    Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());+    Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);+++    block_stack.push_back(&trace_block);+    for (auto bb : body->elements()) {+      Statement_Obj ith = bb->perform(this);+      if (ith) trace->block()->append(ith);+    }+    block_stack.pop_back();+     env_stack.pop_back();-    return 0;+    backtrace_stack.pop_back();++    recursions --;+    return trace.detach();   } -  Statement* Expand::operator()(Content* c)+  Statement_Ptr Expand::operator()(Content_Ptr 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);     }-    Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call,++    Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call,                                        c->pstate(),                                        "@content",-                                       SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate()));-    Statement* stm = call->perform(this);+                                       SASS_MEMORY_NEW(Arguments, c->pstate()));++    Trace_Obj trace = SASS_MEMORY_CAST_PTR(Trace, call->perform(this));+     if (block_stack.back()->is_root()) {       selector_stack.pop_back();     }-    return stm;++    return trace.detach();   }    // produce an error if something is not implemented-  inline Statement* Expand::fallback_impl(AST_Node* n)+  inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n)   {     std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();-    String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), err);+    String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err);     error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace());-    return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg);+    return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), &msg);   }    // process and add to last block on stack-  inline void Expand::append_block(Block* b)+  inline void Expand::append_block(Block_Ptr b)   {+    if (b->is_root()) call_stack.push_back(b);     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* ith = (*b)[i]->perform(this);-      if (ith) *block_stack.back() << ith;+      Statement_Ptr stm = &b->at(i);+      Statement_Obj ith = stm->perform(this);+      if (ith) block_stack.back()->append(ith);     }+    if (b->is_root()) call_stack.pop_back();   }  }
libsass/src/expand.hpp view
@@ -13,65 +13,69 @@   class Listize;   class Context;   class Eval;-  typedef Environment<AST_Node*> Env;+  typedef Environment<AST_Node_Obj> Env;   struct Backtrace; -  class Expand : public Operation_CRTP<Statement*, Expand> {+  class Expand : public Operation_CRTP<Statement_Ptr, Expand> {   public:      Env* environment();     Context& context();-    Selector_List* selector();+    Selector_List_Obj selector();     Backtrace* backtrace();      Context&          ctx;     Eval              eval;+    size_t            recursions;+    bool              in_keyframes;+    bool              at_root_without_rule;+    bool              old_at_root_without_rule;      // it's easier to work with vectors-    std::vector<Env*>      env_stack;-    std::vector<Block*>    block_stack;-    std::vector<String*>   property_stack;-    std::vector<Selector_List*> selector_stack;-    std::vector<Backtrace*>backtrace_stack;-    bool              in_keyframes;+    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;+    std::vector<Backtrace*>        backtrace_stack; -    Statement* fallback_impl(AST_Node* n);+    Statement_Ptr fallback_impl(AST_Node_Ptr n); -  public:-    Expand(Context&, Env*, Backtrace*);-    virtual ~Expand() { }+  private:+    void expand_selector_list(Selector_Obj, Selector_List_Obj extender); -    using Operation<Statement*>::operator();+  public:+    Expand(Context&, Env*, Backtrace*, std::vector<Selector_List_Obj>* stack = NULL);+    ~Expand() { } -    Statement* operator()(Block*);-    Statement* operator()(Ruleset*);-    Statement* operator()(Propset*);-    Statement* operator()(Media_Block*);-    Statement* operator()(Supports_Block*);-    Statement* operator()(At_Root_Block*);-    Statement* operator()(At_Rule*);-    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()(Extension*);-    Statement* operator()(Definition*);-    Statement* operator()(Mixin_Call*);-    Statement* operator()(Content*);+    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);      template <typename U>-    Statement* fallback(U x) { return fallback_impl(x); }+    Statement_Ptr fallback(U x) { return fallback_impl(x); } -    void append_block(Block*);+    void append_block(Block_Ptr);   };  }
libsass/src/extend.cpp view
@@ -1,21 +1,17 @@-#ifdef _MSC_VER-#pragma warning(disable : 4503)-#endif-+#include "sass.hpp" #include "extend.hpp" #include "context.hpp"-#include "to_string.hpp" #include "backtrace.hpp" #include "paths.hpp" #include "parser.hpp" #include "node.hpp" #include "sass_util.hpp"+#include "remove_placeholders.hpp" #include "debug.hpp" #include <iostream> #include <deque> #include <set> - /*  NOTES: @@ -65,7 +61,7 @@ namespace Sass {  -  typedef std::pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+  typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> ExtensionPair;   typedef std::vector<ExtensionPair> SubsetMapEntries;  #ifdef DEBUG@@ -77,7 +73,7 @@       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;+      case Complex_Selector::REFERENCE:   os << "\"/\""; break;     }      return os;@@ -85,23 +81,20 @@     std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) {-    To_String to_string;     for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) {       if (i > 0) os << ", ";-      os << compoundSelector[i]->perform(&to_string);+      os << compoundSelector[i]->to_string();     }     return os;   }    std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) {-    To_String to_string;-    os << simpleSelector.perform(&to_string);+    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) {-    To_String to_string;      if (message) {       std::cerr << message;@@ -119,13 +112,12 @@   }    // Print a string representation of a Compound_Selector-    typedef std::pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+    typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SelsNewSeqPair;     typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection;     // Print a string representation of a Compound_Selector-  static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) {-    To_String to_string;+  static void printCompoundSelector(Compound_Selector_Ptr pCompoundSelector, const char* message=NULL, bool newline=true) {      if (message) {       std::cerr << message;@@ -144,10 +136,9 @@     std::ostream& operator<<(std::ostream& os, Complex_Selector& complexSelector) {-    To_String to_string;      os << "[";-    Complex_Selector* pIter = &complexSelector;+    Complex_Selector_Ptr pIter = &complexSelector;     bool first = true;     while (pIter) {       if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {@@ -164,7 +155,7 @@       first = false;        if (pIter->head()) {-        os << pIter->head()->perform(&to_string);+        os << pIter->head()->to_string();       } else {         os << "NULL_HEAD";       }@@ -178,8 +169,7 @@     // Print a string representation of a Complex_Selector-  static void printComplexSelector(Complex_Selector* pComplexSelector, const char* message=NULL, bool newline=true) {-    To_String to_string;+  static void printComplexSelector(Complex_Selector_Ptr pComplexSelector, const char* message=NULL, bool newline=true) {      if (message) {       std::cerr << message;@@ -197,7 +187,6 @@   }    static void printSelsNewSeqPairCollection(SelsNewSeqPairCollection& collection, const char* message=NULL, bool newline=true) {-    To_String to_string;      if (message) {       std::cerr << message;@@ -211,8 +200,8 @@         std::cerr << ", ";       }       std::cerr << "[";-      Compound_Selector* pSels = pair.first;-      Complex_Selector* pNewSelector = pair.second;+      Compound_Selector_Ptr pSels = pair.first;+      Complex_Selector_Ptr pNewSelector = pair.second;       std::cerr << "[" << *pSels << "], ";       printComplexSelector(pNewSelector, NULL, false);     }@@ -225,7 +214,6 @@    // Print a string representation of a SourcesSet   static void printSourcesSet(SourcesSet& sources, Context& ctx, const char* message=NULL, bool newline=true) {-    To_String to_string;      if (message) {       std::cerr << message;@@ -236,7 +224,7 @@     typedef std::deque<std::string> SourceStrings;     SourceStrings sourceStrings;     for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {-      Complex_Selector* pSource = *iterator;+      Complex_Selector_Ptr pSource = *iterator;       std::stringstream sstream;       sstream << complexSelectorToNode(pSource, ctx);       sourceStrings.push_back(sstream.str());@@ -265,8 +253,8 @@     os << "SUBSET_MAP_ENTRIES[";      for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {-      Complex_Selector* pExtComplexSelector = iterator->first;    // The selector up to where the @extend is (ie, the thing to merge)-      Compound_Selector* pExtCompoundSelector = iterator->second; // The stuff after the @extend+      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 << ", ";@@ -298,14 +286,14 @@   } #endif -  static bool parentSuperselector(Complex_Selector* pOne, Complex_Selector* pTwo, Context& ctx) {+  static bool parentSuperselector(Complex_Selector_Ptr pOne, Complex_Selector_Ptr pTwo, Context& ctx) {     // 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...     Position noPosition(-1, -1, -1);-    Type_Selector fakeParent(ParserState("[FAKE]"), "temp");-    Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/);-    fakeHead.elements().push_back(&fakeParent);-    Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);+    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);@@ -329,8 +317,8 @@     Node result = Node::createCollection();      for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {-      Complex_Selector* pChild = *iter;-      result.collection()->push_back(complexSelectorToNode(pChild, ctx));+      Complex_Selector_Obj pChild = *iter;+      result.collection()->push_back(complexSelectorToNode(&pChild, ctx));     }      return result;@@ -342,7 +330,7 @@      Context& mCtx; -    bool operator()(Complex_Selector* pOne, Complex_Selector* pTwo, Complex_Selector*& pOut) const {+    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|@@ -362,12 +350,12 @@         return false;       } -      if (parentSuperselector(pOne, pTwo, mCtx)) {+      if (parentSuperselector(&pOne, &pTwo, mCtx)) {         pOut = pTwo;         return true;       } -      if (parentSuperselector(pTwo, pOne, mCtx)) {+      if (parentSuperselector(&pTwo, &pOne, mCtx)) {         pOut = pOne;         return true;       }@@ -393,7 +381,7 @@     }  -    Complex_Selector* pCompareOut = NULL;+    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);@@ -430,9 +418,9 @@      for (size_t i = 1; i < x.size(); i++) {       for (size_t j = 1; j < y.size(); j++) {-        Complex_Selector* pCompareOut = NULL;+        Complex_Selector_Obj pCompareOut; -        if (comparator(x[i], y[j], 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]);@@ -570,7 +558,7 @@       for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) {         Node& seq1 = *seqs1Iter; -        Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx);+        Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1, ctx);          // 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.@@ -587,7 +575,7 @@         DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIM SOURCES: "))          for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {-          const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;+          const Complex_Selector_Obj& pCurrentSelector = *sourcesSetIterator;           maxSpecificity = std::max(maxSpecificity, pCurrentSelector->specificity());         } @@ -617,7 +605,7 @@           for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) {             Node& seq2 = *seqs2Iter; -            Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx);+            Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2, ctx);              DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity())             DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))@@ -665,14 +653,14 @@     // 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...     Position noPosition(-1, -1, -1);-    Type_Selector fakeParent(ParserState("[FAKE]"), "temp");-    Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/);-    fakeHead.elements().push_back(&fakeParent);-    Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);+    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* pOneWithFakeParent = nodeToComplexSelector(one, ctx);+    Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one, ctx);     pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);-    Complex_Selector* pTwoWithFakeParent = nodeToComplexSelector(two, ctx);+    Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two, ctx);     pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);      return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);@@ -860,7 +848,7 @@     DefaultLcsComparator lcsDefaultComparator;     Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx); -    if (!(opsLcs == ops1 || opsLcs == ops2)) {+    if (!(nodesEqual(opsLcs, ops1, true) || nodesEqual(opsLcs, ops2, true))) {       return Node::createNil();     } @@ -957,7 +945,7 @@        // 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)) {+      if (!(nodesEqual(opsLcs, ops1, true) || nodesEqual(opsLcs, ops2, true))) {         return Node::createNil();       } @@ -998,9 +986,9 @@           DEBUG_PRINTLN(ALL, "sel1: " << sel1)           DEBUG_PRINTLN(ALL, "sel2: " << sel2) -          Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+          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* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);+          Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(&sel2.selector()->head(), ctx);           pMergedWrapper->head(pMerged);            DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1023,7 +1011,7 @@            if (pMerged) {             Node mergedPerm = Node::createCollection();-            mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));+            mergedPerm.collection()->push_back(Node::createSelector(&pMergedWrapper, ctx));             mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));             newRes.collection()->push_back(mergedPerm);           }@@ -1057,9 +1045,9 @@             DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel)             DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel) -            Complex_Selector* pMergedWrapper = plusSel.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+            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* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head(), ctx);+            Compound_Selector_Ptr pMerged = plusSel.selector()->head()->unify_with(&tildeSel.selector()->head(), ctx);             pMergedWrapper->head(pMerged);              DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1075,7 +1063,7 @@              if (pMerged) {               Node mergedPerm = Node::createCollection();-              mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));+              mergedPerm.collection()->push_back(Node::createSelector(&pMergedWrapper, ctx));               mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));               newRes.collection()->push_back(mergedPerm);             }@@ -1106,9 +1094,9 @@         DEBUG_PRINTLN(ALL, "sel1: " << sel1)         DEBUG_PRINTLN(ALL, "sel2: " << sel2) -        Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+        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* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);+        Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(&sel2.selector()->head(), ctx);         pMergedWrapper->head(pMerged);          DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1118,7 +1106,7 @@         }          res.collection()->push_front(op1);-        res.collection()->push_front(Node::createSelector(pMergedWrapper, ctx));+        res.collection()->push_front(Node::createSelector(&pMergedWrapper, ctx));          DEBUG_PRINTLN(ALL, "RESULT: " << res) @@ -1365,20 +1353,20 @@      // Check for the simple cases     if (one.isNil()) {-      out.collection()->push_back(two.clone(ctx));+      out.collection()->push_back(two.klone(ctx));     } else if (two.isNil()) {-      out.collection()->push_back(one.clone(ctx));+      out.collection()->push_back(one.klone(ctx));     } 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.clone(ctx);-      Node twoCloned = two.clone(ctx);+      Node firstPerm = one.klone(ctx);+      Node twoCloned = two.klone(ctx);       firstPerm.plus(twoCloned);       out.collection()->push_back(firstPerm); -      Node secondPerm = two.clone(ctx);-      Node oneCloned = one.clone(ctx);+      Node secondPerm = two.klone(ctx);+      Node oneCloned = one.klone(ctx);       secondPerm.plus(oneCloned );       out.collection()->push_back(secondPerm);     }@@ -1472,9 +1460,10 @@     afters.plus(path);      while (!afters.collection()->empty()) {-      Node current = afters.collection()->front().clone(ctx);+      Node current = afters.collection()->front().klone(ctx);       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());@@ -1519,9 +1508,9 @@   // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively   // call extendComplexSelector again.   static Node extendComplexSelector(-    Complex_Selector* pComplexSelector,+    Complex_Selector_Ptr pComplexSelector,     Context& ctx,-    ExtensionSubsetMap& subset_map,+    Subset_Map& subset_map,     std::set<Compound_Selector> seen, bool isReplace, bool isOriginal);  @@ -1542,14 +1531,14 @@   class GroupByToAFunctor {   public:     KeyType operator()(ExtensionPair& extPair) const {-      Complex_Selector* pSelector = extPair.first;-      return *pSelector;+      Complex_Selector_Obj pSelector = extPair.first;+      return &pSelector;     }   };   static Node extendCompoundSelector(-    Compound_Selector* pSelector,+    Compound_Selector_Ptr pSelector,     Context& ctx,-    ExtensionSubsetMap& subset_map,+    Subset_Map& subset_map,     std::set<Compound_Selector> seen, bool isReplace) {      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))@@ -1558,17 +1547,15 @@     Node extendedSelectors = Node::createCollection();     // extendedSelectors.got_line_feed = true; -    To_String to_string;--    SubsetMapEntries entries = subset_map.get_v(pSelector->to_str_vec());+    SubsetMapEntries entries = subset_map.get_v(pSelector); -    typedef std::vector<std::pair<Complex_Selector, std::vector<ExtensionPair> > > GroupedByToAResult;+    typedef std::vector<std::pair<Complex_Selector_Obj, std::vector<ExtensionPair> > > GroupedByToAResult; -    GroupByToAFunctor<Complex_Selector> extPairKeyFunctor;+    GroupByToAFunctor<Complex_Selector_Obj> extPairKeyFunctor;     GroupedByToAResult arr;     group_by_to_a(entries, extPairKeyFunctor, arr); -    typedef std::pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+    typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SelsNewSeqPair;     typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection;  @@ -1576,45 +1563,42 @@       for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) {-      std::pair<Complex_Selector, std::vector<ExtensionPair> >& groupedPair = *groupedIter;+      std::pair<Complex_Selector_Obj, std::vector<ExtensionPair> >& groupedPair = *groupedIter; -      Complex_Selector& seq = groupedPair.first;+      Complex_Selector_Obj seq = groupedPair.first;       std::vector<ExtensionPair>& group = groupedPair.second;        DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) --      Compound_Selector* pSels = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate());+// changing this makes aua+      Compound_Selector_Obj pSels = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());       for (std::vector<ExtensionPair>::iterator groupIter = group.begin(), groupIterEnd = group.end(); groupIter != groupIterEnd; groupIter++) {         ExtensionPair& pair = *groupIter;-        Compound_Selector* pCompound = pair.second;+        Compound_Selector_Obj pCompound = pair.second;         for (size_t index = 0; index < pCompound->length(); index++) {-          Simple_Selector* pSimpleSelector = (*pCompound)[index];-          (*pSels) << pSimpleSelector;+          Simple_Selector_Obj pSimpleSelector = (*pCompound)[index];+          pSels->append(&pSimpleSelector);+          pCompound->extended(true);         }       } -      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))+      DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(&pSels, "SELS: ")) -      Complex_Selector* pExtComplexSelector = &seq;    // The selector up to where the @extend is (ie, the thing to merge)-      Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions+      Complex_Selector_Ptr pExtComplexSelector = &seq;    // The selector up to where the @extend is (ie, the thing to merge)        // 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* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx);+      Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(&pSels, ctx);        DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: "))       DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) -      Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->last()->head();-      Compound_Selector* pUnifiedSelector = NULL;+      Compound_Selector_Obj pInnermostCompoundSelector = pExtComplexSelector->last()->head();        if (!pInnermostCompoundSelector) {-        pInnermostCompoundSelector = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate());+        pInnermostCompoundSelector = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());       }--      pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx);-+      Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(&pSelectorWithoutExtendSelectors, ctx);        DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: "))       DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))@@ -1632,22 +1616,22 @@       // 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* pNewSelector = pExtComplexSelector->cloneFully(ctx); // ->first();+      Complex_Selector_Obj pNewSelector = SASS_MEMORY_CLONE(pExtComplexSelector); // ->first(); -      Complex_Selector* pNewInnerMost = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL);+      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);+      pNewSelector->set_innermost(&pNewInnerMost, combinator);  #ifdef DEBUG       SourcesSet debugSet;       debugSet = pNewSelector->sources();       if (debugSet.size() > 0) {-        throw "The new selector should start with no sources. Something needs to be cloned to fix this.";+        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 "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.";+        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 @@ -1671,7 +1655,7 @@       DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))  -      if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);;+      if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);        holder.push_back(std::make_pair(pSels, pNewSelector));     }@@ -1680,8 +1664,8 @@     for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) {       SelsNewSeqPair& pair = *holderIter; -      Compound_Selector* pSels = pair.first;-      Complex_Selector* pNewSelector = pair.second;+      Compound_Selector_Obj pSels = pair.first;+      Complex_Selector_Obj pNewSelector = pair.second;         // RUBY??: next [] if seen.include?(sels)@@ -1695,13 +1679,13 @@         DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx))-      Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen, isReplace, false); // !:isOriginal+      Node recurseExtendedSelectors = extendComplexSelector(&pNewSelector, ctx, subset_map, 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;+        Node newSelector = *iterator;  //        DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << extendedSelectors) //        DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/));@@ -1720,30 +1704,30 @@     static bool complexSelectorHasExtension(-    Complex_Selector* pComplexSelector,+    Complex_Selector_Ptr pComplexSelector,     Context& ctx,-    ExtensionSubsetMap& subset_map) {+    Subset_Map& subset_map,+    std::set<Compound_Selector>& seen) {      bool hasExtension = false; -    Complex_Selector* pIter = pComplexSelector;+    Complex_Selector_Obj pIter = pComplexSelector;      while (!hasExtension && pIter) {-      Compound_Selector* pHead = pIter->head();+      Compound_Selector_Obj pHead = pIter->head();        if (pHead) {-        SubsetMapEntries entries = subset_map.get_v(pHead->to_str_vec());+        SubsetMapEntries entries = subset_map.get_v(pHead);         for (ExtensionPair ext : entries) {           // check if both selectors have the same media block parent-          if (ext.first->media_block() == pComplexSelector->media_block()) continue;-          To_String to_string(&ctx);+          // if (ext.first->media_block() == pComplexSelector->media_block()) continue;           if (ext.second->media_block() == 0) continue;-          if (pComplexSelector->media_block() &&+          if (pHead->media_block() &&               ext.second->media_block()->media_queries() &&-              pComplexSelector->media_block()->media_queries()+              pHead->media_block()->media_queries()           ) {-            std::string query_left(ext.second->media_block()->media_queries()->perform(&to_string));-            std::string query_right(pComplexSelector->media_block()->media_queries()->perform(&to_string));+            std::string query_left(ext.second->media_block()->media_queries()->to_string(ctx.c_options));+            std::string query_right(pHead->media_block()->media_queries()->to_string(ctx.c_options));             if (query_left == query_right) continue;           } @@ -1754,7 +1738,7 @@           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->perform(&to_string) << "\"";+          err << "From \"@extend " << ext.second->to_string(ctx.c_options) << "\"";           err << " on line " << pstate.line+1 << " of " << rel_path << "\n";           error(err.str(), pComplexSelector->pstate());         }@@ -1764,29 +1748,6 @@       pIter = pIter->tail();     } -    if (!hasExtension) {-      /* ToDo: don't break stuff-      std::stringstream err;-      To_String to_string(&ctx);-      std::string cwd(Sass::File::get_cwd());-      std::string sel1(pComplexSelector->perform(&to_string));-      Compound_Selector* pExtendSelector = 0;-      for (auto i : subset_map.values()) {-        if (i.first == pComplexSelector) {-          pExtendSelector = i.second;-          break;-        }-      }-      if (!pExtendSelector || !pExtendSelector->is_optional()) {-        std::string sel2(pExtendSelector ? pExtendSelector->perform(&to_string) : "[unknown]");-        err << "\"" << sel1 << "\" failed to @extend \"" << sel2 << "\"\n";-        err << "The selector \"" << sel2 << "\" was not found.\n";-        err << "Use \"@extend " << sel2 << " !optional\" if the extend should be able to fail.";-        error(err.str(), pExtendSelector ? pExtendSelector->pstate() : pComplexSelector->pstate());-      }-      */-    }-     return hasExtension;   } @@ -1803,9 +1764,9 @@      next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)    */   static Node extendComplexSelector(-    Complex_Selector* pComplexSelector,+    Complex_Selector_Ptr pComplexSelector,     Context& ctx,-    ExtensionSubsetMap& subset_map,+    Subset_Map& subset_map,     std::set<Compound_Selector> seen, bool isReplace, bool isOriginal) {      Node complexSelector = complexSelectorToNode(pComplexSelector, ctx);@@ -1835,17 +1796,17 @@         continue;       } -      Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head();+      Compound_Selector_Obj pCompoundSelector = sseqOrOp.selector()->head();        // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)-      Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen, isReplace);+      Node extended = extendCompoundSelector(&pCompoundSelector, ctx, subset_map, seen, isReplace);       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 an ruby Array instead of a Sequence       // due to the member mapping: choices = extended.map {|seq| seq.members}-      Complex_Selector* pJustCurrentCompoundSelector = sseqOrOp.selector();+      Complex_Selector_Obj pJustCurrentCompoundSelector = sseqOrOp.selector();        // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder?       if (isOriginal && !pComplexSelector->has_placeholder()) {@@ -1859,7 +1820,7 @@       for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end();            iterator != endIterator; ++iterator) {         Node& childNode = *iterator;-        Complex_Selector* pExtensionSelector = nodeToComplexSelector(childNode, ctx);+        Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode, ctx);         if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) {           isSuperselector = true;           break;@@ -1868,7 +1829,7 @@        if (!isSuperselector) {         if (sseqOrOp.got_line_feed) pJustCurrentCompoundSelector->has_line_feed(sseqOrOp.got_line_feed);-        extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx));+        extended.collection()->push_front(complexSelectorToNode(&pJustCurrentCompoundSelector, ctx));       }        DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)@@ -1926,35 +1887,40 @@   /*    This is the equivalent of ruby's CommaSequence.do_extend.   */-  Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) {+  Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething) {+    std::set<Compound_Selector> seen;+    return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);+  } -    To_String to_string(&ctx);+  /*+   This is the equivalent of ruby's CommaSequence.do_extend.+  */+  Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen) { -    Selector_List* pNewSelectors = SASS_MEMORY_NEW(ctx.mem, Selector_List, pSelectorList->pstate(), pSelectorList->length());+    Selector_List_Obj pNewSelectors = SASS_MEMORY_NEW(Selector_List, pSelectorList->pstate(), pSelectorList->length());      extendedSomething = false;      for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {-      Complex_Selector* pSelector = (*pSelectorList)[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, ctx, subset_map)) {-        *pNewSelectors << pSelector;+      if (!complexSelectorHasExtension(&pSelector, ctx, subset_map, seen)) {+        pNewSelectors->append(&pSelector);         continue;       }        extendedSomething = true; -      std::set<Compound_Selector> seen;--      Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace, true);+      Node extendedSelectors = extendComplexSelector(&pSelector, ctx, subset_map, seen, isReplace, true);       if (!pSelector->has_placeholder()) {-        if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {-          *pNewSelectors << pSelector;+        if (!extendedSelectors.contains(complexSelectorToNode(&pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {+          pNewSelectors->append(pSelector);+          continue;         }       } @@ -1963,16 +1929,84 @@         if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue;          Node& childNode = *iterator;-        *pNewSelectors << nodeToComplexSelector(childNode, ctx);+        pNewSelectors->append(nodeToComplexSelector(childNode, ctx));       }     } -    return pNewSelectors;+    Remove_Placeholders remove_placeholders(ctx);+    // 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()) {+          std::set<Compound_Selector> 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 = SASS_MEMORY_CAST(Wrapped_Selector, hs)) {+              ws->selector(SASS_MEMORY_CLONE(ws->selector()));+              if (Selector_List_Obj sl = SASS_MEMORY_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 selectors+                else {+                  // extend the inner list of wrapped selector+                  Selector_List_Obj ext_sl = extendSelectorList(sl, ctx, subset_map, 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+                      if (ext_cs->first() && ext_cs->first()->head()->length() > 0) {+                        Wrapped_Selector_Ptr ext_ws = SASS_MEMORY_CAST(Wrapped_Selector, ext_cs->first()->head()->first());+                        if (ext_ws/* && ext_cs->length() == 1*/) {+                          Selector_List_Obj ws_cs = SASS_MEMORY_CAST(Selector_List, ext_ws->selector());+                          Compound_Selector_Obj ws_ss = ws_cs->first()->head();+                          if (!(+                            SASS_MEMORY_CAST(Pseudo_Selector, ws_ss->first()) ||+                            SASS_MEMORY_CAST(Element_Selector, ws_ss->first()) ||+                            SASS_MEMORY_CAST(Placeholder_Selector, ws_ss->first())+                          )) continue;+                        }+                        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);+                    }+                  }+                }+              } else {+                cpy_head->append(&hs);+              }+            } else {+              cpy_head->append(&hs);+            }+          }+          // replace header+          cur->head(cpy_head);+        }+        // process tail+        cur = cur->tail();+      }+    }+    return pNewSelectors.detach();+   }  -  bool shouldExtendBlock(Block* b) {+  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:@@ -1988,9 +2022,9 @@     // there are no child statements. However .a .b should have extensions applied.      for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* stm = (*b)[i];+      Statement_Obj stm = b->at(i); -      if (typeid(*stm) == typeid(Ruleset)) {+      if (dynamic_cast<Ruleset_Ptr>(&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 {@@ -2005,10 +2039,9 @@    // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change.   template <typename ObjectType>-  static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subset_map) {-    To_String to_string(&ctx);+  static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, Subset_Map& subset_map) { -    DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))+    DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List_Ptr>(pObject->selector())->to_string(ctx.c_options))      // 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.@@ -2018,13 +2051,13 @@     }      bool extendedSomething = false;-    Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subset_map, false, extendedSomething);+    Selector_List_Obj pNewSelectorList = Extend::extendSelectorList(SASS_MEMORY_CAST(Selector_List, pObject->selector()), ctx, subset_map, false, extendedSomething);      if (extendedSomething && pNewSelectorList) {-      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))-      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string))+      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List_Ptr>(pObject->selector())->to_string(ctx.c_options))+      DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string(ctx.c_options))       pNewSelectorList->remove_parent_selectors();-      pObject->selector(pNewSelectorList);+      pObject->selector(&pNewSelectorList);     } else {       DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING")     }@@ -2032,36 +2065,56 @@   -  Extend::Extend(Context& ctx, ExtensionSubsetMap& ssm)+  Extend::Extend(Context& ctx, Subset_Map& ssm)   : ctx(ctx), subset_map(ssm)   { } -  void Extend::operator()(Block* b)+  void Extend::operator()(Block_Ptr b)   {     for (size_t i = 0, L = b->length(); i < L; ++i) {-      (*b)[i]->perform(this);+      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 = it.first ? &it.first->first() : NULL;+        Compound_Selector_Ptr ext = it.second ? &it.second : NULL;+        if (ext && (ext->extended() || ext->is_optional())) continue;+        std::string str_sel(sel->to_string({ NESTED, 5 }));+        std::string str_ext(ext->to_string({ NESTED, 5 }));+        // 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->pstate());+      }+    }+   } -  void Extend::operator()(Ruleset* pRuleset)+  void Extend::operator()(Ruleset_Ptr pRuleset)   {-    extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map);+    extendObjectWithSelectorAndBlock( pRuleset, ctx, subset_map);     pRuleset->block()->perform(this);   } -  void Extend::operator()(Supports_Block* pFeatureBlock)+  void Extend::operator()(Supports_Block_Ptr pFeatureBlock)   {     pFeatureBlock->block()->perform(this);   } -  void Extend::operator()(Media_Block* pMediaBlock)+  void Extend::operator()(Media_Block_Ptr pMediaBlock)   {     pMediaBlock->block()->perform(this);   } -  void Extend::operator()(At_Rule* a)+  void Extend::operator()(Directive_Ptr a)   {-    // Selector_List* ls = dynamic_cast<Selector_List*>(a->selector());+    // Selector_List_Ptr ls = dynamic_cast<Selector_List_Ptr>(a->selector());     // selector_stack.push_back(ls);     if (a->block()) a->block()->perform(this);     // exp.selector_stack.pop_back();
libsass/src/extend.hpp view
@@ -2,6 +2,7 @@ #define SASS_EXTEND_H  #include <string>+#include <set>  #include "ast.hpp" #include "operation.hpp"@@ -12,28 +13,34 @@   class Context;   class Node; -  typedef Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;-   class Extend : public Operation_CRTP<void, Extend> {      Context&            ctx;-    ExtensionSubsetMap& subset_map;+    Subset_Map& subset_map; -    void fallback_impl(AST_Node* n) { }+    void fallback_impl(AST_Node_Ptr n) { }    public:     static Node subweave(Node& one, Node& two, Context& ctx);-    static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething);-    Extend(Context&, ExtensionSubsetMap&);-    virtual ~Extend() { }--    using Operation<void>::operator();+    static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen);+    static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething);+    static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace = false) {+      bool extendedSomething = false;+      return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething);+    }+    static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, std::set<Compound_Selector>& seen) {+      bool isReplace = false;+      bool extendedSomething = false;+      return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);+    }+    Extend(Context&, Subset_Map&);+    ~Extend() { } -    void operator()(Block*);-    void operator()(Ruleset*);-    void operator()(Supports_Block*);-    void operator()(Media_Block*);-    void operator()(At_Rule*);+    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); }
libsass/src/file.cpp view
@@ -1,19 +1,15 @@ #ifdef _WIN32-#ifdef __MINGW32__-#ifndef off64_t-#define off64_t _off64_t    /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */-#endif-#endif-#include <direct.h>-#define getcwd _getcwd-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)+# ifdef __MINGW32__+#  ifndef off64_t+#   define off64_t _off64_t    /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */+#  endif+# endif+# include <direct.h>+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #else-#include <unistd.h>-#endif-#ifdef _MSC_VER-#define NOMINMAX+# include <unistd.h> #endif-+#include "sass.hpp" #include <iostream> #include <fstream> #include <cctype>@@ -27,15 +23,24 @@ #include "sass2scss.h"  #ifdef _WIN32-#include <windows.h>-#endif+# include <windows.h> -#ifndef FS_CASE_SENSITIVE-#ifdef _WIN32-#define FS_CASE_SENSITIVE 0-#else-#define FS_CASE_SENSITIVE 1-#endif+# ifdef _MSC_VER+# include <codecvt>+inline static std::string wstring_to_string(const std::wstring& wstr)+{+    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_converter;+    return wchar_converter.to_bytes(wstr);+}+# else // mingw(/gcc) does not support C++11's codecvt yet.+inline static std::string wstring_to_string(const std::wstring &wstr)+{+    int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);+    std::string strTo(size_needed, 0);+    WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);+    return strTo;+}+# endif #endif  namespace Sass {@@ -46,9 +51,12 @@     std::string get_cwd()     {       const size_t wd_len = 1024;-      char wd[wd_len];-      std::string cwd = getcwd(wd, wd_len);-      #ifdef _WIN32+      #ifndef _WIN32+        char wd[wd_len];+        std::string cwd = getcwd(wd, wd_len);+      #else+        wchar_t wd[wd_len];+        std::string cwd = wstring_to_string(_wgetcwd(wd, wd_len));         //convert backslashes to forward slashes         replace(cwd.begin(), cwd.end(), '\\', '/');       #endif@@ -184,8 +192,11 @@       if (l[l.length()-1] != '/') l += '/';        while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {-        r = r.substr(3);-        size_t pos = find_last_folder_separator(l, l.length() - 2);+        size_t L = l.length(), pos = find_last_folder_separator(l, L - 2);+        bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\');+        bool is_self = pos + 3 == L && (l[pos+1] == '.');+        if (!is_self && !is_slash) r = r.substr(3);+        else if (pos == std::string::npos) break;         l = l.substr(0, pos == std::string::npos ? pos : pos + 1);       } @@ -207,7 +218,7 @@     // create an absolute path by resolving relative paths with cwd     std::string rel2abs(const std::string& path, const std::string& base, const std::string& cwd)     {-      return make_canonical_path(join_paths(join_paths(cwd, base), path));+      return make_canonical_path(join_paths(join_paths(cwd + "/", base + "/"), path));     }      // create a path that is relative to the given base directory
libsass/src/file.hpp view
@@ -4,10 +4,9 @@ #include <string> #include <vector> -namespace Sass {+#include "ast_fwd_decl.hpp" -  class Block;-  class Context;+namespace Sass {    namespace File { @@ -105,9 +104,9 @@   class StyleSheet : public Resource {     public:       // parsed root block-      Block* root;+      Block_Obj root;     public:-      StyleSheet(const Resource& res, Block* root)+      StyleSheet(const Resource& res, Block_Obj root)       : Resource(res), root(root)       { }   };
libsass/src/functions.cpp view
@@ -1,10 +1,10 @@+#include "sass.hpp" #include "functions.hpp" #include "ast.hpp" #include "context.hpp" #include "backtrace.hpp" #include "parser.hpp" #include "constants.hpp"-#include "to_string.hpp" #include "inspect.hpp" #include "extend.hpp" #include "eval.hpp"@@ -14,6 +14,7 @@ #include "sass/base.h" #include "utf8.h" +#include <cstdint> #include <cstdlib> #include <cmath> #include <cctype>@@ -37,13 +38,13 @@   using std::stringstream;   using std::endl; -  Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)+  Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx)   {     Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]"));     sig_parser.lex<Prelexer::identifier>();     std::string name(Util::normalize_underscores(sig_parser.lexed));-    Parameters* params = sig_parser.parse_parameters();-    return SASS_MEMORY_NEW(ctx.mem, Definition,+    Parameters_Obj params = sig_parser.parse_parameters();+    return SASS_MEMORY_NEW(Definition,                            ParserState("[built-in function]"),                            sig,                            name,@@ -52,7 +53,7 @@                            false);   } -  Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)+  Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx)   {     using namespace Prelexer; @@ -65,8 +66,8 @@                                     exactly < Constants::debug_kwd >                    >              >();     std::string name(Util::normalize_underscores(sig_parser.lexed));-    Parameters* params = sig_parser.parse_parameters();-    return SASS_MEMORY_NEW(ctx.mem, Definition,+    Parameters_Obj params = sig_parser.parse_parameters();+    return SASS_MEMORY_NEW(Definition,                            ParserState("[c function]"),                            sig,                            name,@@ -107,7 +108,7 @@     T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)     {       // Minimal error handling -- the expectation is that built-ins will be written correctly!-      T* val = dynamic_cast<T*>(env[argname]);+      T* val = dynamic_cast<T*>(&env[argname]);       if (!val) {         std::string msg("argument `");         msg += argname;@@ -120,24 +121,24 @@       return val;     } -    Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx)+    Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx)     {       // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Map* val = dynamic_cast<Map*>(env[argname]);+      Map_Ptr val = SASS_MEMORY_CAST(Map, env[argname]);       if (val) return val; -      List* lval = dynamic_cast<List*>(env[argname]);-      if (lval && lval->length() == 0) return SASS_MEMORY_NEW(ctx.mem, Map, pstate, 0);+      List_Ptr lval = SASS_MEMORY_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, backtrace);       return val;     } -    Number* get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace)+    Number_Ptr get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace)     {       // Minimal error handling -- the expectation is that built-ins will be written correctly!-      Number* val = get_arg<Number>(argname, env, sig, pstate, backtrace);+      Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);       double v = val->value();       if (!(lo <= v && v <= hi)) {         std::stringstream msg;@@ -151,49 +152,38 @@     #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, backtrace, ctx)      template <typename T>-    T* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx);+    T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx);      template <>-    Selector_List* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {-      To_String to_string(&ctx, false);-      Expression* exp = ARG(argname, Expression);+    Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, 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);       }-      std::string exp_src = exp->perform(&to_string) + "{";-      return Parser::parse_selector(exp_src.c_str(), ctx);-    }--    template <>-    Complex_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {-      To_String to_string(&ctx, false);-      Expression* 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);+      if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+        str->quote_mark(0);       }-      std::string exp_src = exp->perform(&to_string) + "{";-      Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx);-      return (sel_list->length() > 0) ? sel_list->first() : 0;+      std::string exp_src = exp->to_string(ctx.c_options) + "{";+      return Parser::parse_selector(exp_src.c_str(), ctx);     }      template <>-    Compound_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {-      To_String to_string(&ctx, false);-      Expression* exp = ARG(argname, Expression);+    Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, 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);       }-      std::string exp_src = exp->perform(&to_string) + "{";-      Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx);-      return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0;+      if (String_Constant_Ptr str = SASS_MEMORY_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);+      return (sel_list->length() > 0) ? &sel_list->first()->tail()->head() : 0;     }      #ifdef __MINGW32__@@ -211,9 +201,9 @@       return seed;     }     #else-    static std::random_device rd;     uint64_t GetSeed()     {+      std::random_device rd;       return rd();     }     #endif@@ -236,7 +226,7 @@     // RGB FUNCTIONS     //////////////// -    inline double color_num(Number* n) {+    inline double color_num(Number_Ptr n) {       if (n->unit() == "%") {         return std::min(std::max(n->value() * 255 / 100.0, 0.0), 255.0);       } else {@@ -244,7 +234,7 @@       }     } -    inline double alpha_num(Number* n) {+    inline double alpha_num(Number_Ptr n) {       if (n->unit() == "%") {         return std::min(std::max(n->value(), 0.0), 100.0);       } else {@@ -255,7 +245,7 @@     Signature rgb_sig = "rgb($red, $green, $blue)";     BUILT_IN(rgb)     {-      return SASS_MEMORY_NEW(ctx.mem, Color,+      return SASS_MEMORY_NEW(Color,                              pstate,                              color_num(ARG("$red",   Number)),                              color_num(ARG("$green", Number)),@@ -265,7 +255,7 @@     Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";     BUILT_IN(rgba_4)     {-      return SASS_MEMORY_NEW(ctx.mem, Color,+      return SASS_MEMORY_NEW(Color,                              pstate,                              color_num(ARG("$red",   Number)),                              color_num(ARG("$green", Number)),@@ -276,8 +266,8 @@     Signature rgba_2_sig = "rgba($color, $alpha)";     BUILT_IN(rgba_2)     {-      Color* c_arg = ARG("$color", Color);-      Color* new_c = SASS_MEMORY_NEW(ctx.mem, Color, *c_arg);+      Color_Ptr c_arg = ARG("$color", Color);+      Color_Ptr new_c = SASS_MEMORY_COPY(c_arg);       new_c->a(alpha_num(ARG("$alpha", Number)));       new_c->disp("");       return new_c;@@ -285,22 +275,22 @@      Signature red_sig = "red($color)";     BUILT_IN(red)-    { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->r()); }+    { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->r()); }      Signature green_sig = "green($color)";     BUILT_IN(green)-    { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->g()); }+    { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->g()); }      Signature blue_sig = "blue($color)";     BUILT_IN(blue)-    { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->b()); }+    { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); }      Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";     BUILT_IN(mix)     {-      Color*  color1 = ARG("$color-1", Color);-      Color*  color2 = ARG("$color-2", Color);-      Number* weight = ARGR("$weight", Number, 0, 100);+      Color_Ptr  color1 = ARG("$color-1", Color);+      Color_Ptr  color2 = ARG("$color-2", Color);+      Number_Ptr weight = ARGR("$weight", Number, 0, 100);        double p = weight->value()/100;       double w = 2*p - 1;@@ -309,11 +299,11 @@       double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;       double w2 = 1 - w1; -      return SASS_MEMORY_NEW(ctx.mem, Color,+      return SASS_MEMORY_NEW(Color,                              pstate,-                             Sass::round(w1*color1->r() + w2*color2->r()),-                             Sass::round(w1*color1->g() + w2*color2->g()),-                             Sass::round(w1*color1->b() + w2*color2->b()),+                             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));     } @@ -365,7 +355,7 @@       return m1;     } -    Color* hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate)+    Color_Ptr hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate)     {       h /= 360.0;       s /= 100.0;@@ -378,6 +368,12 @@       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);@@ -388,7 +384,7 @@       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(ctx.mem, Color, pstate, r, g, b, a);+      return SASS_MEMORY_NEW(Color, pstate, r, g, b, a);     }      Signature hsl_sig = "hsl($hue, $saturation, $lightness)";@@ -416,38 +412,38 @@     Signature hue_sig = "hue($color)";     BUILT_IN(hue)     {-      Color* rgb_color = ARG("$color", Color);+      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(ctx.mem, Number, pstate, hsl_color.h, "deg");+      return SASS_MEMORY_NEW(Number, pstate, hsl_color.h, "deg");     }      Signature saturation_sig = "saturation($color)";     BUILT_IN(saturation)     {-      Color* rgb_color = ARG("$color", Color);+      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(ctx.mem, Number, pstate, hsl_color.s, "%");+      return SASS_MEMORY_NEW(Number, pstate, hsl_color.s, "%");     }      Signature lightness_sig = "lightness($color)";     BUILT_IN(lightness)     {-      Color* rgb_color = ARG("$color", Color);+      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(ctx.mem, Number, pstate, hsl_color.l, "%");+      return SASS_MEMORY_NEW(Number, pstate, hsl_color.l, "%");     }      Signature adjust_hue_sig = "adjust-hue($color, $degrees)";     BUILT_IN(adjust_hue)     {-      Color* rgb_color = ARG("$color", Color);-      Number* degrees = ARG("$degrees", Number);+      Color_Ptr rgb_color = ARG("$color", Color);+      Number_Ptr degrees = ARG("$degrees", Number);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -462,8 +458,8 @@     Signature lighten_sig = "lighten($color, $amount)";     BUILT_IN(lighten)     {-      Color* rgb_color = ARG("$color", Color);-      Number* amount = ARGR("$amount", Number, 0, 100);+      Color_Ptr rgb_color = ARG("$color", Color);+      Number_Ptr amount = ARGR("$amount", Number, 0, 100);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -484,8 +480,8 @@     Signature darken_sig = "darken($color, $amount)";     BUILT_IN(darken)     {-      Color* rgb_color = ARG("$color", Color);-      Number* amount = ARGR("$amount", Number, 0, 100);+      Color_Ptr rgb_color = ARG("$color", Color);+      Number_Ptr amount = ARGR("$amount", Number, 0, 100);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -508,14 +504,13 @@     BUILT_IN(saturate)     {       // CSS3 filter function overload: pass literal through directly-      Number* amount = dynamic_cast<Number*>(env["$amount"]);+      Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$amount"]);       if (!amount) {-        To_String to_string(&ctx);-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "saturate(" + env["$color"]->perform(&to_string) + ")");+        return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");       }        ARGR("$amount", Number, 0, 100);-      Color* rgb_color = ARG("$color", Color);+      Color_Ptr rgb_color = ARG("$color", Color);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -541,8 +536,8 @@     Signature desaturate_sig = "desaturate($color, $amount)";     BUILT_IN(desaturate)     {-      Color* rgb_color = ARG("$color", Color);-      Number* amount = ARGR("$amount", Number, 0, 100);+      Color_Ptr rgb_color = ARG("$color", Color);+      Number_Ptr amount = ARGR("$amount", Number, 0, 100);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -569,13 +564,12 @@     BUILT_IN(grayscale)     {       // CSS3 filter function overload: pass literal through directly-      Number* amount = dynamic_cast<Number*>(env["$color"]);+      Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]);       if (amount) {-        To_String to_string(&ctx);-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "grayscale(" + amount->perform(&to_string) + ")");+        return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");       } -      Color* rgb_color = ARG("$color", Color);+      Color_Ptr rgb_color = ARG("$color", Color);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -590,7 +584,7 @@     Signature complement_sig = "complement($color)";     BUILT_IN(complement)     {-      Color* rgb_color = ARG("$color", Color);+      Color_Ptr rgb_color = ARG("$color", Color);       HSL hsl_color = rgb_to_hsl(rgb_color->r(),                                  rgb_color->g(),                                  rgb_color->b());@@ -606,14 +600,13 @@     BUILT_IN(invert)     {       // CSS3 filter function overload: pass literal through directly-      Number* amount = dynamic_cast<Number*>(env["$color"]);+      Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]);       if (amount) {-        To_String to_string(&ctx);-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "invert(" + amount->perform(&to_string) + ")");+        return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");       } -      Color* rgb_color = ARG("$color", Color);-      return SASS_MEMORY_NEW(ctx.mem, Color,+      Color_Ptr rgb_color = ARG("$color", Color);+      return SASS_MEMORY_NEW(Color,                              pstate,                              255 - rgb_color->r(),                              255 - rgb_color->g(),@@ -628,29 +621,28 @@     Signature opacity_sig = "opacity($color)";     BUILT_IN(alpha)     {-      String_Constant* ie_kwd = dynamic_cast<String_Constant*>(env["$color"]);+      String_Constant_Ptr ie_kwd = SASS_MEMORY_CAST(String_Constant, env["$color"]);       if (ie_kwd) {-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");+        return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");       }        // CSS3 filter function overload: pass literal through directly-      Number* amount = dynamic_cast<Number*>(env["$color"]);+      Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]);       if (amount) {-        To_String to_string(&ctx);-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "opacity(" + amount->perform(&to_string) + ")");+        return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");       } -      return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->a());+      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* color = ARG("$color", Color);+      Color_Ptr color = ARG("$color", Color);       double amount = ARGR("$amount", Number, 0, 1)->value();       double alpha = std::min(color->a() + amount, 1.0);-      return SASS_MEMORY_NEW(ctx.mem, Color,+      return SASS_MEMORY_NEW(Color,                              pstate,                              color->r(),                              color->g(),@@ -662,10 +654,10 @@     Signature fade_out_sig = "fade-out($color, $amount)";     BUILT_IN(transparentize)     {-      Color* color = ARG("$color", Color);+      Color_Ptr color = ARG("$color", Color);       double amount = ARGR("$amount", Number, 0, 1)->value();       double alpha = std::max(color->a() - amount, 0.0);-      return SASS_MEMORY_NEW(ctx.mem, Color,+      return SASS_MEMORY_NEW(Color,                              pstate,                              color->r(),                              color->g(),@@ -680,27 +672,27 @@     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* color = ARG("$color", Color);-      Number* r = dynamic_cast<Number*>(env["$red"]);-      Number* g = dynamic_cast<Number*>(env["$green"]);-      Number* b = dynamic_cast<Number*>(env["$blue"]);-      Number* h = dynamic_cast<Number*>(env["$hue"]);-      Number* s = dynamic_cast<Number*>(env["$saturation"]);-      Number* l = dynamic_cast<Number*>(env["$lightness"]);-      Number* a = dynamic_cast<Number*>(env["$alpha"]);+      Color_Ptr color = ARG("$color", Color);+      Number_Ptr r = SASS_MEMORY_CAST(Number, env["$red"]);+      Number_Ptr g = SASS_MEMORY_CAST(Number, env["$green"]);+      Number_Ptr b = SASS_MEMORY_CAST(Number, env["$blue"]);+      Number_Ptr h = SASS_MEMORY_CAST(Number, env["$hue"]);+      Number_Ptr s = SASS_MEMORY_CAST(Number, env["$saturation"]);+      Number_Ptr l = SASS_MEMORY_CAST(Number, env["$lightness"]);+      Number_Ptr a = SASS_MEMORY_CAST(Number, env["$alpha"]);        bool rgb = r || g || b;       bool hsl = h || s || l;        if (rgb && hsl) {-        error("cannot specify both RGB and HSL values for `adjust-color`", pstate);+        error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate);       }       if (rgb) {         double rr = r ? ARGR("$red",   Number, -255, 255)->value() : 0;         double gg = g ? ARGR("$green", Number, -255, 255)->value() : 0;         double bb = b ? ARGR("$blue",  Number, -255, 255)->value() : 0;         double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0;-        return SASS_MEMORY_NEW(ctx.mem, Color,+        return SASS_MEMORY_NEW(Color,                                pstate,                                color->r() + rr,                                color->g() + gg,@@ -720,14 +712,14 @@                          pstate);       }       if (a) {-        return SASS_MEMORY_NEW(ctx.mem, Color,+        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);+      error("not enough arguments for `adjust-color'", pstate);       // unreachable       return color;     }@@ -735,27 +727,27 @@     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* color = ARG("$color", Color);-      Number* r = dynamic_cast<Number*>(env["$red"]);-      Number* g = dynamic_cast<Number*>(env["$green"]);-      Number* b = dynamic_cast<Number*>(env["$blue"]);-      Number* h = dynamic_cast<Number*>(env["$hue"]);-      Number* s = dynamic_cast<Number*>(env["$saturation"]);-      Number* l = dynamic_cast<Number*>(env["$lightness"]);-      Number* a = dynamic_cast<Number*>(env["$alpha"]);+      Color_Ptr color = ARG("$color", Color);+      Number_Ptr r = SASS_MEMORY_CAST(Number, env["$red"]);+      Number_Ptr g = SASS_MEMORY_CAST(Number, env["$green"]);+      Number_Ptr b = SASS_MEMORY_CAST(Number, env["$blue"]);+      Number_Ptr h = SASS_MEMORY_CAST(Number, env["$hue"]);+      Number_Ptr s = SASS_MEMORY_CAST(Number, env["$saturation"]);+      Number_Ptr l = SASS_MEMORY_CAST(Number, env["$lightness"]);+      Number_Ptr a = SASS_MEMORY_CAST(Number, env["$alpha"]);        bool rgb = r || g || b;       bool hsl = h || s || l;        if (rgb && hsl) {-        error("cannot specify both RGB and HSL values for `scale-color`", pstate);+        error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate);       }       if (rgb) {         double rscale = (r ? ARGR("$red",   Number, -100.0, 100.0)->value() : 0.0) / 100.0;         double gscale = (g ? ARGR("$green", Number, -100.0, 100.0)->value() : 0.0) / 100.0;         double bscale = (b ? ARGR("$blue",  Number, -100.0, 100.0)->value() : 0.0) / 100.0;         double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;-        return SASS_MEMORY_NEW(ctx.mem, Color,+        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()),@@ -776,14 +768,14 @@       }       if (a) {         double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;-        return SASS_MEMORY_NEW(ctx.mem, Color,+        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);+      error("not enough arguments for `scale-color'", pstate);       // unreachable       return color;     }@@ -791,23 +783,23 @@     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* color = ARG("$color", Color);-      Number* r = dynamic_cast<Number*>(env["$red"]);-      Number* g = dynamic_cast<Number*>(env["$green"]);-      Number* b = dynamic_cast<Number*>(env["$blue"]);-      Number* h = dynamic_cast<Number*>(env["$hue"]);-      Number* s = dynamic_cast<Number*>(env["$saturation"]);-      Number* l = dynamic_cast<Number*>(env["$lightness"]);-      Number* a = dynamic_cast<Number*>(env["$alpha"]);+      Color_Ptr color = ARG("$color", Color);+      Number_Ptr r = SASS_MEMORY_CAST(Number, env["$red"]);+      Number_Ptr g = SASS_MEMORY_CAST(Number, env["$green"]);+      Number_Ptr b = SASS_MEMORY_CAST(Number, env["$blue"]);+      Number_Ptr h = SASS_MEMORY_CAST(Number, env["$hue"]);+      Number_Ptr s = SASS_MEMORY_CAST(Number, env["$saturation"]);+      Number_Ptr l = SASS_MEMORY_CAST(Number, env["$lightness"]);+      Number_Ptr a = SASS_MEMORY_CAST(Number, env["$alpha"]);        bool rgb = r || g || b;       bool hsl = h || s || l;        if (rgb && hsl) {-        error("cannot specify both RGB and HSL values for `change-color`", pstate);+        error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate);       }       if (rgb) {-        return SASS_MEMORY_NEW(ctx.mem, Color,+        return SASS_MEMORY_NEW(Color,                                pstate,                                r ? ARGR("$red",   Number, 0, 255)->value() : color->r(),                                g ? ARGR("$green", Number, 0, 255)->value() : color->g(),@@ -816,7 +808,7 @@       }       if (hsl) {         HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());-        if (h) hsl_struct.h = static_cast<double>(((static_cast<int>(h->value()) % 360) + 360) % 360) / 360.0;+        if (h) hsl_struct.h = std::fmod(h->value(), 360.0);         if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value();         if (l) hsl_struct.l = ARGR("$lightness",  Number, 0, 100)->value();         double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();@@ -824,14 +816,14 @@       }       if (a) {         double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();-        return SASS_MEMORY_NEW(ctx.mem, Color,+        return SASS_MEMORY_NEW(Color,                                pstate,                                color->r(),                                color->g(),                                color->b(),                                alpha);       }-      error("not enough arguments for `change-color`", pstate);+      error("not enough arguments for `change-color'", pstate);       // unreachable       return color;     }@@ -846,7 +838,7 @@     Signature ie_hex_str_sig = "ie-hex-str($color)";     BUILT_IN(ie_hex_str)     {-      Color* c = ARG("$color", Color);+      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());@@ -854,16 +846,16 @@        std::stringstream ss;       ss << '#' << std::setw(2) << std::setfill('0');-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a));-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r));-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g));-      ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b));+      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(ctx.mem, String_Quoted, pstate, result);+      return SASS_MEMORY_NEW(String_Quoted, pstate, result);     }      ///////////////////@@ -873,34 +865,41 @@     Signature unquote_sig = "unquote($string)";     BUILT_IN(sass_unquote)     {-      AST_Node* arg = env["$string"];-      if (String_Quoted* string_quoted = dynamic_cast<String_Quoted*>(arg)) {-        String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, string_quoted->value());+      AST_Node_Obj arg = env["$string"];+      if (String_Quoted_Ptr string_quoted = SASS_MEMORY_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->sass_fix_1291(string_quoted->quote_mark() != 0);+        result->is_delayed(true); // delay colors         return result;       }-      else if (dynamic_cast<String_Constant*>(arg)) {-        return (Expression*) arg;+      else if (SASS_MEMORY_CAST(String_Constant, arg)) {+        return (Expression_Ptr) &arg;       }       else {-        To_String to_string(&ctx, false, true);-        std::string val(arg->perform(&to_string));-        val = dynamic_cast<Null*>(arg) ? "null" : val;+        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 = SASS_MEMORY_CAST(Null, arg) ? "null" : val;+        ctx.c_options.output_style = oldstyle;          deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);-        return (Expression*) arg;+        return (Expression_Ptr) &arg;       }     }      Signature quote_sig = "quote($string)";     BUILT_IN(sass_quote)     {-      To_String to_string(&ctx);-      AST_Node* arg = env["$string"];-      std::string str(quote(arg->perform(&to_string), String_Constant::double_quote()));-      String_Quoted* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);-      result->is_delayed(true);+      AST_Node_Obj arg = env["$string"];+      // only set quote mark to true if already a string+      if (String_Quoted_Ptr qstr = SASS_MEMORY_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;     } @@ -910,7 +909,7 @@     {       size_t len = std::string::npos;       try {-        String_Constant* s = ARG("$string", String_Constant);+        String_Constant_Ptr s = ARG("$string", String_Constant);         len = UTF_8::code_point_count(s->value(), 0, s->value().size());        }@@ -918,7 +917,7 @@       // other errors will be re-thrown       catch (...) { handle_utf8_error(pstate, backtrace); }       // return something even if we had an error (-1)-      return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)len);+      return SASS_MEMORY_NEW(Number, pstate, (double)len);     }      Signature str_insert_sig = "str-insert($string, $insert, $index)";@@ -926,13 +925,13 @@     {       std::string str;       try {-        String_Constant* s = ARG("$string", String_Constant);+        String_Constant_Ptr s = ARG("$string", String_Constant);         str = s->value();         str = unquote(str);-        String_Constant* i = ARG("$insert", String_Constant);+        String_Constant_Ptr i = ARG("$insert", String_Constant);         std::string ins = i->value();         ins = unquote(ins);-        Number* ind = ARG("$index", Number);+        Number_Ptr ind = ARG("$index", Number);         double index = ind->value();         size_t len = UTF_8::code_point_count(str, 0, str.size()); @@ -957,14 +956,14 @@           str = ins + str;         } -        if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+        if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(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, backtrace); }-      return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+      return SASS_MEMORY_NEW(String_Quoted, pstate, str);     }      Signature str_index_sig = "str-index($string, $substring)";@@ -972,8 +971,8 @@     {       size_t index = std::string::npos;       try {-        String_Constant* s = ARG("$string", String_Constant);-        String_Constant* t = ARG("$substring", String_Constant);+        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();@@ -981,7 +980,7 @@          size_t c_index = str.find(substr);         if(c_index == std::string::npos) {-          return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+          return SASS_MEMORY_NEW(Null, pstate);         }         index = UTF_8::code_point_count(str, 0, c_index) + 1;       }@@ -989,7 +988,7 @@       // other errors will be re-thrown       catch (...) { handle_utf8_error(pstate, backtrace); }       // return something even if we had an error (-1)-      return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)index);+      return SASS_MEMORY_NEW(Number, pstate, (double)index);     }      Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";@@ -997,17 +996,33 @@     {       std::string newstr;       try {-        String_Constant* s = ARG("$string", String_Constant);+        String_Constant_Ptr s = ARG("$string", String_Constant);         double start_at = ARG("$start-at", Number)->value();         double end_at = ARG("$end-at", Number)->value();+        String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s);          std::string str = unquote(s->value());          size_t size = utf8::distance(str.begin(), str.end());-        if (end_at <= size * -1.0) { end_at += size; }-        if (end_at < 0) { end_at += size + 1; }++        if (!SASS_MEMORY_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 += size + 1;+          if (start_at < 0)  start_at = 0;+        }         else if (start_at == 0) { ++ start_at; }          if (start_at <= end_at)@@ -1018,20 +1033,20 @@           utf8::advance(end, end_at - start_at + 1, str.end());           newstr = std::string(start, end);         }-        if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+        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, backtrace); }-      return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, newstr);+      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);+      String_Constant_Ptr s = ARG("$string", String_Constant);       std::string str = s->value();        for (size_t i = 0, L = str.length(); i < L; ++i) {@@ -1040,19 +1055,19 @@         }       } -      if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {-        String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss);+      if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+        String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss);         cpy->value(str);         return cpy;       } else {-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+        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);+      String_Constant_Ptr s = ARG("$string", String_Constant);       std::string str = s->value();        for (size_t i = 0, L = str.length(); i < L; ++i) {@@ -1061,12 +1076,12 @@         }       } -      if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {-        String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss);+      if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+        String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss);         cpy->value(str);         return cpy;       } else {-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+        return SASS_MEMORY_NEW(String_Quoted, pstate, str);       }     } @@ -1077,26 +1092,26 @@     Signature percentage_sig = "percentage($number)";     BUILT_IN(percentage)     {-      Number* n = ARG("$number", Number);+      Number_Ptr n = ARG("$number", Number);       if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate);-      return SASS_MEMORY_NEW(ctx.mem, Number, pstate, n->value() * 100, "%");+      return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");     }      Signature round_sig = "round($number)";     BUILT_IN(round)     {-      Number* n = ARG("$number", Number);-      Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+      Number_Ptr n = ARG("$number", Number);+      Number_Ptr r = SASS_MEMORY_COPY(n);       r->pstate(pstate);-      r->value(Sass::round(r->value()));+      r->value(Sass::round(r->value(), ctx.c_options.precision));       return r;     }      Signature ceil_sig = "ceil($number)";     BUILT_IN(ceil)     {-      Number* n = ARG("$number", Number);-      Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+      Number_Ptr n = ARG("$number", Number);+      Number_Ptr r = SASS_MEMORY_COPY(n);       r->pstate(pstate);       r->value(std::ceil(r->value()));       return r;@@ -1105,8 +1120,8 @@     Signature floor_sig = "floor($number)";     BUILT_IN(floor)     {-      Number* n = ARG("$number", Number);-      Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+      Number_Ptr n = ARG("$number", Number);+      Number_Ptr r = SASS_MEMORY_COPY(n);       r->pstate(pstate);       r->value(std::floor(r->value()));       return r;@@ -1115,8 +1130,8 @@     Signature abs_sig = "abs($number)";     BUILT_IN(abs)     {-      Number* n = ARG("$number", Number);-      Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+      Number_Ptr n = ARG("$number", Number);+      Number_Ptr r = SASS_MEMORY_COPY(n);       r->pstate(pstate);       r->value(std::abs(r->value()));       return r;@@ -1125,61 +1140,67 @@     Signature min_sig = "min($numbers...)";     BUILT_IN(min)     {-      List* arglist = ARG("$numbers", List);-      Number* least = 0;+      List_Ptr arglist = ARG("$numbers", List);+      Number_Obj least = NULL;       for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));-        if (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate);+        Expression_Obj val = arglist->value_at_index(i);+        Number_Obj xi = SASS_MEMORY_CAST(Number, val);+        if (!xi) {+          error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate);+        }         if (least) {-          if (Eval::lt(xi, least)) least = xi;+          if (*xi < *least) least = xi;         } else least = xi;       }-      return least;+      return least.detach();     }      Signature max_sig = "max($numbers...)";     BUILT_IN(max)     {-      List* arglist = ARG("$numbers", List);-      Number* greatest = 0;+      List_Ptr arglist = ARG("$numbers", List);+      Number_Obj greatest = NULL;       for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));-        if (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate);+        Expression_Obj val = arglist->value_at_index(i);+        Number_Obj xi = SASS_MEMORY_CAST(Number, val);+        if (!xi) {+          error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate);+        }         if (greatest) {-          if (Eval::lt(greatest, xi)) greatest = xi;+          if (*greatest < *xi) greatest = xi;         } else greatest = xi;       }-      return greatest;+      return greatest.detach();     }      Signature random_sig = "random($limit:false)";     BUILT_IN(random)     {-      AST_Node* arg = env["$limit"];-      Value* v = dynamic_cast<Value*>(arg);-      Number* l = dynamic_cast<Number*>(arg);-      Boolean* b = dynamic_cast<Boolean*>(arg);+      AST_Node_Obj arg = env["$limit"];+      Value_Ptr v = SASS_MEMORY_CAST(Value, arg);+      Number_Ptr l = SASS_MEMORY_CAST(Number, arg);+      Boolean_Ptr b = SASS_MEMORY_CAST(Boolean, arg);       if (l) {         double v = l->value();         if (v < 1) {           stringstream err;-          err << "$limit " << v << " must be greater than or equal to 1 for `random`";+          err << "$limit " << v << " must be greater than or equal to 1 for `random'";           error(err.str(), pstate);         }         bool eq_int = std::fabs(trunc(v) - v) < NUMBER_EPSILON;         if (!eq_int) {           stringstream err;-          err << "Expected $limit to be an integer but got `" << v << "` for `random`";+          err << "Expected $limit to be an integer but got " << v << " for `random'";           error(err.str(), pstate);         }         std::uniform_real_distribution<> distributor(1, v + 1);         uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));-        return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)distributed);+        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(ctx.mem, Number, pstate, distributed);+        return SASS_MEMORY_NEW(Number, pstate, distributed);       } else if (v) {         throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number", v);       } else {@@ -1195,26 +1216,26 @@     Signature length_sig = "length($list)";     BUILT_IN(length)     {-      if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {-        return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)sl->length());+      if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) {+        return SASS_MEMORY_NEW(Number, pstate, (double)sl->length());       }-      Expression* v = ARG("$list", Expression);+      Expression_Ptr v = ARG("$list", Expression);       if (v->concrete_type() == Expression::MAP) {-        Map* map = dynamic_cast<Map*>(env["$list"]);-        return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(map ? map->length() : 1));+        Map_Ptr map = SASS_MEMORY_CAST(Map, env["$list"]);+        return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1));       }       if (v->concrete_type() == Expression::SELECTOR) {-        if (Compound_Selector* h = dynamic_cast<Compound_Selector*>(v)) {-          return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)h->length());-        } else if (Selector_List* ls = dynamic_cast<Selector_List*>(v)) {-          return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)ls->length());+        if (Compound_Selector_Ptr h = dynamic_cast<Compound_Selector_Ptr>(v)) {+          return SASS_MEMORY_NEW(Number, pstate, (double)h->length());+        } else if (Selector_List_Ptr ls = SASS_MEMORY_CAST_PTR(Selector_List, v)) {+          return SASS_MEMORY_NEW(Number, pstate, (double)ls->length());         } else {-          return SASS_MEMORY_NEW(ctx.mem, Number, pstate, 1);+          return SASS_MEMORY_NEW(Number, pstate, 1);         }       } -      List* list = dynamic_cast<List*>(env["$list"]);-      return SASS_MEMORY_NEW(ctx.mem, Number,+      List_Ptr list = SASS_MEMORY_CAST(List, env["$list"]);+      return SASS_MEMORY_NEW(Number,                              pstate,                              (double)(list ? list->size() : 1));     }@@ -1222,24 +1243,24 @@     Signature nth_sig = "nth($list, $n)";     BUILT_IN(nth)     {-      Number* n = ARG("$n", Number);-      Map* m = dynamic_cast<Map*>(env["$list"]);-      if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {+      Number_Ptr n = ARG("$n", Number);+      Map_Ptr m = SASS_MEMORY_CAST(Map, env["$list"]);+      if (Selector_List_Ptr sl = SASS_MEMORY_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);         double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1);         if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);         // return (*sl)[static_cast<int>(index)];-        Listize listize(ctx);+        Listize listize;         return (*sl)[static_cast<int>(index)]->perform(&listize);       }-      List* l = dynamic_cast<List*>(env["$list"]);+      List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);       if (n->value() == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate);       // if the argument isn't a list, then wrap it in a singleton list       if (!m && !l) {-        l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l << ARG("$list", Expression);+        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();@@ -1248,32 +1269,38 @@       if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);        if (m) {-        l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l << m->keys()[static_cast<unsigned int>(index)];-        *l << m->at(m->keys()[static_cast<unsigned int>(index)]);-        return l;+        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 {-        return l->value_at_index(static_cast<int>(index));+        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)     {-      List* l = dynamic_cast<List*>(env["$list"]);-      Number* n = ARG("$n", Number);-      Expression* v = ARG("$value", Expression);+      Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);+      List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+      Number_Obj n = ARG("$n", Number);+      Expression_Obj v = ARG("$value", Expression);       if (!l) {-        l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l << ARG("$list", Expression);+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));       }+      if (m) {+        l = m->to_list(ctx, pstate);+      }       if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);       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);-      List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length(), l->separator());+      List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator());       for (size_t i = 0, L = l->length(); i < L; ++i) {-        *result << ((i == index) ? v : (*l)[i]);+        result->append(((i == index) ? v : (*l)[i]));       }       return result;     }@@ -1281,77 +1308,93 @@     Signature index_sig = "index($list, $value)";     BUILT_IN(index)     {-      List* l = dynamic_cast<List*>(env["$list"]);-      Expression* v = ARG("$value", Expression);+      Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);+      List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+      Expression_Obj v = ARG("$value", Expression);       if (!l) {-        l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l << ARG("$list", Expression);+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));       }+      if (m) {+        l = m->to_list(ctx, pstate);+      }       for (size_t i = 0, L = l->length(); i < L; ++i) {-        if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(i+1));+        if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));       }-      return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+      return SASS_MEMORY_NEW(Null, pstate);     }      Signature join_sig = "join($list1, $list2, $separator: auto)";     BUILT_IN(join)     {-      List* l1 = dynamic_cast<List*>(env["$list1"]);-      List* l2 = dynamic_cast<List*>(env["$list2"]);-      String_Constant* sep = ARG("$separator", String_Constant);+      Map_Obj m1 = SASS_MEMORY_CAST(Map, env["$list1"]);+      Map_Obj m2 = SASS_MEMORY_CAST(Map, env["$list2"]);+      List_Obj l1 = SASS_MEMORY_CAST(List, env["$list1"]);+      List_Obj l2 = SASS_MEMORY_CAST(List, env["$list2"]);+      String_Constant_Obj sep = ARG("$separator", String_Constant);       enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);       if (!l1) {-        l1 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l1 << ARG("$list1", Expression);+        l1 = SASS_MEMORY_NEW(List, pstate, 1);+        l1->append(ARG("$list1", Expression));         sep_val = (l2 ? l2->separator() : SASS_SPACE);       }       if (!l2) {-        l2 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l2 << ARG("$list2", Expression);+        l2 = SASS_MEMORY_NEW(List, pstate, 1);+        l2->append(ARG("$list2", Expression));       }+      if (m1) {+        l1 = m1->to_list(ctx, pstate);+        sep_val = SASS_COMMA;+      }+      if (m2) {+        l2 = m2->to_list(ctx, 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);-      List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, len, sep_val);-      *result += l1;-      *result += l2;-      return result;+      List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val);+      result->concat(&l1);+      result->concat(&l2);+      return result.detach();     }      Signature append_sig = "append($list, $val, $separator: auto)";     BUILT_IN(append)     {-      List* l = dynamic_cast<List*>(env["$list"]);-      Expression* v = ARG("$val", Expression);-      if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {-        Listize listize(ctx);-        l = dynamic_cast<List*>(sl->perform(&listize));+      Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);+      List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+      Expression_Obj v = ARG("$val", Expression);+      if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) {+        Listize listize;+        l = SASS_MEMORY_CAST_PTR(List, sl->perform(&listize));       }-      String_Constant* sep = ARG("$separator", String_Constant);+      String_Constant_Obj sep = ARG("$separator", String_Constant);       if (!l) {-        l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l << ARG("$list", Expression);+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));       }-      List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length() + 1, l->separator());+      if (m) {+        l = m->to_list(ctx, pstate);+      }+      List_Ptr result = SASS_MEMORY_COPY(l);       std::string sep_str(unquote(sep->value()));-      if (sep_str == "space") result->separator(SASS_SPACE);-      else if (sep_str == "comma") result->separator(SASS_COMMA);-      else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);-      *result += l;-      bool is_arglist = l->is_arglist();-      result->is_arglist(is_arglist);-      if (is_arglist) {-        *result << SASS_MEMORY_NEW(ctx.mem, Argument,-                                   v->pstate(),-                                   v,-                                   "",-                                   false,-                                   false);+      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);+      }+      if (l->is_arglist()) {+        result->append(SASS_MEMORY_NEW(Argument,+                                       v->pstate(),+                                       v,+                                       "",+                                       false,+                                       false));        } else {-        *result << v;+        result->append(v);       }       return result;     }@@ -1359,29 +1402,35 @@     Signature zip_sig = "zip($lists...)";     BUILT_IN(zip)     {-      List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$lists", List));+      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* ith = dynamic_cast<List*>(arglist->value_at_index(i));+        List_Obj ith = SASS_MEMORY_CAST(List, arglist->value_at_index(i));+        Map_Obj mith = SASS_MEMORY_CAST(Map, arglist->value_at_index(i));         if (!ith) {-          ith = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-          *ith << arglist->value_at_index(i);+          if (mith) {+            ith = mith->to_list(ctx, pstate);+          } else {+            ith = SASS_MEMORY_NEW(List, pstate, 1);+            ith->append(arglist->value_at_index(i));+          }           if (arglist->is_arglist()) {-            ((Argument*)(*arglist)[i])->value(ith);+            Argument_Obj arg = (Argument_Ptr)(&arglist->at(i));+            arg->value(&ith);           } else {-            (*arglist)[i] = ith;+            (*arglist)[i] = &ith;           }         }         shortest = (i ? std::min(shortest, ith->length()) : ith->length());       }-      List* zippers = SASS_MEMORY_NEW(ctx.mem, List, pstate, shortest, SASS_COMMA);+      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* zipper = SASS_MEMORY_NEW(ctx.mem, List, pstate, L);+        List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L);         for (size_t j = 0; j < L; ++j) {-          *zipper << (*static_cast<List*>(arglist->value_at_index(j)))[i];+          zipper->append(SASS_MEMORY_CAST(List, arglist->value_at_index(j))->at(i));         }-        *zippers << zipper;+        zippers->append(zipper);       }       return zippers;     }@@ -1389,12 +1438,12 @@     Signature list_separator_sig = "list_separator($list)";     BUILT_IN(list_separator)     {-      List* l = dynamic_cast<List*>(env["$list"]);+      List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);       if (!l) {-        l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);-        *l << ARG("$list", Expression);+        l = SASS_MEMORY_NEW(List, pstate, 1);+        l->append(ARG("$list", Expression));       }-      return SASS_MEMORY_NEW(ctx.mem, String_Quoted,+      return SASS_MEMORY_NEW(String_Quoted,                                pstate,                                l->separator() == SASS_COMMA ? "comma" : "space");     }@@ -1406,12 +1455,15 @@     Signature map_get_sig = "map-get($map, $key)";     BUILT_IN(map_get)     {-      Map* m = ARGM("$map", Map, ctx);-      Expression* v = ARG("$key", Expression);+      // 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 {-        return m->at(v);+        Expression_Obj val = m->at(v); // XXX+        return val ? val.detach() : SASS_MEMORY_NEW(Null, pstate);       } catch (const std::out_of_range&) {-        return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+        return SASS_MEMORY_NEW(Null, pstate);       }       catch (...) { throw; }     }@@ -1419,18 +1471,18 @@     Signature map_has_key_sig = "map-has-key($map, $key)";     BUILT_IN(map_has_key)     {-      Map* m = ARGM("$map", Map, ctx);-      Expression* v = ARG("$key", Expression);-      return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, m->has(v));+      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* m = ARGM("$map", Map, ctx);-      List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_COMMA);+      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 << key;+        result->append(key);       }       return result;     }@@ -1438,10 +1490,10 @@     Signature map_values_sig = "map-values($map)";     BUILT_IN(map_values)     {-      Map* m = ARGM("$map", Map, ctx);-      List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_COMMA);+      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 << m->at(key);+        result->append(m->at(key));       }       return result;     }@@ -1449,13 +1501,14 @@     Signature map_merge_sig = "map-merge($map1, $map2)";     BUILT_IN(map_merge)     {-      Map* m1 = ARGM("$map1", Map, ctx);-      Map* m2 = ARGM("$map2", Map, ctx);+      Map_Obj m1 = ARGM("$map1", Map, ctx);+      Map_Obj m2 = ARGM("$map2", Map, ctx);        size_t len = m1->length() + m2->length();-      Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, len);-      *result += m1;-      *result += m2;+      Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len);+      // concat not implemented for maps+      *result += &m1;+      *result += &m2;       return result;     } @@ -1463,9 +1516,9 @@     BUILT_IN(map_remove)     {       bool remove;-      Map* m = ARGM("$map", Map, ctx);-      List* arglist = ARG("$keys", List);-      Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);+      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) {@@ -1479,16 +1532,18 @@     Signature keywords_sig = "keywords($args)";     BUILT_IN(keywords)     {-      List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List));-      Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);+      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) {-        std::string name = std::string(((Argument*)(*arglist)[i])->name());+        Expression_Obj obj = arglist->at(i);+        Argument_Obj arg = (Argument_Ptr)&obj;+        std::string name = std::string(arg->name());         name = name.erase(0, 1); // sanitize name (remove dollar sign)-        *result << std::make_pair(SASS_MEMORY_NEW(ctx.mem, String_Quoted,+        *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,                  pstate, name),-                 ((Argument*)(*arglist)[i])->value());+                 arg->value());       }-      return result;+      return result.detach();     }      //////////////////////////@@ -1498,29 +1553,29 @@     Signature type_of_sig = "type-of($value)";     BUILT_IN(type_of)     {-      Expression* v = ARG("$value", Expression);-      return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, v->type());+      Expression_Ptr v = ARG("$value", Expression);+      return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());     }      Signature unit_sig = "unit($number)";     BUILT_IN(unit)-    { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); }+    { return SASS_MEMORY_NEW(String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); }      Signature unitless_sig = "unitless($number)";     BUILT_IN(unitless)-    { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, ARG("$number", Number)->is_unitless()); }+    { return SASS_MEMORY_NEW(Boolean, pstate, ARG("$number", Number)->is_unitless()); }      Signature comparable_sig = "comparable($number-1, $number-2)";     BUILT_IN(comparable)     {-      Number* n1 = ARG("$number-1", Number);-      Number* n2 = ARG("$number-2", Number);+      Number_Ptr n1 = ARG("$number-1", Number);+      Number_Ptr n2 = ARG("$number-2", Number);       if (n1->is_unitless() || n2->is_unitless()) {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+        return SASS_MEMORY_NEW(Boolean, pstate, true);       }-      Number tmp_n2(*n2);+      Number tmp_n2(n2); // copy       tmp_n2.normalize(n1->find_convertible_unit());-      return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, n1->unit() == tmp_n2.unit());+      return SASS_MEMORY_NEW(Boolean, pstate, n1->unit() == tmp_n2.unit());     }      Signature variable_exists_sig = "variable-exists($name)";@@ -1529,10 +1584,10 @@       std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));        if(d_env.has("$"+s)) {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+        return SASS_MEMORY_NEW(Boolean, pstate, true);       }       else {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+        return SASS_MEMORY_NEW(Boolean, pstate, false);       }     } @@ -1542,10 +1597,10 @@       std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));        if(d_env.has_global("$"+s)) {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+        return SASS_MEMORY_NEW(Boolean, pstate, true);       }       else {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+        return SASS_MEMORY_NEW(Boolean, pstate, false);       }     } @@ -1555,10 +1610,10 @@       std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));        if(d_env.has_global(s+"[f]")) {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+        return SASS_MEMORY_NEW(Boolean, pstate, true);       }       else {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+        return SASS_MEMORY_NEW(Boolean, pstate, false);       }     } @@ -1568,10 +1623,10 @@       std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));        if(d_env.has_global(s+"[m]")) {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+        return SASS_MEMORY_NEW(Boolean, pstate, true);       }       else {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+        return SASS_MEMORY_NEW(Boolean, pstate, false);       }     } @@ -1581,10 +1636,10 @@       std::string s = unquote(ARG("$name", String_Constant)->value());        if(features.find(s) == features.end()) {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+        return SASS_MEMORY_NEW(Boolean, pstate, false);       }       else {-        return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+        return SASS_MEMORY_NEW(Boolean, pstate, true);       }     } @@ -1592,34 +1647,36 @@     BUILT_IN(call)     {       std::string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));-      List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List));+      List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); -      Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);+      Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);       // std::string full_name(name + "[f]");-      // Definition* def = d_env.has(full_name) ? static_cast<Definition*>((d_env)[full_name]) : 0;-      // Parameters* params = def ? def->parameters() : 0;+      // Definition_Ptr def = d_env.has(full_name) ? static_cast<Definition_Ptr>((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* expr = arglist->value_at_index(i);+        Expression_Obj expr = arglist->value_at_index(i);         // if (params && params->has_rest_parameter()) {-        //   Parameter* p = param_size > i ? (*params)[i] : 0;-        //   List* list = dynamic_cast<List*>(expr);+        //   Parameter_Obj p = param_size > i ? (*params)[i] : 0;+        //   List_Ptr list = SASS_MEMORY_CAST(List, expr);         //   if (list && p && !p->is_rest_parameter()) expr = (*list)[0];         // }         if (arglist->is_arglist()) {-          Argument* arg = dynamic_cast<Argument*>((*arglist)[i]);-          *args << SASS_MEMORY_NEW(ctx.mem, Argument,-                                   pstate,-                                   expr,-                                   arg ? arg->name() : "",-                                   arg ? arg->is_rest_argument() : false,-                                   arg ? arg->is_keyword_argument() : false);+          Expression_Obj obj = arglist->at(i);+          Argument_Obj arg = (Argument_Ptr)&obj;+          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 << SASS_MEMORY_NEW(ctx.mem, Argument, pstate, expr);+          args->append(SASS_MEMORY_NEW(Argument, pstate, expr));         }       }-      Function_Call* func = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, name, args);-      Expand expand(ctx, &d_env, backtrace);+      Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, &args);+      Expand expand(ctx, &d_env, backtrace, &selector_stack);+      func->via_call(true); // calc invoke is allowed       return func->perform(&expand.eval);      }@@ -1630,21 +1687,22 @@      Signature not_sig = "not($value)";     BUILT_IN(sass_not)-    { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, ARG("$value", Expression)->is_false()); }+    {+      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, backtrace);-      bool is_true = !ARG("$condition", Expression)->perform(&expand.eval)->is_false();-      if (is_true) {-        return ARG("$if-true", Expression)->perform(&expand.eval);-      }-      else {-        return ARG("$if-false", Expression)->perform(&expand.eval);-      }+      Expand expand(ctx, &d_env, backtrace, &selector_stack);+      Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);+      bool is_true = !cond->is_false();+      Expression_Ptr res = ARG(is_true ? "$if-true" : "$if-false", Expression);+      res = res->perform(&expand.eval);+      res->set_delayed(false); // clone?+      return res;     }      ////////////////@@ -1662,117 +1720,126 @@     // 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* v = ARG("$value", Expression);+      Expression_Ptr v = ARG("$value", Expression);       if (v->concrete_type() == Expression::NULL_VAL) {-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "null");-      } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) {-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "false");+        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 {-        bool parentheses = v->concrete_type() == Expression::MAP ||-                           v->concrete_type() == Expression::LIST;+        // ToDo: fix to_sass for nested parentheses         Sass_Output_Style old_style;-        old_style = ctx.c_options->output_style;-        ctx.c_options->output_style = SASS_STYLE_NESTED;-        To_String to_string(&ctx, false);-        std::string inspect = v->perform(&to_string);-        if (inspect.empty() && parentheses) inspect = "()";-        ctx.c_options->output_style = old_style;-        return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, inspect);+        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)     {-      To_String to_string(&ctx, false);-      List* arglist = ARG("$selectors", List);+      List_Ptr arglist = ARG("$selectors", List);        // Not enough parameters       if( arglist->length() == 0 )-        error("$selectors: At least one selector must be passed", pstate);+        error("$selectors: At least one selector must be passed for `selector-nest'", pstate);        // Parse args into vector of selectors-      std::vector<Selector_List*> parsedSelectors;+      std::vector<Selector_List_Obj> parsedSelectors;       for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression* exp = dynamic_cast<Expression*>(arglist->value_at_index(i));+        Expression_Obj exp = SASS_MEMORY_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);         }-        std::string exp_src = exp->perform(&to_string) + "{";-        Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx);-        parsedSelectors.push_back(sel);+        if (String_Constant_Obj str = SASS_MEMORY_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);+        parsedSelectors.push_back(&sel);       }        // Nothing to do       if( parsedSelectors.empty() ) {-        return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+        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*>::iterator itr = parsedSelectors.begin();-      Selector_List* result = *itr;+      std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();+      Selector_List_Obj result = *itr;       ++itr;        for(;itr != parsedSelectors.end(); ++itr) {-        Selector_List* child = *itr;-        std::vector<Complex_Selector*> exploded;-        Selector_List* rv = child->parentize(result, ctx);+        Selector_List_Obj child = *itr;+        std::vector<Complex_Selector_Obj> exploded;+        selector_stack.push_back(&result);+        Selector_List_Obj rv = child->resolve_parent_refs(ctx, selector_stack);+        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(ctx);+      Listize listize;       return result->perform(&listize);     }      Signature selector_append_sig = "selector-append($selectors...)";     BUILT_IN(selector_append)     {-      To_String to_string;-      List* arglist = ARG("$selectors", List);+      List_Ptr arglist = ARG("$selectors", List);        // Not enough parameters       if( arglist->length() == 0 )-        error("$selectors: At least one selector must be passed", pstate);+        error("$selectors: At least one selector must be passed for `selector-append'", pstate);        // Parse args into vector of selectors-      std::vector<Selector_List*> parsedSelectors;+      std::vector<Selector_List_Obj> parsedSelectors;       for (size_t i = 0, L = arglist->length(); i < L; ++i) {-        Expression* exp = dynamic_cast<Expression*>(arglist->value_at_index(i));+        Expression_Obj exp = SASS_MEMORY_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);         }-        std::string exp_src = exp->perform(&to_string) + "{";-        Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx);-        parsedSelectors.push_back(sel);+        if (String_Constant_Ptr str = SASS_MEMORY_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);+        parsedSelectors.push_back(&sel);       }        // Nothing to do       if( parsedSelectors.empty() ) {-        return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+        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*>::iterator itr = parsedSelectors.begin();-      Selector_List* result = *itr;+      std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();+      Selector_List_Obj result = *itr;       ++itr;        for(;itr != parsedSelectors.end(); ++itr) {-        Selector_List* child = *itr;-        std::vector<Complex_Selector*> newElements;+        Selector_List_Obj child = *itr;+        std::vector<Complex_Selector_Obj> newElements;          // For every COMPLEX_SELECTOR in `result`         // For every COMPLEX_SELECTOR in `child`@@ -1783,74 +1850,73 @@         // 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* parentSeqClone = (*result)[i]->cloneFully(ctx);-            Complex_Selector* childSeq = (*child)[j];-            Complex_Selector* base = childSeq->tail();+            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->perform(&to_string);-              msg += "` to `";-              msg += parentSeqClone->perform(&to_string);;-              msg += "`";+              std::string msg("Can't append \"");+              msg += childSeq->to_string();+              msg += "\" to \"";+              msg += parentSeqClone->to_string();+              msg += "\" for `selector-append'";               error(msg, pstate, backtrace);             }              // Cannot be a Universal selector-            Type_Selector* pType = dynamic_cast<Type_Selector*>(base->head()->first());+            Element_Selector_Obj pType = SASS_MEMORY_CAST(Element_Selector, childSeq->head()->first());             if(pType && pType->name() == "*") {-              std::string msg("Can't append  `");-              msg += childSeq->perform(&to_string);-              msg += "` to `";-              msg += parentSeqClone->perform(&to_string);;-              msg += "`";+              std::string msg("Can't append \"");+              msg += childSeq->to_string();+              msg += "\" to \"";+              msg += parentSeqClone->to_string();+              msg += "\" for `selector-append'";               error(msg, pstate, backtrace);             }              // TODO: Add check for namespace stuff              // append any selectors in childSeq's head-            *(parentSeqClone->innermost()->head()) += (base->head());+            parentSeqClone->innermost()->head()->concat(&base->head());              // Set parentSeqClone new tail             parentSeqClone->innermost()->tail( base->tail() ); -            newElements.push_back(parentSeqClone);+            newElements.push_back(&parentSeqClone);           }         }          result->elements(newElements);       } -      Listize listize(ctx);+      Listize listize;       return result->perform(&listize);     }      Signature selector_unify_sig = "selector-unify($selector1, $selector2)";     BUILT_IN(selector_unify)     {-      Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize);-      Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize);+      Selector_List_Obj selector1 = ARGSEL("$selector1", Selector_List_Obj, p_contextualize);+      Selector_List_Obj selector2 = ARGSEL("$selector2", Selector_List_Obj, p_contextualize); -      Selector_List* result = selector1->unify_with(selector2, ctx);-      Listize listize(ctx);+      Selector_List_Obj result = selector1->unify_with(&selector2, ctx);+      Listize listize;       return result->perform(&listize);     }      Signature simple_selectors_sig = "simple-selectors($selector)";     BUILT_IN(simple_selectors)     {-      Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize);+      Compound_Selector_Obj sel = ARGSEL("$selector", Compound_Selector_Obj, p_contextualize); -      To_String to_string;-      List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA);+      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* ss = (*sel)[i];-        std::string ss_string = ss->perform(&to_string) ;+        Simple_Selector_Obj ss = (*sel)[i];+        std::string ss_string = ss->to_string() ; -        *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, ss->pstate(), ss_string);+        l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));       }        return l;@@ -1859,54 +1925,50 @@     Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";     BUILT_IN(selector_extend)     {-      Selector_List*  selector = ARGSEL("$selector", Selector_List, p_contextualize);-      Selector_List*  extendee = ARGSEL("$extendee", Selector_List, p_contextualize);-      Selector_List*  extender = ARGSEL("$extender", Selector_List, p_contextualize);+      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); -      ExtensionSubsetMap subset_map;+      Subset_Map subset_map;       extender->populate_extends(extendee, ctx, subset_map); -      bool extendedSomething;-      Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething);+      Selector_List_Obj result = Extend::extendSelectorList(selector, ctx, subset_map, false); -      Listize listize(ctx);+      Listize listize;       return result->perform(&listize);     }      Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";     BUILT_IN(selector_replace)     {-      Selector_List*  selector = ARGSEL("$selector", Selector_List, p_contextualize);-      Selector_List*  original = ARGSEL("$original", Selector_List, p_contextualize);-      Selector_List*  replacement = ARGSEL("$replacement", Selector_List, p_contextualize);--      ExtensionSubsetMap subset_map;+      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, ctx, subset_map); -      bool extendedSomething;-      Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething);+      Selector_List_Obj result = Extend::extendSelectorList(selector, ctx, subset_map, true); -      Listize listize(ctx);+      Listize listize;       return result->perform(&listize);     }      Signature selector_parse_sig = "selector-parse($selector)";     BUILT_IN(selector_parse)     {-      Selector_List* sel = ARGSEL("$selector", Selector_List, p_contextualize);+      Selector_List_Obj sel = ARGSEL("$selector", Selector_List_Obj, p_contextualize); -      Listize listize(ctx);+      Listize listize;       return sel->perform(&listize);     }      Signature is_superselector_sig = "is-superselector($super, $sub)";     BUILT_IN(is_superselector)     {-      To_String to_string(&ctx, false);-      Selector_List*  sel_sup = ARGSEL("$super", Selector_List, p_contextualize);-      Selector_List*  sel_sub = ARGSEL("$sub", Selector_List, p_contextualize);+      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(ctx.mem, Boolean, pstate, result);+      return SASS_MEMORY_NEW(Boolean, pstate, result);     }      Signature unique_id_sig = "unique-id()";@@ -1916,7 +1978,7 @@       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(ctx.mem, String_Quoted, pstate, ss.str());+      return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str());     }    }
libsass/src/functions.hpp view
@@ -1,25 +1,23 @@ #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*\-name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace)+#define BUILT_IN(name) Expression_Ptr \+name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace, std::vector<Selector_List_Obj> selector_stack)  namespace Sass {-  class Context;   struct Backtrace;-  class AST_Node;-  class Expression;-  class Definition;-  typedef Environment<AST_Node*> Env;+  typedef Environment<AST_Node_Obj> Env;   typedef const char* Signature;-  typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);+  typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*, std::vector<Selector_List_Obj>); -  Definition* make_native_function(Signature, Native_Function, Context& ctx);-  Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);+  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); 
libsass/src/inspect.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cmath> #include <string> #include <iostream>@@ -20,17 +21,17 @@   Inspect::~Inspect() { }    // statements-  void Inspect::operator()(Block* block)+  void Inspect::operator()(Block_Ptr block)   {     if (!block->is_root()) {       add_open_mapping(block);       append_scope_opener();     }-    if (output_style() == SASS_STYLE_NESTED) indentation += block->tabs();+    if (output_style() == NESTED) indentation += block->tabs();     for (size_t i = 0, L = block->length(); i < L; ++i) {       (*block)[i]->perform(this);     }-    if (output_style() == SASS_STYLE_NESTED) indentation -= block->tabs();+    if (output_style() == NESTED) indentation -= block->tabs();     if (!block->is_root()) {       append_scope_closer();       add_close_mapping(block);@@ -38,26 +39,23 @@    } -  void Inspect::operator()(Ruleset* ruleset)+  void Inspect::operator()(Ruleset_Ptr ruleset)   {-    ruleset->selector()->perform(this);-    ruleset->block()->perform(this);+    if (ruleset->selector()) {+      ruleset->selector()->perform(this);+    }+    if (ruleset->block()) {+      ruleset->block()->perform(this);+    }   } -  void Inspect::operator()(Keyframe_Rule* rule)+  void Inspect::operator()(Keyframe_Rule_Ptr rule)   {-    if (rule->selector()) rule->selector()->perform(this);+    if (rule->name()) rule->name()->perform(this);     if (rule->block()) rule->block()->perform(this);   } -  void Inspect::operator()(Propset* propset)-  {-    propset->property_fragment()->perform(this);-    append_colon_separator();-    propset->block()->perform(this);-  }--  void Inspect::operator()(Bubble* bubble)+  void Inspect::operator()(Bubble_Ptr bubble)   {     append_indentation();     append_token("::BUBBLE", bubble);@@ -66,7 +64,7 @@     append_scope_closer();   } -  void Inspect::operator()(Media_Block* media_block)+  void Inspect::operator()(Media_Block_Ptr media_block)   {     append_indentation();     append_token("@media", media_block);@@ -77,7 +75,7 @@     media_block->block()->perform(this);   } -  void Inspect::operator()(Supports_Block* feature_block)+  void Inspect::operator()(Supports_Block_Ptr feature_block)   {     append_indentation();     append_token("@supports", feature_block);@@ -86,7 +84,7 @@     feature_block->block()->perform(this);   } -  void Inspect::operator()(At_Root_Block* at_root_block)+  void Inspect::operator()(At_Root_Block_Ptr at_root_block)   {     append_indentation();     append_token("@at-root ", at_root_block);@@ -95,7 +93,7 @@     at_root_block->block()->perform(this);   } -  void Inspect::operator()(At_Rule* at_rule)+  void Inspect::operator()(Directive_Ptr at_rule)   {     append_indentation();     append_token(at_rule->keyword(), at_rule);@@ -118,20 +116,22 @@     }   } -  void Inspect::operator()(Declaration* dec)+  void Inspect::operator()(Declaration_Ptr dec)   {     if (dec->value()->concrete_type() == Expression::NULL_VAL) return;     bool was_decl = in_declaration;     in_declaration = true;-    if (output_style() == SASS_STYLE_NESTED)+    if (output_style() == NESTED)       indentation += dec->tabs();     append_indentation();-    dec->property()->perform(this);+    if (dec->property())+      dec->property()->perform(this);     append_colon_separator();      if (dec->value()->concrete_type() == Expression::SELECTOR) {-      Listize listize(*ctx);-      dec->value()->perform(&listize)->perform(this);+      Listize listize;+      Expression_Obj ls = dec->value()->perform(&listize);+      ls->perform(this);     } else {       dec->value()->perform(this);     }@@ -141,12 +141,12 @@       append_string("!important");     }     append_delimiter();-    if (output_style() == SASS_STYLE_NESTED)+    if (output_style() == NESTED)       indentation -= dec->tabs();     in_declaration = was_decl;   } -  void Inspect::operator()(Assignment* assn)+  void Inspect::operator()(Assignment_Ptr assn)   {     append_token(assn->variable(), assn);     append_colon_separator();@@ -158,20 +158,18 @@     append_delimiter();   } -  void Inspect::operator()(Import* import)+  void Inspect::operator()(Import_Ptr import)   {     if (!import->urls().empty()) {       append_token("@import", import);       append_mandatory_space(); -      if (String_Quoted* strq = dynamic_cast<String_Quoted*>(import->urls().front())) {-        strq->is_delayed(false);-      }-       import->urls().front()->perform(this);-      if (import->media_queries()) {-        append_mandatory_space();-        import->media_queries()->perform(this);+      if (import->urls().size() == 1) {+        if (&import->import_queries()) {+          append_mandatory_space();+          import->import_queries()->perform(this);+        }       }       append_delimiter();       for (size_t i = 1, S = import->urls().size(); i < S; ++i) {@@ -179,21 +177,19 @@         append_token("@import", import);         append_mandatory_space(); -        if (String_Quoted* strq = dynamic_cast<String_Quoted*>(import->urls()[i])) {-          strq->is_delayed(false);-        }-         import->urls()[i]->perform(this);-        if (import->media_queries()) {-          append_mandatory_space();-          import->media_queries()->perform(this);+        if (import->urls().size() - 1 == i) {+          if (&import->import_queries()) {+            append_mandatory_space();+            import->import_queries()->perform(this);+          }         }         append_delimiter();       }     }   } -  void Inspect::operator()(Import_Stub* import)+  void Inspect::operator()(Import_Stub_Ptr import)   {     append_indentation();     append_token("@import", import);@@ -202,7 +198,7 @@     append_delimiter();   } -  void Inspect::operator()(Warning* warning)+  void Inspect::operator()(Warning_Ptr warning)   {     append_indentation();     append_token("@warn", warning);@@ -211,7 +207,7 @@     append_delimiter();   } -  void Inspect::operator()(Error* error)+  void Inspect::operator()(Error_Ptr error)   {     append_indentation();     append_token("@error", error);@@ -220,7 +216,7 @@     append_delimiter();   } -  void Inspect::operator()(Debug* debug)+  void Inspect::operator()(Debug_Ptr debug)   {     append_indentation();     append_token("@debug", debug);@@ -229,14 +225,14 @@     append_delimiter();   } -  void Inspect::operator()(Comment* comment)+  void Inspect::operator()(Comment_Ptr comment)   {     in_comment = true;     comment->text()->perform(this);     in_comment = false;   } -  void Inspect::operator()(If* cond)+  void Inspect::operator()(If_Ptr cond)   {     append_indentation();     append_token("@if", cond);@@ -251,7 +247,7 @@     }   } -  void Inspect::operator()(For* loop)+  void Inspect::operator()(For_Ptr loop)   {     append_indentation();     append_token("@for", loop);@@ -264,7 +260,7 @@     loop->block()->perform(this);   } -  void Inspect::operator()(Each* loop)+  void Inspect::operator()(Each_Ptr loop)   {     append_indentation();     append_token("@each", loop);@@ -279,7 +275,7 @@     loop->block()->perform(this);   } -  void Inspect::operator()(While* loop)+  void Inspect::operator()(While_Ptr loop)   {     append_indentation();     append_token("@while", loop);@@ -288,7 +284,7 @@     loop->block()->perform(this);   } -  void Inspect::operator()(Return* ret)+  void Inspect::operator()(Return_Ptr ret)   {     append_indentation();     append_token("@return", ret);@@ -297,7 +293,7 @@     append_delimiter();   } -  void Inspect::operator()(Extension* extend)+  void Inspect::operator()(Extension_Ptr extend)   {     append_indentation();     append_token("@extend", extend);@@ -306,7 +302,7 @@     append_delimiter();   } -  void Inspect::operator()(Definition* def)+  void Inspect::operator()(Definition_Ptr def)   {     append_indentation();     if (def->type() == Definition::MIXIN) {@@ -321,7 +317,7 @@     def->block()->perform(this);   } -  void Inspect::operator()(Mixin_Call* call)+  void Inspect::operator()(Mixin_Call_Ptr call)   {     append_indentation();     append_token("@include", call);@@ -337,15 +333,19 @@     if (!call->block()) append_delimiter();   } -  void Inspect::operator()(Content* content)+  void Inspect::operator()(Content_Ptr content)   {     append_indentation();     append_token("@content", content);     append_delimiter();   } -  void Inspect::operator()(Map* map)+  void Inspect::operator()(Map_Ptr map)   {+    if (output_style() == TO_SASS && map->empty()) {+      append_string("()");+      return;+    }     if (map->empty()) return;     if (map->is_invisible()) return;     bool items_output = false;@@ -360,17 +360,29 @@     append_string(")");   } -  void Inspect::operator()(List* list)+  void Inspect::operator()(List_Ptr list)   {+    if (output_style() == TO_SASS && list->empty()) {+      append_string("()");+      return;+    }     std::string sep(list->separator() == SASS_SPACE ? " " : ",");-    if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && sep == ",") sep += " ";+    if ((output_style() != COMPRESSED) && sep == ",") sep += " ";     else if (in_media_block && sep != " ") sep += " "; // verified     if (list->empty()) return;     bool items_output = false;      bool was_space_array = in_space_array;     bool was_comma_array = in_comma_array;-    if (!in_declaration && (+    // probably ruby sass eqivalent of element_needs_parens+    if (output_style() == TO_SASS &&+        list->length() == 1 &&+        !list->from_selector() &&+        !SASS_MEMORY_CAST(List, list->at(0)) &&+        !SASS_MEMORY_CAST(Selector_List, list->at(0))) {+      append_string("(");+    }+    else if (!in_declaration && (list->separator() == SASS_HASH ||         (list->separator() == SASS_SPACE && in_space_array) ||         (list->separator() == SASS_COMMA && in_comma_array)     )) {@@ -381,9 +393,16 @@     else if (list->separator() == SASS_COMMA) in_comma_array = true;      for (size_t i = 0, L = list->size(); i < L; ++i) {-      Expression* list_item = (*list)[i];-      if (list_item->is_invisible()) {-        continue;+      if (list->separator() == SASS_HASH)+      { sep[0] = i % 2 ? ':' : ','; }+      Expression_Obj list_item = list->at(i);+      if (output_style() != TO_SASS) {+        if (list_item->is_invisible()) {+          // this fixes an issue with "" in a list+          if (!SASS_MEMORY_CAST(String_Constant, list_item)) {+            continue;+          }+        }       }       if (items_output) {         append_string(sep);@@ -396,7 +415,15 @@      in_comma_array = was_comma_array;     in_space_array = was_space_array;-    if (!in_declaration && (+    // probably ruby sass eqivalent of element_needs_parens+    if (output_style() == TO_SASS &&+        list->length() == 1 &&+        !list->from_selector() &&+        !SASS_MEMORY_CAST(List, list->at(0)) &&+        !SASS_MEMORY_CAST(Selector_List, list->at(0))) {+      append_string(",)");+    }+    else if (!in_declaration && (list->separator() == SASS_HASH ||         (list->separator() == SASS_SPACE && in_space_array) ||         (list->separator() == SASS_COMMA && in_comma_array)     )) {@@ -405,85 +432,258 @@    } -  void Inspect::operator()(Binary_Expression* expr)+  void Inspect::operator()(Binary_Expression_Ptr expr)   {     expr->left()->perform(this);+    if ( in_media_block ||+         (output_style() == INSPECT) || (+          expr->op().ws_before+          && (!expr->is_interpolant())+          && (expr->is_left_interpolant() ||+              expr->is_right_interpolant())++    )) append_string(" ");     switch (expr->type()) {-      case Sass_OP::AND: append_string(" and "); break;-      case Sass_OP::OR:  append_string(" or ");  break;-      case Sass_OP::EQ:  append_string(" == ");  break;-      case Sass_OP::NEQ: append_string(" != ");  break;-      case Sass_OP::GT:  append_string(" > ");   break;-      case Sass_OP::GTE: append_string(" >= ");  break;-      case Sass_OP::LT:  append_string(" < ");   break;-      case Sass_OP::LTE: append_string(" <= ");  break;-      case Sass_OP::ADD: append_string(" + ");   break;-      case Sass_OP::SUB: append_string(" - ");   break;-      case Sass_OP::MUL: append_string(" * ");   break;-      case Sass_OP::DIV: append_string(in_media_block ? " / " : "/"); break;-      case Sass_OP::MOD: append_string(" % ");   break;+      case Sass_OP::AND: append_string("&&"); break;+      case Sass_OP::OR:  append_string("||");  break;+      case Sass_OP::EQ:  append_string("==");  break;+      case Sass_OP::NEQ: append_string("!=");  break;+      case Sass_OP::GT:  append_string(">");   break;+      case Sass_OP::GTE: append_string(">=");  break;+      case Sass_OP::LT:  append_string("<");   break;+      case Sass_OP::LTE: append_string("<=");  break;+      case Sass_OP::ADD: append_string("+");   break;+      case Sass_OP::SUB: append_string("-");   break;+      case Sass_OP::MUL: append_string("*");   break;+      case Sass_OP::DIV: append_string("/"); break;+      case Sass_OP::MOD: append_string("%");   break;       default: break; // shouldn't get here     }+    if ( in_media_block ||+         (output_style() == INSPECT) || (+          expr->op().ws_after+          && (!expr->is_interpolant())+          && (expr->is_left_interpolant() ||+              expr->is_right_interpolant())+    )) append_string(" ");     expr->right()->perform(this);   } -  void Inspect::operator()(Unary_Expression* expr)+  void Inspect::operator()(Unary_Expression_Ptr expr)   {     if (expr->type() == Unary_Expression::PLUS) append_string("+");     else                                        append_string("-");     expr->operand()->perform(this);   } -  void Inspect::operator()(Function_Call* call)+  void Inspect::operator()(Function_Call_Ptr call)   {     append_token(call->name(), call);     call->arguments()->perform(this);   } -  void Inspect::operator()(Function_Call_Schema* call)+  void Inspect::operator()(Function_Call_Schema_Ptr call)   {     call->name()->perform(this);     call->arguments()->perform(this);   } -  void Inspect::operator()(Variable* var)+  void Inspect::operator()(Variable_Ptr var)   {     append_token(var->name(), var);   } -  void Inspect::operator()(Textual* txt)+  void Inspect::operator()(Textual_Ptr txt)   {     append_token(txt->value(), txt);   } -  void Inspect::operator()(Number* n)+  void Inspect::operator()(Number_Ptr n)   {-    // use values to_string facility-    bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;-    std::string res(n->to_string(compressed, (int)ctx->c_options->precision));++    std::string res;++    // 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;++    // reset stream for another run+    ss.clear(); ss.str(std::string());++    // take a shortcut for integers+    if (is_int)+    {+      ss.precision(0);+      ss << std::fixed << n->value();+      res = std::string(ss.str());+    }+    // process floats+    else+    {+      // 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);+      }+    }++    // some final cosmetics+    if (res == "0.0") res = "0";+    else if (res == "") res = "0";+    else if (res == "-0") res = "0";+    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);+    }++    // add unit now+    res += n->unit();+     // output the final token     append_token(res, n);   } -  void Inspect::operator()(Color* c)+  // helper function for serializing colors+  template <size_t range>+  static double cap_channel(double c) {+    if      (c > range) return range;+    else if (c < 0)     return 0;+    else                return c;+  }++  void Inspect::operator()(Color_Ptr c)   {-    // use values to_string facility-    bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;-    std::string res(c->to_string(compressed, (int)ctx->c_options->precision));     // output the final token-    append_token(res, c);+    std::stringstream ss;++    // original color name+    // maybe an unknown token+    std::string name = c->disp();++    // resolved color+    std::string res_name = name;++    double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision);+    double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision);+    double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision);+    double a = cap_channel<1>   (c->a());++    // 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);+      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);+      a = cap_channel<1>   (n->a());+    }+    // otherwise get the possible resolved color name+    else {+      double numval = r * 0x10000 + g * 0x100 + b;+      if (color_to_name(numval))+        res_name = color_to_name(numval);+    }++    std::stringstream hexlet;+    bool compressed = opt.output_style == COMPRESSED;+    hexlet << '#' << std::setw(1) << std::setfill('0');+    // create a short color hexlet if there is any need for it+    if (compressed && is_color_doublet(r, g, b) && a == 1) {+      hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);+      hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);+      hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);+    } else {+      hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);+      hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);+      hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);+    }++    if (compressed && !c->is_delayed()) name = "";+    if (opt.output_style == INSPECT && a >= 1) {+      append_token(hexlet.str(), c);+      return;+    }++    // retain the originally specified color definition if unchanged+    if (name != "") {+      ss << name;+    }+    else if (r == 0 && g == 0 && b == 0 && a == 0) {+        ss << "transparent";+    }+    else if (a >= 1) {+      if (res_name != "") {+        if (compressed && hexlet.str().size() < res_name.size()) {+          ss << hexlet.str();+        } else {+          ss << res_name;+        }+      }+      else {+        ss << hexlet.str();+      }+    }+    else {+      ss << "rgba(";+      ss << static_cast<unsigned long>(r) << ",";+      if (!compressed) ss << " ";+      ss << static_cast<unsigned long>(g) << ",";+      if (!compressed) ss << " ";+      ss << static_cast<unsigned long>(b) << ",";+      if (!compressed) ss << " ";+      ss << a << ')';+    }++    append_token(ss.str(), c);+   } -  void Inspect::operator()(Boolean* b)+  void Inspect::operator()(Boolean_Ptr b)   {-    // use values to_string facility-    bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;-    std::string res(b->to_string(compressed, (int)ctx->c_options->precision));     // output the final token-    append_token(res, b);+    append_token(b->value() ? "true" : "false", b);   } -  void Inspect::operator()(String_Schema* ss)+  void Inspect::operator()(String_Schema_Ptr ss)   {     // Evaluation should turn these into String_Constants,     // so this method is only for inspection purposes.@@ -494,30 +694,33 @@     }   } -  void Inspect::operator()(String_Constant* s)+  void Inspect::operator()(String_Constant_Ptr s)   {-    // get options from optional? context-    int precision = ctx ? (int)ctx->c_options->precision : 5;-    bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false;-    // use values to_string facility-    std::string res(s->to_string(compressed, precision));-    // output the final token-    append_token(res, s);+    append_token(s->value(), s);   } -  void Inspect::operator()(String_Quoted* s)+  void Inspect::operator()(String_Quoted_Ptr s)   {-    // get options from optional? context-    int precision = ctx ? (int)ctx->c_options->precision : 5;-    bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false;-    // use values to_string facility-    std::string res(s->to_string(compressed, precision));-    // output the final token-    append_token(res, s);+    if (const char q = s->quote_mark()) {+      append_token(quote(s->value(), q), s);+    } else {+      append_token(s->value(), s);+    }   }-  void Inspect::operator()(Supports_Operator* so)++  void Inspect::operator()(Custom_Error_Ptr e)   {+    append_token(e->message(), e);+  } +  void Inspect::operator()(Custom_Warning_Ptr w)+  {+    append_token(w->message(), w);+  }++  void Inspect::operator()(Supports_Operator_Ptr so)+  {+     if (so->needs_parens(so->left())) append_string("(");     so->left()->perform(this);     if (so->needs_parens(so->left())) append_string(")");@@ -537,16 +740,16 @@     if (so->needs_parens(so->right())) append_string(")");   } -  void Inspect::operator()(Supports_Negation* sn)+  void Inspect::operator()(Supports_Negation_Ptr sn)   {     append_token("not", sn);     append_mandatory_space();-    if (sn->needs_parens(sn->condition())) append_string("(");+    if (sn->needs_parens(&sn->condition())) append_string("(");     sn->condition()->perform(this);-    if (sn->needs_parens(sn->condition())) append_string(")");+    if (sn->needs_parens(&sn->condition())) append_string(")");   } -  void Inspect::operator()(Supports_Declaration* sd)+  void Inspect::operator()(Supports_Declaration_Ptr sd)   {     append_string("(");     sd->feature()->perform(this);@@ -555,15 +758,15 @@     append_string(")");   } -  void Inspect::operator()(Supports_Interpolation* sd)+  void Inspect::operator()(Supports_Interpolation_Ptr sd)   {     sd->value()->perform(this);   } -  void Inspect::operator()(Media_Query* mq)+  void Inspect::operator()(Media_Query_Ptr mq)   {     size_t i = 0;-    if (mq->media_type()) {+    if (&mq->media_type()) {       if      (mq->is_negated())    append_string("not ");       else if (mq->is_restricted()) append_string("only ");       mq->media_type()->perform(this);@@ -577,7 +780,7 @@     }   } -  void Inspect::operator()(Media_Query_Expression* mqe)+  void Inspect::operator()(Media_Query_Expression_Ptr mqe)   {     if (mqe->is_interpolated()) {       mqe->feature()->perform(this);@@ -593,33 +796,25 @@     }   } -  void Inspect::operator()(At_Root_Expression* ae)+  void Inspect::operator()(At_Root_Query_Ptr ae)   {-    if (ae->is_interpolated()) {-      ae->feature()->perform(this);-    }-    else {-      append_string("(");-      ae->feature()->perform(this);-      if (ae->value()) {-        append_colon_separator();-        ae->value()->perform(this);-      }-      append_string(")");+    append_string("(");+    ae->feature()->perform(this);+    if (ae->value()) {+      append_colon_separator();+      ae->value()->perform(this);     }+    append_string(")");   } -  void Inspect::operator()(Null* n)+  void Inspect::operator()(Null_Ptr n)   {-    // use values to_string facility-    bool compressed = output_style() == SASS_STYLE_COMPRESSED;-    std::string res(n->to_string(compressed, (int)ctx->c_options->precision));     // output the final token-    append_token(res, n);+    append_token("null", n);   }    // parameters and arguments-  void Inspect::operator()(Parameter* p)+  void Inspect::operator()(Parameter_Ptr p)   {     append_token(p->name(), p);     if (p->default_value()) {@@ -631,7 +826,7 @@     }   } -  void Inspect::operator()(Parameters* p)+  void Inspect::operator()(Parameters_Ptr p)   {     append_string("(");     if (!p->empty()) {@@ -644,26 +839,29 @@     append_string(")");   } -  void Inspect::operator()(Argument* a)+  void Inspect::operator()(Argument_Ptr a)   {     if (!a->name().empty()) {       append_token(a->name(), a);       append_colon_separator();     }+    if (!a->value()) return;     // Special case: argument nulls can be ignored     if (a->value()->concrete_type() == Expression::NULL_VAL) {       return;     }     if (a->value()->concrete_type() == Expression::STRING) {-      String_Constant* s = static_cast<String_Constant*>(a->value());-      s->perform(this);-    } else a->value()->perform(this);+      String_Constant_Ptr s = SASS_MEMORY_CAST(String_Constant, a->value());+      if (s) s->perform(this);+    } else {+      a->value()->perform(this);+    }     if (a->is_rest_argument()) {       append_string("...");     }   } -  void Inspect::operator()(Arguments* a)+  void Inspect::operator()(Arguments_Ptr a)   {     append_string("(");     if (!a->empty()) {@@ -677,17 +875,17 @@     append_string(")");   } -  void Inspect::operator()(Selector_Schema* s)+  void Inspect::operator()(Selector_Schema_Ptr s)   {     s->contents()->perform(this);   } -  void Inspect::operator()(Parent_Selector* p)+  void Inspect::operator()(Parent_Selector_Ptr p)   {-    append_string("&");+    if (p->is_real_parent_ref()) append_string("&");   } -  void Inspect::operator()(Selector_Placeholder* s)+  void Inspect::operator()(Placeholder_Selector_Ptr s)   {     append_token(s->name(), s);     if (s->has_line_break()) append_optional_linefeed();@@ -695,26 +893,33 @@    } -  void Inspect::operator()(Type_Selector* s)+  void Inspect::operator()(Element_Selector_Ptr s)   {     append_token(s->ns_name(), s);   } -  void Inspect::operator()(Selector_Qualifier* s)+  void Inspect::operator()(Class_Selector_Ptr 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* s)+  void Inspect::operator()(Id_Selector_Ptr 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)+  {     append_string("[");     add_open_mapping(s);     append_token(s->ns_name(), s);     if (!s->matcher().empty()) {       append_string(s->matcher());-      if (s->value()) {+      if (&s->value() && *s->value()) {         s->value()->perform(this);       }     }@@ -722,7 +927,7 @@     append_string("]");   } -  void Inspect::operator()(Pseudo_Selector* s)+  void Inspect::operator()(Pseudo_Selector_Ptr s)   {     append_token(s->ns_name(), s);     if (s->expression()) {@@ -732,7 +937,7 @@     }   } -  void Inspect::operator()(Wrapped_Selector* s)+  void Inspect::operator()(Wrapped_Selector_Ptr s)   {     bool was = in_wrapped;     in_wrapped = true;@@ -746,24 +951,29 @@     in_wrapped = was;   } -  void Inspect::operator()(Compound_Selector* s)+  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() != SASS_STYLE_COMPACT) {+      if (output_style() != COMPACT) {         append_optional_linefeed();       }     }   } -  void Inspect::operator()(Complex_Selector* c)+  void Inspect::operator()(Complex_Selector_Ptr c)   {-    Compound_Selector*           head = c->head();-    Complex_Selector*            tail = c->tail();+    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();@@ -774,7 +984,7 @@     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() == SASS_STYLE_COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;+    if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;      switch (comb) {       case Complex_Selector::ANCESTOR_OF:@@ -810,27 +1020,43 @@     }     if (tail) tail->perform(this);     if (!tail && c->has_line_break()) {-      if (output_style() == SASS_STYLE_COMPACT) {+      if (output_style() == COMPACT) {         append_mandatory_space();       }     }   } -  void Inspect::operator()(Selector_List* g)+  void Inspect::operator()(Selector_List_Ptr g)   {-    if (g->empty()) return; +    if (g->empty()) {+      if (output_style() == TO_SASS) {+        append_token("()", g);+      }+      return;+    }++     bool was_comma_array = in_comma_array;-    if (!in_declaration && in_comma_array) {+    // probably ruby sass eqivalent of element_needs_parens+    if (output_style() == TO_SASS && g->length() == 1 &&+      (!SASS_MEMORY_CAST(List, (*g)[0]) &&+       !SASS_MEMORY_CAST(Selector_List, (*g)[0]))) {       append_string("(");     }+    else if (!in_declaration && in_comma_array) {+      append_string("(");+    }      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;+      schedule_mapping(&g->at(i)->last());+      // add_open_mapping((*g)[i]->last());       (*g)[i]->perform(this);+      // add_close_mapping((*g)[i]->last());       if (i < L - 1) {         scheduled_space = 0;         append_comma_separator();@@ -838,13 +1064,19 @@     }      in_comma_array = was_comma_array;-    if (!in_declaration && in_comma_array) {+    // probably ruby sass eqivalent of element_needs_parens+    if (output_style() == TO_SASS && g->length() == 1 &&+      (!SASS_MEMORY_CAST(List, (*g)[0]) &&+       !SASS_MEMORY_CAST(Selector_List, (*g)[0]))) {+      append_string(",)");+    }+    else if (!in_declaration && in_comma_array) {       append_string(")");     }    } -  void Inspect::fallback_impl(AST_Node* n)+  void Inspect::fallback_impl(AST_Node_Ptr n)   {   } 
libsass/src/inspect.hpp view
@@ -13,7 +13,7 @@     // import all the class-specific methods and override as desired     using Operation_CRTP<void, Inspect>::operator(); -    void fallback_impl(AST_Node* n);+    void fallback_impl(AST_Node_Ptr n);    public: @@ -21,77 +21,79 @@     virtual ~Inspect();      // statements-    virtual void operator()(Block*);-    virtual void operator()(Ruleset*);-    virtual void operator()(Propset*);-    virtual void operator()(Bubble*);-    virtual void operator()(Supports_Block*);-    virtual void operator()(Media_Block*);-    virtual void operator()(At_Root_Block*);-    virtual void operator()(At_Rule*);-    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()(Extension*);-    virtual void operator()(Definition*);-    virtual void operator()(Mixin_Call*);-    virtual void operator()(Content*);+    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);     // expressions-    virtual void operator()(Map*);-    virtual void operator()(List*);-    virtual void operator()(Binary_Expression*);-    virtual void operator()(Unary_Expression*);-    virtual void operator()(Function_Call*);-    virtual void operator()(Function_Call_Schema*);-    // virtual void operator()(Custom_Warning*);-    // virtual void operator()(Custom_Error*);-    virtual void operator()(Variable*);-    virtual void operator()(Textual*);-    virtual void operator()(Number*);-    virtual void operator()(Color*);-    virtual void operator()(Boolean*);-    virtual void operator()(String_Schema*);-    virtual void operator()(String_Constant*);-    virtual void operator()(String_Quoted*);-    virtual void operator()(Supports_Operator*);-    virtual void operator()(Supports_Negation*);-    virtual void operator()(Supports_Declaration*);-    virtual void operator()(Supports_Interpolation*);-    virtual void operator()(Media_Query*);-    virtual void operator()(Media_Query_Expression*);-    virtual void operator()(At_Root_Expression*);-    virtual void operator()(Null*);-    virtual void operator()(Parent_Selector* p);+    virtual void operator()(Map_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()(Textual_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);     // parameters and arguments-    virtual void operator()(Parameter*);-    virtual void operator()(Parameters*);-    virtual void operator()(Argument*);-    virtual void operator()(Arguments*);+    virtual void operator()(Parameter_Ptr);+    virtual void operator()(Parameters_Ptr);+    virtual void operator()(Argument_Ptr);+    virtual void operator()(Arguments_Ptr);     // selectors-    virtual void operator()(Selector_Schema*);-    virtual void operator()(Selector_Placeholder*);-    virtual void operator()(Type_Selector*);-    virtual void operator()(Selector_Qualifier*);-    virtual void operator()(Attribute_Selector*);-    virtual void operator()(Pseudo_Selector*);-    virtual void operator()(Wrapped_Selector*);-    virtual void operator()(Compound_Selector*);-    virtual void operator()(Complex_Selector*);-    virtual void operator()(Selector_List*);+    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);      // template <typename U>-    // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node*>(x)); }+    // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node_Ptr>(x)); }   };  }
libsass/src/json.cpp view
@@ -28,6 +28,10 @@  #include "json.hpp" +// include utf8 library used by libsass+// ToDo: replace internal json utf8 code+#include "utf8.h"+ #include <assert.h> #include <stdint.h> #include <stdio.h>@@ -395,7 +399,13 @@   SB sb;   sb_init(&sb); -  emit_string(&sb, str);+  try {+    emit_string(&sb, str);+  }+  catch (std::exception) {+    sb_free(&sb);+    throw;+  }    return sb_finish(&sb); }@@ -405,10 +415,16 @@   SB sb;   sb_init(&sb); -  if (space != NULL)-    emit_value_indented(&sb, node, space, 0);-  else-    emit_value(&sb, node);+  try {+    if (space != NULL)+      emit_value_indented(&sb, node, space, 0);+    else+      emit_value(&sb, node);+  }+  catch (std::exception) {+    sb_free(&sb);+    throw;+  }    return sb_finish(&sb); }@@ -544,42 +560,50 @@  static void append_node(JsonNode *parent, JsonNode *child) {-  child->parent = parent;-  child->prev = parent->children.tail;-  child->next = NULL;+  if (child != NULL && parent != NULL) {+      child->parent = parent;+      child->prev = parent->children.tail;+      child->next = NULL; -  if (parent->children.tail != NULL)-    parent->children.tail->next = child;-  else-    parent->children.head = child;-  parent->children.tail = child;+      if (parent->children.tail != NULL)+          parent->children.tail->next = child;+      else+          parent->children.head = child;+      parent->children.tail = child;+  } }  static void prepend_node(JsonNode *parent, JsonNode *child) {-  child->parent = parent;-  child->prev = NULL;-  child->next = parent->children.head;+  if (child != NULL && parent != NULL) {+      child->parent = parent;+      child->prev = NULL;+      child->next = parent->children.head; -  if (parent->children.head != NULL)-    parent->children.head->prev = child;-  else-    parent->children.tail = child;-  parent->children.head = child;+      if (parent->children.head != NULL)+          parent->children.head->prev = child;+      else+          parent->children.tail = child;+      parent->children.head = child;+  } }  static void append_member(JsonNode *object, char *key, JsonNode *value) {-  value->key = key;-  append_node(object, value);+  if (value != NULL && object != NULL) {+      value->key = key;+      append_node(object, value);+  } }  void json_append_element(JsonNode *array, JsonNode *element) {-  assert(array->tag == JSON_ARRAY);-  assert(element->parent == NULL);+  if (array != NULL && element !=NULL) {+      assert(array->tag == JSON_ARRAY);+      assert(element->parent == NULL); -  append_node(array, element);+      append_node(array, element);+  } }  void json_prepend_element(JsonNode *array, JsonNode *element)@@ -592,40 +616,47 @@  void json_append_member(JsonNode *object, const char *key, JsonNode *value) {-  assert(object->tag == JSON_OBJECT);-  assert(value->parent == NULL);+  if (object != NULL && key != NULL && value != NULL) {+      assert(object->tag == JSON_OBJECT);+      assert(value->parent == NULL); -  append_member(object, json_strdup(key), value);+      append_member(object, json_strdup(key), value);+  } }  void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) {-  assert(object->tag == JSON_OBJECT);-  assert(value->parent == NULL);+  if (object != NULL && key != NULL && value != NULL) {+      assert(object->tag == JSON_OBJECT);+      assert(value->parent == NULL); -  value->key = json_strdup(key);-  prepend_node(object, value);+      value->key = json_strdup(key);+      prepend_node(object, value);+  } }  void json_remove_from_parent(JsonNode *node) {-  JsonNode *parent = node->parent;+  if (node != NULL) {+      JsonNode *parent = node->parent; -  if (parent != NULL) {-    if (node->prev != NULL)-      node->prev->next = node->next;-    else-      parent->children.head = node->next;-    if (node->next != NULL)-      node->next->prev = node->prev;-    else-      parent->children.tail = node->prev;+      if (parent != NULL) {+          if (node->prev != NULL)+              node->prev->next = node->next;+          else+              parent->children.head = node->next; -    free(node->key);+          if (node->next != NULL)+              node->next->prev = node->prev;+          else+              parent->children.tail = node->prev; -    node->parent = NULL;-    node->prev = node->next = NULL;-    node->key = NULL;+          free(node->key);++          node->parent = NULL;+          node->prev = node->next = NULL;+          node->key = NULL;+      }   } } @@ -1125,6 +1156,13 @@   bool escape_unicode = false;   const char *s = str;   char *b;++// make assertion catchable+#ifndef NDEBUG+  if (!utf8_validate(str)) {+    throw utf8::invalid_utf8(0);+  }+#endif    assert(utf8_validate(str)); 
libsass/src/json.hpp view
@@ -43,21 +43,21 @@   /* only if parent is an object or array (NULL otherwise) */   JsonNode *parent;   JsonNode *prev, *next;-  +   /* only if parent is an object (NULL otherwise) */   char *key; /* Must be valid UTF-8. */-  +   JsonTag tag;   union {     /* JSON_BOOL */     bool bool_;-    +     /* JSON_STRING */     char *string_; /* Must be valid UTF-8. */-    +     /* JSON_NUMBER */     double number_;-    +     /* JSON_ARRAY */     /* JSON_OBJECT */     struct {
libsass/src/lexer.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cctype> #include <cstddef> #include <iostream>@@ -80,9 +81,9 @@     bool is_nonascii(const char& chr)     {       return (-        (unsigned(chr) > 127 && unsigned(chr) < 55296) ||-        (unsigned(chr) > 57343 && unsigned(chr) < 65534) ||-        (unsigned(chr) > 65535 && unsigned(chr) < 1114111)+        (unsigned(chr) >= 128 && unsigned(chr) <= 15572911) ||+        (unsigned(chr) >= 15630464 && unsigned(chr) <= 15712189) ||+        (unsigned(chr) >= 4036001920)       );     } @@ -90,7 +91,8 @@     // valid in a uri (copied from Ruby Sass)     bool is_uri_character(const char& chr)     {-      return unsigned(chr) > 41 && unsigned(chr) < 127;+      return (unsigned(chr) > 41 && unsigned(chr) < 127) ||+             unsigned(chr) == ':' || unsigned(chr) == '/';     }      // check if char is within a reduced ascii range@@ -120,6 +122,7 @@     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* 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; }@@ -127,6 +130,7 @@     // Match multiple ctype characters.     const char* spaces(const char* src) { return one_plus<space>(src); }     const char* digits(const char* src) { return one_plus<digit>(src); }+    const char* hyphens(const char* src) { return one_plus<hyphen>(src); }      // Whitespace handling.     const char* no_spaces(const char* src) { return negate< space >(src); }@@ -136,7 +140,7 @@     const char* any_char(const char* src) { return *src ? src + 1 : src; }      // Match word boundary (zero-width lookahead).-    const char* word_boundary(const char* src) { return is_character(*src) ? 0 : src; }+    const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; }      // Match linefeed /(?:\n|\r\n?)/     const char* re_linebreak(const char* src)
libsass/src/lexer.hpp view
@@ -44,6 +44,7 @@     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);@@ -53,6 +54,7 @@     // Match multiple ctype characters.     const char* spaces(const char* src);     const char* digits(const char* src);+    const char* hyphens(const char* src);      // Whitespace handling.     const char* no_spaces(const char* src);@@ -139,6 +141,25 @@     const char* class_chars(const char* src) {       const char* p = src;       while (class_char<char_class>(p)) ++p;+      return p == src ? 0 : p;+    }++    // Match for members of char class.+    // Regex equivalent: /[^axy]/+    template <const char* neg_char_class>+    const char* neg_class_char(const char* src) {+      if (*src == 0) return 0;+      const char* cc = neg_char_class;+      while (*cc && *src != *cc) ++cc;+      return *cc ? 0 : src + 1;+    }++    // Match for members of char class.+    // Regex equivalent: /[^axy]+/+    template <const char* neg_char_class>+    const char* neg_class_chars(const char* src) {+      const char* p = src;+      while (neg_class_char<neg_char_class>(p)) ++p;       return p == src ? 0 : p;     } 
libsass/src/listize.cpp view
@@ -1,87 +1,85 @@+#include "sass.hpp" #include <iostream> #include <typeinfo> #include <string>  #include "listize.hpp"-#include "to_string.hpp" #include "context.hpp" #include "backtrace.hpp" #include "error_handling.hpp"  namespace Sass { -  Listize::Listize(Context& ctx)-  : ctx(ctx)+  Listize::Listize()   {  } -  Expression* Listize::operator()(Selector_List* sel)+  Expression_Ptr Listize::operator()(Selector_List_Ptr sel)   {-    List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA);+    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) {-      if (!(*sel)[i]) continue;-      *l << (*sel)[i]->perform(this);+      if (!sel->at(i)) continue;+      l->append(sel->at(i)->perform(this));     }-    return l;+    if (l->length()) return l.detach();+    return SASS_MEMORY_NEW(Null, l->pstate());   } -  Expression* Listize::operator()(Compound_Selector* sel)+  Expression_Ptr Listize::operator()(Compound_Selector_Ptr sel)   {-    To_String to_string;     std::string str;     for (size_t i = 0, L = sel->length(); i < L; ++i) {-      Expression* e = (*sel)[i]->perform(this);-      if (e) str += e->perform(&to_string);+      Expression_Ptr e = (*sel)[i]->perform(this);+      if (e) str += e->to_string();     }-    return SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), str);+    return SASS_MEMORY_NEW(String_Quoted, sel->pstate(), str);   } -  Expression* Listize::operator()(Complex_Selector* sel)+  Expression_Ptr Listize::operator()(Complex_Selector_Ptr sel)   {-    List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), 2);--    Compound_Selector* head = sel->head();+    List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), 2);+    l->from_selector(true);+    Compound_Selector_Obj head = sel->head();     if (head && !head->is_empty_reference())     {-      Expression* hh = head->perform(this);-      if (hh) *l << hh;+      Expression_Ptr hh = head->perform(this);+      if (hh) l->append(hh);     } -    To_String to_string;     std::string reference = ! sel->reference() ? ""-      : sel->reference()->perform(&to_string);+      : sel->reference()->to_string();     switch(sel->combinator())     {       case Complex_Selector::PARENT_OF:-        *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), ">");+        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), ">"));       break;       case Complex_Selector::ADJACENT_TO:-        *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "+");+        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "+"));       break;       case Complex_Selector::REFERENCE:-        *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "/" + reference + "/");+        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "/" + reference + "/"));       break;       case Complex_Selector::PRECEDES:-        *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "~");+        l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "~"));       break;       case Complex_Selector::ANCESTOR_OF:       break;     } -    Complex_Selector* tail = sel->tail();+    Complex_Selector_Obj tail = sel->tail();     if (tail)     {-      Expression* tt = tail->perform(this);-      if (tt && tt->concrete_type() == Expression::LIST)-      { *l += static_cast<List*>(tt); }-      else if (tt) *l << static_cast<List*>(tt);+      Expression_Obj tt = tail->perform(this);+      if (List_Ptr ls = SASS_MEMORY_CAST(List, tt))+      { l->concat(ls); }     }     if (l->length() == 0) return 0;-    return l;+    return l.detach();   } -  Expression* Listize::fallback_impl(AST_Node* n)+  Expression_Ptr Listize::fallback_impl(AST_Node_Ptr n)   {-    return dynamic_cast<Expression*>(n);+    return dynamic_cast<Expression_Ptr>(n);   }  }
libsass/src/listize.hpp view
@@ -11,27 +11,23 @@  namespace Sass { -  typedef Environment<AST_Node*> Env;+  typedef Environment<AST_Node_Obj> Env;   struct Backtrace; -  class Listize : public Operation_CRTP<Expression*, Listize> {--    Context&            ctx;+  class Listize : public Operation_CRTP<Expression_Ptr, Listize> { -    Expression* fallback_impl(AST_Node* n);+    Expression_Ptr fallback_impl(AST_Node_Ptr n);    public:-    Listize(Context&);-    virtual ~Listize() { }--    using Operation<Expression*>::operator();+    Listize();+    ~Listize() { } -    Expression* operator()(Selector_List*);-    Expression* operator()(Complex_Selector*);-    Expression* operator()(Compound_Selector*);+    Expression_Ptr operator()(Selector_List_Ptr);+    Expression_Ptr operator()(Complex_Selector_Ptr);+    Expression_Ptr operator()(Compound_Selector_Ptr);      template <typename U>-    Expression* fallback(U x) { return fallback_impl(x); }+    Expression_Ptr fallback(U x) { return fallback_impl(x); }   };  }
+ libsass/src/memory/SharedPtr.cpp view
@@ -0,0 +1,116 @@+#include "../sass.hpp"+#include <iostream>+#include <typeinfo>++#include "SharedPtr.hpp"+#include "../ast_fwd_decl.hpp"++#ifdef DEBUG_SHARED_PTR+#include "../debugger.hpp"+#endif++namespace Sass {++  #ifdef DEBUG_SHARED_PTR+  void SharedObj::dumpMemLeaks() {+    if (!all.empty()) {+      std::cerr << "###################################\n";+      std::cerr << "# REPORTING MISSING DEALLOCATIONS #\n";+      std::cerr << "###################################\n";+      for (auto var : all) {+        if (AST_Node_Ptr ast = SASS_MEMORY_CAST_PTR(AST_Node, var)) {+          debug_ast(ast);+        } else {+          std::cerr << "LEAKED " << var << "\n";+        }+      }+    }+  }+  std::vector<SharedObj*> SharedObj::all;+  #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 ptr = SASS_MEMORY_CAST_PTR(AST_Node, node);+          if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";+        #endif+        if (!node->detached) {+          delete(node);+        }+      }+    }+  }++  void SharedPtr::incRefCount() {+    if (node) {+      ++ node->refcounter;+      node->detached = false;+      #ifdef DEBUG_SHARED_PTR+        if (node->dbg) {+          std::cerr << "+ " << node << " X " << node->refcounter << " (" << this << ") " << "\n";+        }+      #endif+    }+  }++  SharedPtr::~SharedPtr() {+    decRefCount();+  }+++  // the create constructor+  SharedPtr::SharedPtr(SharedObj* ptr)+  : node(ptr) {+    incRefCount();+  }+  // copy assignment operator+  SharedPtr& SharedPtr::operator=(const SharedPtr& rhs) {+    void* cur_ptr = (void*) node;+    void* rhs_ptr = (void*) rhs.node;+    if (cur_ptr == rhs_ptr) {+      return *this;+    }+    decRefCount();+    node = rhs.node;+    incRefCount();+    return *this;+  }++  // the copy constructor+  SharedPtr::SharedPtr(const SharedPtr& obj)+  : node(obj.node) {+    incRefCount();+  }++}
+ libsass/src/memory/SharedPtr.hpp view
@@ -0,0 +1,202 @@+#ifndef SASS_MEMORY_SHARED_PTR_H+#define SASS_MEMORY_SHARED_PTR_H++#include "sass/base.h"++#include <vector>++namespace Sass {++  class SharedPtr;++  ///////////////////////////////////////////////////////////////////////////////+  // Use macros for the allocation task, since overloading operator `new`+  // has been proven to be flaky under certain compilers (see comment below).+  ///////////////////////////////////////////////////////////////////////////////++  #ifdef DEBUG_SHARED_PTR++    #define SASS_MEMORY_NEW(Class, ...) \+      static_cast<Class##_Ptr>((new Class(__VA_ARGS__))->trace(__FILE__, __LINE__)) \++    #define SASS_MEMORY_COPY(obj) \+      ((obj)->copy(__FILE__, __LINE__)) \++    #define SASS_MEMORY_CLONE(obj) \+      ((obj)->clone(__FILE__, __LINE__)) \++  #else++    #define SASS_MEMORY_NEW(Class, ...) \+      new Class(__VA_ARGS__) \++    #define SASS_MEMORY_COPY(obj) \+      ((obj)->copy()) \++    #define SASS_MEMORY_CLONE(obj) \+      ((obj)->clone()) \++  #endif++  #define SASS_MEMORY_CAST(Class, obj) \+    (dynamic_cast<Class##_Ptr>(&obj)) \++  #define SASS_MEMORY_CAST_PTR(Class, ptr) \+    (dynamic_cast<Class##_Ptr>(ptr)) \++  class SharedObj {+  protected:+  friend class SharedPtr;+  friend class Memory_Manager;+    #ifdef DEBUG_SHARED_PTR+      static std::vector<SharedObj*> all;+      std::string file;+      size_t line;+    #endif+    static bool taint;+    long refcounter;+    // long refcount;+    bool detached;+    #ifdef DEBUG_SHARED_PTR+      bool dbg;+    #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;+      }+    #endif+    SharedObj();+    #ifdef DEBUG_SHARED_PTR+      std::string getDbgFile() {+        return file;+      }+      size_t getDbgLine() {+        return line;+      }+      void setDbg(bool dbg) {+        this->dbg = dbg;+      }+    #endif+    static void setTaint(bool val) {+      taint = val;+    }+    virtual ~SharedObj();+    long getRefCount() {+      return refcounter;+    }+  };+++  class SharedPtr {+  private:+    SharedObj* node;+  private:+    void decRefCount();+    void incRefCount();+  public:+    // the empty constructor+    SharedPtr()+    : node(NULL) {};+    // the create constructor+    SharedPtr(SharedObj* ptr);+    // copy assignment operator+    SharedPtr& operator=(const SharedPtr& rhs);+    // move assignment operator+    /* SharedPtr& operator=(SharedPtr&& rhs); */+    // the copy constructor+    SharedPtr(const SharedPtr& obj);+    // the move constructor+    /* SharedPtr(SharedPtr&& obj); */+    // destructor+    ~SharedPtr();+  public:+    SharedObj* obj () {+      return node;+    };+    SharedObj* obj () const {+      return node;+    };+    SharedObj* operator-> () {+      return node;+    };+    bool isNull () {+      return node == NULL;+    };+    bool isNull () const {+      return node == NULL;+    };+    SharedObj* detach() {+      node->detached = true;+      return node;+    };+    SharedObj* detach() const {+      if (node) {+        node->detached = true;+      }+      return node;+    };+    operator bool() {+      return node != NULL;+    };+    operator bool() const {+      return node != NULL;+    };++  };++  template < typename T >+  class SharedImpl : private SharedPtr {+  public:+    SharedImpl()+    : SharedPtr(NULL) {};+    SharedImpl(T* node)+    : SharedPtr(node) {};+    SharedImpl(T&& node)+    : SharedPtr(node) {};+    SharedImpl(const T& node)+    : SharedPtr(node) {};+    ~SharedImpl() {};+  public:+    T* operator& () {+      return static_cast<T*>(this->obj());+    };+    T* operator& () const {+      return static_cast<T*>(this->obj());+    };+    T& operator* () {+      return *static_cast<T*>(this->obj());+    };+    T& operator* () const {+      return *static_cast<T*>(this->obj());+    };+    T* operator-> () {+      return static_cast<T*>(this->obj());+    };+    T* operator-> () const {+      return static_cast<T*>(this->obj());+    };+    T* ptr () {+      return static_cast<T*>(this->obj());+    };+    T* detach() {+      if (this->obj() == NULL) return NULL;+      return static_cast<T*>(SharedPtr::detach());+    }+    bool isNull() {+      return this->obj() == NULL;+    }+    operator bool() {+      return this->obj() != NULL;+    };+    operator bool() const {+      return this->obj() != NULL;+    };+  };++}++#endif
− libsass/src/memory_manager.cpp
@@ -1,76 +0,0 @@-#include "ast.hpp"-#include "memory_manager.hpp"--namespace Sass {--  Memory_Manager::Memory_Manager(size_t size)-  : nodes(std::vector<Memory_Object*>())-  {-    size_t init = size;-    if (init < 8) init = 8;-    // reserve some space-    nodes.reserve(init);-  }--  Memory_Manager::~Memory_Manager()-  {-    // release memory for all controlled nodes-    // avoid calling erase for every single node-    for (size_t i = 0, S = nodes.size(); i < S; ++i) {-      deallocate(nodes[i]);-    }-    // just in case-    nodes.clear();-  }--  Memory_Object* Memory_Manager::add(Memory_Object* np)-  {-    // object has been initialized-    // it can be "deleted" from now on-    np->refcount = 1;-    return np;-  }--  bool Memory_Manager::has(Memory_Object* np)-  {-    // check if the pointer is controlled under our pool-    return find(nodes.begin(), nodes.end(), np) != nodes.end();-  }--  Memory_Object* Memory_Manager::allocate(size_t size)-  {-    // allocate requested memory-    void* heap = malloc(size);-    // init internal refcount status to zero-    (static_cast<Memory_Object*>(heap))->refcount = 0;-    // add the memory under our management-    nodes.push_back(static_cast<Memory_Object*>(heap));-    // cast object to its initial type-    return static_cast<Memory_Object*>(heap);-  }--  void Memory_Manager::deallocate(Memory_Object* np)-  {-    // only call destructor if initialized-    if (np->refcount) np->~Memory_Object();-    // always free the memory-    free(np);-  }--  void Memory_Manager::remove(Memory_Object* np)-  {-    // remove node from pool (no longer active)-    nodes.erase(find(nodes.begin(), nodes.end(), np));-    // you are now in control of the memory-  }--  void Memory_Manager::destroy(Memory_Object* np)-  {-    // remove from pool-    remove(np);-    // release memory-    deallocate(np);-  }--}-
− libsass/src/memory_manager.hpp
@@ -1,48 +0,0 @@-#ifndef SASS_MEMORY_MANAGER_H-#define SASS_MEMORY_MANAGER_H--#include <vector>--namespace Sass {--  class Memory_Object {-  friend class Memory_Manager;-    long refcount;-  public:-    Memory_Object() { refcount = 0; };-    virtual ~Memory_Object() {};-  };--  /////////////////////////////////////////////////////////////////////////////-  // A class for tracking allocations of AST_Node objects. The intended usage-  // is something like: Some_Node* n = new (mem_mgr) Some_Node(...);-  // Then, at the end of the program, the memory manager will delete all of the-  // allocated nodes that have been passed to it.-  // In the future, this class may implement a custom allocator.-  /////////////////////////////////////////////////////////////////////////////-  class Memory_Manager {-    std::vector<Memory_Object*> nodes;--  public:-    Memory_Manager(size_t size = 0);-    ~Memory_Manager();--    bool has(Memory_Object* np);-    Memory_Object* allocate(size_t size);-    void deallocate(Memory_Object* np);-    void remove(Memory_Object* np);-    void destroy(Memory_Object* np);-    Memory_Object* add(Memory_Object* np);--  };-}--///////////////////////////////////////////////////////////////////////////////-// Use macros for the allocation task, since overloading operator `new`-// has been proven to be flaky under certain compilers (see comment below).-///////////////////////////////////////////////////////////////////////////////--#define SASS_MEMORY_NEW(mgr, Class, ...)                                                 \-  (static_cast<Class*>(mgr.add(new (mgr.allocate(sizeof(Class))) Class(__VA_ARGS__))))   \--#endif
libsass/src/node.cpp view
@@ -1,7 +1,7 @@+#include "sass.hpp" #include <vector>  #include "node.hpp"-#include "to_string.hpp" #include "context.hpp" #include "parser.hpp" @@ -14,10 +14,10 @@   }  -  Node Node::createSelector(Complex_Selector* pSelector, Context& ctx) {+  Node Node::createSelector(Complex_Selector_Ptr pSelector, Context& ctx) {     NodeDequePtr null; -    Complex_Selector* pStripped = pSelector->clone(ctx);+    Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(pSelector);     pStripped->tail(NULL);     pStripped->combinator(Complex_Selector::ANCESTOR_OF); @@ -45,21 +45,21 @@   }  -  Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& 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::clone(Context& ctx) const {+  Node Node::klone(Context& ctx) 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.clone(ctx));+        pNewCollection->push_back(toClone.klone(ctx));       }     } -    Node n(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection);+    Node n(mType, mCombinator, mpSelector ? SASS_MEMORY_COPY(mpSelector) : NULL, pNewCollection);     n.got_line_feed = got_line_feed;     return n;   }@@ -101,7 +101,7 @@      } else if (lhs.isSelector()){ -      return selectors_equal(*lhs.selector(), *rhs.selector(), simpleSelectorOrderDependent);+      return selectors_equal(*&lhs.selector(), *&rhs.selector(), simpleSelectorOrderDependent);      } else if (lhs.isCollection()) { @@ -153,8 +153,7 @@      } else if (node.isSelector()){ -      To_String to_string;-      os << node.selector()->head()->perform(&to_string);+      os << node.selector()->head()->to_string();      } else if (node.isCollection()) { @@ -178,7 +177,7 @@ #endif  -  Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx) {+  Node complexSelectorToNode(Complex_Selector_Ptr pToConvert, Context& ctx) {     if (pToConvert == NULL) {       return Node::createNil();     }@@ -188,20 +187,20 @@      // unwrap the selector from parent ref     if (pToConvert->head() && pToConvert->head()->has_parent_ref()) {-      Complex_Selector* tail = pToConvert->tail();+      Complex_Selector_Obj tail = pToConvert->tail();       if (tail) tail->has_line_feed(pToConvert->has_line_feed());-      pToConvert = tail;+      pToConvert = &tail;     }      while (pToConvert) {        bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference(); -      if (pToConvert->head() == NULL || empty_parent_ref) {+      if (pToConvert->head() || empty_parent_ref) {       }        // the first Complex_Selector may contain a dummy head pointer, skip it.-      if (pToConvert->head() != NULL && !empty_parent_ref) {+      if (pToConvert->head() && !empty_parent_ref) {         node.collection()->push_back(Node::createSelector(pToConvert, ctx));         if (has_lf) node.collection()->back().got_line_feed = has_lf;         has_lf = false;@@ -217,21 +216,21 @@         // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());       } -      pToConvert = pToConvert->tail();+      pToConvert = &pToConvert->tail();     }      return node;   }  -  Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx) {+  Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert, Context& ctx) {     if (toConvert.isNil()) {       return NULL;     }       if (!toConvert.isCollection()) {-      throw "The node to convert to a Complex_Selector* must be a collection type or nil.";+      throw "The node to convert to a Complex_Selector_Ptr must be a collection type or nil.";     }  @@ -239,9 +238,9 @@      std::string noPath("");     Position noPosition(-1, -1, -1);-    Complex_Selector* pFirst = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL);+    Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL); -    Complex_Selector* pCurrent = pFirst;+    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);@@ -251,9 +250,11 @@       Node& child = *childIter;        if (child.isSelector()) {-        pCurrent->tail(child.selector()->clone(ctx));   // 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()+        // 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();+        pCurrent = &pCurrent->tail();       } else if (child.isCombinator()) {         pCurrent->combinator(child.combinator());         if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);@@ -262,9 +263,9 @@         if (childIter+1 != childIterEnd) {           Node& nextNode = *(childIter+1);           if (nextNode.isCombinator()) {-            pCurrent->tail(SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL));+            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();+            pCurrent = &pCurrent->tail();           }         }       } else {@@ -273,13 +274,13 @@     }      // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass-    Compound_Selector* fakeHead = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, ParserState("[NODE]"), 1);-    Parent_Selector* selectorRef = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, ParserState("[NODE]"));+    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 pFirst;+    return SASS_MEMORY_COPY(pFirst);   }    // A very naive trim function, which removes duplicates in a node@@ -287,7 +288,7 @@   Node Node::naiveTrim(Node& seqses, Context& ctx) {      std::vector<Node*> res;-    std::vector<Complex_Selector*> known;+    std::vector<Complex_Selector_Obj> known;      NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(),                                 seqsesIterEnd = seqses.collection()->rend();@@ -296,8 +297,8 @@     {       Node& seqs1 = *seqsesIter;       if( seqs1.isSelector() ) {-        Complex_Selector* sel = seqs1.selector();-        std::vector<Complex_Selector*>::iterator it;+        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; }
libsass/src/node.hpp view
@@ -52,16 +52,16 @@      Complex_Selector::Combinator combinator() const { return mCombinator; } -    Complex_Selector* selector() { return mpSelector; }-    const Complex_Selector* selector() const { return mpSelector; }+    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 clone the selector, stripping off the tail and combinator-    static Node createSelector(Complex_Selector* pSelector, Context& ctx);+    // This method will klone the selector, stripping off the tail and combinator+    static Node createSelector(Complex_Selector_Ptr pSelector, Context& ctx);      static Node createCollection();     static Node createCollection(const NodeDeque& values);@@ -69,7 +69,7 @@     static Node createNil();     static Node naiveTrim(Node& seqses, Context& ctx); -    Node clone(Context& ctx) const;+    Node klone(Context& ctx) const;      bool operator==(const Node& rhs) const;     inline bool operator!=(const Node& rhs) const { return !(*this == rhs); }@@ -97,21 +97,21 @@     // 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* pSelector, NodeDequePtr& pCollection);+    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* mpSelector; // this is an AST_Node, so it will be handled by the Memory_Manager+    Complex_Selector_Obj mpSelector;     NodeDequePtr mpCollection;   };  #ifdef DEBUG   std::ostream& operator<<(std::ostream& os, const Node& node); #endif-  Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx);-  Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx);+  Node complexSelectorToNode(Complex_Selector_Ptr pToConvert, Context& ctx);+  Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert, Context& ctx);    bool nodesEqual(const Node& one, const Node& two, bool simpleSelectorOrderDependent); 
libsass/src/operation.hpp view
@@ -8,78 +8,79 @@   template<typename T>   class Operation {   public:-    virtual T operator()(AST_Node* x)               = 0;+    virtual T operator()(AST_Node_Ptr x)               = 0;     virtual ~Operation()                            { }     // statements-    virtual T operator()(Block* x)                  = 0;-    virtual T operator()(Ruleset* x)                = 0;-    virtual T operator()(Propset* x)                = 0;-    virtual T operator()(Bubble* x)                 = 0;-    virtual T operator()(Supports_Block* x)          = 0;-    virtual T operator()(Media_Block* x)            = 0;-    virtual T operator()(At_Root_Block* x)          = 0;-    virtual T operator()(At_Rule* 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()(Extension* x)              = 0;-    virtual T operator()(Definition* x)             = 0;-    virtual T operator()(Mixin_Call* x)             = 0;+    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;     // expressions-    virtual T operator()(List* x)                   = 0;-    virtual T operator()(Map* 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()(Function_Call_Schema* 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()(Textual* x)                = 0;-    virtual T operator()(Number* x)                 = 0;-    virtual T operator()(Color* 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_Expression* x)     = 0;-    virtual T operator()(Null* x)                   = 0;-    virtual T operator()(Parent_Selector* x)        = 0;+    virtual T operator()(List_Ptr x)                   = 0;+    virtual T operator()(Map_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()(Textual_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;     // parameters and arguments-    virtual T operator()(Parameter* x)              = 0;-    virtual T operator()(Parameters* x)             = 0;-    virtual T operator()(Argument* x)               = 0;-    virtual T operator()(Arguments* x)              = 0;+    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;     // selectors-    virtual T operator()(Selector_Schema* x)        = 0;-    virtual T operator()(Selector_Placeholder* x)   = 0;-    virtual T operator()(Type_Selector* x)          = 0;-    virtual T operator()(Selector_Qualifier* x)     = 0;-    virtual T operator()(Attribute_Selector* x)     = 0;-    virtual T operator()(Pseudo_Selector* x)        = 0;-    virtual T operator()(Wrapped_Selector* x)       = 0;-    virtual T operator()(Compound_Selector* x)      = 0;-    virtual T operator()(Complex_Selector* x)       = 0;-    virtual T operator()(Selector_List* x)          = 0;+    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;      template <typename U>     T fallback(U x) { return T(); }@@ -88,84 +89,84 @@   template <typename T, typename D>   class Operation_CRTP : public Operation<T> {   public:-    virtual T operator()(AST_Node* x)               { return static_cast<D*>(this)->fallback(x); }-    virtual ~Operation_CRTP()                       = 0;+    D& impl() { return static_cast<D&>(*this); }+  public:+    T operator()(AST_Node_Ptr x)               { return static_cast<D*>(this)->fallback(x); }     // statements-    virtual T operator()(Block* x)                  { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Ruleset* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Propset* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Bubble* x)                 { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Supports_Block* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Media_Block* x)            { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(At_Root_Block* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(At_Rule* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Keyframe_Rule* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Declaration* x)            { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Assignment* x)             { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Import* x)                 { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Import_Stub* x)            { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Warning* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Error* x)                  { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Debug* x)                  { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Comment* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(If* x)                     { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(For* x)                    { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Each* x)                   { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(While* x)                  { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Return* x)                 { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Content* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Extension* x)              { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Definition* x)             { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Mixin_Call* x)             { return static_cast<D*>(this)->fallback(x); }+    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); }     // expressions-    virtual T operator()(List* x)                   { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Map* x)                    { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Binary_Expression* x)      { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Unary_Expression* x)       { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Function_Call* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Function_Call_Schema* x)   { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Custom_Warning* x)         { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Custom_Error* x)           { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Variable* x)               { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Textual* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Number* x)                 { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Color* x)                  { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Boolean* x)                { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(String_Schema* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(String_Constant* x)        { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(String_Quoted* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Supports_Condition* x)     { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Supports_Operator* x)      { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Supports_Negation* x)      { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Supports_Declaration* x)   { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Supports_Interpolation* x) { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Media_Query* x)            { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Media_Query_Expression* x) { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(At_Root_Expression* x)     { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Null* x)                   { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Parent_Selector* x)        { return static_cast<D*>(this)->fallback(x); }+    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()(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()(Textual_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); }     // parameters and arguments-    virtual T operator()(Parameter* x)              { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Parameters* x)             { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Argument* x)               { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Arguments* x)              { return static_cast<D*>(this)->fallback(x); }+    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); }     // selectors-    virtual T operator()(Selector_Schema* x)        { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Selector_Placeholder* x)   { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Type_Selector* x)          { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Selector_Qualifier* x)     { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Attribute_Selector* x)     { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Pseudo_Selector* x)        { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Wrapped_Selector* x)       { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Compound_Selector* x)      { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Complex_Selector* x)       { return static_cast<D*>(this)->fallback(x); }-    virtual T operator()(Selector_List* x)          { return static_cast<D*>(this)->fallback(x); }+    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); }      template <typename U>-    T fallback(U x)                                 { return T(); }+    T fallback(U x)                         { return T(); }   };-  template<typename T, typename D>-  inline Operation_CRTP<T, D>::~Operation_CRTP()    { }  } 
libsass/src/output.cpp view
@@ -1,56 +1,50 @@+#include "sass.hpp" #include "ast.hpp" #include "output.hpp"-#include "to_string.hpp"  namespace Sass { -  Output::Output(Context* ctx)-  : Inspect(Emitter(ctx)),+  Output::Output(Sass_Output_Options& opt)+  : Inspect(Emitter(opt)),     charset(""),     top_nodes(0)   {}    Output::~Output() { } -  void Output::fallback_impl(AST_Node* n)+  void Output::fallback_impl(AST_Node_Ptr n)   {     return n->perform(this);   } -  void Output::operator()(Number* n)+  void Output::operator()(Number_Ptr n)   {     // use values to_string facility-    To_String to_string(ctx);-    std::string res = n->perform(&to_string);+    std::string res = n->to_string(opt);     // check for a valid unit here     // includes result for reporting-    if (n->numerator_units().size() > 1 ||-        n->denominator_units().size() > 0 ||-        (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != std::string::npos) ||-        (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != std::string::npos)-    ) {-      error(res + " isn't a valid CSS value.", n->pstate());+    if (!n->is_valid_css_unit()) {+      throw Exception::InvalidValue(*n);     }     // output the final token     append_token(res, n);   } -  void Output::operator()(Import* imp)+  void Output::operator()(Import_Ptr imp)   {     top_nodes.push_back(imp);   } -  void Output::operator()(Map* m)+  void Output::operator()(Map_Ptr m)   {-    To_String to_string(ctx);-    std::string dbg(m->perform(&to_string));+    std::string dbg(m->to_string(opt));     error(dbg + " isn't a valid CSS value.", m->pstate());   }    OutputBuffer Output::get_buffer(void)   { -    Emitter emitter(ctx);+    Emitter emitter(opt);     Inspect inspect(emitter);      size_t size_nodes = top_nodes.size();@@ -60,23 +54,25 @@     }      // flush scheduled outputs-    inspect.finalize();+    // maybe omit semicolon if possible+    inspect.finalize(wbuf.buffer.size() == 0);     // prepend buffer on top     prepend_output(inspect.output());     // make sure we end with a linefeed-    if (!ends_with(wbuf.buffer, ctx->linefeed)) {+    if (!ends_with(wbuf.buffer, opt.linefeed)) {       // if the output is not completely empty-      if (!wbuf.buffer.empty()) append_string(ctx->linefeed);+      if (!wbuf.buffer.empty()) append_string(opt.linefeed);     }      // search for unicode char     for(const char& chr : wbuf.buffer) {       // skip all ascii chars-      if (chr >= 0) continue;+      // static cast to unsigned to handle `char` being signed / unsigned+      if (static_cast<unsigned>(chr) < 128) continue;       // declare the charset-      if (output_style() != SASS_STYLE_COMPRESSED)+      if (output_style() != COMPRESSED)         charset = "@charset \"UTF-8\";"-                  + ctx->linefeed;+                + std::string(opt.linefeed);       else charset = "\xEF\xBB\xBF";       // abort search       break;@@ -89,13 +85,12 @@    } -  void Output::operator()(Comment* c)+  void Output::operator()(Comment_Ptr c)   {-    To_String to_string(ctx);-    std::string txt = c->text()->perform(&to_string);+    std::string txt = c->text()->to_string(opt);     // if (indentation && txt == "/**/") return;     bool important = c->is_important();-    if (output_style() != SASS_STYLE_COMPRESSED || important) {+    if (output_style() != COMPRESSED || important) {       if (buffer().size() == 0) {         top_nodes.push_back(c);       } else {@@ -112,87 +107,72 @@     }   } -  void Output::operator()(Ruleset* r)+  void Output::operator()(Ruleset_Ptr r)   {-    Selector* s     = r->selector();-    Block*    b     = r->block();-    bool      decls = false;+    Selector_Obj s     = r->selector();+    Block_Obj    b     = r->block();      // 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) {-        Statement* stm = (*b)[i];-        if (dynamic_cast<Has_Block*>(stm)) {-          stm->perform(this);+        const Statement_Obj& stm = b->at(i);+        if (dynamic_cast<Has_Block_Ptr>(&stm)) {+          if (!dynamic_cast<Declaration_Ptr>(&stm)) {+            stm->perform(this);+          }         }       }       return;     } -    if (b->has_non_hoistable()) {-      decls = true;-      if (output_style() == SASS_STYLE_NESTED) indentation += r->tabs();-      if (ctx && ctx->c_options->source_comments) {-        std::stringstream ss;-        append_indentation();-        ss << "/* line " << r->pstate().line + 1 << ", " << r->pstate().path << " */";-        append_string(ss.str());-        append_optional_linefeed();-      }-      s->perform(this);-      append_scope_opener(b);-      for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        bool bPrintExpression = true;-        // Check print conditions-        if (typeid(*stm) == typeid(Declaration)) {-          Declaration* dec = static_cast<Declaration*>(stm);-          if (dec->value()->concrete_type() == Expression::STRING) {-            String_Constant* valConst = static_cast<String_Constant*>(dec->value());-            std::string val(valConst->value());-            if (auto qstr = dynamic_cast<String_Quoted*>(valConst)) {-              if (!qstr->quote_mark() && val.empty()) {-                bPrintExpression = false;-              }+    if (output_style() == NESTED) indentation += r->tabs();+    if (opt.source_comments) {+      std::stringstream ss;+      append_indentation();+      std::string path(File::abs2rel(r->pstate().path));+      ss << "/* line " << r->pstate().line + 1 << ", " << path << " */";+      append_string(ss.str());+      append_optional_linefeed();+    }+    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);+      bool bPrintExpression = true;+      // Check print conditions+      if (Declaration_Ptr dec = SASS_MEMORY_CAST(Declaration, stm)) {+        if (String_Constant_Ptr valConst = SASS_MEMORY_CAST(String_Constant, dec->value())) {+          std::string val(valConst->value());+          if (String_Quoted_Ptr qstr = SASS_MEMORY_CAST_PTR(String_Quoted, valConst)) {+            if (!qstr->quote_mark() && val.empty()) {+              bPrintExpression = false;             }           }-          else if (dec->value()->concrete_type() == Expression::LIST) {-            List* list = static_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* item = (*list)[list_i];-              if (!item->is_invisible()) all_invisible = false;-            }-            if (all_invisible) bPrintExpression = false;-          }         }-        // Print if OK-        if (!stm->is_hoistable() && bPrintExpression) {-          stm->perform(this);+        else if (List_Ptr list = SASS_MEMORY_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);+            if (!item->is_invisible()) all_invisible = false;+          }+          if (all_invisible) bPrintExpression = false;         }       }-      if (output_style() == SASS_STYLE_NESTED) indentation -= r->tabs();-      append_scope_closer(b);-    }--    if (b->has_hoistable()) {-      if (decls) ++indentation;-      for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (stm->is_hoistable()) {-          stm->perform(this);-        }+      // Print if OK+      if (bPrintExpression) {+        stm->perform(this);       }-      if (decls) --indentation;     }-  }+    if (output_style() == NESTED) indentation -= r->tabs();+    append_scope_closer(&b); -  void Output::operator()(Keyframe_Rule* r)+  }+  void Output::operator()(Keyframe_Rule_Ptr r)   {-    Block* b = r->block();-    Selector* v = r->selector();+    Block_Obj b = r->block();+    Selector_Obj v = r->name(); -    if (v) {+    if (&v) {       v->perform(this);     } @@ -203,124 +183,93 @@      append_scope_opener();     for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* stm = (*b)[i];-      if (!stm->is_hoistable()) {-        stm->perform(this);-        if (i < L - 1) append_special_linefeed();-      }-    }--    for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* stm = (*b)[i];-      if (stm->is_hoistable()) {-        stm->perform(this);-      }+      Statement_Obj stm = b->at(i);+      stm->perform(this);+      if (i < L - 1) append_special_linefeed();     }-     append_scope_closer();   } -  void Output::operator()(Supports_Block* f)+  void Output::operator()(Supports_Block_Ptr f)   {     if (f->is_invisible()) return; -    Supports_Condition* c = f->condition();-    Block* b              = f->block();+    Supports_Condition_Obj c = f->condition();+    Block_Obj b              = f->block();      // 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* stm = (*b)[i];-        if (dynamic_cast<Has_Block*>(stm)) {+        Statement_Obj stm = b->at(i);+        if (dynamic_cast<Has_Block_Ptr>(&stm)) {           stm->perform(this);         }       }       return;     } -    if (output_style() == SASS_STYLE_NESTED) indentation += f->tabs();+    if (output_style() == NESTED) indentation += f->tabs();     append_indentation();     append_token("@supports", f);     append_mandatory_space();     c->perform(this);     append_scope_opener(); -    if (b->has_non_hoistable()) {-      // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable-      append_scope_opener();--      for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (!stm->is_hoistable()) {-          stm->perform(this);-        }-      }--      append_scope_closer();--      for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (stm->is_hoistable()) {-          stm->perform(this);-        }-      }-    }-    else {-      // JMA - not hoisted, just output in order-      for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        stm->perform(this);-        if (i < L - 1) append_special_linefeed();-      }+    for (size_t i = 0, L = b->length(); i < L; ++i) {+      Statement_Obj stm = b->at(i);+      stm->perform(this);+      if (i < L - 1) append_special_linefeed();     } -    if (output_style() == SASS_STYLE_NESTED) indentation -= f->tabs();+    if (output_style() == NESTED) indentation -= f->tabs();      append_scope_closer();    } -  void Output::operator()(Media_Block* m)+  void Output::operator()(Media_Block_Ptr m)   {     if (m->is_invisible()) return; -    List*  q     = m->media_queries();-    Block* b     = m->block();+    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* stm = (*b)[i];-        if (dynamic_cast<Has_Block*>(stm)) {+        Statement_Obj stm = b->at(i);+        if (dynamic_cast<Has_Block_Ptr>(&stm)) {           stm->perform(this);         }       }       return;     }-    if (output_style() == SASS_STYLE_NESTED) indentation += m->tabs();+    if (output_style() == NESTED) indentation += m->tabs();     append_indentation();     append_token("@media", m);     append_mandatory_space();     in_media_block = true;-    q->perform(this);+    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)[i]) (*b)[i]->perform(this);+      if (b->at(i)) {+      Statement_Obj stm = b->at(i);+        stm->perform(this);+      }       if (i < L - 1) append_special_linefeed();     } -    if (output_style() == SASS_STYLE_NESTED) indentation -= m->tabs();+    if (output_style() == NESTED) indentation -= m->tabs();     append_scope_closer();   } -  void Output::operator()(At_Rule* a)+  void Output::operator()(Directive_Ptr a)   {     std::string      kwd   = a->keyword();-    Selector*   s     = a->selector();-    Expression* v     = a->value();-    Block*      b     = a->block();+    Selector_Obj   s     = a->selector();+    Expression_Obj v     = a->value();+    Block_Obj      b     = a->block();      append_indentation();     append_token(kwd, a);@@ -332,7 +281,8 @@     }     if (v) {       append_mandatory_space();-      v->perform(this);+      // ruby sass bug? should use options?+      append_token(v->to_string(/* opt */), &v);     }     if (!b) {       append_delimiter();@@ -340,7 +290,8 @@     }      if (b->is_invisible() || b->length() == 0) {-      return append_string(" {}");+      append_optional_space();+      return append_string("{}");     }      append_scope_opener();@@ -348,25 +299,15 @@     bool format = kwd != "@font-face";;      for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* stm = (*b)[i];-      if (!stm->is_hoistable()) {-        stm->perform(this);-        if (i < L - 1 && format) append_special_linefeed();-      }-    }--    for (size_t i = 0, L = b->length(); i < L; ++i) {-      Statement* stm = (*b)[i];-      if (stm->is_hoistable()) {-        stm->perform(this);-        if (i < L - 1 && format) append_special_linefeed();-      }+      Statement_Obj stm = b->at(i);+      stm->perform(this);+      if (i < L - 1 && format) append_special_linefeed();     }      append_scope_closer();   } -  void Output::operator()(String_Quoted* s)+  void Output::operator()(String_Quoted_Ptr s)   {     if (s->quote_mark()) {       append_token(quote(s->value(), s->quote_mark()), s);@@ -377,10 +318,10 @@     }   } -  void Output::operator()(String_Constant* s)+  void Output::operator()(String_Constant_Ptr s)   {     std::string value(s->value());-    if (s->can_compress_whitespace() && output_style() == SASS_STYLE_COMPRESSED) {+    if (s->can_compress_whitespace() && output_style() == COMPRESSED) {       value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());     }     if (!in_comment) {
libsass/src/output.hpp view
@@ -23,31 +23,29 @@     using Inspect::operator();    public:-    // change to Emitter-    Output(Context* ctx);+    Output(Sass_Output_Options& opt);     virtual ~Output();    protected:     std::string charset;-    std::vector<AST_Node*> top_nodes;+    std::vector<AST_Node_Ptr> top_nodes;    public:     OutputBuffer get_buffer(void); -    virtual void operator()(Map*);-    virtual void operator()(Ruleset*);-    // virtual void operator()(Propset*);-    virtual void operator()(Supports_Block*);-    virtual void operator()(Media_Block*);-    virtual void operator()(At_Rule*);-    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*);+    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); -    void fallback_impl(AST_Node* n);+    void fallback_impl(AST_Node_Ptr n);    }; 
libsass/src/parser.cpp view
@@ -1,2434 +1,2774 @@-#include <cstdlib>-#include <iostream>-#include <vector>-#include "parser.hpp"-#include "file.hpp"-#include "inspect.hpp"-#include "to_string.hpp"-#include "constants.hpp"-#include "util.hpp"-#include "prelexer.hpp"-#include "color_maps.hpp"-#include "sass/functions.h"-#include "error_handling.hpp"--#include <typeinfo>-#include <tuple>--namespace Sass {-  using namespace Constants;-  using namespace Prelexer;--  Parser Parser::from_c_str(const char* str, Context& ctx, ParserState pstate)-  {-    Parser p(ctx, pstate);-    p.source   = str;-    p.position = p.source;-    p.end      = str + strlen(str);-    Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);-    p.block_stack.push_back(root);-    root->is_root(true);-    return p;-  }--  Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate)-  {-    Parser p(ctx, pstate);-    p.source   = beg;-    p.position = p.source;-    p.end      = end;-    Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);-    p.block_stack.push_back(root);-    root->is_root(true);-    return p;-  }--  Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)-  {-    Parser p = Parser::from_c_str(src, ctx, pstate);-    // ToDo: ruby sass errors on parent references-    // ToDo: remap the source-map entries somehow-    return p.parse_selector_list(false);-  }--  bool Parser::peek_newline(const char* start)-  {-    return peek_linefeed(start ? start : position)-           && ! peek_css<exactly<'{'>>(start);-  }--  Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)-  {-    Parser p(ctx, pstate);-    p.source   = t.begin;-    p.position = p.source;-    p.end      = t.end;-    Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);-    p.block_stack.push_back(root);-    root->is_root(true);-    return p;-  }--  /* main entry point to parse root block */-  Block* Parser::parse()-  {-    bool is_root = false;-    Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true);-    read_bom();--    // custom headers-    if (ctx.resources.size() == 1) {-    is_root = true;-      ctx.apply_custom_headers(root, path, pstate);-    }--    block_stack.push_back(root);-    /* bool rv = */ parse_block_nodes(is_root);-    block_stack.pop_back();--    // update for end position-    root->update_pstate(pstate);--    if (position != end) {-      css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");-    }--    return root;-  }---  // convenience function for block parsing-  // will create a new block ad-hoc for you-  // this is the base block parsing function-  Block* Parser::parse_css_block(bool is_root)-  {--    // parse comments before block-    // lex < optional_css_comments >();--    // lex mandatory opener or error out-    if (!lex_css < exactly<'{'> >()) {-      css_error("Invalid CSS", " after ", ": expected \"{\", was ");-    }-    // create new block and push to the selector stack-    Block* block = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, is_root);-    block_stack.push_back(block);--    if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");;--    if (!lex_css < exactly<'}'> >()) {-      css_error("Invalid CSS", " after ", ": expected \"}\", was ");-    }--    // update for end position-    block->update_pstate(pstate);--    // parse comments after block-    // lex < optional_css_comments >();--    block_stack.pop_back();--    return block;-  }--  // convenience function for block parsing-  // will create a new block ad-hoc for you-  // also updates the `in_at_root` flag-  Block* Parser::parse_block(bool is_root)-  {-    LOCAL_FLAG(in_at_root, is_root);-    return parse_css_block(is_root);-  }--  // the main block parsing function-  // parses stuff between `{` and `}`-  bool Parser::parse_block_nodes(bool is_root)-  {--    // loop until end of string-    while (position < end) {--      // we should be able to refactor this-      parse_block_comments();-      lex < css_whitespace >();--      if (lex < exactly<';'> >()) continue;-      if (peek < end_of_file >()) return true;-      if (peek < exactly<'}'> >()) return true;--      if (parse_block_node(is_root)) continue;--      parse_block_comments();--      if (lex_css < exactly<';'> >()) continue;-      if (peek_css < end_of_file >()) return true;-      if (peek_css < exactly<'}'> >()) return true;--      // illegal sass-      return false;-    }-    // return success-    return true;-  }--  // parser for a single node in a block-  // semicolons must be lexed beforehand-  bool Parser::parse_block_node(bool is_root) {--    Block* block = block_stack.back();--    while (lex< block_comment >()) {-      bool is_important = lexed.begin[2] == '!';-      String*  contents = parse_interpolated_chunk(lexed);-      (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);-    }--    // throw away white-space-    // includes line comments-    lex < css_whitespace >();--    Lookahead lookahead_result;--    // also parse block comments--    // first parse everything that is allowed in functions-    if (lex < variable >(true)) { (*block) << parse_assignment(); }-    else if (lex < kwd_err >(true)) { (*block) << parse_error(); }-    else if (lex < kwd_dbg >(true)) { (*block) << parse_debug(); }-    else if (lex < kwd_warn >(true)) { (*block) << parse_warning(); }-    else if (lex < kwd_if_directive >(true)) { (*block) << parse_if_directive(); }-    else if (lex < kwd_for_directive >(true)) { (*block) << parse_for_directive(); }-    else if (lex < kwd_each_directive >(true)) { (*block) << parse_each_directive(); }-    else if (lex < kwd_while_directive >(true)) { (*block) << parse_while_directive(); }-    else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); }--    // abort if we are in function context and have nothing parsed yet-    else if (stack.back() == function_def) {-      error("Functions can only contain variable declarations and control directives", pstate);-    }--    // parse imports to process later-    else if (lex < kwd_import >(true)) {-      if (stack.back() == mixin_def || stack.back() == function_def) {-        error("Import directives may not be used within control directives or mixins.", pstate);-      }-      Import* imp = parse_import();-      // if it is a url, we only add the statement-      if (!imp->urls().empty()) (*block) << imp;-      // process all resources now (add Import_Stub nodes)-      for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {-        (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->incs()[i]);-      }-    }--    else if (lex < kwd_extend >(true)) {-      if (block->is_root()) {-        error("Extend directives may only be used within rules.", pstate);-      }--      Lookahead lookahead = lookahead_for_include(position);-      if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");-      Selector* target;-      if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);-      else                            target = parse_selector_list(true);-      (*block) << SASS_MEMORY_NEW(ctx.mem, Extension, pstate, target);-    }--    // selector may contain interpolations which need delayed evaluation-    else if (!(lookahead_result = lookahead_for_selector(position)).error)-    { (*block) << parse_ruleset(lookahead_result, is_root); }--    // parse multiple specific keyword directives-    else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); }-    else if (lex < kwd_at_root >(true)) { (*block) << parse_at_root_block(); }-    else if (lex < kwd_include_directive >(true)) { (*block) << parse_include_directive(); }-    else if (lex < kwd_content_directive >(true)) { (*block) << parse_content_directive(); }-    else if (lex < kwd_supports_directive >(true)) { (*block) << parse_supports_directive(); }-    else if (lex < kwd_mixin >(true)) { (*block) << parse_definition(Definition::MIXIN); }-    else if (lex < kwd_function >(true)) { (*block) << parse_definition(Definition::FUNCTION); }--    // ignore the @charset directive for now-    else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); }--    // generic at keyword (keep last)-    else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); }--    else if (block->is_root()) {-      lex< css_whitespace >();-      if (position >= end) return true;-      css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was ");-    }-    // parse a declaration-    else-    {-      // ToDo: how does it handle parse errors?-      // maybe we are expected to parse something?-      Declaration* decl = parse_declaration();-      decl->tabs(indentation);-      (*block) << decl;-      // maybe we have a "sub-block"-      if (peek< exactly<'{'> >()) {-        if (decl->is_indented()) ++ indentation;-        // parse a propset that rides on the declaration's property-        (*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block());-        if (decl->is_indented()) -- indentation;-      }-    }-    // something matched-    return true;-  }-  // EO parse_block_nodes--  // parse imports inside the-  Import* Parser::parse_import()-  {-    Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate);-    std::vector<std::pair<std::string,Function_Call*>> to_import;-    bool first = true;-    do {-      while (lex< block_comment >());-      if (lex< quoted_string >()) {-        if (!ctx.call_importers(unquote(std::string(lexed)), path, pstate, imp))-        {-          // push single file import-          // import_single_file(imp, lexed);-          to_import.push_back(std::pair<std::string,Function_Call*>(std::string(lexed), 0));-        }-      }-      else if (lex< uri_prefix >()) {-        Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);-        Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args);-        if (lex< quoted_string >()) {-          Expression* the_url = parse_string();-          *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);-        }-        else if (lex < uri_value >(false)) { // don't skip comments-          String* the_url = parse_interpolated_chunk(lexed);-          *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);-        }-        else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {-          Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed);-          *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);-        }-        else {-          error("malformed URL", pstate);-        }-        if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);-        to_import.push_back(std::pair<std::string, Function_Call*>("", result));-      }-      else {-        if (first) error("@import directive requires a url or quoted path", pstate);-        else error("expecting another url or quoted path in @import list", pstate);-      }-      first = false;-    } while (lex_css< exactly<','> >());--    if (!peek_css<alternatives<exactly<';'>,end_of_file>>()) {-      List* media_queries = parse_media_queries();-      imp->media_queries(media_queries);-    }--    for(auto location : to_import) {-      if (location.second) {-        imp->urls().push_back(location.second);-      } else {-        ctx.import_url(imp, location.first, path);-      }-    }--    return imp;-  }--  Definition* Parser::parse_definition(Definition::Type which_type)-  {-    std::string which_str(lexed);-    if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);-    std::string name(Util::normalize_underscores(lexed));-    if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))-    { error("Invalid function name \"" + name + "\".", pstate); }-    ParserState source_position_of_def = pstate;-    Parameters* params = parse_parameters();-    if (which_type == Definition::MIXIN) stack.push_back(mixin_def);-    else stack.push_back(function_def);-    Block* body = parse_block();-    stack.pop_back();-    Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type);-    return def;-  }--  Parameters* Parser::parse_parameters()-  {-    std::string name(lexed);-    Position position = after_token;-    Parameters* params = SASS_MEMORY_NEW(ctx.mem, Parameters, pstate);-    if (lex_css< exactly<'('> >()) {-      // if there's anything there at all-      if (!peek_css< exactly<')'> >()) {-        do (*params) << parse_parameter();-        while (lex_css< exactly<','> >());-      }-      if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);-    }-    return params;-  }--  Parameter* Parser::parse_parameter()-  {-    while (lex< alternatives < spaces, block_comment > >());-    lex < variable >();-    std::string name(Util::normalize_underscores(lexed));-    ParserState pos = pstate;-    Expression* val = 0;-    bool is_rest = false;-    while (lex< alternatives < spaces, block_comment > >());-    if (lex< exactly<':'> >()) { // there's a default value-      while (lex< block_comment >());-      val = parse_space_list();-      val->is_delayed(false);-    }-    else if (lex< exactly< ellipsis > >()) {-      is_rest = true;-    }-    Parameter* p = SASS_MEMORY_NEW(ctx.mem, Parameter, pos, name, val, is_rest);-    return p;-  }--  Arguments* Parser::parse_arguments()-  {-    std::string name(lexed);-    Position position = after_token;-    Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);-    if (lex_css< exactly<'('> >()) {-      // if there's anything there at all-      if (!peek_css< exactly<')'> >()) {-        do (*args) << parse_argument();-        while (lex_css< exactly<','> >());-      }-      if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);-    }-    return args;-  }--  Argument* Parser::parse_argument()-  {-    if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) {-      position += 2;-      css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-    }--    Argument* arg;-    if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {-      lex_css< variable >();-      std::string name(Util::normalize_underscores(lexed));-      ParserState p = pstate;-      lex_css< exactly<':'> >();-      Expression* val = parse_space_list();-      val->is_delayed(false);-      arg = SASS_MEMORY_NEW(ctx.mem, Argument, p, val, name);-    }-    else {-      bool is_arglist = false;-      bool is_keyword = false;-      Expression* val = parse_space_list();-      val->is_delayed(false);-      if (lex_css< exactly< ellipsis > >()) {-        if (val->concrete_type() == Expression::MAP) is_keyword = true;-        else is_arglist = true;-      }-      arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword);-    }-    return arg;-  }--  Assignment* Parser::parse_assignment()-  {-    std::string name(Util::normalize_underscores(lexed));-    ParserState var_source_position = pstate;-    if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);-    Expression* val;-    Lookahead lookahead = lookahead_for_value(position);-    if (lookahead.has_interpolants && lookahead.found) {-      val = parse_value_schema(lookahead.found);-    } else {-      val = parse_list();-    }-    val->is_delayed(false);-    bool is_default = false;-    bool is_global = false;-    while (peek< alternatives < default_flag, global_flag > >()) {-      if (lex< default_flag >()) is_default = true;-      else if (lex< global_flag >()) is_global = true;-    }-    Assignment* var = SASS_MEMORY_NEW(ctx.mem, Assignment, var_source_position, name, val, is_default, is_global);-    return var;-  }--  // a ruleset connects a selector and a block-  Ruleset* Parser::parse_ruleset(Lookahead lookahead, bool is_root)-  {-    // make sure to move up the the last position-    lex < optional_css_whitespace >(false, true);-    // create the connector object (add parts later)-    Ruleset* ruleset = SASS_MEMORY_NEW(ctx.mem, Ruleset, pstate);-    // parse selector static or as schema to be evaluated later-    if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root));-    else ruleset->selector(parse_selector_schema(lookahead.found));-    // then parse the inner block-    ruleset->block(parse_block());-    // update for end position-    ruleset->update_pstate(pstate);-    // inherit is_root from parent block-    // need this info for sanity checks-    ruleset->is_root(is_root);-    // return AST Node-    return ruleset;-  }--  // parse a selector schema that will be evaluated in the eval stage-  // uses a string schema internally to do the actual schema handling-  // in the eval stage we will be re-parse it into an actual selector-  Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)-  {-    // move up to the start-    lex< optional_spaces >();-    const char* i = position;-    // selector schema re-uses string schema implementation-    String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);-    // the selector schema is pretty much just a wrapper for the string schema-    Selector_Schema* selector_schema = SASS_MEMORY_NEW(ctx.mem, Selector_Schema, pstate, schema);-    selector_schema->media_block(last_media_block);--    // process until end-    while (i < end_of_selector) {-      // try to parse mutliple interpolants-      if (const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector)) {-        // accumulate the preceding segment if the position has advanced-        if (i < p) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));-        // check if the interpolation only contains white-space (error out)-        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;-          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-        }-        // skip over all nested inner interpolations up to our own delimiter-        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);-        // pass inner expression to the parser to resolve nested interpolations-        Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();-        // set status on the list expression-        interpolant->is_interpolant(true);-        // add to the string schema-        (*schema) << interpolant;-        // advance position-        i = j;-      }-      // no more interpolants have been found-      // add the last segment if there is one-      else {-        // make sure to add the last bits of the string up to the end (if any)-        if (i < end_of_selector) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, end_of_selector));-        // exit loop-        i = end_of_selector;-      }-    }-    // EO until eos--    // update position-    position = i;--    // update for end position-    selector_schema->update_pstate(pstate);--    // return parsed result-    return selector_schema;-  }-  // EO parse_selector_schema--  void Parser::parse_charset_directive()-  {-    lex <-      sequence <-        quoted_string,-        optional_spaces,-        exactly <';'>-      >-    >();-  }--  // called after parsing `kwd_include_directive`-  Mixin_Call* Parser::parse_include_directive()-  {-    // lex identifier into `lexed` var-    lex_identifier(); // may error out-    // normalize underscores to hyphens-    std::string name(Util::normalize_underscores(lexed));-    // create the initial mixin call object-    Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call, pstate, name, 0, 0);-    // parse mandatory arguments-    call->arguments(parse_arguments());-    // parse optional block-    if (peek < exactly <'{'> >()) {-      call->block(parse_block());-    }-    // return ast node-    return call;-  }-  // EO parse_include_directive--  // parse a list of complex selectors-  // this is the main entry point for most-  Selector_List* Parser::parse_selector_list(bool in_root)-  {-    bool reloop = true;-    bool had_linefeed = false;-    Complex_Selector* sel = 0;-    To_String to_string(&ctx);-    Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate);-    group->media_block(last_media_block);--    do {-      reloop = false;--      had_linefeed = had_linefeed || peek_newline();--      if (peek_css< class_char < selector_list_delims > >())-        break; // in case there are superfluous commas at the end---      // now parse the complex selector-      sel = parse_complex_selector(in_root);--      if (!sel) return group;--      sel->has_line_feed(had_linefeed);--      had_linefeed = false;--      while (peek_css< exactly<','> >())-      {-        lex< css_comments >(false);-        // consume everything up and including the comma speparator-        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) << 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;-  }-  // 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* Parser::parse_complex_selector(bool in_root)-  {--    String* reference = 0;-    lex < block_comment >();-    // parse the left hand side-    Compound_Selector* lhs = 0;-    // special case if it starts with combinator ([+~>])-    if (!peek_css< class_char < selector_combinator_ops > >()) {-      // parse the left hand side-      lhs = parse_compound_selector();-    }--    // check for end of file condition-    if (peek < end_of_file >()) return 0;--    // parse combinator between lhs and rhs-    Complex_Selector::Combinator combinator;-    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(ctx.mem, String_Constant, pstate, lexed);-      if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?-    }-    else /* if (lex< zero >()) */   combinator = Complex_Selector::ANCESTOR_OF;--    if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;--    // lex < block_comment >();-    // source position of a complex selector points to the combinator-    // ToDo: make sure we update pstate for ancestor of (lex < zero >());-    Complex_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, combinator, lhs);-    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));-      if (sel->tail()) {-        // ToDo: move this logic below into tail setter-        if (sel->tail()->has_reference()) sel->has_reference(true);-        if (sel->tail()->has_placeholder()) sel->has_placeholder(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_reference() && !in_at_root && !in_root) {-      // create the objects to wrap parent selector reference-      Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);-      parent->media_block(last_media_block);-      Compound_Selector* head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);-      head->media_block(last_media_block);-      // add simple selector-      (*head) << 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(ctx.mem, 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);-    }--    // complex selector-    return sel;-  }-  // EO parse_complex_selector--  // parse one compound selector, which is basically-  // a list of simple selectors (directly adjancent)-  // lex them exactly (without skipping white-space)-  Compound_Selector* Parser::parse_compound_selector()-  {-    // init an empty compound selector wrapper-    Compound_Selector* seq = SASS_MEMORY_NEW(ctx.mem, 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 (peek < re_pseudo_selector >())-      {-        (*seq) << parse_simple_selector();-      }-      // parse parent selector-      else if (lex< exactly<'&'> >(false))-      {-        // this produces a linefeed!?-        seq->has_parent_reference(true);-        (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);-      }-      // parse type selector-      else if (lex< re_type_selector >(false))-      {-        (*seq) << SASS_MEMORY_NEW(ctx.mem, 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 {-        Simple_Selector* sel = parse_simple_selector();-        if (!sel) return 0;-        (*seq) << sel;-      }-    }--    if (seq && !peek_css<exactly<'{'>>()) {-      seq->has_line_break(peek_newline());-    }--    // EO while true-    return seq;--  }-  // EO parse_compound_selector--  Simple_Selector* Parser::parse_simple_selector()-  {-    lex < css_comments >(false);-    if (lex< alternatives < id_name, class_name > >()) {-      return SASS_MEMORY_NEW(ctx.mem, Selector_Qualifier, pstate, lexed);-    }-    else if (lex< quoted_string >()) {-      return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, unquote(lexed));-    }-    else if (lex< alternatives < variable, number, static_reference_combinator > >()) {-      return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);-    }-    else if (peek< pseudo_not >()) {-      return parse_negated_selector();-    }-    else if (peek< re_pseudo_selector >()) {-      return parse_pseudo_selector();-    }-    else if (peek< exactly<':'> >()) {-      return parse_pseudo_selector();-    }-    else if (lex < exactly<'['> >()) {-      return parse_attribute_selector();-    }-    else if (lex< placeholder >()) {-      Selector_Placeholder* sel = SASS_MEMORY_NEW(ctx.mem, Selector_Placeholder, pstate, lexed);-      sel->media_block(last_media_block);-      return sel;-    }-    // failed-    return 0;-  }--  Wrapped_Selector* Parser::parse_negated_selector()-  {-    lex< pseudo_not >();-    std::string name(lexed);-    ParserState nsource_position = pstate;-    Selector* negated = parse_selector_list(true);-    if (!lex< exactly<')'> >()) {-      error("negated selector is missing ')'", pstate);-    }-    name.erase(name.size() - 1);-    return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, nsource_position, name, negated);-  }--  // a pseudo selector often starts with one or two colons-  // it can contain more selectors inside parantheses-  Simple_Selector* 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<'('> >-        > >())-    {--      std::string name(lexed);-      name.erase(name.size() - 1);-      ParserState p = pstate;--      // 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* expr = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-        if (expr && lex_css< exactly<')'> >()) {-          expr->can_compress_whitespace(true);-          return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, p, name, expr);-        }-      }-      else if (Selector* wrapped = parse_selector_list(true)) {-        if (wrapped && lex_css< exactly<')'> >()) {-          return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, p, name, wrapped);-        }-      }--    }-    // EO if pseudo selector--    else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {-      return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, pstate, lexed);-    }-    else if(lex < pseudo_prefix >()) {-      css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");-    }--    css_error("Invalid CSS", " after ", ": expected \")\", was ");--    // unreachable statement-    return 0;-  }--  Attribute_Selector* Parser::parse_attribute_selector()-  {-    ParserState p = pstate;-    if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);-    std::string name(lexed);-    if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, "", 0);-    if (!lex_css< alternatives< exact_match, class_match, dash_match,-                                prefix_match, suffix_match, substring_match > >()) {-      error("invalid operator in attribute selector for " + name, pstate);-    }-    std::string matcher(lexed);--    String* value = 0;-    if (lex_css< identifier >()) {-      value = SASS_MEMORY_NEW(ctx.mem, String_Constant, p, lexed);-    }-    else if (lex_css< quoted_string >()) {-      value = parse_interpolated_chunk(lexed, true); // needed!-    }-    else {-      error("expected a string constant or identifier in attribute selector for " + name, pstate);-    }--    if (!lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate);-    return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, matcher, value);-  }--  /* parse block comment and add to block */-  void Parser::parse_block_comments()-  {-    Block* block = block_stack.back();-    while (lex< block_comment >()) {-      bool is_important = lexed.begin[2] == '!';-      String*  contents = parse_interpolated_chunk(lexed);-      (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);-    }-  }--  Declaration* Parser::parse_declaration() {-    String* prop = 0;-    if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {-      prop = parse_identifier_schema();-    }-    else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {-      prop = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-      prop->is_delayed(true);-    }-    else {-      css_error("Invalid CSS", " after ", ": expected \"}\", was ");-    }-    bool is_indented = true;-    const std::string property(lexed);-    if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);-    lex < css_comments >(false);-    if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);-    if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty-    if (peek_css< static_value >()) {-      return SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/);-    }-    else {-      Expression* value;-      Lookahead lookahead = lookahead_for_value(position);-      if (lookahead.found) {-        if (lookahead.has_interpolants) {-          value = parse_value_schema(lookahead.found);-        } else {-          value = parse_list();-        }-      }-      else {-        value = parse_list();-        if (List* list = dynamic_cast<List*>(value)) {-          if (list->length() == 0 && !peek< exactly <'{'> >()) {-            css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-          }-        }-      }-      lex < css_comments >(false);-      auto decl = SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/);-      decl->is_indented(is_indented);-      return decl;-    }-  }--  // parse +/- and return false if negative-  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* Parser::parse_map()-  {-    Expression* key = parse_list();-    Map* map = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);-    if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {-      if (!str->quote_mark() && !str->is_delayed()) {-        if (const Color* col = name_to_color(str->value())) {-          Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);-          c->pstate(str->pstate());-          c->disp(str->value());-          key = c;-        }-      }-    }--    // it's not a map so return the lexed value as a list value-    if (!peek< exactly<':'> >())-    { return key; }--    lex< exactly<':'> >();--    Expression* value = parse_space_list();--    (*map) << std::make_pair(key, value);--    while (lex_css< exactly<','> >())-    {-      // allow trailing commas - #495-      if (peek_css< exactly<')'> >(position))-      { break; }--      Expression* key = parse_list();-      if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {-        if (!str->quote_mark() && !str->is_delayed()) {-          if (const Color* col = name_to_color(str->value())) {-            Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);-            c->pstate(str->pstate());-            c->disp(str->value());-            key = c;-          }-        }-      }--      if (!(lex< exactly<':'> >()))-      { error("invalid syntax", pstate); }--      Expression* value = parse_space_list();--      (*map) << std::make_pair(key, value);-    }--    ParserState ps = map->pstate();-    ps.offset = pstate - ps + pstate.offset;-    map->pstate(ps);--    return map;-  }--  // parse list returns either a space separated list,-  // a comma separated list or any bare expression found.-  // so to speak: we unwrap items from lists if possible here!-  Expression* Parser::parse_list()-  {-    // parse list is relly just an alias-    return parse_comma_list();-  }--  // will return singletons unwrapped-  Expression* Parser::parse_comma_list()-  {-    // check if we have an empty list-    // return the empty list as such-    if (peek_css< alternatives <-          // exactly<'!'>,-          exactly<';'>,-          exactly<'}'>,-          exactly<'{'>,-          exactly<')'>,-          exactly<':'>,-          exactly<ellipsis>,-          default_flag,-          global_flag-        > >(position))-    { return SASS_MEMORY_NEW(ctx.mem, List, pstate, 0); }--    // now try to parse a space list-    Expression* list = parse_space_list();-    // if it's a singleton, return it (don't wrap it)-    if (!peek_css< exactly<','> >(position)) return list;--    // if we got so far, we actually do have a comma list-    List* comma_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_COMMA);-    // wrap the first expression-    (*comma_list) << list;--    while (lex_css< exactly<','> >())-    {-      // check for abort condition-      if (peek_css< alternatives <-            exactly<';'>,-            exactly<'}'>,-            exactly<'{'>,-            exactly<')'>,-            exactly<':'>,-            exactly<ellipsis>,-            default_flag,-            global_flag-          > >(position)-      ) { break; }-      // otherwise add another expression-      (*comma_list) << parse_space_list();-    }-    // return the list-    return comma_list;-  }-  // EO parse_comma_list--  // will return singletons unwrapped-  Expression* Parser::parse_space_list()-  {-    Expression* disj1 = parse_disjunction();-    // if it's a singleton, return it (don't wrap it)-    if (peek_css< alternatives <-          // exactly<'!'>,-          exactly<';'>,-          exactly<'}'>,-          exactly<'{'>,-          exactly<')'>,-          exactly<','>,-          exactly<':'>,-          exactly<ellipsis>,-          default_flag,-          global_flag-        > >(position)-    ) { return disj1; }--    List* space_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_SPACE);-    (*space_list) << disj1;--    while (!(peek_css< alternatives <-               // exactly<'!'>,-               exactly<';'>,-               exactly<'}'>,-               exactly<'{'>,-               exactly<')'>,-               exactly<','>,-               exactly<':'>,-               exactly<ellipsis>,-               default_flag,-               global_flag-           > >(position)) && peek_css< optional_css_whitespace >() != end-    ) {-      // the space is parsed implicitly?-      (*space_list) << parse_disjunction();-    }-    // return the list-    return space_list;-  }-  // EO parse_space_list--  // parse logical OR operation-  Expression* Parser::parse_disjunction()-  {-    // parse the left hand side conjunction-    Expression* conj = parse_conjunction();-    // parse multiple right hand sides-    std::vector<Expression*> operands;-    while (lex_css< kwd_or >())-      operands.push_back(parse_conjunction());-    // if it's a singleton, return it directly-    if (operands.size() == 0) return conj;-    // fold all operands into one binary expression-    return fold_operands(conj, operands, Sass_OP::OR);-  }-  // EO parse_disjunction--  // parse logical AND operation-  Expression* Parser::parse_conjunction()-  {-    // parse the left hand side relation-    Expression* rel = parse_relation();-    // parse multiple right hand sides-    std::vector<Expression*> operands;-    while (lex_css< kwd_and >())-      operands.push_back(parse_relation());-    // if it's a singleton, return it directly-    if (operands.size() == 0) return rel;-    // fold all operands into one binary expression-    return fold_operands(rel, operands, Sass_OP::AND);-  }-  // EO parse_conjunction--  // parse comparison operations-  Expression* Parser::parse_relation()-  {-    // parse the left hand side expression-    Expression* lhs = parse_expression();-    // if it's a singleton, return it (don't wrap it)-    if (!(peek< alternatives <-            kwd_eq,-            kwd_neq,-            kwd_gte,-            kwd_gt,-            kwd_lte,-            kwd_lt-          > >(position)))-    { return lhs; }-    // parse the operator-    enum Sass_OP op-    = lex<kwd_eq>()  ? Sass_OP::EQ-    : lex<kwd_neq>() ? Sass_OP::NEQ-    : lex<kwd_gte>() ? Sass_OP::GTE-    : lex<kwd_lte>() ? Sass_OP::LTE-    : lex<kwd_gt>()  ? Sass_OP::GT-    : lex<kwd_lt>()  ? Sass_OP::LT-    // we checked the possibilites on top of fn-    :                  Sass_OP::EQ;-    // parse the right hand side expression-    Expression* rhs = parse_expression();-    // return binary expression with a left and a right hand side-    return SASS_MEMORY_NEW(ctx.mem, Binary_Expression, lhs->pstate(), op, lhs, rhs);-  }-  // parse_relation--  // parse expression valid for operations-  // called from parse_relation-  // called from parse_for_directive-  // called from parse_media_expression-  // parse addition and subtraction operations-  Expression* Parser::parse_expression()-  {-    Expression* lhs = parse_operators();-    // if it's a singleton, return it (don't wrap it)-    if (!(peek< exactly<'+'> >(position) ||-          // condition is a bit misterious, 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< identifier >(position))-    { return lhs; }--    std::vector<Expression*> operands;-    std::vector<Sass_OP> operators;-    while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {-      operators.push_back(lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB);-      operands.push_back(parse_operators());-    }--    if (operands.size() == 0) return lhs;-    return fold_operands(lhs, operands, operators);-  }--  // parse addition and subtraction operations-  Expression* Parser::parse_operators()-  {-    Expression* factor = parse_factor();-    // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant-    if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) {-      String_Schema* ss = dynamic_cast<String_Schema*>(factor);-      if (ss && ss->has_interpolants()) return factor;-    }-    // if it's a singleton, return it (don't wrap it)-    if (!peek_css< class_char< static_ops > >()) return factor;-    // parse more factors and operators-    std::vector<Expression*> operands; // factors-    std::vector<enum Sass_OP> operators; // ops-    // lex operations to apply to lhs-    while (lex_css< class_char< static_ops > >()) {-      switch(*lexed.begin) {-        case '*': operators.push_back(Sass_OP::MUL); break;-        case '/': operators.push_back(Sass_OP::DIV); break;-        case '%': operators.push_back(Sass_OP::MOD); break;-        default: throw std::runtime_error("unknown static op parsed"); break;-      }-      operands.push_back(parse_factor());-    }-    // operands and operators to binary expression-    return fold_operands(factor, operands, operators);-  }-  // EO parse_operators---  // called from parse_operators-  // called from parse_value_schema-  Expression* Parser::parse_factor()-  {-    lex < css_comments >(false);-    if (lex_css< exactly<'('> >()) {-      // parse_map may return a list-      Expression* value = parse_map();-      // lex the expected closing parenthesis-      if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);-      // expression can be evaluated-      value->is_delayed(false);-      // make sure wrapped lists and division expressions are non-delayed within parentheses-      if (value->concrete_type() == Expression::LIST) {-        List* l = static_cast<List*>(value);-        if (!l->empty()) (*l)[0]->is_delayed(false);-      } else if (typeid(*value) == typeid(Binary_Expression)) {-        Binary_Expression* b = static_cast<Binary_Expression*>(value);-        Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());-        if (lhs && lhs->type() == Sass_OP::DIV) lhs->is_delayed(false);-      }-      return value;-    }-    else if (peek< ie_property >()) {-      return parse_ie_property();-    }-    else if (peek< ie_keyword_arg >()) {-      return parse_ie_keyword_arg();-    }-    else if (peek< exactly< calc_kwd > >() ||-             peek< exactly< moz_calc_kwd > >() ||-             peek< exactly< ms_calc_kwd > >() ||-             peek< exactly< webkit_calc_kwd > >()) {-      return parse_calc_function();-    }-    else if (lex < functional_schema >()) {-      return parse_function_call_schema();-    }-    else if (lex< identifier_schema >()) {-      String* string = parse_identifier_schema();-      if (String_Schema* schema = dynamic_cast<String_Schema*>(string)) {-        if (lex < exactly < '(' > >()) {-          *schema << parse_list();-          lex < exactly < ')' > >();-        }-      }-      return string;-    }-    else if (peek< sequence< uri_prefix, W, real_uri_value > >()) {-      return parse_url_function_string();-    }-    else if (peek< re_functional >()) {-      return parse_function_call();-    }-    else if (lex< exactly<'+'> >()) {-      return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());-    }-    else if (lex< exactly<'-'> >()) {-      return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());-    }-    else if (lex< sequence< kwd_not > >()) {-      return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());-    }-    else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {-      if (parse_number_prefix()) return parse_value(); // prefix is positive-      return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());-    }-    else {-      return parse_value();-    }-  }--  // parse one value for a list-  Expression* Parser::parse_value()-  {-    lex< css_comments >(false);-    if (lex< ampersand >())-    {-      return SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); }--    if (lex< kwd_important >())-    { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); }--    if (const char* stop = peek< value_schema >())-    { return parse_value_schema(stop); }--    // string may be interpolated-    if (lex< quoted_string >())-    { return parse_string(); }--    if (lex< kwd_true >())-    { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); }--    if (lex< kwd_false >())-    { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false); }--    if (lex< kwd_null >())-    { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); }--    if (lex< identifier >()) {-      return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-    }--    if (lex< percentage >())-    { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); }--    // match hex number first because 0x000 looks like a number followed by an indentifier-    if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >())-    { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); }--    if (lex< sequence < exactly <'#'>, identifier > >())-    { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); }--    // also handle the 10em- foo special case-    if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())-    { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); }--    if (lex< sequence< static_component, one_plus< strict_identifier > > >())-    { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }--    if (lex< number >())-    { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); }--    if (lex< variable >())-    { return SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed)); }--    // Special case handling for `%` proceeding an interpolant.-    if (lex< sequence< exactly<'%'>, optional< percentage > > >())-    { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }--    error("error reading values after " + lexed.to_string(), pstate);--    // unreachable statement-    return 0;-  }--  // this parses interpolation inside other strings-  // means the result should later be quoted again-  String* Parser::parse_interpolated_chunk(Token chunk, bool constant)-  {-    const char* i = chunk.begin;-    // see if there any interpolants-    const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);-    if (!p) {-      String_Quoted* str_quoted = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(i, chunk.end));-      if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');-      str_quoted->is_delayed(true);-      return str_quoted;-    }--    String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);-    while (i < chunk.end) {-      p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);-      if (p) {-        if (i < p) {-          // accumulate the preceding segment if it's nonempty-          (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));-        }-        // we need to skip anything inside strings-        // create a new target in parser/prelexer-        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;-          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-        }-        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace-        if (j) { --j;-          // parse the interpolant and accumulate it-          Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();-          interp_node->is_interpolant(true);-          (*schema) << interp_node;-          i = j;-        }-        else {-          // throw an error if the interpolant is unterminated-          error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);-        }-      }-      else { // no interpolants left; add the last segment if nonempty-        // check if we need quotes here (was not sure after merge)-        if (i < chunk.end) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, chunk.end));-        break;-      }-      ++ i;-    }--    return schema;-  }--  String_Constant* Parser::parse_static_expression()-  {-    if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {-      return parse_static_value();-    }-    return 0;-  }--  String_Constant* Parser::parse_static_value()-  {-    lex< static_value >();-    Token str(lexed);-    --str.end;-    --position;--    String_Constant* str_node = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, str.time_wspace());-    str_node->is_delayed(true);-    return str_node;-  }--  String* Parser::parse_string()-  {-    return parse_interpolated_chunk(Token(lexed));-  }--  String* Parser::parse_ie_property()-  {-    lex< ie_property >();-    Token str(lexed);-    const char* i = str.begin;-    // see if there any interpolants-    const char* p = find_first_in_interval< exactly<hash_lbrace> >(str.begin, str.end);-    if (!p) {-      return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end));-    }--    String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);-    while (i < str.end) {-      p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);-      if (p) {-        if (i < p) {-          (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty-        }-        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;-          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-        }-        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace-        if (j) {-          // parse the interpolant and accumulate it-          Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();-          interp_node->is_interpolant(true);-          (*schema) << interp_node;-          i = j;-        }-        else {-          // throw an error if the interpolant is unterminated-          error("unterminated interpolant inside IE function " + str.to_string(), pstate);-        }-      }-      else { // no interpolants left; add the last segment if nonempty-        if (i < str.end) {-          (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, str.end));-        }-        break;-      }-    }-    return schema;-  }--  String* Parser::parse_ie_keyword_arg()-  {-    String_Schema* kwd_arg = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate, 3);-    if (lex< variable >()) {-      *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed));-    } else {-      lex< alternatives< identifier_schema, identifier > >();-      *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-    }-    lex< exactly<'='> >();-    *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-    if (peek< variable >()) *kwd_arg << parse_list();-    else if (lex< number >()) *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, Util::normalize_decimals(lexed));-    else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }-    return kwd_arg;-  }--  String_Schema* Parser::parse_value_schema(const char* stop)-  {-    // initialize the string schema object to add tokens-    String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);--    if (peek<exactly<'}'>>()) {-      css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-    }--    const char* e = 0;-    size_t num_items = 0;-    while (position < stop) {-      // parse space between tokens-      if (lex< spaces >() && num_items) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");-      }-      if ((e = peek< re_functional >()) && e < stop) {-        (*schema) << parse_function_call();-      }-      // lex an interpolant /#{...}/-      else if (lex< exactly < hash_lbrace > >()) {-        // Try to lex static expression first-        if (peek< exactly< rbrace > >()) {-          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-        }-        if (lex< re_static_expression >()) {-          (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-        } else {-          (*schema) << parse_list();-        }-        // ToDo: no error check here?-        lex < exactly < rbrace > >();-      }-      // lex some string constants-      else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);-        if (*position == '"' || *position == '\'') {-          (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");-        }-      }-      // lex a quoted string-      else if (lex< quoted_string >()) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed, '"');-        if (*position == '"' || *position == '\'' || alpha(position)) {-          (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");-        }-      }-      // lex (normalized) variable-      else if (lex< variable >()) {-        std::string name(Util::normalize_underscores(lexed));-        (*schema) << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, name);-      }-      // lex percentage value-      else if (lex< percentage >()) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed);-      }-      // lex dimension value-      else if (lex< dimension >()) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed);-      }-      // lex number value-      else if (lex< number >()) {-        (*schema) <<  SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed);-      }-      // lex hex color value-      else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed);-      }-      else if (lex< sequence < exactly <'#'>, identifier > >()) {-        (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed);-      }-      // lex a value in parentheses-      else if (peek< parenthese_scope >()) {-        (*schema) << parse_factor();-      }-      else {-        return schema;-      }-      ++num_items;-    }-    return schema;-  }--  // this parses interpolation outside other strings-  // means the result must not be quoted again later-  String* Parser::parse_identifier_schema()-  {-    Token id(lexed);-    const char* i = id.begin;-    // see if there any interpolants-    const char* p = find_first_in_interval< exactly<hash_lbrace> >(id.begin, id.end);-    if (!p) {-      return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end));-    }--    String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);-    while (i < id.end) {-      p = find_first_in_interval< exactly<hash_lbrace> >(i, id.end);-      if (p) {-        if (i < p) {-          // accumulate the preceding segment if it's nonempty-          const char* o = position; position = i;-          *schema << parse_value_schema(p);-          position = o;-        }-        // we need to skip anything inside strings-        // create a new target in parser/prelexer-        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p;-          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");-        }-        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace-        if (j) {-          // parse the interpolant and accumulate it-          Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();-          interp_node->is_interpolant(true);-          (*schema) << interp_node;-          schema->has_interpolants(true);-          i = j;-        }-        else {-          // throw an error if the interpolant is unterminated-          error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);-        }-      }-      else { // no interpolants left; add the last segment if nonempty-        if (i < end) {-          const char* o = position; position = i;-          *schema << parse_value_schema(id.end);-          position = o;-        }-        break;-      }-    }-    return schema;-  }--  // calc functions should preserve arguments-  Function_Call* Parser::parse_calc_function()-  {-    lex< identifier >();-    std::string name(lexed);-    ParserState call_pos = pstate;-    lex< exactly<'('> >();-    ParserState arg_pos = pstate;-    const char* arg_beg = position;-    parse_list();-    const char* arg_end = position;-    lex< skip_over_scopes <-          exactly < '(' >,-          exactly < ')' >-        > >();--    Argument* arg = SASS_MEMORY_NEW(ctx.mem, Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));-    Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, arg_pos);-    *args << arg;-    return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);-  }--  String* Parser::parse_url_function_string()-  {-    const char* p = position;--    lex< uri_prefix >();-    std::string prefix = lexed;--    lex< real_uri_value >(false);-    std::string uri = lexed;--    if (peek< exactly< hash_lbrace > >()) {-      const char* pp = position;-      // TODO: error checking for unclosed interpolants-      while (peek< exactly< hash_lbrace > >(pp)) {-        pp = sequence< interpolant, real_uri_value >(pp);-      }-      position = peek< real_uri_suffix >(pp);-      return parse_interpolated_chunk(Token(p, position));-    } else {-      lex< real_uri_suffix >();-      std::string res = prefix + Util::rtrim(uri) + lexed.to_string();-      return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res);-    }--  }--  Function_Call* Parser::parse_function_call()-  {-    lex< identifier >();-    std::string name(lexed);--    ParserState call_pos = pstate;-    Arguments* args = parse_arguments();-    return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);-  }--  Function_Call_Schema* Parser::parse_function_call_schema()-  {-    String* name = parse_identifier_schema();-    ParserState source_position_of_call = pstate;--    Function_Call_Schema* the_call = SASS_MEMORY_NEW(ctx.mem, Function_Call_Schema, source_position_of_call, name, parse_arguments());-    return the_call;-  }--  Content* Parser::parse_content_directive()-  {-    if (stack.back() != mixin_def) {-      error("@content may only be used within a mixin", pstate);-    }-    return SASS_MEMORY_NEW(ctx.mem, Content, pstate);-  }--  If* Parser::parse_if_directive(bool else_if)-  {-    ParserState if_source_position = pstate;-    Expression* predicate = parse_list();-    predicate->is_delayed(false);-    Block* block = parse_block();-    Block* alternative = 0;--    // only throw away comment if we parse a case-    // we want all other comments to be parsed-    if (lex_css< elseif_directive >()) {-      alternative = SASS_MEMORY_NEW(ctx.mem, Block, pstate);-      (*alternative) << parse_if_directive(true);-    }-    else if (lex_css< kwd_else_directive >()) {-      alternative = parse_block();-    }-    return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative);-  }--  For* Parser::parse_for_directive()-  {-    ParserState for_source_position = pstate;-    lex_variable();-    std::string var(Util::normalize_underscores(lexed));-    if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);-    Expression* lower_bound = parse_expression();-    lower_bound->is_delayed(false);-    bool inclusive = false;-    if (lex< kwd_through >()) inclusive = true;-    else if (lex< kwd_to >()) inclusive = false;-    else                  error("expected 'through' or 'to' keyword in @for directive", pstate);-    Expression* upper_bound = parse_expression();-    upper_bound->is_delayed(false);-    Block* body = parse_block();-    return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive);-  }--  // helper to parse a var token-  Token Parser::lex_variable()-  {-    // peek for dollar sign first-    if (!peek< exactly <'$'> >()) {-      css_error("Invalid CSS", " after ", ": expected \"$\", was ");-    }-    // we expect a simple identfier as the call name-    if (!lex< sequence < exactly <'$'>, identifier > >()) {-      lex< exactly <'$'> >(); // move pstate and position up-      css_error("Invalid CSS", " after ", ": expected identifier, was ");-    }-    // return object-    return token;-  }-  // helper to parse identifier-  Token Parser::lex_identifier()-  {-    // we expect a simple identfier as the call name-    if (!lex< identifier >()) { // ToDo: pstate wrong?-      css_error("Invalid CSS", " after ", ": expected identifier, was ");-    }-    // return object-    return token;-  }--  Each* Parser::parse_each_directive()-  {-    ParserState each_source_position = pstate;-    std::vector<std::string> vars;-    lex_variable();-    vars.push_back(Util::normalize_underscores(lexed));-    while (lex< exactly<','> >()) {-      if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);-      vars.push_back(Util::normalize_underscores(lexed));-    }-    if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);-    Expression* list = parse_list();-    list->is_delayed(false);-    if (list->concrete_type() == Expression::LIST) {-      List* l = static_cast<List*>(list);-      for (size_t i = 0, L = l->length(); i < L; ++i) {-        (*l)[i]->is_delayed(false);-      }-    }-    Block* body = parse_block();-    return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body);-  }--  // called after parsing `kwd_while_directive`-  While* Parser::parse_while_directive()-  {-    // create the initial while call object-    While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0);-    // parse mandatory predicate-    Expression* predicate = parse_list();-    predicate->is_delayed(false);-    call->predicate(predicate);-    // parse mandatory block-    call->block(parse_block());-    // return ast node-    return call;-  }--  // EO parse_while_directive-  Media_Block* Parser::parse_media_block()-  {-    Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0);-    media_block->media_queries(parse_media_queries());--    Media_Block* prev_media_block = last_media_block;-    last_media_block = media_block;-    media_block->block(parse_css_block());-    last_media_block = prev_media_block;--    return media_block;-  }--  List* Parser::parse_media_queries()-  {-    List* media_queries = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_COMMA);-    if (!peek_css < exactly <'{'> >()) (*media_queries) << parse_media_query();-    while (lex_css < exactly <','> >()) (*media_queries) << parse_media_query();-    return media_queries;-  }--  // Expression* Parser::parse_media_query()-  Media_Query* Parser::parse_media_query()-  {-    Media_Query* media_query = SASS_MEMORY_NEW(ctx.mem, Media_Query, pstate);--    lex < css_comments >(false);-    if (lex < kwd_not >()) media_query->is_negated(true);-    else if (lex < kwd_only >()) media_query->is_restricted(true);--    lex < css_comments >(false);-    if (lex < identifier_schema >())  media_query->media_type(parse_identifier_schema());-    else if (lex < identifier >())    media_query->media_type(parse_interpolated_chunk(lexed));-    else                             (*media_query) << parse_media_expression();--    while (lex_css < kwd_and >()) (*media_query) << parse_media_expression();-    if (lex < identifier_schema >()) {-      String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);-      *schema << media_query->media_type();-      *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");-      *schema << parse_identifier_schema();-      media_query->media_type(schema);-    }-    while (lex_css < kwd_and >()) (*media_query) << parse_media_expression();-    return media_query;-  }--  Media_Query_Expression* Parser::parse_media_expression()-  {-    if (lex < identifier_schema >()) {-      String* ss = parse_identifier_schema();-      return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, pstate, ss, 0, true);-    }-    if (!lex_css< exactly<'('> >()) {-      error("media query expression must begin with '('", pstate);-    }-    Expression* feature = 0;-    if (peek_css< exactly<')'> >()) {-      error("media feature required in media query expression", pstate);-    }-    feature = parse_expression();-    Expression* expression = 0;-    if (lex< exactly<':'> >()) {-      expression = parse_list();-    }-    if (!lex< exactly<')'> >()) {-      error("unclosed parenthesis in media query expression", pstate);-    }-    return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression);-  }--  // lexed after `kwd_supports_directive`-  // these are very similar to media blocks-  Supports_Block* Parser::parse_supports_directive()-  {-    Supports_Condition* cond = parse_supports_condition();-    // create the ast node object for the support queries-    Supports_Block* query = SASS_MEMORY_NEW(ctx.mem, Supports_Block, pstate, cond);-    // additional block is mandatory-    // parse inner block-    query->block(parse_block());-    // return ast node-    return query;-  }--  // parse one query operation-  // may encounter nested queries-  Supports_Condition* Parser::parse_supports_condition()-  {-    lex < css_whitespace >();-    Supports_Condition* cond = parse_supports_negation();-    if (!cond) cond = parse_supports_operator();-    if (!cond) cond = parse_supports_interpolation();-    return cond;-  }--  Supports_Condition* Parser::parse_supports_negation()-  {-    if (!lex < kwd_not >()) return 0;--    Supports_Condition* cond = parse_supports_condition_in_parens();-    return SASS_MEMORY_NEW(ctx.mem, Supports_Negation, pstate, cond);-  }--  Supports_Condition* Parser::parse_supports_operator()-  {-    Supports_Condition* cond = parse_supports_condition_in_parens();-    if (!cond) return 0;--    while (true) {-      Supports_Operator::Operand op = Supports_Operator::OR;-      if (lex < kwd_and >()) { op = Supports_Operator::AND; }-      else if(!lex < kwd_or >()) { break; }--      lex < css_whitespace >();-      Supports_Condition* right = parse_supports_condition_in_parens();--      // Supports_Condition* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Condition, *static_cast<Supports_Condition*>(cond));-      cond = SASS_MEMORY_NEW(ctx.mem, Supports_Operator, pstate, cond, right, op);-    }-    return cond;-  }--  Supports_Condition* Parser::parse_supports_interpolation()-  {-    if (!lex < interpolant >()) return 0;--    String* interp = parse_interpolated_chunk(lexed);-    if (!interp) return 0;--    return SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation, pstate, interp);-  }--  // TODO: This needs some major work. Although feature conditions-  // look like declarations their semantics differ siginificantly-  Supports_Condition* Parser::parse_supports_declaration()-  {-    Supports_Condition* cond = 0;-    // parse something declaration like-    Declaration* declaration = parse_declaration();-    if (!declaration) error("@supports condition expected declaration", pstate);-    cond = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,-                     declaration->pstate(),-                     declaration->property(),-                     declaration->value());-    // ToDo: maybe we need an additional error condition?-    return cond;-  }--  Supports_Condition* Parser::parse_supports_condition_in_parens()-  {-    Supports_Condition* interp = parse_supports_interpolation();-    if (interp != 0) return interp;--    if (!lex < exactly <'('> >()) return 0;-    lex < css_whitespace >();--    Supports_Condition* cond = parse_supports_condition();-    if (cond != 0) {-      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);-    } else {-      cond = parse_supports_declaration();-      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);-    }-    lex < css_whitespace >();-    return cond;-  }--  At_Root_Block* Parser::parse_at_root_block()-  {-    ParserState at_source_position = pstate;-    Block* body = 0;-    At_Root_Expression* expr = 0;-    Lookahead lookahead_result;-    LOCAL_FLAG(in_at_root, true);-    if (lex< exactly<'('> >()) {-      expr = parse_at_root_expression();-    }-    if (peek < exactly<'{'> >()) {-      body = parse_block(true);-    }-    else if ((lookahead_result = lookahead_for_selector(position)).found) {-      Ruleset* r = parse_ruleset(lookahead_result, false);-      body = SASS_MEMORY_NEW(ctx.mem, Block, r->pstate(), 1, true);-      *body << r;-    }-    At_Root_Block* at_root = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, at_source_position, body);-    if (expr) at_root->expression(expr);-    return at_root;-  }--  At_Root_Expression* Parser::parse_at_root_expression()-  {-    if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);--    if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {-      css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");-    }--    Declaration* declaration = parse_declaration();-    List* value = SASS_MEMORY_NEW(ctx.mem, List, declaration->value()->pstate(), 1);--    if (declaration->value()->concrete_type() == Expression::LIST) {-        value = static_cast<List*>(declaration->value());-    }-    else *value << declaration->value();--    At_Root_Expression* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression,-                                               declaration->pstate(),-                                               declaration->property(),-                                               value);-    if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);-    return cond;-  }--  At_Rule* Parser::parse_at_rule()-  {-    std::string kwd(lexed);--    if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);--    At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd);-    Lookahead lookahead = lookahead_for_include(position);-    if (lookahead.found && !lookahead.has_interpolants) {-      at_rule->selector(parse_selector_list(true));-    }--    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;-  }--  Warning* Parser::parse_warning()-  {-    return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list());-  }--  Error* Parser::parse_error()-  {-    return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list());-  }--  Debug* Parser::parse_debug()-  {-    return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list());-  }--  Return* Parser::parse_return_directive()-  {-    // check that we do not have an empty list (ToDo: check if we got all cases)-    if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >())-    { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }-    return SASS_MEMORY_NEW(ctx.mem, Return, pstate, parse_list());-  }--  Lookahead Parser::lookahead_for_selector(const char* start)-  {-    // init result struct-    Lookahead rv = Lookahead();-    // get start position-    const char* p = start ? start : position;-    // match in one big "regex"-    rv.error = p;-    if (const char* q =-      peek <-        one_plus <-          alternatives <-            // consume whitespace and comments-            spaces, block_comment, line_comment,-            // match `/deep/` selector (pass-trough)-            // there is no functionality for it yet-            schema_reference_combinator,-            // match selector ops /[*&%,()\[\]]/-            class_char < selector_lookahead_ops >,-            // match selector combinators /[>+~]/-            class_char < selector_combinator_ops >,-            // match attribute compare operators-            alternatives <-              exact_match, class_match, dash_match,-              prefix_match, suffix_match, substring_match-            >,-            // main selector match-            sequence <-              // allow namespace prefix-              optional < namespace_schema >,-              // modifiers prefixes-              alternatives <-                sequence <-                  exactly <'#'>,-                  // not for interpolation-                  negate < exactly <'{'> >-                >,-                // class match-                exactly <'.'>,-                // single or double colon-                optional < pseudo_prefix >-              >,-              // accept hypens in token-              one_plus < sequence <-                // can start with hyphens-                zero_plus < exactly<'-'> >,-                // now the main token-                alternatives <-                  kwd_optional,-                  exactly <'*'>,-                  quoted_string,-                  interpolant,-                  identifier,-                  percentage,-                  dimension,-                  variable,-                  alnum-                >-              > >,-              // can also end with hyphens-              zero_plus < exactly<'-'> >-            >-          >-        >-      >(p)-    ) {-      while (p < q) {-        // did we have interpolations?-        if (*p == '#' && *(p+1) == '{') {-          rv.has_interpolants = true;-          p = q; break;-        }-        ++ p;-      }-      // store anyway  }---      // ToDo: remove-      rv.error = q;-      rv.position = q;-      // check expected opening bracket-      // only after successfull matching-      if (peek < exactly<'{'> >(q)) rv.found = q;-      // else if (peek < exactly<';'> >(q)) rv.found = q;-      // else if (peek < exactly<'}'> >(q)) rv.found = q;-      if (rv.found || *p == 0) rv.error = 0;-    }--    rv.parsable = ! rv.has_interpolants;--    // return result-    return rv;--  }-  // EO lookahead_for_selector--  // used in parse_block_nodes and parse_at_rule-  // ToDo: actual usage is still not really clear to me?-  Lookahead Parser::lookahead_for_include(const char* start)-  {-    // we actually just lookahead for a selector-    Lookahead rv = lookahead_for_selector(start);-    // but the "found" rules are different-    if (const char* p = rv.position) {-      // check for additional abort condition-      if (peek < exactly<';'> >(p)) rv.found = p;-      else if (peek < exactly<'}'> >(p)) rv.found = p;-    }-    // return result-    return rv;-  }-  // EO lookahead_for_include--  // look ahead for a token with interpolation in it-  // we mostly use the result if there is an interpolation-  // everything that passes here gets parsed as one schema-  // meaning it will not be parsed as a space separated list-  Lookahead Parser::lookahead_for_value(const char* start)-  {-    // init result struct-    Lookahead rv = Lookahead();-    // get start position-    const char* p = start ? start : position;-    // match in one big "regex"-    if (const char* q =-      peek <-        non_greedy <-          alternatives <-            // consume whitespace-            block_comment, spaces,-            // main tokens-            interpolant,-            identifier,-            variable,-            // issue #442-            sequence <-              parenthese_scope,-              interpolant-            >-          >,-          sequence <-            optional_spaces,-            alternatives <-              exactly<'{'>,-              exactly<'}'>,-              exactly<';'>-            >-          >-        >-      >(p)-    ) {-      if (p == q) return rv;-      while (p < q) {-        // did we have interpolations?-        if (*p == '#' && *(p+1) == '{') {-          rv.has_interpolants = true;-          p = q; break;-        }-        ++ p;-      }-      // store anyway-      // ToDo: remove-      rv.position = q;-      // check expected opening bracket-      // only after successfull matching-      if (peek < exactly<'{'> >(q)) rv.found = q;-      else if (peek < exactly<';'> >(q)) rv.found = q;-      else if (peek < exactly<'}'> >(q)) rv.found = q;-    }--    // return result-    return rv;-  }-  // EO lookahead_for_value--  void Parser::read_bom()-  {-    size_t skip = 0;-    std::string encoding;-    bool utf_8 = false;-    switch ((unsigned char) source[0]) {-    case 0xEF:-      skip = check_bom_chars(source, end, utf_8_bom, 3);-      encoding = "UTF-8";-      utf_8 = true;-      break;-    case 0xFE:-      skip = check_bom_chars(source, end, utf_16_bom_be, 2);-      encoding = "UTF-16 (big endian)";-      break;-    case 0xFF:-      skip = check_bom_chars(source, end, utf_16_bom_le, 2);-      skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);-      encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");-      break;-    case 0x00:-      skip = check_bom_chars(source, end, utf_32_bom_be, 4);-      encoding = "UTF-32 (big endian)";-      break;-    case 0x2B:-      skip = check_bom_chars(source, end, utf_7_bom_1, 4)-           | check_bom_chars(source, end, utf_7_bom_2, 4)-           | check_bom_chars(source, end, utf_7_bom_3, 4)-           | check_bom_chars(source, end, utf_7_bom_4, 4)-           | check_bom_chars(source, end, utf_7_bom_5, 5);-      encoding = "UTF-7";-      break;-    case 0xF7:-      skip = check_bom_chars(source, end, utf_1_bom, 3);-      encoding = "UTF-1";-      break;-    case 0xDD:-      skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);-      encoding = "UTF-EBCDIC";-      break;-    case 0x0E:-      skip = check_bom_chars(source, end, scsu_bom, 3);-      encoding = "SCSU";-      break;-    case 0xFB:-      skip = check_bom_chars(source, end, bocu_1_bom, 3);-      encoding = "BOCU-1";-      break;-    case 0x84:-      skip = check_bom_chars(source, end, gb_18030_bom, 4);-      encoding = "GB-18030";-      break;-    }-    if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);-    position += skip;-  }--  size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)-  {-    size_t skip = 0;-    if (src + len > end) return 0;-    for (size_t i = 0; i < len; ++i, ++skip) {-      if ((unsigned char) src[i] != bom[i]) return 0;-    }-    return skip;-  }---  Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, enum Sass_OP op)-  {-    for (size_t i = 0, S = operands.size(); i < S; ++i) {-      base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]);-      Binary_Expression* b = static_cast<Binary_Expression*>(base);-      if (op == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {-        base->is_delayed(true);-      }-      else {-        b->left()->is_delayed(false);-        b->right()->is_delayed(false);-      }-    }-    return base;-  }--  Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<enum Sass_OP>& ops)-  {-    for (size_t i = 0, S = operands.size(); i < S; ++i) {-      base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);-      Binary_Expression* b = static_cast<Binary_Expression*>(base);-      if (ops[i] == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {-        base->is_delayed(true);-      }-      else {-        b->left()->is_delayed(false);-        b->right()->is_delayed(false);-      }-    }-    return base;-  }--  void Parser::error(std::string msg, Position pos)-  {-    throw Exception::InvalidSass(ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);-  }--  // print a css parsing error with actual context information from parsed source-  void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle)-  {-    int max_len = 18;-    const char* pos = peek < optional_spaces >();--    const char* last_pos(pos - 1);-    // backup position to last significant char-    while ((!*last_pos || Prelexer::is_space(*last_pos)) && last_pos > source) -- last_pos;--    bool ellipsis_left = false;-    const char* pos_left(last_pos + 1);-    const char* end_left(last_pos + 1);-    while (pos_left > source) {-      if (end_left - pos_left >= max_len) {-        ellipsis_left = true;-        break;-      }--      const char* prev = pos_left - 1;-      if (*prev == '\r') break;-      if (*prev == '\n') break;-      pos_left = prev;-    }-    if (pos_left < source) {-      pos_left = source;-    }--    bool ellipsis_right = false;-    const char* end_right(pos);-    const char* pos_right(pos);-    while (end_right <= end) {-      if (end_right - pos_right > max_len) {-        ellipsis_right = true;-        break;-      }-      if (*end_right == '\r') break;-      if (*end_right == '\n') break;-      ++ end_right;-    }-    if (end_right > end) end_right = end;--    std::string left(pos_left, end_left);-    std::string right(pos_right, end_right);-    if (ellipsis_left) left = ellipsis + left.substr(left.size() - 15);-    if (ellipsis_right) right = right.substr(right.size() - 15) + ellipsis;+#include "sass.hpp"+#include <cstdlib>+#include <iostream>+#include <vector>+#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"++// Notes about delayed: some ast nodes can have delayed evaluation so+// they can preserve their original semantics if needed. This is most+// prominently exhibited by the division operation, since it is not+// only a valid operation, but also a valid css statement (i.e. for+// fonts, as in `16px/24px`). When parsing lists and expression we+// unwrap single items from lists and other operations. A nested list+// must not be delayed, only the items of the first level sometimes+// are delayed (as with argument lists). To achieve this we need to+// pass status to the list parser, so this can be set correctly.+// Another case with delayed values are colors. In compressed mode+// only processed values get compressed (other are left as written).++#include <typeinfo>+#include <tuple>++namespace Sass {+  using namespace Constants;+  using namespace Prelexer;++  Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source)+  {+    pstate.offset.column = 0;+    pstate.offset.line = 0;+    Parser p(ctx, pstate);+    p.source   = source ? source : beg;+    p.position = beg ? beg : p.source;+    p.end      = p.position + strlen(p.position);+    Block_Obj root = SASS_MEMORY_NEW(Block, pstate);+    p.block_stack.push_back(root);+    root->is_root(true);+    return p;+  }++  Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source)+  {+    pstate.offset.column = 0;+    pstate.offset.line = 0;+    Parser p(ctx, pstate);+    p.source   = source ? source : beg;+    p.position = beg ? beg : p.source;+    p.end      = end ? end : p.position + strlen(p.position);+    Block_Obj root = SASS_MEMORY_NEW(Block, pstate);+    p.block_stack.push_back(root);+    root->is_root(true);+    return p;+  }++   void Parser::advanceToNextToken() {+      lex < css_comments >(false);+      // advance to position+      pstate += pstate.offset;+      pstate.offset.column = 0;+      pstate.offset.line = 0;+    }++  Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source)+  {+    Parser p = Parser::from_c_str(beg, ctx, pstate, source);+    // ToDo: ruby sass errors on parent references+    // ToDo: remap the source-map entries somehow+    return p.parse_selector_list(false);+  }++  bool Parser::peek_newline(const char* start)+  {+    return peek_linefeed(start ? start : position)+           && ! peek_css<exactly<'{'>>(start);+  }++  Parser Parser::from_token(Token t, Context& ctx, ParserState pstate, const char* source)+  {+    Parser p(ctx, pstate);+    p.source   = source ? source : t.begin;+    p.position = t.begin ? t.begin : p.source;+    p.end      = t.end ? t.end : p.position + strlen(p.position);+    Block_Obj root = SASS_MEMORY_NEW(Block, pstate);+    p.block_stack.push_back(root);+    root->is_root(true);+    return p;+  }++  /* main entry point to parse root block */+  Block_Obj Parser::parse()+  {+    bool is_root = false;+    Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true);+    read_bom();++    // custom headers+    if (ctx.resources.size() == 1) {+    is_root = true;+      ctx.apply_custom_headers(&root, path, pstate);+    }++    block_stack.push_back(root);+    /* bool rv = */ parse_block_nodes(is_root);+    block_stack.pop_back();++    // update for end position+    root->update_pstate(pstate);++    if (position != end) {+      css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");+    }++    return root;+  }+++  // convenience function for block parsing+  // will create a new block ad-hoc for you+  // this is the base block parsing function+  Block_Obj Parser::parse_css_block(bool is_root)+  {++    // parse comments before block+    // lex < optional_css_comments >();++    // lex mandatory opener or error out+    if (!lex_css < exactly<'{'> >()) {+      css_error("Invalid CSS", " after ", ": expected \"{\", was ");+    }+    // create new block and push to the selector stack+    Block_Obj block = SASS_MEMORY_NEW(Block, pstate, 0, is_root);+    block_stack.push_back(block);++    if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");++    if (!lex_css < exactly<'}'> >()) {+      css_error("Invalid CSS", " after ", ": expected \"}\", was ");+    }++    // update for end position+    // this seems to be done somewhere else+    // but that fixed selector schema issue+    // block->update_pstate(pstate);++    // parse comments after block+    // lex < optional_css_comments >();++    block_stack.pop_back();++    return &block;+  }++  // convenience function for block parsing+  // will create a new block ad-hoc for you+  // also updates the `in_at_root` flag+  Block_Obj Parser::parse_block(bool is_root)+  {+    LOCAL_FLAG(in_at_root, is_root);+    return parse_css_block(is_root);+  }++  // the main block parsing function+  // parses stuff between `{` and `}`+  bool Parser::parse_block_nodes(bool is_root)+  {++    // loop until end of string+    while (position < end) {++      // we should be able to refactor this+      parse_block_comments();+      lex < css_whitespace >();++      if (lex < exactly<';'> >()) continue;+      if (peek < end_of_file >()) return true;+      if (peek < exactly<'}'> >()) return true;++      if (parse_block_node(is_root)) continue;++      parse_block_comments();++      if (lex_css < exactly<';'> >()) continue;+      if (peek_css < end_of_file >()) return true;+      if (peek_css < exactly<'}'> >()) return true;++      // illegal sass+      return false;+    }+    // return success+    return true;+  }++  // parser for a single node in a block+  // semicolons must be lexed beforehand+  bool Parser::parse_block_node(bool is_root) {++    Block_Obj block = block_stack.back();++    parse_block_comments();++    // throw away white-space+    // includes line comments+    lex < css_whitespace >();++    Lookahead lookahead_result;++    // also parse block comments++    // first parse everything that is allowed in functions+    if (lex < variable >(true)) { block->append(&parse_assignment()); }+    else if (lex < kwd_err >(true)) { block->append(&parse_error()); }+    else if (lex < kwd_dbg >(true)) { block->append(&parse_debug()); }+    else if (lex < kwd_warn >(true)) { block->append(&parse_warning()); }+    else if (lex < kwd_if_directive >(true)) { block->append(&parse_if_directive()); }+    else if (lex < kwd_for_directive >(true)) { block->append(&parse_for_directive()); }+    else if (lex < kwd_each_directive >(true)) { block->append(&parse_each_directive()); }+    else if (lex < kwd_while_directive >(true)) { block->append(&parse_while_directive()); }+    else if (lex < kwd_return_directive >(true)) { block->append(&parse_return_directive()); }++    // parse imports to process later+    else if (lex < kwd_import >(true)) {+      Scope parent = stack.empty() ? Scope::Rules : stack.back();+      if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) {+        if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20+          error("Import directives may not be used within control directives or mixins.", pstate);+        }+      }+      Import_Obj imp = parse_import();+      // if it is a url, we only add the statement+      if (!imp->urls().empty()) block->append(&imp);+      // process all resources now (add Import_Stub nodes)+      for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {+        block->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));+      }+    }++    else if (lex < kwd_extend >(true)) {+      Lookahead lookahead = lookahead_for_include(position);+      if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");+      Selector_Obj target;+      if (lookahead.has_interpolants) target = &parse_selector_schema(lookahead.found);+      else                            target = &parse_selector_list(true);+      block->append(SASS_MEMORY_NEW(Extension, pstate, &target));+    }++    // selector may contain interpolations which need delayed evaluation+    else if (!(lookahead_result = lookahead_for_selector(position)).error)+    { block->append(&parse_ruleset(lookahead_result, is_root)); }++    // parse multiple specific keyword directives+    else if (lex < kwd_media >(true)) { block->append(&parse_media_block()); }+    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()); }+    else if (lex < kwd_supports_directive >(true)) { block->append(&parse_supports_directive()); }+    else if (lex < kwd_mixin >(true)) { block->append(&parse_definition(Definition::MIXIN)); }+    else if (lex < kwd_function >(true)) { block->append(&parse_definition(Definition::FUNCTION)); }++    // ignore the @charset directive for now+    else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); }++    // 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 /* && block->is_root() */) {+      lex< css_whitespace >();+      if (position >= end) return true;+      css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was ");+    }+    // parse a declaration+    else+    {+      // ToDo: how does it handle parse errors?+      // maybe we are expected to parse something?+      Declaration_Obj decl = parse_declaration();+      decl->tabs(indentation);+      block->append(&decl);+      // maybe we have a "sub-block"+      if (peek< exactly<'{'> >()) {+        if (decl->is_indented()) ++ indentation;+        // parse a propset that rides on the declaration's property+        stack.push_back(Scope::Properties);+        decl->block(parse_block());+        stack.pop_back();+        if (decl->is_indented()) -- indentation;+      }+    }+    // something matched+    return true;+  }+  // EO parse_block_nodes++  // parse imports inside the+  Import_Obj Parser::parse_import()+  {+    Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);+    std::vector<std::pair<std::string,Function_Call_Obj>> to_import;+    bool first = true;+    do {+      while (lex< block_comment >());+      if (lex< quoted_string >()) {+        to_import.push_back(std::pair<std::string,Function_Call_Obj>(std::string(lexed), 0));+      }+      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);++        if (lex< quoted_string >()) {+          Expression_Obj the_url = &parse_string();+          args->append(SASS_MEMORY_NEW(Argument, the_url->pstate(), &the_url));+        }+        else if (String_Obj the_url = parse_url_function_argument()) {+          args->append(SASS_MEMORY_NEW(Argument, the_url->pstate(), &the_url));+        }+        else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {+          Expression_Obj the_url = parse_list(); // parse_interpolated_chunk(lexed);+          args->append(SASS_MEMORY_NEW(Argument, the_url->pstate(), &the_url));+        }+        else {+          error("malformed URL", pstate);+        }+        if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);+        to_import.push_back(std::pair<std::string, Function_Call_Obj>("", &result));+      }+      else {+        if (first) error("@import directive requires a url or quoted path", pstate);+        else error("expecting another url or quoted path in @import list", pstate);+      }+      first = false;+    } while (lex_css< exactly<','> >());++    if (!peek_css< alternatives< exactly<';'>, exactly<'}'>, end_of_file > >()) {+      List_Obj import_queries = parse_media_queries();+      imp->import_queries(import_queries);+    }++    for(auto location : to_import) {+      if (location.second) {+        imp->urls().push_back(&location.second);+      } else if (!ctx.call_importers(unquote(location.first), path, pstate, &imp)) {+        ctx.import_url(&imp, location.first, path);+      }+    }++    return imp;+  }++  Definition_Obj Parser::parse_definition(Definition::Type which_type)+  {+    std::string which_str(lexed);+    if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);+    std::string name(Util::normalize_underscores(lexed));+    if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))+    { error("Invalid function name \"" + name + "\".", pstate); }+    ParserState source_position_of_def = pstate;+    Parameters_Obj params = parse_parameters();+    if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin);+    else stack.push_back(Scope::Function);+    Block_Obj body = parse_block();+    stack.pop_back();+    return SASS_MEMORY_NEW(Definition, source_position_of_def, name, &params, &body, which_type);+  }++  Parameters_Obj Parser::parse_parameters()+  {+    std::string name(lexed);+    Position position = after_token;+    Parameters_Obj params = SASS_MEMORY_NEW(Parameters, pstate);+    if (lex_css< exactly<'('> >()) {+      // if there's anything there at all+      if (!peek_css< exactly<')'> >()) {+        do params->append(&parse_parameter());+        while (lex_css< exactly<','> >());+      }+      if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);+    }+    return params;+  }++  Parameter_Obj Parser::parse_parameter()+  {+    while (lex< alternatives < spaces, block_comment > >());+    lex < variable >();+    std::string name(Util::normalize_underscores(lexed));+    ParserState pos = pstate;+    Expression_Obj val;+    bool is_rest = false;+    while (lex< alternatives < spaces, block_comment > >());+    if (lex< exactly<':'> >()) { // there's a default value+      while (lex< block_comment >());+      val = parse_space_list();+    }+    else if (lex< exactly< ellipsis > >()) {+      is_rest = true;+    }+    return SASS_MEMORY_NEW(Parameter, pos, name, &val, is_rest);+  }++  Arguments_Obj Parser::parse_arguments()+  {+    std::string name(lexed);+    Position position = after_token;+    Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);+    if (lex_css< exactly<'('> >()) {+      // if there's anything there at all+      if (!peek_css< exactly<')'> >()) {+        do args->append(&parse_argument());+        while (lex_css< exactly<','> >());+      }+      if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);+    }+    return args;+  }++  Argument_Obj Parser::parse_argument()+  {+    if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) {+      position += 2;+      css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+    }++    Argument_Obj arg;+    if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {+      lex_css< variable >();+      std::string name(Util::normalize_underscores(lexed));+      ParserState p = pstate;+      lex_css< exactly<':'> >();+      Expression_Obj val = parse_space_list();+      arg = SASS_MEMORY_NEW(Argument, p, val, name);+    }+    else {+      bool is_arglist = false;+      bool is_keyword = false;+      Expression_Obj val = parse_space_list();+      List_Ptr l = SASS_MEMORY_CAST(List, val);+      if (lex_css< exactly< ellipsis > >()) {+        if (val->concrete_type() == Expression::MAP || (+           (l != NULL && l->separator() == SASS_HASH)+        )) is_keyword = true;+        else is_arglist = true;+      }+      arg = SASS_MEMORY_NEW(Argument, pstate, val, "", is_arglist, is_keyword);+    }+    return arg;+  }++  Assignment_Obj Parser::parse_assignment()+  {+    std::string name(Util::normalize_underscores(lexed));+    ParserState var_source_position = pstate;+    if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);+    if (peek_css< alternatives < exactly<';'>, end_of_file > >()) {+      css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+    }+    Expression_Obj val;+    Lookahead lookahead = lookahead_for_value(position);+    if (lookahead.has_interpolants && lookahead.found) {+      val = &parse_value_schema(lookahead.found);+    } else {+      val = parse_list();+    }+    bool is_default = false;+    bool is_global = false;+    while (peek< alternatives < default_flag, global_flag > >()) {+      if (lex< default_flag >()) is_default = true;+      else if (lex< global_flag >()) is_global = true;+    }+    return SASS_MEMORY_NEW(Assignment, var_source_position, name, val, is_default, is_global);+  }++  // a ruleset connects a selector and a block+  Ruleset_Obj Parser::parse_ruleset(Lookahead lookahead, bool is_root)+  {+    // make sure to move up the the last position+    lex < optional_css_whitespace >(false, true);+    // 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(is_root));+    else ruleset->selector(&parse_selector_schema(lookahead.found));+    // then parse the inner block+    stack.push_back(Scope::Rules);+    ruleset->block(parse_block());+    stack.pop_back();+    // update for end position+    ruleset->update_pstate(pstate);+    ruleset->block()->update_pstate(pstate);+    // inherit is_root from parent block+    // need this info for sanity checks+    ruleset->is_root(is_root);+    // return AST Node+    return ruleset;+  }++  // parse a selector schema that will be evaluated in the eval stage+  // uses a string schema internally to do the actual schema handling+  // in the eval stage we will be re-parse it into an actual selector+  Selector_Schema_Obj Parser::parse_selector_schema(const char* end_of_selector)+  {+    // move up to the start+    lex< optional_spaces >();+    const char* i = position;+    // selector schema re-uses string schema implementation+    String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+    // the selector schema is pretty much just a wrapper for the string schema+    Selector_Schema_Ptr selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema);+    selector_schema->media_block(last_media_block);++    // process until end+    while (i < end_of_selector) {+      // try to parse mutliple 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) {+          std::string parsed(i, p);+          String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed);+          pstate += Offset(parsed);+          str->update_pstate(pstate);+          schema->append(&str);+        }++        // check if the interpolation only contains white-space (error out)+        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;+          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+        }+        // skip over all nested inner interpolations up to our own delimiter+        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);+        // pass inner expression to the parser to resolve nested interpolations+        pstate.add(p, p+2);+        Expression_Obj interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();+        // set status on the list expression+        interpolant->is_interpolant(true);+        // schema->has_interpolants(true);+        // add to the string schema+        schema->append(&interpolant);+        // advance parser state+        pstate.add(p+2, j);+        // advance position+        i = j;+      }+      // no more interpolants have been found+      // add the last segment if there is one+      else {+        // make sure to add the last bits of the string up to the end (if any)+        if (i < end_of_selector) {+          std::string parsed(i, end_of_selector);+          String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed);+          pstate += Offset(parsed);+          str->update_pstate(pstate);+          i = end_of_selector;+          schema->append(&str);+        }+        // exit loop+      }+    }+    // EO until eos++    // update position+    position = i;++    // update for end position+    selector_schema->update_pstate(pstate);+    schema->update_pstate(pstate);++    after_token = before_token = pstate;++    // return parsed result+    return selector_schema;+  }+  // EO parse_selector_schema++  void Parser::parse_charset_directive()+  {+    lex <+      sequence <+        quoted_string,+        optional_spaces,+        exactly <';'>+      >+    >();+  }++  // called after parsing `kwd_include_directive`+  Mixin_Call_Obj Parser::parse_include_directive()+  {+    // lex identifier into `lexed` var+    lex_identifier(); // may error out+    // 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);+    // parse mandatory arguments+    call->arguments(parse_arguments());+    // parse optional block+    if (peek < exactly <'{'> >()) {+      call->block(parse_block());+    }+    // 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 in_root)+  {+    bool reloop = true;+    bool had_linefeed = false;+    Complex_Selector_Obj sel;+    Selector_List_Obj group = SASS_MEMORY_NEW(Selector_List, pstate);+    group->media_block(last_media_block);+    do {+      reloop = false;++      had_linefeed = had_linefeed || peek_newline();++      if (peek_css< class_char < selector_list_delims > >())+        break; // in case there are superfluous commas at the end+++      // now parse the complex selector+      sel = parse_complex_selector(in_root);++      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 in_root)+  {++    String_Ptr reference = 0;+    lex < block_comment >();+    advanceToNextToken();+    Complex_Selector_Obj sel = SASS_MEMORY_NEW(Complex_Selector, pstate);++    // 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();+    }++    // check for end of file condition+    if (peek < end_of_file >()) return 0;++    // parse combinator between lhs and rhs+    Complex_Selector::Combinator combinator;+    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?+    }+    else /* if (lex< zero >()) */   combinator = Complex_Selector::ANCESTOR_OF;++    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() && !in_at_root && !in_root) {+      // 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<exactly<'{'>>()) {+      seq->has_line_break(peek_newline());+    }++    // EO while true+    return seq;++  }+  // EO parse_compound_selector++  Simple_Selector_Obj Parser::parse_simple_selector()+  {+    lex < css_comments >(false);+    if (lex< class_name >()) {+      return SASS_MEMORY_NEW(Class_Selector, pstate, lexed);+    }+    else if (lex< id_name >()) {+      return SASS_MEMORY_NEW(Id_Selector, pstate, lexed);+    }+    else if (lex< quoted_string >()) {+      return SASS_MEMORY_NEW(Element_Selector, pstate, unquote(lexed));+    }+    else if (lex< alternatives < variable, number, static_reference_combinator > >()) {+      return SASS_MEMORY_NEW(Element_Selector, pstate, lexed);+    }+    else if (peek< pseudo_not >()) {+      return &parse_negated_selector();+    }+    else if (peek< re_pseudo_selector >()) {+      return &parse_pseudo_selector();+    }+    else if (peek< exactly<':'> >()) {+      return &parse_pseudo_selector();+    }+    else if (lex < exactly<'['> >()) {+      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;+    }+    // failed+    return 0;+  }++  Wrapped_Selector_Obj Parser::parse_negated_selector()+  {+    lex< pseudo_not >();+    std::string name(lexed);+    ParserState nsource_position = pstate;+    Selector_List_Obj negated = parse_selector_list(true);+    if (!lex< exactly<')'> >()) {+      error("negated selector is missing ')'", pstate);+    }+    name.erase(name.size() - 1);+    return SASS_MEMORY_NEW(Wrapped_Selector, nsource_position, name, &negated);+  }++  // 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<'('> >+        > >())+    {++      std::string name(lexed);+      name.erase(name.size() - 1);+      ParserState p = pstate;++      // 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_Ptr expr = SASS_MEMORY_NEW(String_Constant, pstate, lexed);+        if (expr && lex_css< exactly<')'> >()) {+          expr->can_compress_whitespace(true);+          return SASS_MEMORY_NEW(Pseudo_Selector, p, name, expr);+        }+      }+      else if (Selector_List_Obj wrapped = parse_selector_list(true)) {+        if (wrapped && lex_css< exactly<')'> >()) {+          return SASS_MEMORY_NEW(Wrapped_Selector, p, name, &wrapped);+        }+      }++    }+    // EO if pseudo selector++    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 ");+    }++    css_error("Invalid CSS", " after ", ": expected \")\", was ");++    // unreachable statement+    return 0;+  }++  Attribute_Selector_Obj Parser::parse_attribute_selector()+  {+    ParserState p = pstate;+    if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);+    std::string name(lexed);+    if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0);+    if (!lex_css< alternatives< exact_match, class_match, dash_match,+                                prefix_match, suffix_match, substring_match > >()) {+      error("invalid operator in attribute selector for " + name, pstate);+    }+    std::string matcher(lexed);++    String_Obj value = 0;+    if (lex_css< identifier >()) {+      value = SASS_MEMORY_NEW(String_Constant, p, lexed);+    }+    else if (lex_css< quoted_string >()) {+      value = &parse_interpolated_chunk(lexed, true); // needed!+    }+    else {+      error("expected a string constant or identifier in attribute selector for " + name, pstate);+    }++    if (!lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate);+    return SASS_MEMORY_NEW(Attribute_Selector, p, name, matcher, value);+  }++  /* parse block comment and add to block */+  void Parser::parse_block_comments()+  {+    Block_Obj block = block_stack.back();++    while (lex< block_comment >()) {+      bool is_important = lexed.begin[2] == '!';+      // flag on second param is to skip loosely over comments+      String_Obj contents = parse_interpolated_chunk(lexed, true);+      block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));+    }+  }++  Declaration_Obj Parser::parse_declaration() {+    String_Obj prop;+    if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {+      prop = parse_identifier_schema();+    }+    else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {+      prop = SASS_MEMORY_NEW(String_Constant, pstate, lexed);+    }+    else {+      css_error("Invalid CSS", " after ", ": expected \"}\", was ");+    }+    bool is_indented = true;+    const std::string property(lexed);+    if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);+    lex < css_comments >(false);+    if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);+    if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty+    if (peek_css< static_value >()) {+      return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, &parse_static_value()/*, lex<kwd_important>()*/);+    }+    else {+      Expression_Obj value;+      Lookahead lookahead = lookahead_for_value(position);+      if (lookahead.found) {+        if (lookahead.has_interpolants) {+          value = &parse_value_schema(lookahead.found);+        } else {+          value = &parse_list(DELAYED);+        }+      }+      else {+        value = &parse_list(DELAYED);+        if (List_Ptr list = SASS_MEMORY_CAST(List, value)) {+          if (list->length() == 0 && !peek< exactly <'{'> >()) {+            css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+          }+        }+      }+      lex < css_comments >(false);+      Declaration_Obj decl = SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/);+      decl->is_indented(is_indented);+      decl->update_pstate(pstate);+      return decl;+    }+  }++  // parse +/- and return false if negative+  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()+  {+    Expression_Obj key = parse_list();+    List_Obj map = SASS_MEMORY_NEW(List, pstate, 0, SASS_HASH);++    // it's not a map so return the lexed value as a list value+    if (!lex_css< exactly<':'> >())+    { return key; }++    Expression_Obj value = parse_space_list();++    map->append(key);+    map->append(value);++    while (lex_css< exactly<','> >())+    {+      // allow trailing commas - #495+      if (peek_css< exactly<')'> >(position))+      { break; }++      Expression_Obj key = parse_space_list();++      if (!(lex< exactly<':'> >()))+      { css_error("Invalid CSS", " after ", ": expected \":\", was "); }++      Expression_Obj value = parse_space_list();++      map->append(key);+      map->append(value);+    }++    ParserState ps = map->pstate();+    ps.offset = pstate - ps + pstate.offset;+    map->pstate(ps);++    return &map;+  }++  // parse list returns either a space separated list,+  // a comma separated list or any bare expression found.+  // so to speak: we unwrap items from lists if possible here!+  Expression_Obj Parser::parse_list(bool delayed)+  {+    return parse_comma_list(delayed);+  }++  // will return singletons unwrapped+  Expression_Obj Parser::parse_comma_list(bool delayed)+  {+    // check if we have an empty list+    // return the empty list as such+    if (peek_css< alternatives <+          // exactly<'!'>,+          exactly<';'>,+          exactly<'}'>,+          exactly<'{'>,+          exactly<')'>,+          exactly<':'>,+          end_of_file,+          exactly<ellipsis>,+          default_flag,+          global_flag+        > >(position))+    {+      // return an empty list (nothing to delay)+      return SASS_MEMORY_NEW(List, pstate, 0);+    }++    // now try to parse a space list+    Expression_Obj list = parse_space_list();+    // if it's a singleton, return it (don't wrap it)+    if (!peek_css< exactly<','> >(position)) {+      // set_delay doesn't apply to list children+      // so this will only undelay single values+      if (!delayed) list->set_delayed(false);+      return list;+    }++    // if we got so far, we actually do have a comma list+    List_Obj comma_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA);+    // wrap the first expression+    comma_list->append(list);++    while (lex_css< exactly<','> >())+    {+      // check for abort condition+      if (peek_css< alternatives <+            exactly<';'>,+            exactly<'}'>,+            exactly<'{'>,+            exactly<')'>,+            exactly<':'>,+            end_of_file,+            exactly<ellipsis>,+            default_flag,+            global_flag+          > >(position)+      ) { break; }+      // otherwise add another expression+      comma_list->append(parse_space_list());+    }+    // return the list+    return &comma_list;+  }+  // EO parse_comma_list++  // will return singletons unwrapped+  Expression_Obj Parser::parse_space_list()+  {+    Expression_Obj disj1 = parse_disjunction();+    // if it's a singleton, return it (don't wrap it)+    if (peek_css< alternatives <+          // exactly<'!'>,+          exactly<';'>,+          exactly<'}'>,+          exactly<'{'>,+          exactly<')'>,+          exactly<','>,+          exactly<':'>,+          end_of_file,+          exactly<ellipsis>,+          default_flag,+          global_flag+        > >(position)+    ) { return disj1; }++    List_Obj space_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_SPACE);+    space_list->append(disj1);++    while (!(peek_css< alternatives <+               // exactly<'!'>,+               exactly<';'>,+               exactly<'}'>,+               exactly<'{'>,+               exactly<')'>,+               exactly<','>,+               exactly<':'>,+               end_of_file,+               exactly<ellipsis>,+               default_flag,+               global_flag+           > >(position)) && peek_css< optional_css_whitespace >() != end+    ) {+      // the space is parsed implicitly?+      space_list->append(parse_disjunction());+    }+    // return the list+    return &space_list;+  }+  // EO parse_space_list++  // parse logical OR operation+  Expression_Obj Parser::parse_disjunction()+  {+    advanceToNextToken();+    ParserState state(pstate);+    // parse the left hand side conjunction+    Expression_Obj conj = parse_conjunction();+    // parse multiple right hand sides+    std::vector<Expression_Obj> operands;+    while (lex_css< kwd_or >())+      operands.push_back(parse_conjunction());+    // if it's a singleton, return it directly+    if (operands.size() == 0) return conj;+    // fold all operands into one binary expression+    Expression_Obj ex = fold_operands(conj, operands, { Sass_OP::OR });+    state.offset = pstate - state + pstate.offset;+    ex->pstate(state);+    return ex;+  }+  // EO parse_disjunction++  // parse logical AND operation+  Expression_Obj Parser::parse_conjunction()+  {+    advanceToNextToken();+    ParserState state(pstate);+    // parse the left hand side relation+    Expression_Obj rel = parse_relation();+    // parse multiple right hand sides+    std::vector<Expression_Obj> operands;+    while (lex_css< kwd_and >()) {+      operands.push_back(&parse_relation());+    }+    // if it's a singleton, return it directly+    if (operands.size() == 0) return rel;+    // fold all operands into one binary expression+    Expression_Obj ex = fold_operands(rel, operands, { Sass_OP::AND });+    state.offset = pstate - state + pstate.offset;+    ex->pstate(state);+    return ex;+  }+  // EO parse_conjunction++  // parse comparison operations+  Expression_Obj Parser::parse_relation()+  {+    advanceToNextToken();+    ParserState state(pstate);+    // parse the left hand side expression+    Expression_Obj lhs = parse_expression();+    std::vector<Expression_Obj> operands;+    std::vector<Operand> operators;+    // if it's a singleton, return it (don't wrap it)+    while (peek< alternatives <+            kwd_eq,+            kwd_neq,+            kwd_gte,+            kwd_gt,+            kwd_lte,+            kwd_lt+          > >(position))+    {+      // is directly adjancent to expression?+      bool left_ws = peek < css_comments >() != NULL;+      // parse the operator+      enum Sass_OP op+      = lex<kwd_eq>()  ? Sass_OP::EQ+      : lex<kwd_neq>() ? Sass_OP::NEQ+      : lex<kwd_gte>() ? Sass_OP::GTE+      : lex<kwd_lte>() ? Sass_OP::LTE+      : lex<kwd_gt>()  ? Sass_OP::GT+      : lex<kwd_lt>()  ? Sass_OP::LT+      // we checked the possibilities on top of fn+      :                  Sass_OP::EQ;+      // is directly adjacent to expression?+      bool right_ws = peek < css_comments >() != NULL;+      operators.push_back({ op, left_ws, right_ws });+      operands.push_back(&parse_expression());+      left_ws = peek < css_comments >() != NULL;+    }+    // we are called recursively for list, so we first+    // fold inner binary expression which has delayed+    // correctly set to zero. After folding we also unwrap+    // single nested items. So we cannot set delay on the+    // returned result here, as we have lost nestings ...+    Expression_Obj ex = fold_operands(lhs, operands, operators);+    state.offset = pstate - state + pstate.offset;+    ex->pstate(state);+    return ex;+  }+  // parse_relation++  // parse expression valid for operations+  // called from parse_relation+  // called from parse_for_directive+  // called from parse_media_expression+  // parse addition and subtraction operations+  Expression_Obj Parser::parse_expression()+  {+    advanceToNextToken();+    ParserState state(pstate);+    // parses multiple add and subtract operations+    // NOTE: make sure that identifiers starting with+    // NOTE: dashes do NOT count as subtract operation+    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+          (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))+    { return lhs; }++    std::vector<Expression_Obj> operands;+    std::vector<Operand> operators;+    bool left_ws = peek < css_comments >() != NULL;+    while (+      lex_css< exactly<'+'> >() ||++      (+      ! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position)+      && lex_css< sequence< negate< digit >, exactly<'-'> > >()+      )++    ) {++      bool right_ws = peek < css_comments >() != NULL;+      operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws });+      operands.push_back(&parse_operators());+      left_ws = peek < css_comments >() != NULL;+    }++    if (operands.size() == 0) return lhs;+    Expression_Obj ex = fold_operands(lhs, operands, operators);+    state.offset = pstate - state + pstate.offset;+    ex->pstate(state);+    return ex;+  }++  // parse addition and subtraction operations+  Expression_Obj Parser::parse_operators()+  {+    advanceToNextToken();+    ParserState state(pstate);+    Expression_Obj factor = parse_factor();+    // if it's a singleton, return it (don't wrap it)+    std::vector<Expression_Obj> operands; // factors+    std::vector<Operand> operators; // ops+    // lex operations to apply to lhs+    const char* left_ws = peek < css_comments >();+    while (lex_css< class_char< static_ops > >()) {+      const char* right_ws = peek < css_comments >();+      switch(*lexed.begin) {+        case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break;+        case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break;+        case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break;+        default: throw std::runtime_error("unknown static op parsed"); break;+      }+      operands.push_back(parse_factor());+      left_ws = peek < css_comments >();+    }+    // operands and operators to binary expression+    Expression_Obj ex = fold_operands(factor, operands, operators);+    state.offset = pstate - state + pstate.offset;+    ex->pstate(state);+    return ex;+  }+  // EO parse_operators+++  // called from parse_operators+  // called from parse_value_schema+  Expression_Obj Parser::parse_factor()+  {+    lex < css_comments >(false);+    if (lex_css< exactly<'('> >()) {+      // parse_map may return a list+      Expression_Obj value = parse_map();+      // lex the expected closing parenthesis+      if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);+      // expression can be evaluated+      return &value;+    }+    // string may be interpolated+    // if (lex< quoted_string >()) {+    //   return &parse_string();+    // }+    else if (peek< ie_property >()) {+      return &parse_ie_property();+    }+    else if (peek< ie_keyword_arg >()) {+      return &parse_ie_keyword_arg();+    }+    else if (peek< sequence < calc_fn_call, exactly <'('> > >()) {+      return &parse_calc_function();+    }+    else if (lex < functional_schema >()) {+      return &parse_function_call_schema();+    }+    else if (lex< identifier_schema >()) {+      String_Obj string = parse_identifier_schema();+      if (String_Schema_Ptr schema = SASS_MEMORY_CAST(String_Schema, string)) {+        if (lex < exactly < '(' > >()) {+          schema->append(&parse_list());+          lex < exactly < ')' > >();+        }+      }+      return &string;+    }+    else if (peek< sequence< uri_prefix, W, real_uri_value > >()) {+      return &parse_url_function_string();+    }+    else if (peek< re_functional >()) {+      return &parse_function_call();+    }+    else if (lex< exactly<'+'> >()) {+      Unary_Expression_Ptr 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());+      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());+      if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());+      return ex;+    }+    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();+    }+  }++  // parse one value for a list+  Expression_Obj Parser::parse_value()+  {+    lex< css_comments >(false);+    if (lex< ampersand >())+    {+      return SASS_MEMORY_NEW(Parent_Selector, pstate); }++    if (lex< kwd_important >())+    { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); }++    // parse `10%4px` into separated items and not a schema+    if (lex< sequence < percentage, lookahead < number > > >())+    { return SASS_MEMORY_NEW(Textual, pstate, Textual::PERCENTAGE, lexed); }++    if (lex< sequence < number, lookahead< sequence < op, number > > > >())+    { return SASS_MEMORY_NEW(Textual, pstate, Textual::NUMBER, lexed); }++    // string may be interpolated+    if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >())+    { return &parse_string(); }++    if (const char* stop = peek< value_schema >())+    { return &parse_value_schema(stop); }++    // string may be interpolated+    if (lex< quoted_string >())+    { return &parse_string(); }++    if (lex< kwd_true >())+    { return SASS_MEMORY_NEW(Boolean, pstate, true); }++    if (lex< kwd_false >())+    { return SASS_MEMORY_NEW(Boolean, pstate, false); }++    if (lex< kwd_null >())+    { return SASS_MEMORY_NEW(Null, pstate); }++    if (lex< identifier >()) {+      return SASS_MEMORY_NEW(String_Constant, pstate, lexed);+    }++    if (lex< percentage >())+    { return SASS_MEMORY_NEW(Textual, pstate, Textual::PERCENTAGE, lexed); }++    // match hex number first because 0x000 looks like a number followed by an identifier+    if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >())+    { return SASS_MEMORY_NEW(Textual, pstate, Textual::HEX, lexed); }++    if (lex< sequence < exactly <'#'>, identifier > >())+    { return SASS_MEMORY_NEW(String_Quoted, pstate, lexed); }++    // also handle the 10em- foo special case+    // alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression+    if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >())+    { return SASS_MEMORY_NEW(Textual, pstate, Textual::DIMENSION, lexed); }++    if (lex< sequence< static_component, one_plus< strict_identifier > > >())+    { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); }++    if (lex< number >())+    { return SASS_MEMORY_NEW(Textual, pstate, Textual::NUMBER, lexed); }++    if (lex< variable >())+    { return SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed)); }++    // Special case handling for `%` proceeding an interpolant.+    if (lex< sequence< exactly<'%'>, optional< percentage > > >())+    { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); }++    css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");++    // unreachable statement+    return 0;+  }++  // this parses interpolation inside other strings+  // means the result should later be quoted again+  String_Obj Parser::parse_interpolated_chunk(Token chunk, bool constant)+  {+    const char* i = chunk.begin;+    // see if there any interpolants+    const char* p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :+                    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));+      if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');+      return str_quoted;+    }++    String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+    schema->is_interpolant(true);+    while (i < chunk.end) {+      p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :+          find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);+      if (p) {+        if (i < p) {+          // accumulate the preceding segment if it's nonempty+          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, p)));+        }+        // we need to skip anything inside strings+        // create a new target in parser/prelexer+        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;+          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+        }+        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace+        if (j) { --j;+          // parse the interpolant and accumulate it+          Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();+          interp_node->is_interpolant(true);+          schema->append(&interp_node);+          i = j;+        }+        else {+          // throw an error if the interpolant is unterminated+          error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);+        }+      }+      else { // no interpolants left; add the last segment if nonempty+        // check if we need quotes here (was not sure after merge)+        if (i < chunk.end) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, chunk.end)));+        break;+      }+      ++ i;+    }++    return schema;+  }++  String_Constant_Obj Parser::parse_static_value()+  {+    lex< static_value >();+    Token str(lexed);+    // static values always have trailing white-+    // space and end delimiter (\s*[;]$) included+    -- pstate.offset.column;+    --str.end;+    --position;++    String_Constant_Ptr str_node = SASS_MEMORY_NEW(String_Constant, pstate, str.time_wspace());+    return str_node;+  }++  String_Obj Parser::parse_string()+  {+    return parse_interpolated_chunk(Token(lexed));+  }++  String_Obj Parser::parse_ie_property()+  {+    lex< ie_property >();+    Token str(lexed);+    const char* i = str.begin;+    // see if there any interpolants+    const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(str.begin, str.end);+    if (!p) {+      return SASS_MEMORY_NEW(String_Quoted, pstate, std::string(str.begin, str.end));+    }++    String_Schema_Ptr 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) {+        if (i < p) {+          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, p))); // accumulate the preceding segment if it's nonempty+        }+        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;+          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+        }+        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace+        if (j) {+          // parse the interpolant and accumulate it+          Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();+          interp_node->is_interpolant(true);+          schema->append(&interp_node);+          i = j;+        }+        else {+          // throw an error if the interpolant is unterminated+          error("unterminated interpolant inside IE function " + str.to_string(), pstate);+        }+      }+      else { // no interpolants left; add the last segment if nonempty+        if (i < str.end) {+          schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, str.end)));+        }+        break;+      }+    }+    return schema;+  }++  String_Obj Parser::parse_ie_keyword_arg()+  {+    String_Schema_Ptr 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 {+      lex< alternatives< identifier_schema, identifier > >();+      kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+    }+    lex< exactly<'='> >();+    kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+    if (peek< variable >()) kwd_arg->append(&parse_list());+    else if (lex< number >()) kwd_arg->append(SASS_MEMORY_NEW(Textual, pstate, Textual::NUMBER, Util::normalize_decimals(lexed)));+    else if (peek < ie_keyword_arg_value >()) { kwd_arg->append(&parse_list()); }+    return kwd_arg;+  }++  String_Schema_Obj Parser::parse_value_schema(const char* stop)+  {+    // initialize the string schema object to add tokens+    String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);++    if (peek<exactly<'}'>>()) {+      css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+    }++    const char* e = 0;+    const char* ee = end;+    end = stop;+    size_t num_items = 0;+    bool need_space = false;+    while (position < stop) {+      // parse space between tokens+      if (lex< spaces >() && num_items) {+        need_space = true;+      }+      if (need_space) {+        need_space = false;+        // schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));+      }+      if ((e = peek< re_functional >()) && e < stop) {+        schema->append(&parse_function_call());+      }+      // lex an interpolant /#{...}/+      else if (lex< exactly < hash_lbrace > >()) {+        // Try to lex static expression first+        if (peek< exactly< rbrace > >()) {+          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+        }+        Expression_Obj ex = 0;+        if (lex< re_static_expression >()) {+          ex = SASS_MEMORY_NEW(String_Constant, pstate, lexed);+        } else {+          ex = parse_list();+        }+        ex->is_interpolant(true);+        schema->append(ex);+        if (!lex < exactly < rbrace > >()) {+          css_error("Invalid CSS", " after ", ": expected \"}\", was ");+        }+      }+      // lex some string constants or other valid token+      // Note: [-+] chars are left over from i.e. `#{3}+3`+      else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) {+        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+      }+      // lex a quoted string+      else if (lex< quoted_string >()) {+        // need_space = true;+        // if (schema->length()) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));+        // else need_space = true;+        schema->append(&parse_string());+        if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) {+          // need_space = true;+        }+        if (peek < exactly < '-' > >()) break;+      }+      else if (lex< sequence < identifier > >()) {+        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+        if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) {+           // need_space = true;+        }+      }+      // lex (normalized) variable+      else if (lex< variable >()) {+        std::string name(Util::normalize_underscores(lexed));+        schema->append(SASS_MEMORY_NEW(Variable, pstate, name));+      }+      // lex percentage value+      else if (lex< percentage >()) {+        schema->append(SASS_MEMORY_NEW(Textual, pstate, Textual::PERCENTAGE, lexed));+      }+      // lex dimension value+      else if (lex< dimension >()) {+        schema->append(SASS_MEMORY_NEW(Textual, pstate, Textual::DIMENSION, lexed));+      }+      // lex number value+      else if (lex< number >()) {+        schema->append( SASS_MEMORY_NEW(Textual, pstate, Textual::NUMBER, lexed));+      }+      // lex hex color value+      else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {+        schema->append(SASS_MEMORY_NEW(Textual, pstate, Textual::HEX, lexed));+      }+      else if (lex< sequence < exactly <'#'>, identifier > >()) {+        schema->append(SASS_MEMORY_NEW(String_Quoted, pstate, lexed));+      }+      // lex a value in parentheses+      else if (peek< parenthese_scope >()) {+        schema->append(&parse_factor());+      }+      else {+        break;+      }+      ++num_items;+    }+    if (position != stop) {+      schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(position, stop)));+      position = stop;+    }+    end = ee;+    return schema;+  }++  // this parses interpolation outside other strings+  // means the result must not be quoted again later+  String_Obj Parser::parse_identifier_schema()+  {+    Token id(lexed);+    const char* i = id.begin;+    // see if there any interpolants+    const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(id.begin, id.end);+    if (!p) {+      return SASS_MEMORY_NEW(String_Constant, pstate, std::string(id.begin, id.end));+    }++    String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);+    while (i < id.end) {+      p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, id.end);+      if (p) {+        if (i < p) {+          // accumulate the preceding segment if it's nonempty+          const char* o = position; position = i;+          schema->append(&parse_value_schema(p));+          position = o;+        }+        // we need to skip anything inside strings+        // create a new target in parser/prelexer+        if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p;+          css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+        }+        const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace+        if (j) {+          // parse the interpolant and accumulate it+          Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(DELAYED);+          interp_node->is_interpolant(true);+          schema->append(interp_node);+          // schema->has_interpolants(true);+          i = j;+        }+        else {+          // throw an error if the interpolant is unterminated+          error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);+        }+      }+      else { // no interpolants left; add the last segment if nonempty+        if (i < end) {+          const char* o = position; position = i;+          schema->append(&parse_value_schema(id.end));+          position = o;+        }+        break;+      }+    }+    return schema ? schema.detach() : 0;+  }++  // calc functions should preserve arguments+  Function_Call_Obj Parser::parse_calc_function()+  {+    lex< identifier >();+    std::string name(lexed);+    ParserState call_pos = pstate;+    lex< exactly<'('> >();+    ParserState arg_pos = pstate;+    const char* arg_beg = position;+    parse_list();+    const char* arg_end = position;+    lex< skip_over_scopes <+          exactly < '(' >,+          exactly < ')' >+        > >();++    Argument_Obj arg = SASS_MEMORY_NEW(Argument, arg_pos, &parse_interpolated_chunk(Token(arg_beg, arg_end)));+    Arguments_Obj args = SASS_MEMORY_NEW(Arguments, arg_pos);+    args->append(&arg);+    return SASS_MEMORY_NEW(Function_Call, call_pos, name, args);+  }++  String_Obj Parser::parse_url_function_string()+  {+    std::string prefix("");+    if (lex< uri_prefix >()) {+      prefix = std::string(lexed);+    }++    lex < optional_spaces >();+    String_Obj url_string = parse_url_function_argument();++    std::string suffix("");+    if (lex< real_uri_suffix >()) {+      suffix = std::string(lexed);+    }++    std::string uri("");+    if (&url_string) {+      uri = url_string->to_string({ NESTED, 5 });+    }++    if (String_Schema_Ptr schema = dynamic_cast<String_Schema_Ptr>(&url_string)) {+      String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate);+      res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix));+      res->append(schema);+      res->append(SASS_MEMORY_NEW(String_Constant, pstate, suffix));+      return &res;+    } else {+      std::string res = prefix + uri + suffix;+      return SASS_MEMORY_NEW(String_Constant, pstate, res);+    }+  }++  String_Obj Parser::parse_url_function_argument()+  {+    const char* p = position;++    std::string uri("");+    if (lex< real_uri_value >(false)) {+      uri = lexed.to_string();+    }++    if (peek< exactly< hash_lbrace > >()) {+      const char* pp = position;+      // TODO: error checking for unclosed interpolants+      while (pp && peek< exactly< hash_lbrace > >(pp)) {+        pp = sequence< interpolant, real_uri_value >(pp);+      }+      position = pp;+      return &parse_interpolated_chunk(Token(p, position));+    }+    else if (uri != "") {+      std::string res = Util::rtrim(uri);+      return SASS_MEMORY_NEW(String_Constant, pstate, res);+    }++    return 0;+  }++  Function_Call_Obj Parser::parse_function_call()+  {+    lex< identifier >();+    std::string name(lexed);++    ParserState call_pos = pstate;+    Arguments_Obj args = parse_arguments();+    return SASS_MEMORY_NEW(Function_Call, call_pos, name, args);+  }++  Function_Call_Schema_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);+  }++  Content_Obj Parser::parse_content_directive()+  {+    return SASS_MEMORY_NEW(Content, pstate);+  }++  If_Obj Parser::parse_if_directive(bool else_if)+  {+    stack.push_back(Scope::Control);+    ParserState if_source_position = pstate;+    bool root = block_stack.back()->is_root();+    Expression_Obj predicate = parse_list();+    Block_Obj block = parse_block(root);+    Block_Obj alternative = NULL;++    // only throw away comment if we parse a case+    // we want all other comments to be parsed+    if (lex_css< elseif_directive >()) {+      alternative = SASS_MEMORY_NEW(Block, pstate);+      alternative->append(&parse_if_directive(true));+    }+    else if (lex_css< kwd_else_directive >()) {+      alternative = &parse_block(root);+    }+    stack.pop_back();+    return SASS_MEMORY_NEW(If, if_source_position, predicate, block, alternative);+  }++  For_Obj Parser::parse_for_directive()+  {+    stack.push_back(Scope::Control);+    ParserState for_source_position = pstate;+    bool root = block_stack.back()->is_root();+    lex_variable();+    std::string var(Util::normalize_underscores(lexed));+    if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);+    Expression_Obj lower_bound = parse_expression();+    bool inclusive = false;+    if (lex< kwd_through >()) inclusive = true;+    else if (lex< kwd_to >()) inclusive = false;+    else                  error("expected 'through' or 'to' keyword in @for directive", pstate);+    Expression_Obj upper_bound = parse_expression();+    Block_Obj body = parse_block(root);+    stack.pop_back();+    return SASS_MEMORY_NEW(For, for_source_position, var, lower_bound, upper_bound, body, inclusive);+  }++  // helper to parse a var token+  Token Parser::lex_variable()+  {+    // peek for dollar sign first+    if (!peek< exactly <'$'> >()) {+      css_error("Invalid CSS", " after ", ": expected \"$\", was ");+    }+    // we expect a simple identifier as the call name+    if (!lex< sequence < exactly <'$'>, identifier > >()) {+      lex< exactly <'$'> >(); // move pstate and position up+      css_error("Invalid CSS", " after ", ": expected identifier, was ");+    }+    // return object+    return token;+  }+  // helper to parse identifier+  Token Parser::lex_identifier()+  {+    // we expect a simple identifier as the call name+    if (!lex< identifier >()) { // ToDo: pstate wrong?+      css_error("Invalid CSS", " after ", ": expected identifier, was ");+    }+    // return object+    return token;+  }++  Each_Obj Parser::parse_each_directive()+  {+    stack.push_back(Scope::Control);+    ParserState each_source_position = pstate;+    bool root = block_stack.back()->is_root();+    std::vector<std::string> vars;+    lex_variable();+    vars.push_back(Util::normalize_underscores(lexed));+    while (lex< exactly<','> >()) {+      if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);+      vars.push_back(Util::normalize_underscores(lexed));+    }+    if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);+    Expression_Obj list = parse_list();+    Block_Obj body = parse_block(root);+    stack.pop_back();+    return SASS_MEMORY_NEW(Each, each_source_position, vars, list, body);+  }++  // called after parsing `kwd_while_directive`+  While_Obj Parser::parse_while_directive()+  {+    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);+    // parse mandatory predicate+    Expression_Obj predicate = parse_list();+    call->predicate(predicate);+    // parse mandatory block+    call->block(parse_block(root));+    // return ast node+    stack.pop_back();+    // return ast node+    return call.detach();+  }++  // EO parse_while_directive+  Media_Block_Obj Parser::parse_media_block()+  {+    stack.push_back(Scope::Media);+    Media_Block_Obj media_block = SASS_MEMORY_NEW(Media_Block, pstate, 0, 0);++    media_block->media_queries(parse_media_queries());++    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;+    stack.pop_back();+    return media_block.detach();+  }++  List_Obj Parser::parse_media_queries()+  {+    advanceToNextToken();+    List_Obj queries = SASS_MEMORY_NEW(List, pstate, 0, SASS_COMMA);+    if (!peek_css < exactly <'{'> >()) queries->append(&parse_media_query());+    while (lex_css < exactly <','> >()) queries->append(&parse_media_query());+    queries->update_pstate(pstate);+    return queries.detach();+  }++  // Expression_Ptr Parser::parse_media_query()+  Media_Query_Obj Parser::parse_media_query()+  {+    advanceToNextToken();+    Media_Query_Obj media_query = SASS_MEMORY_NEW(Media_Query, pstate);+    if (lex < kwd_not >()) { media_query->is_negated(true); lex < css_comments >(false); }+    else if (lex < kwd_only >()) { media_query->is_restricted(true); lex < css_comments >(false); }++    if (lex < identifier_schema >())  media_query->media_type(&parse_identifier_schema());+    else if (lex < identifier >())    media_query->media_type(&parse_interpolated_chunk(lexed));+    else                             media_query->append(&parse_media_expression());++    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);+      schema->append(&media_query->media_type());+      schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));+      schema->append(&parse_identifier_schema());+      media_query->media_type(schema);+    }+    while (lex_css < kwd_and >()) media_query->append(&parse_media_expression());++    media_query->update_pstate(pstate);++    return media_query;+  }++  Media_Query_Expression_Obj Parser::parse_media_expression()+  {+    if (lex < identifier_schema >()) {+      String_Obj ss = parse_identifier_schema();+      return SASS_MEMORY_NEW(Media_Query_Expression, pstate, &ss, 0, true);+    }+    if (!lex_css< exactly<'('> >()) {+      error("media query expression must begin with '('", pstate);+    }+    Expression_Obj feature = 0;+    if (peek_css< exactly<')'> >()) {+      error("media feature required in media query expression", pstate);+    }+    feature = &parse_expression();+    Expression_Obj expression = 0;+    if (lex_css< exactly<':'> >()) {+      expression = &parse_list(DELAYED);+    }+    if (!lex_css< exactly<')'> >()) {+      error("unclosed parenthesis in media query expression", pstate);+    }+    return SASS_MEMORY_NEW(Media_Query_Expression, feature->pstate(), feature, expression);+  }++  // lexed after `kwd_supports_directive`+  // these are very similar to media blocks+  Supports_Block_Obj Parser::parse_supports_directive()+  {+    Supports_Condition_Obj cond = parse_supports_condition();+    // create the ast node object for the support queries+    Supports_Block_Obj query = SASS_MEMORY_NEW(Supports_Block, pstate, cond);+    // additional block is mandatory+    // parse inner block+    query->block(parse_block());+    // return ast node+    return query;+  }++  // parse one query operation+  // may encounter nested queries+  Supports_Condition_Obj Parser::parse_supports_condition()+  {+    lex < css_whitespace >();+    Supports_Condition_Obj cond = 0;+    if ((cond = parse_supports_negation())) return cond;+    if ((cond = parse_supports_operator())) 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();+    return SASS_MEMORY_NEW(Supports_Negation, pstate, &cond);+  }++  Supports_Condition_Obj Parser::parse_supports_operator()+  {+    Supports_Condition_Obj cond = parse_supports_condition_in_parens();+    if (!&cond) return 0;++    while (true) {+      Supports_Operator::Operand op = Supports_Operator::OR;+      if (lex < kwd_and >()) { op = Supports_Operator::AND; }+      else if(!lex < kwd_or >()) { break; }++      lex < css_whitespace >();+      Supports_Condition_Obj right = parse_supports_condition_in_parens();++      // Supports_Condition_Ptr cc = SASS_MEMORY_NEW(Supports_Condition, *static_cast<Supports_Condition_Ptr>(cond));+      cond = SASS_MEMORY_NEW(Supports_Operator, pstate, &cond, &right, op);+    }+    return cond;+  }++  Supports_Condition_Obj Parser::parse_supports_interpolation()+  {+    if (!lex < interpolant >()) return 0;++    String_Obj interp = parse_interpolated_chunk(lexed);+    if (!interp) return 0;++    return SASS_MEMORY_NEW(Supports_Interpolation, pstate, &interp);+  }++  // TODO: This needs some major work. Although feature conditions+  // look like declarations their semantics differ significantly+  Supports_Condition_Obj Parser::parse_supports_declaration()+  {+    Supports_Condition_Ptr cond = 0;+    // parse something declaration like+    Declaration_Obj declaration = parse_declaration();+    if (!declaration) error("@supports condition expected declaration", pstate);+    cond = SASS_MEMORY_NEW(Supports_Declaration,+                     declaration->pstate(),+                     &declaration->property(),+                     declaration->value());+    // ToDo: maybe we need an additional error condition?+    return cond;+  }++  Supports_Condition_Obj Parser::parse_supports_condition_in_parens()+  {+    Supports_Condition_Obj interp = parse_supports_interpolation();+    if (&interp != 0) return interp;++    if (!lex < exactly <'('> >()) return 0;+    lex < css_whitespace >();++    Supports_Condition_Obj cond = parse_supports_condition();+    if (&cond != 0) {+      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);+    } else {+      cond = parse_supports_declaration();+      if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);+    }+    lex < css_whitespace >();+    return cond;+  }++  At_Root_Block_Obj Parser::parse_at_root_block()+  {+    ParserState at_source_position = pstate;+    Block_Obj body = 0;+    At_Root_Query_Obj expr;+    Lookahead lookahead_result;+    LOCAL_FLAG(in_at_root, true);+    if (lex_css< exactly<'('> >()) {+      expr = parse_at_root_query();+    }+    if (peek_css < exactly<'{'> >()) {+      lex <optional_spaces>();+      body = &parse_block(true);+    }+    else if ((lookahead_result = lookahead_for_selector(position)).found) {+      Ruleset_Obj r = parse_ruleset(lookahead_result, false);+      body = SASS_MEMORY_NEW(Block, r->pstate(), 1, true);+      body->append(&r);+    }+    At_Root_Block_Obj at_root = SASS_MEMORY_NEW(At_Root_Block, at_source_position, body);+    if (&expr) at_root->expression(&expr);+    return at_root;+  }++  At_Root_Query_Obj Parser::parse_at_root_query()+  {+    if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);++    if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {+      css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");+    }++    Expression_Obj feature = parse_list();+    if (!lex_css< exactly<':'> >()) error("style declaration must contain a value", pstate);+    Expression_Obj expression = parse_list();+    List_Obj value = SASS_MEMORY_NEW(List, feature->pstate(), 1);++    if (expression->concrete_type() == Expression::LIST) {+        value = SASS_MEMORY_CAST(List, expression);+    }+    else value->append(expression);++    At_Root_Query_Obj cond = SASS_MEMORY_NEW(At_Root_Query,+                                          value->pstate(),+                                          feature,+                                          &value);+    if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);+    return cond;+  }++  Directive_Obj Parser::parse_special_directive()+  {+    std::string kwd(lexed);++    if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);++    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(true));+    }++    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_prefixed_directive()+  {+    std::string kwd(lexed);++    if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);++    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(true));+    }++    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);+    String_Schema_Obj val = parse_almost_any_value();+    // strip left and right if they are of type string+    directive->value(&val);+    if (peek< exactly<'{'> >()) {+      directive->block(parse_block());+    }+    return directive;+  }++  Expression_Obj Parser::lex_interpolation()+  {+    if (lex < interpolant >(true) != NULL) {+      return &parse_interpolated_chunk(lexed, true);+    }+    return 0;+  }++  Expression_Obj Parser::lex_interp_uri()+  {+    // create a string schema by lexing optional interpolations+    return lex_interp< re_string_uri_open, re_string_uri_close >();+  }++  Expression_Obj Parser::lex_interp_string()+  {+    Expression_Obj rv = 0;+    if ((rv = lex_interp< re_string_double_open, re_string_double_close >())) return rv;+    if ((rv = lex_interp< re_string_single_open, re_string_single_close >())) return rv;+    return rv;+  }++  Expression_Obj Parser::lex_almost_any_value_chars()+  {+    const char* match =+    lex <+      one_plus <+        alternatives <+          sequence <+            exactly <'\\'>,+            any_char+          >,+          sequence <+            negate <+              sequence <+                exactly < url_kwd >,+                exactly <'('>+              >+            >,+            neg_class_char <+              almost_any_value_class+            >+          >,+          sequence <+            exactly <'/'>,+            negate <+              alternatives <+                exactly <'/'>,+                exactly <'*'>+              >+            >+          >,+          sequence <+            exactly <'\\'>,+            exactly <'#'>,+            negate <+              exactly <'{'>+            >+          >,+          sequence <+            exactly <'!'>,+            negate <+              alpha+            >+          >+        >+      >+    >(false);+    if (match) {+      return SASS_MEMORY_NEW(String_Constant, pstate, lexed);+    }+    return NULL;+  }++  Expression_Obj Parser::lex_almost_any_value_token()+  {+    Expression_Obj rv = 0;+    if (*position == 0) return 0;+    if ((rv = &lex_almost_any_value_chars())) return rv;+    // if ((rv = lex_block_comment())) return rv;+    // if ((rv = lex_single_line_comment())) return rv;+    if ((rv = &lex_interp_string())) return rv;+    if ((rv = &lex_interp_uri())) return rv;+    if ((rv = &lex_interpolation())) return rv;+    return rv;+  }++  String_Schema_Obj Parser::parse_almost_any_value()+  {++    String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);+    if (*position == 0) return 0;+    lex < spaces >(false);+    Expression_Obj token = lex_almost_any_value_token();+    if (!token) return 0;+    schema->append(token);+    if (*position == 0) {+      schema->rtrim();+      return schema.detach();+    }++    while ((token = &lex_almost_any_value_token())) {+      schema->append(token);+    }++    lex < css_whitespace >();++    schema->rtrim();++    return schema.detach();+  }++  Warning_Obj Parser::parse_warning()+  {+    if (stack.back() != Scope::Root &&+        stack.back() != Scope::Function &&+        stack.back() != Scope::Mixin &&+        stack.back() != Scope::Control &&+        stack.back() != Scope::Rules) {+      error("Illegal nesting: Only properties may be nested beneath properties.", pstate);+    }+    return SASS_MEMORY_NEW(Warning, pstate, parse_list(DELAYED));+  }++  Error_Obj Parser::parse_error()+  {+    if (stack.back() != Scope::Root &&+        stack.back() != Scope::Function &&+        stack.back() != Scope::Mixin &&+        stack.back() != Scope::Control &&+        stack.back() != Scope::Rules) {+      error("Illegal nesting: Only properties may be nested beneath properties.", pstate);+    }+    return SASS_MEMORY_NEW(Error, pstate, parse_list(DELAYED));+  }++  Debug_Obj Parser::parse_debug()+  {+    if (stack.back() != Scope::Root &&+        stack.back() != Scope::Function &&+        stack.back() != Scope::Mixin &&+        stack.back() != Scope::Control &&+        stack.back() != Scope::Rules) {+      error("Illegal nesting: Only properties may be nested beneath properties.", pstate);+    }+    return SASS_MEMORY_NEW(Debug, pstate, parse_list(DELAYED));+  }++  Return_Obj Parser::parse_return_directive()+  {+    // check that we do not have an empty list (ToDo: check if we got all cases)+    if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >())+    { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }+    return SASS_MEMORY_NEW(Return, pstate, &parse_list());+  }++  Lookahead Parser::lookahead_for_selector(const char* start)+  {+    // init result struct+    Lookahead rv = Lookahead();+    // get start position+    const char* p = start ? start : position;+    // match in one big "regex"+    rv.error = p;+    if (const char* q =+      peek <+        re_selector_list+      >(p)+    ) {+      while (p < q) {+        // did we have interpolations?+        if (*p == '#' && *(p+1) == '{') {+          rv.has_interpolants = true;+          p = q; break;+        }+        ++ p;+      }+      // store anyway  }+++      // ToDo: remove+      rv.error = q;+      rv.position = q;+      // check expected opening bracket+      // only after successfull matching+      if (peek < exactly<'{'> >(q)) rv.found = q;+      else if (peek < exactly<'('> >(q)) rv.found = q;+      // else if (peek < exactly<';'> >(q)) rv.found = q;+      // else if (peek < exactly<'}'> >(q)) rv.found = q;+      if (rv.found || *p == 0) rv.error = 0;+    }++    rv.parsable = ! rv.has_interpolants;++    // return result+    return rv;++  }+  // EO lookahead_for_selector++  // used in parse_block_nodes and parse_special_directive+  // ToDo: actual usage is still not really clear to me?+  Lookahead Parser::lookahead_for_include(const char* start)+  {+    // we actually just lookahead for a selector+    Lookahead rv = lookahead_for_selector(start);+    // but the "found" rules are different+    if (const char* p = rv.position) {+      // check for additional abort condition+      if (peek < exactly<';'> >(p)) rv.found = p;+      else if (peek < exactly<'}'> >(p)) rv.found = p;+    }+    // return result+    return rv;+  }+  // EO lookahead_for_include++  // look ahead for a token with interpolation in it+  // we mostly use the result if there is an interpolation+  // everything that passes here gets parsed as one schema+  // meaning it will not be parsed as a space separated list+  Lookahead Parser::lookahead_for_value(const char* start)+  {+    // init result struct+    Lookahead rv = Lookahead();+    // get start position+    const char* p = start ? start : position;+    // match in one big "regex"+    if (const char* q =+      peek <+        non_greedy <+          alternatives <+            // consume whitespace+            block_comment, // spaces,+            // main tokens+            sequence <+              interpolant,+              optional <+                quoted_string+              >+            >,+            identifier,+            variable,+            // issue #442+            sequence <+              parenthese_scope,+              interpolant,+              optional <+                quoted_string+              >+            >+          >,+          sequence <+            // optional_spaces,+            alternatives <+              exactly<'{'>,+              exactly<'}'>,+              exactly<';'>+            >+          >+        >+      >(p)+    ) {+      if (p == q) return rv;+      while (p < q) {+        // did we have interpolations?+        if (*p == '#' && *(p+1) == '{') {+          rv.has_interpolants = true;+          p = q; break;+        }+        ++ p;+      }+      // store anyway+      // ToDo: remove+      rv.position = q;+      // check expected opening bracket+      // only after successful matching+      if (peek < exactly<'{'> >(q)) rv.found = q;+      else if (peek < exactly<';'> >(q)) rv.found = q;+      else if (peek < exactly<'}'> >(q)) rv.found = q;+    }++    // return result+    return rv;+  }+  // EO lookahead_for_value++  void Parser::read_bom()+  {+    size_t skip = 0;+    std::string encoding;+    bool utf_8 = false;+    switch ((unsigned char) source[0]) {+    case 0xEF:+      skip = check_bom_chars(source, end, utf_8_bom, 3);+      encoding = "UTF-8";+      utf_8 = true;+      break;+    case 0xFE:+      skip = check_bom_chars(source, end, utf_16_bom_be, 2);+      encoding = "UTF-16 (big endian)";+      break;+    case 0xFF:+      skip = check_bom_chars(source, end, utf_16_bom_le, 2);+      skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);+      encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");+      break;+    case 0x00:+      skip = check_bom_chars(source, end, utf_32_bom_be, 4);+      encoding = "UTF-32 (big endian)";+      break;+    case 0x2B:+      skip = check_bom_chars(source, end, utf_7_bom_1, 4)+           | check_bom_chars(source, end, utf_7_bom_2, 4)+           | check_bom_chars(source, end, utf_7_bom_3, 4)+           | check_bom_chars(source, end, utf_7_bom_4, 4)+           | check_bom_chars(source, end, utf_7_bom_5, 5);+      encoding = "UTF-7";+      break;+    case 0xF7:+      skip = check_bom_chars(source, end, utf_1_bom, 3);+      encoding = "UTF-1";+      break;+    case 0xDD:+      skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);+      encoding = "UTF-EBCDIC";+      break;+    case 0x0E:+      skip = check_bom_chars(source, end, scsu_bom, 3);+      encoding = "SCSU";+      break;+    case 0xFB:+      skip = check_bom_chars(source, end, bocu_1_bom, 3);+      encoding = "BOCU-1";+      break;+    case 0x84:+      skip = check_bom_chars(source, end, gb_18030_bom, 4);+      encoding = "GB-18030";+      break;+    }+    if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);+    position += skip;+  }++  size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)+  {+    size_t skip = 0;+    if (src + len > end) return 0;+    for (size_t i = 0; i < len; ++i, ++skip) {+      if ((unsigned char) src[i] != bom[i]) return 0;+    }+    return skip;+  }+++  Expression_Obj Parser::fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, Operand op)+  {+    for (size_t i = 0, S = operands.size(); i < S; ++i) {+      base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), op, &base, operands[i]);+    }+    return base;+  }++  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 = dynamic_cast<String_Schema_Ptr>(&base)) {+      // return schema;+      if (schema->has_interpolants()) {+        if (i + 1 < operands.size() && (+             (ops[0].operand == Sass_OP::EQ)+          || (ops[0].operand == Sass_OP::ADD)+          || (ops[0].operand == Sass_OP::DIV)+          || (ops[0].operand == Sass_OP::MUL)+          || (ops[0].operand == Sass_OP::NEQ)+          || (ops[0].operand == Sass_OP::LT)+          || (ops[0].operand == Sass_OP::GT)+          || (ops[0].operand == Sass_OP::LTE)+          || (ops[0].operand == Sass_OP::GTE)+        )) {+          Expression_Obj rhs = fold_operands(operands[i], operands, ops, i + 1);+          rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[0], schema, &rhs);+          return rhs;+        }+        // return schema;+      }+    }++    for (size_t S = operands.size(); i < S; ++i) {+      if (String_Schema_Ptr schema = dynamic_cast<String_Schema_Ptr>(&operands[i])) {+        if (schema->has_interpolants()) {+          if (i + 1 < S) {+            Expression_Obj rhs = fold_operands(operands[i+1], operands, ops, i + 2);+            rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], schema, &rhs);+            base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], &base, &rhs);+            return base;+          }+          base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], &base, operands[i]);+          return base;+        } else {+          base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], &base, operands[i]);+        }+      } else {+        base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], &base, operands[i]);+      }+      Binary_Expression_Ptr b = static_cast<Binary_Expression_Ptr>(&base);+      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 = dynamic_cast<Binary_Expression_Ptr>(&base)) {+      if (SASS_MEMORY_CAST(Binary_Expression, b->left())) base->set_delayed(false);+      if (SASS_MEMORY_CAST(Binary_Expression, b->right())) base->set_delayed(false);+    }+    return base;+  }++  void Parser::error(std::string msg, Position pos)+  {+    throw Exception::InvalidSass(ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);+  }++  // print a css parsing error with actual context information from parsed source+  void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle)+  {+    int max_len = 18;+    const char* end = this->end;+    while (*end != 0) ++ end;+    const char* pos = peek < optional_spaces >();++    const char* last_pos(pos);+    if (last_pos > source) {+      utf8::prior(last_pos, source);+    }+    // backup position to last significant char+    while (last_pos > source && last_pos < end) {+      if (!Prelexer::is_space(*last_pos)) break;+      utf8::prior(last_pos, source);+    }++    bool ellipsis_left = false;+    const char* pos_left(last_pos);+    const char* end_left(last_pos);++    utf8::next(pos_left, end);+    utf8::next(end_left, end);+    while (pos_left > source) {+      if (utf8::distance(pos_left, end_left) >= max_len) {+        utf8::prior(pos_left, source);+        ellipsis_left = *(pos_left) != '\n' &&+                        *(pos_left) != '\r';+        utf8::next(pos_left, end);+        break;+      }++      const char* prev = pos_left;+      utf8::prior(prev, source);+      if (*prev == '\r') break;+      if (*prev == '\n') break;+      pos_left = prev;+    }+    if (pos_left < source) {+      pos_left = source;+    }++    bool ellipsis_right = false;+    const char* end_right(pos);+    const char* pos_right(pos);+    while (end_right < end) {+      if (utf8::distance(pos_right, end_right) > max_len) {+        ellipsis_left = *(pos_right) != '\n' &&+                        *(pos_right) != '\r';+        break;+      }+      if (*end_right == '\r') break;+      if (*end_right == '\n') break;+      utf8::next(end_right, end);+    }+    // if (*end_right == 0) end_right ++;++    std::string left(pos_left, end_left);+    std::string right(pos_right, end_right);+    size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0;+    size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0;+    if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos);+    if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis;     // now pass new message to the more generic error function     error(msg + prefix + quote(left) + middle + quote(right), pstate);   }
libsass/src/parser.hpp view
@@ -23,12 +23,12 @@   class Parser : public ParserState {   public: -    enum Syntactic_Context { nothing, mixin_def, function_def };+    enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules };      Context& ctx;-    std::vector<Block*> block_stack;-    std::vector<Syntactic_Context> stack;-    Media_Block* last_media_block;+    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;@@ -42,16 +42,16 @@     bool in_at_root;      Parser(Context& ctx, const ParserState& pstate)-    : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), last_media_block(0),+    : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),       source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0)-    { in_at_root = false; stack.push_back(nothing); }+    { in_at_root = false; 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, ParserState pstate = ParserState("[CSTRING]"));-    static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));-    static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"));+    static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);+    static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);+    static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);     // special static parsers to convert strings into certain selectors-    static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"));+    static Selector_List_Obj parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);  #ifdef __clang__ @@ -66,6 +66,10 @@ #endif  +    // skip current token and next whitespace+    // moves ParserState right before next token+    void advanceToNextToken();+     bool peek_newline(const char* start = 0);      // skip over spaces, tabs and line comments@@ -99,6 +103,15 @@     // peek will only skip over space, tabs and line comment     // return the position where the lexer match will occur     template <Prelexer::prelexer mx>+    const char* match(const char* start = 0)+    {+      // match the given prelexer+      return mx(position);+    }++    // peek will only skip over space, tabs and line comment+    // return the position where the lexer match will occur+    template <Prelexer::prelexer mx>     const char* peek(const char* start = 0)     { @@ -107,8 +120,11 @@       const char* it_before_token = sneak < mx >(start);        // match the given prelexer-      return mx(it_before_token);+      const char* match = mx(it_before_token); +      // check if match is in valid range+      return match <= end ? match : 0;+     }      // white-space handling is built into the lexer@@ -121,6 +137,8 @@     const char* lex(bool lazy = true, bool force = false)     { +      if (*position == 0) return 0;+       // position considered before lexed token       // we can skip whitespace or comments for       // lazy developers (but we need control)@@ -133,6 +151,9 @@       // now call matcher to get position after token       const char* it_after_token = mx(it_before_token); +      // check if match is in valid range+      if (it_after_token > end) return 0;+       // maybe we want to update the parser state anyway?       if (force == false) {         // assertion that we got a valid match@@ -210,84 +231,89 @@                    const std::string& middle = ", was: ");     void read_bom(); -    Block* parse();-    Import* parse_import();-    Definition* parse_definition(Definition::Type which_type);-    Parameters* parse_parameters();-    Parameter* parse_parameter();-    Mixin_Call* parse_include_directive();-    Arguments* parse_arguments();-    Argument* parse_argument();-    Assignment* parse_assignment();-    // Propset* parse_propset();-    Ruleset* parse_ruleset(Lookahead lookahead, bool is_root = false);-    Selector_Schema* parse_selector_schema(const char* end_of_selector);-    Selector_List* parse_selector_list(bool at_root = false);-    Complex_Selector* parse_complex_selector(bool in_root = true);-    Compound_Selector* parse_compound_selector();-    Simple_Selector* parse_simple_selector();-    Wrapped_Selector* parse_negated_selector();-    Simple_Selector* parse_pseudo_selector();-    Attribute_Selector* parse_attribute_selector();-    Block* parse_block(bool is_root = false);-    Block* parse_css_block(bool is_root = false);+    Block_Obj parse();+    Import_Obj parse_import();+    Definition_Obj parse_definition(Definition::Type which_type);+    Parameters_Obj parse_parameters();+    Parameter_Obj parse_parameter();+    Mixin_Call_Obj parse_include_directive();+    Arguments_Obj parse_arguments();+    Argument_Obj parse_argument();+    Assignment_Obj parse_assignment();+    Ruleset_Obj parse_ruleset(Lookahead lookahead, bool is_root = false);+    Selector_Schema_Obj parse_selector_schema(const char* end_of_selector);+    Selector_List_Obj parse_selector_list(bool at_root = false);+    Complex_Selector_Obj parse_complex_selector(bool in_root = true);+    Compound_Selector_Obj parse_compound_selector();+    Simple_Selector_Obj parse_simple_selector();+    Wrapped_Selector_Obj parse_negated_selector();+    Simple_Selector_Obj 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* parse_declaration();-    Expression* parse_map_value();-    Expression* parse_map();-    Expression* parse_list();-    Expression* parse_comma_list();-    Expression* parse_space_list();-    Expression* parse_disjunction();-    Expression* parse_conjunction();-    Expression* parse_relation();-    Expression* parse_expression();-    Expression* parse_operators();-    Expression* parse_factor();-    Expression* parse_value2();-    Expression* parse_value();-    Function_Call* parse_calc_function();-    Function_Call* parse_function_call();-    Function_Call_Schema* parse_function_call_schema();-    String* parse_url_function_string();-    String* parse_interpolated_chunk(Token, bool constant = false);-    String* parse_string();-    String_Constant* parse_static_expression();-    // String_Constant* parse_static_property();-    String_Constant* parse_static_value();-    String* parse_ie_property();-    String* parse_ie_keyword_arg();-    String_Schema* parse_value_schema(const char* stop);-    String* parse_identifier_schema();-    // String_Schema* parse_url_schema();-    If* parse_if_directive(bool else_if = false);-    For* parse_for_directive();-    Each* parse_each_directive();-    While* parse_while_directive();-    Return* parse_return_directive();-    Content* parse_content_directive();+    Declaration_Obj parse_declaration();+    Expression_Obj parse_map();+    Expression_Obj parse_list(bool delayed = false);+    Expression_Obj parse_comma_list(bool delayed = false);+    Expression_Obj parse_space_list();+    Expression_Obj parse_disjunction();+    Expression_Obj parse_conjunction();+    Expression_Obj parse_relation();+    Expression_Obj parse_expression();+    Expression_Obj parse_operators();+    Expression_Obj parse_factor();+    Expression_Obj parse_value();+    Function_Call_Obj parse_calc_function();+    Function_Call_Obj parse_function_call();+    Function_Call_Schema_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);+    String_Obj parse_string();+    String_Constant_Obj parse_static_value();+    String_Obj parse_ie_property();+    String_Obj parse_ie_keyword_arg();+    String_Schema_Obj parse_value_schema(const char* stop);+    String_Obj parse_identifier_schema();+    If_Obj parse_if_directive(bool else_if = false);+    For_Obj parse_for_directive();+    Each_Obj parse_each_directive();+    While_Obj parse_while_directive();+    Return_Obj parse_return_directive();+    Content_Obj parse_content_directive();     void parse_charset_directive();-    Media_Block* parse_media_block();-    List* parse_media_queries();-    Media_Query* parse_media_query();-    Media_Query_Expression* parse_media_expression();-    Supports_Block* parse_supports_directive();-    Supports_Condition* parse_supports_condition();-    Supports_Condition* parse_supports_negation();-    Supports_Condition* parse_supports_operator();-    Supports_Condition* parse_supports_interpolation();-    Supports_Condition* parse_supports_declaration();-    Supports_Condition* parse_supports_condition_in_parens();-    At_Root_Block* parse_at_root_block();-    At_Root_Expression* parse_at_root_expression();-    At_Rule* parse_at_rule();-    Warning* parse_warning();-    Error* parse_error();-    Debug* parse_debug();+    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_negation();+    Supports_Condition_Obj parse_supports_operator();+    Supports_Condition_Obj parse_supports_interpolation();+    Supports_Condition_Obj parse_supports_declaration();+    Supports_Condition_Obj parse_supports_condition_in_parens();+    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(); +    // be more like ruby sass+    Expression_Obj lex_almost_any_value_token();+    Expression_Obj lex_almost_any_value_chars();+    Expression_Obj lex_interp_string();+    Expression_Obj lex_interp_uri();+    Expression_Obj lex_interpolation();+     // these will throw errors     Token lex_variable();     Token lex_identifier();@@ -298,11 +324,39 @@     Lookahead lookahead_for_selector(const char* start = 0);     Lookahead lookahead_for_include(const char* start = 0); -    Expression* fold_operands(Expression* base, std::vector<Expression*>& operands, Sass_OP op);-    Expression* fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<Sass_OP>& ops);+    Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, Operand op);+    Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, std::vector<Operand>& ops, size_t i = 0);      void throw_syntax_error(std::string message, size_t ln = 0);     void throw_read_error(std::string message, size_t ln = 0);+++    template <Prelexer::prelexer open, Prelexer::prelexer close>+    Expression_Obj lex_interp()+    {+      if (lex < open >(false)) {+        String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);+        // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";+        schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+        if (position[0] == '#' && position[1] == '{') {+          Expression_Obj itpl = lex_interpolation();+          if (&itpl) schema->append(&itpl);+          while (lex < close >(false)) {+            // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";+            schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+            if (position[0] == '#' && position[1] == '{') {+              Expression_Obj itpl = lex_interpolation();+              if (&itpl) schema->append(&itpl);+            } else {+              return &schema;+            }+          }+        } else {+          return SASS_MEMORY_NEW(String_Constant, pstate, lexed);+        }+      }+      return 0;+    }   };    size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
libsass/src/plugins.cpp view
@@ -7,6 +7,7 @@ #include <dlfcn.h> #endif +#include "sass.hpp" #include <iostream> #include "output.hpp" #include "plugins.hpp"
libsass/src/position.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "position.hpp"  namespace Sass {
libsass/src/prelexer.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cctype> #include <cstddef> #include <iostream>@@ -15,6 +16,251 @@   namespace Prelexer {  +    /*++        def string_re(open, close)+          /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m+        end+      end++      # A hash of regular expressions that are used for tokenizing strings.+      #+      # The key is a `[Symbol, Boolean]` pair.+      # The symbol represents which style of quotation to use,+      # while the boolean represents whether or not the string+      # is following an interpolated segment.+      STRING_REGULAR_EXPRESSIONS = {+        :double => {+          /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m+          false => string_re('"', '"'),+          true => string_re('', '"')+        },+        :single => {+          false => string_re("'", "'"),+          true => string_re('', "'")+        },+        :uri => {+          false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+          true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+        },+        # Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a+        # non-standard version of http://www.w3.org/TR/css3-conditional/+        :url_prefix => {+          false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+          true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+        },+        :domain => {+          false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+          true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+        }+      }+    */++    /*+      /#{open}+        (+          \\.+          |+          \# (?!\{)+          |+          [^#{close}\\#]+        )*+        (#{close}|#\{)+      /m+      false => string_re('"', '"'),+      true => string_re('', '"')+    */+    extern const char string_double_negates[] = "\"\\#";+    const char* re_string_double_close(const char* src)+    {+      return sequence <+        // valid chars+        zero_plus <+          alternatives <+            // escaped char+            sequence <+              exactly <'\\'>,+              any_char+            >,+            // non interpolate hash+            sequence <+              exactly <'#'>,+              negate <+                exactly <'{'>+              >+            >,+            // other valid chars+            neg_class_char <+              string_double_negates+            >+          >+        >,+        // quoted string closer+        // or interpolate opening+        alternatives <+          exactly <'"'>,+          lookahead < exactly< hash_lbrace > >+        >+      >(src);+    }++    const char* re_string_double_open(const char* src)+    {+      return sequence <+        // quoted string opener+        exactly <'"'>,+        // valid chars+        zero_plus <+          alternatives <+            // escaped char+            sequence <+              exactly <'\\'>,+              any_char+            >,+            // non interpolate hash+            sequence <+              exactly <'#'>,+              negate <+                exactly <'{'>+              >+            >,+            // other valid chars+            neg_class_char <+              string_double_negates+            >+          >+        >,+        // quoted string closer+        // or interpolate opening+        alternatives <+          exactly <'"'>,+          lookahead < exactly< hash_lbrace > >+        >+      >(src);+    }++    extern const char string_single_negates[] = "'\\#";+    const char* re_string_single_close(const char* src)+    {+      return sequence <+        // valid chars+        zero_plus <+          alternatives <+            // escaped char+            sequence <+              exactly <'\\'>,+              any_char+            >,+            // non interpolate hash+            sequence <+              exactly <'#'>,+              negate <+                exactly <'{'>+              >+            >,+            // other valid chars+            neg_class_char <+              string_single_negates+            >+          >+        >,+        // quoted string closer+        // or interpolate opening+        alternatives <+          exactly <'\''>,+          lookahead < exactly< hash_lbrace > >+        >+      >(src);+    }++    const char* re_string_single_open(const char* src)+    {+      return sequence <+        // quoted string opener+        exactly <'\''>,+        // valid chars+        zero_plus <+          alternatives <+            // escaped char+            sequence <+              exactly <'\\'>,+              any_char+            >,+            // non interpolate hash+            sequence <+              exactly <'#'>,+              negate <+                exactly <'{'>+              >+            >,+            // other valid chars+            neg_class_char <+              string_single_negates+            >+          >+        >,+        // quoted string closer+        // or interpolate opening+        alternatives <+          exactly <'\''>,+          lookahead < exactly< hash_lbrace > >+        >+      >(src);+    }++    /*+      :uri => {+        false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+        true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+      },+    */+    const char* re_string_uri_close(const char* src)+    {+      return sequence <+        non_greedy<+          alternatives<+            class_char< real_uri_chars >,+            uri_character,+            NONASCII,+            ESCAPE+          >,+          alternatives<+            sequence < optional < W >, exactly <')'> >,+            lookahead < exactly< hash_lbrace > >+          >+        >,+        optional <+          sequence < optional < W >, exactly <')'> >+        >+      >(src);+    }++    const char* re_string_uri_open(const char* src)+    {+      return sequence <+        exactly <'u'>,+        exactly <'r'>,+        exactly <'l'>,+        exactly <'('>,+        W,+        alternatives<+          quoted_string,+          non_greedy<+            alternatives<+              class_char< real_uri_chars >,+              uri_character,+              NONASCII,+              ESCAPE+            >,+            alternatives<+              sequence < W, exactly <')'> >,+              exactly< hash_lbrace >+            >+          >+        >+      >(src);+    }+     // Match a line comment (/.*?(?=\n|\r\n?|\Z)/.     const char* line_comment(const char* src)     {@@ -153,8 +399,8 @@              >(src);     } -    // Match CSS unit identifier.-    const char* unit_identifier(const char* src)+    // Match a single CSS unit+    const char* one_unit(const char* src)     {       return sequence <                optional < exactly <'-'> >,@@ -169,6 +415,34 @@              >(src);     } +    // Match numerator/denominator CSS units+    const char* multiple_units(const char* src)+    {+      return+        sequence <+          one_unit,+          zero_plus <+            sequence <+              exactly <'*'>,+              one_unit+            >+          >+        >(src);+    }++    // Match complex CSS unit identifiers+    const char* unit_identifier(const char* src)+    {+      return sequence <+        multiple_units,+        optional <+          sequence <+          exactly <'/'>,+          multiple_units+        > >+      >(src);+    }+     const char* identifier_alnums(const char* src)     {       return one_plus< identifier_alnum >(src);@@ -194,7 +468,12 @@                  sequence <                    zero_plus <                      alternatives <-                       identifier,+                       sequence <+                         optional <+                           exactly <'$'>+                         >,+                         identifier+                       >,                        exactly <'-'>                      >                    >,@@ -202,9 +481,13 @@                    zero_plus <                      alternatives <                        digits,-                       identifier,+                       sequence <+                         optional <+                           exactly <'$'>+                         >,+                         identifier+                       >,                        quoted_string,-                       exactly<'+'>,                        exactly<'-'>                      >                    >@@ -278,24 +561,187 @@       >(src);     } -    const char* value_schema(const char* src) {-      // follows this pattern: ([xyz]*i[xyz]*)+-      return one_plus< sequence< zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string > >,-                                 interpolant,-                                 zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string, exactly<'%'> > > > >(src);+    const char* sass_value(const char* src) {+      return alternatives <+        quoted_string,+        identifier,+        percentage,+        hex,+        dimension,+        number+      >(src);     } -    /* not used anymore - remove?-    const char* filename(const char* src) {-      return one_plus< alternatives< identifier, number, exactly<'.'> > >(src);+    // this is basically `one_plus < sass_value >`+    // takes care to not parse invalid combinations+    const char* value_combinations(const char* src) {+      // `2px-2px` is invalid combo+      bool was_number = false;+      const char* pos = src;+      while (src) {+        if ((pos = alternatives < quoted_string, identifier, percentage, hex >(src))) {+          was_number = false;+          src = pos;+        } else if (!was_number && !exactly<'+'>(src) && (pos = alternatives < dimension, number >(src))) {+          was_number = true;+          src = pos;+        } else {+          break;+        }+      }+      return src;     }-    */ +    // must be at least one interpolant+    // can be surrounded by sass values+    // make sure to never parse (dim)(dim)+    // since this wrongly consumes `2px-1px`+    // `2px1px` is valid number (unit `px1px`)+    const char* value_schema(const char* src)+    {+      return sequence <+        one_plus <+          sequence <+            optional < value_combinations >,+            interpolant,+            optional < value_combinations >+          >+        >+      >(src);+    }+     // Match CSS '@' keywords.     const char* at_keyword(const char* src) {       return sequence<exactly<'@'>, identifier>(src);     } +    /*+        tok(%r{+          (+            \\.+          |+            (?!url\()+            [^"'/\#!;\{\}] # "+          |+            /(?![\*\/])+          |+            \#(?!\{)+          |+            !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed.+          )++        }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri ||+                interpolation(:warn_for_color)+    */+    const char* re_almost_any_value_token(const char* src) {++      return alternatives <+        one_plus <+          alternatives <+            sequence <+              exactly <'\\'>,+              any_char+            >,+            sequence <+              negate <+                sequence <+                  exactly < url_kwd >,+                  exactly <'('>+                >+              >,+              neg_class_char <+                almost_any_value_class+              >+            >,+            sequence <+              exactly <'/'>,+              negate <+                alternatives <+                  exactly <'/'>,+                  exactly <'*'>+                >+              >+            >,+            sequence <+              exactly <'\\'>,+              exactly <'#'>,+              negate <+                exactly <'{'>+              >+            >,+            sequence <+              exactly <'!'>,+              negate <+                alpha+              >+            >+          >+        >,+        block_comment,+        line_comment,+        interpolant,+        space,+        sequence <+          exactly<'u'>,+          exactly<'r'>,+          exactly<'l'>,+          exactly<'('>,+          zero_plus <+            alternatives <+              class_char< real_uri_chars >,+              uri_character,+              NONASCII,+              ESCAPE+            >+          >,+          // false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+          // true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+          exactly<')'>+        >+      >(src);+    }++    /*+      DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,+        :each, :while, :if, :else, :extend, :import, :media, :charset, :content,+        :_moz_document, :at_root, :error]+    */+    const char* re_special_directive(const char* src) {+      return alternatives <+        word < mixin_kwd >,+        word < include_kwd >,+        word < function_kwd >,+        word < return_kwd >,+        word < debug_kwd >,+        word < warn_kwd >,+        word < for_kwd >,+        word < each_kwd >,+        word < while_kwd >,+        word < if_kwd >,+        word < else_kwd >,+        word < extend_kwd >,+        word < import_kwd >,+        word < media_kwd >,+        word < charset_kwd >,+        word < content_kwd >,+        // exactly < moz_document_kwd >,+        word < at_root_kwd >,+        word < error_kwd >+      >(src);+    }++    const char* re_prefixed_directive(const char* src) {+      return sequence <+        optional <+          sequence <+            exactly <'-'>,+            one_plus < alnum >,+            exactly <'-'>+          >+        >,+        exactly < supports_kwd >+      >(src);+    }+     const char* re_reference_combinator(const char* src) {       return sequence <         optional <@@ -434,7 +880,7 @@       return one_plus< alternatives< alnum,                                      exactly<'-'>,                                      exactly<'_'>,-                                     exactly<'\\'> > >(src);+                                     escape_seq > >(src);     }      const char* kwd_warn(const char* src) {@@ -512,7 +958,7 @@     }      const char* hyphens_and_identifier(const char* src) {-      return sequence< zero_plus< exactly< '-' > >, identifier >(src);+      return sequence< zero_plus< exactly< '-' > >, identifier_alnums >(src);     }     const char* hyphens_and_name(const char* src) {       return sequence< zero_plus< exactly< '-' > >, name >(src);@@ -522,11 +968,11 @@     }     // Match CSS id names.     const char* id_name(const char* src) {-      return sequence<exactly<'#'>, name>(src);+      return sequence<exactly<'#'>, identifier_alnums >(src);     }     // Match CSS class names.     const char* class_name(const char* src) {-      return sequence<exactly<'.'>, identifier>(src);+      return sequence<exactly<'.'>, identifier >(src);     }     // Attribute name in an attribute selector.     const char* attribute_name(const char* src) {@@ -535,10 +981,13 @@     }     // match placeholder selectors     const char* placeholder(const char* src) {-      return sequence<exactly<'%'>, identifier>(src);+      return sequence<exactly<'%'>, identifier_alnums >(src);     }     // Match CSS numeric constants. +    const char* op(const char* src) {+      return class_char<op_chars>(src);+    }     const char* sign(const char* src) {       return class_char<sign_chars>(src);     }@@ -603,23 +1052,19 @@     // Match CSS uri specifiers.      const char* uri_prefix(const char* src) {-      return exactly<url_kwd>(src);-    }-    const char* uri_value(const char* src)-    {-      return-      sequence <-        negate <-          exactly < '$' >+      return sequence <+        exactly <+          url_kwd         >,         zero_plus <-          alternatives <-            alnum,-            interpolant,-            exactly <'/'>,-            class_char < uri_chars >+          sequence <+            exactly <'-'>,+            one_plus <+              alpha+            >           >-        >+        >,+        exactly <'('>       >(src);     } @@ -752,6 +1197,22 @@       return sequence<exactly<'$'>, identifier>(src);     } +    // parse `calc`, `-a-calc` and `--b-c-calc`+    // but do not parse `foocalc` or `foo-calc`+    const char* calc_fn_call(const char* src) {+      return sequence <+        optional < sequence <+          hyphens,+          one_plus < sequence <+            strict_identifier,+            hyphens+          > >+        > >,+        exactly < calc_fn_kwd >,+        word_boundary+      >(src);+    }+     // Match Sass boolean keywords.     const char* kwd_true(const char* src) {       return word<true_kwd>(src);@@ -1094,6 +1555,83 @@       >(src);     } +    const char* re_selector_list(const char* src) {+      return alternatives <+        // partial bem selector+        sequence <+          ampersand,+          one_plus <+            exactly < '-' >+          >,+          word_boundary,+          optional_spaces+        >,+        // main selector matching+        one_plus <+          alternatives <+            // consume whitespace and comments+            spaces, block_comment, line_comment,+            // match `/deep/` selector (pass-trough)+            // there is no functionality for it yet+            schema_reference_combinator,+            // match selector ops /[*&%,\[\]]/+            class_char < selector_lookahead_ops >,+            // match selector combinators /[>+~]/+            class_char < selector_combinator_ops >,+            // match attribute compare operators+            sequence <+              exactly <'('>,+              optional_spaces,+              optional <re_selector_list>,+              optional_spaces,+              exactly <')'>+            >,+            alternatives <+              exact_match, class_match, dash_match,+              prefix_match, suffix_match, substring_match+            >,+            // main selector match+            sequence <+              // allow namespace prefix+              optional < namespace_schema >,+              // modifiers prefixes+              alternatives <+                sequence <+                  exactly <'#'>,+                  // not for interpolation+                  negate < exactly <'{'> >+                >,+                // class match+                exactly <'.'>,+                // single or double colon+                optional < pseudo_prefix >+              >,+              // accept hypens in token+              one_plus < sequence <+                // can start with hyphens+                zero_plus < exactly<'-'> >,+                // now the main token+                alternatives <+                  kwd_optional,+                  exactly <'*'>,+                  quoted_string,+                  interpolant,+                  identifier,+                  variable,+                  percentage,+                  binomial,+                  dimension,+                  alnum+                >+              > >,+              // can also end with hyphens+              zero_plus < exactly<'-'> >+            >+          >+        >+      >(src);+    }+     const char* type_selector(const char* src) {       return sequence< optional<namespace_schema>, identifier>(src);     }@@ -1107,34 +1645,44 @@       return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src);     } -    template <size_t size, prelexer mx, prelexer pad>-    const char* padded_token(const char* src)-    {-      size_t got = 0;-      const char* pos = src;-      while (got < size) {-        if (!mx(pos)) break;-        ++ pos; ++ got;-      }-      while (got < size) {-        if (!pad(pos)) break;-        ++ pos; ++ got;-      }-      return got ? pos : 0;-    }+    // lexer special_fn: these functions cannot be overloaded+    // (/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)+    const char* re_special_fun(const char* src) { -    template <size_t min, size_t max, prelexer mx>-    const char* minmax_range(const char* src)-    {-      size_t got = 0;-      const char* pos = src;-      while (got < max) {-        if (!mx(pos)) break;-        ++ pos; ++ got;+      // match this first as we test prefix hyphens+      if (const char* calc = calc_fn_call(src)) {+        return calc;       }-      if (got < min) return 0;-      if (got > max) return 0;-      return pos;++      return sequence <+        optional <+          sequence <+            exactly <'-'>,+            one_plus <+              alternatives <+                alpha,+                exactly <'+'>,+                exactly <'-'>+              >+            >+          >+        >,+        alternatives <+          word < expression_kwd >,+          sequence <+            sequence <+              exactly < progid_kwd >,+              exactly <':'>+            >,+            zero_plus <+              alternatives <+                char_range <'a', 'z'>,+                exactly <'.'>+              >+            >+          >+        >+      >(src);     }    }
libsass/src/prelexer.hpp view
@@ -162,13 +162,19 @@       return src;     } +    // equivalent of STRING_REGULAR_EXPRESSIONS+    const char* re_string_double_open(const char* src);+    const char* re_string_double_close(const char* src);+    const char* re_string_single_open(const char* src);+    const char* re_string_single_close(const char* src);+    const char* re_string_uri_open(const char* src);+    const char* re_string_uri_close(const char* src);+     // Match a line comment.     const char* line_comment(const char* src);-    const char* line_comment_prefix(const char* src);      // Match a block comment.     const char* block_comment(const char* src);-    const char* block_comment_prefix(const char* src);     // Match either.     const char* comment(const char* src);     // Match double- and single-quoted strings.@@ -200,6 +206,8 @@     const char* strict_identifier_alpha(const char* src);     const char* strict_identifier_alnum(const char* src);     // Match a CSS unit identifier.+    const char* one_unit(const char* src);+    const char* multiple_units(const char* src);     const char* unit_identifier(const char* src);     // const char* strict_identifier_alnums(const char* src);     // Match reference selector.@@ -210,11 +218,17 @@     // Match interpolant schemas     const char* identifier_schema(const char* src);     const char* value_schema(const char* src);+    const char* sass_value(const char* src);     // const char* filename(const char* src);     // const char* filename_schema(const char* src);     // const char* url_schema(const char* src);     // const char* url_value(const char* src);     const char* vendor_prefix(const char* src);++    const char* re_special_directive(const char* src);+    const char* re_prefixed_directive(const char* src);+    const char* re_almost_any_value_token(const char* src);+     // Match CSS '@' keywords.     const char* at_keyword(const char* src);     const char* kwd_import(const char* src);@@ -252,6 +266,7 @@     const char* re_nothing(const char* src);     const char* re_type_selector2(const char* src); +    const char* re_special_fun(const char* src);      const char* kwd_warn(const char* src);     const char* kwd_err(const char* src);@@ -259,6 +274,7 @@      const char* kwd_null(const char* src); +    const char* re_selector_list(const char* src);     const char* re_type_selector(const char* src);     const char* re_static_expression(const char* src); @@ -282,6 +298,7 @@     // Match placeholder selectors.     const char* placeholder(const char* src);     // Match CSS numeric constants.+    const char* op(const char* src);     const char* sign(const char* src);     const char* unsigned_number(const char* src);     const char* number(const char* src);@@ -296,7 +313,6 @@     // const char* rgb_prefix(const char* src);     // Match CSS uri specifiers.     const char* uri_prefix(const char* src);-    const char* uri_value(const char* src);     // Match CSS "!important" keyword.     const char* kwd_important(const char* src);     // Match CSS "!optional" keyword.@@ -329,6 +345,7 @@      // Match SCSS variable names.     const char* variable(const char* src);+    const char* calc_fn_call(const char* src);      // IE stuff     const char* ie_progid(const char* src);@@ -381,6 +398,18 @@       }       return 0;     }+    template<prelexer mx, prelexer skip>+    const char* find_first_in_interval(const char* beg, const char* end) {+      bool esc = false;+      while ((beg < end) && *beg) {+        if (esc) esc = false;+        else if (*beg == '\\') esc = true;+        else if (const char* pos = skip(beg)) beg = pos;+        else if (mx(beg)) return beg;+        ++beg;+      }+      return 0;+    }     template <prelexer mx>     unsigned int count_interval(const char* beg, const char* end) {       unsigned int counter = 0;@@ -405,10 +434,42 @@     }      template <size_t size, prelexer mx, prelexer pad>-    const char* padded_token(const char* src);+    const char* padded_token(const char* src)+    {+      size_t got = 0;+      const char* pos = src;+      while (got < size) {+        if (!mx(pos)) break;+        ++ pos; ++ got;+      }+      while (got < size) {+        if (!pad(pos)) break;+        ++ pos; ++ got;+      }+      return got ? pos : 0;+    }      template <size_t min, size_t max, prelexer mx>-    const char* minmax_range(const char* src);+    const char* minmax_range(const char* src)+    {+      size_t got = 0;+      const char* pos = src;+      while (got < max) {+        if (!mx(pos)) break;+        ++ pos; ++ got;+      }+      if (got < min) return 0;+      if (got > max) return 0;+      return pos;+    }++    template <char min, char max>+    const char* char_range(const char* src)+    {+      if (*src < min) return 0;+      if (*src > max) return 0;+      return src + 1;+    }    } }
libsass/src/remove_placeholders.cpp view
@@ -1,7 +1,7 @@+#include "sass.hpp" #include "remove_placeholders.hpp" #include "context.hpp" #include "inspect.hpp"-#include "to_string.hpp" #include <iostream>  namespace Sass {@@ -10,45 +10,75 @@     : ctx(ctx)     { } -    void Remove_Placeholders::operator()(Block* b) {+    void Remove_Placeholders::operator()(Block_Ptr b) {         for (size_t i = 0, L = b->length(); i < L; ++i) {-            (*b)[i]->perform(this);+            Statement_Ptr st = &b->at(i);+            st->perform(this);         }     } -    void Remove_Placeholders::operator()(Ruleset* r) {+    Selector_List_Ptr Remove_Placeholders::remove_placeholders(Selector_List_Ptr sl)+    {+      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));+          }+      }++      return new_sl;++    }+++    void Remove_Placeholders::operator()(Ruleset_Ptr r) {         // Create a new selector group without placeholders-        Selector_List* sl = static_cast<Selector_List*>(r->selector());+        Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, r->selector());          if (sl) {-            Selector_List* new_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate());--            for (size_t i = 0, L = sl->length(); i < L; ++i) {-                if (!(*sl)[i]->contains_placeholder()) {-                    *new_sl << (*sl)[i];+          // 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 = SASS_MEMORY_CAST(Wrapped_Selector, ss)) {+                    if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, ws->selector())) {+                      Selector_List_Ptr clean = remove_placeholders(sl);+                      // also clean superflous parent selectors+                      // probably not really the correct place+                      clean->remove_parent_selectors();+                      ws->selector(clean);+                    }+                  }                 }+              }+              cs = cs->tail();             }--            // Set the new placeholder selector list-            r->selector(new_sl);+          }         }          // Iterate into child blocks-        Block* b = r->block();+        Block_Obj b = r->block();          for (size_t i = 0, L = b->length(); i < L; ++i) {-            if ((*b)[i]) (*b)[i]->perform(this);+            if (b->at(i)) {+                Statement_Obj st = b->at(i);+                st->perform(this);+            }         }     } -    void Remove_Placeholders::operator()(Media_Block* m) {-        Block* b = m->block();-        for (size_t i = 0, L = b->length(); i < L; ++i) {-            if ((*b)[i]) (*b)[i]->perform(this);-        }+    void Remove_Placeholders::operator()(Media_Block_Ptr m) {+        operator()(&m->block());     }+    void Remove_Placeholders::operator()(Supports_Block_Ptr m) {+        operator()(&m->block());+    } -    void Remove_Placeholders::operator()(At_Rule* a) {+    void Remove_Placeholders::operator()(Directive_Ptr a) {         if (a->block()) a->block()->perform(this);     } 
libsass/src/remove_placeholders.hpp view
@@ -15,18 +15,20 @@          Context&          ctx; -        void fallback_impl(AST_Node* n) {}+        void fallback_impl(AST_Node_Ptr n) {}      public:-        Remove_Placeholders(Context&);-        virtual ~Remove_Placeholders() { }+      Selector_List_Ptr remove_placeholders(Selector_List_Ptr); -        using Operation<void>::operator();+    public:+        Remove_Placeholders(Context&);+        ~Remove_Placeholders() { } -        void operator()(Block*);-        void operator()(Ruleset*);-        void operator()(Media_Block*);-        void operator()(At_Rule*);+        void operator()(Block_Ptr);+        void operator()(Ruleset_Ptr);+        void operator()(Media_Block_Ptr);+        void operator()(Supports_Block_Ptr);+        void operator()(Directive_Ptr);          template <typename U>         void fallback(U x) { return fallback_impl(x); }
libsass/src/sass.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstdlib> #include <cstring> #include <vector>@@ -10,18 +11,42 @@ extern "C" {   using namespace Sass; +  // Allocate libsass heap memory+  // Don't forget string termination!+  void* ADDCALL sass_alloc_memory(size_t size)+  {+    void* ptr = malloc(size);+    if (ptr == NULL)+      out_of_memory();+    return ptr;+  }++  char* ADDCALL sass_copy_c_string(const char* str)+  {+    size_t len = strlen(str) + 1;+    char* cpy = (char*) sass_alloc_memory(len);+    std::memcpy(cpy, str, len);+    return cpy;+  }++  // Deallocate libsass heap memory+  void ADDCALL sass_free_memory(void* ptr)+  {+    if (ptr) free (ptr);+  }+   // caller must free the returned memory   char* ADDCALL sass_string_quote (const char *str, const char quote_mark)   {     std::string quoted = quote(str, quote_mark);-    return sass_strdup(quoted.c_str());+    return sass_copy_c_string(quoted.c_str());   }    // caller must free the returned memory   char* ADDCALL sass_string_unquote (const char *str)   {     std::string unquoted = unquote(str);-    return sass_strdup(unquoted.c_str());+    return sass_copy_c_string(unquoted.c_str());   }    // Make sure to free the returned value!@@ -29,13 +54,40 @@   char* ADDCALL sass_resolve_file (const char* file, const char* paths[])   {     std::string resolved(File::find_file(file, paths));-    return sass_strdup(resolved.c_str());+    return sass_copy_c_string(resolved.c_str());   }    // Get compiled libsass version   const char* ADDCALL libsass_version(void)   {     return LIBSASS_VERSION;+  }++  // Get compiled libsass version+  const char* ADDCALL libsass_language_version(void)+  {+    return LIBSASS_LANGUAGE_VERSION;+  }++}++namespace Sass {++  // helper to aid dreaded MSVC debug mode+  char* sass_copy_string(std::string str)+  {+    // In MSVC the following can lead to segfault:+    // sass_copy_c_string(stream.str().c_str());+    // Reason is that the string returned by str() is disposed before+    // sass_copy_c_string is invoked. The string is actually a stack+    // object, so indeed nobody is holding on to it. So it seems+    // perfectly fair to release it right away. So the const char*+    // by c_str will point to invalid memory. I'm not sure if this is+    // the behavior for all compiler, but I'm pretty sure we would+    // have gotten more issues reported if that would be the case.+    // Wrapping it in a functions seems the cleanest approach as the+    // function must hold on to the stack variable until it's done.+    return sass_copy_c_string(str.c_str());   }  }
+ libsass/src/sass.hpp view
@@ -0,0 +1,136 @@+// must be the first include in all compile units+#ifndef SASS_SASS_H+#define SASS_SASS_H++// undefine extensions macro to tell sys includes+// that we do not want any macros to be exported+// mainly fixes an issue on SmartOS (SEC macro)+#undef __EXTENSIONS__++#ifdef _MSC_VER+#pragma warning(disable : 4005)+#endif++// aplies to MSVC and MinGW+#ifdef _WIN32+// we do not want the ERROR macro+# define NOGDI+// we do not want the min/max macro+# define NOMINMAX+// we do not want the IN/OUT macro+# define _NO_W32_PSEUDO_MODIFIERS+#endif+++// should we be case insensitive+// when dealing with files or paths+#ifndef FS_CASE_SENSITIVE+# ifdef _WIN32+#  define FS_CASE_SENSITIVE 0+# else+#  define FS_CASE_SENSITIVE 1+# endif+#endif++// path separation char+#ifndef PATH_SEP+# ifdef _WIN32+#  define PATH_SEP ';'+# else+#  define PATH_SEP ':'+# endif+#endif+++// include C-API header+#include "sass/base.h"++// For C++ helper+#include <string>++// output behaviours+namespace Sass {++  // create some C++ aliases for the most used options+  const static Sass_Output_Style NESTED = SASS_STYLE_NESTED;+  const static Sass_Output_Style COMPACT = SASS_STYLE_COMPACT;+  const static Sass_Output_Style EXPANDED = SASS_STYLE_EXPANDED;+  const static Sass_Output_Style COMPRESSED = SASS_STYLE_COMPRESSED;+  // 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;++  // helper to aid dreaded MSVC debug mode+  // see implementation for more details+  char* sass_copy_string(std::string str);++}++// input behaviours+enum Sass_Input_Style {+  SASS_CONTEXT_NULL,+  SASS_CONTEXT_FILE,+  SASS_CONTEXT_DATA,+  SASS_CONTEXT_FOLDER+};++// simple linked list+struct string_list {+  string_list* next;+  char* string;+};++// sass config options structure+struct Sass_Inspect_Options {++  // Output style for the generated css code+  // A value from above SASS_STYLE_* constants+  enum Sass_Output_Style output_style;++  // Precision for fractional numbers+  int precision;++  // initialization list (constructor with defaults)+  Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED,+                       int precision = 5)+  : output_style(style), precision(precision)+  { }++};++// sass config options structure+struct Sass_Output_Options : Sass_Inspect_Options {++  // String to be used for indentation+  const char* indent;+  // String to be used to for line feeds+  const char* linefeed;++  // Emit comments in the generated CSS indicating+  // the corresponding source line.+  bool source_comments;++  // initialization list (constructor with defaults)+  Sass_Output_Options(struct Sass_Inspect_Options opt,+                      const char* indent = "  ",+                      const char* linefeed = "\n",+                      bool source_comments = false)+  : Sass_Inspect_Options(opt),+    indent(indent), linefeed(linefeed),+    source_comments(source_comments)+  { }++  // initialization list (constructor with defaults)+  Sass_Output_Options(Sass_Output_Style style = Sass::NESTED,+                      int precision = 5,+                      const char* indent = "  ",+                      const char* linefeed = "\n",+                      bool source_comments = false)+  : Sass_Inspect_Options(style, precision),+    indent(indent), linefeed(linefeed),+    source_comments(source_comments)+  { }++};++#endif
libsass/src/sass2scss.cpp view
@@ -528,6 +528,13 @@ 			// reset converter state 			converter.selector = false; +			// looks like some undocumented behavior ...+			// https://github.com/mgreter/sass2scss/issues/29+			if (sass.substr(pos_left, 1) == "\\") {+				converter.selector = true;+				sass[pos_left] = ' ';+			}+ 			// check if we have sass property syntax 			if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::") 			{@@ -567,6 +574,12 @@ 					} 				} +				// check if we have a BEM property (one colon and no selector)+				if (sass.substr(pos_left, 1) == ":" && converter.selector == true) {+					size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left);+					sass = indent + sass.substr(pos_left + 1, pos_wspace) + ":";+				}+ 			}  			// terminate some statements immediately@@ -574,13 +587,19 @@ 				sass.substr(pos_left, 5) == "@warn" || 				sass.substr(pos_left, 6) == "@debug" || 				sass.substr(pos_left, 6) == "@error" ||-				sass.substr(pos_left, 8) == "@charset"+				sass.substr(pos_left, 8) == "@charset" ||+				sass.substr(pos_left, 10) == "@namespace" 			) { sass = indent + sass.substr(pos_left); } 			// replace some specific sass shorthand directives (if not fallowed by a white space character)-			else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)+			else if (sass.substr(pos_left, 1) == "=") 			{ sass = indent + "@mixin " + sass.substr(pos_left + 1); }-			else if (sass.substr(pos_left, 1) == "+" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)-			{ sass = indent + "@include " + sass.substr(pos_left + 1); }+			else if (sass.substr(pos_left, 1) == "+")+			{+				// must be followed by a mixin call (no whitespace afterwards or at ending directly)+				if (sass[pos_left+1] != 0 && sass[pos_left+1] != ' ' && sass[pos_left+1] != '\t') {+					sass = indent + "@include " + sass.substr(pos_left + 1);+				}+			}  			// add quotes for import if needed 			else if (sass.substr(pos_left, 7) == "@import")
libsass/src/sass_context.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstring> #include <stdexcept> #include <sstream>@@ -5,82 +6,88 @@ #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"  #define LFEED "\n" -extern "C" {-  using namespace Sass;--  static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { *to = *from; }--  #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \-    type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \-    void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; }-  #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \-    type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \-    void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \-    { free(options->option); options->option = option || def ? sass_strdup(option ? option : def) : 0; }--  #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \-    type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; }-  #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \-    type sass_context_take_##option (struct Sass_Context* ctx) \-    { type foo = ctx->option; ctx->option = 0; return foo; }+// C++ helper+namespace Sass {+  // see sass_copy_c_string(std::string str)+  static inline JsonNode* json_mkstream(const std::stringstream& stream)+  {+    // hold on to string on stack!+    std::string str(stream.str());+    return json_mkstring(str.c_str());+  } -  static int handle_errors(Sass_Context* c_ctx) {+  static int handle_error(Sass_Context* c_ctx) {     try {-     throw;+      throw;     }     catch (Exception::Base& e) {       std::stringstream msg_stream;       std::string cwd(Sass::File::get_cwd());-      std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); -      std::string msg_prefix("Error: ");+      std::string msg_prefix(e.errtype());       bool got_newline = false;-      msg_stream << msg_prefix;+      msg_stream << msg_prefix << ": ";       const char* msg = e.what();-      while(msg && *msg) {+      while (msg && *msg) {         if (*msg == '\r') {           got_newline = true;-        } else if (*msg == '\n') {+        }+        else if (*msg == '\n') {           got_newline = true;-        } else if (got_newline) {-          msg_stream << std::string(msg_prefix.size(), ' ');+        }+        else if (got_newline) {+          msg_stream << std::string(msg_prefix.size() + 2, ' ');           got_newline = false;         }         msg_stream << *msg;-        ++ msg;+        ++msg;       }       if (!got_newline) msg_stream << "\n";-      msg_stream << std::string(msg_prefix.size(), ' ');-      msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";+      if (e.import_stack) {+        for (size_t i = 1; i < e.import_stack->size() - 1; ++i) {+          std::string path((*e.import_stack)[i]->imp_path);+          std::string rel_path(Sass::File::abs2rel(path, cwd, cwd));+          msg_stream << std::string(msg_prefix.size() + 2, ' ');+          msg_stream << (i == 1 ? " on line " : " from line ");+          msg_stream << e.pstate.line + 1 << " of " << rel_path << "\n";+        }+      }+      else {+        std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));+        msg_stream << std::string(msg_prefix.size() + 2, ' ');+        msg_stream << " on line " << e.pstate.line + 1 << " of " << rel_path << "\n";+      }        // now create the code trace (ToDo: maybe have util functions?)       if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) {         size_t line = e.pstate.line;         const char* line_beg = e.pstate.src;         while (line_beg && *line_beg && line) {-          if (*line_beg == '\n') -- line;-          ++ line_beg;+          if (*line_beg == '\n') --line;+          ++line_beg;         }         const char* line_end = line_beg;         while (line_end && *line_end && *line_end != '\n') {           if (*line_end == '\n') break;           if (*line_end == '\r') break;-          line_end ++;+          line_end++;         }         size_t max_left = 42; size_t max_right = 78;         size_t move_in = e.pstate.column > max_left ? e.pstate.column - max_left : 0;         size_t shorten = (line_end - line_beg) - move_in > max_right ?-                         (line_end - line_beg) - move_in - max_right : 0;+          (line_end - line_beg) - move_in - max_right : 0;         msg_stream << ">> " << std::string(line_beg + move_in, line_end - shorten) << "\n";         msg_stream << "   " << std::string(e.pstate.column - move_in, '-') << "^\n";       }@@ -88,18 +95,18 @@       JsonNode* json_err = json_mkobject();       json_append_member(json_err, "status", json_mknumber(1));       json_append_member(json_err, "file", json_mkstring(e.pstate.path));-      json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line+1)));-      json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column+1)));+      json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line + 1)));+      json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column + 1)));       json_append_member(json_err, "message", json_mkstring(e.what()));-      json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str()));--      c_ctx->error_json = json_stringify(json_err, "  ");;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_text = sass_strdup(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 = 1;-      c_ctx->error_file = sass_strdup(e.pstate.path);-      c_ctx->error_line = e.pstate.line+1;-      c_ctx->error_column = e.pstate.column+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->output_string = 0;       c_ctx->source_map_string = 0;@@ -111,9 +118,11 @@       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()));-      c_ctx->error_json = json_stringify(json_err, "  ");;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_text = sass_strdup(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;@@ -122,12 +131,14 @@     catch (std::exception& e) {       std::stringstream msg_stream;       JsonNode* json_err = json_mkobject();-      msg_stream << "Error: " << e.what() << std::endl;+      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()));-      c_ctx->error_json = json_stringify(json_err, "  ");;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_text = sass_strdup(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;@@ -136,12 +147,14 @@     catch (std::string& e) {       std::stringstream msg_stream;       JsonNode* json_err = json_mkobject();-      msg_stream << "Error: " << e << std::endl;+      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()));-      c_ctx->error_json = json_stringify(json_err, "  ");;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_text = sass_strdup(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;@@ -150,12 +163,14 @@     catch (const char* e) {       std::stringstream msg_stream;       JsonNode* json_err = json_mkobject();-      msg_stream << "Error: " << e << std::endl;+      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_ctx->error_json = json_stringify(json_err, "  ");;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_text = sass_strdup(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;@@ -167,9 +182,10 @@       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"));-      c_ctx->error_json = json_stringify(json_err, "  ");;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_text = sass_strdup("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;@@ -178,45 +194,100 @@     return c_ctx->error_status;   } -  // generic compilation function (not exported, use file/data compile instead)-  static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw()+  // allow one error handler to throw another error+  // this can happen with invalid utf8 and json lib+  static int handle_errors(Sass_Context* c_ctx) {+    try { return handle_error(c_ctx); }+    catch (...) { return handle_error(c_ctx); }+    return c_ctx->error_status;+  }++  static Block_Obj sass_parse_block(Sass_Compiler* compiler) throw()   {++    // assert valid pointer+    if (compiler == 0) return 0;+    // The cpp context must be set by now+    Context* cpp_ctx = compiler->cpp_ctx;+    Sass_Context* c_ctx = compiler->c_ctx;+    // We will take care to wire up the rest+    compiler->cpp_ctx->c_compiler = compiler;+    compiler->state = SASS_COMPILER_PARSED;+     try { -      // convert include path linked list to static array-      struct string_list* inc = c_ctx->include_paths;-      // very poor loop to get the length of the linked list-      size_t inc_size = 0; while (inc) { inc_size ++; inc = inc->next; }-      // create char* array to hold all paths plus null terminator-      const char** include_paths = (const char**) calloc(inc_size + 1, sizeof(char*));-      if (include_paths == 0) throw(std::bad_alloc());-      // reset iterator-      inc = c_ctx->include_paths;-      // copy over the paths-      for (size_t i = 0; inc; i++) {-        include_paths[i] = inc->string;-        inc = inc->next;-      }+      // get input/output path from options+      std::string input_path = safe_str(c_ctx->input_path);+      std::string output_path = safe_str(c_ctx->output_path); -      // convert plugin path linked list to static array-      struct string_list* imp = c_ctx->plugin_paths;-      // very poor loop to get the length of the linked list-      size_t imp_size = 0; while (imp) { imp_size ++; imp = imp->next; }-      // create char* array to hold all paths plus null terminator-      const char** plugin_paths = (const char**) calloc(imp_size + 1, sizeof(char*));-      if (plugin_paths == 0) throw(std::bad_alloc());-      // reset iterator-      imp = c_ctx->plugin_paths;-      // copy over the paths-      for (size_t i = 0; imp; i++) {-        plugin_paths[i] = imp->string;-        imp = imp->next;-      }+      // maybe skip some entries of included files+      // we do not include stdin for data contexts+      bool skip = c_ctx->type == SASS_CONTEXT_DATA; -      // free intermediate data-      free(include_paths);-      free(plugin_paths);+      // dispatch parse call+      Block_Obj root(cpp_ctx->parse());+      // abort on errors+      if (!root) return 0; +      // skip all prefixed files? (ToDo: check srcmap)+      // IMO source-maps should point to headers already+      // therefore don't skip it for now. re-enable or+      // remove completely once this is tested+      size_t headers = cpp_ctx->head_imports;++      // copy the included files on to the context (dont forget to free later)+      if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL)+        throw(std::bad_alloc());++      // return parsed block+      return root;++    }+    // pass errors to generic error handler+    catch (...) { handle_errors(c_ctx); }++    // error+    return 0;++  }++}++extern "C" {+  using namespace Sass;++  static void sass_clear_options (struct Sass_Options* options);+  static void sass_reset_options (struct Sass_Options* options);+  static void copy_options(struct Sass_Options* to, struct Sass_Options* from) {+    // do not overwrite ourself+    if (to == from) return;+    // free assigned memory+    sass_clear_options(to);+    // move memory+    *to = *from;+    // Reset pointers on source+    sass_reset_options(from);+  }++  #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \+    type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \+    void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; }+  #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \+    type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \+    void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \+    { free(options->option); options->option = option || def ? sass_copy_c_string(option ? option : def) : 0; }++  #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \+    type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; }+  #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \+    type sass_context_take_##option (struct Sass_Context* ctx) \+    { type foo = ctx->option; ctx->option = 0; return foo; }+++  // generic compilation function (not exported, use file/data compile instead)+  static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw()+  {+    try {       // register our custom functions       if (c_ctx->c_functions) {         auto this_func_data = c_ctx->c_functions;@@ -276,58 +347,6 @@    } -  static Block* sass_parse_block (Sass_Compiler* compiler) throw()-  {--    // assert valid pointer-    if (compiler == 0) return 0;-    // The cpp context must be set by now-    Context* cpp_ctx = compiler->cpp_ctx;-    Sass_Context* c_ctx = compiler->c_ctx;-    // We will take care to wire up the rest-    compiler->cpp_ctx->c_compiler = compiler;-    compiler->state = SASS_COMPILER_PARSED;--    try {--      // get input/output path from options-      std::string input_path = safe_str(c_ctx->input_path);-      std::string output_path = safe_str(c_ctx->output_path);--      // parsed root block-      Block* root = 0;--      // maybe skip some entries of included files-      // we do not include stdin for data contexts-      bool skip = c_ctx->type == SASS_CONTEXT_DATA;--      // dispatch parse call-      root = cpp_ctx->parse();-      // abort on errors-      if (!root) return 0;--      // skip all prefixed files? (ToDo: check srcmap)-      // IMO source-maps should point to headers already-      // therefore don't skip it for now. re-enable or-      // remove completely once this is tested-      size_t headers = cpp_ctx->head_imports;--      // copy the included files on to the context (dont forget to free later)-      if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL)-        throw(std::bad_alloc());--      // return parsed block-      return root;--    }-    // pass errors to generic error handler-    catch (...) { handle_errors(c_ctx); }--    // error-    return 0;--  }-   // generic compilation function (not exported, use file/data compile instead)   static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx)   {@@ -365,6 +384,7 @@    Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path)   {+    SharedObj::setTaint(true); // needed for static colors     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;@@ -398,14 +418,14 @@   struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx)   {     if (data_ctx == 0) return 0;-    Context* cpp_ctx = new Data_Context(data_ctx);+    Context* cpp_ctx = new Data_Context(*data_ctx);     return sass_prepare_context(data_ctx, cpp_ctx);   }    struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx)   {     if (file_ctx == 0) return 0;-    Context* cpp_ctx = new File_Context(file_ctx);+    Context* cpp_ctx = new File_Context(*file_ctx);     return sass_prepare_context(file_ctx, cpp_ctx);   } @@ -416,10 +436,11 @@       return data_ctx->error_status;     try {       if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); }-      if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); }+      // empty source string is a valid case, even if not really usefull (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; }-    Context* cpp_ctx = new Data_Context(data_ctx);+    Context* cpp_ctx = new Data_Context(*data_ctx);     return sass_compile_context(data_ctx, cpp_ctx);   } @@ -433,7 +454,7 @@       if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); }     }     catch (...) { return handle_errors(file_ctx) | 1; }-    Context* cpp_ctx = new File_Context(file_ctx);+    Context* cpp_ctx = new File_Context(*file_ctx);     return sass_compile_context(file_ctx, cpp_ctx);   } @@ -447,7 +468,7 @@     if (compiler->c_ctx->error_status)       return compiler->c_ctx->error_status;     // parse the context we have set up (file or data)-    compiler->root = sass_parse_block(compiler);+    compiler->root = &sass_parse_block(compiler);     // success     return 0;   }@@ -459,12 +480,12 @@     if (compiler->state != SASS_COMPILER_PARSED) return -1;     if (compiler->c_ctx == NULL) return 1;     if (compiler->cpp_ctx == NULL) return 1;-    if (compiler->root == NULL) return 1;+    if (compiler->root.isNull()) return 1;     if (compiler->c_ctx->error_status)       return compiler->c_ctx->error_status;     compiler->state = SASS_COMPILER_EXECUTED;     Context* cpp_ctx = compiler->cpp_ctx;-    Block* root = compiler->root;+    Block_Obj root = compiler->root;     // compile the parsed root block     try { compiler->c_ctx->output_string = cpp_ctx->render(root); }     // pass catched errors to generic error handler@@ -476,6 +497,24 @@   }    // helper function, not exported, only accessible locally+  static void sass_reset_options (struct Sass_Options* options)+  {+    // free pointer before+    // or copy/move them+    options->input_path = 0;+    options->output_path = 0;+    options->plugin_path = 0;+    options->include_path = 0;+    options->source_map_file = 0;+    options->source_map_root = 0;+    options->c_functions = 0;+    options->c_importers = 0;+    options->c_headers = 0;+    options->plugin_paths = 0;+    options->include_paths = 0;+  }++  // helper function, not exported, only accessible locally   static void sass_clear_options (struct Sass_Options* options)   {     if (options == 0) return;@@ -527,12 +566,25 @@         cur = next;       }     }+    // Free options strings+    free(options->input_path);+    free(options->output_path);+    free(options->plugin_path);+    free(options->include_path);+    free(options->source_map_file);+    free(options->source_map_root);     // Free custom functions     free(options->c_functions);     // Free custom importers     free(options->c_importers);     free(options->c_headers);     // Reset our pointers+    options->input_path = 0;+    options->output_path = 0;+    options->plugin_path = 0;+    options->include_path = 0;+    options->source_map_file = 0;+    options->source_map_root = 0;     options->c_functions = 0;     options->c_importers = 0;     options->c_headers = 0;@@ -545,19 +597,13 @@   static void sass_clear_context (struct Sass_Context* ctx)   {     if (ctx == 0) return;-    // release the allocated memory (mostly via sass_strdup)+    // release the allocated memory (mostly via sass_copy_c_string)     if (ctx->output_string)     free(ctx->output_string);     if (ctx->source_map_string) free(ctx->source_map_string);     if (ctx->error_message)     free(ctx->error_message);     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->input_path)        free(ctx->input_path);-    if (ctx->output_path)       free(ctx->output_path);-    if (ctx->plugin_path)       free(ctx->plugin_path);-    if (ctx->include_path)      free(ctx->include_path);-    if (ctx->source_map_file)   free(ctx->source_map_file);-    if (ctx->source_map_root)   free(ctx->source_map_root);     free_string_array(ctx->included_files);     // play safe and reset properties     ctx->output_string = 0;@@ -566,12 +612,11 @@     ctx->error_text = 0;     ctx->error_json = 0;     ctx->error_file = 0;-    ctx->input_path = 0;-    ctx->output_path = 0;-    ctx->include_path = 0;-    ctx->source_map_file = 0;-    ctx->source_map_root = 0;     ctx->included_files = 0;+    // debug leaked memory+    #ifdef DEBUG_SHARED_PTR+      SharedObj::dumpMemLeaks();+    #endif     // now clear the options     sass_clear_options(ctx);   }@@ -583,10 +628,17 @@     }     Context* cpp_ctx = compiler->cpp_ctx;     if (cpp_ctx) delete(cpp_ctx);-    compiler->cpp_ctx = 0;+    compiler->cpp_ctx = NULL;+    compiler->c_ctx = NULL;+    compiler->root = NULL;     free(compiler);   } +  void ADDCALL sass_delete_options (struct Sass_Options* options)+  {+    sass_clear_options(options); free(options);+  }+   // Deallocate all associated memory with file context   void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx)   {@@ -633,6 +685,7 @@   IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_comments);   IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed);   IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents);+  IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_file_urls);   IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url);   IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src);   IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions);@@ -675,7 +728,7 @@      struct string_list* include_path = (struct string_list*) calloc(1, sizeof(struct string_list));     if (include_path == 0) return;-    include_path->string = path ? sass_strdup(path) : 0;+    include_path->string = path ? sass_copy_c_string(path) : 0;     struct string_list* last = options->include_paths;     if (!options->include_paths) {       options->include_paths = include_path;@@ -693,7 +746,7 @@      struct string_list* plugin_path = (struct string_list*) calloc(1, sizeof(struct string_list));     if (plugin_path == 0) return;-    plugin_path->string = path ? sass_strdup(path) : 0;+    plugin_path->string = path ? sass_copy_c_string(path) : 0;     struct string_list* last = options->plugin_paths;     if (!options->plugin_paths) {       options->plugin_paths = plugin_path;
libsass/src/sass_context.hpp view
@@ -2,36 +2,12 @@ #define SASS_SASS_CONTEXT_H  #include "sass.h"+#include "sass.hpp" #include "context.hpp" #include "ast_fwd_decl.hpp" -// Input behaviours-enum Sass_Input_Style {-  SASS_CONTEXT_NULL,-  SASS_CONTEXT_FILE,-  SASS_CONTEXT_DATA,-  SASS_CONTEXT_FOLDER-};--// simple linked list-struct string_list {-  string_list* next;-  char* string;-};- // sass config options structure-struct Sass_Options {--  // Precision for fractional numbers-  int precision;--  // Output style for the generated css code-  // A value from above SASS_STYLE_* constants-  enum Sass_Output_Style output_style;--  // Emit comments in the generated CSS indicating-  // the corresponding source line.-  bool source_comments;+struct Sass_Options : Sass_Output_Options {    // embed sourceMappingUrl as data uri   bool source_map_embed;@@ -39,6 +15,9 @@   // embed include contents in maps   bool source_map_contents; +  // create file urls for sources+  bool source_map_file_urls;+   // Disable sourceMappingUrl in css output   bool omit_source_map_url; @@ -59,11 +38,6 @@   // information in source-maps etc.   char* output_path; -  // String to be used for indentation-  const char* indent;-  // String to be used to for line feeds-  const char* linefeed;-   // Colon-separated list of paths   // Semicolon-separated on Windows   // Maybe use array interface instead?@@ -150,7 +124,7 @@   // Sass::Context   Sass::Context* cpp_ctx;   // Sass::Block-  Sass::Block* root;+  Sass::Block_Obj root; };  #endif
libsass/src/sass_functions.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstring> #include "util.hpp" #include "context.hpp"@@ -71,8 +72,8 @@   {     Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import));     if (v == 0) return 0;-    v->imp_path = imp_path ? sass_strdup(imp_path) : 0;-    v->abs_path = abs_path ? sass_strdup(abs_path) : 0;+    v->imp_path = imp_path ? sass_copy_c_string(imp_path) : 0;+    v->abs_path = abs_path ? sass_copy_c_string(abs_path) : 0;     v->source = source;     v->srcmap = srcmap;     v->error = 0;@@ -92,7 +93,7 @@   {     if (import == 0) return 0;     if (import->error) free(import->error);-    import->error = error ? sass_strdup(error) : 0;+    import->error = error ? sass_copy_c_string(error) : 0;     import->line = line ? line : -1;     import->column = col ? col : -1;     return import;
− libsass/src/sass_interface.cpp
@@ -1,216 +0,0 @@-#include <string>-#include <cstdlib>-#include <cstring>-#include <sstream>-#include <iostream>--#include "util.hpp"-#include "context.hpp"-#include "inspect.hpp"-#include "error_handling.hpp"-#include "sass/base.h"-#include "sass/interface.h"--#define LFEED "\n"--extern "C" {--  sass_context* sass_new_context()-  { return (sass_context*) calloc(1, sizeof(sass_context)); }--  void sass_free_context(sass_context* ctx)-  {-    if (ctx->output_string)     free(ctx->output_string);-    if (ctx->source_map_string) free(ctx->source_map_string);-    if (ctx->error_message)     free(ctx->error_message);-    if (ctx->c_functions)       free(ctx->c_functions);--    Sass::free_string_array(ctx->included_files);--    free(ctx);-  }--  sass_file_context* sass_new_file_context()-  { return (sass_file_context*) calloc(1, sizeof(sass_file_context)); }--  void sass_free_file_context(sass_file_context* ctx)-  {-    if (ctx->output_string)     free(ctx->output_string);-    if (ctx->source_map_string) free(ctx->source_map_string);-    if (ctx->error_message)     free(ctx->error_message);-    if (ctx->c_functions)       free(ctx->c_functions);--    Sass::free_string_array(ctx->included_files);--    free(ctx);-  }--  sass_folder_context* sass_new_folder_context()-  { return (sass_folder_context*) calloc(1, sizeof(sass_folder_context)); }--  void sass_free_folder_context(sass_folder_context* ctx)-  {-    Sass::free_string_array(ctx->included_files);-    free(ctx);-  }--  int sass_compile(sass_context* c_ctx)-  {-    using namespace Sass;-    try {-      std::string input_path = safe_str(c_ctx->input_path);-      int lastindex = static_cast<int>(input_path.find_last_of("."));-      std::string output_path;-      if (!c_ctx->output_path) {-        if (input_path != "") {-          output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";-        }-      }-      else {-          output_path = c_ctx->output_path;-      }-      Data_Context cpp_ctx(-        (Sass_Data_Context*) 0-      );-      if (c_ctx->c_functions) {-        Sass_Function_List this_func_data = c_ctx->c_functions;-        while ((this_func_data) && (*this_func_data)) {-          cpp_ctx.c_functions.push_back(*this_func_data);-          ++this_func_data;-        }-      }-      Block* root = cpp_ctx.parse();-      c_ctx->output_string = cpp_ctx.render(root);-      c_ctx->source_map_string = cpp_ctx.render_srcmap();-      c_ctx->error_message = 0;-      c_ctx->error_status = 0;--      if (copy_strings(cpp_ctx.get_included_files(true), &c_ctx->included_files, 1) == NULL)-        throw(std::bad_alloc());-    }-    catch (Exception::InvalidSass& e) {-      std::stringstream msg_stream;-      msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch(std::bad_alloc& ba) {-      std::stringstream msg_stream;-      msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch (std::exception& e) {-      std::stringstream msg_stream;-      msg_stream << "Error: " << e.what() << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch (std::string& e) {-      std::stringstream msg_stream;-      msg_stream << "Error: " << e << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch (...) {-      // couldn't find the specified file in the include paths; report an error-      std::stringstream msg_stream;-      msg_stream << "Unknown error occurred" << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    return 0;-  }--  int sass_compile_file(sass_file_context* c_ctx)-  {-    using namespace Sass;-    try {-      std::string input_path = safe_str(c_ctx->input_path);-      int lastindex = static_cast<int>(input_path.find_last_of("."));-      std::string output_path;-      if (!c_ctx->output_path) {-          output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";-      }-      else {-          output_path = c_ctx->output_path;-      }-      File_Context cpp_ctx(-        (Sass_File_Context*) 0-      );-      if (c_ctx->c_functions) {-        Sass_Function_List this_func_data = c_ctx->c_functions;-        while ((this_func_data) && (*this_func_data)) {-          cpp_ctx.c_functions.push_back(*this_func_data);-          ++this_func_data;-        }-      }-      Block* root = cpp_ctx.parse();-      c_ctx->output_string = cpp_ctx.render(root);-      c_ctx->source_map_string = cpp_ctx.render_srcmap();-      c_ctx->error_message = 0;-      c_ctx->error_status = 0;--      if (copy_strings(cpp_ctx.get_included_files(false), &c_ctx->included_files) == NULL)-        throw(std::bad_alloc());-    }-    catch (Exception::InvalidSass& e) {-      std::stringstream msg_stream;-      msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch(std::bad_alloc& ba) {-      std::stringstream msg_stream;-      msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch (std::exception& e) {-      std::stringstream msg_stream;-      msg_stream << "Error: " << e.what() << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch (std::string& e) {-      std::stringstream msg_stream;-      msg_stream << "Error: " << e << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    catch (...) {-      // couldn't find the specified file in the include paths; report an error-      std::stringstream msg_stream;-      msg_stream << "Unknown error occurred" << std::endl;-      c_ctx->error_message = sass_strdup(msg_stream.str().c_str());-      c_ctx->error_status = 1;-      c_ctx->output_string = 0;-      c_ctx->source_map_string = 0;-    }-    return 0;-  }--  int sass_compile_folder(sass_folder_context* c_ctx)-  {-    return 1;-  }--}
libsass/src/sass_util.cpp view
@@ -1,5 +1,5 @@+#include "sass.hpp" #include "node.hpp"-#include "to_string.hpp"  namespace Sass { @@ -38,7 +38,6 @@     end   */   Node paths(const Node& arrs, Context& ctx) {-    To_String to_string(&ctx);      Node loopStart = Node::createCollection();     loopStart.collection()->push_back(Node::createCollection());
libsass/src/sass_util.hpp view
@@ -40,7 +40,7 @@     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) {+      if (nodesEqual(one, two, true)) {         out = one;         return true;       }@@ -221,28 +221,28 @@      std::map<unsigned int, KeyType> order; -    std::map<KeyType, std::vector<EnumType> > grouped;+    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) == grouped.end()) {+      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, newCollection));+        grouped.insert(std::make_pair(key->hash(), newCollection));       } else {-        std::vector<EnumType>& collection = grouped.at(key);+        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);+      std::vector<EnumType>& values = grouped.at(key->hash());        std::pair<KeyType, std::vector<EnumType> > grouping = std::make_pair(key, values); 
libsass/src/sass_values.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstdlib> #include <cstring> #include "util.hpp"@@ -90,7 +91,7 @@     if (v == 0) return 0;     v->number.tag = SASS_NUMBER;     v->number.value = val;-    v->number.unit = unit ? sass_strdup(unit) : 0;+    v->number.unit = unit ? sass_copy_c_string(unit) : 0;     if (v->number.unit == 0) { free(v); return 0; }     return v;   }@@ -113,7 +114,7 @@     if (v == 0) return 0;     v->string.quoted = false;     v->string.tag = SASS_STRING;-    v->string.value = val ? sass_strdup(val) : 0;+    v->string.value = val ? sass_copy_c_string(val) : 0;     if (v->string.value == 0) { free(v); return 0; }     return v;   }@@ -124,7 +125,7 @@     if (v == 0) return 0;     v->string.quoted = true;     v->string.tag = SASS_STRING;-    v->string.value = val ? sass_strdup(val) : 0;+    v->string.value = val ? sass_copy_c_string(val) : 0;     if (v->string.value == 0) { free(v); return 0; }     return v;   }@@ -165,7 +166,7 @@     union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));     if (v == 0) return 0;     v->error.tag = SASS_ERROR;-    v->error.message = msg ? sass_strdup(msg) : 0;+    v->error.message = msg ? sass_copy_c_string(msg) : 0;     if (v->error.message == 0) { free(v); return 0; }     return v;   }@@ -175,7 +176,7 @@     union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));     if (v == 0) return 0;     v->warning.tag = SASS_WARNING;-    v->warning.message = msg ? sass_strdup(msg) : 0;+    v->warning.message = msg ? sass_copy_c_string(msg) : 0;     if (v->warning.message == 0) { free(v); return 0; }     return v;   }@@ -274,58 +275,60 @@    union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* v, bool compressed, int precision)   {-    Memory_Manager mem;-    Value* val = sass_value_to_ast_node(mem, v);-    std::string str(val->to_string(compressed, precision));+    Value_Obj val = sass_value_to_ast_node(v);+    Sass_Inspect_Options options(compressed ? COMPRESSED : NESTED, precision);+    std::string str(val->to_string(options));     return sass_make_qstring(str.c_str());   }    union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b)   { -    Sass::Value* rv = 0;-    Memory_Manager mem;+    Sass::Value_Ptr rv = 0;      try { -      Value* lhs = sass_value_to_ast_node(mem, a);-      Value* rhs = sass_value_to_ast_node(mem, b);+      Value_Obj lhs = sass_value_to_ast_node(a);+      Value_Obj rhs = sass_value_to_ast_node(b);+      struct Sass_Inspect_Options options(NESTED, 5);        // see if it's a relational expression       switch(op) {-        case Sass_OP::EQ:  return sass_make_boolean(Eval::eq(lhs, rhs));-        case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs));-        case Sass_OP::GT:  return sass_make_boolean(!Eval::lt(lhs, rhs) && !Eval::eq(lhs, rhs));-        case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs));-        case Sass_OP::LT:  return sass_make_boolean(Eval::lt(lhs, rhs));-        case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs) || Eval::eq(lhs, rhs));+        case Sass_OP::EQ:  return sass_make_boolean(Eval::eq(&lhs, &rhs));+        case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(&lhs, &rhs));+        case Sass_OP::GT:  return sass_make_boolean(!Eval::lt(&lhs, &rhs, "gt") && !Eval::eq(&lhs, &rhs));+        case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(&lhs, &rhs, "gte"));+        case Sass_OP::LT:  return sass_make_boolean(Eval::lt(&lhs, &rhs, "lt"));+        case Sass_OP::LTE: return sass_make_boolean(Eval::lt(&lhs, &rhs, "lte") || Eval::eq(&lhs, &rhs));+        case Sass_OP::AND: return ast_node_to_sass_value(lhs->is_false() ? &lhs : &rhs);+        case Sass_OP::OR:  return ast_node_to_sass_value(lhs->is_false() ? &rhs : &lhs);         default:           break;       }        if (sass_value_is_number(a) && sass_value_is_number(b)) {-        const Number* l_n = dynamic_cast<const Number*>(lhs);-        const Number* r_n = dynamic_cast<const Number*>(rhs);-        rv = Eval::op_numbers(mem, op, *l_n, *r_n);+        Number_Ptr_Const l_n = SASS_MEMORY_CAST(Number, lhs);+        Number_Ptr_Const r_n = SASS_MEMORY_CAST(Number, rhs);+        rv = Eval::op_numbers(op, *l_n, *r_n, options);       }       else if (sass_value_is_number(a) && sass_value_is_color(a)) {-        const Number* l_n = dynamic_cast<const Number*>(lhs);-        const Color* r_c = dynamic_cast<const Color*>(rhs);-        rv = Eval::op_number_color(mem, op, *l_n, *r_c);+        Number_Ptr_Const l_n = SASS_MEMORY_CAST(Number, lhs);+        Color_Ptr_Const r_c = SASS_MEMORY_CAST(Color, rhs);+        rv = Eval::op_number_color(op, *l_n, *r_c, options);       }       else if (sass_value_is_color(a) && sass_value_is_number(b)) {-        const Color* l_c = dynamic_cast<const Color*>(lhs);-        const Number* r_n = dynamic_cast<const Number*>(rhs);-        rv = Eval::op_color_number(mem, op, *l_c, *r_n);+        Color_Ptr_Const l_c = SASS_MEMORY_CAST(Color, lhs);+        Number_Ptr_Const r_n = SASS_MEMORY_CAST(Number, rhs);+        rv = Eval::op_color_number(op, *l_c, *r_n, options);       }       else if (sass_value_is_color(a) && sass_value_is_color(b)) {-        const Color* l_c = dynamic_cast<const Color*>(lhs);-        const Color* r_c = dynamic_cast<const Color*>(rhs);-        rv = Eval::op_colors(mem, op, *l_c, *r_c);+        Color_Ptr_Const l_c = SASS_MEMORY_CAST(Color, lhs);+        Color_Ptr_Const r_c = SASS_MEMORY_CAST(Color, rhs);+        rv = Eval::op_colors(op, *l_c, *r_c, options);       }       else /* convert other stuff to string and apply operation */ {-        Value* l_v = dynamic_cast<Value*>(lhs);-        Value* r_v = dynamic_cast<Value*>(rhs);-        rv = Eval::op_strings(mem, op, *l_v, *r_v);+        Value_Ptr l_v = SASS_MEMORY_CAST(Value, lhs);+        Value_Ptr r_v = SASS_MEMORY_CAST(Value, rhs);+        rv = Eval::op_strings(op, *l_v, *r_v, options);       }        // ToDo: maybe we should should return null value?
libsass/src/source_map.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <string> #include <sstream> #include <iostream>@@ -16,7 +17,7 @@    std::string SourceMap::render_srcmap(Context &ctx) { -    const bool include_sources = ctx.c_options->source_map_contents;+    const bool include_sources = ctx.c_options.source_map_contents;     const std::vector<std::string> links = ctx.srcmap_links;     const std::vector<Resource>& sources(ctx.resources); @@ -24,20 +25,32 @@      json_append_member(json_srcmap, "version", json_mknumber(3)); +    const char *include = file.c_str();+    JsonNode *json_include = json_mkstring(include);+    json_append_member(json_srcmap, "file", json_include);+     // pass-through sourceRoot option     if (!ctx.source_map_root.empty()) {       JsonNode* root = json_mkstring(ctx.source_map_root.c_str());       json_append_member(json_srcmap, "sourceRoot", root);     } -    const char *include = file.c_str();-    JsonNode *json_include = json_mkstring(include);-    json_append_member(json_srcmap, "file", json_include);-     JsonNode *json_includes = json_mkarray();     for (size_t i = 0; i < source_index.size(); ++i) {-      const char *include = links[source_index[i]].c_str();-      JsonNode *json_include = json_mkstring(include);+      std::string include(links[source_index[i]]);+      if (ctx.c_options.source_map_file_urls) {+        include = File::rel2abs(include);+        // check for windows abs path+        if (include[0] == '/') {+          // ends up with three slashes+          include = "file://" + include;+        } else {+          // needs an additional slash+          include = "file:///" + include;+        }+      }+      const char* inc = include.c_str();+      JsonNode *json_include = json_mkstring(inc);       json_append_element(json_includes, json_include);     }     json_append_member(json_srcmap, "sources", json_includes);@@ -53,15 +66,15 @@         json_append_member(json_srcmap, "sourcesContent", json_contents);     } -    std::string mappings = serialize_mappings();-    JsonNode *json_mappings = json_mkstring(mappings.c_str());-    json_append_member(json_srcmap, "mappings", json_mappings);-     JsonNode *json_names = json_mkarray();     // so far we have no implementation for names     // no problem as we do not alter any identifiers     json_append_member(json_srcmap, "names", json_names); +    std::string mappings = serialize_mappings();+    JsonNode *json_mappings = json_mkstring(mappings.c_str());+    json_append_member(json_srcmap, "mappings", json_mappings);+     char *str = json_stringify(json_srcmap, "\t");     std::string result = std::string(str);     free(str);@@ -159,12 +172,12 @@     current_position += offset;   } -  void SourceMap::add_open_mapping(AST_Node* node)+  void SourceMap::add_open_mapping(const AST_Node_Ptr node)   {     mappings.push_back(Mapping(node->pstate(), current_position));   } -  void SourceMap::add_close_mapping(AST_Node* node)+  void SourceMap::add_close_mapping(const AST_Node_Ptr node)   {     mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position));   }
libsass/src/source_map.hpp view
@@ -28,8 +28,8 @@     void prepend(const Offset& offset);     void append(const OutputBuffer& out);     void prepend(const OutputBuffer& out);-    void add_open_mapping(AST_Node* node);-    void add_close_mapping(AST_Node* node);+    void add_open_mapping(const AST_Node_Ptr node);+    void add_close_mapping(const AST_Node_Ptr node);      std::string render_srcmap(Context &ctx);     ParserState remap(const ParserState& pstate);
+ libsass/src/subset_map.cpp view
@@ -0,0 +1,57 @@+#include "sass.hpp"+#include "ast.hpp"+#include "subset_map.hpp"++namespace Sass {++  void Subset_Map::put(const Compound_Selector_Obj& sel, const Subset_Map_Val& 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<Subset_Map_Val> Subset_Map::get_kv(const Compound_Selector_Obj& sel)+  {+    // std::vector<Subset_Map_Key> s = sel->to_str_vec();+    // std::set<std::string> dict(s.begin(), s.end());+    std::unordered_set<Simple_Selector_Obj, HashSimpleSelector, CompareSimpleSelector> dict(sel->begin(), sel->end());+    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<Subset_Map_Val> results;+    for (size_t i = 0, S = indices.size(); i < S; ++i) {+      results.push_back(values_[indices[i]]);+    }+    return results;+  }++  std::vector<Subset_Map_Val> Subset_Map::get_v(const Compound_Selector_Obj& sel)+  {+    return get_kv(sel);+  }++}
libsass/src/subset_map.hpp view
@@ -7,7 +7,9 @@ #include <algorithm> #include <iterator> +#include "ast_fwd_decl.hpp" + // #include <iostream> // #include <sstream> // template<typename T>@@ -56,89 +58,18 @@  namespace Sass { -  template<typename F, typename S, typename T>-  struct triple {-    F first;-    S second;-    T third;--    triple(const F& f, const S& s, const T& t) : first(f), second(s), third(t) { }-  };--  template<typename F, typename S, typename T>-  triple<F, S, T> make_triple(const F& f, const S& s, const T& t)-  { return triple<F, S, T>(f, s, t); }--  template<typename K, typename V>   class Subset_Map {   private:-    std::vector<V> values_;-    std::map<K, std::vector<triple<std::vector<K>, std::set<K>, size_t> > > hash_;+    std::vector<Subset_Map_Val> values_;+    std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> > > hash_;   public:-    void put(const std::vector<K>& s, const V& value);-    std::vector<std::pair<V, std::vector<K> > > get_kv(const std::vector<K>& s);-    std::vector<V> get_v(const std::vector<K>& s);+    void put(const Compound_Selector_Obj& sel, const Subset_Map_Val& value);+    std::vector<Subset_Map_Val> get_kv(const Compound_Selector_Obj& s);+    std::vector<Subset_Map_Val> get_v(const Compound_Selector_Obj& s);     bool empty() { return values_.empty(); }     void clear() { values_.clear(); hash_.clear(); }-    const std::vector<V> values(void) { return values_; }+    const std::vector<Subset_Map_Val> values(void) { return values_; }   };--  template<typename K, typename V>-  void Subset_Map<K, V>::put(const std::vector<K>& s, const V& value)-  {-    if (s.empty()) throw "internal error: subset map keys may not be empty";-    size_t index = values_.size();-    values_.push_back(value);-    std::set<K> ss;-    for (size_t i = 0, S = s.size(); i < S; ++i)-    { ss.insert(s[i]); }-    for (size_t i = 0, S = s.size(); i < S; ++i)-    {-      hash_[s[i]];-      hash_[s[i]].push_back(make_triple(s, ss, index));-    }-  }--  template<typename K, typename V>-  std::vector<std::pair<V, std::vector<K> > > Subset_Map<K, V>::get_kv(const std::vector<K>& s)-  {-    std::vector<K> sorted = s;-    sort(sorted.begin(), sorted.end());-    std::vector<std::pair<size_t, std::vector<K> > > indices;-    for (size_t i = 0, S = s.size(); i < S; ++i) {-      if (!hash_.count(s[i])) {-        continue;-      }-      std::vector<triple<std::vector<K>, std::set<K>, size_t> > subsets = hash_[s[i]];-      // std::cerr << "length of subsets: " << subsets.size() << std::endl;-      for (size_t j = 0, T = subsets.size(); j < T; ++j) {-        if (!includes(sorted.begin(), sorted.end(), subsets[j].second.begin(), subsets[j].second.end())) {-          // std::cout << vector_to_string(s) << " doesn't include " << set_to_string(subsets[j].second) << std::endl;-          continue;-        }-        indices.push_back(std::make_pair(subsets[j].third, subsets[j].first));-        // std::cerr << "pushed " << subsets[j].third << " and " << vector_to_string(subsets[j].first) << " onto indices" << std::endl;-      }-    }-    sort(indices.begin(), indices.end());-    typename std::vector<std::pair<size_t, std::vector<K> > >::iterator indices_end = unique(indices.begin(), indices.end());-    indices.resize(distance(indices.begin(), indices_end));--    std::vector<std::pair<V, std::vector<K> > > results;-    for (size_t i = 0, S = indices.size(); i < S; ++i) {-      results.push_back(std::make_pair(values_[indices[i].first], indices[i].second));-    }-    return results;-  }--  template<typename K, typename V>-  std::vector<V> Subset_Map<K, V>::get_v(const std::vector<K>& s)-  {-    std::vector<std::pair<V, std::vector<K> > > kvs = get_kv(s);-    std::vector<V> results;-    for (size_t i = 0, S = kvs.size(); i < S; ++i) results.push_back(kvs[i].first);-    return results;-  }  } 
libsass/src/to_c.cpp view
@@ -1,27 +1,28 @@+#include "sass.hpp" #include "to_c.hpp" #include "ast.hpp"  namespace Sass { -  union Sass_Value* To_C::fallback_impl(AST_Node* n)+  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* b)+  union Sass_Value* To_C::operator()(Boolean_Ptr b)   { return sass_make_boolean(b->value()); } -  union Sass_Value* To_C::operator()(Number* n)+  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* w)+  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* e)+  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* c)+  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* s)+  union Sass_Value* To_C::operator()(String_Constant_Ptr s)   {     if (s->quote_mark()) {       return sass_make_qstring(s->value().c_str());@@ -30,10 +31,10 @@     }   } -  union Sass_Value* To_C::operator()(String_Quoted* s)+  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* l)+  union Sass_Value* To_C::operator()(List_Ptr l)   {     union Sass_Value* v = sass_make_list(l->length(), l->separator());     for (size_t i = 0, L = l->length(); i < L; ++i) {@@ -42,7 +43,7 @@     return v;   } -  union Sass_Value* To_C::operator()(Map* m)+  union Sass_Value* To_C::operator()(Map_Ptr m)   {     union Sass_Value* v = sass_make_map(m->length());     int i = 0;@@ -54,7 +55,7 @@     return v;   } -  union Sass_Value* To_C::operator()(Arguments* a)+  union Sass_Value* To_C::operator()(Arguments_Ptr a)   {     union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA);     for (size_t i = 0, L = a->length(); i < L; ++i) {@@ -63,11 +64,11 @@     return v;   } -  union Sass_Value* To_C::operator()(Argument* a)+  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* n)+  union Sass_Value* To_C::operator()(Null_Ptr n)   { return sass_make_null(); }  };
libsass/src/to_c.hpp view
@@ -8,31 +8,29 @@ namespace Sass {    class To_C : public Operation_CRTP<union Sass_Value*, To_C> {-    // import all the class-specific methods and override as desired-    using Operation<union Sass_Value*>::operator();     // override this to define a catch-all-    union Sass_Value* fallback_impl(AST_Node* n);+    union Sass_Value* fallback_impl(AST_Node_Ptr n);    public:      To_C() { }-    virtual ~To_C() { }+    ~To_C() { } -    union Sass_Value* operator()(Boolean*);-    union Sass_Value* operator()(Number*);-    union Sass_Value* operator()(Color*);-    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*);+    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* x)+    union Sass_Value* fallback(AST_Node_Ptr x)     { return fallback_impl(x); }   }; 
− libsass/src/to_string.cpp
@@ -1,48 +0,0 @@-#include <cmath>-#include <sstream>-#include <iomanip>-#include <iostream>--#include "ast.hpp"-#include "inspect.hpp"-#include "context.hpp"-#include "to_string.hpp"--namespace Sass {--  To_String::To_String(Context* ctx, bool in_declaration, bool in_debug)-  : ctx(ctx), in_declaration(in_declaration), in_debug(in_debug) { }-  To_String::~To_String() { }--  inline std::string To_String::fallback_impl(AST_Node* n)-  {-    Emitter emitter(ctx);-    Inspect i(emitter);-    i.in_declaration = in_declaration;-    i.in_debug = in_debug;-    if (n) n->perform(&i);-    return i.get_buffer();-  }--  inline std::string To_String::operator()(String_Schema* s)-  {-    std::string acc("");-    for (size_t i = 0, L = s->length(); i < L; ++i) {-      acc += s->elements()[i]->perform(this);-    }-    return acc;-  }--  inline std::string To_String::operator()(String_Quoted* s)-  {-    return s->value();-  }--  inline std::string To_String::operator()(String_Constant* s)-  {-    return s->value();-  }--  inline std::string To_String::operator()(Null* n)-  { return in_debug ? "null" : ""; }-}
− libsass/src/to_string.hpp
@@ -1,38 +0,0 @@-#ifndef SASS_TO_STRING_H-#define SASS_TO_STRING_H--#include <string>--#include "operation.hpp"--namespace Sass {--  class Context;-  class Null;--  class To_String : public Operation_CRTP<std::string, To_String> {-    // import all the class-specific methods and override as desired-    using Operation<std::string>::operator();-    // override this to define a catch-all-    std::string fallback_impl(AST_Node* n);--    Context* ctx;-    bool in_declaration;-    bool in_debug;--  public:--    To_String(Context* ctx = 0, bool in_declaration = true, bool in_debug = false);-    virtual ~To_String();--    std::string operator()(Null* n);-    std::string operator()(String_Schema*);-    std::string operator()(String_Quoted*);-    std::string operator()(String_Constant*);--    template <typename U>-    std::string fallback(U n) { return fallback_impl(n); }-  };-}--#endif
libsass/src/to_value.cpp view
@@ -1,10 +1,10 @@+#include "sass.hpp" #include "ast.hpp" #include "to_value.hpp"-#include "to_string.hpp"  namespace Sass { -  Value* To_Value::fallback_impl(AST_Node* n)+  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@@ -14,96 +14,94 @@   }    // Custom_Error is a valid value-  Value* To_Value::operator()(Custom_Error* e)+  Value_Ptr To_Value::operator()(Custom_Error_Ptr e)   {     return e;   }    // Custom_Warning is a valid value-  Value* To_Value::operator()(Custom_Warning* w)+  Value_Ptr To_Value::operator()(Custom_Warning_Ptr w)   {     return w;   }    // Boolean is a valid value-  Value* To_Value::operator()(Boolean* b)+  Value_Ptr To_Value::operator()(Boolean_Ptr b)   {     return b;   }    // Number is a valid value-  Value* To_Value::operator()(Number* n)+  Value_Ptr To_Value::operator()(Number_Ptr n)   {     return n;   }    // Color is a valid value-  Value* To_Value::operator()(Color* c)+  Value_Ptr To_Value::operator()(Color_Ptr c)   {     return c;   }    // String_Constant is a valid value-  Value* To_Value::operator()(String_Constant* s)+  Value_Ptr To_Value::operator()(String_Constant_Ptr s)   {     return s;   }    // String_Quoted is a valid value-  Value* To_Value::operator()(String_Quoted* s)+  Value_Ptr To_Value::operator()(String_Quoted_Ptr s)   {     return s;   }    // List is a valid value-  Value* To_Value::operator()(List* l)+  Value_Ptr To_Value::operator()(List_Ptr l)   {-    List* ll = SASS_MEMORY_NEW(mem, List,+    List_Obj ll = SASS_MEMORY_NEW(List,                                l->pstate(),                                l->length(),                                l->separator(),                                l->is_arglist());     for (size_t i = 0, L = l->length(); i < L; ++i) {-      *ll << (*l)[i]->perform(this);+      ll->append((*l)[i]->perform(this));     }-    return ll;+    return ll.detach();   }    // Map is a valid value-  Value* To_Value::operator()(Map* m)+  Value_Ptr To_Value::operator()(Map_Ptr m)   {     return m;   }    // Null is a valid value-  Value* To_Value::operator()(Null* n)+  Value_Ptr To_Value::operator()(Null_Ptr n)   {     return n;   }    // Argument returns its value-  Value* To_Value::operator()(Argument* arg)+  Value_Ptr To_Value::operator()(Argument_Ptr arg)   {     if (!arg->name().empty()) return 0;     return arg->value()->perform(this);   }    // Selector_List is converted to a string-  Value* To_Value::operator()(Selector_List* s)+  Value_Ptr To_Value::operator()(Selector_List_Ptr s)   {-    To_String to_string(&ctx);-    return SASS_MEMORY_NEW(mem, String_Quoted,+    return SASS_MEMORY_NEW(String_Quoted,                            s->pstate(),-                           s->perform(&to_string));+                           s->to_string(ctx.c_options));   }    // Binary_Expression is converted to a string-  Value* To_Value::operator()(Binary_Expression* s)+  Value_Ptr To_Value::operator()(Binary_Expression_Ptr s)   {-    To_String to_string(&ctx);-    return SASS_MEMORY_NEW(mem, String_Quoted,+    return SASS_MEMORY_NEW(String_Quoted,                            s->pstate(),-                           s->perform(&to_string));+                           s->to_string(ctx.c_options));   }  };
libsass/src/to_value.hpp view
@@ -7,42 +7,41 @@  namespace Sass { -  class To_Value : public Operation_CRTP<Value*, To_Value> {+  class To_Value : public Operation_CRTP<Value_Ptr, To_Value> { -    Value* fallback_impl(AST_Node* n);+    Value_Ptr fallback_impl(AST_Node_Ptr n);    private:      Context& ctx;-    Memory_Manager& mem;    public: -    To_Value(Context& ctx, Memory_Manager& mem)-    : ctx(ctx), mem(mem)+    To_Value(Context& ctx)+    : ctx(ctx)     { }-    virtual ~To_Value() { }-    using Operation<Value*>::operator();+    ~To_Value() { }+    using Operation<Value_Ptr>::operator(); -    Value* operator()(Argument*);-    Value* operator()(Boolean*);-    Value* operator()(Number*);-    Value* operator()(Color*);-    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_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);      // convert to string via `To_String`-    Value* operator()(Selector_List*);-    Value* operator()(Binary_Expression*);+    Value_Ptr operator()(Selector_List_Ptr);+    Value_Ptr operator()(Binary_Expression_Ptr);      // fallback throws error     template <typename U>-    Value* fallback(U x) { return fallback_impl(x); }+    Value_Ptr fallback(U x) { return fallback_impl(x); }   };  }
libsass/src/units.cpp view
@@ -1,10 +1,7 @@+#include "sass.hpp" #include <stdexcept> #include "units.hpp" -#ifdef IN-#undef IN-#endif- namespace Sass {    /* the conversion matrix can be readed the following way */@@ -47,93 +44,133 @@   const double resolution_conversion_factors[3][3] =   {              /*  dpi        dpcm       dppx     */-    /* dpi  */ { 1,         2.54,      96       },-    /* dpcm */ { 1/2.54,    1,         96/2.54  },-    /* dppx */ { 1/96.0,    2.54/96,   1        }+    /* dpi  */ { 1,         1/2.54,    1/96.0   },+    /* dpcm */ { 2.54,      1,         2.54/96  },+    /* dppx */ { 96,        96/2.54,   1        }   }; -  SassUnitType get_unit_type(SassUnit unit)+  UnitClass get_unit_type(UnitType unit)   {     switch (unit & 0xFF00)     {-      case SassUnitType::LENGTH:      return SassUnitType::LENGTH; break;-      case SassUnitType::ANGLE:       return SassUnitType::ANGLE; break;-      case SassUnitType::TIME:        return SassUnitType::TIME; break;-      case SassUnitType::FREQUENCY:   return SassUnitType::FREQUENCY; break;-      case SassUnitType::RESOLUTION:  return SassUnitType::RESOLUTION; break;-      default:                        return SassUnitType::INCOMMENSURABLE; break;+      case UnitClass::LENGTH:      return UnitClass::LENGTH; break;+      case UnitClass::ANGLE:       return UnitClass::ANGLE; break;+      case UnitClass::TIME:        return UnitClass::TIME; break;+      case UnitClass::FREQUENCY:   return UnitClass::FREQUENCY; break;+      case UnitClass::RESOLUTION:  return UnitClass::RESOLUTION; break;+      default:                     return UnitClass::INCOMMENSURABLE; break;     }   }; -  SassUnit string_to_unit(const std::string& s)+  std::string get_unit_class(UnitType unit)   {+    switch (unit & 0xFF00)+    {+      case UnitClass::LENGTH:      return "LENGTH"; break;+      case UnitClass::ANGLE:       return "ANGLE"; break;+      case UnitClass::TIME:        return "TIME"; break;+      case UnitClass::FREQUENCY:   return "FREQUENCY"; break;+      case UnitClass::RESOLUTION:  return "RESOLUTION"; break;+      default:                     return "INCOMMENSURABLE"; break;+    }+  };++  UnitType string_to_unit(const std::string& s)+  {     // size units-    if      (s == "px")   return SassUnit::PX;-    else if (s == "pt")   return SassUnit::PT;-    else if (s == "pc")   return SassUnit::PC;-    else if (s == "mm")   return SassUnit::MM;-    else if (s == "cm")   return SassUnit::CM;-    else if (s == "in")   return SassUnit::IN;+    if      (s == "px")   return UnitType::PX;+    else if (s == "pt")   return UnitType::PT;+    else if (s == "pc")   return UnitType::PC;+    else if (s == "mm")   return UnitType::MM;+    else if (s == "cm")   return UnitType::CM;+    else if (s == "in")   return UnitType::IN;     // angle units-    else if (s == "deg")  return SassUnit::DEG;-    else if (s == "grad") return SassUnit::GRAD;-    else if (s == "rad")  return SassUnit::RAD;-    else if (s == "turn") return SassUnit::TURN;+    else if (s == "deg")  return UnitType::DEG;+    else if (s == "grad") return UnitType::GRAD;+    else if (s == "rad")  return UnitType::RAD;+    else if (s == "turn") return UnitType::TURN;     // time units-    else if (s == "s")    return SassUnit::SEC;-    else if (s == "ms")   return SassUnit::MSEC;+    else if (s == "s")    return UnitType::SEC;+    else if (s == "ms")   return UnitType::MSEC;     // frequency units-    else if (s == "Hz")   return SassUnit::HERTZ;-    else if (s == "kHz")  return SassUnit::KHERTZ;+    else if (s == "Hz")   return UnitType::HERTZ;+    else if (s == "kHz")  return UnitType::KHERTZ;     // resolutions units-    else if (s == "dpi")  return SassUnit::DPI;-    else if (s == "dpcm") return SassUnit::DPCM;-    else if (s == "dppx") return SassUnit::DPPX;+    else if (s == "dpi")  return UnitType::DPI;+    else if (s == "dpcm") return UnitType::DPCM;+    else if (s == "dppx") return UnitType::DPPX;     // for unknown units-    else return SassUnit::UNKNOWN;+    else return UnitType::UNKNOWN;   } -  const char* unit_to_string(SassUnit unit)+  const char* unit_to_string(UnitType unit)   {     switch (unit) {       // size units-      case SassUnit::PX:      return "px"; break;-      case SassUnit::PT:      return "pt"; break;-      case SassUnit::PC:      return "pc"; break;-      case SassUnit::MM:      return "mm"; break;-      case SassUnit::CM:      return "cm"; break;-      case SassUnit::IN:      return "in"; break;+      case UnitType::PX:      return "px"; break;+      case UnitType::PT:      return "pt"; break;+      case UnitType::PC:      return "pc"; break;+      case UnitType::MM:      return "mm"; break;+      case UnitType::CM:      return "cm"; break;+      case UnitType::IN:      return "in"; break;       // angle units-      case SassUnit::DEG:     return "deg"; break;-      case SassUnit::GRAD:    return "grad"; break;-      case SassUnit::RAD:     return "rad"; break;-      case SassUnit::TURN:    return "turn"; break;+      case UnitType::DEG:     return "deg"; break;+      case UnitType::GRAD:    return "grad"; break;+      case UnitType::RAD:     return "rad"; break;+      case UnitType::TURN:    return "turn"; break;       // time units-      case SassUnit::SEC:     return "s"; break;-      case SassUnit::MSEC:    return "ms"; break;+      case UnitType::SEC:     return "s"; break;+      case UnitType::MSEC:    return "ms"; break;       // frequency units-      case SassUnit::HERTZ:   return "Hz"; break;-      case SassUnit::KHERTZ:  return "kHz"; break;+      case UnitType::HERTZ:   return "Hz"; break;+      case UnitType::KHERTZ:  return "kHz"; break;       // resolutions units-      case SassUnit::DPI:     return "dpi"; break;-      case SassUnit::DPCM:    return "dpcm"; break;-      case SassUnit::DPPX:    return "dppx"; break;+      case UnitType::DPI:     return "dpi"; break;+      case UnitType::DPCM:    return "dpcm"; break;+      case UnitType::DPPX:    return "dppx"; break;       // for unknown units       default:                return ""; break;     }   } +  std::string unit_to_class(const std::string& s)+  {+    if      (s == "px")   return "LENGTH";+    else if (s == "pt")   return "LENGTH";+    else if (s == "pc")   return "LENGTH";+    else if (s == "mm")   return "LENGTH";+    else if (s == "cm")   return "LENGTH";+    else if (s == "in")   return "LENGTH";+    // angle units+    else if (s == "deg")  return "ANGLE";+    else if (s == "grad") return "ANGLE";+    else if (s == "rad")  return "ANGLE";+    else if (s == "turn") return "ANGLE";+    // time units+    else if (s == "s")    return "TIME";+    else if (s == "ms")   return "TIME";+    // frequency units+    else if (s == "Hz")   return "FREQUENCY";+    else if (s == "kHz")  return "FREQUENCY";+    // resolutions units+    else if (s == "dpi")  return "RESOLUTION";+    else if (s == "dpcm") return "RESOLUTION";+    else if (s == "dppx") return "RESOLUTION";+    // for unknown units+    return "CUSTOM:" + s;+  }+   // throws incompatibleUnits exceptions   double conversion_factor(const std::string& s1, const std::string& s2, bool strict)   {     // assert for same units     if (s1 == s2) return 1;     // get unit enum from string-    SassUnit u1 = string_to_unit(s1);-    SassUnit u2 = string_to_unit(s2);+    UnitType u1 = string_to_unit(s1);+    UnitType u2 = string_to_unit(s2);     // query unit group types-    SassUnitType t1 = get_unit_type(u1);-    SassUnitType t2 = get_unit_type(u2);+    UnitClass t1 = get_unit_type(u1);+    UnitClass t2 = get_unit_type(u2);     // get absolute offset     // used for array acces     size_t i1 = u1 - t1;@@ -144,17 +181,17 @@     // only process known units     if (u1 != UNKNOWN && u2 != UNKNOWN) {       switch (t1) {-        case SassUnitType::LENGTH:              return size_conversion_factors[i1][i2]; break;-        case SassUnitType::ANGLE:             return angle_conversion_factors[i1][i2]; break;-        case SassUnitType::TIME:              return time_conversion_factors[i1][i2]; break;-        case SassUnitType::FREQUENCY:         return frequency_conversion_factors[i1][i2]; break;-        case SassUnitType::RESOLUTION:        return resolution_conversion_factors[i1][i2]; break;+        case UnitClass::LENGTH:            return size_conversion_factors[i1][i2]; break;+        case UnitClass::ANGLE:             return angle_conversion_factors[i1][i2]; break;+        case UnitClass::TIME:              return time_conversion_factors[i1][i2]; break;+        case UnitClass::FREQUENCY:         return frequency_conversion_factors[i1][i2]; break;+        case UnitClass::RESOLUTION:        return resolution_conversion_factors[i1][i2]; break;         // ToDo: should we throw error here?-        case SassUnitType::INCOMMENSURABLE:   return 0; break;+        case UnitClass::INCOMMENSURABLE:   return 0; break;       }     }     // fallback-    return 1;+    return 0;   }  }
libsass/src/units.hpp view
@@ -7,9 +7,9 @@  namespace Sass { -  const double PI = acos(-1);+  const double PI = std::acos(-1); -  enum SassUnitType {+  enum UnitClass {     LENGTH = 0x000,     ANGLE = 0x100,     TIME = 0x200,@@ -18,10 +18,10 @@     INCOMMENSURABLE = 0x500   }; -  enum SassUnit {+  enum UnitType {      // size units-    IN = SassUnitType::LENGTH,+    IN = UnitClass::LENGTH,     CM,     PC,     MM,@@ -58,9 +58,11 @@   extern const double frequency_conversion_factors[2][2];   extern const double resolution_conversion_factors[3][3]; -  enum SassUnit string_to_unit(const std::string&);-  const char* unit_to_string(SassUnit unit);-  enum SassUnitType get_unit_type(SassUnit unit);+  enum Sass::UnitType string_to_unit(const std::string&);+  const char* unit_to_string(Sass::UnitType unit);+  enum Sass::UnitClass get_unit_type(Sass::UnitType unit);+  std::string get_unit_class(Sass::UnitType unit);+  std::string unit_to_class(const std::string&);   // throws incompatibleUnits exceptions   double conversion_factor(const std::string&, const std::string&, bool = true); @@ -68,14 +70,16 @@   {     public:       const char* msg;-      incompatibleUnits(SassUnit a, SassUnit b)+      incompatibleUnits(Sass::UnitType a, Sass::UnitType b)       : exception()       {         std::stringstream ss;         ss << "Incompatible units: ";         ss << "'" << unit_to_string(a) << "' and ";         ss << "'" << unit_to_string(b) << "'";-        msg = ss.str().c_str();+        // hold on to string on stack!+        std::string str(ss.str());+        msg = str.c_str();       }       virtual const char* what() const throw()       {
libsass/src/utf8/unchecked.h view
@@ -32,13 +32,13 @@  namespace utf8 {-    namespace unchecked +    namespace unchecked     {         template <typename octet_iterator>         octet_iterator append(uint32_t cp, octet_iterator result)         {             if (cp < 0x80)                        // one octet-                *(result++) = static_cast<uint8_t>(cp);  +                *(result++) = static_cast<uint8_t>(cp);             else if (cp < 0x800) {                // two octets                 *(result++) = static_cast<uint8_t>((cp >> 6)          | 0xc0);                 *(result++) = static_cast<uint8_t>((cp & 0x3f)        | 0x80);@@ -70,28 +70,28 @@                     cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);                     break;                 case 3:-                    ++it; +                    ++it;                     cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);                     ++it;                     cp += (*it) & 0x3f;                     break;                 case 4:                     ++it;-                    cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);                +                    cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);                     ++it;                     cp += (utf8::internal::mask8(*it) << 6) & 0xfff;                     ++it;-                    cp += (*it) & 0x3f; +                    cp += (*it) & 0x3f;                     break;             }             ++it;-            return cp;        +            return cp;         }          template <typename octet_iterator>         uint32_t peek_next(octet_iterator it)         {-            return utf8::unchecked::next(it);    +            return utf8::unchecked::next(it);         }          template <typename octet_iterator>@@ -121,14 +121,14 @@         distance (octet_iterator first, octet_iterator last)         {             typename std::iterator_traits<octet_iterator>::difference_type dist;-            for (dist = 0; first < last; ++dist) +            for (dist = 0; first < last; ++dist)                 utf8::unchecked::next(first);             return dist;         }          template <typename u16bit_iterator, typename octet_iterator>         octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)-        {       +        {             while (start != end) {                 uint32_t cp = utf8::internal::mask16(*start++);             // Take care of surrogate pairs first@@ -138,7 +138,7 @@                 }                 result = utf8::unchecked::append(cp, result);             }-            return result;         +            return result;         }          template <typename u16bit_iterator, typename octet_iterator>@@ -176,7 +176,7 @@          // The iterator class         template <typename octet_iterator>-          class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { +          class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {             octet_iterator it;             public:             iterator () {}@@ -188,15 +188,15 @@                 octet_iterator temp = it;                 return utf8::unchecked::next(temp);             }-            bool operator == (const iterator& rhs) const -            { +            bool operator == (const iterator& rhs) const+            {                 return (it == rhs.it);             }             bool operator != (const iterator& rhs) const             {                 return !(operator == (rhs));             }-            iterator& operator ++ () +            iterator& operator ++ ()             {                 ::std::advance(it, utf8::internal::sequence_length(it));                 return *this;@@ -206,7 +206,7 @@                 iterator temp = *this;                 ::std::advance(it, utf8::internal::sequence_length(it));                 return temp;-            }  +            }             iterator& operator -- ()             {                 utf8::unchecked::prior(it);@@ -221,7 +221,7 @@           }; // class iterator      } // namespace utf8::unchecked-} // namespace utf8 +} // namespace utf8   #endif // header guard
libsass/src/utf8_string.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <string> #include <vector> #include <cstdlib>@@ -68,6 +69,8 @@       }     } +    #ifdef _WIN32+     // utf16 functions     using std::wstring; @@ -92,6 +95,8 @@                      back_inserter(utf16));       return utf16;     }++    #endif    } }
libsass/src/util.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "sass.h" #include "ast.hpp" #include "util.hpp"@@ -11,38 +12,17 @@  namespace Sass { -  #define out_of_memory() do {            \-      std::cerr << "Out of memory.\n";    \-      exit(EXIT_FAILURE);                 \-    } while (0)--  double round(double val)+  double round(double val, size_t precision)   {+    // 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);     // work around some compiler issue     // cygwin has it not defined in std     using namespace std;--    // This was later repatched in 3.4.20-    // which is as yet unreleased.-    // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93-    if (fmod(val, 1) - 0.5 > -0.00001) return std::ceil(val);     return ::round(val);--    // Use this version once sass-spec is at 3.4.20-    // if (fmod(val, 1) - 0.5 > -0.00001) return ::round(val);-    // return value > 0 ? std::ceil(val) : std::floor(val);   } -  /* Sadly, sass_strdup is not portable. */-  char *sass_strdup(const char *str)-  {-    char *ret = (char*) malloc(strlen(str) + 1);-    if (ret == NULL)-      out_of_memory();-    strcpy(ret, str);-    return ret;-  }-   /* Locale unspecific atof function. */   double sass_atof(const char *str)   {@@ -55,7 +35,7 @@       if(found != NULL){         // substitution is required. perform the substitution on a copy         // of the string. This is slower but it is thread safe.-        char *copy = sass_strdup(str);+        char *copy = sass_copy_c_string(str);         *(copy + (found - str)) = separator;         double res = atof(copy);         free(copy);@@ -104,94 +84,6 @@     return *array = arr;   } -  std::string string_eval_escapes(const std::string& s)-  {--    std::string out("");-    bool esc = false;-    for (size_t i = 0, L = s.length(); i < L; ++i) {-      if(s[i] == '\\' && esc == false) {-        esc = true;--        // escape length-        size_t len = 1;--        // 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;--        // hex string?-        if (len > 1) {--          // convert the extracted hex string to code point value-          // ToDo: Maybe we could do this without creating a substring-          uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);--          if (cp == 0) cp = 0xFFFD;--          // assert invalid code points-          if (cp >= 1) {--            // use a very simple approach to convert via utf8 lib-            // maybe there is a more elegant way; maybe we shoud-            // convert the whole output from string to a stream!?-            // allocate memory for utf8 char and convert to utf8-            unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);-            for(size_t m = 0; u[m] && m < 5; m++) out.push_back(u[m]);--            // skip some more chars?-            i += len - 1; esc = false;-            if (cp == 10) out += ' ';--          }--        }--      }-      else {-        out += s[i];-        esc = false;-      }-    }-    return out;--  }--  // double escape every escape sequences-  // escape unescaped quotes and backslashes-  std::string string_escape(const std::string& str)-  {-    std::string out("");-    for (auto i : str) {-      // escape some characters-      if (i == '"') out += '\\';-      if (i == '\'') out += '\\';-      if (i == '\\') out += '\\';-      out += i;-    }-    return out;-  }--  // unescape every escape sequence-  // only removes unescaped backslashes-  std::string string_unescape(const std::string& str)-  {-    std::string out("");-    bool esc = false;-    for (auto i : str) {-      if (esc || i != '\\') {-        esc = false;-        out += i;-      } else {-        esc = true;-      }-    }-    // open escape sequence at end-    // maybe it should thow an error-    if (esc) { out += '\\'; }-    return out;-  }-   // read css string (handle multiline DELIM)   std::string read_css_string(const std::string& str)   {@@ -211,29 +103,10 @@       }       out.push_back(i);     }-    if (esc) out += '\\';-    return out;-  }--  // evacuate unescaped quoted-  // leave everything else untouched-  std::string evacuate_quotes(const std::string& str)-  {-    std::string out("");-    bool esc = false;-    for (auto i : str) {-      if (!esc) {-        // ignore next character-        if (i == '\\') esc = true;-        // evacuate unescaped quotes-        else if (i == '"') out += '\\';-        else if (i == '\'') out += '\\';-      }-      // get escaped char now-      else { esc = false; }-      // remove nothing-      out += i;-    }+    // happens when parsing does not correctly skip+    // over escaped sequences for ie. interpolations+    // one example: foo\#{interpolate}+    // if (esc) out += '\\';     return out;   } @@ -272,13 +145,20 @@     return out;   } -  // bell character is replaces with space+  // bell characters are replaced with spaces+  void newline_to_space(std::string& str)+  {+    std::replace(str.begin(), str.end(), '\n', ' ');+  }++  // bell characters are replaced with spaces+  // also eats spaces after line-feeds (ltrim)   std::string string_to_output(const std::string& str)   {     std::string out("");     bool lf = false;     for (auto i : str) {-      if (i == 10) {+      if (i == '\n') {         out += ' ';         lf = true;       } else if (!(lf && isspace(i))) {@@ -321,38 +201,6 @@     else return text;   } -   std::string normalize_wspace(const std::string& str)-  {-    bool ws = false;-    bool esc = false;-    std::string text = "";-    for(const char& i : str) {-      if (!esc && i == '\\') {-        esc = true;-        ws = false;-        text += i;-      } else if (esc) {-        esc = false;-        ws = false;-        text += i;-      } else if (-        i == ' ' ||-        i == '\r' ||-        i == '\n' ||-        i == '	'-      ) {-        // only add one space-        if (!ws) text += ' ';-        ws = true;-      } else {-        ws = false;-        text += i;-      }-    }-    if (esc) text += '\\';-    return text;-  }-   // find best quote_mark by detecting if the string contains any single   // or double quotes. When a single quote is found, we not we want a double   // quote as quote_mark. Otherwise we check if the string cotains any double@@ -373,7 +221,7 @@     return quote_mark;   } -  std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences)+  std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences, bool strict)   {      // not enough room for quotes@@ -426,7 +274,7 @@           // assert invalid code points           if (cp == 0) cp = 0xFFFD;           // replace bell character-          // if (cp == 10) cp = 32;+          // if (cp == '\n') cp = 32;            // use a very simple approach to convert via utf8 lib           // maybe there is a more elegant way; maybe we shoud@@ -451,6 +299,9 @@       //   error("Unescaped delimiter in string to unquote found. [" + s + "]", ParserState("[UNQUOTE]"));       // }       else {+        if (strict && !skipped) {+          if (s[i] == q) return s;+        }         skipped = false;         unq.push_back(s[i]);       }@@ -462,7 +313,7 @@    } -  std::string quote(const std::string& s, char q, bool keep_linefeed_whitespace)+  std::string quote(const std::string& s, char q)   {      // autodetect with fallback to given quote@@ -488,13 +339,27 @@        int cp = utf8::next(it, end); +      // in case of \r, check if the next in sequence+      // is \n and then advance the iterator and skip \r+      if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') {+        cp = utf8::next(it, end);+      }+       if (cp == '\n') {         quoted.push_back('\\');         quoted.push_back('a');         // we hope we can remove this flag once we figure out         // why ruby sass has these different output behaviors-        if (keep_linefeed_whitespace)+        // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")+        using namespace Prelexer;+        if (alternatives <+          Prelexer::char_range<'a', 'f'>,+          Prelexer::char_range<'A', 'F'>,+          Prelexer::char_range<'0', '9'>,+          space+        >(it) != NULL) {           quoted.push_back(' ');+        }       } else if (cp < 127) {         quoted.push_back((char) cp);       } else {@@ -573,30 +438,15 @@       return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;     } -    // compress a color sixtuplet if possible-    // input: "#CC9900" -> output: "#C90"-    std::string normalize_sixtuplet(const std::string& col) {-      if(-        col.substr(1, 1) == col.substr(2, 1) &&-        col.substr(3, 1) == col.substr(4, 1) &&-        col.substr(5, 1) == col.substr(6, 1)-      ) {-        return std::string("#" + col.substr(1, 1)-                          + col.substr(3, 1)-                          + col.substr(5, 1));-      } else {-        return std::string(col);-      }-    }--    bool isPrintable(Ruleset* r, Sass_Output_Style style) {+    bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style) {       if (r == NULL) {         return false;       } -      Block* b = r->block();+      Block_Obj b = r->block(); -      bool hasSelectors = static_cast<Selector_List*>(r->selector())->length() > 0;+      Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, r->selector());+      bool hasSelectors = sl ? sl->length() > 0 : false;        if (!hasSelectors) {         return false;@@ -605,25 +455,25 @@       bool hasDeclarations = false;       bool hasPrintableChildBlocks = false;       for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (dynamic_cast<At_Rule*>(stm)) {+        Statement_Obj stm = b->at(i);+        if (dynamic_cast<Directive_Ptr>(&stm)) {           return true;-        } else if (dynamic_cast<Has_Block*>(stm)) {-          Block* pChildBlock = ((Has_Block*)stm)->block();-          if (isPrintable(pChildBlock, style)) {+        } else if (Declaration_Ptr d = dynamic_cast<Declaration_Ptr>(&stm)) {+          return isPrintable(d, style);+        } else if (dynamic_cast<Has_Block_Ptr>(&stm)) {+          Block_Obj pChildBlock = ((Has_Block_Ptr)&stm)->block();+          if (isPrintable(&pChildBlock, style)) {             hasPrintableChildBlocks = true;           }-        } else if (Comment* c = dynamic_cast<Comment*>(stm)) {+        } else if (Comment_Ptr c = dynamic_cast<Comment_Ptr>(&stm)) {           // keep for uncompressed-          if (style != SASS_STYLE_COMPRESSED) {+          if (style != COMPRESSED) {             hasDeclarations = true;           }           // output style compressed           if (c->is_important()) {             hasDeclarations = c->is_important();           }-        } else if (Declaration* d = dynamic_cast<Declaration*>(stm)) {-          return isPrintable(d, style);         } else {           hasDeclarations = true;         }@@ -636,49 +486,43 @@       return false;     } -    bool isPrintable(String_Constant* s, Sass_Output_Style style)+    bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style)     {       return ! s->value().empty();     } -    bool isPrintable(String_Quoted* s, Sass_Output_Style style)+    bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style)     {       return true;     } -    bool isPrintable(Declaration* d, Sass_Output_Style style)+    bool isPrintable(Declaration_Ptr d, Sass_Output_Style style)     {-      Expression* val = d->value();-      if (String_Quoted* sq = dynamic_cast<String_Quoted*>(val)) return isPrintable(sq, style);-      if (String_Constant* sc = dynamic_cast<String_Constant*>(val)) return isPrintable(sc, style);+      Expression_Obj val = d->value();+      if (String_Quoted_Obj sq = SASS_MEMORY_CAST(String_Quoted, val)) return isPrintable(&sq, style);+      if (String_Constant_Obj sc = SASS_MEMORY_CAST(String_Constant, val)) return isPrintable(&sc, style);       return true;     } -    bool isPrintable(Supports_Block* f, Sass_Output_Style style) {+    bool isPrintable(Supports_Block_Ptr f, Sass_Output_Style style) {       if (f == NULL) {         return false;       } -      Block* b = f->block();+      Block_Obj b = f->block(); -//      bool hasSelectors = f->selector() && static_cast<Selector_List*>(f->selector())->length() > 0;+//      bool hasSelectors = f->selector() && static_cast<Selector_List_Ptr>(f->selector())->length() > 0;        bool hasDeclarations = false;       bool hasPrintableChildBlocks = false;       for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (!stm->is_hoistable()) {-          // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0),-          // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector-          // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with-          // a declaration in it).-        }-        else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+        Statement_Obj stm = b->at(i);+        if (dynamic_cast<Declaration_Ptr>(&stm) || dynamic_cast<Directive_Ptr>(&stm)) {           hasDeclarations = true;         }-        else if (dynamic_cast<Has_Block*>(stm)) {-          Block* pChildBlock = ((Has_Block*)stm)->block();-          if (isPrintable(pChildBlock, style)) {+        else if (dynamic_cast<Has_Block_Ptr>(&stm)) {+          Block_Obj pChildBlock = ((Has_Block_Ptr)&stm)->block();+          if (isPrintable(&pChildBlock, style)) {             hasPrintableChildBlocks = true;           }         }@@ -691,62 +535,95 @@       return false;     } -    bool isPrintable(Media_Block* m, Sass_Output_Style style)+    bool isPrintable(Media_Block_Ptr m, Sass_Output_Style style)     {       if (m == 0) return false;-      Block* b = m->block();+      Block_Obj b = m->block();       if (b == 0) return false;       for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (typeid(*stm) == typeid(At_Rule)) return true;-        if (typeid(*stm) == typeid(Declaration)) return true;-        if (Has_Block* child = dynamic_cast<Has_Block*>(stm)) {-          if (isPrintable(child->block(), style)) return true;+        Statement_Obj stm = b->at(i);+        if (dynamic_cast<Directive_Ptr>(&stm)) return true;+        else if (dynamic_cast<Declaration_Ptr>(&stm)) return true;+        else if (dynamic_cast<Comment_Ptr>(&stm)) {+          Comment_Ptr c = (Comment_Ptr) &stm;+          if (isPrintable(c, style)) {+            return true;+          }         }+        else if (dynamic_cast<Ruleset_Ptr>(&stm)) {+          Ruleset_Ptr r = (Ruleset_Ptr) &stm;+          if (isPrintable(r, style)) {+            return true;+          }+        }+        else if (dynamic_cast<Supports_Block_Ptr>(&stm)) {+          Supports_Block_Ptr f = (Supports_Block_Ptr) &stm;+          if (isPrintable(f, style)) {+            return true;+          }+        }+        else if (dynamic_cast<Media_Block_Ptr>(&stm)) {+          Media_Block_Ptr m = (Media_Block_Ptr) &stm;+          if (isPrintable(m, style)) {+            return true;+          }+        }+        else if (dynamic_cast<Has_Block_Ptr>(&stm) && isPrintable(((Has_Block_Ptr)&stm)->block(), style)) {+          return true;+        }       }       return false;     } -    bool isPrintable(Block* b, Sass_Output_Style style) {-      if (b == NULL) {+    bool isPrintable(Comment_Ptr c, Sass_Output_Style style)+    {+      // keep for uncompressed+      if (style != COMPRESSED) {+        return true;+      }+      // output style compressed+      if (c->is_important()) {+        return true;+      }+      // not printable+      return false;+    };++    bool isPrintable(Block_Obj b, Sass_Output_Style style) {+      if (!b) {         return false;       }        for (size_t i = 0, L = b->length(); i < L; ++i) {-        Statement* stm = (*b)[i];-        if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+        Statement_Obj stm = b->at(i);+        if (dynamic_cast<Declaration_Ptr>(&stm) || dynamic_cast<Directive_Ptr>(&stm)) {           return true;         }-        else if (typeid(*stm) == typeid(Comment)) {-          Comment* c = (Comment*) stm;-          // keep for uncompressed-          if (style != SASS_STYLE_COMPRESSED) {-            return true;-          }-          // output style compressed-          if (c->is_important()) {+        else if (dynamic_cast<Comment_Ptr>(&stm)) {+          Comment_Ptr c = (Comment_Ptr) &stm;+          if (isPrintable(c, style)) {             return true;           }         }-        else if (typeid(*stm) == typeid(Ruleset)) {-          Ruleset* r = (Ruleset*) stm;+        else if (dynamic_cast<Ruleset_Ptr>(&stm)) {+          Ruleset_Ptr r = (Ruleset_Ptr) &stm;           if (isPrintable(r, style)) {             return true;           }         }-        else if (typeid(*stm) == typeid(Supports_Block)) {-          Supports_Block* f = (Supports_Block*) stm;+        else if (dynamic_cast<Supports_Block_Ptr>(&stm)) {+          Supports_Block_Ptr f = (Supports_Block_Ptr) &stm;           if (isPrintable(f, style)) {             return true;           }         }-        else if (typeid(*stm) == typeid(Media_Block)) {-          Media_Block* m = (Media_Block*) stm;+        else if (dynamic_cast<Media_Block_Ptr>(&stm)) {+          Media_Block_Ptr m = (Media_Block_Ptr) &stm;           if (isPrintable(m, style)) {             return true;           }         }-        else if (dynamic_cast<Has_Block*>(stm) && isPrintable(((Has_Block*)stm)->block(), style)) {+        else if (dynamic_cast<Has_Block_Ptr>(&stm) && isPrintable(((Has_Block_Ptr)&stm)->block(), style)) {           return true;         }       }@@ -754,27 +631,9 @@       return false;     } -    std::string vecJoin(const std::vector<std::string>& vec, const std::string& sep)-    {-      switch (vec.size())-      {-        case 0:-            return std::string("");-        case 1:-            return vec[0];-        default:-            std::ostringstream os;-            os << vec[0];-            for (size_t i = 1; i < vec.size(); i++) {-              os << sep << vec[i];-            }-            return os.str();-      }+    bool isAscii(const char chr) {+      return unsigned(chr) < 128;     }--     bool isAscii(const char chr) {-       return unsigned(chr) < 128;-     }    } }
libsass/src/util.hpp view
@@ -4,6 +4,7 @@ #include <vector> #include <string> #include <assert.h>+#include "sass.hpp" #include "sass/base.h" #include "ast_fwd_decl.hpp" @@ -11,24 +12,24 @@  namespace Sass { -  double round(double val);-  char* sass_strdup(const char* str);+  #define out_of_memory() do {            \+      std::cerr << "Out of memory.\n";    \+      exit(EXIT_FAILURE);                 \+    } while (0)++  double round(double val, size_t precision = 0);   double sass_atof(const char* str);   const char* safe_str(const char *, const char* = "");   void free_string_array(char **);   char **copy_strings(const std::vector<std::string>&, char ***, int = 0);-  std::string string_escape(const std::string& str);-  std::string string_unescape(const std::string& str);-  std::string string_eval_escapes(const std::string& str);   std::string read_css_string(const std::string& str);-  std::string evacuate_quotes(const std::string& str);   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 normalize_wspace(const std::string& str);+  void newline_to_space(std::string& str); -  std::string quote(const std::string&, char q = 0, bool keep_linefeed_whitespace = false);-  std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false);+  std::string quote(const std::string&, char q = 0);+  std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false, bool strict = true);   char detect_best_quotemark(const char* s, char qm = '"');    bool is_hex_doublet(double n);@@ -42,18 +43,15 @@      std::string normalize_underscores(const std::string& str);     std::string normalize_decimals(const std::string& str);-    std::string normalize_sixtuplet(const std::string& col); -    std::string vecJoin(const std::vector<std::string>& vec, const std::string& sep);-    bool containsAnyPrintableStatements(Block* b);--    bool isPrintable(Ruleset* r, Sass_Output_Style style = SASS_STYLE_NESTED);-    bool isPrintable(Supports_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED);-    bool isPrintable(Media_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED);-    bool isPrintable(Block* b, Sass_Output_Style style = SASS_STYLE_NESTED);-    bool isPrintable(String_Constant* s, Sass_Output_Style style = SASS_STYLE_NESTED);-    bool isPrintable(String_Quoted* s, Sass_Output_Style style = SASS_STYLE_NESTED);-    bool isPrintable(Declaration* d, Sass_Output_Style style = SASS_STYLE_NESTED);+    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);+    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);    }
libsass/src/values.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "sass.h" #include "values.hpp" @@ -6,35 +7,36 @@ namespace Sass {    // convert value from C++ side to C-API-  union Sass_Value* ast_node_to_sass_value (const Expression* val)+  union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val)   {     if (val->concrete_type() == Expression::NUMBER)     {-      const Number* res = dynamic_cast<const Number*>(val);+      Number_Ptr_Const res = dynamic_cast<Number_Ptr_Const>(val);       return sass_make_number(res->value(), res->unit().c_str());     }     else if (val->concrete_type() == Expression::COLOR)     {-      const Color* col = dynamic_cast<const Color*>(val);+      Color_Ptr_Const col = dynamic_cast<Color_Ptr_Const>(val);       return sass_make_color(col->r(), col->g(), col->b(), col->a());     }     else if (val->concrete_type() == Expression::LIST)     {-      const List* l = dynamic_cast<const List*>(val);+      List_Ptr_Const l = dynamic_cast<List_Ptr_Const>(val);       union Sass_Value* list = sass_make_list(l->size(), l->separator());       for (size_t i = 0, L = l->length(); i < L; ++i) {-        auto val = ast_node_to_sass_value((*l)[i]);+        Expression_Obj obj = l->at(i);+        auto val = ast_node_to_sass_value(&obj);         sass_list_set_value(list, i, val);       }       return list;     }     else if (val->concrete_type() == Expression::MAP)     {-      const Map* m = dynamic_cast<const Map*>(val);+      Map_Ptr_Const m = dynamic_cast<Map_Ptr_Const>(val);       union Sass_Value* map = sass_make_map(m->length());-      size_t i = 0; for (auto key : m->keys()) {-        sass_map_set_key(map, i, ast_node_to_sass_value(key));-        sass_map_set_value(map, i, ast_node_to_sass_value(m->at(key)));+      size_t i = 0; for (Expression_Obj key : m->keys()) {+        sass_map_set_key(map, i, ast_node_to_sass_value(&key));+        sass_map_set_value(map, i, ast_node_to_sass_value(&m->at(key)));         ++ i;       }       return map;@@ -45,16 +47,16 @@     }     else if (val->concrete_type() == Expression::BOOLEAN)     {-      const Boolean* res = dynamic_cast<const Boolean*>(val);+      Boolean_Ptr_Const res = dynamic_cast<Boolean_Ptr_Const>(val);       return sass_make_boolean(res->value());     }     else if (val->concrete_type() == Expression::STRING)     {-      if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(val))+      if (String_Quoted_Ptr_Const qstr = dynamic_cast<String_Quoted_Ptr_Const>(val))       {         return sass_make_qstring(qstr->value().c_str());       }-      else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(val))+      else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(val))       {         return sass_make_string(cstr->value().c_str());       }@@ -63,22 +65,22 @@   }    // convert value from C-API to C++ side-  Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val)+  Value_Ptr sass_value_to_ast_node (const union Sass_Value* val)   {     switch (sass_value_get_tag(val)) {       case SASS_NUMBER:-        return SASS_MEMORY_NEW(mem, Number,+        return SASS_MEMORY_NEW(Number,                                ParserState("[C-VALUE]"),                                sass_number_get_value(val),                                sass_number_get_unit(val));       break;       case SASS_BOOLEAN:-        return SASS_MEMORY_NEW(mem, Boolean,+        return SASS_MEMORY_NEW(Boolean,                                ParserState("[C-VALUE]"),                                sass_boolean_get_value(val));       break;       case SASS_COLOR:-        return SASS_MEMORY_NEW(mem, Color,+        return SASS_MEMORY_NEW(Color,                                ParserState("[C-VALUE]"),                                sass_color_get_r(val),                                sass_color_get_g(val),@@ -87,46 +89,46 @@       break;       case SASS_STRING:         if (sass_string_is_quoted(val)) {-          return SASS_MEMORY_NEW(mem, String_Quoted,+          return SASS_MEMORY_NEW(String_Quoted,                                  ParserState("[C-VALUE]"),                                  sass_string_get_value(val));         } else {-          return SASS_MEMORY_NEW(mem, String_Constant,+          return SASS_MEMORY_NEW(String_Constant,                                  ParserState("[C-VALUE]"),                                  sass_string_get_value(val));         }       break;       case SASS_LIST: {-        List* l = SASS_MEMORY_NEW(mem, List,+        List_Ptr l = SASS_MEMORY_NEW(List,                                   ParserState("[C-VALUE]"),                                   sass_list_get_length(val),                                   sass_list_get_separator(val));         for (size_t i = 0, L = sass_list_get_length(val); i < L; ++i) {-          *l << sass_value_to_ast_node(mem, sass_list_get_value(val, i));+          l->append(sass_value_to_ast_node(sass_list_get_value(val, i)));         }         return l;       }       break;       case SASS_MAP: {-        Map* m = SASS_MEMORY_NEW(mem, Map, ParserState("[C-VALUE]"));+        Map_Ptr 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(mem, sass_map_get_key(val, i)),-            sass_value_to_ast_node(mem, sass_map_get_value(val, i)));+            sass_value_to_ast_node(sass_map_get_key(val, i)),+            sass_value_to_ast_node(sass_map_get_value(val, i)));         }         return m;       }       break;       case SASS_NULL:-        return SASS_MEMORY_NEW(mem, Null, ParserState("[C-VALUE]"));+        return SASS_MEMORY_NEW(Null, ParserState("[C-VALUE]"));       break;       case SASS_ERROR:-        return SASS_MEMORY_NEW(mem, Custom_Error,+        return SASS_MEMORY_NEW(Custom_Error,                                ParserState("[C-VALUE]"),                                sass_error_get_message(val));       break;       case SASS_WARNING:-        return SASS_MEMORY_NEW(mem, Custom_Warning,+        return SASS_MEMORY_NEW(Custom_Warning,                                ParserState("[C-VALUE]"),                                sass_warning_get_message(val));       break;
libsass/src/values.hpp view
@@ -5,8 +5,8 @@  namespace Sass { -  union Sass_Value* ast_node_to_sass_value (const Expression* val);-  Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val);+  union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val);+  Value_Ptr sass_value_to_ast_node (const union Sass_Value* val);  } #endif
tests/Main.hs view
@@ -35,7 +35,7 @@ #ifndef EXTERNAL_LIBSASS     it "should report correct version" $ do         str <- peekCString libsass_version-        str `shouldBe` "3.3.2"+        str `shouldBe` "3.4.3" #endif      it "should support quoted strings" $ withCString "sample" $ \cstr -> do