hlibsass 0.1.6.1 → 0.1.7.0
raw patch · 105 files changed
+11652/−9580 lines, 105 filessetup-changedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Bindings.Libsass.Base: sass_resolve_file :: CString -> Ptr CString -> IO CString
- Bindings.Libsass.Context: sass_option_get_plugin_path :: Ptr SassOptions -> IO CString
+ Bindings.Libsass.Context: sass_compiler_find_file :: CString -> Ptr SassCompiler -> IO CString
+ Bindings.Libsass.Context: sass_compiler_find_include :: CString -> Ptr SassCompiler -> IO CString
+ Bindings.Libsass.Context: sass_compiler_get_callee_entry :: Ptr SassCompiler -> CSize -> IO SassCalleeEntry
+ Bindings.Libsass.Context: sass_compiler_get_callee_stack_size :: Ptr SassCompiler -> IO CSize
+ Bindings.Libsass.Context: sass_compiler_get_last_callee :: Ptr SassCompiler -> IO SassCalleeEntry
+ Bindings.Libsass.Context: sass_find_file :: CString -> Ptr SassOptions -> IO CString
+ Bindings.Libsass.Context: sass_find_include :: CString -> Ptr SassOptions -> IO CString
+ Bindings.Libsass.Context: sass_option_get_include_path_size :: Ptr SassOptions -> IO CSize
+ Bindings.Libsass.Functions: sass_callee_get_column :: SassCalleeEntry -> IO CSize
+ Bindings.Libsass.Functions: sass_callee_get_env :: SassCalleeEntry -> IO SassEnvFrame
+ Bindings.Libsass.Functions: sass_callee_get_line :: SassCalleeEntry -> IO CSize
+ Bindings.Libsass.Functions: sass_callee_get_name :: SassCalleeEntry -> IO CString
+ Bindings.Libsass.Functions: sass_callee_get_path :: SassCalleeEntry -> IO CString
+ Bindings.Libsass.Functions: sass_callee_get_type :: SassCalleeEntry -> IO CInt
+ Bindings.Libsass.Functions: sass_delete_function :: SassFunctionEntry -> IO ()
+ Bindings.Libsass.Functions: sass_delete_function_list :: SassFunctionList -> IO ()
+ Bindings.Libsass.Functions: sass_delete_importer_list :: SassImporterList -> IO ()
+ Bindings.Libsass.Functions: sass_env_get_global :: SassEnvFrame -> CString -> IO (Ptr SassValue)
+ Bindings.Libsass.Functions: sass_env_get_lexical :: SassEnvFrame -> CString -> IO (Ptr SassValue)
+ Bindings.Libsass.Functions: sass_env_get_local :: SassEnvFrame -> CString -> IO (Ptr SassValue)
+ Bindings.Libsass.Functions: sass_env_set_global :: SassEnvFrame -> CString -> Ptr SassValue -> IO ()
+ Bindings.Libsass.Functions: sass_env_set_lexical :: SassEnvFrame -> CString -> Ptr SassValue -> IO ()
+ Bindings.Libsass.Functions: sass_env_set_local :: SassEnvFrame -> CString -> Ptr SassValue -> IO ()
+ Bindings.Libsass.Types: SassCalleeCFunction :: SassCalleeType
+ Bindings.Libsass.Types: SassCalleeFunction :: SassCalleeType
+ Bindings.Libsass.Types: SassCalleeMixin :: SassCalleeType
+ Bindings.Libsass.Types: data SassCallee
+ Bindings.Libsass.Types: data SassCalleeType
+ Bindings.Libsass.Types: data SassEnv
+ Bindings.Libsass.Types: instance GHC.Classes.Eq Bindings.Libsass.Types.SassCalleeType
+ Bindings.Libsass.Types: instance GHC.Enum.Enum Bindings.Libsass.Types.SassCalleeType
+ Bindings.Libsass.Types: instance GHC.Show.Show Bindings.Libsass.Types.SassCalleeType
+ Bindings.Libsass.Types: type SassCalleeEntry = Ptr SassCallee
+ Bindings.Libsass.Types: type SassEnvFrame = Ptr SassEnv
+ Bindings.Libsass.Values: sass_list_get_is_bracketed :: Ptr SassValue -> IO Bool
+ Bindings.Libsass.Values: sass_list_set_is_bracketed :: Ptr SassValue -> Bool -> IO ()
- Bindings.Libsass.Context: sass_option_get_include_path :: Ptr SassOptions -> IO CString
+ Bindings.Libsass.Context: sass_option_get_include_path :: Ptr SassOptions -> CSize -> IO CString
- Bindings.Libsass.Values: sass_make_list :: CSize -> CInt -> IO (Ptr SassValue)
+ Bindings.Libsass.Values: sass_make_list :: CSize -> CInt -> Bool -> IO (Ptr SassValue)
Files
- Bindings/Libsass/Base.hs +0/−5
- Bindings/Libsass/Context.hs +43/−8
- Bindings/Libsass/Functions.hs +70/−0
- Bindings/Libsass/Types.hs +21/−2
- Bindings/Libsass/Values.hs +10/−0
- CHANGELOG.md +5/−0
- Setup.hs +7/−2
- hlibsass.cabal +1/−1
- libsass/Makefile +10/−6
- libsass/Makefile.conf +3/−0
- libsass/VERSION +1/−1
- libsass/include/sass/base.h +0/−3
- libsass/include/sass/context.h +17/−2
- libsass/include/sass/functions.h +31/−0
- libsass/include/sass/values.h +3/−1
- libsass/include/sass/version.h +1/−1
- libsass/include/sass/version.h.in +1/−1
- libsass/include/sass2scss.h +1/−1
- libsass/src/GNUmakefile.am +1/−1
- libsass/src/ast.cpp +2221/−2446
- libsass/src/ast.hpp +377/−419
- libsass/src/ast_def_macros.hpp +26/−1
- libsass/src/ast_fwd_decl.cpp +29/−0
- libsass/src/ast_fwd_decl.hpp +94/−21
- libsass/src/b64/encode.h +3/−1
- libsass/src/backtrace.cpp +46/−0
- libsass/src/backtrace.hpp +7/−54
- libsass/src/bind.cpp +72/−50
- libsass/src/bind.hpp +0/−1
- libsass/src/cencode.c +6/−0
- libsass/src/check_nesting.cpp +157/−135
- libsass/src/check_nesting.hpp +11/−10
- libsass/src/color_maps.cpp +10/−6
- libsass/src/color_maps.hpp +6/−8
- libsass/src/constants.cpp +4/−3
- libsass/src/constants.hpp +4/−3
- libsass/src/context.cpp +62/−45
- libsass/src/context.hpp +8/−1
- libsass/src/cssize.cpp +104/−93
- libsass/src/cssize.hpp +4/−5
- libsass/src/debugger.hpp +246/−244
- libsass/src/emitter.cpp +30/−6
- libsass/src/emitter.hpp +7/−0
- libsass/src/environment.cpp +67/−16
- libsass/src/environment.hpp +28/−7
- libsass/src/error_handling.cpp +92/−64
- libsass/src/error_handling.hpp +64/−43
- libsass/src/eval.cpp +1662/−1697
- libsass/src/eval.hpp +17/−23
- libsass/src/expand.cpp +180/−153
- libsass/src/expand.hpp +4/−5
- libsass/src/extend.cpp +299/−291
- libsass/src/extend.hpp +46/−11
- libsass/src/file.cpp +101/−34
- libsass/src/file.hpp +14/−3
- libsass/src/functions.cpp +560/−311
- libsass/src/functions.hpp +8/−5
- libsass/src/inspect.cpp +106/−51
- libsass/src/inspect.hpp +5/−2
- libsass/src/lexer.cpp +15/−7
- libsass/src/lexer.hpp +13/−4
- libsass/src/listize.cpp +3/−2
- libsass/src/listize.hpp +0/−1
- libsass/src/memory/SharedPtr.cpp +16/−18
- libsass/src/memory/SharedPtr.hpp +47/−43
- libsass/src/node.cpp +34/−38
- libsass/src/node.hpp +6/−8
- libsass/src/operation.hpp +2/−2
- libsass/src/operators.cpp +240/−0
- libsass/src/operators.hpp +30/−0
- libsass/src/output.cpp +22/−20
- libsass/src/parser.cpp +3117/−2772
- libsass/src/parser.hpp +54/−21
- libsass/src/plugins.cpp +28/−10
- libsass/src/position.cpp +21/−3
- libsass/src/position.hpp +2/−1
- libsass/src/prelexer.cpp +104/−19
- libsass/src/prelexer.hpp +10/−3
- libsass/src/remove_placeholders.cpp +9/−10
- libsass/src/remove_placeholders.hpp +1/−5
- libsass/src/sass.cpp +58/−2
- libsass/src/sass.hpp +5/−2
- libsass/src/sass_context.cpp +66/−58
- libsass/src/sass_context.hpp +3/−4
- libsass/src/sass_functions.cpp +63/−1
- libsass/src/sass_functions.hpp +19/−1
- libsass/src/sass_util.cpp +3/−3
- libsass/src/sass_util.hpp +4/−4
- libsass/src/sass_values.cpp +42/−39
- libsass/src/sass_values.hpp +1/−0
- libsass/src/source_map.cpp +16/−18
- libsass/src/subset_map.cpp +6/−8
- libsass/src/subset_map.hpp +6/−6
- libsass/src/to_c.cpp +2/−2
- libsass/src/to_value.cpp +8/−3
- libsass/src/to_value.hpp +1/−0
- libsass/src/units.cpp +349/−45
- libsass/src/units.hpp +39/−22
- libsass/src/utf8/checked.h +7/−0
- libsass/src/utf8/unchecked.h +7/−0
- libsass/src/utf8_string.cpp +1/−1
- libsass/src/util.cpp +139/−45
- libsass/src/util.hpp +4/−2
- libsass/src/values.cpp +15/−23
- tests/Main.hs +1/−1
Bindings/Libsass/Base.hs view
@@ -25,11 +25,6 @@ :: 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
Bindings/Libsass/Context.hs view
@@ -139,14 +139,6 @@ :: Ptr SassOptions -> IO CString -foreign import ccall unsafe "sass/context.h" sass_option_get_plugin_path- :: Ptr SassOptions- -> IO CString--foreign import ccall unsafe "sass/context.h" sass_option_get_include_path- :: Ptr SassOptions- -> IO CString- foreign import ccall unsafe "sass/context.h" sass_option_get_source_map_file :: Ptr SassOptions -> IO CString@@ -306,6 +298,15 @@ :: Ptr SassContext -> IO (Ptr CString) +foreign import ccall unsafe "sass/context.h" sass_option_get_include_path_size+ :: Ptr SassOptions+ -> IO CSize++foreign import ccall unsafe "sass/context.h" sass_option_get_include_path+ :: Ptr SassOptions+ -> CSize+ -> IO CString+ foreign import ccall unsafe "sass/context.h" sass_context_get_included_files_size :: Ptr SassContext -> IO CSize@@ -363,6 +364,19 @@ -> CSize -> IO SassImportEntry +foreign import ccall unsafe "sass/context.h" sass_compiler_get_callee_stack_size+ :: Ptr SassCompiler+ -> IO CSize++foreign import ccall unsafe "sass/context.h" sass_compiler_get_last_callee+ :: Ptr SassCompiler+ -> IO SassCalleeEntry++foreign import ccall unsafe "sass/context.h" sass_compiler_get_callee_entry+ :: Ptr SassCompiler+ -> CSize+ -> IO SassCalleeEntry+ foreign import ccall unsafe "sass/context.h" sass_option_push_plugin_path :: Ptr SassOptions -> CString@@ -372,3 +386,24 @@ :: Ptr SassOptions -> CString -> IO ()++foreign import ccall unsafe "sass/context.h" sass_find_file+ :: CString+ -> Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass/context.h" sass_find_include+ :: CString+ -> Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass/context.h" sass_compiler_find_file+ :: CString+ -> Ptr SassCompiler+ -> IO CString++foreign import ccall unsafe "sass/context.h" sass_compiler_find_include+ :: CString+ -> Ptr SassCompiler+ -> IO CString+
Bindings/Libsass/Functions.hs view
@@ -20,6 +20,10 @@ -> SassImporterEntry -> IO () +foreign import ccall unsafe "sass/functions.h" sass_delete_importer_list+ :: SassImporterList+ -> IO ()+ foreign import ccall unsafe "sass/functions.h" sass_make_importer :: SassImporterFn -> CDouble@@ -76,6 +80,63 @@ -> CSize -> IO SassImportEntry +foreign import ccall unsafe "sass/functions.h" sass_callee_get_name+ :: SassCalleeEntry+ -> IO CString++foreign import ccall unsafe "sass/functions.h" sass_callee_get_path+ :: SassCalleeEntry+ -> IO CString++foreign import ccall unsafe "sass/functions.h" sass_callee_get_line+ :: SassCalleeEntry+ -> IO CSize++foreign import ccall unsafe "sass/functions.h" sass_callee_get_column+ :: SassCalleeEntry+ -> IO CSize++foreign import ccall unsafe "sass/functions.h" sass_callee_get_type+ :: SassCalleeEntry+ -> IO CInt -- ^ Returns 'SassCalleeType'++foreign import ccall unsafe "sass/functions.h" sass_callee_get_env+ :: SassCalleeEntry+ -> IO SassEnvFrame++foreign import ccall unsafe "sass/functions.h" sass_env_get_lexical+ :: SassEnvFrame+ -> CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/functions.h" sass_env_set_lexical+ :: SassEnvFrame+ -> CString+ -> Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass/functions.h" sass_env_get_local+ :: SassEnvFrame+ -> CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/functions.h" sass_env_set_local+ :: SassEnvFrame+ -> CString+ -> Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass/functions.h" sass_env_get_global+ :: SassEnvFrame+ -> CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/functions.h" sass_env_set_global+ :: SassEnvFrame+ -> CString+ -> Ptr SassValue+ -> IO ()+ foreign import ccall unsafe "sass/functions.h" sass_import_get_imp_path :: SassImportEntry -> IO CString@@ -130,6 +191,14 @@ -> Ptr () -> IO SassFunctionEntry +foreign import ccall unsafe "sass/functions.h" sass_delete_function+ :: SassFunctionEntry+ -> IO ()++foreign import ccall unsafe "sass/functions.h" sass_delete_function_list+ :: SassFunctionList+ -> IO ()+ foreign import ccall unsafe "sass/functions.h" sass_function_get_list_entry :: SassFunctionList -> CSize@@ -152,3 +221,4 @@ foreign import ccall unsafe "sass/functions.h" sass_function_get_cookie :: SassFunctionEntry -> IO (Ptr ())+
Bindings/Libsass/Types.hs view
@@ -33,8 +33,6 @@ -- From sass/context.h -data SassCompiler-data SassOptions data SassContext data SassFileContext data SassDataContext@@ -56,10 +54,16 @@ -- From sass/functions.h +data SassEnv+data SassCallee data SassImport+data SassOptions+data SassCompiler data SassImporter data SassFunction +type SassEnvFrame = Ptr SassEnv+type SassCalleeEntry = Ptr SassCallee type SassImportEntry = Ptr SassImport type SassImportList = Ptr (Ptr SassImport) type SassImporterEntry = Ptr SassImporter@@ -81,6 +85,21 @@ -> Ptr SassOptions -> IO (Ptr SassValue) type SassFunctionFn = FunPtr SassFunctionFnType++data SassCalleeType = SassCalleeMixin+ | SassCalleeFunction+ | SassCalleeCFunction+ deriving (Eq, Show)++instance Enum SassCalleeType where+ fromEnum SassCalleeMixin = 0+ fromEnum SassCalleeFunction = 1+ fromEnum SassCalleeCFunction = 2++ toEnum 0 = SassCalleeMixin+ toEnum 1 = SassCalleeFunction+ toEnum 2 = SassCalleeCFunction+ toEnum u = error ("SassCalleeType.toEnum: Cannot match " ++ show u) -- From sass/values.h
Bindings/Libsass/Values.hs view
@@ -35,6 +35,7 @@ foreign import ccall unsafe "sass/values.h" sass_make_list :: CSize -> CInt -- ^ 'SassSeparator'+ -> Bool -> IO (Ptr SassValue) foreign import ccall unsafe "sass/values.h" sass_make_map@@ -201,6 +202,15 @@ foreign import ccall unsafe "sass/values.h" sass_list_set_separator :: Ptr SassValue -> CInt -- ^ 'SassSeparator'+ -> IO ()++foreign import ccall unsafe "sass/values.h" sass_list_get_is_bracketed+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass/values.h" sass_list_set_is_bracketed+ :: Ptr SassValue+ -> Bool -> IO () foreign import ccall unsafe "sass/values.h" sass_list_get_value
CHANGELOG.md view
@@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.1.7.0] - 2018-03-24+### Changed+- Update to Libsass 3.5.2+ ## [0.1.6.1] - 2017-08-06 ### Fixed - Build process (`Stack.hs`) works with Cabal 1.X and Cabal 2.X@@ -74,6 +78,7 @@ - Bindings to Libsass C API - Basic tests +[0.1.7.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.6.1...v0.1.7.0 [0.1.6.1]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.6.0...v0.1.6.1 [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
Setup.hs view
@@ -16,7 +16,6 @@ import Distribution.System import System.Directory (getCurrentDirectory) -#ifdef MIN_VERSION_Cabal #if MIN_VERSION_Cabal(2, 0, 0) import Distribution.Version (mkVersion) #else@@ -26,7 +25,6 @@ mkFlagName :: String -> FlagName mkFlagName = FlagName #endif-#endif main = defaultMainWithHooks hooksFix where@@ -132,7 +130,14 @@ writeFile "libsass/VERSION" ver return emptyHookedBuildInfo +#if MIN_VERSION_Cabal(2, 2, 0) getCabalFlag :: String -> ConfigFlags -> Bool+getCabalFlag name flags = fromMaybe False (lookupFlagAssignment (mkFlagName name') allFlags)+ where allFlags = configConfigurationsFlags flags+ name' = map toLower name+#else+getCabalFlag :: String -> ConfigFlags -> Bool getCabalFlag name flags = fromMaybe False (lookup (mkFlagName name') allFlags) where allFlags = configConfigurationsFlags flags name' = map toLower name+#endif
hlibsass.cabal view
@@ -1,5 +1,5 @@ name: hlibsass-version: 0.1.6.1+version: 0.1.7.0 license: MIT license-file: LICENSE author: Jakub Fijałkowski <fiolek94@gmail.com>
libsass/Makefile view
@@ -15,10 +15,14 @@ CFLAGS ?= -Wall CXXFLAGS ?= -Wall LDFLAGS ?= -Wall-ifneq "$(COVERAGE)" "yes"+ifeq "x$(COVERAGE)" "x" CFLAGS += -O2 CXXFLAGS += -O2 LDFLAGS += -O2+else+ CFLAGS += -O1 -fno-omit-frame-pointer+ CXXFLAGS += -O1 -fno-omit-frame-pointer+ LDFLAGS += -O1 -fno-omit-frame-pointer endif LDFLAGS += -Wl,-undefined,error CAT ?= $(if $(filter $(OS),Windows_NT),type,cat)@@ -295,7 +299,7 @@ install-headers $(SASSC_BIN): $(BUILD)- $(MAKE) -C $(SASS_SASSC_PATH)+ $(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD)-dev sassc: $(SASSC_BIN) $(SASSC_BIN) -v@@ -305,16 +309,16 @@ $(SASSC_BIN) -v test: $(SASSC_BIN)- $(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)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_build: $(SASSC_BIN)- $(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)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_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)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) 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)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) clean-objects: lib -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la
libsass/Makefile.conf view
@@ -13,6 +13,7 @@ functions.cpp \ color_maps.cpp \ environment.cpp \+ ast_fwd_decl.cpp \ bind.cpp \ file.cpp \ util.cpp \@@ -40,6 +41,8 @@ sass_context.cpp \ sass_functions.cpp \ sass2scss.cpp \+ backtrace.cpp \+ operators.cpp \ to_c.cpp \ to_value.cpp \ source_map.cpp \
libsass/VERSION view
@@ -1,1 +1,1 @@-3.4.3+3.5.2
libsass/include/sass/base.h view
@@ -75,9 +75,6 @@ ADDAPI char* ADDCALL sass_string_quote (const char* str, const char quote_mark); ADDAPI char* ADDCALL sass_string_unquote (const char* str); -// 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[]);- // Implemented sass language version // Hardcoded version 3.4 for time being ADDAPI const char* ADDCALL libsass_version(void);
libsass/include/sass/context.h view
@@ -81,8 +81,6 @@ ADDAPI const char* ADDCALL sass_option_get_linefeed (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_input_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_output_path (struct Sass_Options* options);-ADDAPI const char* ADDCALL sass_option_get_plugin_path (struct Sass_Options* options);-ADDAPI const char* ADDCALL sass_option_get_include_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_source_map_file (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_source_map_root (struct Sass_Options* options); ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_headers (struct Sass_Options* options);@@ -124,6 +122,10 @@ ADDAPI const char* ADDCALL sass_context_get_source_map_string (struct Sass_Context* ctx); ADDAPI char** ADDCALL sass_context_get_included_files (struct Sass_Context* ctx); +// Getters for options include path array+ADDAPI size_t ADDCALL sass_option_get_include_path_size(struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_include_path(struct Sass_Options* options, size_t i);+ // Calculate the size of the stored null terminated array ADDAPI size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx); @@ -143,10 +145,23 @@ ADDAPI size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler); ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler); ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx);+ADDAPI size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler);+ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler);+ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx); // Push function for paths (no manipulation support for now) ADDAPI void ADDCALL sass_option_push_plugin_path (struct Sass_Options* options, const char* path); ADDAPI void ADDCALL sass_option_push_include_path (struct Sass_Options* options, const char* path);++// Resolve a file via the given include paths in the sass option struct+// find_file looks for the exact file name while find_include does a regular sass include+ADDAPI char* ADDCALL sass_find_file (const char* path, struct Sass_Options* opt);+ADDAPI char* ADDCALL sass_find_include (const char* path, struct Sass_Options* opt);++// Resolve a file relative to last import or include paths in the sass option struct+// find_file looks for the exact file name while find_include does a regular sass include+ADDAPI char* ADDCALL sass_compiler_find_file (const char* path, struct Sass_Compiler* compiler);+ADDAPI char* ADDCALL sass_compiler_find_include (const char* path, struct Sass_Compiler* compiler); #ifdef __cplusplus } // __cplusplus defined.
libsass/include/sass/functions.h view
@@ -11,12 +11,18 @@ // Forward declaration+struct Sass_Env;+struct Sass_Callee; struct Sass_Import; struct Sass_Options; struct Sass_Compiler; struct Sass_Importer; struct Sass_Function; +// Typedef helpers for callee lists+typedef struct Sass_Env (*Sass_Env_Frame);+// Typedef helpers for callee lists+typedef struct Sass_Callee (*Sass_Callee_Entry); // Typedef helpers for import lists typedef struct Sass_Import (*Sass_Import_Entry); typedef struct Sass_Import* (*Sass_Import_List);@@ -34,11 +40,18 @@ typedef union Sass_Value* (*Sass_Function_Fn) (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler); +// Type of function calls+enum Sass_Callee_Type {+ SASS_CALLEE_MIXIN,+ SASS_CALLEE_FUNCTION,+ SASS_CALLEE_C_FUNCTION,+}; // Creator for sass custom importer return argument list ADDAPI Sass_Importer_List ADDCALL sass_make_importer_list (size_t length); ADDAPI Sass_Importer_Entry ADDCALL sass_importer_get_list_entry (Sass_Importer_List list, size_t idx); ADDAPI void ADDCALL sass_importer_set_list_entry (Sass_Importer_List list, size_t idx, Sass_Importer_Entry entry);+ADDAPI void ADDCALL sass_delete_importer_list (Sass_Importer_List list); // Creators for custom importer callback (with some additional pointer)@@ -66,6 +79,22 @@ ADDAPI void ADDCALL sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry); ADDAPI Sass_Import_Entry ADDCALL sass_import_get_list_entry (Sass_Import_List list, size_t idx); +// Getters for callee entry+ADDAPI const char* ADDCALL sass_callee_get_name (Sass_Callee_Entry);+ADDAPI const char* ADDCALL sass_callee_get_path (Sass_Callee_Entry);+ADDAPI size_t ADDCALL sass_callee_get_line (Sass_Callee_Entry);+ADDAPI size_t ADDCALL sass_callee_get_column (Sass_Callee_Entry);+ADDAPI enum Sass_Callee_Type ADDCALL sass_callee_get_type (Sass_Callee_Entry);+ADDAPI Sass_Env_Frame ADDCALL sass_callee_get_env (Sass_Callee_Entry);++// Getters and Setters for environments (lexical, local and global)+ADDAPI union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame, const char*);+ADDAPI void ADDCALL sass_env_set_lexical (Sass_Env_Frame, const char*, union Sass_Value*);+ADDAPI union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame, const char*);+ADDAPI void ADDCALL sass_env_set_local (Sass_Env_Frame, const char*, union Sass_Value*);+ADDAPI union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame, const char*);+ADDAPI void ADDCALL sass_env_set_global (Sass_Env_Frame, const char*, union Sass_Value*);+ // Getters for import entry ADDAPI const char* ADDCALL sass_import_get_imp_path (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_abs_path (Sass_Import_Entry);@@ -90,6 +119,8 @@ // Creators for sass function list and function descriptors ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length); ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie);+ADDAPI void ADDCALL sass_delete_function (Sass_Function_Entry entry);+ADDAPI void ADDCALL sass_delete_function_list (Sass_Function_List list); // Setters and getters for callbacks on function lists ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos);
libsass/include/sass/values.h view
@@ -50,7 +50,7 @@ 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_list (size_t len, enum Sass_Separator sep, bool is_bracketed); 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);@@ -116,6 +116,8 @@ // Getters and setters for Sass_List ADDAPI enum Sass_Separator ADDCALL sass_list_get_separator (const union Sass_Value* v); ADDAPI void ADDCALL sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value);+ADDAPI bool ADDCALL sass_list_get_is_bracketed (const union Sass_Value* v);+ADDAPI void ADDCALL sass_list_set_is_bracketed (union Sass_Value* v, bool value); // Getters and setters for Sass_List values ADDAPI union Sass_Value* ADDCALL sass_list_get_value (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value);
libsass/include/sass/version.h view
@@ -6,7 +6,7 @@ #endif #ifndef LIBSASS_LANGUAGE_VERSION-#define LIBSASS_LANGUAGE_VERSION "3.4"+#define LIBSASS_LANGUAGE_VERSION "3.5" #endif #endif
libsass/include/sass/version.h.in view
@@ -6,7 +6,7 @@ #endif #ifndef LIBSASS_LANGUAGE_VERSION-#define LIBSASS_LANGUAGE_VERSION "3.4"+#define LIBSASS_LANGUAGE_VERSION "3.5" #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.1.0"+#define SASS2SCSS_VERSION "1.1.1" #endif // add namespace for c++
libsass/src/GNUmakefile.am view
@@ -34,7 +34,7 @@ libsass_la_SOURCES = ${CSOURCES} ${SOURCES} -libsass_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 0:9:0+libsass_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 1:0:0 if ENABLE_TESTS if ENABLE_COVERAGE
libsass/src/ast.cpp view
@@ -2,2450 +2,2225 @@ #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);-+#include "eval.hpp"+#include "extend.hpp"+#include "emitter.hpp"+#include "color_maps.hpp"+#include "ast_fwd_decl.hpp"+#include <set>+#include <iomanip>+#include <iostream>+#include <algorithm>+#include <functional>+#include <cctype>+#include <locale>++namespace Sass {++ static Null sass_null(ParserState("null"));++ bool Wrapped_Selector::find ( bool (*f)(AST_Node_Obj) )+ {+ // check children first+ if (selector_) {+ if (selector_->find(f)) return true;+ }+ // execute last+ return f(this);+ }++ bool Selector_List::find ( bool (*f)(AST_Node_Obj) )+ {+ // check children first+ for (Complex_Selector_Obj sel : elements()) {+ if (sel->find(f)) return true;+ }+ // execute last+ return f(this);+ }++ bool Compound_Selector::find ( bool (*f)(AST_Node_Obj) )+ {+ // check children first+ for (Simple_Selector_Obj sel : elements()) {+ if (sel->find(f)) return true;+ }+ // execute last+ return f(this);+ }++ bool Complex_Selector::find ( bool (*f)(AST_Node_Obj) )+ {+ // check children first+ if (head_ && head_->find(f)) return true;+ if (tail_ && tail_->find(f)) return true;+ // execute last+ return f(this);+ }++ bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const {+ if (Supports_Operator_Obj op = Cast<Supports_Operator>(cond)) {+ return op->operand() != operand();+ }+ return Cast<Supports_Negation>(cond) != NULL;+ }++ bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const {+ return Cast<Supports_Negation>(cond) ||+ Cast<Supports_Operator>(cond);+ }++ void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v")+ {+ str.erase( str.find_last_not_of( delimiters ) + 1 );+ }++ void String_Constant::rtrim()+ {+ str_rtrim(value_);+ }++ void String_Schema::rtrim()+ {+ if (!empty()) {+ if (String_Ptr str = Cast<String>(last())) str->rtrim();+ }+ }++ void Argument::set_delayed(bool delayed)+ {+ if (value_) value_->set_delayed(delayed);+ is_delayed(delayed);+ }++ void Arguments::set_delayed(bool delayed)+ {+ for (Argument_Obj arg : elements()) {+ if (arg) arg->set_delayed(delayed);+ }+ is_delayed(delayed);+ }+++ bool At_Root_Query::exclude(std::string str)+ {+ bool with = feature() && unquote(feature()->to_string()).compare("with") == 0;+ List_Ptr l = static_cast<List_Ptr>(value().ptr());+ std::string v;++ if (with)+ {+ if (!l || l->length() == 0) return str.compare("rule") != 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->to_string());+ if (v.compare("all") == 0 || v == str) return false;+ }+ return true;+ }+ else+ {+ if (!l || !l->length()) return str.compare("rule") == 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->to_string());+ if (v.compare("all") == 0 || v == str) return true;+ }+ return false;+ }+ }++ void AST_Node::update_pstate(const ParserState& pstate)+ {+ pstate_.offset += pstate - pstate_ + pstate.offset;+ }++ bool Simple_Selector::is_ns_eq(const Simple_Selector& r) const+ {+ // https://github.com/sass/sass/issues/2229+ if ((has_ns_ == r.has_ns_) ||+ (has_ns_ && ns_.empty()) ||+ (r.has_ns_ && r.ns_.empty())+ ) {+ if (ns_.empty() && r.ns() == "*") return false;+ else if (r.ns().empty() && ns() == "*") return false;+ else return ns() == r.ns();+ }+ return false;+ }++ bool Compound_Selector::operator< (const Compound_Selector& rhs) const+ {+ size_t L = std::min(length(), rhs.length());+ for (size_t i = 0; i < L; ++i)+ {+ Simple_Selector_Obj l = (*this)[i];+ Simple_Selector_Obj r = rhs[i];+ if (!l && !r) return false;+ else if (!r) return false;+ else if (!l) return true;+ else if (*l != *r)+ { return *l < *r; }+ }+ // just compare the length now+ return length() < rhs.length();+ }++ bool Compound_Selector::has_parent_ref() const+ {+ for (Simple_Selector_Obj s : *this) {+ if (s && s->has_parent_ref()) return true;+ }+ return false;+ }++ bool Compound_Selector::has_real_parent_ref() const+ {+ for (Simple_Selector_Obj s : *this) {+ if (s && s->has_real_parent_ref()) return true;+ }+ return false;+ }++ bool Complex_Selector::has_parent_ref() const+ {+ return (head() && head()->has_parent_ref()) ||+ (tail() && tail()->has_parent_ref());+ }++ bool Complex_Selector::has_real_parent_ref() const+ {+ return (head() && head()->has_real_parent_ref()) ||+ (tail() && tail()->has_real_parent_ref());+ }++ bool Complex_Selector::operator< (const Complex_Selector& rhs) const+ {+ // const iterators for tails+ Complex_Selector_Ptr_Const l = this;+ Complex_Selector_Ptr_Const r = &rhs;+ Compound_Selector_Ptr l_h = NULL;+ Compound_Selector_Ptr r_h = NULL;+ if (l) l_h = l->head();+ if (r) r_h = r->head();+ // process all tails+ while (true)+ {+ #ifdef DEBUG+ // skip empty ancestor first+ if (l && l->is_empty_ancestor())+ {+ l_h = NULL;+ l = l->tail();+ if(l) l_h = l->head();+ continue;+ }+ // skip empty ancestor first+ if (r && r->is_empty_ancestor())+ {+ r_h = NULL;+ r = r->tail();+ if (r) r_h = r->head();+ continue;+ }+ #endif+ // check for valid selectors+ if (!l) return !!r;+ if (!r) return false;+ // both are null+ else if (!l_h && !r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next headers+ l_h = NULL; r_h = NULL;+ if (l) l_h = l->head();+ if (r) r_h = r->head();+ }+ // one side is null+ else if (!r_h) return true;+ else if (!l_h) return false;+ // heads ok and equal+ else if (*l_h == *r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next headers+ l_h = NULL; r_h = NULL;+ if (l) l_h = l->head();+ if (r) r_h = r->head();+ }+ // heads are not equal+ else return *l_h < *r_h;+ }+ }++ bool Complex_Selector::operator== (const Complex_Selector& rhs) const+ {+ // const iterators for tails+ Complex_Selector_Ptr_Const l = this;+ Complex_Selector_Ptr_Const r = &rhs;+ Compound_Selector_Ptr l_h = NULL;+ Compound_Selector_Ptr r_h = NULL;+ if (l) l_h = l->head();+ if (r) r_h = r->head();+ // process all tails+ while (true)+ {+ #ifdef DEBUG+ // skip empty ancestor first+ if (l && l->is_empty_ancestor())+ {+ l_h = NULL;+ l = l->tail();+ if (l) l_h = l->head();+ continue;+ }+ // skip empty ancestor first+ if (r && r->is_empty_ancestor())+ {+ r_h = NULL;+ r = r->tail();+ if (r) r_h = r->head();+ continue;+ }+ #endif+ // check the pointers+ if (!r) return !l;+ if (!l) return !r;+ // both are null+ if (!l_h && !r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next heads+ l_h = NULL; r_h = NULL;+ if (l) l_h = l->head();+ if (r) r_h = r->head();+ }+ // equals if other head is empty+ else if ((!l_h && !r_h) ||+ (!l_h && r_h->empty()) ||+ (!r_h && l_h->empty()) ||+ (l_h && r_h && *l_h == *r_h))+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() == r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next heads+ l_h = NULL; r_h = NULL;+ if (l) l_h = l->head();+ if (r) r_h = r->head();+ }+ // abort+ else break;+ }+ // unreachable+ return false;+ }++ Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs)+ {+ if (empty()) return rhs;+ Compound_Selector_Obj unified = SASS_MEMORY_COPY(rhs);+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if (unified.isNull()) break;+ unified = at(i)->unify_with(unified);+ }+ return unified.detach();+ }++ bool Complex_Selector::operator== (const Selector& rhs) const+ {+ if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;+ if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;+ if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;+ if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;+ throw std::runtime_error("invalid selector base classes to compare");+ }+++ bool Complex_Selector::operator< (const Selector& rhs) const+ {+ if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;+ if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;+ if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;+ if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool Compound_Selector::operator== (const Selector& rhs) const+ {+ if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;+ if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;+ if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;+ if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool Compound_Selector::operator< (const Selector& rhs) const+ {+ if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;+ if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;+ if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;+ if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool Selector_Schema::operator== (const Selector& rhs) const+ {+ if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;+ if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;+ if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;+ if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool Selector_Schema::operator< (const Selector& rhs) const+ {+ if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;+ if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;+ if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;+ if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool Simple_Selector::operator== (const Selector& rhs) const+ {+ if (Simple_Selector_Ptr_Const sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;+ return false;+ }++ bool Simple_Selector::operator< (const Selector& rhs) const+ {+ if (Simple_Selector_Ptr_Const sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;+ return false;+ }++ bool Simple_Selector::operator== (const Simple_Selector& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (const Pseudo_Selector* lhs = Cast<Pseudo_Selector>(this)) {return *lhs == rhs; }+ else if (const Wrapped_Selector* lhs = Cast<Wrapped_Selector>(this)) {return *lhs == rhs; }+ else if (const Element_Selector* lhs = Cast<Element_Selector>(this)) {return *lhs == rhs; }+ else if (const Attribute_Selector* lhs = Cast<Attribute_Selector>(this)) {return *lhs == rhs; }+ else if (name_ == rhs.name_)+ { return is_ns_eq(rhs); }+ else return false;+ }++ bool Simple_Selector::operator< (const Simple_Selector& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (const Pseudo_Selector* lhs = Cast<Pseudo_Selector>(this)) {return *lhs < rhs; }+ else if (const Wrapped_Selector* lhs = Cast<Wrapped_Selector>(this)) {return *lhs < rhs; }+ else if (const Element_Selector* lhs = Cast<Element_Selector>(this)) {return *lhs < rhs; }+ else if (const Attribute_Selector* lhs = Cast<Attribute_Selector>(this)) {return *lhs < rhs; }+ if (is_ns_eq(rhs))+ { return name_ < rhs.name_; }+ return ns_ < rhs.ns_;+ }++ bool Selector_List::operator== (const Selector& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (Selector_List_Ptr_Const sl = Cast<Selector_List>(&rhs)) { return *this == *sl; }+ else if (Complex_Selector_Ptr_Const cpx = Cast<Complex_Selector>(&rhs)) { return *this == *cpx; }+ else if (Compound_Selector_Ptr_Const cpd = Cast<Compound_Selector>(&rhs)) { return *this == *cpd; }+ // no compare method+ return this == &rhs;+ }++ // Selector lists can be compared to comma lists+ bool Selector_List::operator== (const Expression& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (List_Ptr_Const ls = Cast<List>(&rhs)) { return *ls == *this; }+ if (Selector_Ptr_Const ls = Cast<Selector>(&rhs)) { return *this == *ls; }+ // compare invalid (maybe we should error?)+ return false;+ }++ bool Selector_List::operator== (const Selector_List& rhs) const+ {+ // for array access+ size_t i = 0, n = 0;+ size_t iL = length();+ size_t nL = rhs.length();+ // create temporary vectors and sort them+ std::vector<Complex_Selector_Obj> l_lst = this->elements();+ std::vector<Complex_Selector_Obj> r_lst = rhs.elements();+ std::sort(l_lst.begin(), l_lst.end(), OrderNodes());+ std::sort(r_lst.begin(), r_lst.end(), OrderNodes());+ // process loop+ while (true)+ {+ // first check for valid index+ if (i == iL) return iL == nL;+ else if (n == nL) return iL == nL;+ // the access the vector items+ Complex_Selector_Obj l = l_lst[i];+ Complex_Selector_Obj r = r_lst[n];+ // skip nulls+ if (!l) ++i;+ else if (!r) ++n;+ // do the check+ else if (*l != *r)+ { return false; }+ // advance+ ++i; ++n;+ }+ // there is no break?!+ }++ bool Selector_List::operator< (const Selector& rhs) const+ {+ if (Selector_List_Ptr_Const sp = Cast<Selector_List>(&rhs)) return *this < *sp;+ return false;+ }++ bool Selector_List::operator< (const Selector_List& rhs) const+ {+ size_t l = rhs.length();+ if (length() < l) l = length();+ for (size_t i = 0; i < l; i ++) {+ if (*at(i) < *rhs.at(i)) return true;+ }+ return false;+ }++ Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs)+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ { if (to_string() == rhs->at(i)->to_string()) return rhs; }++ // check for pseudo elements because they are always last+ size_t i, L;+ bool found = false;+ if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector) || typeid(*this) == typeid(Attribute_Selector))+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((Cast<Pseudo_Selector>((*rhs)[i]) || Cast<Wrapped_Selector>((*rhs)[i]) || Cast<Attribute_Selector>((*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element())+ { found = true; break; }+ }+ }+ else+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if (Cast<Pseudo_Selector>((*rhs)[i]) || Cast<Wrapped_Selector>((*rhs)[i]) || Cast<Attribute_Selector>((*rhs)[i]))+ { found = true; break; }+ }+ }+ if (!found)+ {+ rhs->append(this);+ } else {+ rhs->elements().insert(rhs->elements().begin() + i, this);+ }+ return rhs;+ }++ Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs)+ {+ // check if ns can be extended+ // true for no ns or universal+ if (has_universal_ns())+ {+ // but dont extend with universal+ // true for valid ns and universal+ if (!rhs->is_universal_ns())+ {+ // overwrite the name if star is given as name+ if (this->name() == "*") { this->name(rhs->name()); }+ // now overwrite the namespace name and flag+ this->ns(rhs->ns()); this->has_ns(rhs->has_ns());+ // return copy+ return this;+ }+ }+ // namespace may changed, check the name now+ // overwrite star (but not with another star)+ if (name() == "*" && rhs->name() != "*")+ {+ // simply set the new name+ this->name(rhs->name());+ // return copy+ return this;+ }+ // return original+ return this;+ }++ Compound_Selector_Ptr Element_Selector::unify_with(Compound_Selector_Ptr rhs)+ {+ // TODO: handle namespaces++ // if the rhs is empty, just return a copy of this+ if (rhs->length() == 0) {+ rhs->append(this);+ return rhs;+ }++ Simple_Selector_Ptr rhs_0 = rhs->at(0);+ // otherwise, this is a tag name+ if (name() == "*")+ {+ if (typeid(*rhs_0) == typeid(Element_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ Element_Selector_Ptr ts = Cast<Element_Selector>(rhs_0);+ rhs->at(0) = this->unify_with(ts);+ return rhs;+ }+ else if (Cast<Class_Selector>(rhs_0) || Cast<Id_Selector>(rhs_0)) {+ // qualifier is `.class`, so we can prefix with `ns|*.class`+ if (has_ns() && !rhs_0->has_ns()) {+ if (ns() != "*") rhs->elements().insert(rhs->begin(), this);+ }+ return rhs;+ }+++ return rhs;+ }++ if (typeid(*rhs_0) == typeid(Element_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0;+ // otherwise create new compound and unify first simple selector+ rhs->at(0) = this->unify_with(rhs_0);+ return rhs;++ }+ // else it's a tag name and a bunch of qualifiers -- just append them+ if (name() != "*") rhs->elements().insert(rhs->begin(), this);+ return rhs;+ }++ Compound_Selector_Ptr Class_Selector::unify_with(Compound_Selector_Ptr rhs)+ {+ rhs->has_line_break(has_line_break());+ return Simple_Selector::unify_with(rhs);+ }++ Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs)+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if (Id_Selector_Ptr sel = Cast<Id_Selector>(rhs->at(i))) {+ if (sel->name() != name()) return 0;+ }+ }+ rhs->has_line_break(has_line_break());+ return Simple_Selector::unify_with(rhs);+ }++ Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs)+ {+ if (is_pseudo_element())+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if (Pseudo_Selector_Ptr sel = Cast<Pseudo_Selector>(rhs->at(i))) {+ if (sel->is_pseudo_element() && sel->name() != name()) return 0;+ }+ }+ }+ return Simple_Selector::unify_with(rhs);+ }++ bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const+ {+ if (is_ns_eq(rhs)) {+ if (name() == rhs.name()) {+ if (matcher() == rhs.matcher()) {+ bool no_lhs_val = value().isNull();+ bool no_rhs_val = rhs.value().isNull();+ if (no_lhs_val && no_rhs_val) return false; // equal+ else if (no_lhs_val) return true; // lhs is null+ else if (no_rhs_val) return false; // rhs is null+ return *value() < *rhs.value(); // both are given+ } else { return matcher() < rhs.matcher(); }+ } else { return name() < rhs.name(); }+ } else { return ns() < rhs.ns(); }+ }++ bool Attribute_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Attribute_Selector_Ptr_Const w = Cast<Attribute_Selector>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const+ {+ // get optional value state+ bool no_lhs_val = value().isNull();+ bool no_rhs_val = rhs.value().isNull();+ // both are null, therefore equal+ if (no_lhs_val && no_rhs_val) {+ return (name() == rhs.name())+ && (matcher() == rhs.matcher())+ && (is_ns_eq(rhs));+ }+ // both are defined, evaluate+ if (no_lhs_val == no_rhs_val) {+ return (name() == rhs.name())+ && (matcher() == rhs.matcher())+ && (is_ns_eq(rhs))+ && (*value() == *rhs.value());+ }+ // not equal+ return false;++ }++ bool Attribute_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Attribute_Selector_Ptr_Const w = Cast<Attribute_Selector>(&rhs))+ {+ return is_ns_eq(rhs) &&+ name() == rhs.name() &&+ *this == *w;+ }+ return false;+ }++ bool Element_Selector::operator< (const Element_Selector& rhs) const+ {+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Element_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Element_Selector_Ptr_Const w = Cast<Element_Selector>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Element_Selector::operator== (const Element_Selector& rhs) const+ {+ return is_ns_eq(rhs) &&+ name() == rhs.name();+ }++ bool Element_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Element_Selector_Ptr_Const w = Cast<Element_Selector>(&rhs))+ {+ return is_ns_eq(rhs) &&+ name() == rhs.name() &&+ *this == *w;+ }+ return false;+ }++ bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const+ {+ if (is_ns_eq(rhs) && name() == rhs.name())+ {+ String_Obj lhs_ex = expression();+ String_Obj rhs_ex = rhs.expression();+ if (rhs_ex && lhs_ex) return *lhs_ex == *rhs_ex;+ else return lhs_ex.ptr() == rhs_ex.ptr();+ }+ else return false;+ }++ bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Pseudo_Selector_Ptr_Const w = Cast<Pseudo_Selector>(&rhs))+ {+ return *this == *w;+ }+ return is_ns_eq(rhs) &&+ name() == rhs.name();+ }++ bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const+ {+ if (is_ns_eq(rhs) && name() == rhs.name())+ {+ String_Obj lhs_ex = expression();+ String_Obj rhs_ex = rhs.expression();+ if (rhs_ex && lhs_ex) return *lhs_ex < *rhs_ex;+ else return lhs_ex.ptr() < rhs_ex.ptr();+ }+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Pseudo_Selector_Ptr_Const w = Cast<Pseudo_Selector>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const+ {+ if (is_ns_eq(rhs) && name() == rhs.name())+ { return *(selector()) == *(rhs.selector()); }+ else return false;+ }++ bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Wrapped_Selector_Ptr_Const w = Cast<Wrapped_Selector>(&rhs))+ {+ return *this == *w;+ }+ return is_ns_eq(rhs) &&+ name() == rhs.name();+ }++ bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const+ {+ if (is_ns_eq(rhs) && name() == rhs.name())+ { return *(selector()) < *(rhs.selector()); }+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Wrapped_Selector_Ptr_Const w = Cast<Wrapped_Selector>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(rhs))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Obj sub)+ {+ if (this->name() != sub->name()) return false;+ if (this->name() == ":current") return false;+ if (Selector_List_Obj rhs_list = Cast<Selector_List>(sub->selector())) {+ if (Selector_List_Obj lhs_list = Cast<Selector_List>(selector())) {+ return lhs_list->is_superselector_of(rhs_list);+ }+ }+ coreError("is_superselector expected a Selector_List", sub->pstate());+ return false;+ }++ bool Compound_Selector::is_superselector_of(Selector_List_Obj rhs, std::string wrapped)+ {+ for (Complex_Selector_Obj item : rhs->elements()) {+ if (is_superselector_of(item, wrapped)) return true;+ }+ return false;+ }++ bool Compound_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapped)+ {+ if (rhs->head()) return is_superselector_of(rhs->head(), wrapped);+ return false;+ }++ bool Compound_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)+ {+ Compound_Selector_Ptr lhs = this;+ Simple_Selector_Ptr lbase = lhs->base();+ Simple_Selector_Ptr rbase = rhs->base();++ // Check if pseudo-elements are the same between the selectors++ std::set<std::string> lpsuedoset, rpsuedoset;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if ((*this)[i]->is_pseudo_element()) {+ std::string pseudo((*this)[i]->to_string());+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ lpsuedoset.insert(pseudo);+ }+ }+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((*rhs)[i]->is_pseudo_element()) {+ std::string pseudo((*rhs)[i]->to_string());+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ rpsuedoset.insert(pseudo);+ }+ }+ if (lpsuedoset != rpsuedoset) {+ return false;+ }++ // would like to replace this without stringification+ // https://github.com/sass/sass/issues/2229+ // SimpleSelectorSet lset, rset;+ std::set<std::string> lset, rset;++ if (lbase && rbase)+ {+ if (lbase->to_string() == rbase->to_string()) {+ for (size_t i = 1, L = length(); i < L; ++i)+ { lset.insert((*this)[i]->to_string()); }+ for (size_t i = 1, L = rhs->length(); i < L; ++i)+ { rset.insert((*rhs)[i]->to_string()); }+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());+ }+ return false;+ }++ for (size_t i = 0, iL = length(); i < iL; ++i)+ {+ Selector_Obj wlhs = (*this)[i];+ // very special case for wrapped matches selector+ if (Wrapped_Selector_Obj wrapped = Cast<Wrapped_Selector>(wlhs)) {+ if (wrapped->name() == ":not") {+ if (Selector_List_Obj not_list = Cast<Selector_List>(wrapped->selector())) {+ if (not_list->is_superselector_of(rhs, wrapped->name())) return false;+ } else {+ throw std::runtime_error("wrapped not selector is not a list");+ }+ }+ if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {+ wlhs = wrapped->selector();+ if (Selector_List_Obj list = Cast<Selector_List>(wrapped->selector())) {+ if (Compound_Selector_Obj comp = Cast<Compound_Selector>(rhs)) {+ if (!wrapping.empty() && wrapping != wrapped->name()) return false;+ if (wrapping.empty() || wrapping != wrapped->name()) {;+ if (list->is_superselector_of(comp, wrapped->name())) return true;+ }+ }+ }+ }+ Simple_Selector_Ptr rhs_sel = NULL;+ if (rhs->elements().size() > i) rhs_sel = (*rhs)[i];+ if (Wrapped_Selector_Ptr wrapped_r = Cast<Wrapped_Selector>(rhs_sel)) {+ if (wrapped->name() == wrapped_r->name()) {+ if (wrapped->is_superselector_of(wrapped_r)) {+ continue;+ }}+ }+ }+ // match from here on as strings+ lset.insert(wlhs->to_string());+ }++ for (size_t n = 0, nL = rhs->length(); n < nL; ++n)+ {+ Selector_Obj r = (*rhs)[n];+ if (Wrapped_Selector_Obj wrapped = Cast<Wrapped_Selector>(r)) {+ if (wrapped->name() == ":not") {+ if (Selector_List_Obj ls = Cast<Selector_List>(wrapped->selector())) {+ ls->remove_parent_selectors();+ if (is_superselector_of(ls, wrapped->name())) return false;+ }+ }+ if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {+ if (!wrapping.empty()) {+ if (wrapping != wrapped->name()) return false;+ }+ if (Selector_List_Obj ls = Cast<Selector_List>(wrapped->selector())) {+ ls->remove_parent_selectors();+ return (is_superselector_of(ls, wrapped->name()));+ }+ }+ }+ rset.insert(r->to_string());+ }++ //for (auto l : lset) { cerr << "l: " << l << endl; }+ //for (auto r : rset) { cerr << "r: " << r << endl; }++ if (lset.empty()) return true;+ // return true if rset contains all the elements of lset+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());++ }++ // create complex selector (ancestor of) from compound selector+ Complex_Selector_Obj Compound_Selector::to_complex()+ {+ // create an intermediate complex selector+ return SASS_MEMORY_NEW(Complex_Selector,+ pstate(),+ Complex_Selector::ANCESTOR_OF,+ this,+ 0);+ }++ Selector_List_Ptr Complex_Selector::unify_with(Complex_Selector_Ptr other)+ {++ // get last tails (on the right side)+ Complex_Selector_Obj l_last = this->last();+ Complex_Selector_Obj r_last = other->last();++ // check valid pointers (assertion)+ SASS_ASSERT(l_last, "lhs is null");+ SASS_ASSERT(r_last, "rhs is null");++ // Not sure about this check, but closest way I could check+ // was to see if this is a ruby 'SimpleSequence' equivalent.+ // It seems to do the job correctly as some specs react to this+ if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0;+ if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0;++ // get the headers for the last tails+ Compound_Selector_Obj l_last_head = l_last->head();+ Compound_Selector_Obj r_last_head = r_last->head();++ // check valid head pointers (assertion)+ SASS_ASSERT(l_last_head, "lhs head is null");+ SASS_ASSERT(r_last_head, "rhs head is null");++ // get the unification of the last compound selectors+ Compound_Selector_Obj unified = r_last_head->unify_with(l_last_head);++ // abort if we could not unify heads+ if (unified == 0) return 0;++ // check for universal (star: `*`) selector+ bool is_universal = l_last_head->is_universal() ||+ r_last_head->is_universal();++ if (is_universal)+ {+ // move the head+ l_last->head(0);+ r_last->head(unified);+ }++ // create nodes from both selectors+ Node lhsNode = complexSelectorToNode(this);+ Node rhsNode = complexSelectorToNode(other);++ // overwrite universal base+ if (!is_universal)+ {+ // create some temporaries to convert to node+ Complex_Selector_Obj fake = unified->to_complex();+ Node unified_node = complexSelectorToNode(fake);+ // add to permutate the list?+ rhsNode.plus(unified_node);+ }++ // do some magic we inherit from node and extend+ Node node = subweave(lhsNode, rhsNode);+ Selector_List_Obj result = SASS_MEMORY_NEW(Selector_List, pstate());+ NodeDequePtr col = node.collection(); // move from collection to list+ for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++)+ { result->append(nodeToComplexSelector(Node::naiveTrim(*it))); }++ // only return if list has some entries+ return result->length() ? result.detach() : 0;++ }++ bool Compound_Selector::operator== (const Compound_Selector& rhs) const+ {+ // for array access+ size_t i = 0, n = 0;+ size_t iL = length();+ size_t nL = rhs.length();+ // create temporary vectors and sort them+ std::vector<Simple_Selector_Obj> l_lst = this->elements();+ std::vector<Simple_Selector_Obj> r_lst = rhs.elements();+ std::sort(l_lst.begin(), l_lst.end(), OrderNodes());+ std::sort(r_lst.begin(), r_lst.end(), OrderNodes());+ // process loop+ while (true)+ {+ // first check for valid index+ if (i == iL) return iL == nL;+ else if (n == nL) return iL == nL;+ // the access the vector items+ Simple_Selector_Obj l = l_lst[i];+ Simple_Selector_Obj r = r_lst[n];+ // skip nulls+ if (!l) ++i;+ if (!r) ++n;+ // do the check now+ else if (*l != *r)+ { return false; }+ // advance now+ ++i; ++n;+ }+ // there is no break?!+ }++ bool Complex_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)+ {+ return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);+ }++ bool Complex_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapping)+ {+ Complex_Selector_Ptr lhs = this;+ // check for selectors with leading or trailing combinators+ if (!lhs->head() || !rhs->head())+ { return false; }+ Complex_Selector_Obj l_innermost = lhs->innermost();+ if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)+ { return false; }+ Complex_Selector_Obj r_innermost = rhs->innermost();+ if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF)+ { return false; }+ // more complex (i.e., longer) selectors are always more specific+ size_t l_len = lhs->length(), r_len = rhs->length();+ if (l_len > r_len)+ { return false; }++ if (l_len == 1)+ { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); }++ // we have to look one tail deeper, since we cary the+ // combinator around for it (which is important here)+ if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {+ Complex_Selector_Obj lhs_tail = lhs->tail();+ Complex_Selector_Obj rhs_tail = rhs->tail();+ if (lhs_tail->combinator() != rhs_tail->combinator()) return false;+ if (lhs_tail->head() && !rhs_tail->head()) return false;+ if (!lhs_tail->head() && rhs_tail->head()) return false;+ if (lhs_tail->head() && rhs_tail->head()) {+ if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;+ }+ }++ bool found = false;+ Complex_Selector_Obj marker = rhs;+ for (size_t i = 0, L = rhs->length(); i < L; ++i) {+ if (i == L-1)+ { return false; }+ if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping))+ { found = true; break; }+ marker = marker->tail();+ }+ if (!found)+ { return false; }++ /*+ Hmm, I hope I have the logic right:++ if lhs has a combinator:+ if !(marker has a combinator) return false+ if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false+ return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)+ else if marker has a combinator:+ if !(marker.combinator == ">") return false+ return lhs.tail.is_superselector_of(marker.tail)+ else+ return lhs.tail.is_superselector_of(marker.tail)+ */+ if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() == Complex_Selector::ANCESTOR_OF)+ { return false; }+ if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))+ { return false; }+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() != Complex_Selector::PARENT_OF)+ { return false; }+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ return lhs->tail()->is_superselector_of(marker->tail());+ }++ size_t Complex_Selector::length() const+ {+ // TODO: make this iterative+ if (!tail()) return 1;+ return 1 + tail()->length();+ }++ // append another complex selector at the end+ // check if we need to append some headers+ // then we need to check for the combinator+ // only then we can safely set the new tail+ void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces)+ {++ Complex_Selector_Obj t = ss->tail();+ Combinator c = ss->combinator();+ String_Obj r = ss->reference();+ Compound_Selector_Obj h = ss->head();++ if (ss->has_line_feed()) has_line_feed(true);+ if (ss->has_line_break()) has_line_break(true);++ // append old headers+ if (h && h->length()) {+ if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {+ traces.push_back(Backtrace(pstate()));+ throw Exception::InvalidParent(this, traces, ss);+ } else if (last()->head_ && last()->head_->length()) {+ Compound_Selector_Obj rh = last()->head();+ size_t i;+ size_t L = h->length();+ if (Cast<Element_Selector>(h->first())) {+ if (Class_Selector_Ptr cs = Cast<Class_Selector>(rh->last())) {+ Class_Selector_Ptr sqs = SASS_MEMORY_COPY(cs);+ sqs->name(sqs->name() + (*h)[0]->name());+ sqs->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = sqs;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append((*h)[i]);+ } else if (Id_Selector_Ptr is = Cast<Id_Selector>(rh->last())) {+ Id_Selector_Ptr sqs = SASS_MEMORY_COPY(is);+ sqs->name(sqs->name() + (*h)[0]->name());+ sqs->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = sqs;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append((*h)[i]);+ } else if (Element_Selector_Ptr ts = Cast<Element_Selector>(rh->last())) {+ Element_Selector_Ptr tss = SASS_MEMORY_COPY(ts);+ tss->name(tss->name() + (*h)[0]->name());+ tss->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = tss;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append((*h)[i]);+ } else if (Placeholder_Selector_Ptr ps = Cast<Placeholder_Selector>(rh->last())) {+ Placeholder_Selector_Ptr pss = SASS_MEMORY_COPY(ps);+ pss->name(pss->name() + (*h)[0]->name());+ pss->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = pss;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append((*h)[i]);+ } else {+ last()->head_->concat(h);+ }+ } else {+ last()->head_->concat(h);+ }+ } else if (last()->head_) {+ last()->head_->concat(h);+ }+ } else {+ // std::cerr << "has no or empty head\n";+ }++ if (last()) {+ if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {+ Complex_Selector_Ptr inter = SASS_MEMORY_NEW(Complex_Selector, pstate());+ inter->reference(r);+ inter->combinator(c);+ inter->tail(t);+ last()->tail(inter);+ } else {+ if (last()->combinator() == ANCESTOR_OF) {+ last()->combinator(c);+ last()->reference(r);+ }+ last()->tail(t);+ }+ }++ }++ Selector_List_Obj Selector_List::eval(Eval& eval)+ {+ Selector_List_Obj list = schema() ?+ eval(schema()) : eval(this);+ list->schema(schema());+ return list;+ }++ Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)+ {+ if (!this->has_parent_ref()) return this;+ Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());+ Selector_List_Ptr ps = pstack.back();+ for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {+ for (size_t si = 0, sL = this->length(); si < sL; ++si) {+ Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent);+ ss->concat(rv);+ }+ }+ return ss;+ }++ Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)+ {+ Complex_Selector_Obj tail = this->tail();+ Compound_Selector_Obj head = this->head();+ Selector_List_Ptr parents = pstack.back();++ if (!this->has_real_parent_ref() && !implicit_parent) {+ Selector_List_Ptr retval = SASS_MEMORY_NEW(Selector_List, pstate());+ retval->append(this);+ return retval;+ }++ // first resolve_parent_refs the tail (which may return an expanded list)+ Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0;++ if (head && head->length() > 0) {++ Selector_List_Obj retval;+ // we have a parent selector in a simple compound list+ // mix parent complex selector into the compound list+ if (Cast<Parent_Selector>((*head)[0])) {+ retval = SASS_MEMORY_NEW(Selector_List, pstate());++ // it turns out that real parent references reach+ // across @at-root rules, which comes unexpected+ if (parents == NULL && head->has_real_parent_ref()) {+ int i = pstack.size() - 1;+ while (!parents && i > -1) {+ parents = pstack.at(i--);+ }+ }++ if (parents && parents->length()) {+ if (tails && tails->length() > 0) {+ for (size_t n = 0, nL = tails->length(); n < nL; ++n) {+ for (size_t i = 0, iL = parents->length(); i < iL; ++i) {+ Complex_Selector_Obj t = (*tails)[n];+ Complex_Selector_Obj parent = (*parents)[i];+ Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);+ Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);+ ss->tail(t ? SASS_MEMORY_CLONE(t) : NULL);+ Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);+ // remove parent selector from sequence+ if (h->length()) {+ h->erase(h->begin());+ ss->head(h);+ } else {+ ss->head(NULL);+ }+ // adjust for parent selector (1 char)+ // if (h->length()) {+ // ParserState state(h->at(0)->pstate());+ // state.offset.column += 1;+ // state.column -= 1;+ // (*h)[0]->pstate(state);+ // }+ // keep old parser state+ s->pstate(pstate());+ // append new tail+ s->append(ss, traces);+ retval->append(s);+ }+ }+ }+ // have no tails but parents+ // loop above is inside out+ else {+ for (size_t i = 0, iL = parents->length(); i < iL; ++i) {+ Complex_Selector_Obj parent = (*parents)[i];+ Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);+ Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);+ // this is only if valid if the parent has no trailing op+ // otherwise we cannot append more simple selectors to head+ if (parent->last()->combinator() != ANCESTOR_OF) {+ traces.push_back(Backtrace(pstate()));+ throw Exception::InvalidParent(parent, traces, ss);+ }+ ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL);+ Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);+ // remove parent selector from sequence+ if (h->length()) {+ h->erase(h->begin());+ ss->head(h);+ } else {+ ss->head(NULL);+ }+ // \/ IMO ruby sass bug \/+ ss->has_line_feed(false);+ // adjust for parent selector (1 char)+ // if (h->length()) {+ // ParserState state(h->at(0)->pstate());+ // state.offset.column += 1;+ // state.column -= 1;+ // (*h)[0]->pstate(state);+ // }+ // keep old parser state+ s->pstate(pstate());+ // append new tail+ s->append(ss, traces);+ retval->append(s);+ }+ }+ }+ // have no parent but some tails+ else {+ if (tails && tails->length() > 0) {+ for (size_t n = 0, nL = tails->length(); n < nL; ++n) {+ Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);+ cpy->tail(SASS_MEMORY_CLONE(tails->at(n)));+ cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));+ for (size_t i = 1, L = this->head()->length(); i < L; ++i)+ cpy->head()->append((*this->head())[i]);+ if (!cpy->head()->length()) cpy->head(0);+ retval->append(cpy->skip_empty_reference());+ }+ }+ // have no parent nor tails+ else {+ Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);+ cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));+ for (size_t i = 1, L = this->head()->length(); i < L; ++i)+ cpy->head()->append((*this->head())[i]);+ if (!cpy->head()->length()) cpy->head(0);+ retval->append(cpy->skip_empty_reference());+ }+ }+ }+ // no parent selector in head+ else {+ retval = this->tails(tails);+ }++ for (Simple_Selector_Obj ss : head->elements()) {+ if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {+ if (Selector_List_Ptr sl = Cast<Selector_List>(ws->selector())) {+ if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent));+ }+ }+ }++ return retval.detach();++ }+ // has no head+ return this->tails(tails);+ }++ Selector_List_Ptr Complex_Selector::tails(Selector_List_Ptr tails)+ {+ Selector_List_Ptr rv = SASS_MEMORY_NEW(Selector_List, pstate_);+ if (tails && tails->length()) {+ for (size_t i = 0, iL = tails->length(); i < iL; ++i) {+ Complex_Selector_Obj pr = SASS_MEMORY_CLONE(this);+ pr->tail(tails->at(i));+ rv->append(pr);+ }+ }+ else {+ rv->append(this);+ }+ return rv;+ }++ // return the last tail that is defined+ Complex_Selector_Obj Complex_Selector::first()+ {+ // declare variables used in loop+ Complex_Selector_Obj cur = this;+ Compound_Selector_Obj head;+ // processing loop+ while (cur)+ {+ // get the head+ head = cur->head_;+ // abort (and return) if it is not a parent selector+ if (!head || head->length() != 1 || !Cast<Parent_Selector>((*head)[0])) {+ break;+ }+ // advance to next+ cur = cur->tail_;+ }+ // result+ return cur;+ }++ // return the last tail that is defined+ Complex_Selector_Obj Complex_Selector::last()+ {+ Complex_Selector_Ptr cur = this;+ Complex_Selector_Ptr nxt = cur;+ // loop until last+ while (nxt) {+ cur = nxt;+ nxt = cur->tail();+ }+ return cur;+ }++ Complex_Selector::Combinator Complex_Selector::clear_innermost()+ {+ Combinator c;+ if (!tail() || tail()->tail() == 0)+ { c = combinator(); combinator(ANCESTOR_OF); tail(0); }+ else+ { c = tail()->clear_innermost(); }+ return c;+ }++ void Complex_Selector::set_innermost(Complex_Selector_Obj val, Combinator c)+ {+ if (!tail())+ { tail(val); combinator(c); }+ else+ { tail()->set_innermost(val, c); }+ }++ void Complex_Selector::cloneChildren()+ {+ if (head()) head(SASS_MEMORY_CLONE(head()));+ if (tail()) tail(SASS_MEMORY_CLONE(tail()));+ }++ void Compound_Selector::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ void Selector_List::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ void Wrapped_Selector::cloneChildren()+ {+ selector(SASS_MEMORY_CLONE(selector()));+ }++ // remove parent selector references+ // basically unwraps parsed selectors+ void Selector_List::remove_parent_selectors()+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = length(); i < L; ++i) {+ if (!(*this)[i]->head()) continue;+ if ((*this)[i]->head()->is_empty_reference()) {+ // simply move to the next tail if we have "no" combinator+ if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) {+ if ((*this)[i]->tail()) {+ if ((*this)[i]->has_line_feed()) {+ (*this)[i]->tail()->has_line_feed(true);+ }+ (*this)[i] = (*this)[i]->tail();+ }+ }+ // otherwise remove the first item from head+ else {+ (*this)[i]->head()->erase((*this)[i]->head()->begin());+ }+ }+ }+ }++ size_t Wrapped_Selector::hash()+ {+ if (hash_ == 0) {+ hash_combine(hash_, Simple_Selector::hash());+ if (selector_) hash_combine(hash_, selector_->hash());+ }+ return hash_;+ }+ bool Wrapped_Selector::has_parent_ref() const {+ // if (has_reference()) return true;+ if (!selector()) return false;+ return selector()->has_parent_ref();+ }+ bool Wrapped_Selector::has_real_parent_ref() const {+ // if (has_reference()) return true;+ if (!selector()) return false;+ return selector()->has_real_parent_ref();+ }+ unsigned long Wrapped_Selector::specificity() const+ {+ return selector_ ? selector_->specificity() : 0;+ }+++ bool Selector_List::has_parent_ref() const+ {+ for (Complex_Selector_Obj s : elements()) {+ if (s && s->has_parent_ref()) return true;+ }+ return false;+ }++ bool Selector_List::has_real_parent_ref() const+ {+ for (Complex_Selector_Obj s : elements()) {+ if (s && s->has_real_parent_ref()) return true;+ }+ return false;+ }++ bool Selector_Schema::has_parent_ref() const+ {+ if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {+ return schema->length() > 0 && Cast<Parent_Selector>(schema->at(0)) != NULL;+ }+ return false;+ }++ bool Selector_Schema::has_real_parent_ref() const+ {+ if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {+ Parent_Selector_Obj p = Cast<Parent_Selector>(schema->at(0));+ return schema->length() > 0 && p && p->is_real_parent_ref();+ }+ return false;+ }++ void Selector_List::adjust_after_pushing(Complex_Selector_Obj c)+ {+ // if (c->has_reference()) has_reference(true);+ }++ // it's a superselector if every selector of the right side+ // list is a superselector of the given left side selector+ bool Complex_Selector::is_superselector_of(Selector_List_Obj sub, std::string wrapping)+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = sub->length(); i < L; ++i) {+ if (!is_superselector_of((*sub)[i], wrapping)) return false;+ }+ return true;+ }++ // it's a superselector if every selector of the right side+ // list is a superselector of the given left side selector+ bool Selector_List::is_superselector_of(Selector_List_Obj sub, std::string wrapping)+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = sub->length(); i < L; ++i) {+ if (!is_superselector_of((*sub)[i], wrapping)) return false;+ }+ return true;+ }++ // it's a superselector if every selector on the right side+ // is a superselector of any one of the left side selectors+ bool Selector_List::is_superselector_of(Compound_Selector_Obj sub, std::string wrapping)+ {+ // Check every lhs selector against right hand+ for(size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_superselector_of(sub, wrapping)) return true;+ }+ return false;+ }++ // it's a superselector if every selector on the right side+ // is a superselector of any one of the left side selectors+ bool Selector_List::is_superselector_of(Complex_Selector_Obj sub, std::string wrapping)+ {+ // Check every lhs selector against right hand+ for(size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_superselector_of(sub)) return true;+ }+ return false;+ }++ Selector_List_Ptr Selector_List::unify_with(Selector_List_Ptr rhs) {+ std::vector<Complex_Selector_Obj> unified_complex_selectors;+ // Unify all of children with RHS's children, storing the results in `unified_complex_selectors`+ for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) {+ Complex_Selector_Obj seq1 = (*this)[lhs_i];+ for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {+ Complex_Selector_Ptr seq2 = rhs->at(rhs_i);++ Selector_List_Obj result = seq1->unify_with(seq2);+ if( result ) {+ for(size_t i = 0, L = result->length(); i < L; ++i) {+ unified_complex_selectors.push_back( (*result)[i] );+ }+ }+ }+ }++ // Creates the final Selector_List by combining all the complex selectors+ Selector_List_Ptr final_result = SASS_MEMORY_NEW(Selector_List, pstate());+ for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {+ final_result->append(*itr);+ }+ return final_result;+ }++ void Selector_List::populate_extends(Selector_List_Obj extendee, Subset_Map& extends)+ {++ Selector_List_Ptr extender = this;+ for (auto complex_sel : extendee->elements()) {+ Complex_Selector_Obj c = complex_sel;+++ // Ignore any parent selectors, until we find the first non Selectorerence head+ Compound_Selector_Obj compound_sel = c->head();+ Complex_Selector_Obj pIter = complex_sel;+ while (pIter) {+ Compound_Selector_Obj pHead = pIter->head();+ if (pHead && Cast<Parent_Selector>(pHead->elements()[0]) == NULL) {+ compound_sel = pHead;+ break;+ }++ pIter = pIter->tail();+ }++ if (!pIter->head() || pIter->tail()) {+ coreError("nested selectors may not be extended", c->pstate());+ }++ compound_sel->is_optional(extendee->is_optional());++ for (size_t i = 0, L = extender->length(); i < L; ++i) {+ extends.put(compound_sel, std::make_pair((*extender)[i], compound_sel));+ }+ }+ };++ void Compound_Selector::append(Simple_Selector_Ptr element)+ {+ Vectorized<Simple_Selector_Obj>::append(element);+ pstate_.offset += element->pstate().offset;+ }++ Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs)+ {+ Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate());+ // result->has_parent_reference(has_parent_reference());++ // not very efficient because it needs to preserve order+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ bool found = false;+ std::string thisSelector((*this)[i]->to_string());+ for (size_t j = 0, M = rhs->length(); j < M; ++j)+ {+ if (thisSelector == (*rhs)[j]->to_string())+ {+ found = true;+ break;+ }+ }+ if (!found) result->append((*this)[i]);+ }++ return result;+ }++ void Compound_Selector::mergeSources(ComplexSelectorSet& sources)+ {+ for (ComplexSelectorSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) {+ this->sources_.insert(SASS_MEMORY_CLONE(*iterator));+ }+ }++ Argument_Obj Arguments::get_rest_argument()+ {+ if (this->has_rest_argument()) {+ for (Argument_Obj arg : this->elements()) {+ if (arg->is_rest_argument()) {+ return arg;+ }+ }+ }+ return NULL;+ }++ Argument_Obj Arguments::get_keyword_argument()+ {+ if (this->has_keyword_argument()) {+ for (Argument_Obj arg : this->elements()) {+ if (arg->is_keyword_argument()) {+ return arg;+ }+ }+ }+ return NULL;+ }++ void Arguments::adjust_after_pushing(Argument_Obj a)+ {+ if (!a->name().empty()) {+ if (has_keyword_argument()) {+ coreError("named arguments must precede variable-length argument", a->pstate());+ }+ has_named_arguments(true);+ }+ else if (a->is_rest_argument()) {+ if (has_rest_argument()) {+ coreError("functions and mixins may only be called with one variable-length argument", a->pstate());+ }+ if (has_keyword_argument_) {+ coreError("only keyword arguments may follow variable arguments", a->pstate());+ }+ has_rest_argument(true);+ }+ else if (a->is_keyword_argument()) {+ if (has_keyword_argument()) {+ coreError("functions and mixins may only be called with one keyword argument", a->pstate());+ }+ has_keyword_argument(true);+ }+ else {+ if (has_rest_argument()) {+ coreError("ordinal arguments must precede variable-length arguments", a->pstate());+ }+ if (has_named_arguments()) {+ coreError("ordinal arguments must precede named arguments", a->pstate());+ }+ }+ }++ bool Ruleset::is_invisible() const {+ if (Selector_List_Ptr sl = Cast<Selector_List>(selector())) {+ for (size_t i = 0, L = sl->length(); i < L; ++i)+ if (!(*sl)[i]->has_placeholder()) return false;+ }+ return true;+ }++ bool Media_Block::is_invisible() const {+ for (size_t i = 0, L = block()->length(); i < L; ++i) {+ Statement_Obj stm = block()->at(i);+ if (!stm->is_invisible()) return false;+ }+ return true;+ }++ Number::Number(ParserState pstate, double val, std::string u, bool zero)+ : Value(pstate),+ Units(),+ value_(val),+ zero_(zero),+ hash_(0)+ {+ size_t l = 0;+ size_t r;+ if (!u.empty()) {+ bool nominator = true;+ while (true) {+ r = u.find_first_of("*/", l);+ std::string unit(u.substr(l, r == std::string::npos ? r : r - l));+ if (!unit.empty()) {+ if (nominator) numerators.push_back(unit);+ else denominators.push_back(unit);+ }+ if (r == std::string::npos) break;+ // ToDo: should error for multiple slashes+ // if (!nominator && u[r] == '/') error(...)+ if (u[r] == '/')+ nominator = false;+ // strange math parsing?+ // else if (u[r] == '*')+ // nominator = true;+ l = r + 1;+ }+ }+ concrete_type(NUMBER);+ }++ // cancel out unnecessary units+ void Number::reduce()+ {+ // apply conversion factor+ value_ *= this->Units::reduce();+ }++ void Number::normalize()+ {+ // apply conversion factor+ value_ *= this->Units::normalize();+ }++ bool Custom_Warning::operator== (const Expression& rhs) const+ {+ if (Custom_Warning_Ptr_Const r = Cast<Custom_Warning>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ bool Custom_Error::operator== (const Expression& rhs) const+ {+ if (Custom_Error_Ptr_Const r = Cast<Custom_Error>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ bool Number::operator== (const Expression& rhs) const+ {+ if (auto rhsnr = Cast<Number>(&rhs)) {+ return *this == *rhsnr;+ }+ return false;+ }++ bool Number::operator== (const Number& rhs) const+ {+ Number l(*this), r(rhs); l.reduce(); r.reduce();+ size_t lhs_units = l.numerators.size() + l.denominators.size();+ size_t rhs_units = r.numerators.size() + r.denominators.size();+ // unitless and only having one unit seems equivalent (will change in future)+ if (!lhs_units || !rhs_units) {+ return NEAR_EQUAL(l.value(), r.value());+ }+ l.normalize(); r.normalize();+ Units &lhs_unit = l, &rhs_unit = r;+ return lhs_unit == rhs_unit &&+ NEAR_EQUAL(l.value(), r.value());+ }++ bool Number::operator< (const Number& rhs) const+ {+ Number l(*this), r(rhs); l.reduce(); r.reduce();+ size_t lhs_units = l.numerators.size() + l.denominators.size();+ size_t rhs_units = r.numerators.size() + r.denominators.size();+ // unitless and only having one unit seems equivalent (will change in future)+ if (!lhs_units || !rhs_units) {+ return l.value() < r.value();+ }+ l.normalize(); r.normalize();+ Units &lhs_unit = l, &rhs_unit = r;+ if (!(lhs_unit == rhs_unit)) {+ /* ToDo: do we always get usefull backtraces? */+ throw Exception::IncompatibleUnits(rhs, *this);+ }+ return lhs_unit < rhs_unit ||+ l.value() < r.value();+ }++ bool String_Quoted::operator== (const Expression& rhs) const+ {+ if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(&rhs)) {+ return (value() == qstr->value());+ } else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(&rhs)) {+ return (value() == cstr->value());+ }+ return false;+ }++ bool String_Constant::is_invisible() const {+ return value_.empty() && quote_mark_ == 0;+ }++ bool String_Constant::operator== (const Expression& rhs) const+ {+ if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(&rhs)) {+ return (value() == qstr->value());+ } else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(&rhs)) {+ return (value() == cstr->value());+ }+ return false;+ }++ bool String_Schema::is_left_interpolant(void) const+ {+ return length() && first()->is_left_interpolant();+ }+ bool String_Schema::is_right_interpolant(void) const+ {+ return length() && last()->is_right_interpolant();+ }++ bool String_Schema::operator== (const Expression& rhs) const+ {+ if (String_Schema_Ptr_Const r = Cast<String_Schema>(&rhs)) {+ if (length() != r->length()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj rv = (*r)[i];+ Expression_Obj lv = (*this)[i];+ if (!lv || !rv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Boolean::operator== (const Expression& rhs) const+ {+ if (Boolean_Ptr_Const r = Cast<Boolean>(&rhs)) {+ return (value() == r->value());+ }+ return false;+ }++ bool Color::operator== (const Expression& rhs) const+ {+ if (Color_Ptr_Const r = Cast<Color>(&rhs)) {+ return r_ == r->r() &&+ g_ == r->g() &&+ b_ == r->b() &&+ a_ == r->a();+ }+ return false;+ }++ bool List::operator== (const Expression& rhs) const+ {+ if (List_Ptr_Const r = Cast<List>(&rhs)) {+ if (length() != r->length()) return false;+ if (separator() != r->separator()) return false;+ if (is_bracketed() != r->is_bracketed()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj rv = r->at(i);+ Expression_Obj lv = this->at(i);+ if (!lv || !rv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Map::operator== (const Expression& rhs) const+ {+ if (Map_Ptr_Const r = Cast<Map>(&rhs)) {+ if (length() != r->length()) return false;+ for (auto key : keys()) {+ Expression_Obj lv = at(key);+ Expression_Obj rv = r->at(key);+ if (!rv || !lv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Null::operator== (const Expression& rhs) const+ {+ return rhs.concrete_type() == NULL_VAL;+ }++ bool Function::operator== (const Expression& rhs) const+ {+ if (Function_Ptr_Const r = Cast<Function>(&rhs)) {+ Definition_Ptr_Const d1 = Cast<Definition>(definition());+ Definition_Ptr_Const d2 = Cast<Definition>(r->definition());+ return d1 && d2 && d1 == d2 && is_css() == r->is_css();+ }+ return false;+ }++ size_t List::size() const {+ if (!is_arglist_) return length();+ // arglist expects a list of arguments+ // so we need to break before keywords+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj obj = this->at(i);+ if (Argument_Ptr arg = Cast<Argument>(obj)) {+ if (!arg->name().empty()) return i;+ }+ }+ return length();+ }++ Expression_Obj Hashed::at(Expression_Obj k) const+ {+ if (elements_.count(k))+ { return elements_.at(k); }+ else { return NULL; }+ }++ bool Binary_Expression::is_left_interpolant(void) const+ {+ return is_interpolant() || (left() && left()->is_left_interpolant());+ }+ bool Binary_Expression::is_right_interpolant(void) const+ {+ return is_interpolant() || (right() && right()->is_right_interpolant());+ }++ const std::string AST_Node::to_string(Sass_Inspect_Options opt) const+ {+ Sass_Output_Options out(opt);+ Emitter emitter(out);+ Inspect i(emitter);+ i.in_declaration = true;+ // ToDo: inspect should be const+ const_cast<AST_Node_Ptr>(this)->perform(&i);+ return i.get_buffer();+ }++ const std::string AST_Node::to_string() const+ {+ return to_string({ NESTED, 5 });+ }++ std::string String_Quoted::inspect() const+ {+ return quote(value_, '*');+ }++ std::string String_Constant::inspect() const+ {+ return quote(value_, '*');+ }++ bool Declaration::is_invisible() const+ {+ if (is_custom_property()) return false;++ return !(value_ && value_->concrete_type() != Expression::NULL_VAL);+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Additional method on Lists to retrieve values directly or from an encompassed Argument.+ //////////////////////////////////////////////////////////////////////////////////////////+ Expression_Obj List::value_at_index(size_t i) {+ Expression_Obj obj = this->at(i);+ if (is_arglist_) {+ if (Argument_Ptr arg = Cast<Argument>(obj)) {+ return arg->value();+ } else {+ return obj;+ }+ } else {+ return obj;+ }+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Convert map to (key, value) list.+ //////////////////////////////////////////////////////////////////////////////////////////+ List_Obj Map::to_list(ParserState& pstate) {+ List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA);++ for (auto key : keys()) {+ List_Obj l = SASS_MEMORY_NEW(List, pstate, 2);+ l->append(key);+ l->append(at(key));+ ret->append(l);+ }++ return ret;+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Copy implementations+ //////////////////////////////////////////////////////////////////////////////////////////++ #ifdef DEBUG_SHARED_PTR++ #define IMPLEMENT_AST_OPERATORS(klass) \+ klass##_Ptr klass::copy(std::string file, size_t line) const { \+ klass##_Ptr cpy = new klass(this); \+ cpy->trace(file, line); \+ return cpy; \+ } \+ klass##_Ptr klass::clone(std::string file, size_t line) const { \+ klass##_Ptr cpy = copy(file, line); \+ cpy->cloneChildren(); \+ return cpy; \+ } \++ #else++ #define IMPLEMENT_AST_OPERATORS(klass) \+ klass##_Ptr klass::copy() const { \+ return new klass(this); \+ } \+ klass##_Ptr klass::clone() const { \+ klass##_Ptr cpy = copy(); \+ cpy->cloneChildren(); \+ return cpy; \+ } \++ #endif++ IMPLEMENT_AST_OPERATORS(Supports_Operator);+ IMPLEMENT_AST_OPERATORS(Supports_Negation);+ IMPLEMENT_AST_OPERATORS(Compound_Selector);+ IMPLEMENT_AST_OPERATORS(Complex_Selector);+ IMPLEMENT_AST_OPERATORS(Element_Selector);+ IMPLEMENT_AST_OPERATORS(Class_Selector);+ IMPLEMENT_AST_OPERATORS(Id_Selector);+ IMPLEMENT_AST_OPERATORS(Pseudo_Selector);+ IMPLEMENT_AST_OPERATORS(Wrapped_Selector);+ IMPLEMENT_AST_OPERATORS(Selector_List);+ IMPLEMENT_AST_OPERATORS(Ruleset);+ IMPLEMENT_AST_OPERATORS(Media_Block);+ IMPLEMENT_AST_OPERATORS(Custom_Warning);+ IMPLEMENT_AST_OPERATORS(Custom_Error);+ IMPLEMENT_AST_OPERATORS(List);+ IMPLEMENT_AST_OPERATORS(Map);+ IMPLEMENT_AST_OPERATORS(Function);+ IMPLEMENT_AST_OPERATORS(Number);+ IMPLEMENT_AST_OPERATORS(Binary_Expression);+ IMPLEMENT_AST_OPERATORS(String_Schema);+ IMPLEMENT_AST_OPERATORS(String_Constant);+ IMPLEMENT_AST_OPERATORS(String_Quoted);+ IMPLEMENT_AST_OPERATORS(Boolean);+ IMPLEMENT_AST_OPERATORS(Color);+ IMPLEMENT_AST_OPERATORS(Null);+ IMPLEMENT_AST_OPERATORS(Parent_Selector);+ IMPLEMENT_AST_OPERATORS(Import);+ IMPLEMENT_AST_OPERATORS(Import_Stub);+ IMPLEMENT_AST_OPERATORS(Function_Call);+ IMPLEMENT_AST_OPERATORS(Directive);+ IMPLEMENT_AST_OPERATORS(At_Root_Block);+ IMPLEMENT_AST_OPERATORS(Supports_Block);+ IMPLEMENT_AST_OPERATORS(While);+ IMPLEMENT_AST_OPERATORS(Each);+ IMPLEMENT_AST_OPERATORS(For);+ IMPLEMENT_AST_OPERATORS(If);+ IMPLEMENT_AST_OPERATORS(Mixin_Call);+ IMPLEMENT_AST_OPERATORS(Extension);+ IMPLEMENT_AST_OPERATORS(Media_Query);+ IMPLEMENT_AST_OPERATORS(Media_Query_Expression);+ IMPLEMENT_AST_OPERATORS(Debug);+ IMPLEMENT_AST_OPERATORS(Error);+ IMPLEMENT_AST_OPERATORS(Warning);+ IMPLEMENT_AST_OPERATORS(Assignment);+ IMPLEMENT_AST_OPERATORS(Return);+ IMPLEMENT_AST_OPERATORS(At_Root_Query);+ IMPLEMENT_AST_OPERATORS(Variable);+ IMPLEMENT_AST_OPERATORS(Comment);+ IMPLEMENT_AST_OPERATORS(Attribute_Selector);+ IMPLEMENT_AST_OPERATORS(Supports_Interpolation);+ IMPLEMENT_AST_OPERATORS(Supports_Declaration);+ IMPLEMENT_AST_OPERATORS(Supports_Condition);+ IMPLEMENT_AST_OPERATORS(Parameters);+ IMPLEMENT_AST_OPERATORS(Parameter);+ IMPLEMENT_AST_OPERATORS(Arguments);+ IMPLEMENT_AST_OPERATORS(Argument);+ IMPLEMENT_AST_OPERATORS(Unary_Expression);+ IMPLEMENT_AST_OPERATORS(Function_Call_Schema);+ IMPLEMENT_AST_OPERATORS(Block);+ IMPLEMENT_AST_OPERATORS(Content);+ IMPLEMENT_AST_OPERATORS(Trace);+ IMPLEMENT_AST_OPERATORS(Keyframe_Rule);+ IMPLEMENT_AST_OPERATORS(Bubble);+ IMPLEMENT_AST_OPERATORS(Selector_Schema);+ IMPLEMENT_AST_OPERATORS(Placeholder_Selector);+ IMPLEMENT_AST_OPERATORS(Definition);+ IMPLEMENT_AST_OPERATORS(Declaration); }
libsass/src/ast.hpp view
@@ -1,6 +1,7 @@ #ifndef SASS_AST_H #define SASS_AST_H +#include "sass.hpp" #include <set> #include <deque> #include <vector>@@ -74,6 +75,9 @@ // Note: most methods follow precision option const double NUMBER_EPSILON = 0.00000000000001; + // macro to test if numbers are equal within a small error margin+ #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON+ // ToDo: where does this fit best? // We don't share this with C-API? class Operand {@@ -120,12 +124,14 @@ 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 const std::string to_string(Sass_Inspect_Options opt) const;+ virtual const std::string to_string() const; virtual void cloneChildren() {};+ // generic find function (not fully implemented yet)+ // ToDo: add specific implementions to all children+ virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); }; public: void update_pstate(const ParserState& pstate);- void set_pstate_offset(const Offset& offset); public: Offset off() { return pstate(); } Position pos() { return pstate(); }@@ -133,7 +139,22 @@ }; inline AST_Node::~AST_Node() { } + //////////////////////////////////////////////////////////////////////+ // define cast template now (need complete type)+ ////////////////////////////////////////////////////////////////////// + template<class T>+ T* Cast(AST_Node* ptr) {+ return ptr && typeid(T) == typeid(*ptr) ?+ static_cast<T*>(ptr) : NULL;+ };++ template<class T>+ const T* Cast(const AST_Node* ptr) {+ return ptr && typeid(T) == typeid(*ptr) ?+ static_cast<const T*>(ptr) : NULL;+ };+ ////////////////////////////////////////////////////////////////////// // Abstract base class for expressions. This side of the AST hierarchy // represents elements in value contexts, which exist primarily to be@@ -151,9 +172,11 @@ MAP, SELECTOR, NULL_VAL,+ FUNCTION_VAL, C_WARNING, C_ERROR, FUNCTION,+ VARIABLE, NUM_TYPES }; enum Simple_Type {@@ -186,7 +209,7 @@ { } virtual operator bool() { return true; } virtual ~Expression() { }- virtual std::string type() { return ""; /* TODO: raise an error? */ }+ virtual std::string type() const { return ""; /* TODO: raise an error? */ } virtual bool is_invisible() const { return false; } static std::string type_name() { return ""; } virtual bool is_false() { return false; }@@ -343,8 +366,11 @@ 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(); }+ Hashed(size_t s = 0)+ : elements_(ExpressionMap(s)),+ list_(std::vector<Expression_Obj>()),+ hash_(0), duplicate_key_(NULL)+ { elements_.reserve(s); list_.reserve(s); } virtual ~Hashed(); size_t length() const { return list_.size(); } bool empty() const { return list_.empty(); }@@ -454,7 +480,6 @@ //////////////////////// 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)@@ -464,14 +489,12 @@ Block(ParserState pstate, size_t s = 0, bool r = false) : Statement(pstate), Vectorized<Statement_Obj>(s),- is_root_(r),- is_at_root_(false)+ is_root_(r) { } Block(const Block* ptr) : Statement(ptr), Vectorized<Statement_Obj>(*ptr),- is_root_(ptr->is_root_),- is_at_root_(ptr->is_at_root_)+ is_root_(ptr->is_root_) { } virtual bool has_content() {@@ -509,17 +532,15 @@ // of style declarations. ///////////////////////////////////////////////////////////////////////////// class Ruleset : public Has_Block {- ADD_PROPERTY(Selector_Obj, selector)- ADD_PROPERTY(bool, at_root);+ ADD_PROPERTY(Selector_List_Obj, selector) 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)+ Ruleset(ParserState pstate, Selector_List_Obj s = 0, Block_Obj b = 0)+ : Has_Block(pstate, b), selector_(s), is_root_(false) { statement_type(RULESET); } Ruleset(const Ruleset* ptr) : Has_Block(ptr), selector_(ptr->selector_),- at_root_(ptr->at_root_), is_root_(ptr->is_root_) { statement_type(RULESET); } bool is_invisible() const;@@ -551,13 +572,15 @@ // Trace. ///////////////// class Trace : public Has_Block {- ADD_PROPERTY(std::string, name)+ ADD_CONSTREF(char, type)+ ADD_CONSTREF(std::string, name) public:- Trace(ParserState pstate, std::string n, Block_Obj b = 0)- : Has_Block(pstate, b), name_(n)+ Trace(ParserState pstate, std::string n, Block_Obj b = 0, char type = 'm')+ : Has_Block(pstate, b), type_(type), name_(n) { } Trace(const Trace* ptr) : Has_Block(ptr),+ type_(ptr->type_), name_(ptr->name_) { } ATTACH_AST_OPERATIONS(Trace)@@ -573,9 +596,6 @@ 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); }@@ -590,11 +610,11 @@ // optional statement block. /////////////////////////////////////////////////////////////////////// class Directive : public Has_Block {- ADD_PROPERTY(std::string, keyword)- ADD_PROPERTY(Selector_Obj, selector)+ ADD_CONSTREF(std::string, keyword)+ ADD_PROPERTY(Selector_List_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)+ Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel = 0, Block_Obj b = 0, Expression_Obj val = 0) : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed { statement_type(DIRECTIVE); } Directive(const Directive* ptr)@@ -626,7 +646,7 @@ 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)+ ADD_PROPERTY(Selector_List_Obj, name) public: Keyframe_Rule(ParserState pstate, Block_Obj b) : Has_Block(pstate, b), name_()@@ -645,19 +665,22 @@ ADD_PROPERTY(String_Obj, property) ADD_PROPERTY(Expression_Obj, value) ADD_PROPERTY(bool, is_important)+ ADD_PROPERTY(bool, is_custom_property) ADD_PROPERTY(bool, is_indented) public: Declaration(ParserState pstate,- String_Obj prop, Expression_Obj val, bool i = false, Block_Obj b = 0)- : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_indented_(false)+ String_Obj prop, Expression_Obj val, bool i = false, bool c = false, Block_Obj b = 0)+ : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false) { statement_type(DECLARATION); } Declaration(const Declaration* ptr) : Has_Block(ptr), property_(ptr->property_), value_(ptr->value_), is_important_(ptr->is_important_),+ is_custom_property_(ptr->is_custom_property_), is_indented_(ptr->is_indented_) { statement_type(DECLARATION); }+ virtual bool is_invisible() const; ATTACH_AST_OPERATIONS(Declaration) ATTACH_OPERATIONS() };@@ -666,7 +689,7 @@ // Assignments -- variable and value. ///////////////////////////////////// class Assignment : public Statement {- ADD_PROPERTY(std::string, variable)+ ADD_CONSTREF(std::string, variable) ADD_PROPERTY(Expression_Obj, value) ADD_PROPERTY(bool, is_default) ADD_PROPERTY(bool, is_global)@@ -811,7 +834,7 @@ 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)+ : Has_Block(pstate, con), predicate_(pred), alternative_(alt) { statement_type(IF); } If(const If* ptr) : Has_Block(ptr),@@ -830,7 +853,7 @@ // The Sass `@for` control directive. ///////////////////////////////////// class For : public Has_Block {- ADD_PROPERTY(std::string, variable)+ ADD_CONSTREF(std::string, variable) ADD_PROPERTY(Expression_Obj, lower_bound) ADD_PROPERTY(Expression_Obj, upper_bound) ADD_PROPERTY(bool, is_inclusive)@@ -904,9 +927,9 @@ // The Sass `@extend` directive. //////////////////////////////// class Extension : public Statement {- ADD_PROPERTY(Selector_Obj, selector)+ ADD_PROPERTY(Selector_List_Obj, selector) public:- Extension(ParserState pstate, Selector_Obj s)+ Extension(ParserState pstate, Selector_List_Obj s) : Statement(pstate), selector_(s) { statement_type(EXTEND); } Extension(const Extension* ptr)@@ -921,13 +944,12 @@ // 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>);+ typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>); class Definition : public Has_Block { public: enum Type { MIXIN, FUNCTION };- ADD_PROPERTY(std::string, name)+ ADD_CONSTREF(std::string, name) ADD_PROPERTY(Parameters_Obj, parameters) ADD_PROPERTY(Env*, environment) ADD_PROPERTY(Type, type)@@ -1009,7 +1031,7 @@ // Mixin calls (i.e., `@include ...`). ////////////////////////////////////// class Mixin_Call : public Has_Block {- ADD_PROPERTY(std::string, name)+ ADD_CONSTREF(std::string, name) ADD_PROPERTY(Arguments_Obj, arguments) public: Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Block_Obj b = 0)@@ -1028,11 +1050,15 @@ // The @content directive for mixin content blocks. /////////////////////////////////////////////////// class Content : public Statement {- ADD_PROPERTY(Media_Block_Obj, media_block)+ ADD_PROPERTY(Media_Block_Ptr, media_block) public:- Content(ParserState pstate) : Statement(pstate)+ Content(ParserState pstate)+ : Statement(pstate),+ media_block_(NULL) { statement_type(CONTENT); }- Content(const Content* ptr) : Statement(ptr)+ Content(const Content* ptr)+ : Statement(ptr),+ media_block_(ptr->media_block_) { statement_type(CONTENT); } ATTACH_AST_OPERATIONS(Content) ATTACH_OPERATIONS()@@ -1047,14 +1073,16 @@ private: ADD_PROPERTY(enum Sass_Separator, separator) ADD_PROPERTY(bool, is_arglist)+ ADD_PROPERTY(bool, is_bracketed) ADD_PROPERTY(bool, from_selector) public: List(ParserState pstate,- size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false)+ size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false) : Value(pstate), Vectorized<Expression_Obj>(size), separator_(sep), is_arglist_(argl),+ is_bracketed_(bracket), from_selector_(false) { concrete_type(LIST); } List(const List* ptr)@@ -1062,15 +1090,16 @@ Vectorized<Expression_Obj>(*ptr), separator_(ptr->separator_), is_arglist_(ptr->is_arglist_),+ is_bracketed_(ptr->is_bracketed_), from_selector_(ptr->from_selector_) { concrete_type(LIST); }- std::string type() { return is_arglist_ ? "arglist" : "list"; }+ std::string type() const { return is_arglist_ ? "arglist" : "list"; } static std::string type_name() { return "list"; } const char* sep_string(bool compressed = false) const { return separator() == SASS_SPACE ? " " : (compressed ? "," : ", "); }- bool is_invisible() const { return empty(); }+ bool is_invisible() const { return empty() && !is_bracketed(); } Expression_Obj value_at_index(size_t i); virtual size_t size() const;@@ -1079,6 +1108,7 @@ { if (hash_ == 0) { hash_ = std::hash<std::string>()(sep_string());+ hash_combine(hash_, std::hash<bool>()(is_bracketed())); for (size_t i = 0, L = length(); i < L; ++i) hash_combine(hash_, (elements()[i])->hash()); }@@ -1112,10 +1142,10 @@ : Value(ptr), Hashed(*ptr) { concrete_type(MAP); }- std::string type() { return "map"; }+ std::string type() const { return "map"; } static std::string type_name() { return "map"; } bool is_invisible() const { return empty(); }- List_Obj to_list(Context& ctx, ParserState& pstate);+ List_Obj to_list(ParserState& pstate); virtual size_t hash() {@@ -1137,25 +1167,46 @@ 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;+ case AND: return "and";+ case OR: return "or";+ case EQ: return "eq";+ case NEQ: return "neq";+ case GT: return "gt";+ case GTE: return "gte";+ case LT: return "lt";+ case LTE: return "lte";+ case ADD: return "plus";+ case SUB: return "sub";+ case MUL: return "times";+ case DIV: return "div";+ case MOD: return "mod"; // this is only used internally!- case NUM_OPS: return "[OPS]"; break;- default: return "invalid"; break;+ case NUM_OPS: return "[OPS]";+ default: return "invalid"; } } + inline static const std::string sass_op_separator(enum Sass_OP op) {+ switch (op) {+ case AND: return "&&";+ case OR: return "||";+ case EQ: return "==";+ case NEQ: return "!=";+ case GT: return ">";+ case GTE: return ">=";+ case LT: return "<";+ case LTE: return "<=";+ case ADD: return "+";+ case SUB: return "-";+ case MUL: return "*";+ case DIV: return "/";+ case MOD: return "%";+ // this is only used internally!+ case NUM_OPS: return "[OPS]";+ default: return "invalid";+ }+ }+ ////////////////////////////////////////////////////////////////////////// // Binary expressions. Represents logical, relational, and arithmetic // operations. Templatized to avoid large switch statements and repetitive@@ -1163,9 +1214,9 @@ ////////////////////////////////////////////////////////////////////////// class Binary_Expression : public PreValue { private:- ADD_HASHED(Operand, op)- ADD_HASHED(Expression_Obj, left)- ADD_HASHED(Expression_Obj, right)+ HASH_PROPERTY(Operand, op)+ HASH_PROPERTY(Expression_Obj, left)+ HASH_PROPERTY(Expression_Obj, right) size_t hash_; public: Binary_Expression(ParserState pstate,@@ -1180,44 +1231,10 @@ 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;- }+ return sass_op_to_name(optype()); } 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;- }+ return sass_op_separator(optype()); } bool is_left_interpolant(void) const; bool is_right_interpolant(void) const;@@ -1236,11 +1253,11 @@ { try {- Binary_Expression_Ptr_Const m = dynamic_cast<Binary_Expression_Ptr_Const>(&rhs);+ Binary_Expression_Ptr_Const m = Cast<Binary_Expression>(&rhs); if (m == 0) return false; return type() == m->type() &&- left() == m->left() &&- right() == m->right();+ *left() == *m->left() &&+ *right() == *m->right(); } catch (std::bad_cast&) {@@ -1251,13 +1268,13 @@ virtual size_t hash() { if (hash_ == 0) {- hash_ = std::hash<size_t>()(type());+ hash_ = std::hash<size_t>()(optype()); hash_combine(hash_, left()->hash()); hash_combine(hash_, right()->hash()); } return hash_; }- enum Sass_OP type() const { return op_.operand; }+ enum Sass_OP optype() const { return op_.operand; } ATTACH_AST_OPERATIONS(Binary_Expression) ATTACH_OPERATIONS() };@@ -1267,37 +1284,38 @@ //////////////////////////////////////////////////////////////////////////// class Unary_Expression : public Expression { public:- enum Type { PLUS, MINUS, NOT };+ enum Type { PLUS, MINUS, NOT, SLASH }; private:- ADD_HASHED(Type, type)- ADD_HASHED(Expression_Obj, operand)+ HASH_PROPERTY(Type, optype)+ HASH_PROPERTY(Expression_Obj, operand) size_t hash_; public: Unary_Expression(ParserState pstate, Type t, Expression_Obj o)- : Expression(pstate), type_(t), operand_(o), hash_(0)+ : Expression(pstate), optype_(t), operand_(o), hash_(0) { } Unary_Expression(const Unary_Expression* ptr) : Expression(ptr),- type_(ptr->type_),+ optype_(ptr->optype_), 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;+ switch (optype_) {+ case PLUS: return "plus";+ case MINUS: return "minus";+ case SLASH: return "slash";+ case NOT: return "not";+ default: return "invalid"; } } virtual bool operator==(const Expression& rhs) const { try {- Unary_Expression_Ptr_Const m = dynamic_cast<Unary_Expression_Ptr_Const>(&rhs);+ Unary_Expression_Ptr_Const m = Cast<Unary_Expression>(&rhs); if (m == 0) return false; return type() == m->type() &&- operand() == m->operand();+ *operand() == *m->operand(); } catch (std::bad_cast&) {@@ -1308,7 +1326,7 @@ virtual size_t hash() { if (hash_ == 0) {- hash_ = std::hash<size_t>()(type_);+ hash_ = std::hash<size_t>()(optype_); hash_combine(hash_, operand()->hash()); }; return hash_;@@ -1321,8 +1339,8 @@ // Individual argument objects for mixin and function calls. //////////////////////////////////////////////////////////// class Argument : public Expression {- ADD_HASHED(Expression_Obj, value)- ADD_HASHED(std::string, name)+ HASH_PROPERTY(Expression_Obj, value)+ HASH_CONSTREF(std::string, name) ADD_PROPERTY(bool, is_rest_argument) ADD_PROPERTY(bool, is_keyword_argument) size_t hash_;@@ -1331,7 +1349,7 @@ : 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_);+ coreError("variable-length argument may not be passed by name", pstate_); } } Argument(const Argument* ptr)@@ -1343,7 +1361,7 @@ hash_(ptr->hash_) { if (!name_.empty() && is_rest_argument_) {- error("variable-length argument may not be passed by name", pstate_);+ coreError("variable-length argument may not be passed by name", pstate_); } } @@ -1352,7 +1370,7 @@ { try {- Argument_Ptr_Const m = dynamic_cast<Argument_Ptr_Const>(&rhs);+ Argument_Ptr_Const m = Cast<Argument>(&rhs); if (!(m && name() == m->name())) return false; return *value() == *m->value(); }@@ -1412,19 +1430,55 @@ ATTACH_OPERATIONS() }; + ////////////////////////////////////////////////////+ // Function reference.+ ////////////////////////////////////////////////////+ class Function : public Value {+ public:+ ADD_PROPERTY(Definition_Obj, definition)+ ADD_PROPERTY(bool, is_css)+ public:+ Function(ParserState pstate, Definition_Obj def, bool css)+ : Value(pstate), definition_(def), is_css_(css)+ { concrete_type(FUNCTION_VAL); }+ Function(const Function* ptr)+ : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_)+ { concrete_type(FUNCTION_VAL); }++ std::string type() const { return "function"; }+ static std::string type_name() { return "function"; }+ bool is_invisible() const { return true; }++ std::string name() {+ if (definition_) {+ return definition_->name();+ }+ return "";+ }++ virtual bool operator== (const Expression& rhs) const;++ ATTACH_AST_OPERATIONS(Function)+ ATTACH_OPERATIONS()+ };+ ////////////////// // Function calls. ////////////////// class Function_Call : public PreValue {- ADD_HASHED(std::string, name)- ADD_HASHED(Arguments_Obj, arguments)+ HASH_CONSTREF(std::string, name)+ HASH_PROPERTY(Arguments_Obj, arguments)+ HASH_PROPERTY(Function_Obj, func) ADD_PROPERTY(bool, via_call) ADD_PROPERTY(void*, cookie) size_t hash_; public: Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie)- : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(cookie), hash_(0)+ : PreValue(pstate), name_(n), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0) { concrete_type(FUNCTION); }+ Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func)+ : PreValue(pstate), name_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0)+ { concrete_type(FUNCTION); } Function_Call(ParserState pstate, std::string n, Arguments_Obj args) : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); }@@ -1432,20 +1486,26 @@ : PreValue(ptr), name_(ptr->name_), arguments_(ptr->arguments_),+ func_(ptr->func_), via_call_(ptr->via_call_), cookie_(ptr->cookie_), hash_(ptr->hash_) { concrete_type(FUNCTION); } + bool is_css() {+ if (func_) return func_->is_css();+ return false;+ }+ virtual bool operator==(const Expression& rhs) const { try {- Function_Call_Ptr_Const m = dynamic_cast<Function_Call_Ptr_Const>(&rhs);+ Function_Call_Ptr_Const m = Cast<Function_Call>(&rhs); if (!(m && name() == m->name())) return false; if (!(m && arguments()->length() == m->arguments()->length())) return false; for (size_t i =0, L = arguments()->length(); i < L; ++i)- if (!((*arguments())[i] == (*m->arguments())[i])) return false;+ if (!(*(*arguments())[i] == *(*m->arguments())[i])) return false; return true; } catch (std::bad_cast&)@@ -1491,20 +1551,20 @@ // Variable references. /////////////////////// class Variable : public PreValue {- ADD_PROPERTY(std::string, name)+ ADD_CONSTREF(std::string, name) public: Variable(ParserState pstate, std::string n) : PreValue(pstate), name_(n)- { }+ { concrete_type(VARIABLE); } Variable(const Variable* ptr) : PreValue(ptr), name_(ptr->name_)- { }+ { concrete_type(VARIABLE); } virtual bool operator==(const Expression& rhs) const { try {- Variable_Ptr_Const e = dynamic_cast<Variable_Ptr_Const>(&rhs);+ Variable_Ptr_Const e = Cast<Variable>(&rhs); return e && name() == e->name(); } catch (std::bad_cast&)@@ -1523,108 +1583,45 @@ 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)+ class Number : public Value, public Units {+ HASH_PROPERTY(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),+ Units(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"; }+ std::string type() const { 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;+ void reduce();+ void normalize(); virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash<double>()(value_);- for (const auto numerator : numerator_units())+ for (const auto numerator : numerators) hash_combine(hash_, std::hash<std::string>()(numerator));- for (const auto denominator : denominator_units())+ for (const auto denominator : denominators) hash_combine(hash_, std::hash<std::string>()(denominator)); } return hash_; } virtual bool operator< (const Number& rhs) const;+ virtual bool operator== (const Number& rhs) const; virtual bool operator== (const Expression& rhs) const;- virtual bool eq(const Expression& rhs) const; ATTACH_AST_OPERATIONS(Number) ATTACH_OPERATIONS() };@@ -1633,11 +1630,11 @@ // 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)+ HASH_PROPERTY(double, r)+ HASH_PROPERTY(double, g)+ HASH_PROPERTY(double, b)+ HASH_PROPERTY(double, a)+ ADD_CONSTREF(std::string, disp) size_t hash_; public: Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "")@@ -1653,7 +1650,7 @@ disp_(ptr->disp_), hash_(ptr->hash_) { concrete_type(COLOR); }- std::string type() { return "color"; }+ std::string type() const { return "color"; } static std::string type_name() { return "color"; } virtual size_t hash()@@ -1677,7 +1674,7 @@ // Errors from Sass_Values. ////////////////////////////// class Custom_Error : public Value {- ADD_PROPERTY(std::string, message)+ ADD_CONSTREF(std::string, message) public: Custom_Error(ParserState pstate, std::string msg) : Value(pstate), message_(msg)@@ -1694,7 +1691,7 @@ // Warnings from Sass_Values. ////////////////////////////// class Custom_Warning : public Value {- ADD_PROPERTY(std::string, message)+ ADD_CONSTREF(std::string, message) public: Custom_Warning(ParserState pstate, std::string msg) : Value(pstate), message_(msg)@@ -1711,7 +1708,7 @@ // Booleans. //////////// class Boolean : public Value {- ADD_HASHED(bool, value)+ HASH_PROPERTY(bool, value) size_t hash_; public: Boolean(ParserState pstate, bool val)@@ -1724,7 +1721,7 @@ hash_(ptr->hash_) { concrete_type(BOOLEAN); } virtual operator bool() { return value_; }- std::string type() { return "bool"; }+ std::string type() const { return "bool"; } static std::string type_name() { return "bool"; } virtual bool is_false() { return !value_; } @@ -1757,8 +1754,6 @@ 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();@@ -1773,19 +1768,20 @@ // evaluation phase. /////////////////////////////////////////////////////////////////////// class String_Schema : public String, public Vectorized<Expression_Obj> {- // ADD_PROPERTY(bool, has_interpolants)+ ADD_PROPERTY(bool, css) size_t hash_; public:- String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)- : String(pstate), Vectorized<Expression_Obj>(size), hash_(0)+ String_Schema(ParserState pstate, size_t size = 0, bool css = true)+ : String(pstate), Vectorized<Expression_Obj>(size), css_(css), hash_(0) { concrete_type(STRING); } String_Schema(const String_Schema* ptr) : String(ptr), Vectorized<Expression_Obj>(*ptr),+ css_(ptr->css_), hash_(ptr->hash_) { concrete_type(STRING); } - std::string type() { return "string"; }+ std::string type() const { return "string"; } static std::string type_name() { return "string"; } bool is_left_interpolant(void) const;@@ -1798,8 +1794,6 @@ return false; } virtual void rtrim();- virtual void ltrim();- virtual void trim(); virtual size_t hash() {@@ -1825,7 +1819,7 @@ class String_Constant : public String { ADD_PROPERTY(char, quote_mark) ADD_PROPERTY(bool, can_compress_whitespace)- ADD_HASHED(std::string, value)+ HASH_CONSTREF(std::string, value) protected: size_t hash_; public:@@ -1836,24 +1830,22 @@ 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, std::string val, bool css = true)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0) { }- String_Constant(ParserState pstate, const char* beg)- : 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, bool css = true)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0) { }- String_Constant(ParserState pstate, const char* beg, const char* end)- : 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 char* beg, const char* end, bool css = true)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0) { }- String_Constant(ParserState pstate, const Token& tok)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)+ String_Constant(ParserState pstate, const Token& tok, bool css = true)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0) { }- std::string type() { return "string"; }+ std::string type() const { 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() {@@ -1881,8 +1873,8 @@ 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)+ bool strict_unquoting = true, bool css = true)+ : String_Constant(pstate, val, css) { if (skip_unquoting == false) { value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting);@@ -2103,7 +2095,7 @@ if (s->statement_type() == Statement::DIRECTIVE) {- if (Directive_Obj dir = SASS_MEMORY_CAST(Directive, s))+ if (Directive_Obj dir = Cast<Directive>(s)) { std::string keyword(dir->keyword()); if (keyword.length() > 0) keyword.erase(0, 1);@@ -2122,7 +2114,7 @@ { return expression()->exclude("supports"); }- if (Directive_Obj dir = SASS_MEMORY_CAST(Directive, s))+ if (Directive_Obj dir = Cast<Directive>(s)) { if (dir->is_keyframes()) return expression()->exclude("keyframes"); }@@ -2139,7 +2131,7 @@ 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"; }+ std::string type() const { return "null"; } static std::string type_name() { return "null"; } bool is_invisible() const { return true; } operator bool() { return false; }@@ -2172,7 +2164,7 @@ // Individual parameter objects for mixins and functions. ///////////////////////////////////////////////////////// class Parameter : public AST_Node {- ADD_PROPERTY(std::string, name)+ ADD_CONSTREF(std::string, name) ADD_PROPERTY(Expression_Obj, default_value) ADD_PROPERTY(bool, is_rest_parameter) public:@@ -2180,9 +2172,11 @@ 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_);- }+ // tried to come up with a spec test for this, but it does no longer+ // get past the parser (it error out earlier). A spec test was added!+ // if (default_value_ && is_rest_parameter_) {+ // error("variable-length parameter may not have a default value", pstate_);+ // } } Parameter(const Parameter* ptr) : AST_Node(ptr),@@ -2190,9 +2184,11 @@ 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_);- }+ // tried to come up with a spec test for this, but it does no longer+ // get past the parser (it error out earlier). A spec test was added!+ // if (default_value_ && is_rest_parameter_) {+ // error("variable-length parameter may not have a default value", pstate_);+ // } } ATTACH_AST_OPERATIONS(Parameter) ATTACH_OPERATIONS()@@ -2210,23 +2206,23 @@ 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());+ if (has_rest_parameter()) {+ coreError("optional parameters may not be combined with variable-length parameters", p->pstate()); }- has_optional_parameters_ = true;+ 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());+ if (has_rest_parameter()) {+ coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate()); }- has_rest_parameter_ = true;+ has_rest_parameter(true); } else {- if (has_rest_parameter_) {- error("required parameters must precede variable-length parameters", p->pstate());+ if (has_rest_parameter()) {+ coreError("required parameters must precede variable-length parameters", p->pstate()); }- if (has_optional_parameters_) {- error("required parameters must precede optional parameters", p->pstate());+ if (has_optional_parameters()) {+ coreError("required parameters must precede optional parameters", p->pstate()); } } }@@ -2266,9 +2262,8 @@ protected: size_t hash_; public:- Selector(ParserState pstate, bool r = false, bool h = false)+ Selector(ParserState pstate) : Expression(pstate),- // has_reference_(r), has_line_feed_(false), has_line_break_(false), is_optional_(false),@@ -2286,21 +2281,19 @@ { concrete_type(SELECTOR); } virtual ~Selector() = 0; virtual size_t hash() = 0;- virtual unsigned long specificity() {- return 0;- }+ virtual unsigned long specificity() const = 0; virtual void set_media_block(Media_Block_Ptr mb) { media_block(mb); }- virtual bool has_parent_ref() {+ virtual bool has_parent_ref() const { return false; }- virtual bool has_real_parent_ref() {+ virtual bool has_real_parent_ref() const { return false; } // dispatch to correct handlers- virtual bool operator<(const Selector& rhs) const;- virtual bool operator==(const Selector& rhs) const;+ virtual bool operator<(const Selector& rhs) const = 0;+ virtual bool operator==(const Selector& rhs) const = 0; ATTACH_VIRTUAL_AST_OPERATIONS(Selector); }; inline Selector::~Selector() { }@@ -2309,20 +2302,36 @@ // Interpolated selectors -- the interpolated String will be expanded and // re-parsed into a normal selector class. /////////////////////////////////////////////////////////////////////////- class Selector_Schema : public Selector {+ class Selector_Schema : public AST_Node { ADD_PROPERTY(String_Obj, contents)- ADD_PROPERTY(bool, at_root);+ ADD_PROPERTY(bool, connect_parent);+ // must not be a reference counted object+ // otherwise we create circular references+ ADD_PROPERTY(Media_Block_Ptr, media_block)+ // store computed hash+ size_t hash_; public: Selector_Schema(ParserState pstate, String_Obj c)- : Selector(pstate), contents_(c), at_root_(false)+ : AST_Node(pstate),+ contents_(c),+ connect_parent_(true),+ media_block_(NULL),+ hash_(0) { } Selector_Schema(const Selector_Schema* ptr)- : Selector(ptr),+ : AST_Node(ptr), contents_(ptr->contents_),- at_root_(ptr->at_root_)+ connect_parent_(ptr->connect_parent_),+ media_block_(ptr->media_block_),+ hash_(ptr->hash_) { }- virtual bool has_parent_ref();- virtual bool has_real_parent_ref();+ virtual bool has_parent_ref() const;+ virtual bool has_real_parent_ref() const;+ virtual bool operator<(const Selector& rhs) const;+ virtual bool operator==(const Selector& rhs) const;+ // selector schema is not yet a final selector, so we do not+ // have a specificity for it yet. We need to+ virtual unsigned long specificity() const { return 0; } virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, contents_->hash());@@ -2337,8 +2346,8 @@ // Abstract base class for simple selectors. //////////////////////////////////////////// class Simple_Selector : public Selector {- ADD_PROPERTY(std::string, ns)- ADD_PROPERTY(std::string, name)+ ADD_CONSTREF(std::string, ns)+ ADD_CONSTREF(std::string, name) ADD_PROPERTY(Simple_Type, simple_type) ADD_PROPERTY(bool, has_ns) public:@@ -2360,10 +2369,6 @@ 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("");@@ -2380,6 +2385,8 @@ } return hash_; }+ // namespace compare functions+ bool is_ns_eq(const Simple_Selector& r) const; // namespace query functions bool is_universal_ns() const {@@ -2412,11 +2419,10 @@ } 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 Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);+ virtual bool has_parent_ref() const { return false; };+ virtual bool has_real_parent_ref() const { return false; };+ virtual bool is_pseudo_element() const { return false; } virtual bool is_superselector_of(Compound_Selector_Obj sub) { return false; } @@ -2448,14 +2454,14 @@ 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()+ bool is_real_parent_ref() const { return real(); };+ virtual bool has_parent_ref() const { return true; };+ virtual bool has_real_parent_ref() const { return is_real_parent_ref(); };+ virtual unsigned long specificity() const { return 0; }- std::string type() { return "selector"; }+ std::string type() const { return "selector"; } static std::string type_name() { return "selector"; } ATTACH_AST_OPERATIONS(Parent_Selector) ATTACH_OPERATIONS()@@ -2472,7 +2478,7 @@ Placeholder_Selector(const Placeholder_Selector* ptr) : Simple_Selector(ptr) { }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { return Constants::Specificity_Base; }@@ -2495,13 +2501,17 @@ Element_Selector(const Element_Selector* ptr) : Simple_Selector(ptr) { }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { 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&);+ virtual Simple_Selector_Ptr unify_with(Simple_Selector_Ptr);+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);+ virtual bool operator==(const Simple_Selector& rhs) const;+ virtual bool operator==(const Element_Selector& rhs) const;+ virtual bool operator<(const Simple_Selector& rhs) const;+ virtual bool operator<(const Element_Selector& rhs) const; ATTACH_AST_OPERATIONS(Element_Selector) ATTACH_OPERATIONS() };@@ -2517,15 +2527,11 @@ Class_Selector(const Class_Selector* ptr) : Simple_Selector(ptr) { }- virtual bool unique() const- {- return false;- }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { return Constants::Specificity_Class; }- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); ATTACH_AST_OPERATIONS(Class_Selector) ATTACH_OPERATIONS() };@@ -2541,15 +2547,11 @@ Id_Selector(const Id_Selector* ptr) : Simple_Selector(ptr) { }- virtual bool unique() const- {- return true;- }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { return Constants::Specificity_ID; }- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); ATTACH_AST_OPERATIONS(Id_Selector) ATTACH_OPERATIONS() };@@ -2558,17 +2560,19 @@ // Attribute selectors -- e.g., [src*=".jpg"], etc. /////////////////////////////////////////////////// class Attribute_Selector : public Simple_Selector {- ADD_PROPERTY(std::string, matcher)+ ADD_CONSTREF(std::string, matcher) // this cannot be changed to obj atm!!!!!!????!!!!!!! ADD_PROPERTY(String_Obj, value) // might be interpolated+ ADD_PROPERTY(char, modifier); public:- Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v)- : Simple_Selector(pstate, n), matcher_(m), value_(v)+ Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o = 0)+ : Simple_Selector(pstate, n), matcher_(m), value_(v), modifier_(o) { simple_type(ATTR_SEL); } Attribute_Selector(const Attribute_Selector* ptr) : Simple_Selector(ptr), matcher_(ptr->matcher_),- value_(ptr->value_)+ value_(ptr->value_),+ modifier_(ptr->modifier_) { simple_type(ATTR_SEL); } virtual size_t hash() {@@ -2579,7 +2583,7 @@ } return hash_; }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { return Constants::Specificity_Attr; }@@ -2617,14 +2621,6 @@ : 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.@@ -2633,7 +2629,7 @@ // 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()+ virtual bool is_pseudo_element() const { return (name_[0] == ':' && name_[1] == ':') || is_pseudo_class_element(name_);@@ -2646,7 +2642,7 @@ } return hash_; }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { if (is_pseudo_element()) return Constants::Specificity_Element;@@ -2656,7 +2652,7 @@ 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&);+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); ATTACH_AST_OPERATIONS(Pseudo_Selector) ATTACH_OPERATIONS() };@@ -2665,9 +2661,9 @@ // 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)+ ADD_PROPERTY(Selector_List_Obj, selector) public:- Wrapped_Selector(ParserState pstate, std::string n, Selector_Obj sel)+ Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel) : Simple_Selector(pstate, n), selector_(sel) { simple_type(WRAPPED_SEL); } Wrapped_Selector(const Wrapped_Selector* ptr)@@ -2676,28 +2672,11 @@ 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 size_t hash();+ virtual bool has_parent_ref() const;+ virtual bool has_real_parent_ref() const;+ virtual unsigned long specificity() const;+ virtual bool find ( bool (*f)(AST_Node_Obj) ); virtual bool operator==(const Simple_Selector& rhs) const; virtual bool operator==(const Wrapped_Selector& rhs) const; virtual bool operator<(const Simple_Selector& rhs) const;@@ -2707,18 +2686,13 @@ 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_;+ ComplexSelectorSet sources_; ADD_PROPERTY(bool, extended); ADD_PROPERTY(bool, has_parent_reference); protected:@@ -2755,20 +2729,15 @@ } Complex_Selector_Obj to_complex();- Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs, Context& ctx);+ Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs); // 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 {+ virtual bool has_parent_ref() const;+ virtual bool has_real_parent_ref() const;+ Simple_Selector_Ptr base() const { if (length() == 0) return 0; // ToDo: why is this needed?- if (SASS_MEMORY_CAST(Element_Selector, (*this)[0]))- return &(*this)[0];+ if (Cast<Element_Selector>((*this)[0]))+ return (*this)[0]; return 0; } virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapped = "");@@ -2782,7 +2751,7 @@ } return Selector::hash_; }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { int sum = 0; for (size_t i = 0, L = length(); i < L; ++i)@@ -2802,19 +2771,21 @@ bool is_empty_reference() { return length() == 1 &&- SASS_MEMORY_CAST(Parent_Selector, (*this)[0]);+ 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 find ( bool (*f)(AST_Node_Obj) );+ virtual bool operator<(const Selector& rhs) const;+ virtual bool operator==(const Selector& rhs) const; virtual bool operator<(const Compound_Selector& rhs) const; virtual bool operator==(const Compound_Selector& rhs) const; inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); } - SourcesSet& sources() { return sources_; }+ ComplexSelectorSet& sources() { return sources_; } void clearSources() { sources_.clear(); }- void mergeSources(SourcesSet& sources, Context& ctx);+ void mergeSources(ComplexSelectorSet& sources); - Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs, Context& ctx);+ Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs); virtual void cloneChildren(); ATTACH_AST_OPERATIONS(Compound_Selector) ATTACH_OPERATIONS()@@ -2829,10 +2800,10 @@ 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);+ HASH_CONSTREF(Combinator, combinator)+ HASH_PROPERTY(Compound_Selector_Obj, head)+ HASH_PROPERTY(Complex_Selector_Obj, tail)+ HASH_PROPERTY(String_Obj, reference); public: bool contains_placeholder() { if (head() && head()->contains_placeholder()) return true;@@ -2855,8 +2826,8 @@ head_(ptr->head_), tail_(ptr->tail_), reference_(ptr->reference_) {};- virtual bool has_parent_ref();- virtual bool has_real_parent_ref();+ virtual bool has_parent_ref() const;+ virtual bool has_real_parent_ref() const; Complex_Selector_Obj skip_empty_reference() {@@ -2878,10 +2849,7 @@ combinator() == Combinator::ANCESTOR_OF; } - Complex_Selector_Obj context(Context&);--- Selector_List_Ptr tails(Context& ctx, Selector_List_Ptr tails);+ Selector_List_Ptr tails(Selector_List_Ptr tails); // front returns the first real tail // skips over parent and empty ones@@ -2893,13 +2861,13 @@ 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);+ Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true); virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");- Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs, Context& ctx);+ Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs); Combinator clear_innermost();- void append(Context&, Complex_Selector_Obj);+ void append(Complex_Selector_Obj, Backtraces& traces); void set_innermost(Complex_Selector_Obj, Combinator); virtual size_t hash() {@@ -2928,55 +2896,58 @@ if (tail_ && tail_->has_placeholder()) return true; return false; }+ virtual bool find ( bool (*f)(AST_Node_Obj) );+ virtual bool operator<(const Selector& rhs) const;+ virtual bool operator==(const Selector& rhs) const; virtual bool operator<(const Complex_Selector& rhs) const; virtual bool operator==(const Complex_Selector& rhs) const; inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }- SourcesSet sources()+ const ComplexSelectorSet sources() { //s = Set.new //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} //s - SourcesSet srcs;+ ComplexSelectorSet srcs; Compound_Selector_Obj pHead = head(); Complex_Selector_Obj pTail = tail(); if (pHead) {- SourcesSet& headSources = pHead->sources();+ const ComplexSelectorSet& headSources = pHead->sources(); srcs.insert(headSources.begin(), headSources.end()); } if (pTail) {- SourcesSet tailSources = pTail->sources();+ const ComplexSelectorSet& tailSources = pTail->sources(); srcs.insert(tailSources.begin(), tailSources.end()); } return srcs; }- void addSources(SourcesSet& sources, Context& ctx) {+ void addSources(ComplexSelectorSet& sources) { // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} Complex_Selector_Ptr pIter = this; while (pIter) {- Compound_Selector_Ptr pHead = &pIter->head();+ Compound_Selector_Ptr pHead = pIter->head(); if (pHead) {- pHead->mergeSources(sources, ctx);+ pHead->mergeSources(sources); } - pIter = &pIter->tail();+ pIter = pIter->tail(); } } void clearSources() { Complex_Selector_Ptr pIter = this; while (pIter) {- Compound_Selector_Ptr pHead = &pIter->head();+ Compound_Selector_Ptr pHead = pIter->head(); if (pHead) { pHead->clearSources(); } - pIter = &pIter->tail();+ pIter = pIter->tail(); } } @@ -2985,36 +2956,40 @@ 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)+ ADD_PROPERTY(Selector_Schema_Obj, schema)+ ADD_CONSTREF(std::vector<std::string>, wspace) protected: void adjust_after_pushing(Complex_Selector_Obj c); public: Selector_List(ParserState pstate, size_t s = 0)- : Selector(pstate), Vectorized<Complex_Selector_Obj>(s), wspace_(0)+ : Selector(pstate),+ Vectorized<Complex_Selector_Obj>(s),+ schema_(NULL),+ wspace_(0) { } Selector_List(const Selector_List* ptr) : Selector(ptr), Vectorized<Complex_Selector_Obj>(*ptr),+ schema_(ptr->schema_), wspace_(ptr->wspace_) { }- std::string type() { return "list"; }+ std::string type() const { return "list"; } // remove parent selector references // basically unwraps parsed selectors- virtual bool has_parent_ref();- virtual bool has_real_parent_ref();+ virtual bool has_parent_ref() const;+ virtual bool has_real_parent_ref() const; void remove_parent_selectors();- Selector_List_Ptr resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);+ Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true); virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");- Selector_List_Ptr unify_with(Selector_List_Ptr, Context&);- void populate_extends(Selector_List_Obj, Context&, Subset_Map&);+ Selector_List_Ptr unify_with(Selector_List_Ptr);+ void populate_extends(Selector_List_Obj, Subset_Map&);+ Selector_List_Obj eval(Eval& eval); virtual size_t hash() { if (Selector::hash_ == 0) {@@ -3023,10 +2998,10 @@ } return Selector::hash_; }- virtual unsigned long specificity()+ virtual unsigned long specificity() const { unsigned long sum = 0;- unsigned long specificity = 0;+ unsigned long specificity; for (size_t i = 0, L = length(); i < L; ++i) { specificity = (*this)[i]->specificity();@@ -3046,6 +3021,7 @@ } return false; }+ virtual bool find ( bool (*f)(AST_Node_Obj) ); virtual bool operator<(const Selector& rhs) const; virtual bool operator==(const Selector& rhs) const; virtual bool operator<(const Selector_List& rhs) const;@@ -3056,24 +3032,6 @@ 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); } };
libsass/src/ast_def_macros.hpp view
@@ -19,13 +19,22 @@ this->orig = var; *(this->var) = orig; }+ void reset()+ {+ *(this->var) = this->orig;+ } ~LocalOption() { *(this->var) = this->orig; } }; #define LOCAL_FLAG(name,opt) LocalOption<bool> flag_##name(name, opt)+#define LOCAL_COUNT(name,opt) LocalOption<size_t> cnt_##name(name, opt) +#define NESTING_GUARD(name) \+ LocalOption<size_t> cnt_##name(name, name + 1); \+ if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \+ #define ATTACH_OPERATIONS()\ virtual void perform(Operation<void>* op) { (*op)(this); }\ virtual AST_Node_Ptr perform(Operation<AST_Node_Ptr>* op) { return (*op)(this); }\@@ -44,12 +53,28 @@ type name(type name##__) { return name##_ = name##__; }\ private: -#define ADD_HASHED(type, name)\+#define HASH_PROPERTY(type, name)\ protected:\ type name##_;\ public:\ type name() const { return name##_; }\ type name(type name##__) { hash_ = 0; return name##_ = name##__; }\+private:++#define ADD_CONSTREF(type, name) \+protected: \+ type name##_; \+public: \+ const type& name() const { return name##_; } \+ void name(type name##__) { name##_ = name##__; } \+private:++#define HASH_CONSTREF(type, name) \+protected: \+ type name##_; \+public: \+ const type& name() const { return name##_; } \+ void name(type name##__) { hash_ = 0; name##_ = name##__; } \ private: #endif
+ libsass/src/ast_fwd_decl.cpp view
@@ -0,0 +1,29 @@+#include "ast.hpp"++namespace Sass {++ #define IMPLEMENT_BASE_CAST(T) \+ template<> \+ T* Cast(AST_Node* ptr) { \+ return dynamic_cast<T*>(ptr); \+ }; \+ \+ template<> \+ const T* Cast(const AST_Node* ptr) { \+ return dynamic_cast<const T*>(ptr); \+ }; \++ IMPLEMENT_BASE_CAST(AST_Node)+ IMPLEMENT_BASE_CAST(Expression)+ IMPLEMENT_BASE_CAST(Statement)+ IMPLEMENT_BASE_CAST(Has_Block)+ IMPLEMENT_BASE_CAST(PreValue)+ IMPLEMENT_BASE_CAST(Value)+ IMPLEMENT_BASE_CAST(List)+ IMPLEMENT_BASE_CAST(String)+ IMPLEMENT_BASE_CAST(String_Constant)+ IMPLEMENT_BASE_CAST(Supports_Condition)+ IMPLEMENT_BASE_CAST(Selector)+ IMPLEMENT_BASE_CAST(Simple_Selector)++}
libsass/src/ast_fwd_decl.hpp view
@@ -1,12 +1,17 @@ #ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H +#include <map>+#include <set>+#include <deque> #include <vector>+#include <typeinfo> #include <iostream> #include <algorithm> #include <unordered_map> #include <unordered_set> #include "memory/SharedPtr.hpp"+#include "sass/functions.h" ///////////////////////////////////////////// // Forward declarations for the AST visitors.@@ -128,6 +133,9 @@ class Map; typedef Map* Map_Ptr; typedef Map const* Map_Ptr_Const;+ class Function;+ typedef Function* Function_Ptr;+ typedef Function const* Function_Ptr_Const; class Mixin_Call; typedef Mixin_Call* Mixin_Call_Ptr;@@ -154,9 +162,6 @@ 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;@@ -310,6 +315,7 @@ IMPL_MEM_OBJ(Expression); IMPL_MEM_OBJ(List); IMPL_MEM_OBJ(Map);+ IMPL_MEM_OBJ(Function); IMPL_MEM_OBJ(Binary_Expression); IMPL_MEM_OBJ(Unary_Expression); IMPL_MEM_OBJ(Function_Call);@@ -317,7 +323,6 @@ 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);@@ -353,37 +358,105 @@ IMPL_MEM_OBJ(Complex_Selector); IMPL_MEM_OBJ(Selector_List); + // ###########################################################################+ // Implement compare, order and hashing operations for AST Nodes+ // ########################################################################### - struct HashExpression {- size_t operator() (Expression_Obj ex) const;+ struct HashNodes {+ template <class T>+ size_t operator() (const T& ex) const {+ return ex.isNull() ? 0 : ex->hash();+ } };- struct CompareExpression {- bool operator()(const Expression_Obj& lhs, const Expression_Obj& rhs) const;+ struct OrderNodes {+ template <class T>+ bool operator() (const T& lhs, const T& rhs) const {+ return !lhs.isNull() && !rhs.isNull() && *lhs < *rhs;+ } };-- struct HashSimpleSelector {- size_t operator() (Simple_Selector_Obj ex) const;+ struct CompareNodes {+ template <class T>+ bool operator() (const T& lhs, const T& rhs) const {+ // code around sass logic issue. 1px == 1 is true+ // but both items are still different keys in maps+ if (dynamic_cast<Number*>(lhs.ptr()))+ if (dynamic_cast<Number*>(rhs.ptr()))+ return lhs->hash() == rhs->hash();+ return !lhs.isNull() && !rhs.isNull() && *lhs == *rhs;+ } }; - struct CompareSimpleSelector {- bool operator()(Simple_Selector_Obj lhs, Simple_Selector_Obj rhs) const;- };+ // ###########################################################################+ // some often used typedefs+ // ########################################################################### typedef std::unordered_map< Expression_Obj, // key Expression_Obj, // value- HashExpression, // hasher- CompareExpression // compare+ HashNodes, // hasher+ CompareNodes // compare > ExpressionMap; typedef std::unordered_set< Expression_Obj, // value- HashExpression, // hasher- CompareExpression // compare+ HashNodes, // hasher+ CompareNodes // 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;+ typedef std::string SubSetMapKey;+ typedef std::vector<std::string> SubSetMapKeys;++ typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> SubSetMapPair;+ typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SubSetMapLookup;+ typedef std::vector<SubSetMapPair> SubSetMapPairs;+ typedef std::vector<SubSetMapLookup> SubSetMapLookups;++ typedef std::pair<Complex_Selector_Obj, SubSetMapPairs> SubSetMapResult;+ typedef std::vector<SubSetMapResult> SubSetMapResults;++ typedef std::deque<Complex_Selector_Obj> ComplexSelectorDeque;+ typedef std::set<Simple_Selector_Obj, OrderNodes> SimpleSelectorSet;+ typedef std::set<Complex_Selector_Obj, OrderNodes> ComplexSelectorSet;+ typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet;+ typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict;++ typedef std::vector<Sass_Import_Entry>* ImporterStack;++ // only to switch implementations for testing+ #define environment_map std::map++ // ###########################################################################+ // explicit type conversion functions+ // ###########################################################################++ template<class T>+ T* Cast(AST_Node* ptr);++ template<class T>+ const T* Cast(const AST_Node* ptr);++ // sometimes you know the class you want to cast to is final+ // in this case a simple typeid check is faster and safe to use++ #define DECLARE_BASE_CAST(T) \+ template<> T* Cast(AST_Node* ptr); \+ template<> const T* Cast(const AST_Node* ptr); \++ // ###########################################################################+ // implement specialization for final classes+ // ###########################################################################++ DECLARE_BASE_CAST(AST_Node)+ DECLARE_BASE_CAST(Expression)+ DECLARE_BASE_CAST(Statement)+ DECLARE_BASE_CAST(Has_Block)+ DECLARE_BASE_CAST(PreValue)+ DECLARE_BASE_CAST(Value)+ DECLARE_BASE_CAST(List)+ DECLARE_BASE_CAST(String)+ DECLARE_BASE_CAST(String_Constant)+ DECLARE_BASE_CAST(Supports_Condition)+ DECLARE_BASE_CAST(Selector)+ DECLARE_BASE_CAST(Simple_Selector) }
libsass/src/b64/encode.h view
@@ -24,7 +24,9 @@ encoder(int buffersize_in = BUFFERSIZE) : _buffersize(buffersize_in)- {}+ {+ base64_init_encodestate(&_state);+ } int encode(char value_in) {
+ libsass/src/backtrace.cpp view
@@ -0,0 +1,46 @@+#include "backtrace.hpp"++namespace Sass {++ const std::string traces_to_string(Backtraces traces, std::string indent) {++ std::stringstream ss;+ std::string cwd(File::get_cwd());++ bool first = true;+ size_t i_beg = traces.size() - 1;+ size_t i_end = std::string::npos;+ for (size_t i = i_beg; i != i_end; i --) {++ const Backtrace& trace = traces[i];++ // make path relative to the current directory+ std::string rel_path(File::abs2rel(trace.pstate.path, cwd, cwd));++ // skip functions on error cases (unsure why ruby sass does this)+ // if (trace.caller.substr(0, 6) == ", in f") continue;++ if (first) {+ ss << indent;+ ss << "on line ";+ ss << trace.pstate.line + 1;+ ss << " of " << rel_path;+ // ss << trace.caller;+ first = false;+ } else {+ ss << trace.caller;+ ss << std::endl;+ ss << indent;+ ss << "from line ";+ ss << trace.pstate.line + 1;+ ss << " of " << rel_path;+ }++ }++ ss << std::endl;+ return ss.str();++ }++};
libsass/src/backtrace.hpp view
@@ -1,75 +1,28 @@ #ifndef SASS_BACKTRACE_H #define SASS_BACKTRACE_H +#include <vector> #include <sstream>- #include "file.hpp" #include "position.hpp" namespace Sass { - struct Backtrace { - Backtrace* parent; ParserState pstate;- std::string caller;+ std::string caller; - Backtrace(Backtrace* prn, ParserState pstate, std::string c)- : parent(prn),- pstate(pstate),+ Backtrace(ParserState pstate, std::string c = "")+ : pstate(pstate), caller(c) { } - std::string to_string(bool warning = false)- {- size_t i = -1;- std::stringstream ss;- std::string cwd(Sass::File::get_cwd());- Backtrace* this_point = this;-- if (!warning) ss << std::endl << "Backtrace:";- // the first tracepoint (which is parent-less) is an empty placeholder- while (this_point->parent) {-- // make path relative to the current directory- std::string rel_path(Sass::File::abs2rel(this_point->pstate.path, cwd, cwd));-- if (warning) {- ss << std::endl- << "\t"- << (++i == 0 ? "on" : "from")- << " line "- << this_point->pstate.line + 1- << " of "- << rel_path;- } else {- ss << std::endl- << "\t"- << rel_path- << ":"- << this_point->pstate.line + 1- << this_point->parent->caller;- }-- this_point = this_point->parent;- }-- return ss.str();- }+ }; - size_t depth()- {- size_t d = 0;- Backtrace* p = parent;- while (p) {- ++d;- p = p->parent;- }- return d-1;- }+ typedef std::vector<Backtrace> Backtraces; - };+ const std::string traces_to_string(Backtraces traces, std::string indent = "\t"); }
libsass/src/bind.cpp view
@@ -2,6 +2,7 @@ #include "bind.hpp" #include "ast.hpp" #include "context.hpp"+#include "expand.hpp" #include "eval.hpp" #include <map> #include <iostream>@@ -14,9 +15,11 @@ std::string callee(type + " " + name); std::map<std::string, Parameter_Obj> param_map;+ List_Obj varargs = SASS_MEMORY_NEW(List, as->pstate());+ varargs->is_arglist(true); // enable keyword size handling for (size_t i = 0, L = as->length(); i < L; ++i) {- if (auto str = SASS_MEMORY_CAST(String_Quoted, (*as)[i]->value())) {+ if (auto str = Cast<String_Quoted>((*as)[i]->value())) { // force optional quotes (only if needed) if (str->quote_mark()) { str->quote_mark('*');@@ -42,7 +45,7 @@ if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) {- if (List_Obj l = SASS_MEMORY_CAST(List, a->value())) {+ if (List_Obj l = Cast<List>(a->value())) { if (l->length() == 0) { ++ ia; continue; }@@ -51,7 +54,7 @@ std::stringstream msg; msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << " for `" << name << "'";- return error(msg.str(), as->pstate());+ return error(msg.str(), as->pstate(), eval->exp.traces); } Parameter_Obj p = ps->at(ip); @@ -61,7 +64,7 @@ if (a->is_rest_argument()) { // We should always get a list for rest arguments- if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) {+ if (List_Obj rest = Cast<List>(a->value())) { // create a new list object for wrapped items List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(),@@ -70,12 +73,12 @@ true); // wrap each item from list as an argument for (Expression_Obj item : rest->elements()) {- if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, item)) {+ if (Argument_Obj arg = Cast<Argument>(item)) { arglist->append(SASS_MEMORY_COPY(arg)); // copy } else { arglist->append(SASS_MEMORY_NEW(Argument, item->pstate(),- &item,+ item, "", false, false));@@ -93,15 +96,20 @@ // expand keyword arguments into their parameters List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); env->local_frame()[p->name()] = arglist;- Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value());+ Map_Obj argmap = Cast<Map>(a->value()); for (auto key : argmap->keys()) {- 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));+ if (String_Constant_Obj str = Cast<String_Constant>(key)) {+ std::string param = unquote(str->value());+ arglist->append(SASS_MEMORY_NEW(Argument,+ key->pstate(),+ argmap->at(key),+ "$" + param,+ false,+ false));+ } else {+ eval->exp.traces.push_back(Backtrace(key->pstate()));+ throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);+ } } } else {@@ -117,25 +125,25 @@ // get and post inc a = (*as)[ia++]; // maybe we have another list as argument- List_Obj ls = SASS_MEMORY_CAST(List, a->value());+ List_Obj ls = Cast<List>(a->value()); // skip any list completely if empty if (ls && ls->empty() && a->is_rest_argument()) continue; Expression_Obj value = a->value();- if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, value)) {- arglist->append(&arg);+ if (Argument_Obj arg = 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())) {+ if (List_Obj rest = 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);+ for (size_t i = 0, L = rest->length(); i < L; ++i) {+ Expression_Obj obj = rest->value_at_index(i); arglist->append(SASS_MEMORY_NEW(Argument, obj->pstate(),- &obj,+ obj, "", false, false));@@ -155,7 +163,7 @@ } } // assign new arglist to environment- env->local_frame()[p->name()] = &arglist;+ env->local_frame()[p->name()] = arglist; } // consumed parameter ++ip;@@ -166,9 +174,16 @@ // 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_Obj arglist = SASS_MEMORY_CAST(List, a->value());+ List_Obj arglist = Cast<List>(a->value());+ if (!arglist) {+ if (Expression_Obj arg = Cast<Expression>(a->value())) {+ arglist = SASS_MEMORY_NEW(List, a->pstate(), 1);+ arglist->append(arg);+ }+ }+ // empty rest arg - treat all args as default values- if (!arglist->length()) {+ if (!arglist || !arglist->length()) { break; } else { if (arglist->length() > LP - ip && !ps->has_rest_parameter()) {@@ -187,8 +202,8 @@ } // otherwise move one of the rest args into the param, converting to argument if necessary Expression_Obj obj = arglist->at(0);- if (!(a = SASS_MEMORY_CAST(Argument, obj))) {- Expression_Ptr a_to_convert = &obj;+ if (!(a = Cast<Argument>(obj))) {+ Expression_Ptr a_to_convert = obj; a = SASS_MEMORY_NEW(Argument, a_to_convert->pstate(), a_to_convert,@@ -202,17 +217,22 @@ } } else if (a->is_keyword_argument()) {- Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value());+ Map_Obj argmap = Cast<Map>(a->value()); for (auto key : argmap->keys()) {- std::string name = "$" + unquote(SASS_MEMORY_CAST(String_Constant, key)->value());+ String_Constant_Ptr val = Cast<String_Constant>(key);+ if (val == NULL) {+ eval->exp.traces.push_back(Backtrace(key->pstate()));+ throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);+ }+ std::string param = "$" + unquote(val->value()); - if (!param_map.count(name)) {+ if (!param_map.count(param)) { std::stringstream msg;- msg << callee << " has no parameter named " << name;- error(msg.str(), a->pstate());+ msg << callee << " has no parameter named " << param;+ error(msg.str(), a->pstate(), eval->exp.traces); }- env->local_frame()[name] = &argmap->at(&key);+ env->local_frame()[param] = argmap->at(key); } ++ia; continue;@@ -225,32 +245,38 @@ std::stringstream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee;- error(msg.str(), a->pstate());+ error(msg.str(), a->pstate(), eval->exp.traces); } // ordinal arg -- bind it to the next param- env->local_frame()[p->name()] = &a->value();+ env->local_frame()[p->name()] = a->value(); ++ip; } else { // named arg -- bind it to the appropriately named param if (!param_map.count(a->name())) {- std::stringstream msg;- msg << callee << " has no parameter named " << a->name();- error(msg.str(), a->pstate());+ if (ps->has_rest_parameter()) {+ varargs->append(a);+ } else {+ std::stringstream msg;+ msg << callee << " has no parameter named " << a->name();+ error(msg.str(), a->pstate(), eval->exp.traces);+ } }- if (param_map[a->name()]->is_rest_parameter()) {- std::stringstream msg;- msg << "argument " << a->name() << " of " << callee- << "cannot be used as named argument";- error(msg.str(), a->pstate());+ if (param_map[a->name()]) {+ if (param_map[a->name()]->is_rest_parameter()) {+ std::stringstream msg;+ msg << "argument " << a->name() << " of " << callee+ << "cannot be used as named argument";+ error(msg.str(), a->pstate(), eval->exp.traces);+ } } if (env->has_local(a->name())) { std::stringstream msg; msg << "parameter " << p->name() << "provided more than once in call to " << callee;- error(msg.str(), a->pstate());+ error(msg.str(), a->pstate(), eval->exp.traces); }- env->local_frame()[a->name()] = &a->value();+ env->local_frame()[a->name()] = a->value(); } } // EO while ia@@ -265,11 +291,7 @@ // cerr << "********" << endl; if (!env->has_local(leftover->name())) { if (leftover->is_rest_parameter()) {- env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(List,- leftover->pstate(),- 0,- SASS_COMMA,- true);+ env->local_frame()[leftover->name()] = varargs; } else if (leftover->default_value()) { Expression_Ptr dv = leftover->default_value()->perform(eval);@@ -277,7 +299,7 @@ } else { // param is unbound and has no default value -- error- throw Exception::MissingArgument(as->pstate(), name, leftover->name(), type);+ throw Exception::MissingArgument(as->pstate(), eval->exp.traces, name, leftover->name(), type); } } }
libsass/src/bind.hpp view
@@ -6,7 +6,6 @@ #include "ast_fwd_decl.hpp" namespace Sass {- typedef Environment<AST_Node_Obj> Env; void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Context*, Env*, Eval*); }
libsass/src/cencode.c view
@@ -46,6 +46,9 @@ result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4;+ #ifndef _MSC_VER+ __attribute__ ((fallthrough));+ #endif case step_B: if (plainchar == plaintextend) {@@ -57,6 +60,9 @@ result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2;+ #ifndef _MSC_VER+ __attribute__ ((fallthrough));+ #endif case step_C: if (plainchar == plaintextend) {
libsass/src/check_nesting.cpp view
@@ -7,15 +7,20 @@ CheckNesting::CheckNesting() : parents(std::vector<Statement_Ptr>()),- parent(0),- current_mixin_definition(0)+ traces(std::vector<Backtrace>()),+ parent(0), current_mixin_definition(0) { } + void error(AST_Node_Ptr node, Backtraces traces, std::string msg) {+ traces.push_back(Backtrace(node->pstate()));+ throw Exception::InvalidSass(node->pstate(), traces, msg);+ }+ Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent) { Statement_Ptr old_parent = this->parent; - if (At_Root_Block_Ptr root = SASS_MEMORY_CAST_PTR(At_Root_Block, parent)) {+ if (At_Root_Block_Ptr root = Cast<At_Root_Block>(parent)) { std::vector<Statement_Ptr> old_parents = this->parents; std::vector<Statement_Ptr> new_parents; @@ -39,9 +44,15 @@ } } - At_Root_Block_Ptr ar = SASS_MEMORY_CAST_PTR(At_Root_Block, parent);- Statement_Ptr ret = this->visit_children(&ar->block());+ At_Root_Block_Ptr ar = Cast<At_Root_Block>(parent);+ Block_Ptr ret = ar->block(); + if (ret != NULL) {+ for (auto n : ret->elements()) {+ n->perform(this);+ }+ }+ this->parent = old_parent; this->parents = old_parents; @@ -54,11 +65,17 @@ this->parents.push_back(parent); - Block_Ptr b = SASS_MEMORY_CAST_PTR(Block, parent);+ Block_Ptr b = Cast<Block>(parent); + if (Trace_Ptr trace = Cast<Trace>(parent)) {+ if (trace->type() == 'i') {+ this->traces.push_back(Backtrace(trace->pstate()));+ }+ }+ if (!b) {- if (Has_Block_Ptr bb = SASS_MEMORY_CAST(Has_Block, *parent)) {- b = &bb->block();+ if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {+ b = bb->block(); } } @@ -67,9 +84,16 @@ n->perform(this); } }+ this->parent = old_parent; this->parents.pop_back(); + if (Trace_Ptr trace = Cast<Trace>(parent)) {+ if (trace->type() == 'i') {+ this->traces.pop_back();+ }+ }+ return b; } @@ -97,10 +121,21 @@ return n; } + Statement_Ptr CheckNesting::operator()(If_Ptr i)+ {+ this->visit_children(i);++ if (Block_Ptr b = Cast<Block>(i->alternative())) {+ for (auto n : b->elements()) n->perform(this);+ }++ return i;+ }+ Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s) {- Block_Ptr b1 = SASS_MEMORY_CAST_PTR(Block, s);- Has_Block_Ptr b2 = SASS_MEMORY_CAST_PTR(Has_Block, s);+ Block_Ptr b1 = Cast<Block>(s);+ Has_Block_Ptr b2 = Cast<Has_Block>(s); return b1 || b2 ? visit_children(s) : s; } @@ -108,91 +143,82 @@ { if (!this->parent) return true; - if (SASS_MEMORY_CAST_PTR(Content, node))- { this->invalid_content_parent(this->parent); }+ if (Cast<Content>(node))+ { this->invalid_content_parent(this->parent, node); } if (is_charset(node))- { this->invalid_charset_parent(this->parent); }+ { this->invalid_charset_parent(this->parent, node); } - if (SASS_MEMORY_CAST_PTR(Extension, node))- { this->invalid_extend_parent(this->parent); }+ if (Cast<Extension>(node))+ { this->invalid_extend_parent(this->parent, node); } - // if (SASS_MEMORY_CAST(Import, node))+ // if (Cast<Import>(node)) // { this->invalid_import_parent(this->parent); } if (this->is_mixin(node))- { this->invalid_mixin_definition_parent(this->parent); }+ { this->invalid_mixin_definition_parent(this->parent, node); } if (this->is_function(node))- { this->invalid_function_parent(this->parent); }+ { this->invalid_function_parent(this->parent, node); } 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 (Declaration_Ptr d = Cast<Declaration>(node))+ {+ this->invalid_prop_parent(this->parent, node);+ this->invalid_value_child(d->value());+ } - if (SASS_MEMORY_CAST_PTR(Declaration, this->parent))+ if (Cast<Declaration>(this->parent)) { this->invalid_prop_child(node); } - if (SASS_MEMORY_CAST_PTR(Return, node))- { this->invalid_return_parent(this->parent); }+ if (Cast<Return>(node))+ { this->invalid_return_parent(this->parent, node); } return true; } - void CheckNesting::invalid_content_parent(Statement_Ptr parent)+ void CheckNesting::invalid_content_parent(Statement_Ptr parent, AST_Node_Ptr node) { if (!this->current_mixin_definition) {- throw Exception::InvalidSass(- parent->pstate(),- "@content may only be used within a mixin."- );+ error(node, traces, "@content may only be used within a mixin."); } } - void CheckNesting::invalid_charset_parent(Statement_Ptr parent)+ void CheckNesting::invalid_charset_parent(Statement_Ptr parent, AST_Node_Ptr node) { if (!( is_root_node(parent) )) {- throw Exception::InvalidSass(- parent->pstate(),- "@charset may only be used at the root of a document."- );+ error(node, traces, "@charset may only be used at the root of a document."); } } - void CheckNesting::invalid_extend_parent(Statement_Ptr parent)+ void CheckNesting::invalid_extend_parent(Statement_Ptr parent, AST_Node_Ptr node) { if (!(- SASS_MEMORY_CAST_PTR(Ruleset, parent) ||- SASS_MEMORY_CAST_PTR(Mixin_Call, parent) ||+ Cast<Ruleset>(parent) ||+ Cast<Mixin_Call>(parent) || is_mixin(parent) )) {- throw Exception::InvalidSass(- parent->pstate(),- "Extend directives may only be used within rules."- );+ error(node, traces, "Extend directives may only be used within rules."); } } - // void CheckNesting::invalid_import_parent(Statement_Ptr parent)+ // void CheckNesting::invalid_import_parent(Statement_Ptr parent, AST_Node_Ptr node) // { // 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) ||+ // Cast<Each>(pp) ||+ // Cast<For>(pp) ||+ // Cast<If>(pp) ||+ // Cast<While>(pp) ||+ // Cast<Trace>(pp) ||+ // Cast<Mixin_Call>(pp) || // is_mixin(pp) // ) {- // throw Exception::InvalidSass(- // parent->pstate(),- // "Import directives may not be defined within control directives or other mixins."- // );+ // error(node, traces, "Import directives may not be defined within control directives or other mixins."); // } // } @@ -201,49 +227,40 @@ // } // if (false/*n.css_import?*/) {- // throw Exception::InvalidSass(- // parent->pstate(),- // "CSS import directives may only be used at the root of a document."- // );+ // error(node, traces, "CSS import directives may only be used at the root of a document."); // } // } - void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent)+ void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent, AST_Node_Ptr node) { 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) ||+ Cast<Each>(pp) ||+ Cast<For>(pp) ||+ Cast<If>(pp) ||+ Cast<While>(pp) ||+ Cast<Trace>(pp) ||+ Cast<Mixin_Call>(pp) || is_mixin(pp) ) {- throw Exception::InvalidSass(- parent->pstate(),- "Mixins may not be defined within control directives or other mixins."- );+ error(node, traces, "Mixins may not be defined within control directives or other mixins."); } } } - void CheckNesting::invalid_function_parent(Statement_Ptr parent)+ void CheckNesting::invalid_function_parent(Statement_Ptr parent, AST_Node_Ptr node) { 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) ||+ Cast<Each>(pp) ||+ Cast<For>(pp) ||+ Cast<If>(pp) ||+ Cast<While>(pp) ||+ Cast<Trace>(pp) ||+ Cast<Mixin_Call>(pp) || is_mixin(pp) ) {- throw Exception::InvalidSass(- parent->pstate(),- "Functions may not be defined within control directives or other mixins."- );+ error(node, traces, "Functions may not be defined within control directives or other mixins."); } } }@@ -251,70 +268,75 @@ 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) ||+ Cast<Each>(child) ||+ Cast<For>(child) ||+ Cast<If>(child) ||+ Cast<While>(child) ||+ Cast<Trace>(child) ||+ Cast<Comment>(child) ||+ Cast<Debug>(child) ||+ Cast<Return>(child) ||+ Cast<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)+ Cast<Assignment>(child) ||+ Cast<Warning>(child) ||+ Cast<Error>(child) )) {- throw Exception::InvalidSass(- child->pstate(),- "Functions can only contain variable declarations and control directives."- );+ error(child, traces, "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)+ Cast<Each>(child) ||+ Cast<For>(child) ||+ Cast<If>(child) ||+ Cast<While>(child) ||+ Cast<Trace>(child) ||+ Cast<Comment>(child) ||+ Cast<Declaration>(child) ||+ Cast<Mixin_Call>(child) )) {- throw Exception::InvalidSass(- child->pstate(),- "Illegal nesting: Only properties may be nested beneath properties."- );+ error(child, traces, "Illegal nesting: Only properties may be nested beneath properties."); } } - void CheckNesting::invalid_prop_parent(Statement_Ptr parent)+ void CheckNesting::invalid_prop_parent(Statement_Ptr parent, AST_Node_Ptr node) { 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)+ Cast<Ruleset>(parent) ||+ Cast<Keyframe_Rule>(parent) ||+ Cast<Declaration>(parent) ||+ Cast<Mixin_Call>(parent) )) {- throw Exception::InvalidSass(- parent->pstate(),- "Properties are only allowed within rules, directives, mixin includes, or other properties."- );+ error(node, traces, "Properties are only allowed within rules, directives, mixin includes, or other properties."); } } - void CheckNesting::invalid_return_parent(Statement_Ptr parent)+ void CheckNesting::invalid_value_child(AST_Node_Ptr d) {+ if (Map_Ptr m = Cast<Map>(d)) {+ traces.push_back(Backtrace(m->pstate()));+ throw Exception::InvalidValue(traces, *m);+ }+ if (Number_Ptr n = Cast<Number>(d)) {+ if (!n->is_valid_css_unit()) {+ traces.push_back(Backtrace(n->pstate()));+ throw Exception::InvalidValue(traces, *n);+ }+ }++ // error(dbg + " isn't a valid CSS value.", m->pstate(),);++ }++ void CheckNesting::invalid_return_parent(Statement_Ptr parent, AST_Node_Ptr node)+ { if (!this->is_function(parent)) {- throw Exception::InvalidSass(- parent->pstate(),- "@return may only be used within a function."- );+ error(node, traces, "@return may only be used within a function."); } } @@ -326,51 +348,51 @@ !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) ||+ return Cast<Import>(parent) ||+ Cast<Each>(parent) ||+ Cast<For>(parent) ||+ Cast<If>(parent) ||+ Cast<While>(parent) ||+ Cast<Trace>(parent) || valid_bubble_node; } bool CheckNesting::is_charset(Statement_Ptr n) {- Directive_Ptr d = SASS_MEMORY_CAST_PTR(Directive, n);+ Directive_Ptr d = Cast<Directive>(n); return d && d->keyword() == "charset"; } bool CheckNesting::is_mixin(Statement_Ptr n) {- Definition_Ptr def = SASS_MEMORY_CAST_PTR(Definition, n);+ Definition_Ptr def = Cast<Definition>(n); return def && def->type() == Definition::MIXIN; } bool CheckNesting::is_function(Statement_Ptr n) {- Definition_Ptr def = SASS_MEMORY_CAST_PTR(Definition, n);+ Definition_Ptr def = Cast<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;+ if (Cast<Ruleset>(n)) return false; - Block_Ptr b = SASS_MEMORY_CAST_PTR(Block, n);+ Block_Ptr b = Cast<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;+ return Cast<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);+ return Cast<Directive>(n) ||+ Cast<Import>(n) ||+ Cast<Media_Block>(n) ||+ Cast<Supports_Block>(n); } }
libsass/src/check_nesting.hpp view
@@ -6,11 +6,10 @@ namespace Sass { - typedef Environment<AST_Node_Obj> Env;- class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> { std::vector<Statement_Ptr> parents;+ Backtraces traces; Statement_Ptr parent; Definition_Ptr current_mixin_definition; @@ -24,10 +23,11 @@ Statement_Ptr operator()(Block_Ptr); Statement_Ptr operator()(Definition_Ptr);+ Statement_Ptr operator()(If_Ptr); template <typename U> Statement_Ptr fallback(U x) {- Statement_Ptr n = SASS_MEMORY_CAST_PTR(Statement, x);+ Statement_Ptr n = Cast<Statement>(x); if (this->should_visit(n)) { return fallback_impl(n); }@@ -35,17 +35,18 @@ } private:- void invalid_content_parent(Statement_Ptr);- void invalid_charset_parent(Statement_Ptr);- void invalid_extend_parent(Statement_Ptr);+ void invalid_content_parent(Statement_Ptr, AST_Node_Ptr);+ void invalid_charset_parent(Statement_Ptr, AST_Node_Ptr);+ void invalid_extend_parent(Statement_Ptr, AST_Node_Ptr); // void invalid_import_parent(Statement_Ptr);- void invalid_mixin_definition_parent(Statement_Ptr);- void invalid_function_parent(Statement_Ptr);+ void invalid_mixin_definition_parent(Statement_Ptr, AST_Node_Ptr);+ void invalid_function_parent(Statement_Ptr, AST_Node_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);+ void invalid_prop_parent(Statement_Ptr, AST_Node_Ptr);+ void invalid_return_parent(Statement_Ptr, AST_Node_Ptr);+ void invalid_value_child(AST_Node_Ptr); bool is_transparent_parent(Statement_Ptr, Statement_Ptr);
libsass/src/color_maps.cpp view
@@ -607,16 +607,20 @@ Color_Ptr_Const name_to_color(const char* key) {- auto p = names_to_colors.find(key);- if (p != names_to_colors.end()) {- return p->second;- }- return 0;+ return name_to_color(std::string(key)); } Color_Ptr_Const name_to_color(const std::string& key) {- return name_to_color(key.c_str());+ // case insensitive lookup. See #2462+ std::string lower{key};+ std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);++ auto p = names_to_colors.find(lower.c_str());+ if (p != names_to_colors.end()) {+ return p->second;+ }+ return 0; } const char* color_to_name(const int key)
libsass/src/color_maps.hpp view
@@ -1,3 +1,4 @@+ #ifndef SASS_COLOR_MAPS_H #define SASS_COLOR_MAPS_H @@ -319,14 +320,11 @@ extern const Color transparent; } - extern const std::map<const int, const char*> colors_to_names;- extern const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors;-- 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);+ Color_Ptr_Const name_to_color(const char*);+ Color_Ptr_Const name_to_color(const std::string&);+ const char* color_to_name(const int);+ const char* color_to_name(const Color&);+ const char* color_to_name(const double); }
libsass/src/constants.cpp view
@@ -79,11 +79,12 @@ extern const char supports_kwd[] = "@supports"; extern const char keyframes_kwd[] = "keyframes"; extern const char only_kwd[] = "only";- extern const char rgb_kwd[] = "rgb(";+ extern const char rgb_fn_kwd[] = "rgb(";+ extern const char url_fn_kwd[] = "url("; extern const char url_kwd[] = "url";- // extern const char url_prefix_kwd[] = "url-prefix(";+ // extern const char url_prefix_fn_kwd[] = "url-prefix("; extern const char important_kwd[] = "important";- extern const char pseudo_not_kwd[] = ":not(";+ extern const char pseudo_not_fn_kwd[] = ":not("; extern const char even_kwd[] = "even"; extern const char odd_kwd[] = "odd"; extern const char progid_kwd[] = "progid";
libsass/src/constants.hpp view
@@ -79,11 +79,12 @@ extern const char supports_kwd[]; extern const char keyframes_kwd[]; extern const char only_kwd[];- extern const char rgb_kwd[];+ extern const char rgb_fn_kwd[];+ extern const char url_fn_kwd[]; extern const char url_kwd[];- // extern const char url_prefix_kwd[];+ // extern const char url_prefix_fn_kwd[]; extern const char important_kwd[];- extern const char pseudo_not_kwd[];+ extern const char pseudo_not_fn_kwd[]; extern const char even_kwd[]; extern const char odd_kwd[]; extern const char progid_kwd[];
libsass/src/context.cpp view
@@ -67,11 +67,15 @@ plugins(), emitter(c_options), + ast_gc(), strings(), resources(), sheets(), subset_map(), import_stack(),+ callee_stack(),+ traces(),+ c_compiler(NULL), c_headers (std::vector<Sass_Importer_Entry>()), c_importers (std::vector<Sass_Importer_Entry>()),@@ -87,8 +91,9 @@ { - // add cwd to include paths- include_paths.push_back(CWD);+ // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default.+ // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment.+ // include_paths.push_back(CWD); // collect more paths from different options collect_include_paths(c_options.include_path);@@ -129,7 +134,7 @@ Context::~Context() {- // resources were allocated by strdup or malloc+ // resources were allocated by malloc for (size_t i = 0; i < resources.size(); ++i) { free(resources[i].contents); free(resources[i].srcmap);@@ -246,10 +251,9 @@ return vec; } - // 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, ParserState* prstate)+ void Context::register_resource(const Include& inc, const Resource& res) { // do not parse same resource twice@@ -297,21 +301,22 @@ 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 cwd(File::get_cwd());+ // make path relative to the current directory 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);+ stack += "\n " + std::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) ++ " imports " + std::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd)); } // 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);+ throw Exception::InvalidSyntax(pstate, traces, 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));+ Parser p(Parser::from_c_str(contents, *this, traces, pstate)); // do not yet dispose these buffers sass_import_take_source(import); sass_import_take_srcmap(import);@@ -326,7 +331,15 @@ ast_pair(inc.abs_path, { res, root }); // register resulting resource sheets.insert(ast_pair);+ } + // 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, ParserState& prstate)+ {+ traces.push_back(Backtrace(prstate));+ register_resource(inc, res);+ traces.pop_back(); } // Add a new import to the context (called from `import_url`)@@ -346,7 +359,7 @@ for (size_t i = 0, L = resolved.size(); i < L; ++i) { msg_stream << " " << resolved[i].imp_path << "\n"; } msg_stream << "Please delete or rename all but one of these files." << "\n";- error(msg_stream.str(), pstate);+ error(msg_stream.str(), pstate, traces); } // process the resolved entry@@ -358,7 +371,7 @@ // 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 }, &pstate);+ register_resource(resolved[0], { contents, 0 }, pstate); // return resolved entry return resolved[0]; }@@ -391,15 +404,15 @@ 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);+ 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 + ".", pstate, traces); } imp->incs().push_back(include); }@@ -415,12 +428,12 @@ // need one correct import bool has_import = false; // process all custom importers (or custom headers)- for (Sass_Importer_Entry& importer : importers) {+ for (Sass_Importer_Entry& importer_ent : importers) { // int priority = sass_importer_get_priority(importer);- Sass_Importer_Fn fn = sass_importer_get_function(importer);+ Sass_Importer_Fn fn = sass_importer_get_function(importer_ent); // skip importer if it returns NULL if (Sass_Import_List includes =- fn(load_path.c_str(), importer, c_compiler)+ fn(load_path.c_str(), importer_ent, c_compiler) ) { // get c pointer copy to iterate over Sass_Import_List it_includes = includes;@@ -435,18 +448,18 @@ // create the importer struct Importer importer(uniq_path, ctx_path); // query data from the current include- Sass_Import_Entry include = *it_includes;- char* source = 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);+ Sass_Import_Entry include_ent = *it_includes;+ char* source = sass_import_take_source(include_ent);+ char* srcmap = sass_import_take_srcmap(include_ent);+ size_t line = sass_import_get_error_line(include_ent);+ size_t column = sass_import_get_error_column(include_ent);+ const char *abs_path = sass_import_get_abs_path(include_ent); // 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 }, &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)));+ if (const char* err_message = sass_import_get_error_message(include_ent)) {+ if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate);+ if (line == std::string::npos && column == std::string::npos) error(err_message, pstate, traces);+ else error(err_message, ParserState(ctx_path, source, Position(line, column)), traces); } // content for import was set else if (source) {@@ -458,7 +471,7 @@ // attach information to AST node imp->incs().push_back(include); // register the resource buffers- register_resource(include, { source, srcmap }, &pstate);+ register_resource(include, { source, srcmap }, pstate); } // only a path was retuned // try to load it like normal@@ -528,11 +541,11 @@ 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->append(&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->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));@@ -644,30 +657,33 @@ for (size_t i = 0, S = c_functions.size(); i < S; ++i) { register_c_function(*this, &global, c_functions[i]); } // create initial backtrace entry- Backtrace backtrace(0, ParserState("", 0), ""); // create crtp visitor objects- Expand expand(*this, &global, &backtrace);- Cssize cssize(*this, &backtrace);+ Expand expand(*this, &global);+ Cssize cssize(*this); CheckNesting check_nesting;- // check nesting- check_nesting(&root);+ // check nesting in all files+ for (auto sheet : sheets) {+ auto styles = sheet.second;+ check_nesting(styles.root);+ } // expand and eval the tree- root = expand(&root);+ root = expand(root); // check nesting- check_nesting(&root);+ check_nesting(root); // merge and bubble certain rules- root = cssize(&root);+ root = cssize(root); // should we extend something? if (!subset_map.empty()) { // create crtp visitor object- Extend extend(*this, subset_map);+ Extend extend(subset_map);+ extend.setEval(expand.eval); // extend tree nodes- extend(&root);+ extend(root); } // clean up by removing empty placeholders // ToDo: maybe we can do this somewhere else?- Remove_Placeholders remove_placeholders(*this);+ Remove_Placeholders remove_placeholders; root->perform(&remove_placeholders); // return processed tree return root;@@ -695,10 +711,8 @@ char* Context::render_srcmap() { if (source_map_file == "") return 0;- char* result = 0; std::string map = emitter.render_srcmap(*this);- result = sass_copy_c_string(map.c_str());- return result;+ return sass_copy_c_string(map.c_str()); } @@ -810,6 +824,7 @@ register_function(ctx, append_sig, append, env); register_function(ctx, zip_sig, zip, env); register_function(ctx, list_separator_sig, list_separator, env);+ register_function(ctx, is_bracketed_sig, is_bracketed, env); // Map Functions register_function(ctx, map_get_sig, map_get, env); register_function(ctx, map_merge_sig, map_merge, env);@@ -829,6 +844,8 @@ register_function(ctx, mixin_exists_sig, mixin_exists, env); register_function(ctx, feature_exists_sig, feature_exists, env); register_function(ctx, call_sig, call, env);+ register_function(ctx, content_exists_sig, content_exists, env);+ register_function(ctx, get_function_sig, get_function, env); // Boolean Functions register_function(ctx, not_sig, sass_not, env); register_function(ctx, if_sig, sass_if, env);
libsass/src/context.hpp view
@@ -15,6 +15,7 @@ #include "environment.hpp" #include "source_map.hpp" #include "subset_map.hpp"+#include "backtrace.hpp" #include "output.hpp" #include "plugins.hpp" #include "file.hpp"@@ -43,6 +44,9 @@ Plugins plugins; Output emitter; + // generic ast node garbage container+ // used to avoid possible circular refs+ std::vector<AST_Node_Obj> ast_gc; // resources add under our control // these are guaranteed to be freed std::vector<char*> strings;@@ -50,6 +54,8 @@ std::map<const std::string, StyleSheet> sheets; Subset_Map subset_map; std::vector<Sass_Import_Entry> import_stack;+ std::vector<Sass_Callee> callee_stack;+ std::vector<Backtrace> traces; struct Sass_Compiler* c_compiler; @@ -90,7 +96,8 @@ virtual char* render(Block_Obj root); virtual char* render_srcmap(); - void register_resource(const Include&, const Resource&, ParserState* = 0);+ void register_resource(const Include&, const Resource&);+ void register_resource(const Include&, const Resource&, ParserState&); std::vector<Include> find_includes(const Importer& import); Include load_import(const Importer&, ParserState pstate);
libsass/src/cssize.cpp view
@@ -5,15 +5,14 @@ #include "cssize.hpp" #include "context.hpp"-#include "backtrace.hpp" namespace Sass { - Cssize::Cssize(Context& ctx, Backtrace* bt)+ Cssize::Cssize(Context& ctx) : ctx(ctx),+ traces(ctx.traces), block_stack(std::vector<Block_Ptr>()),- p_stack(std::vector<Statement_Ptr>()),- backtrace(bt)+ p_stack(std::vector<Statement_Ptr>()) { } Statement_Ptr Cssize::parent()@@ -23,25 +22,28 @@ Block_Ptr Cssize::operator()(Block_Ptr b) {- Block_Ptr bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root());+ Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); // bb->tabs(b->tabs()); block_stack.push_back(bb); append_block(b, bb); block_stack.pop_back();- return bb;+ return bb.detach(); } Statement_Ptr Cssize::operator()(Trace_Ptr t) {- return t->block()->perform(this);+ traces.push_back(Backtrace(t->pstate()));+ auto result = t->block()->perform(this);+ traces.pop_back();+ return result; } Statement_Ptr Cssize::operator()(Declaration_Ptr d) {- String_Obj property = SASS_MEMORY_CAST(String, d->property());+ String_Obj property = Cast<String>(d->property()); - if (Declaration_Ptr dd = dynamic_cast<Declaration_Ptr>(parent())) {- String_Obj parent_property = SASS_MEMORY_CAST(String, dd->property());+ if (Declaration_Ptr dd = Cast<Declaration>(parent())) {+ String_Obj parent_property = Cast<String>(dd->property()); property = SASS_MEMORY_NEW(String_Constant, d->property()->pstate(), parent_property->to_string() + "-" + property->to_string());@@ -54,17 +56,18 @@ d->pstate(), property, d->value(),- d->is_important());+ d->is_important(),+ d->is_custom_property()); 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.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);+ bb->unshift(dd); } return bb.detach(); }@@ -89,7 +92,7 @@ r->pstate(), r->keyword(), r->selector(),- r->block() ? operator()(&r->block()) : 0);+ r->block() ? operator()(r->block()) : 0); if (r->value()) rr->value(r->value()); p_stack.pop_back(); @@ -99,10 +102,10 @@ Statement_Obj s = r->block()->at(i); if (s->statement_type() != Statement::BUBBLE) directive_exists = true; else {- Bubble_Obj s_obj = SASS_MEMORY_CAST(Bubble, s);+ Bubble_Obj s_obj = Cast<Bubble>(s); s = s_obj->node(); if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false;- else directive_exists = (static_cast<Directive_Ptr>(&s)->keyword() == rr->keyword());+ else directive_exists = (Cast<Directive>(s)->keyword() == rr->keyword()); } }@@ -110,12 +113,14 @@ Block_Ptr result = SASS_MEMORY_NEW(Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) {- Directive_Ptr empty_node = SASS_MEMORY_CAST(Directive, rr);+ Directive_Ptr empty_node = Cast<Directive>(rr); empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate())); result->append(empty_node); } - Block_Obj ss = debubble(rr->block() ? &rr->block() : SASS_MEMORY_NEW(Block, rr->pstate()), &rr);+ Block_Obj db = rr->block();+ if (db.isNull()) db = SASS_MEMORY_NEW(Block, rr->pstate());+ Block_Obj ss = debubble(db, rr); for (size_t i = 0, L = ss->length(); i < L; ++i) { result->append(ss->at(i)); }@@ -129,10 +134,10 @@ Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(),- operator()(&r->block()));- if (&r->name()) rr->name(r->name());+ operator()(r->block()));+ if (!r->name().isNull()) rr->name(r->name()); - return debubble(&rr->block(), &rr);+ return debubble(rr->block(), rr); } Statement_Ptr Cssize::operator()(Ruleset_Ptr r)@@ -142,11 +147,11 @@ // 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());+ 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());+ if (Cast<Statement>(bb) == NULL) {+ error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces); } Ruleset_Obj rr = SASS_MEMORY_NEW(Ruleset, r->pstate(),@@ -158,31 +163,31 @@ p_stack.pop_back(); if (!rr->block()) {- error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate());+ error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces); } 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_Ptr s = &rr->block()->at(i);+ Statement_Ptr s = rr->block()->at(i); if (bubblable(s)) rules->append(s); if (!bubblable(s)) props->append(s); } if (props->length()) {- Block_Obj bb = SASS_MEMORY_NEW(Block, rr->block()->pstate());- bb->concat(&props);- rr->block(bb);+ Block_Obj pb = SASS_MEMORY_NEW(Block, rr->block()->pstate());+ pb->concat(props);+ rr->block(pb); for (size_t i = 0, L = rules->length(); i < L; i++) {- Statement_Ptr stm = &rules->at(i);+ Statement_Ptr stm = rules->at(i); stm->tabs(stm->tabs() + 1); } - rules->unshift(&rr);+ rules->unshift(rr); } Block_Ptr ptr = rules;@@ -194,7 +199,7 @@ } if (!(!rules->length() ||- !bubblable(&rules->last()) ||+ !bubblable(rules->last()) || parent()->statement_type() == Statement::RULESET)) { rules->last()->group_end(true);@@ -219,13 +224,13 @@ Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block, m->pstate(),- &m->media_queries(),- operator()(&m->block()));+ m->media_queries(),+ operator()(m->block())); mm->tabs(m->tabs()); p_stack.pop_back(); - return debubble(&mm->block(), &mm);+ return debubble(mm->block(), mm); } Statement_Ptr Cssize::operator()(Supports_Block_Ptr m)@@ -240,13 +245,13 @@ Supports_Block_Obj mm = SASS_MEMORY_NEW(Supports_Block, m->pstate(),- &m->condition(),- operator()(&m->block()));+ m->condition(),+ operator()(m->block())); mm->tabs(m->tabs()); p_stack.pop_back(); - return debubble(&mm->block(), &mm);+ return debubble(mm->block(), mm); } Statement_Ptr Cssize::operator()(At_Root_Block_Ptr m)@@ -257,15 +262,15 @@ tmp |= m->exclude_node(s); } - if (!tmp)+ if (!tmp && m->block()) {- Block_Ptr bb = operator()(&m->block());+ Block_Ptr bb = operator()(m->block()); for (size_t i = 0, L = bb->length(); i < L; ++i) { // (bb->elements())[i]->tabs(m->tabs()); Statement_Obj stm = bb->at(i);- if (bubblable(&stm)) stm->tabs(stm->tabs() + m->tabs());+ 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; } @@ -280,13 +285,13 @@ Statement_Ptr Cssize::bubble(Directive_Ptr m) { 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()));+ Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs());- new_rule->block()->concat(&m->block());+ new_rule->block()->concat(m->block()); Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate());- wrapper_block->append(&new_rule);+ wrapper_block->append(new_rule); Directive_Obj mm = SASS_MEMORY_NEW(Directive, m->pstate(), m->keyword(),@@ -294,20 +299,23 @@ wrapper_block); if (m->value()) mm->value(m->value()); - Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), &mm);+ Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } Statement_Ptr Cssize::bubble(At_Root_Block_Ptr m) {+ if (!m || !m->block()) return NULL; 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());- new_rule->block()->concat(&m->block());-+ Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent())); Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());- wrapper_block->append(&new_rule);+ if (new_rule) {+ new_rule->block(bb);+ new_rule->tabs(this->parent()->tabs());+ new_rule->block()->concat(m->block());+ wrapper_block->append(new_rule);+ }+ At_Root_Block_Ptr mm = SASS_MEMORY_NEW(At_Root_Block, m->pstate(), wrapper_block,@@ -318,7 +326,7 @@ Statement_Ptr Cssize::bubble(Supports_Block_Ptr m) {- Ruleset_Obj parent = static_cast<Ruleset_Ptr>(SASS_MEMORY_COPY(this->parent()));+ Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,@@ -326,13 +334,13 @@ parent->selector(), bb); new_rule->tabs(parent->tabs());- new_rule->block()->concat(&m->block());+ new_rule->block()->concat(m->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());@@ -343,7 +351,7 @@ Statement_Ptr Cssize::bubble(Media_Block_Ptr m) {- Ruleset_Obj parent = static_cast<Ruleset_Ptr>(SASS_MEMORY_COPY(this->parent()));+ Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,@@ -351,32 +359,31 @@ parent->selector(), bb); new_rule->tabs(parent->tabs());- new_rule->block()->concat(&m->block());+ new_rule->block()->concat(m->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(),- wrapper_block,- 0);+ m->media_queries(),+ wrapper_block); mm->tabs(m->tabs()); - return SASS_MEMORY_NEW(Bubble, mm->pstate(), &mm);+ return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); } bool Cssize::bubblable(Statement_Ptr s) {- return dynamic_cast<Ruleset_Ptr>(s) || s->bubbles();+ return Cast<Ruleset>(s) || s->bubbles(); } Block_Ptr Cssize::flatten(Block_Ptr b) { 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)) {+ Statement_Ptr ss = b->at(i);+ if (Block_Ptr bb = Cast<Block>(ss)) { Block_Obj bs = flatten(bb); for (size_t j = 0, K = bs->length(); j < K; ++j) { result->append(bs->at(j));@@ -395,7 +402,7 @@ 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;+ bool key = Cast<Bubble>(value) != NULL; if (!results.empty() && results.back().first == key) {@@ -424,45 +431,50 @@ if (!is_bubble) { if (!parent) {- result->append(&slice);+ result->append(slice); } else if (previous_parent) {- previous_parent->block()->concat(&slice);+ previous_parent->block()->concat(slice); } else {- previous_parent = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(parent));+ previous_parent = Cast<Has_Block>(SASS_MEMORY_COPY(parent)); previous_parent->block(slice); previous_parent->tabs(parent->tabs()); - result->append(&previous_parent);+ result->append(previous_parent); } continue; } for (size_t j = 0, K = slice->length(); j < K; ++j) {- Statement_Ptr ss = NULL;+ Statement_Ptr ss; Statement_Obj stm = slice->at(j); // this has to go now here (too bad)- Bubble_Obj node = SASS_MEMORY_CAST(Bubble, stm);+ Bubble_Obj node = 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) m1 = Cast<Media_Block>(parent);+ if (node) m2 = Cast<Media_Block>(node->node()); if (!parent || parent->statement_type() != Statement::MEDIA || node->node()->statement_type() != Statement::MEDIA ||- (m1 && m2 && &m1->media_queries() == &m2->media_queries())+ (m1 && m2 && *m1->media_queries() == *m2->media_queries()) ) {- ss = &node->node();+ ss = node->node(); } else {- List_Obj mq = merge_media_queries(static_cast<Media_Block_Ptr>(&node->node()), static_cast<Media_Block_Ptr>(parent));+ List_Obj mq = merge_media_queries(+ Cast<Media_Block>(node->node()),+ Cast<Media_Block>(parent)+ ); if (!mq->length()) continue;- static_cast<Media_Block_Ptr>(&node->node())->media_queries(mq);- ss = &node->node();+ if (Media_Block* b = Cast<Media_Block>(node->node())) {+ b->media_queries(mq);+ }+ ss = node->node(); } if (!ss) continue;@@ -470,8 +482,6 @@ ss->tabs(ss->tabs() + node->tabs()); ss->group_end(node->group_end()); - if (!ss) continue;- Block_Obj bb = SASS_MEMORY_NEW(Block, children->pstate(), children->length(),@@ -483,7 +493,7 @@ children->length(), children->is_root()); - Block_Ptr wrapper = flatten(&bb);+ Block_Ptr wrapper = flatten(bb); wrapper_block->append(wrapper); if (wrapper->length()) {@@ -491,12 +501,12 @@ } if (wrapper_block) {- result->append(&wrapper_block);+ result->append(wrapper_block); } } } - return flatten(&result);+ return flatten(result); } Statement_Ptr Cssize::fallback_impl(AST_Node_Ptr n)@@ -508,7 +518,7 @@ { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj ith = b->at(i)->perform(this);- if (Block_Ptr bb = SASS_MEMORY_CAST(Block, ith)) {+ if (Block_Ptr bb = Cast<Block>(ith)) { for (size_t j = 0, K = bb->length(); j < K; ++j) { cur->append(bb->at(j)); }@@ -530,8 +540,8 @@ for (size_t j = 0, K = m2->media_queries()->length(); j < K; j++) { Expression_Obj l1 = m1->media_queries()->at(i); Expression_Obj l2 = m2->media_queries()->at(j);- Media_Query_Ptr mq1 = SASS_MEMORY_CAST(Media_Query, l1);- Media_Query_Ptr mq2 = SASS_MEMORY_CAST(Media_Query, l2);+ Media_Query_Ptr mq1 = Cast<Media_Query>(l1);+ Media_Query_Ptr mq2 = Cast<Media_Query>(l2); Media_Query_Ptr mq = merge_media_query(mq1, mq2); if (mq) qq->append(mq); }@@ -548,9 +558,9 @@ std::string mod; std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");- std::string t1 = &mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : "";+ std::string 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()->to_string(ctx.c_options) : "";+ std::string t2 = mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : ""; if (t1.empty()) t1 = t2;@@ -578,10 +588,11 @@ } Media_Query_Ptr mm = SASS_MEMORY_NEW(Media_Query,--mq1->pstate(), 0,-mq1->length() + mq2->length(), mod == "not", mod == "only"-);+ mq1->pstate(),+ 0,+ mq1->length() + mq2->length(),+ mod == "not",+ mod == "only"); if (!type.empty()) { mm->media_type(SASS_MEMORY_NEW(String_Quoted, mq1->pstate(), type));
libsass/src/cssize.hpp view
@@ -8,20 +8,19 @@ namespace Sass { - typedef Environment<AST_Node_Obj> Env; struct Backtrace; class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> { Context& ctx;- std::vector<Block_Ptr> block_stack;- std::vector<Statement_Ptr> p_stack;- Backtrace* backtrace;+ Backtraces& traces;+ std::vector<Block_Ptr> block_stack;+ std::vector<Statement_Ptr> p_stack; Statement_Ptr fallback_impl(AST_Node_Ptr n); public:- Cssize(Context&, Backtrace*);+ Cssize(Context&); ~Cssize() { } Selector_List_Ptr selector();
libsass/src/debugger.hpp view
@@ -10,11 +10,15 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind = "", Env* env = 0); -inline void debug_sources_set(SourcesSet& set, std::string ind = "")+inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {+ debug_ast(const_cast<AST_Node*>(node), ind, env);+}++inline void debug_sources_set(ComplexSelectorSet& set, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &pair : set) {- debug_ast(&pair, ind + "");+ debug_ast(pair, ind + ""); // debug_ast(set[pair], ind + "first: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";@@ -64,30 +68,30 @@ { if (node == 0) return; if (ind == "") std::cerr << "####################################################################\n";- if (dynamic_cast<Bubble_Ptr>(node)) {- Bubble_Ptr bubble = dynamic_cast<Bubble_Ptr>(node);+ if (Cast<Bubble>(node)) {+ Bubble_Ptr bubble = Cast<Bubble>(node); std::cerr << ind << "Bubble " << bubble; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl;- debug_ast(&bubble->node(), ind + " ", env);- } else if (dynamic_cast<Trace_Ptr>(node)) {- Trace_Ptr trace = dynamic_cast<Trace_Ptr>(node);+ debug_ast(bubble->node(), ind + " ", env);+ } else if (Cast<Trace>(node)) {+ Trace_Ptr trace = Cast<Trace>(node); std::cerr << ind << "Trace " << trace; std::cerr << " (" << pstate_source_position(node) << ")"- << " [name:" << trace->name() << "]"+ << " [name:" << trace->name() << ", type: " << trace->type() << "]" << 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);+ debug_ast(trace->block(), ind + " ", env);+ } else if (Cast<At_Root_Block>(node)) {+ At_Root_Block_Ptr root_block = Cast<At_Root_Block>(node); std::cerr << ind << "At_Root_Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << root_block->tabs(); std::cerr << std::endl;- debug_ast(&root_block->expression(), ind + ":", env);- debug_ast(&root_block->block(), ind + " ", env);- } else if (dynamic_cast<Selector_List_Ptr>(node)) {- Selector_List_Ptr selector = dynamic_cast<Selector_List_Ptr>(node);+ debug_ast(root_block->expression(), ind + ":", env);+ debug_ast(root_block->block(), ind + " ", env);+ } else if (Cast<Selector_List>(node)) {+ Selector_List_Ptr selector = Cast<Selector_List>(node); std::cerr << ind << "Selector_List " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -99,15 +103,16 @@ std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl;+ debug_ast(selector->schema(), ind + "#{} "); - for(const Complex_Selector_Obj& 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_Ptr>(node)) {-// Expression_Ptr expression = dynamic_cast<Expression_Ptr>(node);+// } else if (Cast<Expression>(node)) {+// Expression_Ptr expression = Cast<Expression>(node); // std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl; - } else if (dynamic_cast<Parent_Selector_Ptr>(node)) {- Parent_Selector_Ptr selector = dynamic_cast<Parent_Selector_Ptr>(node);+ } else if (Cast<Parent_Selector>(node)) {+ Parent_Selector_Ptr selector = Cast<Parent_Selector>(node); std::cerr << ind << "Parent_Selector " << selector; // if (selector->not_selector()) cerr << " [in_declaration]"; std::cerr << " (" << pstate_source_position(node) << ")";@@ -116,8 +121,8 @@ std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; // debug_ast(selector->selector(), ind + "->", env); - } else if (dynamic_cast<Complex_Selector_Ptr>(node)) {- Complex_Selector_Ptr selector = dynamic_cast<Complex_Selector_Ptr>(node);+ } else if (Cast<Complex_Selector>(node)) {+ Complex_Selector_Ptr selector = Cast<Complex_Selector>(node); std::cerr << ind << "Complex_Selector " << selector << " (" << pstate_source_position(node) << ")" << " <" << selector->hash() << ">"@@ -141,16 +146,16 @@ } // 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();+ ComplexSelectorSet set = selector->sources(); // debug_sources_set(set, ind + " @--> ");- } else if (dynamic_cast<Compound_Selector_Ptr>(node)) {- Compound_Selector_Ptr selector = dynamic_cast<Compound_Selector_Ptr>(node);+ } else if (Cast<Compound_Selector>(node)) {+ Compound_Selector_Ptr selector = Cast<Compound_Selector>(node); std::cerr << ind << "Compound_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -162,9 +167,9 @@ std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;- for(const Simple_Selector_Obj& i : selector->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<Wrapped_Selector_Ptr>(node)) {- Wrapped_Selector_Ptr selector = dynamic_cast<Wrapped_Selector_Ptr>(node);+ for(const Simple_Selector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Wrapped_Selector>(node)) {+ Wrapped_Selector_Ptr selector = Cast<Wrapped_Selector>(node); std::cerr << ind << "Wrapped_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -174,9 +179,9 @@ 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);+ debug_ast(selector->selector(), ind + " () ", env);+ } else if (Cast<Pseudo_Selector>(node)) {+ Pseudo_Selector_Ptr selector = Cast<Pseudo_Selector>(node); std::cerr << ind << "Pseudo_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -186,9 +191,9 @@ 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);+ debug_ast(selector->expression(), ind + " <= ", env);+ } else if (Cast<Attribute_Selector>(node)) {+ Attribute_Selector_Ptr selector = Cast<Attribute_Selector>(node); std::cerr << ind << "Attribute_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -198,9 +203,9 @@ 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);+ debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);+ } else if (Cast<Class_Selector>(node)) {+ Class_Selector_Ptr selector = Cast<Class_Selector>(node); std::cerr << ind << "Class_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -210,8 +215,8 @@ 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);+ } else if (Cast<Id_Selector>(node)) {+ Id_Selector_Ptr selector = Cast<Id_Selector>(node); std::cerr << ind << "Id_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -221,8 +226,8 @@ 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);+ } else if (Cast<Element_Selector>(node)) {+ Element_Selector_Ptr selector = Cast<Element_Selector>(node); std::cerr << ind << "Element_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";@@ -233,9 +238,9 @@ 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)) {+ } else if (Cast<Placeholder_Selector>(node)) { - Placeholder_Selector_Ptr selector = dynamic_cast<Placeholder_Selector_Ptr>(node);+ Placeholder_Selector_Ptr selector = Cast<Placeholder_Selector>(node); std::cerr << ind << "Placeholder_Selector [" << selector->ns_name() << "] " << selector; std::cerr << " (" << pstate_source_position(selector) << ")" << " <" << selector->hash() << ">"@@ -245,212 +250,211 @@ << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; - } else if (dynamic_cast<Simple_Selector*>(node)) {- Simple_Selector* selector = dynamic_cast<Simple_Selector*>(node);+ } else if (Cast<Simple_Selector>(node)) {+ Simple_Selector* selector = Cast<Simple_Selector>(node); std::cerr << ind << "Simple_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<Selector_Schema_Ptr>(node)) {- Selector_Schema_Ptr selector = dynamic_cast<Selector_Schema_Ptr>(node);+ } else if (Cast<Selector_Schema>(node)) {+ Selector_Schema_Ptr selector = Cast<Selector_Schema>(node); std::cerr << ind << "Selector_Schema " << selector; std::cerr << " (" << pstate_source_position(node) << ")"- << (selector->at_root() && selector->at_root() ? " [@ROOT]" : "") << " [@media:" << selector->media_block() << "]"- << (selector->has_line_break() ? " [line-break]": " -")- << (selector->has_line_feed() ? " [line-feed]": " -")+ << (selector->connect_parent() ? " [connect-parent]": " -") << 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_Ptr>(node)) {- Selector_Ptr selector = dynamic_cast<Selector_Ptr>(node);+ } else if (Cast<Selector>(node)) {+ Selector_Ptr selector = Cast<Selector>(node); std::cerr << ind << "Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; - } else if (dynamic_cast<Media_Query_Expression_Ptr>(node)) {- Media_Query_Expression_Ptr block = dynamic_cast<Media_Query_Expression_Ptr>(node);+ } else if (Cast<Media_Query_Expression>(node)) {+ Media_Query_Expression_Ptr block = Cast<Media_Query_Expression>(node); std::cerr << ind << "Media_Query_Expression " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -") << 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_Ptr>(node)) {- Media_Query_Ptr block = dynamic_cast<Media_Query_Ptr>(node);+ } else if (Cast<Media_Query>(node)) {+ Media_Query_Ptr block = Cast<Media_Query>(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(const 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_Ptr>(node)) {- Media_Block_Ptr block = dynamic_cast<Media_Block_Ptr>(node);+ } else if (Cast<Media_Block>(node)) {+ Media_Block_Ptr block = Cast<Media_Block>(node); std::cerr << ind << "Media_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(&block->media_queries(), ind + " =@ ");- if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<Supports_Block_Ptr>(node)) {- Supports_Block_Ptr block = dynamic_cast<Supports_Block_Ptr>(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 (Cast<Supports_Block>(node)) {+ Supports_Block_Ptr block = Cast<Supports_Block>(node); std::cerr << ind << "Supports_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(&block->condition(), ind + " =@ ");- debug_ast(&block->block(), ind + " <>");- } else if (dynamic_cast<Supports_Operator_Ptr>(node)) {- Supports_Operator_Ptr block = dynamic_cast<Supports_Operator_Ptr>(node);+ debug_ast(block->condition(), ind + " =@ ");+ debug_ast(block->block(), ind + " <>");+ } else if (Cast<Supports_Operator>(node)) {+ Supports_Operator_Ptr block = Cast<Supports_Operator>(node); std::cerr << ind << "Supports_Operator " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl;- debug_ast(&block->left(), ind + " left) ");- debug_ast(&block->right(), ind + " right) ");- } else if (dynamic_cast<Supports_Negation_Ptr>(node)) {- Supports_Negation_Ptr block = dynamic_cast<Supports_Negation_Ptr>(node);+ debug_ast(block->left(), ind + " left) ");+ debug_ast(block->right(), ind + " right) ");+ } else if (Cast<Supports_Negation>(node)) {+ Supports_Negation_Ptr block = Cast<Supports_Negation>(node); std::cerr << ind << "Supports_Negation " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl;- 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);+ debug_ast(block->condition(), ind + " condition) ");+ } else if (Cast<At_Root_Query>(node)) {+ At_Root_Query_Ptr block = Cast<At_Root_Query>(node); std::cerr << ind << "At_Root_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl;- debug_ast(&block->feature(), ind + " feature) ");- debug_ast(&block->value(), ind + " value) ");- } else if (dynamic_cast<Supports_Declaration_Ptr>(node)) {- Supports_Declaration_Ptr block = dynamic_cast<Supports_Declaration_Ptr>(node);+ debug_ast(block->feature(), ind + " feature) ");+ debug_ast(block->value(), ind + " value) ");+ } else if (Cast<Supports_Declaration>(node)) {+ Supports_Declaration_Ptr block = Cast<Supports_Declaration>(node); 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_Ptr>(node)) {- Block_Ptr root_block = dynamic_cast<Block_Ptr>(node);+ debug_ast(block->feature(), ind + " feature) ");+ debug_ast(block->value(), ind + " value) ");+ } else if (Cast<Block>(node)) {+ Block_Ptr root_block = Cast<Block>(node); std::cerr << ind << "Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; if (root_block->is_root()) std::cerr << " [root]"; std::cerr << " " << root_block->tabs() << std::endl;- 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);+ for(const Statement_Obj& i : root_block->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Warning>(node)) {+ Warning_Ptr block = Cast<Warning>(node); std::cerr << ind << "Warning " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(&block->message(), ind + " : ");- } else if (dynamic_cast<Error_Ptr>(node)) {- Error_Ptr block = dynamic_cast<Error_Ptr>(node);+ debug_ast(block->message(), ind + " : ");+ } else if (Cast<Error>(node)) {+ Error_Ptr block = Cast<Error>(node); std::cerr << ind << "Error " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Debug_Ptr>(node)) {- Debug_Ptr block = dynamic_cast<Debug_Ptr>(node);+ } else if (Cast<Debug>(node)) {+ Debug_Ptr block = Cast<Debug>(node); std::cerr << ind << "Debug " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(&block->value(), ind + " ");- } else if (dynamic_cast<Comment_Ptr>(node)) {- Comment_Ptr block = dynamic_cast<Comment_Ptr>(node);+ debug_ast(block->value(), ind + " ");+ } else if (Cast<Comment>(node)) {+ Comment_Ptr block = Cast<Comment>(node); std::cerr << ind << "Comment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl;- debug_ast(&block->text(), ind + "// ", env);- } else if (dynamic_cast<If_Ptr>(node)) {- If_Ptr block = dynamic_cast<If_Ptr>(node);+ debug_ast(block->text(), ind + "// ", env);+ } else if (Cast<If>(node)) {+ If_Ptr block = Cast<If>(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_Ptr>(node)) {- Return_Ptr block = dynamic_cast<Return_Ptr>(node);+ debug_ast(block->predicate(), ind + " = ");+ debug_ast(block->block(), ind + " <>");+ debug_ast(block->alternative(), ind + " ><");+ } else if (Cast<Return>(node)) {+ Return_Ptr block = Cast<Return>(node); std::cerr << ind << "Return " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Extension_Ptr>(node)) {- Extension_Ptr block = dynamic_cast<Extension_Ptr>(node);+ } else if (Cast<Extension>(node)) {+ Extension_Ptr block = Cast<Extension>(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_Ptr>(node)) {- Content_Ptr block = dynamic_cast<Content_Ptr>(node);+ debug_ast(block->selector(), ind + "-> ", env);+ } else if (Cast<Content>(node)) {+ Content_Ptr block = Cast<Content>(node); std::cerr << ind << "Content " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [@media:" << block->media_block() << "]"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Import_Stub_Ptr>(node)) {- Import_Stub_Ptr block = dynamic_cast<Import_Stub_Ptr>(node);+ } else if (Cast<Import_Stub>(node)) {+ Import_Stub_Ptr block = Cast<Import_Stub>(node); std::cerr << ind << "Import_Stub " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->imp_path() << "] "; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Import_Ptr>(node)) {- Import_Ptr block = dynamic_cast<Import_Ptr>(node);+ } else if (Cast<Import>(node)) {+ Import_Ptr block = Cast<Import>(node); std::cerr << ind << "Import " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; // std::vector<std::string> files_;- 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);+ for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env);+ debug_ast(block->import_queries(), ind + "@@ ");+ } else if (Cast<Assignment>(node)) {+ Assignment_Ptr block = Cast<Assignment>(node); 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_Ptr>(node)) {- Declaration_Ptr block = dynamic_cast<Declaration_Ptr>(node);+ debug_ast(block->value(), ind + "=", env);+ } else if (Cast<Declaration>(node)) {+ Declaration_Ptr block = Cast<Declaration>(node); std::cerr << ind << "Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [is_custom_property: " << block->is_custom_property() << "] "; std::cerr << " " << block->tabs() << std::endl;- 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);+ debug_ast(block->property(), ind + " prop: ", env);+ debug_ast(block->value(), ind + " value: ", env);+ debug_ast(block->block(), ind + " ", env);+ } else if (Cast<Keyframe_Rule>(node)) {+ Keyframe_Rule_Ptr has_block = Cast<Keyframe_Rule>(node); std::cerr << ind << "Keyframe_Rule " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl;- if (has_block->name()) debug_ast(&has_block->name(), ind + "@");- if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<Directive_Ptr>(node)) {- Directive_Ptr block = dynamic_cast<Directive_Ptr>(node);+ if (has_block->name()) debug_ast(has_block->name(), ind + "@");+ if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Directive>(node)) {+ Directive_Ptr block = Cast<Directive>(node); 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(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);+ 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 (Cast<Each>(node)) {+ Each_Ptr block = Cast<Each>(node); std::cerr << ind << "Each " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<For_Ptr>(node)) {- For_Ptr block = dynamic_cast<For_Ptr>(node);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<For>(node)) {+ For_Ptr block = Cast<For>(node); std::cerr << ind << "For " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<While_Ptr>(node)) {- While_Ptr block = dynamic_cast<While_Ptr>(node);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<While>(node)) {+ While_Ptr block = Cast<While>(node); std::cerr << ind << "While " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<Definition_Ptr>(node)) {- Definition_Ptr block = dynamic_cast<Definition_Ptr>(node);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Definition>(node)) {+ Definition_Ptr block = Cast<Definition>(node); std::cerr << ind << "Definition " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << block->name() << "] ";@@ -459,73 +463,69 @@ // 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()) debug_ast(&block->block(), ind + " ", env);- } else if (dynamic_cast<Mixin_Call_Ptr>(node)) {- Mixin_Call_Ptr block = dynamic_cast<Mixin_Call_Ptr>(node);+ debug_ast(block->parameters(), ind + " params: ", env);+ if (block->block()) debug_ast(block->block(), ind + " ", env);+ } else if (Cast<Mixin_Call>(node)) {+ Mixin_Call_Ptr block = Cast<Mixin_Call>(node); 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()) debug_ast(&block->block(), ind + " ", env);- } else if (Ruleset_Ptr ruleset = dynamic_cast<Ruleset_Ptr>(node)) {+ debug_ast(block->arguments(), ind + " args: ");+ if (block->block()) debug_ast(block->block(), ind + " ", env);+ } else if (Ruleset_Ptr ruleset = Cast<Ruleset>(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_Ptr>(node)) {- Block_Ptr block = dynamic_cast<Block_Ptr>(node);+ debug_ast(ruleset->selector(), ind + ">");+ debug_ast(ruleset->block(), ind + " ");+ } else if (Cast<Block>(node)) {+ Block_Ptr block = Cast<Block>(node); std::cerr << ind << "Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_invisible() ? " [INVISIBLE]" : ""); std::cerr << " [indent: " << block->tabs() << "]" << std::endl;- for(const Statement_Obj& i : block->elements()) { debug_ast(&i, ind + " ", env); }- } else if (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->value() << "]";- std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";- if (expression->is_delayed()) std::cerr << " [delayed]";- std::cerr << std::endl;- } else if (dynamic_cast<Variable_Ptr>(node)) {- Variable_Ptr expression = dynamic_cast<Variable_Ptr>(node);+ for(const Statement_Obj& i : block->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Variable>(node)) {+ Variable_Ptr expression = Cast<Variable>(node); std::cerr << ind << "Variable " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]" << std::endl; std::string name(expression->name());- if (env && env->has(name)) debug_ast(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);+ if (env && env->has(name)) debug_ast(Cast<Expression>((*env)[name]), ind + " -> ", env);+ } else if (Cast<Function_Call_Schema>(node)) {+ Function_Call_Schema_Ptr expression = Cast<Function_Call_Schema>(node); std::cerr << ind << "Function_Call_Schema " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << "" << std::endl;- debug_ast(&expression->name(), ind + "name: ", env);- debug_ast(&expression->arguments(), ind + " args: ", env);- } else if (dynamic_cast<Function_Call_Ptr>(node)) {- Function_Call_Ptr expression = dynamic_cast<Function_Call_Ptr>(node);+ debug_ast(expression->name(), ind + "name: ", env);+ debug_ast(expression->arguments(), ind + " args: ", env);+ } else if (Cast<Function_Call>(node)) {+ Function_Call_Ptr expression = Cast<Function_Call>(node); std::cerr << ind << "Function_Call " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]";+ if (expression->is_css()) std::cerr << " [css]"; 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);+ debug_ast(expression->arguments(), ind + " args: ", env);+ debug_ast(expression->func(), ind + " func: ", env);+ } else if (Cast<Function>(node)) {+ Function_Ptr expression = Cast<Function>(node);+ std::cerr << ind << "Function " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ if (expression->is_css()) std::cerr << " [css]";+ std::cerr << std::endl;+ debug_ast(expression->definition(), ind + " definition: ", env);+ } else if (Cast<Arguments>(node)) {+ Arguments_Ptr expression = Cast<Arguments>(node); std::cerr << ind << "Arguments " << expression; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << " (" << pstate_source_position(node) << ")";@@ -533,41 +533,41 @@ 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(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);+ for(const Argument_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Argument>(node)) {+ Argument_Ptr expression = Cast<Argument>(node); std::cerr << ind << "Argument " << expression; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " [" << expression->value() << "]";+ std::cerr << " [" << expression->value().ptr() << "]"; 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_Ptr>(node)) {- Parameters_Ptr expression = dynamic_cast<Parameters_Ptr>(node);+ debug_ast(expression->value(), ind + " value: ", env);+ } else if (Cast<Parameters>(node)) {+ Parameters_Ptr expression = Cast<Parameters>(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(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);+ for(const Parameter_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Parameter>(node)) {+ Parameter_Ptr expression = Cast<Parameter>(node); std::cerr << ind << "Parameter " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->name() << "] ";- std::cerr << " [default: " << expression->default_value() << "] ";+ std::cerr << " [default: " << expression->default_value().ptr() << "] "; std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl;- } else if (dynamic_cast<Unary_Expression_Ptr>(node)) {- Unary_Expression_Ptr expression = dynamic_cast<Unary_Expression_Ptr>(node);+ } else if (Cast<Unary_Expression>(node)) {+ Unary_Expression_Ptr expression = Cast<Unary_Expression>(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_Ptr>(node)) {- Binary_Expression_Ptr expression = dynamic_cast<Binary_Expression_Ptr>(node);+ debug_ast(expression->operand(), ind + " operand: ", env);+ } else if (Cast<Binary_Expression>(node)) {+ Binary_Expression_Ptr expression = Cast<Binary_Expression>(node); std::cerr << ind << "Binary_Expression " << expression; if (expression->is_interpolant()) std::cerr << " [is interpolant] "; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";@@ -577,67 +577,69 @@ 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_Ptr>(node)) {- Map_Ptr expression = dynamic_cast<Map_Ptr>(node);+ debug_ast(expression->left(), ind + " left: ", env);+ debug_ast(expression->right(), ind + " right: ", env);+ } else if (Cast<Map>(node)) {+ Map_Ptr expression = Cast<Map>(node); std::cerr << ind << "Map " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [Hashed]" << std::endl; for (const auto& i : expression->elements()) {- debug_ast(&i.first, ind + " key: ");- debug_ast(&i.second, ind + " val: ");+ debug_ast(i.first, ind + " key: ");+ debug_ast(i.second, ind + " val: "); }- } else if (dynamic_cast<List_Ptr>(node)) {- List_Ptr expression = dynamic_cast<List_Ptr>(node);+ } else if (Cast<List>(node)) {+ List_Ptr expression = Cast<List>(node); std::cerr << ind << "List " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " <<- (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map" : "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() << "] " <<+ " [bracketed: " << expression->is_bracketed() << "] " <<+ " [expanded: " << expression->is_expanded() << "] " << " [hash: " << expression->hash() << "] " << std::endl;- 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);+ for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Content>(node)) {+ Content_Ptr expression = Cast<Content>(node); std::cerr << ind << "Content " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [@media:" << expression->media_block() << "]"; std::cerr << " [Statement]" << std::endl;- } else if (dynamic_cast<Boolean_Ptr>(node)) {- Boolean_Ptr expression = dynamic_cast<Boolean_Ptr>(node);+ } else if (Cast<Boolean>(node)) {+ Boolean_Ptr expression = Cast<Boolean>(node); std::cerr << ind << "Boolean " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << "]" << std::endl;- } else if (dynamic_cast<Color_Ptr>(node)) {- Color_Ptr expression = dynamic_cast<Color_Ptr>(node);+ } else if (Cast<Color>(node)) {+ Color_Ptr expression = Cast<Color>(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_Ptr>(node)) {- Number_Ptr expression = dynamic_cast<Number_Ptr>(node);+ } else if (Cast<Number>(node)) {+ Number_Ptr expression = Cast<Number>(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<Null_Ptr>(node)) {- Null_Ptr expression = dynamic_cast<Null_Ptr>(node);+ } else if (Cast<Null>(node)) {+ Null_Ptr expression = Cast<Null>(node); std::cerr << ind << "Null " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] " // " [hash: " << expression->hash() << "] " << std::endl;- } else if (dynamic_cast<String_Quoted_Ptr>(node)) {- String_Quoted_Ptr expression = dynamic_cast<String_Quoted_Ptr>(node);+ } else if (Cast<String_Quoted>(node)) {+ String_Quoted_Ptr expression = Cast<String_Quoted>(node); std::cerr << ind << "String_Quoted " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]";@@ -645,8 +647,8 @@ 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_Ptr>(node)) {- String_Constant_Ptr expression = dynamic_cast<String_Constant_Ptr>(node);+ } else if (Cast<String_Constant>(node)) {+ String_Constant_Ptr expression = Cast<String_Constant>(node); std::cerr << ind << "String_Constant " << expression; if (expression->concrete_type()) { std::cerr << " " << expression->concrete_type();@@ -656,28 +658,29 @@ if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;- } else if (dynamic_cast<String_Schema_Ptr>(node)) {- String_Schema_Ptr expression = dynamic_cast<String_Schema_Ptr>(node);+ } else if (Cast<String_Schema>(node)) {+ String_Schema_Ptr expression = Cast<String_Schema>(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->css()) std::cerr << " [css]"; if (expression->is_delayed()) std::cerr << " [delayed]"; 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(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);+ for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<String>(node)) {+ String_Ptr expression = Cast<String>(node); std::cerr << ind << "String " << expression; std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;- } else if (dynamic_cast<Expression_Ptr>(node)) {- Expression_Ptr expression = dynamic_cast<Expression_Ptr>(node);+ } else if (Cast<Expression>(node)) {+ Expression_Ptr expression = Cast<Expression>(node); std::cerr << ind << "Expression " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; switch (expression->concrete_type()) {@@ -694,16 +697,18 @@ case Expression::Concrete_Type::C_ERROR: std::cerr << " [C_ERROR]"; break; case Expression::Concrete_Type::FUNCTION: std::cerr << " [FUNCTION]"; break; case Expression::Concrete_Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;+ case Expression::Concrete_Type::VARIABLE: std::cerr << " [VARIABLE]"; break;+ case Expression::Concrete_Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break; } std::cerr << std::endl;- } else if (dynamic_cast<Has_Block_Ptr>(node)) {- Has_Block_Ptr has_block = dynamic_cast<Has_Block_Ptr>(node);+ } else if (Cast<Has_Block>(node)) {+ Has_Block_Ptr has_block = Cast<Has_Block>(node); std::cerr << ind << "Has_Block " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl;- if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(&i, ind + " ", env); }- } else if (dynamic_cast<Statement_Ptr>(node)) {- Statement_Ptr statement = dynamic_cast<Statement_Ptr>(node);+ if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Cast<Statement>(node)) {+ Statement_Ptr statement = Cast<Statement>(node); std::cerr << ind << "Statement " << statement; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << statement->tabs() << std::endl;@@ -735,7 +740,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 ";@@ -776,21 +781,18 @@ { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &it : map.values()) {- debug_ast(&it.first, ind + "first: ");- debug_ast(&it.second, ind + "second: ");+ debug_ast(it.first, ind + "first: ");+ debug_ast(it.second, ind + "second: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; } -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 = "")+inline void debug_subset_entries(SubSetMapPairs* entries, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &pair : *entries) {- debug_ast(&pair.first, ind + "first: ");- debug_ast(&pair.second, ind + "second: ");+ debug_ast(pair.first, ind + "first: ");+ debug_ast(pair.second, ind + "second: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; }
libsass/src/emitter.cpp view
@@ -14,7 +14,9 @@ scheduled_space(0), scheduled_linefeed(0), scheduled_delimiter(false),+ scheduled_crutch(0), scheduled_mapping(0),+ in_custom_property(false), in_comment(false), in_wrapped(false), in_media_block(false),@@ -101,10 +103,30 @@ // prepend some text or token to the buffer void Emitter::prepend_string(const std::string& text) {- wbuf.smap.prepend(Offset(text));+ // do not adjust mappings for utf8 bom+ // seems they are not counted in any UA+ if (text.compare("\xEF\xBB\xBF") != 0) {+ wbuf.smap.prepend(Offset(text));+ } wbuf.buffer = text + wbuf.buffer; } + char Emitter::last_char()+ {+ return wbuf.buffer.back();+ }++ // append a single char to the buffer+ void Emitter::append_char(const char chr)+ {+ // write space/lf+ flush_schedules();+ // add to buffer+ wbuf.buffer += chr;+ // account for data in source-maps+ wbuf.smap.append(Offset(chr));+ }+ // append some text or token to the buffer void Emitter::append_string(const std::string& text) {@@ -145,9 +167,9 @@ add_open_mapping(node); // hotfix for browser issues // this is pretty ugly indeed- if (scheduled_mapping) {- add_open_mapping(scheduled_mapping);- scheduled_mapping = 0;+ if (scheduled_crutch) {+ add_open_mapping(scheduled_crutch);+ scheduled_crutch = 0; } append_string(text); add_close_mapping(node);@@ -193,7 +215,7 @@ { scheduled_space = 0; append_string(":");- append_optional_space();+ if (!in_custom_property) append_optional_space(); } void Emitter::append_mandatory_space()@@ -206,7 +228,9 @@ if ((output_style() != COMPRESSED) && buffer().size()) { unsigned char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) {- append_mandatory_space();+ if (last_char() != '(') {+ append_mandatory_space();+ } } } }
libsass/src/emitter.hpp view
@@ -37,9 +37,12 @@ size_t scheduled_space; size_t scheduled_linefeed; bool scheduled_delimiter;+ AST_Node_Ptr scheduled_crutch; AST_Node_Ptr scheduled_mapping; public:+ // output strings different in custom css properties+ bool in_custom_property; // output strings different in comments bool in_comment; // selector list does not get linefeeds@@ -66,11 +69,15 @@ void prepend_output(const OutputBuffer& out); // append some text or token to the buffer void append_string(const std::string& text);+ // append a single character to buffer+ void append_char(const char chr); // append some white-space only text void append_wspace(const std::string& text); // append some text or token to the buffer // this adds source-mappings for node start and end void append_token(const std::string& text, const AST_Node_Ptr node);+ // query last appended character+ char last_char(); public: // syntax sugar void append_indentation();
libsass/src/environment.cpp view
@@ -6,17 +6,17 @@ template <typename T> Environment<T>::Environment(bool is_shadow)- : local_frame_(std::map<std::string, T>()),+ : local_frame_(environment_map<std::string, T>()), parent_(0), is_shadow_(false) { } template <typename T> Environment<T>::Environment(Environment<T>* env, bool is_shadow)- : local_frame_(std::map<std::string, T>()),+ : local_frame_(environment_map<std::string, T>()), parent_(env), is_shadow_(is_shadow) { } template <typename T> Environment<T>::Environment(Environment<T>& env, bool is_shadow)- : local_frame_(std::map<std::string, T>()),+ : local_frame_(environment_map<std::string, T>()), parent_(&env), is_shadow_(is_shadow) { } @@ -45,7 +45,7 @@ } template <typename T>- std::map<std::string, T>& Environment<T>::local_frame() {+ environment_map<std::string, T>& Environment<T>::local_frame() { return local_frame_; } @@ -53,15 +53,28 @@ bool Environment<T>::has_local(const std::string& key) const { return local_frame_.find(key) != local_frame_.end(); } + template <typename T> EnvResult+ Environment<T>::find_local(const std::string& key)+ {+ auto end = local_frame_.end();+ auto it = local_frame_.find(key);+ return EnvResult(it, it != end);+ }+ template <typename T> T& Environment<T>::get_local(const std::string& key) { return local_frame_[key]; } template <typename T>- void Environment<T>::set_local(const std::string& key, T val)+ void Environment<T>::set_local(const std::string& key, const T& val) { local_frame_[key] = val; }+ template <typename T>+ void Environment<T>::set_local(const std::string& key, T&& val)+ {+ local_frame_[key] = val;+ } template <typename T> void Environment<T>::del_local(const std::string& key)@@ -86,10 +99,15 @@ { return (*global_env())[key]; } template <typename T>- void Environment<T>::set_global(const std::string& key, T val)+ void Environment<T>::set_global(const std::string& key, const T& val) { global_env()->local_frame_[key] = val; }+ template <typename T>+ void Environment<T>::set_global(const std::string& key, T&& val)+ {+ global_env()->local_frame_[key] = val;+ } template <typename T> void Environment<T>::del_global(const std::string& key)@@ -126,12 +144,14 @@ // either update already existing lexical value // or if flag is set, we create one if no lexical found template <typename T>- void Environment<T>::set_lexical(const std::string& key, T val)+ void Environment<T>::set_lexical(const std::string& key, const T& val) {- auto cur = this; bool shadow = false;- while (cur->is_lexical() || shadow) {- if (cur->has_local(key)) {- cur->set_local(key, val);+ Environment<T>* cur = this;+ bool shadow = false;+ while ((cur && cur->is_lexical()) || shadow) {+ EnvResult rv(cur->find_local(key));+ if (rv.found) {+ rv.it->second = val; return; } shadow = cur->is_shadow();@@ -139,6 +159,23 @@ } set_local(key, val); }+ // this one moves the value+ template <typename T>+ void Environment<T>::set_lexical(const std::string& key, T&& val)+ {+ Environment<T>* cur = this;+ bool shadow = false;+ while ((cur && cur->is_lexical()) || shadow) {+ EnvResult rv(cur->find_local(key));+ if (rv.found) {+ rv.it->second = val;+ return;+ }+ shadow = cur->is_shadow();+ cur = cur->parent_;+ }+ set_local(key, val);+ } // look on the full stack for key // include all scopes available@@ -155,6 +192,20 @@ return false; } + // look on the full stack for key+ // include all scopes available+ template <typename T> EnvResult+ Environment<T>::find(const std::string& key)+ {+ auto cur = this;+ while (true) {+ EnvResult rv(cur->find_local(key));+ if (rv.found) return rv;+ cur = cur->parent_;+ if (!cur) return rv;+ }+ };+ // use array access for getter and setter functions template <typename T> T& Environment<T>::operator[](const std::string& key)@@ -168,7 +219,7 @@ } return get_local(key); }-+/* #ifdef DEBUG template <typename T> size_t Environment<T>::print(std::string prefix)@@ -176,10 +227,10 @@ size_t indent = 0; if (parent_) indent = parent_->print(prefix) + 1; std::cerr << prefix << std::string(indent, ' ') << "== " << this << std::endl;- for (typename std::map<std::string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) {+ for (typename environment_map<std::string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) {- std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second;- if (Value_Ptr val = SASS_MEMORY_CAST_PTR(Value, i->second))+ std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second;+ if (Value_Ptr val = Cast<Value>(i->second)) { std::cerr << " : " << val->to_string(); } std::cerr << std::endl; }@@ -187,7 +238,7 @@ return indent ; } #endif-+*/ // compile implementation for AST_Node template class Environment<AST_Node_Obj>;
libsass/src/environment.hpp view
@@ -2,17 +2,26 @@ #define SASS_ENVIRONMENT_H #include <string>-#include <map>- #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" namespace Sass { + typedef environment_map<std::string, AST_Node_Obj>::iterator EnvIter;++ class EnvResult {+ public:+ EnvIter it;+ bool found;+ public:+ EnvResult(EnvIter it, bool found)+ : it(it), found(found) {}+ };+ template <typename T> class Environment { // TODO: test with map- std::map<std::string, T> local_frame_;+ environment_map<std::string, T> local_frame_; ADD_PROPERTY(Environment*, parent) ADD_PROPERTY(bool, is_shadow) @@ -37,14 +46,17 @@ // scope operates on the current frame - std::map<std::string, T>& local_frame();+ environment_map<std::string, T>& local_frame(); bool has_local(const std::string& key) const; + EnvResult find_local(const std::string& key);+ T& get_local(const std::string& key); // set variable on the current frame- void set_local(const std::string& key, T val);+ void set_local(const std::string& key, const T& val);+ void set_local(const std::string& key, T&& val); void del_local(const std::string& key); @@ -60,7 +72,8 @@ T& get_global(const std::string& key); // set a variable on the global frame- void set_global(const std::string& key, T val);+ void set_global(const std::string& key, const T& val);+ void set_global(const std::string& key, T&& val); void del_global(const std::string& key); @@ -72,12 +85,17 @@ // see if we have a lexical we could update // either update already existing lexical value // or we create a new one on the current frame- void set_lexical(const std::string& key, T val);+ void set_lexical(const std::string& key, T&& val);+ void set_lexical(const std::string& key, const T& val); // look on the full stack for key // include all scopes available bool has(const std::string& key) const; + // look on the full stack for key+ // include all scopes available+ EnvResult find(const std::string& key);+ // use array access for getter and setter functions T& operator[](const std::string& key); @@ -86,6 +104,9 @@ #endif };++ // define typedef for our use case+ typedef Environment<AST_Node_Obj> Env; }
libsass/src/error_handling.cpp view
@@ -10,19 +10,18 @@ namespace Exception { - Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)+ Base::Base(ParserState pstate, std::string msg, Backtraces traces) : std::runtime_error(msg), msg(msg),- prefix("Error"), pstate(pstate),- import_stack(import_stack)+ prefix("Error"), pstate(pstate), traces(traces) { } - InvalidSass::InvalidSass(ParserState pstate, std::string msg)- : Base(pstate, msg)+ InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg)+ : Base(pstate, msg, traces) { } - InvalidParent::InvalidParent(Selector_Ptr parent, Selector_Ptr selector)- : Base(selector->pstate()), parent(parent), selector(selector)+ InvalidParent::InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector)+ : Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector) { msg = "Invalid parent selector for \""; msg += selector->to_string(Sass_Inspect_Options());@@ -31,55 +30,40 @@ msg += "\""; } - 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)+ InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg)+ : Base(pstate, def_msg, traces), name(name), arg(arg) {+ msg = "Variable keyword argument map must have string keys.\n";+ msg += name + " is not a string in " + arg->to_string() + ".";+ }++ InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value)+ : Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value)+ { msg = arg + ": \""; if (value) msg += value->to_string(Sass_Inspect_Options()); msg += "\" is not a " + type; msg += " for `" + fn + "'"; } - MissingArgument::MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype)- : Base(pstate), fn(fn), arg(arg), fntype(fntype)+ MissingArgument::MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype)+ : Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype) { msg = fntype + " " + fn; msg += " is missing argument "; msg += arg + "."; } - InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)- : Base(pstate, msg, import_stack)+ InvalidSyntax::InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg)+ : Base(pstate, msg, traces) { } - 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";- }+ NestingLimitError::NestingLimitError(ParserState pstate, Backtraces traces, std::string msg)+ : Base(pstate, msg, traces)+ { } - DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org)- : Base(org.pstate()), dup(dup), org(org)+ DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org)+ : Base(org.pstate(), def_msg, traces), dup(dup), org(org) { msg = "Duplicate key "; msg += dup.get_duplicate_key()->inspect();@@ -88,8 +72,8 @@ msg += ")."; } - TypeMismatch::TypeMismatch(const Expression& var, const std::string type)- : Base(var.pstate()), var(var), type(type)+ TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const std::string type)+ : Base(var.pstate(), def_msg, traces), var(var), type(type) { msg = var.to_string(); msg += " is not an ";@@ -97,21 +81,20 @@ msg += "."; } - InvalidValue::InvalidValue(const Expression& val)- : Base(val.pstate()), val(val)+ InvalidValue::InvalidValue(Backtraces traces, const Expression& val)+ : Base(val.pstate(), def_msg, traces), val(val) { msg = val.to_string(); msg += " isn't a valid CSS value."; } - StackError::StackError(const AST_Node& node)- : Base(node.pstate()), node(node)+ StackError::StackError(Backtraces traces, const AST_Node& node)+ : Base(node.pstate(), def_msg, traces), node(node) { msg = "stack level too deep"; } - IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs)- : lhs(lhs), rhs(rhs)+ IncompatibleUnits::IncompatibleUnits(const Units& lhs, const Units& rhs) { msg = "Incompatible units: '"; msg += rhs.unit();@@ -120,20 +103,54 @@ msg += "'."; } - AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)- : lhs(lhs), rhs(rhs), op(op)+ IncompatibleUnits::IncompatibleUnits(const UnitType lhs, const UnitType rhs) {+ msg = "Incompatible units: '";+ msg += unit_to_string(rhs);+ msg += "' and '";+ msg += unit_to_string(lhs);+ msg += "'.";+ }++ AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+ : OperationError(), lhs(lhs), rhs(rhs), op(op)+ { msg = "Alpha channels must be equal: "; msg += lhs->to_string({ NESTED, 5 });- msg += " " + op + " ";+ msg += " " + sass_op_to_name(op) + " "; msg += rhs->to_string({ NESTED, 5 }); msg += "."; } + ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)+ : OperationError(), lhs(lhs), rhs(rhs)+ {+ msg = "divided by 0";+ } - SassValueError::SassValueError(ParserState pstate, OperationError& err)- : Base(pstate, err.what())+ UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+ : OperationError(), lhs(lhs), rhs(rhs), op(op) {+ msg = def_op_msg + ": \"";+ msg += lhs->to_string({ NESTED, 5 });+ msg += " " + sass_op_to_name(op) + " ";+ msg += rhs->to_string({ TO_SASS, 5 });+ msg += "\".";+ }++ InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+ : UndefinedOperation(lhs, rhs, op)+ {+ msg = def_op_null_msg + ": \"";+ msg += lhs->inspect();+ msg += " " + sass_op_to_name(op) + " ";+ msg += rhs->inspect();+ msg += "\".";+ }++ SassValueError::SassValueError(Backtraces traces, ParserState pstate, OperationError& err)+ : Base(pstate, err.what(), traces)+ { msg = err.what(); prefix = err.errtype(); }@@ -143,13 +160,22 @@ void warn(std::string msg, ParserState pstate) {- std::cerr << "Warning: " << msg<< std::endl;+ std::cerr << "Warning: " << msg << std::endl; } + void warning(std::string msg, ParserState pstate)+ {+ std::string cwd(Sass::File::get_cwd());+ std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd));+ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));+ std::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.path));++ std::cerr << "WARNING on line " << pstate.line+1 << ", column " << pstate.column+1 << " of " << output_path << ":" << std::endl;+ std::cerr << msg << std::endl << std::endl;+ }+ void warn(std::string msg, ParserState pstate, Backtrace* bt) {- Backtrace top(bt, pstate, "");- msg += top.to_string(); warn(msg, pstate); } @@ -165,7 +191,7 @@ std::cerr << " on line " << pstate.line+1 << " of " << output_path << std::endl; } - void deprecated(std::string msg, std::string msg2, ParserState pstate)+ void deprecated(std::string msg, std::string msg2, bool with_column, ParserState pstate) { std::string cwd(Sass::File::get_cwd()); std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd));@@ -173,9 +199,10 @@ std::string output_path(Sass::File::path_for_console(rel_path, pstate.path, pstate.path)); std::cerr << "DEPRECATION WARNING on line " << pstate.line + 1;+ if (with_column) std::cerr << ", column " << pstate.column + pstate.offset.column + 1; if (output_path.length()) std::cerr << " of " << output_path; std::cerr << ":" << std::endl;- std::cerr << msg << " and will be an error in future versions of Sass." << std::endl;+ std::cerr << msg << std::endl; if (msg2.length()) std::cerr << msg2 << std::endl; std::cerr << std::endl; }@@ -192,16 +219,17 @@ std::cerr << "This will be an error in future versions of Sass." << std::endl; } - void error(std::string msg, ParserState pstate)+ // should be replaced with error with backtraces+ void coreError(std::string msg, ParserState pstate) {- throw Exception::InvalidSyntax(pstate, msg);+ Backtraces traces;+ throw Exception::InvalidSyntax(pstate, traces, msg); } - void error(std::string msg, ParserState pstate, Backtrace* bt)+ void error(std::string msg, ParserState pstate, Backtraces& traces) {- Backtrace top(bt, pstate, "");- msg += "\n" + top.to_string();- error(msg, pstate);+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidSyntax(pstate, traces, msg); } }
libsass/src/error_handling.hpp view
@@ -5,6 +5,9 @@ #include <sstream> #include <stdexcept> #include "position.hpp"+#include "backtrace.hpp"+#include "ast_fwd_decl.hpp"+#include "sass/functions.h" namespace Sass { @@ -15,6 +18,7 @@ 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";+ const std::string def_nesting_limit = "Code too deeply neested"; class Base : public std::runtime_error { protected:@@ -22,9 +26,9 @@ std::string prefix; public: ParserState pstate;- std::vector<Sass_Import_Entry>* import_stack;+ Backtraces traces; public:- Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0);+ Base(ParserState pstate, std::string msg, Backtraces traces); virtual const char* errtype() const { return prefix.c_str(); } virtual const char* what() const throw() { return msg.c_str(); } virtual ~Base() throw() {};@@ -32,7 +36,7 @@ class InvalidSass : public Base { public:- InvalidSass(ParserState pstate, std::string msg);+ InvalidSass(ParserState pstate, Backtraces traces, std::string msg); virtual ~InvalidSass() throw() {}; }; @@ -41,7 +45,7 @@ Selector_Ptr parent; Selector_Ptr selector; public:- InvalidParent(Selector_Ptr parent, Selector_Ptr selector);+ InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector); virtual ~InvalidParent() throw() {}; }; @@ -51,7 +55,7 @@ std::string arg; std::string fntype; public:- MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype);+ MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype); virtual ~MissingArgument() throw() {}; }; @@ -62,38 +66,29 @@ std::string type; const Value_Ptr value; public:- InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0);+ InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0); virtual ~InvalidArgumentType() throw() {}; }; - class InvalidSyntax : public Base {+ class InvalidVarKwdType : public Base {+ protected:+ std::string name;+ const Argument_Ptr arg; public:- InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0);- virtual ~InvalidSyntax() throw() {};+ InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg = 0);+ virtual ~InvalidVarKwdType() 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)- {};+ class InvalidSyntax : public Base { public:- virtual const char* errtype() const { return "Error"; }- virtual const char* what() const throw() { return msg.c_str(); }- virtual ~OperationError() throw() {};+ InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg);+ virtual ~InvalidSyntax() throw() {}; }; - class ZeroDivisionError : public OperationError {- protected:- const Expression& lhs;- const Expression& rhs;+ class NestingLimitError : public Base { public:- ZeroDivisionError(const Expression& lhs, const Expression& rhs);- virtual const char* errtype() const { return "ZeroDivisionError"; }- virtual ~ZeroDivisionError() throw() {};+ NestingLimitError(ParserState pstate, Backtraces traces, std::string msg = def_nesting_limit);+ virtual ~NestingLimitError() throw() {}; }; class DuplicateKeyError : public Base {@@ -101,7 +96,7 @@ const Map& dup; const Expression& org; public:- DuplicateKeyError(const Map& dup, const Expression& org);+ DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org); virtual const char* errtype() const { return "Error"; } virtual ~DuplicateKeyError() throw() {}; };@@ -111,7 +106,7 @@ const Expression& var; const std::string type; public:- TypeMismatch(const Expression& var, const std::string type);+ TypeMismatch(Backtraces traces, const Expression& var, const std::string type); virtual const char* errtype() const { return "Error"; } virtual ~TypeMismatch() throw() {}; };@@ -120,7 +115,7 @@ protected: const Expression& val; public:- InvalidValue(const Expression& val);+ InvalidValue(Backtraces traces, const Expression& val); virtual const char* errtype() const { return "Error"; } virtual ~InvalidValue() throw() {}; };@@ -129,17 +124,42 @@ protected: const AST_Node& node; public:- StackError(const AST_Node& node);+ StackError(Backtraces traces, const AST_Node& node); virtual const char* errtype() const { return "SystemStackError"; } virtual ~StackError() throw() {}; }; + /* common virtual base class (has no pstate or trace) */+ 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 IncompatibleUnits : public OperationError { protected:- const Number& lhs;- const Number& rhs;+ // const Sass::UnitType lhs;+ // const Sass::UnitType rhs; public:- IncompatibleUnits(const Number& lhs, const Number& rhs);+ IncompatibleUnits(const Units& lhs, const Units& rhs);+ IncompatibleUnits(const UnitType lhs, const UnitType rhs); virtual ~IncompatibleUnits() throw() {}; }; @@ -147,16 +167,16 @@ protected: Expression_Ptr_Const lhs; Expression_Ptr_Const rhs;- const std::string op;+ const Sass_OP op; public:- UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+ UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op); // virtual const char* errtype() const { return "Error"; } virtual ~UndefinedOperation() throw() {}; }; class InvalidNullOperation : public UndefinedOperation { public:- InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+ InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op); virtual ~InvalidNullOperation() throw() {}; }; @@ -164,16 +184,16 @@ protected: Expression_Ptr_Const lhs; Expression_Ptr_Const rhs;- const std::string op;+ const Sass_OP op; public:- AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+ AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op); // virtual const char* errtype() const { return "Error"; } virtual ~AlphaChannelsNotEqual() throw() {}; }; class SassValueError : public Base { public:- SassValueError(ParserState pstate, OperationError& err);+ SassValueError(Backtraces traces, ParserState pstate, OperationError& err); virtual ~SassValueError() throw() {}; }; @@ -181,14 +201,15 @@ void warn(std::string msg, ParserState pstate); void warn(std::string msg, ParserState pstate, Backtrace* bt);+ void warning(std::string msg, ParserState pstate); void deprecated_function(std::string msg, ParserState pstate);- void deprecated(std::string msg, std::string msg2, ParserState pstate);+ void deprecated(std::string msg, std::string msg2, bool with_column, ParserState pstate); void deprecated_bind(std::string msg, ParserState pstate); // void deprecated(std::string msg, ParserState pstate, Backtrace* bt); - void error(std::string msg, ParserState pstate);- void error(std::string msg, ParserState pstate, Backtrace* bt);+ void coreError(std::string msg, ParserState pstate);+ void error(std::string msg, ParserState pstate, Backtraces& traces); }
libsass/src/eval.cpp view
@@ -12,1702 +12,1667 @@ #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());- }- }+#include "operators.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"+#include "sass_functions.hpp"++namespace Sass {++ Eval::Eval(Expand& exp)+ : exp(exp),+ ctx(exp.ctx),+ traces(exp.traces),+ force(false),+ is_in_comment(false),+ is_in_selector_schema(false)+ {+ bool_true = SASS_MEMORY_NEW(Boolean, "[NA]", true);+ bool_false = SASS_MEMORY_NEW(Boolean, "[NA]", false);+ }+ Eval::~Eval() { }++ Env* Eval::environment()+ {+ return exp.environment();+ }++ Selector_List_Obj Eval::selector()+ {+ return exp.selector();+ }++ 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 = 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 = 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 = 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) {+ traces.push_back(Backtrace(low->pstate()));+ throw Exception::TypeMismatch(traces, *low, "integer");+ }+ Expression_Obj high = f->upper_bound()->perform(this);+ if (high->concrete_type() != Expression::NUMBER) {+ traces.push_back(Backtrace(high->pstate()));+ throw Exception::TypeMismatch(traces, *high, "integer");+ }+ Number_Obj sass_start = Cast<Number>(low);+ Number_Obj sass_end = 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(), traces);+ }+ 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);+ Block_Obj body = f->block();+ Expression_Ptr val = 0;+ if (start < end) {+ if (f->is_inclusive()) ++end;+ for (double i = start;+ i < end;+ ++i) {+ Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());+ 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) {+ Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());+ 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 = Cast<Map>(expr);+ }+ else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {+ Listize listize;+ Expression_Obj rv = ls->perform(&listize);+ list = Cast<List>(rv);+ }+ else if (expr->concrete_type() != Expression::LIST) {+ list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);+ list->append(expr);+ }+ else {+ list = 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 && Cast<Selector_List>(list)) {+ list = Cast<List>(list);+ }+ for (size_t i = 0, L = list->length(); i < L; ++i) {+ Expression_Ptr item = list->at(i);+ // unwrap value if the expression is an argument+ if (Argument_Ptr arg = Cast<Argument>(item)) item = arg->value();+ // check if we got passed a list of args (investigate)+ if (List_Ptr scalars = Cast<List>(item)) {+ if (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), item);+ for (size_t j = 1, K = variables.size(); j < K; ++j) {+ // XXX: this is never hit via spec tests+ Expression_Ptr res = SASS_MEMORY_NEW(Null, expr->pstate());+ 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]")) {++ // add call stack entry+ ctx.callee_stack.push_back({+ "@warn",+ w->pstate().path,+ w->pstate().line + 1,+ w->pstate().column + 1,+ SASS_CALLEE_FUNCTION,+ { env }+ });++ Definition_Ptr def = Cast<Definition>((*env)["@warn[f]"]);+ // Block_Obj body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ ctx.c_options.output_style = outstyle;+ ctx.callee_stack.pop_back();+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::string result(unquote(message->to_sass()));+ std::cerr << "WARNING: " << result << std::endl;+ traces.push_back(Backtrace(w->pstate()));+ std::cerr << traces_to_string(traces, " ");+ std::cerr << std::endl;+ ctx.c_options.output_style = outstyle;+ traces.pop_back();+ 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]")) {++ // add call stack entry+ ctx.callee_stack.push_back({+ "@error",+ e->pstate().path,+ e->pstate().line + 1,+ e->pstate().column + 1,+ SASS_CALLEE_FUNCTION,+ { env }+ });++ Definition_Ptr def = Cast<Definition>((*env)["@error[f]"]);+ // Block_Obj body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ ctx.c_options.output_style = outstyle;+ ctx.callee_stack.pop_back();+ sass_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(), traces);+ 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]")) {++ // add call stack entry+ ctx.callee_stack.push_back({+ "@debug",+ d->pstate().path,+ d->pstate().line + 1,+ d->pstate().column + 1,+ SASS_CALLEE_FUNCTION,+ { env }+ });++ Definition_Ptr def = Cast<Definition>((*env)["@debug[f]"]);+ // Block_Obj body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ ctx.c_options.output_style = outstyle;+ ctx.callee_stack.pop_back();+ sass_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_Obj key = (*l)[i+0]->perform(this);+ Expression_Obj 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()) {+ traces.push_back(Backtrace(l->pstate()));+ throw Exception::DuplicateKeyError(traces, *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(),+ l->is_bracketed());+ 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()) {+ traces.push_back(Backtrace(m->pstate()));+ throw Exception::DuplicateKeyError(traces, *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);+ if (ex_val == NULL) continue;+ ex_val = ex_val->perform(this);+ *mm << std::make_pair(ex_key, ex_val);+ }++ // check the evaluated keys aren't duplicates.+ if (mm->has_duplicate_key()) {+ traces.push_back(Backtrace(m->pstate()));+ throw Exception::DuplicateKeyError(traces, *mm, *m);+ }++ mm->is_expanded(true);+ return mm.detach();+ }++ Expression_Ptr Eval::operator()(Binary_Expression_Ptr b_in)+ {++ Expression_Obj lhs = b_in->left();+ Expression_Obj rhs = b_in->right();+ enum Sass_OP op_type = b_in->optype();++ if (op_type == Sass_OP::AND) {+ // LOCAL_FLAG(force, true);+ lhs = lhs->perform(this);+ if (!*lhs) return lhs.detach();+ return rhs->perform(this);+ }+ else if (op_type == Sass_OP::OR) {+ // LOCAL_FLAG(force, true);+ lhs = lhs->perform(this);+ if (*lhs) return lhs.detach();+ return rhs->perform(this);+ }++ // Evaluate variables as early o+ while (Variable_Ptr l_v = Cast<Variable>(lhs)) {+ lhs = operator()(l_v);+ }+ while (Variable_Ptr r_v = Cast<Variable>(rhs)) {+ rhs = operator()(r_v);+ }++ Binary_Expression_Obj b = b_in;++ // Evaluate sub-expressions early on+ while (Binary_Expression_Ptr l_b = Cast<Binary_Expression>(lhs)) {+ if (!force && l_b->is_delayed()) break;+ lhs = operator()(l_b);+ }+ while (Binary_Expression_Ptr r_b = Cast<Binary_Expression>(rhs)) {+ if (!force && r_b->is_delayed()) break;+ rhs = operator()(r_b);+ }++ // 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();+ }++ // specific types we know are final+ // handle them early to avoid overhead+ if (Number_Ptr l_n = Cast<Number>(lhs)) {+ // lhs is number and rhs is number+ if (Number_Ptr r_n = Cast<Number>(rhs)) {+ try {+ switch (op_type) {+ case Sass_OP::EQ: return *l_n == *r_n ? bool_true : bool_false;+ case Sass_OP::NEQ: return *l_n == *r_n ? bool_false : bool_true;+ case Sass_OP::LT: return *l_n < *r_n ? bool_true : bool_false;+ case Sass_OP::GTE: return *l_n < *r_n ? bool_false : bool_true;+ case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false;+ case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true;+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:+ return Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, b_in->pstate());+ default: break;+ }+ }+ catch (Exception::OperationError& err)+ {+ traces.push_back(Backtrace(b_in->pstate()));+ throw Exception::SassValueError(traces, b_in->pstate(), err);+ }+ }+ // lhs is number and rhs is color+ else if (Color_Ptr r_c = Cast<Color>(rhs)) {+ try {+ switch (op_type) {+ case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false;+ case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true;+ case Sass_OP::LT: return *l_n < *r_c ? bool_true : bool_false;+ case Sass_OP::GTE: return *l_n < *r_c ? bool_false : bool_true;+ case Sass_OP::LTE: return *l_n < *r_c || *l_n == *r_c ? bool_true : bool_false;+ case Sass_OP::GT: return *l_n < *r_c || *l_n == *r_c ? bool_false : bool_true;+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:+ return Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate());+ default: break;+ }+ }+ catch (Exception::OperationError& err)+ {+ traces.push_back(Backtrace(b_in->pstate()));+ throw Exception::SassValueError(traces, b_in->pstate(), err);+ }+ }+ }+ else if (Color_Ptr l_c = Cast<Color>(lhs)) {+ // lhs is color and rhs is color+ if (Color_Ptr r_c = Cast<Color>(rhs)) {+ try {+ switch (op_type) {+ case Sass_OP::EQ: return *l_c == *r_c ? bool_true : bool_false;+ case Sass_OP::NEQ: return *l_c == *r_c ? bool_false : bool_true;+ case Sass_OP::LT: return *l_c < *r_c ? bool_true : bool_false;+ case Sass_OP::GTE: return *l_c < *r_c ? bool_false : bool_true;+ case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false;+ case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true;+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:+ return Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, b_in->pstate());+ default: break;+ }+ }+ catch (Exception::OperationError& err)+ {+ traces.push_back(Backtrace(b_in->pstate()));+ throw Exception::SassValueError(traces, b_in->pstate(), err);+ }+ }+ // lhs is color and rhs is number+ else if (Number_Ptr r_n = Cast<Number>(rhs)) {+ try {+ switch (op_type) {+ case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false;+ case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true;+ case Sass_OP::LT: return *l_c < *r_n ? bool_true : bool_false;+ case Sass_OP::GTE: return *l_c < *r_n ? bool_false : bool_true;+ case Sass_OP::LTE: return *l_c < *r_n || *l_c == *r_n ? bool_true : bool_false;+ case Sass_OP::GT: return *l_c < *r_n || *l_c == *r_n ? bool_false : bool_true;+ case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:+ return Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate());+ default: break;+ }+ }+ catch (Exception::OperationError& err)+ {+ traces.push_back(Backtrace(b_in->pstate()));+ throw Exception::SassValueError(traces, b_in->pstate(), err);+ }+ }+ }++ String_Schema_Obj ret_schema;++ // only the last item will be used to eval the binary expression+ if (String_Schema_Ptr s_l = Cast<String_Schema>(b->left())) {+ if (!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 = 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);+ }+ }++ // 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);+ }++ // 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;+ Expression::Concrete_Type r_type;++ // Is one of the operands an interpolant?+ String_Schema_Obj s1 = Cast<String_Schema>(b->left());+ String_Schema_Obj s2 = Cast<String_Schema>(b->right());+ Binary_Expression_Obj b1 = Cast<Binary_Expression>(b->left());+ Binary_Expression_Obj b2 = 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 = 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) {+ lhs = Parser::lexed_dimension(b->pstate(), str->value());+ }+ }+ // If possible upgrade RHS to a number (for string to number compare)+ if (String_Constant_Ptr str = Cast<String_Constant>(rhs)) {+ std::string value(str->value());+ const char* start = value.c_str();+ if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) {+ rhs = Parser::lexed_dimension(b->pstate(), str->value());+ }+ }+ }++ To_Value to_value(ctx);+ Value_Obj v_l = Cast<Value>(lhs->perform(&to_value));+ Value_Obj v_r = Cast<Value>(rhs->perform(&to_value));++ 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(), Operators::eq(lhs, rhs));+ case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::neq(lhs, rhs));+ case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gt(lhs, rhs));+ case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gte(lhs, rhs));+ case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lt(lhs, rhs));+ case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lte(lhs, rhs));+ default: break;+ }+ }+ catch (Exception::OperationError& err)+ {+ // throw Exception::Base(b->pstate(), err.what());+ traces.push_back(Backtrace(b->pstate()));+ throw Exception::SassValueError(traces, 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;+ try {+ ParserState pstate(b->pstate());+ if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {+ Number_Ptr l_n = Cast<Number>(lhs);+ Number_Ptr r_n = Cast<Number>(rhs);+ l_n->reduce(); r_n->reduce();+ rv = Operators::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 = Cast<Number>(lhs);+ Color_Ptr r_c = Cast<Color>(rhs);+ rv = Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, pstate);+ }+ else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {+ Color_Ptr l_c = Cast<Color>(lhs);+ Number_Ptr r_n = Cast<Number>(rhs);+ rv = Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, pstate);+ }+ else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {+ Color_Ptr l_c = Cast<Color>(lhs);+ Color_Ptr r_c = Cast<Color>(rhs);+ rv = Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, pstate);+ }+ else {+ To_Value to_value(ctx);+ // this will leak if perform does not return a value!+ Value_Obj v_l = Cast<Value>(lhs->perform(&to_value));+ Value_Obj v_r = Cast<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 || l_type == Expression::FUNCTION_VAL) {+ traces.push_back(Backtrace(v_l->pstate()));+ throw Exception::InvalidValue(traces, *v_l);+ }+ if (r_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) {+ traces.push_back(Backtrace(v_r->pstate()));+ throw Exception::InvalidValue(traces, *v_r);+ }+ Value_Ptr ex = Operators::op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress+ if (String_Constant_Ptr str = Cast<String_Constant>(ex))+ {+ if (str->concrete_type() == Expression::STRING)+ {+ String_Constant_Ptr lstr = Cast<String_Constant>(lhs);+ String_Constant_Ptr rstr = 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)+ {+ traces.push_back(Backtrace(b->pstate()));+ // throw Exception::Base(b->pstate(), err.what());+ throw Exception::SassValueError(traces, 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->optype() == Unary_Expression::NOT) {+ Boolean_Ptr result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);+ result->value(!result->value());+ return result;+ }+ else if (Number_Obj nr = Cast<Number>(operand)) {+ // negate value for minus unary expression+ if (u->optype() == Unary_Expression::MINUS) {+ Number_Obj cpy = SASS_MEMORY_COPY(nr);+ cpy->value( - cpy->value() ); // negate value+ return cpy.detach(); // return the copy+ }+ else if (u->optype() == Unary_Expression::SLASH) {+ std::string str = '/' + nr->to_string(ctx.c_options);+ return SASS_MEMORY_NEW(String_Constant, u->pstate(), str);+ }+ // nothing for positive+ return nr.detach();+ }+ else {+ // Special cases: +/- variables which evaluate to null ouput just +/-,+ // but +/- null itself outputs the string+ if (operand->concrete_type() == Expression::NULL_VAL && Cast<Variable>(u->operand())) {+ u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), ""));+ }+ // Never apply unary opertions on colors @see #2140+ else if (Color_Ptr color = Cast<Color>(operand)) {+ // Use the color name if this was eval with one+ if (color->disp().length() > 0) {+ operand = SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp());+ u->operand(operand);+ }+ }+ else {+ u->operand(operand);+ }++ return SASS_MEMORY_NEW(String_Quoted,+ u->pstate(),+ u->inspect());+ }+ // unreachable+ return u;+ }++ Expression_Ptr Eval::operator()(Function_Call_Ptr c)+ {+ if (traces.size() > 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(), traces);+ }+ 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 = Cast<List>(arg->value())) {+ if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate(), traces);+ }+ }+ args = Cast<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(), traces);+ }+ 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 = Cast<Arguments>(args->perform(this));+ }+ Definition_Ptr def = Cast<Definition>((*env)[full_name]);++ if (c->func()) def = c->func()->definition();++ 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 = 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(), traces);+ def = 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();++ if (c->is_css()) return result.detach();++ 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);+ std::string msg(", in function `" + c->name() + "`");+ traces.push_back(Backtrace(c->pstate(), msg));+ ctx.callee_stack.push_back({+ c->name().c_str(),+ c->pstate().path,+ c->pstate().line + 1,+ c->pstate().column + 1,+ SASS_CALLEE_FUNCTION,+ { env }+ });++ // 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(), traces, exp.selector_stack);+ }+ if (!result) {+ error(std::string("Function ") + c->name() + " finished without @return", c->pstate(), traces);+ }+ ctx.callee_stack.pop_back();+ traces.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);+ std::string msg(", in function `" + c->name() + "`");+ traces.push_back(Backtrace(c->pstate(), msg));+ ctx.callee_stack.push_back({+ c->name().c_str(),+ c->pstate().path,+ c->pstate().line + 1,+ c->pstate().column + 1,+ SASS_CALLEE_C_FUNCTION,+ { env }+ });++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA, false);+ for(size_t i = 0; i < params->length(); i++) {+ Parameter_Obj param = params->at(i);+ std::string key = param->name();+ AST_Node_Obj node = fn_env.get_local(key);+ Expression_Obj arg = Cast<Expression>(node);+ sass_list_set_value(c_args, i, arg->perform(&to_c));+ }+ 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(), traces);+ } else if (sass_value_get_tag(c_val) == SASS_WARNING) {+ error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), traces);+ }+ result = cval_to_astnode(c_val, traces, c->pstate());++ ctx.callee_stack.pop_back();+ traces.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)+ {+ Expression_Obj value = 0;+ Env* env = environment();+ const std::string& name(v->name());+ EnvResult rv(env->find(name));+ if (rv.found) value = static_cast<Expression*>(rv.it->second.ptr());+ else error("Undefined variable: \"" + v->name() + "\".", v->pstate(), traces);+ if (Argument_Ptr arg = Cast<Argument>(value)) value = arg->value();+ if (Number_Ptr nr = Cast<Number>(value)) nr->zero(true); // force flag+ value->is_interpolant(v->is_interpolant());+ if (force) value->is_expanded(false);+ value->set_delayed(false); // verified+ value = value->perform(this);+ if(!force) rv.it->second = value;+ return value.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 = 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 = Cast<Number>(ex)) {+ Number reduced(nr);+ reduced.reduce();+ if (!reduced.is_valid_css_unit()) {+ traces.push_back(Backtrace(nr->pstate()));+ throw Exception::InvalidValue(traces, *nr);+ }+ }+ if (Argument_Ptr arg = Cast<Argument>(ex)) {+ ex = arg->value();+ }+ if (String_Quoted_Ptr sq = 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 (Cast<Null>(ex)) { return; }++ // parent selector needs another go+ if (Cast<Parent_Selector>(ex)) {+ // XXX: this is never hit via spec tests+ ex = ex->perform(this);+ }++ if (List_Ptr l = Cast<List>(ex)) {+ List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator());+ // this fixes an issue with bourbon sample, not really sure why+ // if (l->size() && Cast<Null>((*l)[0])) { res += ""; }+ 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 = Cast<Null>(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));+ str = read_hex_escapes(str); // read escapes+ newline_to_space(str); // replace directly+ res += str; // append to result string+ } else {+ res += (ll->to_string(ctx.c_options));+ }+ ll->is_interpolant(l->is_interpolant());+ }++ // Value+ // 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 {+ std::string str(ex ? ex->to_string(ctx.c_options) : "");+ if (into_quotes) str = read_hex_escapes(str);+ res += str; // append to result string+ }+ }++ 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 (!Cast<String_Quoted>((*s)[0]) && !Cast<String_Quoted>((*s)[L - 1])) {+ if (String_Constant_Ptr l = Cast<String_Constant>((*s)[0])) {+ if (String_Constant_Ptr r = Cast<String_Constant>((*s)[L - 1])) {+ if (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 = 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 = 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, s->css());+ }+ // string schema seems to have a special unquoting behavior (also handles "nested" quotes)+ String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false, s->css());+ // if (s->is_interpolant()) str->quote_mark(0);+ // String_Constant_Ptr str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);+ 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(),+ Cast<Supports_Condition>(left),+ Cast<Supports_Condition>(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(),+ Cast<Supports_Condition>(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(),+ Cast<String>(feature),+ value);+ return ee;+ }++ Media_Query_Ptr Eval::operator()(Media_Query_Ptr q)+ {+ String_Obj t = q->media_type();+ t = static_cast<String_Ptr>(t.isNull() ? 0 : t->perform(this));+ 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 && Cast<String_Quoted>(feature)) {+ feature = SASS_MEMORY_NEW(String_Quoted,+ feature->pstate(),+ Cast<String_Quoted>(feature)->value());+ }+ Expression_Obj value = e->value();+ value = (value ? value->perform(this) : 0);+ if (value && Cast<String_Quoted>(value)) {+ // XXX: this is never hit via spec tests+ value = SASS_MEMORY_NEW(String_Quoted,+ value->pstate(),+ 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 = 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 = Cast<Argument>(rest)->value()->perform(this);++ Sass_Separator separator = SASS_COMMA;+ List_Ptr ls = Cast<List>(splat);+ Map_Ptr ms = 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 = 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.++ Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate)+ {+ using std::strlen;+ using std::strcpy;+ Expression_Ptr e = NULL;+ switch (sass_value_get_tag(v)) {+ case SASS_BOOLEAN: {+ e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));+ } break;+ case SASS_NUMBER: {+ e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));+ } break;+ case SASS_COLOR: {+ e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));+ } break;+ case SASS_STRING: {+ if (sass_string_is_quoted(v))+ e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));+ else {+ e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));+ }+ } break;+ case SASS_LIST: {+ List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));+ for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {+ l->append(cval_to_astnode(sass_list_get_value(v, i), traces, pstate));+ }+ l->is_bracketed(sass_list_get_is_bracketed(v));+ e = l;+ } break;+ case SASS_MAP: {+ Map_Ptr m = SASS_MEMORY_NEW(Map, pstate);+ for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {+ *m << std::make_pair(+ cval_to_astnode(sass_map_get_key(v, i), traces, pstate),+ cval_to_astnode(sass_map_get_value(v, i), traces, pstate));+ }+ e = m;+ } break;+ case SASS_NULL: {+ e = SASS_MEMORY_NEW(Null, pstate);+ } break;+ case SASS_ERROR: {+ error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces);+ } break;+ case SASS_WARNING: {+ error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces);+ } break;+ default: break;+ }+ return e;+ }++ Selector_List_Ptr Eval::operator()(Selector_List_Ptr s)+ {+ std::vector<Selector_List_Obj> rv;+ Selector_List_Obj sl = SASS_MEMORY_NEW(Selector_List, s->pstate());+ sl->is_optional(s->is_optional());+ sl->media_block(s->media_block());+ sl->is_optional(s->is_optional());+ for (size_t i = 0, iL = s->length(); i < iL; ++i) {+ rv.push_back(operator()((*s)[i]));+ }++ // we should actually permutate parent first+ // but here we have permutated the selector first+ size_t round = 0;+ while (round != std::string::npos) {+ bool abort = true;+ for (size_t i = 0, iL = rv.size(); i < iL; ++i) {+ if (rv[i]->length() > round) {+ sl->append((*rv[i])[round]);+ abort = false;+ }+ }+ if (abort) {+ round = std::string::npos;+ } else {+ ++ round;+ }++ }+ return sl.detach();+ }+++ Selector_List_Ptr Eval::operator()(Complex_Selector_Ptr s)+ {+ bool implicit_parent = !exp.old_at_root_without_rule;+ if (is_in_selector_schema) exp.selector_stack.push_back(0);+ Selector_List_Obj resolved = s->resolve_parent_refs(exp.selector_stack, traces, implicit_parent);+ if (is_in_selector_schema) exp.selector_stack.pop_back();+ for (size_t i = 0; i < resolved->length(); i++) {+ Complex_Selector_Ptr is = resolved->at(i)->first();+ while (is) {+ if (is->head()) {+ is->head(operator()(is->head()));+ }+ is = is->tail();+ }+ }+ return resolved.detach();+ }++ Compound_Selector_Ptr Eval::operator()(Compound_Selector_Ptr s)+ {+ for (size_t i = 0; i < s->length(); i++) {+ Simple_Selector_Ptr ss = s->at(i);+ // skip parents here (called via resolve_parent_refs)+ if (ss == NULL || Cast<Parent_Selector>(ss)) continue;+ s->at(i) = Cast<Simple_Selector>(ss->perform(this));+ }+ return s;+ }++ Selector_List_Ptr Eval::operator()(Selector_Schema_Ptr s)+ {+ LOCAL_FLAG(is_in_selector_schema, true);+ // the parser will look for a brace to end the selector+ ctx.c_options.in_selector = true; // do not compress colors+ Expression_Obj sel = s->contents()->perform(this);+ std::string result_str(sel->to_string(ctx.c_options));+ ctx.c_options.in_selector = false; // flag temporary only+ result_str = unquote(Util::rtrim(result_str));+ char* temp_cstr = sass_copy_c_string(result_str.c_str());+ ctx.strings.push_back(temp_cstr); // attach to context+ Parser p = Parser::from_c_str(temp_cstr, ctx, traces, s->pstate());+ p.last_media_block = s->media_block();+ // a selector schema may or may not connect to parent?+ bool chroot = s->connect_parent() == false;+ Selector_List_Obj sl = p.parse_selector_list(chroot);+ auto vec_str_rend = ctx.strings.rend();+ auto vec_str_rbegin = ctx.strings.rbegin();+ // remove the first item searching from the back+ // we cannot assume our item is still the last one+ // order is not important, so we can optimize this+ auto it = std::find(vec_str_rbegin, vec_str_rend, temp_cstr);+ // undefined behavior if not found!+ if (it != vec_str_rend) {+ // overwrite with last item+ *it = ctx.strings.back();+ // remove last one from vector+ ctx.strings.pop_back();+ // free temporary copy+ free(temp_cstr);+ }+ flag_is_in_selector_schema.reset();+ 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());+ }+ }++ Simple_Selector_Ptr Eval::operator()(Simple_Selector_Ptr s)+ {+ return s;+ }++ // hotfix to avoid invalid nested `:not` selectors+ // probably the wrong place, but this should ultimately+ // be fixed by implement superselector correctly for `:not`+ // first use of "find" (ATM only implemented for selectors)+ bool hasNotSelector(AST_Node_Obj obj) {+ if (Wrapped_Selector_Ptr w = Cast<Wrapped_Selector>(obj)) {+ return w->name() == ":not";+ }+ return false;+ }++ Wrapped_Selector_Ptr Eval::operator()(Wrapped_Selector_Ptr s)+ {++ if (s->name() == ":not") {+ if (exp.selector_stack.back()) {+ if (s->selector()->find(hasNotSelector)) {+ s->selector()->clear();+ s->name(" ");+ } else if (s->selector()->length() == 1) {+ Complex_Selector_Ptr cs = s->selector()->at(0);+ if (cs->tail()) {+ s->selector()->clear();+ s->name(" ");+ }+ } else if (s->selector()->length() > 1) {+ s->selector()->clear();+ s->name(" ");+ }+ }+ }+ return s;+ }; }
libsass/src/eval.hpp view
@@ -18,16 +18,20 @@ Expression_Ptr fallback_impl(AST_Node_Ptr n); public:- Expand& exp;+ Expand& exp; Context& ctx;+ Backtraces& traces; Eval(Expand& exp); ~Eval(); bool force; bool is_in_comment;+ bool is_in_selector_schema; + Boolean_Obj bool_true;+ Boolean_Obj bool_false;+ Env* environment();- Backtrace* backtrace(); Selector_List_Obj selector(); // for evaluating function bodies@@ -49,7 +53,6 @@ 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);@@ -57,7 +60,7 @@ Expression_Ptr operator()(String_Quoted_Ptr); Expression_Ptr operator()(String_Constant_Ptr); // Expression_Ptr operator()(Selector_List_Ptr);- Expression_Ptr operator()(Media_Query_Ptr);+ Media_Query_Ptr operator()(Media_Query_Ptr); Expression_Ptr operator()(Media_Query_Expression_Ptr); Expression_Ptr operator()(At_Root_Query_Ptr); Expression_Ptr operator()(Supports_Operator_Ptr);@@ -72,14 +75,15 @@ // these will return selectors 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)- 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; };+ Compound_Selector_Ptr operator()(Compound_Selector_Ptr);+ Simple_Selector_Ptr operator()(Simple_Selector_Ptr s);+ Wrapped_Selector_Ptr operator()(Wrapped_Selector_Ptr s);+ // they don't have any specific implementation (yet)+ // Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; };+ // Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; };+ // Class_Selector_Ptr operator()(Class_Selector_Ptr s) { return s; };+ // Id_Selector_Ptr operator()(Id_Selector_Ptr s) { return s; };+ // Placeholder_Selector_Ptr operator()(Placeholder_Selector_Ptr s) { return s; }; // actual evaluated selectors Selector_List_Ptr operator()(Selector_Schema_Ptr); Expression_Ptr operator()(Parent_Selector_Ptr);@@ -87,22 +91,12 @@ template <typename U> 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_Obj, Expression_Obj);- static bool lt(Expression_Obj, Expression_Obj, std::string op);- // -- arithmetic on the combinations that matter- 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: void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false); }; - Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate = ParserState("[AST]"));+ Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate = ParserState("[AST]")); }
libsass/src/expand.cpp view
@@ -9,14 +9,16 @@ #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp"+#include "sass_functions.hpp" namespace Sass { // simple endless recursion protection const size_t maxRecursion = 500; - Expand::Expand(Context& ctx, Env* env, Backtrace* bt, std::vector<Selector_List_Obj>* stack)+ Expand::Expand(Context& ctx, Env* env, std::vector<Selector_List_Obj>* stack) : ctx(ctx),+ traces(ctx.traces), eval(Eval(*this)), recursions(0), in_keyframes(false),@@ -26,8 +28,7 @@ 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*>())+ media_block_stack(std::vector<Media_Block_Ptr>()) { env_stack.push_back(0); env_stack.push_back(env);@@ -36,15 +37,8 @@ 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); } - Context& Expand::context()- {- return ctx;- }- Env* Expand::environment() { if (env_stack.size() > 0)@@ -59,13 +53,6 @@ return 0; } - Backtrace* Expand::backtrace()- {- if (backtrace_stack.size() > 0)- return backtrace_stack.back();- return 0;- }- // blocks create new variable scopes Block_Ptr Expand::operator()(Block_Ptr b) {@@ -78,7 +65,7 @@ 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!@@ -95,12 +82,14 @@ LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule); if (in_keyframes) {- Block_Ptr bb = operator()(&r->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->name(SASS_MEMORY_CAST_PTR(Selector_List, r->selector()->perform(&eval)));- selector_stack.pop_back();+ if (Selector_List_Ptr s = r->selector()) {+ selector_stack.push_back(0);+ k->name(s->eval(eval));+ selector_stack.pop_back();+ } } return k.detach(); }@@ -108,50 +97,54 @@ // 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_Ptr selector_list = SASS_MEMORY_CAST(Selector_List, r->selector())) {+ // `&` is allowed in `@at-root`!+ bool has_parent_selector = false;+ for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) {+ Selector_List_Obj ll = selector_stack.at(i);+ has_parent_selector = ll != 0 && ll->length() > 0;+ }++ Selector_List_Obj sel = r->selector();+ if (sel) sel = sel->eval(eval);++ // check for parent selectors in base level rules+ if (r->is_root() || (block_stack.back() && block_stack.back()->is_root())) {+ if (Selector_List_Ptr selector_list = Cast<Selector_List>(r->selector())) { for (Complex_Selector_Obj complex_selector : selector_list->elements()) {- Complex_Selector_Ptr tail = &complex_selector;+ Complex_Selector_Ptr tail = complex_selector; while (tail) { if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {- if (SASS_MEMORY_CAST(Parent_Selector, header) == NULL) continue; // skip all others+ Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header);+ if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue; std::string sel_str(complex_selector->to_string(ctx.c_options));- error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace());+ error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), traces); }- tail = &tail->tail();+ tail = tail->tail(); } } } }-- 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");-- 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());+ else {+ if (sel->length() == 0 || sel->has_parent_ref()) {+ if (sel->has_real_parent_ref() && !has_parent_selector) {+ error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), traces);+ } } } - selector_stack.push_back(&sel);+ // do not connect parent again+ sel->remove_parent_selectors();+ selector_stack.push_back(sel); Env env(environment()); if (block_stack.back()->is_root()) { env_stack.push_back(&env); } sel->set_media_block(media_block_stack.back()); Block_Obj blk = 0;- if (r->block()) blk = operator()(&r->block());+ if (r->block()) blk = operator()(r->block()); Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset, r->pstate(),- &sel,+ sel, blk); selector_stack.pop_back(); if (block_stack.back()->is_root()) {@@ -169,27 +162,33 @@ Expression_Obj condition = f->condition()->perform(&eval); Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block, f->pstate(),- SASS_MEMORY_CAST(Supports_Condition, condition),- operator()(&f->block()));+ Cast<Supports_Condition>(condition),+ operator()(f->block())); return ff.detach(); } Statement_Ptr Expand::operator()(Media_Block_Ptr m) {- media_block_stack.push_back(m);- Expression_Obj mq = m->media_queries()->perform(&eval);+ Media_Block_Obj cpy = SASS_MEMORY_COPY(m);+ // Media_Blocks are prone to have circular references+ // Copy could leak memory if it does not get picked up+ // Looks like we are able to reset block reference for copy+ // Good as it will ensure a low memory overhead for this fix+ // So this is a cheap solution with a minimal price+ ctx.ast_gc.push_back(cpy); cpy->block(0);+ Expression_Obj mq = eval(m->media_queries()); std::string str_mq(mq->to_string(ctx.c_options)); 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());+ Parser p(Parser::from_c_str(str, ctx, traces, mq->pstate()));+ mq = p.parse_media_queries(); // re-assign now+ cpy->media_queries(mq);+ media_block_stack.push_back(cpy);+ Block_Obj blk = operator()(m->block()); Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block, m->pstate(),- ls,- blk,- 0);+ mq,+ blk); media_block_stack.pop_back(); mm->tabs(m->tabs()); return mm;@@ -198,7 +197,7 @@ Statement_Ptr Expand::operator()(At_Root_Block_Ptr a) { Block_Obj ab = a->block();- Expression_Obj ae = &a->expression();+ Expression_Obj ae = a->expression(); if (ae) ae = ae->perform(&eval); else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());@@ -208,23 +207,23 @@ ; - Block_Obj bb = ab ? operator()(&ab) : NULL;+ Block_Obj bb = ab ? operator()(ab) : NULL; At_Root_Block_Obj aa = SASS_MEMORY_NEW(At_Root_Block, a->pstate(), bb,- SASS_MEMORY_CAST(At_Root_Query, ae));+ Cast<At_Root_Query>(ae)); return aa.detach(); } Statement_Ptr Expand::operator()(Directive_Ptr a) { LOCAL_FLAG(in_keyframes, a->is_keyframes());- Block_Ptr ab = &a->block();- Selector_Ptr as = &a->selector();- Expression_Ptr av = &a->value();+ Block_Ptr ab = a->block();+ Selector_List_Ptr as = a->selector();+ Expression_Ptr av = a->value(); selector_stack.push_back(0); if (av) av = av->perform(&eval);- if (as) as = SASS_MEMORY_CAST_PTR(Selector, as->perform(&eval));+ if (as) as = eval(as); selector_stack.pop_back(); Block_Ptr bb = ab ? operator()(ab) : NULL; Directive_Ptr aa = SASS_MEMORY_NEW(Directive,@@ -241,14 +240,14 @@ 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);+ String_Obj new_p = 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;+ Block_Obj bb = ab ? operator()(ab) : NULL; if (!bb) { if (!value || (value->is_invisible() && !d->is_important())) return 0; }@@ -257,6 +256,7 @@ new_p, value, d->is_important(),+ d->is_custom_property(), bb); decl->tabs(d->tabs()); return decl;@@ -265,11 +265,11 @@ Statement_Ptr Expand::operator()(Assignment_Ptr a) { Env* env = environment();- std::string var(a->variable());+ const std::string& var(a->variable()); if (a->is_global()) { if (a->is_default()) { if (env->has_global(var)) {- Expression_Obj e = SASS_MEMORY_CAST(Expression, env->get_global(var));+ Expression_Obj e = Cast<Expression>(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); }@@ -288,7 +288,7 @@ while (cur && cur->is_lexical()) { if (cur->has_local(var)) { if (AST_Node_Obj node = cur->get_local(var)) {- Expression_Obj e = SASS_MEMORY_CAST(Expression, node);+ Expression_Obj e = Cast<Expression>(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(&eval)); }@@ -304,7 +304,7 @@ } else if (env->has_global(var)) { if (AST_Node_Obj node = env->get_global(var)) {- Expression_Obj e = SASS_MEMORY_CAST(Expression, node);+ Expression_Obj e = Cast<Expression>(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); }@@ -328,7 +328,7 @@ 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));+ result->import_queries(Cast<List>(ex)); } for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { result->urls().push_back(imp->urls()[i]->perform(&eval));@@ -340,10 +340,11 @@ Statement_Ptr Expand::operator()(Import_Stub_Ptr i) {+ traces.push_back(Backtrace(i->pstate())); // 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());+ if (Cast<Block>(parent) == NULL) {+ error("Import directives may not be used within control directives or mixins.", i->pstate(), traces); } // we don't seem to need that actually afterall Sass_Import_Entry import = sass_make_import(@@ -352,10 +353,18 @@ 0, 0 ); ctx.import_stack.push_back(import);++ Block_Obj trace_block = SASS_MEMORY_NEW(Block, i->pstate());+ Trace_Obj trace = SASS_MEMORY_NEW(Trace, i->pstate(), i->imp_path(), trace_block, 'i');+ block_stack.back()->append(trace);+ block_stack.push_back(trace_block);+ 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();+ block_stack.pop_back();+ traces.pop_back(); return 0; } @@ -382,8 +391,13 @@ Statement_Ptr Expand::operator()(Comment_Ptr c) {+ if (ctx.output_style() == COMPRESSED) {+ // comments should not be evaluated in compact+ // https://github.com/sass/libsass/issues/2359+ if (!c->is_important()) return NULL;+ } 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());+ Comment_Ptr rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important()); eval.is_in_comment = false; // TODO: eval the text, once we're parsing/storing it as a String_Schema return rv;@@ -396,10 +410,10 @@ call_stack.push_back(i); Expression_Obj rv = i->predicate()->perform(&eval); if (*rv) {- append_block(&i->block());+ append_block(i->block()); } else {- Block_Ptr alt = &i->alternative();+ Block_Ptr alt = i->alternative(); if (alt) append_block(alt); } call_stack.pop_back();@@ -414,20 +428,22 @@ std::string variable(f->variable()); Expression_Obj low = f->lower_bound()->perform(&eval); if (low->concrete_type() != Expression::NUMBER) {- throw Exception::TypeMismatch(*low, "integer");+ traces.push_back(Backtrace(low->pstate()));+ throw Exception::TypeMismatch(traces, *low, "integer"); } Expression_Obj high = f->upper_bound()->perform(&eval); if (high->concrete_type() != Expression::NUMBER) {- throw Exception::TypeMismatch(*high, "integer");+ traces.push_back(Backtrace(high->pstate()));+ throw Exception::TypeMismatch(traces, *high, "integer"); }- Number_Obj sass_start = SASS_MEMORY_CAST(Number, low);- Number_Obj sass_end = SASS_MEMORY_CAST(Number, high);+ Number_Obj sass_start = Cast<Number>(low);+ Number_Obj sass_end = 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());+ error(msg.str(), low->pstate(), traces); } double start = sass_start->value(); double end = sass_end->value();@@ -435,17 +451,14 @@ 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();+ 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);+ Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());+ env.set_local(variable, it); append_block(body); } } else {@@ -453,9 +466,8 @@ for (double i = start; i > end; --i) {- it = SASS_MEMORY_COPY(it);- it->value(i);- env.set_local(variable, &it);+ Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());+ env.set_local(variable, it); append_block(body); } }@@ -473,25 +485,25 @@ List_Obj list = 0; Map_Obj map; if (expr->concrete_type() == Expression::MAP) {- map = SASS_MEMORY_CAST(Map, expr);+ map = Cast<Map>(expr); }- else if (Selector_List_Ptr ls = SASS_MEMORY_CAST(Selector_List, expr)) {+ else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) { Listize listize; Expression_Obj rv = ls->perform(&listize);- list = SASS_MEMORY_CAST(List, rv);+ list = Cast<List>(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);+ list = Cast<List>(expr); } // remember variables and then reset them Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(e);- Block_Ptr body = &e->block();+ Block_Ptr body = e->block(); if (map) { for (auto key : map->keys()) {@@ -502,43 +514,43 @@ List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE); variable->append(k); variable->append(v);- env.set_local(variables[0], &variable);+ 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();- if (list->length() == 1 && SASS_MEMORY_CAST(Selector_List, list)) {- list = SASS_MEMORY_CAST(List, list);+ if (list->length() == 1 && Cast<Selector_List>(list)) {+ list = Cast<List>(list); } for (size_t i = 0, L = list->length(); i < L; ++i) {- Expression_Obj e = list->at(i);+ Expression_Obj item = list->at(i); // unwrap value if the expression is an argument- if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, e)) e = arg->value();+ if (Argument_Obj arg = Cast<Argument>(item)) item = arg->value(); // check if we got passed a list of args (investigate)- if (List_Obj scalars = SASS_MEMORY_CAST(List, e)) {+ if (List_Obj scalars = Cast<List>(item)) { if (variables.size() == 1) { List_Obj var = scalars; // if (arglist) var = (*scalars)[0];- env.set_local(variables[0], &var);+ env.set_local(variables[0], var); } else { for (size_t j = 0, K = variables.size(); j < K; ++j) { 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.at(0), &e);+ env.set_local(variables.at(0), item); for (size_t j = 1, K = variables.size(); j < K; ++j) { Expression_Obj res = SASS_MEMORY_NEW(Null, expr->pstate());- env.set_local(variables[j], &res);+ env.set_local(variables[j], res); } } }@@ -553,12 +565,12 @@ Statement_Ptr Expand::operator()(While_Ptr w) { Expression_Obj pred = w->predicate();- Block_Ptr body = &w->block();+ 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) {+ while (!cond->is_false()) { append_block(body); cond = pred->perform(&eval); }@@ -569,21 +581,21 @@ Statement_Ptr Expand::operator()(Return_Ptr r) {- error("@return may only be used within a function", r->pstate(), backtrace());+ error("@return may only be used within a function", r->pstate(), traces); return 0; } void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) { - if (Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, s)) {+ if (Selector_List_Obj sl = Cast<Selector_List>(s)) { for (Complex_Selector_Obj complex_selector : sl->elements()) { Complex_Selector_Obj tail = complex_selector; while (tail) { if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {- if (SASS_MEMORY_CAST(Parent_Selector, header) == NULL) continue; // skip all others+ if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others std::string sel_str(complex_selector->to_string(ctx.c_options));- error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace());+ error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), traces); } tail = tail->tail(); }@@ -591,20 +603,20 @@ } - Selector_List_Obj contextualized = SASS_MEMORY_CAST_PTR(Selector_List, s->perform(&eval));+ Selector_List_Obj contextualized = Cast<Selector_List>(s->perform(&eval)); if (contextualized == false) return; for (auto complex_sel : contextualized->elements()) { Complex_Selector_Obj c = complex_sel; if (!c->head() || c->tail()) { std::string sel_str(contextualized->to_string(ctx.c_options));- error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace());+ error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), traces); }- Compound_Selector_Obj placeholder = c->head();- if (contextualized->is_optional()) placeholder->is_optional(true);+ Compound_Selector_Obj target = c->head();+ if (contextualized->is_optional()) target->is_optional(true); for (size_t i = 0, L = extender->length(); i < L; ++i) { Complex_Selector_Obj sel = (*extender)[i]; if (!(sel->head() && sel->head()->length() > 0 &&- SASS_MEMORY_CAST(Parent_Selector, (*sel->head())[0])))+ Cast<Parent_Selector>((*sel->head())[0]))) { Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate()); hh->media_block((*extender)[i]->media_block());@@ -613,13 +625,13 @@ if (sel->has_line_feed()) ssel->has_line_feed(true); Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate()); ps->media_block((*extender)[i]->media_block());- hh->append(&ps);+ 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, std::make_pair(sel, placeholder));+ ctx.subset_map.put(target, std::make_pair(sel, target)); } } @@ -627,31 +639,30 @@ 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 (Selector_List_Ptr extender = selector()) {+ Selector_List_Ptr sl = e->selector();+ // abort on invalid selector+ if (sl == NULL) return NULL;+ if (Selector_Schema_Ptr schema = sl->schema()) { if (schema->has_real_parent_ref()) {- sl = eval(&schema);+ // put root block on stack again (ignore parents)+ // selector schema must not connect in eval!+ block_stack.push_back(block_stack.at(1));+ sl = eval(sl->schema());+ block_stack.pop_back(); } else { selector_stack.push_back(0);- sl = eval(&schema);- sl->remove_parent_selectors();+ sl = eval(sl->schema()); 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);+ expand_selector_list(sl, extender); selector_stack.pop_back(); } return 0;@@ -662,7 +673,7 @@ Env* env = environment(); Definition_Obj dd = SASS_MEMORY_COPY(d); env->local_frame()[d->name() +- (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = ⅆ+ (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd; if (d->type() == Definition::FUNCTION && ( Prelexer::calc_fn_call(d->name().c_str()) ||@@ -671,9 +682,9 @@ d->name() == "url" )) { deprecated(- "Naming a function \"" + d->name() + "\" is disallowed",+ "Naming a function \"" + d->name() + "\" is disallowed and will be an error in future versions of Sass.", "This name conflicts with an existing CSS function with special parse rules.",- d->pstate()+ false, d->pstate() ); } @@ -684,9 +695,8 @@ Statement_Ptr Expand::operator()(Mixin_Call_Ptr c) {- if (recursions > maxRecursion) {- throw Exception::StackError(*c);+ throw Exception::StackError(traces, *c); } recursions ++;@@ -694,19 +704,28 @@ Env* env = environment(); std::string full_name(c->name() + "[m]"); if (!env->has(full_name)) {- error("no mixin named " + c->name(), c->pstate(), backtrace());+ error("no mixin named " + c->name(), c->pstate(), traces); }- Definition_Obj def = SASS_MEMORY_CAST(Definition, (*env)[full_name]);+ Definition_Obj def = 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());+ error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), traces); } 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);+ Arguments_Obj args = Cast<Arguments>(rv);+ std::string msg(", in mixin `" + c->name() + "`");+ traces.push_back(Backtrace(c->pstate(), msg));+ ctx.callee_stack.push_back({+ c->name().c_str(),+ c->pstate().path,+ c->pstate().line + 1,+ c->pstate().column + 1,+ SASS_CALLEE_MIXIN,+ { env }+ });+ Env new_env(def->environment()); env_stack.push_back(&new_env); if (c->block()) {@@ -718,7 +737,7 @@ 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);@@ -726,16 +745,24 @@ 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);+ env->set_global("is_in_mixin", bool_true);+ if (Block_Ptr pr = block_stack.back()) {+ trace_block->is_root(pr->is_root());+ }+ block_stack.push_back(trace_block); for (auto bb : body->elements()) {+ if (Ruleset_Ptr r = Cast<Ruleset>(bb)) {+ r->is_root(trace_block->is_root());+ } Statement_Obj ith = bb->perform(this); if (ith) trace->block()->append(ith); } block_stack.pop_back();+ env->del_global("is_in_mixin"); + ctx.callee_stack.pop_back(); env_stack.pop_back();- backtrace_stack.pop_back();+ traces.pop_back(); recursions --; return trace.detach();@@ -756,7 +783,7 @@ "@content", SASS_MEMORY_NEW(Arguments, c->pstate())); - Trace_Obj trace = SASS_MEMORY_CAST_PTR(Trace, call->perform(this));+ Trace_Obj trace = Cast<Trace>(call->perform(this)); if (block_stack.back()->is_root()) { selector_stack.pop_back();@@ -770,8 +797,8 @@ { std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name(); String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err);- error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace());- return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), &msg);+ error("unknown internal error; please contact the LibSass maintainers", n->pstate(), traces);+ return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg); } // process and add to last block on stack@@ -779,7 +806,7 @@ { if (b->is_root()) call_stack.push_back(b); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Ptr stm = &b->at(i);+ Statement_Ptr stm = b->at(i); Statement_Obj ith = stm->perform(this); if (ith) block_stack.back()->append(ith); }
libsass/src/expand.hpp view
@@ -13,18 +13,16 @@ class Listize; class Context; class Eval;- typedef Environment<AST_Node_Obj> Env; struct Backtrace; class Expand : public Operation_CRTP<Statement_Ptr, Expand> { public: Env* environment();- Context& context(); Selector_List_Obj selector();- Backtrace* backtrace(); Context& ctx;+ Backtraces& traces; Eval eval; size_t recursions; bool in_keyframes;@@ -37,15 +35,16 @@ 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; + Boolean_Obj bool_true;+ Statement_Ptr fallback_impl(AST_Node_Ptr n); private: void expand_selector_list(Selector_Obj, Selector_List_Obj extender); public:- Expand(Context&, Env*, Backtrace*, std::vector<Selector_List_Obj>* stack = NULL);+ Expand(Context&, Env*, std::vector<Selector_List_Obj>* stack = NULL); ~Expand() { } Block_Ptr operator()(Block_Ptr);
libsass/src/extend.cpp view
@@ -4,6 +4,7 @@ #include "backtrace.hpp" #include "paths.hpp" #include "parser.hpp"+#include "expand.hpp" #include "node.hpp" #include "sass_util.hpp" #include "remove_placeholders.hpp"@@ -61,8 +62,6 @@ namespace Sass { - typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> ExtensionPair;- typedef std::vector<ExtensionPair> SubsetMapEntries; #ifdef DEBUG @@ -112,11 +111,6 @@ } // Print a string representation of a Compound_Selector- 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_Ptr pCompoundSelector, const char* message=NULL, bool newline=true) { if (message) {@@ -186,14 +180,14 @@ } } - static void printSelsNewSeqPairCollection(SelsNewSeqPairCollection& collection, const char* message=NULL, bool newline=true) {+ static void printSelsNewSeqPairCollection(SubSetMapLookups& collection, const char* message=NULL, bool newline=true) { if (message) { std::cerr << message; } bool first = true; std::cerr << "[";- for(SelsNewSeqPair& pair : collection) {+ for(SubSetMapLookup& pair : collection) { if (first) { first = false; } else {@@ -212,8 +206,8 @@ } } - // Print a string representation of a SourcesSet- static void printSourcesSet(SourcesSet& sources, Context& ctx, const char* message=NULL, bool newline=true) {+ // Print a string representation of a ComplexSelectorSet+ static void printSourcesSet(ComplexSelectorSet& sources, const char* message=NULL, bool newline=true) { if (message) { std::cerr << message;@@ -223,17 +217,17 @@ // the differences we see when debug printing. typedef std::deque<std::string> SourceStrings; SourceStrings sourceStrings;- for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {+ for (ComplexSelectorSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) { Complex_Selector_Ptr pSource = *iterator; std::stringstream sstream;- sstream << complexSelectorToNode(pSource, ctx);+ sstream << complexSelectorToNode(pSource); sourceStrings.push_back(sstream.str()); } // Sort to get consistent output std::sort(sourceStrings.begin(), sourceStrings.end()); - std::cerr << "SourcesSet[";+ std::cerr << "ComplexSelectorSet["; for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) { std::string source = *iterator; if (iterator != sourceStrings.begin()) {@@ -249,10 +243,10 @@ } - std::ostream& operator<<(std::ostream& os, SubsetMapEntries& entries) {+ std::ostream& operator<<(std::ostream& os, SubSetMapPairs& entries) { os << "SUBSET_MAP_ENTRIES["; - for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {+ for (SubSetMapPairs::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) { Complex_Selector_Obj pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge) Compound_Selector_Obj pExtCompoundSelector = iterator->second; // The stuff after the @extend @@ -286,17 +280,16 @@ } #endif - static bool parentSuperselector(Complex_Selector_Ptr pOne, Complex_Selector_Ptr pTwo, Context& ctx) {+ static bool parentSuperselector(Complex_Selector_Ptr pOne, Complex_Selector_Ptr pTwo) { // TODO: figure out a better way to create a Complex_Selector from scratch // TODO: There's got to be a better way. This got ugly quick...- Position noPosition(-1, -1, -1); 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*/);+ 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);+ pOne->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);+ pTwo->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF); bool isSuperselector = pOne->is_superselector_of(pTwo); @@ -306,19 +299,19 @@ return isSuperselector; } - void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out, Context& ctx) {+ void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out) { for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { Node& child = *iter;- out.push_back(nodeToComplexSelector(child, ctx));+ out.push_back(nodeToComplexSelector(child)); } } - Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque, Context& ctx) {+ Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque) { Node result = Node::createCollection(); for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) { Complex_Selector_Obj pChild = *iter;- result.collection()->push_back(complexSelectorToNode(&pChild, ctx));+ result.collection()->push_back(complexSelectorToNode(pChild)); } return result;@@ -326,9 +319,7 @@ class LcsCollectionComparator { public:- LcsCollectionComparator(Context& ctx) : mCtx(ctx) {}-- Context& mCtx;+ LcsCollectionComparator() {} bool operator()(Complex_Selector_Obj pOne, Complex_Selector_Obj pTwo, Complex_Selector_Obj& pOut) const { /*@@ -341,7 +332,7 @@ end */ - if (selectors_equal(*pOne, *pTwo, true /*simpleSelectorOrderDependent*/)) {+ if (*pOne == *pTwo) { pOut = pOne; return true; }@@ -350,12 +341,12 @@ return false; } - if (parentSuperselector(&pOne, &pTwo, mCtx)) {+ if (parentSuperselector(pOne, pTwo)) { pOut = pTwo; return true; } - if (parentSuperselector(&pTwo, &pOne, mCtx)) {+ if (parentSuperselector(pTwo, pOne)) { pOut = pOne; return true; }@@ -420,7 +411,7 @@ for (size_t j = 1; j < y.size(); j++) { 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]);@@ -445,7 +436,7 @@ http://en.wikipedia.org/wiki/Longest_common_subsequence_problem */- void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, Context& ctx, ComplexSelectorDeque& out) {+ void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) { //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y) // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output @@ -525,7 +516,7 @@ /* - IMPROVEMENT: We could probably work directly in the output trimmed deque. */- static Node trim(Node& seqses, Context& ctx, bool isReplace) {+ Node Extend::trim(Node& seqses, bool isReplace) { // See the comments in the above ruby code before embarking on understanding this function. // Avoid poor performance in extreme cases.@@ -558,7 +549,7 @@ for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) { Node& seq1 = *seqs1Iter; - Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1, ctx);+ Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1); // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code // for a good description of sources.@@ -569,12 +560,12 @@ // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely // a guess though. unsigned long maxSpecificity = isReplace ? pSeq1->specificity() : 0;- SourcesSet sources = pSeq1->sources();+ ComplexSelectorSet sources = pSeq1->sources(); DEBUG_PRINTLN(TRIM, "TRIM SEQ1: " << seq1)- DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIM SOURCES: "))+ DEBUG_EXEC(TRIM, printSourcesSet(sources, "TRIM SOURCES: ")) - for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {+ for (ComplexSelectorSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) { const Complex_Selector_Obj& pCurrentSelector = *sourcesSetIterator; maxSpecificity = std::max(maxSpecificity, pCurrentSelector->specificity()); }@@ -605,7 +596,7 @@ for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) { Node& seq2 = *seqs2Iter; - Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2, ctx);+ Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2); DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity()) DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))@@ -649,19 +640,18 @@ - static bool parentSuperselector(const Node& one, const Node& two, Context& ctx) {+ static bool parentSuperselector(const Node& one, const Node& two) { // TODO: figure out a better way to create a Complex_Selector from scratch // TODO: There's got to be a better way. This got ugly quick...- Position noPosition(-1, -1, -1); 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*/);+ fakeHead->elements().push_back(fakeParent);+ Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, NULL /*tail*/); - Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one, ctx);- pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);- Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two, ctx);- pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);+ Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one);+ pOneWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);+ Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two);+ pTwoWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF); return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent); }@@ -669,14 +659,13 @@ class ParentSuperselectorChunker { public:- ParentSuperselectorChunker(Node& lcs, Context& ctx) : mLcs(lcs), mCtx(ctx) {}+ ParentSuperselectorChunker(Node& lcs) : mLcs(lcs) {} Node& mLcs;- Context& mCtx; bool operator()(const Node& seq) const { // {|s| parent_superselector?(s.first, lcs.first)} if (seq.collection()->size() == 0) return false;- return parentSuperselector(seq.collection()->front(), mLcs.collection()->front(), mCtx);+ return parentSuperselector(seq.collection()->front(), mLcs.collection()->front()); } }; @@ -730,7 +719,7 @@ } Node chunk2 = Node::createCollection();- while (!chunker(seq2)) {+ while (!seq2.collection()->empty() && !chunker(seq2)) { chunk2.collection()->push_back(seq2.collection()->front()); seq2.collection()->pop_front(); }@@ -772,7 +761,7 @@ } - static Node groupSelectors(Node& seq, Context& ctx) {+ static Node groupSelectors(Node& seq) { Node newSeq = Node::createCollection(); Node tail = Node::createCollection();@@ -832,7 +821,7 @@ return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2) end */- static Node mergeInitialOps(Node& seq1, Node& seq2, Context& ctx) {+ static Node mergeInitialOps(Node& seq1, Node& seq2) { Node ops1 = Node::createCollection(); Node ops2 = Node::createCollection(); @@ -846,9 +835,9 @@ // If neither sequence is a subsequence of the other, they cannot be merged successfully DefaultLcsComparator lcsDefaultComparator;- Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx);+ Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator); - if (!(nodesEqual(opsLcs, ops1, true) || nodesEqual(opsLcs, ops2, true))) {+ if (!(opsLcs == ops1 || opsLcs == ops2)) { return Node::createNil(); } @@ -923,7 +912,7 @@ end end */- static Node mergeFinalOps(Node& seq1, Node& seq2, Context& ctx, Node& res) {+ static Node mergeFinalOps(Node& seq1, Node& seq2, Node& res) { Node ops1 = Node::createCollection(); Node ops2 = Node::createCollection();@@ -941,11 +930,11 @@ if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) { DefaultLcsComparator lcsDefaultComparator;- Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx);+ Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator); // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up. - if (!(nodesEqual(opsLcs, ops1, true) || nodesEqual(opsLcs, ops2, true))) {+ if (!(opsLcs == ops1 || opsLcs == ops2)) { return Node::createNil(); } @@ -988,7 +977,7 @@ Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)- Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(&sel2.selector()->head(), ctx);+ Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head()); pMergedWrapper->head(pMerged); DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1011,7 +1000,7 @@ if (pMerged) { Node mergedPerm = Node::createCollection();- mergedPerm.collection()->push_back(Node::createSelector(&pMergedWrapper, ctx));+ mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper)); mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); newRes.collection()->push_back(mergedPerm); }@@ -1025,12 +1014,10 @@ } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) { Node tildeSel = sel1;- Node tildeOp = op1; Node plusSel = sel2; Node plusOp = op2; if (op1.combinator() != Complex_Selector::PRECEDES) { tildeSel = sel2;- tildeOp = op2; plusSel = sel1; plusOp = op1; }@@ -1047,7 +1034,7 @@ Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(plusSel.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)- Compound_Selector_Ptr pMerged = plusSel.selector()->head()->unify_with(&tildeSel.selector()->head(), ctx);+ Compound_Selector_Ptr pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head()); pMergedWrapper->head(pMerged); DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1063,7 +1050,7 @@ if (pMerged) { Node mergedPerm = Node::createCollection();- mergedPerm.collection()->push_back(Node::createSelector(&pMergedWrapper, ctx));+ mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper)); mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO)); newRes.collection()->push_back(mergedPerm); }@@ -1096,7 +1083,7 @@ Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)- Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(&sel2.selector()->head(), ctx);+ Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head()); pMergedWrapper->head(pMerged); DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1106,7 +1093,7 @@ } res.collection()->push_front(op1);- res.collection()->push_front(Node::createSelector(&pMergedWrapper, ctx));+ res.collection()->push_front(Node::createSelector(pMergedWrapper)); DEBUG_PRINTLN(ALL, "RESULT: " << res) @@ -1114,7 +1101,7 @@ return Node::createNil(); } - return mergeFinalOps(seq1, seq2, ctx, res);+ return mergeFinalOps(seq1, seq2, res); } else if (!ops1.collection()->empty()) { @@ -1129,7 +1116,7 @@ res.collection()->push_front(seq1.collection()->back()); seq1.collection()->pop_back(); - return mergeFinalOps(seq1, seq2, ctx, res);+ return mergeFinalOps(seq1, seq2, res); } else { // !ops2.collection()->empty() @@ -1143,7 +1130,7 @@ res.collection()->push_front(seq2.collection()->back()); seq2.collection()->pop_back(); - return mergeFinalOps(seq1, seq2, ctx, res);+ return mergeFinalOps(seq1, seq2, res); } @@ -1186,7 +1173,7 @@ result end */- Node Extend::subweave(Node& one, Node& two, Context& ctx) {+ Node subweave(Node& one, Node& two) { // Check for the simple cases if (one.collection()->size() == 0) { Node out = Node::createCollection();@@ -1199,8 +1186,6 @@ return out; } -- Node seq1 = Node::createCollection(); seq1.plus(one); Node seq2 = Node::createCollection();@@ -1209,7 +1194,7 @@ DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1) DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2) - Node init = mergeInitialOps(seq1, seq2, ctx);+ Node init = mergeInitialOps(seq1, seq2); if (init.isNil()) { return Node::createNil(); }@@ -1217,7 +1202,7 @@ DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init) Node res = Node::createCollection();- Node fin = mergeFinalOps(seq1, seq2, ctx, res);+ Node fin = mergeFinalOps(seq1, seq2, res); if (fin.isNil()) { return Node::createNil(); }@@ -1245,23 +1230,23 @@ - Node groupSeq1 = groupSelectors(seq1, ctx);+ Node groupSeq1 = groupSelectors(seq1); DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1) - Node groupSeq2 = groupSelectors(seq2, ctx);+ Node groupSeq2 = groupSelectors(seq2); DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2) ComplexSelectorDeque groupSeq1Converted;- nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted, ctx);+ nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted); ComplexSelectorDeque groupSeq2Converted;- nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted, ctx);+ nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted); ComplexSelectorDeque out;- LcsCollectionComparator collectionComparator(ctx);- lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, ctx, out);- Node seqLcs = complexSelectorDequeToNode(out, ctx);+ LcsCollectionComparator collectionComparator;+ lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, out);+ Node seqLcs = complexSelectorDequeToNode(out); DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs) @@ -1275,7 +1260,7 @@ while (!seqLcs.collection()->empty()) {- ParentSuperselectorChunker superselectorChunker(seqLcs, ctx);+ ParentSuperselectorChunker superselectorChunker(seqLcs); Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker); diff.collection()->push_back(chunksResult); @@ -1320,7 +1305,7 @@ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff) - Node pathsResult = paths(diff, ctx);+ Node pathsResult = paths(diff); DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult) @@ -1330,7 +1315,7 @@ pathsIter != pathsEndIter; ++pathsIter) { Node& child = *pathsIter;- child = flatten(child, ctx);+ child = flatten(child); } DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult)@@ -1348,25 +1333,25 @@ } /* // disabled to avoid clang warning [-Wunused-function]- static Node subweaveNaive(const Node& one, const Node& two, Context& ctx) {+ static Node subweaveNaive(const Node& one, const Node& two) { Node out = Node::createCollection(); // Check for the simple cases if (one.isNil()) {- out.collection()->push_back(two.klone(ctx));+ out.collection()->push_back(two.klone()); } else if (two.isNil()) {- out.collection()->push_back(one.klone(ctx));+ out.collection()->push_back(one.klone()); } else { // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B // See https://gist.github.com/nex3/7609394 for details. - Node firstPerm = one.klone(ctx);- Node twoCloned = two.klone(ctx);+ Node firstPerm = one.klone();+ Node twoCloned = two.klone(); firstPerm.plus(twoCloned); out.collection()->push_back(firstPerm); - Node secondPerm = two.klone(ctx);- Node oneCloned = one.klone(ctx);+ Node secondPerm = two.klone();+ Node oneCloned = one.klone(); secondPerm.plus(oneCloned ); out.collection()->push_back(secondPerm); }@@ -1449,7 +1434,7 @@ return befores end */- static Node weave(Node& path, Context& ctx) {+ Node Extend::weave(Node& path) { DEBUG_PRINTLN(WEAVE, "WEAVE: " << path) @@ -1460,7 +1445,7 @@ afters.plus(path); while (!afters.collection()->empty()) {- Node current = afters.collection()->front().klone(ctx);+ Node current = afters.collection()->front().klone(); afters.collection()->pop_front(); DEBUG_PRINTLN(WEAVE, "CURRENT: " << current) if (current.collection()->size() == 0) continue;@@ -1476,7 +1461,7 @@ for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) { Node& before = *beforesIter; - Node sub = Extend::subweave(before, current, ctx);+ Node sub = subweave(before, current); DEBUG_PRINTLN(WEAVE, "SUB: " << sub) @@ -1491,6 +1476,13 @@ toPush.plus(seqs); toPush.plus(last_current); + // move line feed from inner to outer selector (very hacky indeed)+ if (last_current.collection() && last_current.collection()->front().selector()) {+ toPush.got_line_feed = last_current.collection()->front().got_line_feed;+ last_current.collection()->front().selector()->has_line_feed(false);+ last_current.collection()->front().got_line_feed = false;+ }+ tempResult.collection()->push_back(toPush); }@@ -1505,16 +1497,6 @@ - // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively- // call extendComplexSelector again.- static Node extendComplexSelector(- Complex_Selector_Ptr pComplexSelector,- Context& ctx,- Subset_Map& subset_map,- std::set<Compound_Selector> seen, bool isReplace, bool isOriginal);--- /* This is the equivalent of ruby's SimpleSequence.do_extend. @@ -1530,65 +1512,60 @@ template<typename KeyType> class GroupByToAFunctor { public:- KeyType operator()(ExtensionPair& extPair) const {+ KeyType operator()(SubSetMapPair& extPair) const { Complex_Selector_Obj pSelector = extPair.first;- return &pSelector;+ return pSelector; } };- static Node extendCompoundSelector(- Compound_Selector_Ptr pSelector,- Context& ctx,- Subset_Map& subset_map,- std::set<Compound_Selector> seen, bool isReplace) {+ Node Extend::extendCompoundSelector(Compound_Selector_Ptr pSelector, CompoundSelectorSet& seen, bool isReplace) { + /* this turned out to be too much overhead+ probably due to holding a "Node" object+ // check if we already extended this selector+ // we can do this since subset_map is "static"+ auto memoized = memoizeCompound.find(pSelector);+ if (memoized != memoizeCompound.end()) {+ return memoized->second.klone();+ }+ */+ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: ")) // TODO: Ruby has another loop here to skip certain members? - Node extendedSelectors = Node::createCollection();+ // let RESULTS be an empty list of complex selectors+ Node results = Node::createCollection(); // extendedSelectors.got_line_feed = true; - SubsetMapEntries entries = subset_map.get_v(pSelector);-- typedef std::vector<std::pair<Complex_Selector_Obj, std::vector<ExtensionPair> > > GroupedByToAResult;+ SubSetMapPairs entries = subset_map.get_v(pSelector); GroupByToAFunctor<Complex_Selector_Obj> extPairKeyFunctor;- GroupedByToAResult arr;+ SubSetMapResults arr; group_by_to_a(entries, extPairKeyFunctor, arr); - typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SelsNewSeqPair;- typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection;--- SelsNewSeqPairCollection holder;-+ SubSetMapLookups holder; - for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) {- std::pair<Complex_Selector_Obj, std::vector<ExtensionPair> >& groupedPair = *groupedIter;+ // for each (EXTENDER, TARGET) in MAP.get(COMPOUND):+ for (SubSetMapResult& groupedPair : arr) { Complex_Selector_Obj seq = groupedPair.first;- std::vector<ExtensionPair>& group = groupedPair.second;+ SubSetMapPairs& group = groupedPair.second; - DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: "))+ DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(seq, "SEQ: ")) -// 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_Obj pCompound = pair.second;- for (size_t index = 0; index < pCompound->length(); index++) {- Simple_Selector_Obj pSimpleSelector = (*pCompound)[index];- pSels->append(&pSimpleSelector);- pCompound->extended(true);- }+ for (SubSetMapPair& pair : group) {+ pair.second->extended(true);+ pSels->concat(pair.second); } - DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(&pSels, "SELS: "))+ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) - Complex_Selector_Ptr pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge)+ // The selector up to where the @extend is (ie, the thing to merge)+ Complex_Selector_Ptr pExtComplexSelector = seq; // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL? // RUBY: self_without_sel = Sass::Util.array_minus(members, sels)- Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(&pSels, ctx);+ Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(pSels); DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: "))@@ -1598,7 +1575,7 @@ if (!pInnermostCompoundSelector) { pInnermostCompoundSelector = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate()); }- Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(&pSelectorWithoutExtendSelectors, ctx);+ Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors); DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))@@ -1621,10 +1598,10 @@ 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;+ ComplexSelectorSet debugSet; debugSet = pNewSelector->sources(); if (debugSet.size() > 0) { throw std::runtime_error("The new selector should start with no sources. Something needs to be cloned to fix this.");@@ -1638,21 +1615,22 @@ // if (pSelector && pSelector->has_line_feed()) pNewInnerMost->has_line_feed(true); // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending.- DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector, ctx))+ DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector)) - DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, ctx, "SOURCES NEW SEQ BEGIN: "))+ DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, "SOURCES NEW SEQ BEGIN: ")) - SourcesSet newSourcesSet = pSelector->sources();- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES THIS EXTEND: "))+ // I actually want to create a copy here (performance!)+ ComplexSelectorSet newSourcesSet = pSelector->sources(); // XXX+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES THIS EXTEND: ")) newSourcesSet.insert(pExtComplexSelector);- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES WITH NEW SOURCE: "))+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES WITH NEW SOURCE: ")) // RUBY: new_seq.add_sources!(sources + [seq])- pNewSelector->addSources(newSourcesSet, ctx);+ pNewSelector->addSources(newSourcesSet); - DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet newSet = pNewSelector->sources(); printSourcesSet(newSet, ctx, "SOURCES ON NEW SELECTOR AFTER ADD: "))- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))+ DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet newSet = pNewSelector->sources(); printSourcesSet(newSet, "SOURCES ON NEW SELECTOR AFTER ADD: "))+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: ")) if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);@@ -1661,25 +1639,24 @@ } - for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) {- SelsNewSeqPair& pair = *holderIter;+ for (SubSetMapLookup& pair : holder) { Compound_Selector_Obj pSels = pair.first; Complex_Selector_Obj pNewSelector = pair.second; // RUBY??: next [] if seen.include?(sels)- if (seen.find(*pSels) != seen.end()) {+ if (seen.find(pSels) != seen.end()) { continue; } - std::set<Compound_Selector> recurseSeen(seen);- recurseSeen.insert(*pSels);+ CompoundSelectorSet recurseSeen(seen);+ recurseSeen.insert(pSels); - DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx))- Node recurseExtendedSelectors = extendComplexSelector(&pNewSelector, ctx, subset_map, recurseSeen, isReplace, false); // !:isOriginal+ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector))+ Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, recurseSeen, isReplace, false); // !:isOriginal DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) @@ -1687,38 +1664,39 @@ iterator != endIterator; ++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*/));+// DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << results)+// DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << results.contains(newSelector, false /*simpleSelectorOrderDependent*/)); - if (!extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)) {+ if (!results.contains(newSelector)) { // DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR")- extendedSelectors.collection()->push_back(newSelector);+ results.collection()->push_back(newSelector); } } } DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: ")) - return extendedSelectors;+ // this turned out to be too much overhead+ // memory results in a map table - since extending is very expensive+ // memoizeCompound.insert(std::pair<Compound_Selector_Obj, Node>(pSelector, results));++ return results; } - static bool complexSelectorHasExtension(- Complex_Selector_Ptr pComplexSelector,- Context& ctx,- Subset_Map& subset_map,- std::set<Compound_Selector>& seen) {+ // check if selector has something to be extended by subset_map+ bool Extend::complexSelectorHasExtension(Complex_Selector_Ptr selector, CompoundSelectorSet& seen) { bool hasExtension = false; - Complex_Selector_Obj pIter = pComplexSelector;+ Complex_Selector_Obj pIter = selector; while (!hasExtension && pIter) { Compound_Selector_Obj pHead = pIter->head(); if (pHead) {- SubsetMapEntries entries = subset_map.get_v(pHead);- for (ExtensionPair ext : entries) {+ SubSetMapPairs entries = subset_map.get_v(pHead);+ for (SubSetMapPair ext : entries) { // check if both selectors have the same media block parent // if (ext.first->media_block() == pComplexSelector->media_block()) continue; if (ext.second->media_block() == 0) continue;@@ -1726,8 +1704,8 @@ ext.second->media_block()->media_queries() && pHead->media_block()->media_queries() ) {- 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));+ std::string query_left(ext.second->media_block()->media_queries()->to_string());+ std::string query_right(pHead->media_block()->media_queries()->to_string()); if (query_left == query_right) continue; } @@ -1738,9 +1716,9 @@ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); err << "You may not @extend an outer selector from within @media.\n"; err << "You may only @extend selectors within the same directive.\n";- err << "From \"@extend " << ext.second->to_string(ctx.c_options) << "\"";+ err << "From \"@extend " << ext.second->to_string() << "\""; err << " on line " << pstate.line+1 << " of " << rel_path << "\n";- error(err.str(), pComplexSelector->pstate());+ error(err.str(), selector->pstate(), eval->exp.traces); } if (entries.size() > 0) hasExtension = true; }@@ -1763,23 +1741,25 @@ the combinator and compound selector are one unit next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence) */- static Node extendComplexSelector(- Complex_Selector_Ptr pComplexSelector,- Context& ctx,- Subset_Map& subset_map,- std::set<Compound_Selector> seen, bool isReplace, bool isOriginal) {+ Node Extend::extendComplexSelector(Complex_Selector_Ptr selector, CompoundSelectorSet& seen, bool isReplace, bool isOriginal) { - Node complexSelector = complexSelectorToNode(pComplexSelector, ctx);- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector)+ // check if we already extended this selector+ // we can do this since subset_map is "static"+ auto memoized = memoizeComplex.find(selector);+ if (memoized != memoizeComplex.end()) {+ return memoized->second;+ } - Node extendedNotExpanded = Node::createCollection();+ // convert the input selector to extend node format+ Node complexSelector = complexSelectorToNode(selector);+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) - for (NodeDeque::iterator complexSelIter = complexSelector.collection()->begin(),- complexSelIterEnd = complexSelector.collection()->end();- complexSelIter != complexSelIterEnd; ++complexSelIter)- {+ // let CHOICES be an empty list of selector-lists+ // create new collection to hold the results+ Node choices = Node::createCollection(); - Node& sseqOrOp = *complexSelIter;+ // for each compound selector COMPOUND in COMPLEX:+ for (Node& sseqOrOp : *complexSelector.collection()) { DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp) @@ -1792,94 +1772,93 @@ Node inner = Node::createCollection(); outer.collection()->push_back(inner); inner.collection()->push_back(sseqOrOp);- extendedNotExpanded.collection()->push_back(outer);+ choices.collection()->push_back(outer); continue; } - Compound_Selector_Obj pCompoundSelector = sseqOrOp.selector()->head();+ // verified now that node is a valid selector+ Complex_Selector_Obj sseqSel = sseqOrOp.selector();+ Compound_Selector_Obj sseqHead = sseqSel->head(); + // let EXTENDED be extend_compound(COMPOUND, SEEN)+ // extend the compound selector against the given subset_map // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)- Node extended = extendCompoundSelector(&pCompoundSelector, ctx, subset_map, seen, isReplace);+ Node extended = extendCompoundSelector(sseqHead, seen, isReplace); // slow(17%)! if (sseqOrOp.got_line_feed) extended.got_line_feed = true; DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended) -- // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with an ruby Array instead of a Sequence- // due to the member mapping: choices = extended.map {|seq| seq.members}- Complex_Selector_Obj pJustCurrentCompoundSelector = sseqOrOp.selector();-+ // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with a ruby+ // Array instead of a Sequence due to the member mapping: choices = extended.map {|seq| seq.members} // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder?- if (isOriginal && !pComplexSelector->has_placeholder()) {- SourcesSet srcset;- srcset.insert(pComplexSelector);- pJustCurrentCompoundSelector->addSources(srcset, ctx);- DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector)+ if (isOriginal && !selector->has_placeholder()) {+ ComplexSelectorSet srcset;+ srcset.insert(selector);+ sseqSel->addSources(srcset);+ // DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector) } bool isSuperselector = false;- for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end();- iterator != endIterator; ++iterator) {- Node& childNode = *iterator;- Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode, ctx);- if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) {+ // if no complex selector in EXTENDED is a superselector of COMPOUND:+ for (Node& childNode : *extended.collection()) {+ Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode);+ if (pExtensionSelector->is_superselector_of(sseqSel)) { isSuperselector = true; break; } } if (!isSuperselector) {- if (sseqOrOp.got_line_feed) pJustCurrentCompoundSelector->has_line_feed(sseqOrOp.got_line_feed);- extended.collection()->push_front(complexSelectorToNode(&pJustCurrentCompoundSelector, ctx));+ // add a complex selector composed only of COMPOUND to EXTENDED+ if (sseqOrOp.got_line_feed) sseqSel->has_line_feed(sseqOrOp.got_line_feed);+ extended.collection()->push_front(complexSelectorToNode(sseqSel)); } DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended) + // add EXTENDED to CHOICES // Aggregate our current extensions- extendedNotExpanded.collection()->push_back(extended);+ choices.collection()->push_back(extended); } - DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << extendedNotExpanded)+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << choices) // Ruby Equivalent: paths- Node paths = Sass::paths(extendedNotExpanded, ctx);+ Node paths = Sass::paths(choices); DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths) --- // Ruby Equivalent: weave+ // let WEAVES be an empty list of selector lists Node weaves = Node::createCollection(); - for (NodeDeque::iterator pathsIter = paths.collection()->begin(), pathsEndIter = paths.collection()->end(); pathsIter != pathsEndIter; ++pathsIter) {- Node& path = *pathsIter;- Node weaved = weave(path, ctx);+ // for each list of complex selectors PATH in paths(CHOICES):+ for (Node& path : *paths.collection()) {+ // add weave(PATH) to WEAVES+ Node weaved = weave(path); // slow(12%)! weaved.got_line_feed = path.got_line_feed; weaves.collection()->push_back(weaved); } DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves) -- // Ruby Equivalent: trim- Node trimmed = trim(weaves, ctx, isReplace);+ Node trimmed(trim(weaves, isReplace)); // slow(19%)! DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed) - // Ruby Equivalent: flatten- Node extendedSelectors = flatten(trimmed, ctx, 1);+ Node flattened(flatten(trimmed, 1)); DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors)-- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector) + // memory results in a map table - since extending is very expensive+ memoizeComplex.insert(std::pair<Complex_Selector_Obj, Node>(selector, flattened)); - return extendedSelectors;+ // return trim(WEAVES)+ return flattened; } @@ -1887,20 +1866,23 @@ /* 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;- return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);- }-- /*- 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) {+ // We get a selector list with has something to extend and a subset_map with+ // all extenders. Pick the ones that match our selectors in the list.+ Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen) { Selector_List_Obj pNewSelectors = SASS_MEMORY_NEW(Selector_List, pSelectorList->pstate(), pSelectorList->length()); - extendedSomething = false;+ // check if we already extended this selector+ // we can do this since subset_map is "static"+ auto memoized = memoizeList.find(pSelectorList);+ if (memoized != memoizeList.end()) {+ extendedSomething = true;+ return memoized->second;+ } + extendedSomething = false;+ // process each comlplex selector in the selector list.+ // Find the ones that can be extended by given subset_map. for (size_t index = 0, length = pSelectorList->length(); index < length; index++) { Complex_Selector_Obj pSelector = (*pSelectorList)[index]; @@ -1909,89 +1891,100 @@ // 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, seen)) {- pNewSelectors->append(&pSelector);+ if (!complexSelectorHasExtension(pSelector, seen)) {+ pNewSelectors->append(pSelector); continue; } + // complexSelectorHasExtension was true! extendedSomething = true; - Node extendedSelectors = extendComplexSelector(&pSelector, ctx, subset_map, seen, isReplace, true);+ // now do the actual extension of the complex selector+ Node extendedSelectors = extendComplexSelector(pSelector, seen, isReplace, true);+ if (!pSelector->has_placeholder()) {- if (!extendedSelectors.contains(complexSelectorToNode(&pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {+ Node nSelector(complexSelectorToNode(pSelector));+ if (!extendedSelectors.contains(nSelector)) { pNewSelectors->append(pSelector); continue; } } - for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) {+ bool doReplace = isReplace;+ for (Node& childNode : *extendedSelectors.collection()) { // When it is a replace, skip the first one, unless there is only one- if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue;-- Node& childNode = *iterator;- pNewSelectors->append(nodeToComplexSelector(childNode, ctx));+ if(doReplace && extendedSelectors.collection()->size() > 1 ) {+ doReplace = false;+ continue;+ }+ pNewSelectors->append(nodeToComplexSelector(childNode)); } } - Remove_Placeholders remove_placeholders(ctx);+ Remove_Placeholders remove_placeholders; // it seems that we have to remove the place holders early here // normally we do this as the very last step (compare to ruby sass)- pNewSelectors = remove_placeholders.remove_placeholders(&pNewSelectors);+ 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());+ if (cur->head() && seen.find(cur->head()) == seen.end()) {+ CompoundSelectorSet recseen(seen);+ recseen.insert(cur->head()); // create a copy since we add multiple items if stuff get unwrapped Compound_Selector_Obj cpy_head = SASS_MEMORY_NEW(Compound_Selector, cur->pstate()); for (Simple_Selector_Obj hs : *cur->head()) {- if (Wrapped_Selector_Obj ws = SASS_MEMORY_CAST(Wrapped_Selector, hs)) {+ if (Wrapped_Selector_Obj ws = Cast<Wrapped_Selector>(hs)) { ws->selector(SASS_MEMORY_CLONE(ws->selector()));- if (Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, ws->selector())) {+ if (Selector_List_Obj sl = Cast<Selector_List>(ws->selector())) { // special case for ruby ass if (sl->empty()) { // this seems inconsistent but it is how ruby sass seems to remove parentheses cpy_head->append(SASS_MEMORY_NEW(Element_Selector, hs->pstate(), ws->name())); }- // has wrapped selectors- else {+ // has wrapped not selectors+ else if (ws->name() == ":not") { // extend the inner list of wrapped selector- Selector_List_Obj ext_sl = extendSelectorList(sl, ctx, subset_map, recseen);+ bool extended = false;+ Selector_List_Obj ext_sl = extendSelectorList(sl, false, extended, recseen); for (size_t i = 0; i < ext_sl->length(); i += 1) { if (Complex_Selector_Obj ext_cs = ext_sl->at(i)) { // create clones for wrapped selector and the inner list- Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(&ws);+ 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;- }+ Compound_Selector_Obj ext_head = NULL;+ if (ext_cs->first()) ext_head = ext_cs->first()->head();+ if (ext_head && ext_head && ext_head->length() > 0) { cpy_ws_sl->append(ext_cs->first()); } // assign list to clone- cpy_ws->selector(&cpy_ws_sl);+ cpy_ws->selector(cpy_ws_sl); // append the clone- cpy_head->append(&cpy_ws);+ cpy_head->append(cpy_ws); } }+ if (eval && extended) {+ eval->exp.selector_stack.push_back(pNewSelectors);+ cpy_head->perform(eval);+ eval->exp.selector_stack.pop_back();+ } }+ // has wrapped selectors+ else {+ Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);+ Selector_List_Obj ext_sl = extendSelectorList(sl, recseen);+ cpy_ws->selector(ext_sl);+ cpy_head->append(cpy_ws);+ } } else {- cpy_head->append(&hs);+ cpy_head->append(hs); } } else {- cpy_head->append(&hs);+ cpy_head->append(hs); } } // replace header@@ -2001,6 +1994,10 @@ cur = cur->tail(); } }++ // memory results in a map table - since extending is very expensive+ memoizeList.insert(std::pair<Selector_List_Obj, Selector_List_Obj>(pSelectorList, pNewSelectors));+ return pNewSelectors.detach(); }@@ -2024,8 +2021,9 @@ for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i); - 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.+ if (Cast<Ruleset>(stm)) {+ // Do nothing. This doesn't count as a statement that causes extension since we'll+ // iterate over this rule set in a future visit and try to extend it. } else { return true;@@ -2038,37 +2036,45 @@ // 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, Subset_Map& subset_map) {+ // Every Ruleset in the whole tree is calling this function. We decide if there+ // was is @extend that matches our selector. If we find one, we will go further+ // and call the extend magic for our selector. The subset_map contains all blocks+ // where @extend was found. Pick the ones that match our selector!+ void Extend::extendObjectWithSelectorAndBlock(Ruleset_Ptr pObject) { - DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List_Ptr>(pObject->selector())->to_string(ctx.c_options))+ DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << Cast<Selector_List>(pObject->selector())->to_string()) - // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions- // of doing so, so for now, let's just not extend things that won't be output later.+ // Ruby sass seems to filter nodes that don't have any content well before we get here.+ // I'm not sure the repercussions of doing so, so for now, let's just not extend things+ // that won't be output later. Profiling shows this may us 0.2% or so. if (!shouldExtendBlock(pObject->block())) { DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT") return; } bool extendedSomething = false;- Selector_List_Obj pNewSelectorList = Extend::extendSelectorList(SASS_MEMORY_CAST(Selector_List, pObject->selector()), ctx, subset_map, false, extendedSomething); + CompoundSelectorSet seen;+ Selector_List_Obj pNewSelectorList = extendSelectorList(pObject->selector(), false, extendedSomething, seen);+ if (extendedSomething && pNewSelectorList) {- 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))+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << pObject->selector()->to_string())+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string()) pNewSelectorList->remove_parent_selectors();- pObject->selector(&pNewSelectorList);+ pObject->selector(pNewSelectorList); } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") } } --- Extend::Extend(Context& ctx, Subset_Map& ssm)- : ctx(ctx), subset_map(ssm)+ Extend::Extend(Subset_Map& ssm)+ : subset_map(ssm), eval(NULL) { } + void Extend::setEval(Eval& e) {+ eval = &e;+ }+ void Extend::operator()(Block_Ptr b) { for (size_t i = 0, L = b->length(); i < L; ++i) {@@ -2080,17 +2086,19 @@ 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;+ Complex_Selector_Ptr sel = NULL;+ Compound_Selector_Ptr ext = NULL;+ if (it.first) sel = it.first->first();+ if (it.second) ext = it.second; if (ext && (ext->extended() || ext->is_optional())) continue;- std::string str_sel(sel->to_string({ NESTED, 5 }));- std::string str_ext(ext->to_string({ NESTED, 5 }));+ std::string str_sel(sel ? sel->to_string({ NESTED, 5 }) : "NULL");+ std::string str_ext(ext ? ext->to_string({ NESTED, 5 }) : "NULL"); // debug_ast(sel, "sel: "); // debug_ast(ext, "ext: "); error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n" "The selector \"" + str_ext + "\" was not found.\n" "Use \"@extend " + str_ext + " !optional\" if the"- " extend should be able to fail.", ext->pstate());+ " extend should be able to fail.", (ext ? ext->pstate() : NULL), eval->exp.traces); } } @@ -2098,7 +2106,7 @@ void Extend::operator()(Ruleset_Ptr pRuleset) {- extendObjectWithSelectorAndBlock( pRuleset, ctx, subset_map);+ extendObjectWithSelectorAndBlock( pRuleset ); pRuleset->block()->perform(this); } @@ -2114,7 +2122,7 @@ void Extend::operator()(Directive_Ptr a) {- // Selector_List_Ptr ls = dynamic_cast<Selector_List_Ptr>(a->selector());+ // Selector_List_Ptr ls = Cast<Selector_List>(a->selector()); // selector_stack.push_back(ls); if (a->block()) a->block()->perform(this); // exp.selector_stack.pop_back();
libsass/src/extend.hpp view
@@ -5,35 +5,70 @@ #include <set> #include "ast.hpp"+#include "node.hpp"+#include "eval.hpp" #include "operation.hpp" #include "subset_map.hpp"+#include "ast_fwd_decl.hpp" namespace Sass { - class Context;- class Node;+ Node subweave(Node& one, Node& two); class Extend : public Operation_CRTP<void, Extend> { - Context& ctx; Subset_Map& subset_map;+ Eval* eval; void fallback_impl(AST_Node_Ptr n) { } + private:++ std::unordered_map<+ Selector_List_Obj, // key+ Selector_List_Obj, // value+ HashNodes, // hasher+ CompareNodes // compare+ > memoizeList;++ std::unordered_map<+ Complex_Selector_Obj, // key+ Node, // value+ HashNodes, // hasher+ CompareNodes // compare+ > memoizeComplex;++ /* this turned out to be too much overhead+ re-evaluate once we store an ast selector+ std::unordered_map<+ Compound_Selector_Obj, // key+ Node, // value+ HashNodes, // hasher+ CompareNodes // compare+ > memoizeCompound;+ */++ void extendObjectWithSelectorAndBlock(Ruleset_Ptr pObject);+ Node extendComplexSelector(Complex_Selector_Ptr sel, CompoundSelectorSet& seen, bool isReplace, bool isOriginal);+ Node extendCompoundSelector(Compound_Selector_Ptr sel, CompoundSelectorSet& seen, bool isReplace);+ bool complexSelectorHasExtension(Complex_Selector_Ptr selector, CompoundSelectorSet& seen);+ Node trim(Node& seqses, bool isReplace);+ Node weave(Node& path);+ public:- static Node subweave(Node& one, Node& two, Context& ctx);- 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) {+ void setEval(Eval& eval);+ Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen);+ Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace = false) { bool extendedSomething = false;- return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething);+ CompoundSelectorSet seen;+ return extendSelectorList(pSelectorList, isReplace, extendedSomething, seen); }- static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, std::set<Compound_Selector>& seen) {+ Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, CompoundSelectorSet& seen) { bool isReplace = false; bool extendedSomething = false;- return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);+ return extendSelectorList(pSelectorList, isReplace, extendedSomething, seen); }- Extend(Context&, Subset_Map&);+ Extend(Subset_Map&); ~Extend() { } void operator()(Block_Ptr);
libsass/src/file.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #ifdef _WIN32 # ifdef __MINGW32__ # ifndef off64_t@@ -9,7 +10,6 @@ #else # include <unistd.h> #endif-#include "sass.hpp" #include <iostream> #include <fstream> #include <cctype>@@ -20,6 +20,7 @@ #include "context.hpp" #include "prelexer.hpp" #include "utf8_string.hpp"+#include "sass_functions.hpp" #include "sass2scss.h" #ifdef _WIN32@@ -48,15 +49,22 @@ // return the current directory // always with forward slashes+ // always with trailing slash std::string get_cwd() {- const size_t wd_len = 1024;+ const size_t wd_len = 4096; #ifndef _WIN32 char wd[wd_len];- std::string cwd = getcwd(wd, wd_len);+ char* pwd = getcwd(wd, wd_len);+ // we should check error for more detailed info (e.g. ENOENT)+ // http://man7.org/linux/man-pages/man2/getcwd.2.html#ERRORS+ if (pwd == NULL) throw Exception::OperationError("cwd gone missing");+ std::string cwd = pwd; #else wchar_t wd[wd_len];- std::string cwd = wstring_to_string(_wgetcwd(wd, wd_len));+ wchar_t* pwd = _wgetcwd(wd, wd_len);+ if (pwd == NULL) throw Exception::OperationError("cwd gone missing");+ std::string cwd = wstring_to_string(pwd); //convert backslashes to forward slashes replace(cwd.begin(), cwd.end(), '\\', '/'); #endif@@ -68,8 +76,15 @@ bool file_exists(const std::string& path) { #ifdef _WIN32- std::wstring wpath = UTF_8::convert_to_utf16(path);- DWORD dwAttrib = GetFileAttributesW(wpath.c_str());+ wchar_t resolved[32768];+ // windows unicode filepaths are encoded in utf16+ std::string abspath(join_paths(get_cwd(), path));+ std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));+ std::replace(wpath.begin(), wpath.end(), '/', '\\');+ DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);+ if (rv > 32767) throw Exception::OperationError("Path is too long");+ if (rv == 0) throw Exception::OperationError("Path could not be resolved");+ DWORD dwAttrib = GetFileAttributesW(resolved); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))); #else@@ -99,7 +114,7 @@ // helper function to find the last directory seperator inline size_t find_last_folder_separator(const std::string& path, size_t limit = std::string::npos) {- size_t pos = std::string::npos;+ size_t pos; size_t pos_p = path.find_last_of('/', limit); #ifdef _WIN32 size_t pos_w = path.find_last_of('\\', limit);@@ -150,7 +165,7 @@ pos = 0; // remove all self references inside the path string while((pos = path.find("/./", pos)) != std::string::npos) path.erase(pos, 2); - pos = 0; // remove all leading and trailing self references+ // remove all leading and trailing self references while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2); while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2); @@ -191,6 +206,13 @@ if (is_absolute_path(r)) return r; if (l[l.length()-1] != '/') l += '/'; + // this does a logical cleanup of the right hand path+ // Note that this does collapse x/../y sections into y.+ // This is by design. If /foo on your system is a symlink+ // to /bar/baz, then /foo/../cd is actually /bar/cd,+ // not /cd as a naive ../ removal would give you.+ // will only work on leading double dot dirs on rhs+ // therefore it is safe if lhs is already resolved cwd while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) { 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] == '\\');@@ -301,13 +323,9 @@ // (2) underscore + given // (3) underscore + given + extension // (4) given + extension- std::vector<Include> resolve_includes(const std::string& root, const std::string& file)+ std::vector<Include> resolve_includes(const std::string& root, const std::string& file, const std::vector<std::string>& exts) { std::string filename = join_paths(root, file);- // supported extensions- const std::vector<std::string> exts = {- ".scss", ".sass", ".css"- }; // split the filename std::string base(dir_name(file)); std::string name(base_name(file));@@ -336,9 +354,42 @@ return includes; } - // helper function to resolve a filename+ std::vector<std::string> find_files(const std::string& file, const std::vector<std::string> paths)+ {+ std::vector<std::string> includes;+ for (std::string path : paths) {+ std::string abs_path(join_paths(path, file));+ if (file_exists(abs_path)) includes.push_back(abs_path);+ }+ return includes;+ }++ std::vector<std::string> find_files(const std::string& file, struct Sass_Compiler* compiler)+ {+ // get the last import entry to get current base directory+ // struct Sass_Options* options = sass_compiler_get_options(compiler);+ Sass_Import_Entry import = sass_compiler_get_last_import(compiler);+ const std::vector<std::string>& incs = compiler->cpp_ctx->include_paths;+ // create the vector with paths to lookup+ std::vector<std::string> paths(1 + incs.size());+ paths.push_back(dir_name(import->abs_path));+ paths.insert(paths.end(), incs.begin(), incs.end());+ // dispatch to find files in paths+ return find_files(file, paths);+ }++ // helper function to search one file in all include paths+ // this is normally not used internally by libsass (C-API sugar) std::string find_file(const std::string& file, const std::vector<std::string> paths) {+ if (file.empty()) return file;+ auto res = find_files(file, paths);+ return res.empty() ? "" : res.front();+ }++ // helper function to resolve a filename+ std::string find_include(const std::string& file, const std::vector<std::string> paths)+ { // search in every include path for a match for (size_t i = 0, S = paths.size(); i < S; ++i) {@@ -349,20 +400,6 @@ return std::string(""); } - // inc paths can be directly passed from C code- std::string find_file(const std::string& file, const char* paths[])- {- if (paths == 0) return std::string("");- std::vector<std::string> includes(0);- // includes.push_back(".");- const char** it = paths;- while (it && *it) {- includes.push_back(*it);- ++it;- }- return find_file(file, includes);- }- // try to load the given filename // returned memory must be freed // will auto convert .sass files@@ -371,16 +408,24 @@ #ifdef _WIN32 BYTE* pBuffer; DWORD dwBytes;+ wchar_t resolved[32768]; // windows unicode filepaths are encoded in utf16- std::wstring wpath = UTF_8::convert_to_utf16(path);- HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);+ std::string abspath(join_paths(get_cwd(), path));+ std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));+ std::replace(wpath.begin(), wpath.end(), '/', '\\');+ DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);+ if (rv > 32767) throw Exception::OperationError("Path is too long");+ if (rv == 0) throw Exception::OperationError("Path could not be resolved");+ HANDLE hFile = CreateFileW(resolved, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; DWORD dwFileLength = GetFileSize(hFile, NULL); if (dwFileLength == INVALID_FILE_SIZE) return 0; // allocate an extra byte for the null char- pBuffer = (BYTE*)malloc((dwFileLength+1)*sizeof(BYTE));+ // and another one for edge-cases in lexer+ pBuffer = (BYTE*)malloc((dwFileLength+2)*sizeof(BYTE)); ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);- pBuffer[dwFileLength] = '\0';+ pBuffer[dwFileLength+0] = '\0';+ pBuffer[dwFileLength+1] = '\0'; CloseHandle(hFile); // just convert from unsigned char* char* contents = (char*) pBuffer;@@ -392,10 +437,12 @@ if (file.is_open()) { size_t size = file.tellg(); // allocate an extra byte for the null char- contents = (char*) malloc((size+1)*sizeof(char));+ // and another one for edge-cases in lexer+ contents = (char*) malloc((size+2)*sizeof(char)); file.seekg(0, std::ios::beg); file.read(contents, size);- contents[size] = '\0';+ contents[size+0] = '\0';+ contents[size+1] = '\0'; file.close(); } #endif@@ -412,6 +459,26 @@ } else { return contents; }+ }++ // split a path string delimited by semicolons or colons (OS dependent)+ std::vector<std::string> split_path_list(const char* str)+ {+ std::vector<std::string> paths;+ if (str == NULL) return paths;+ // find delimiter via prelexer (return zero at end)+ const char* end = Prelexer::find_first<PATH_SEP>(str);+ // search until null delimiter+ while (end) {+ // add path from current position to delimiter+ paths.push_back(std::string(str, end - str));+ str = end + 1; // skip delimiter+ end = Prelexer::find_first<PATH_SEP>(str);+ }+ // add path from current position to end+ paths.push_back(std::string(str));+ // return back+ return paths; } }
libsass/src/file.hpp view
@@ -4,6 +4,7 @@ #include <string> #include <vector> +#include "sass/context.h" #include "ast_fwd_decl.hpp" namespace Sass {@@ -47,10 +48,17 @@ std::string abs2rel(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd()); // helper function to resolve a filename+ // searching without variations in all paths+ std::string find_file(const std::string& file, struct Sass_Compiler* options); std::string find_file(const std::string& file, const std::vector<std::string> paths);- // inc paths can be directly passed from C code- std::string find_file(const std::string& file, const char** paths); + // helper function to resolve a include filename+ // this has the original resolve logic for sass include+ std::string find_include(const std::string& file, const std::vector<std::string> paths);++ // split a path string delimited by semicolons or colons (OS dependent)+ std::vector<std::string> split_path_list(const char* paths);+ // try to load the given filename // returned memory must be freed // will auto convert .sass files@@ -113,7 +121,10 @@ namespace File { - std::vector<Include> resolve_includes(const std::string& root, const std::string& file);+ static std::vector<std::string> defaultExtensions = { ".scss", ".sass", ".css" };++ std::vector<Include> resolve_includes(const std::string& root, const std::string& file,+ const std::vector<std::string>& exts = defaultExtensions); }
libsass/src/functions.cpp view
@@ -10,6 +10,7 @@ #include "eval.hpp" #include "util.hpp" #include "expand.hpp"+#include "operators.hpp" #include "utf8_string.hpp" #include "sass/base.h" #include "utf8.h"@@ -30,17 +31,34 @@ #include "wincrypt.h" #endif -#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, backtrace)-#define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, pstate, lo, hi, backtrace)-#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx)+#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)+#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, traces, ctx) +// return a number object (copied since we want to have reduced units)+#define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy++// special function for weird hsla percent (10px == 10% == 10 != 0.1)+#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double++// macros for common ranges (u mean unsigned or upper, r for full range)+#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double+#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double+#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double+#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double+#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double+#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double++// macros for color related inputs (rbg and alpha/opacity values)+#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double+#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double+ namespace Sass { using std::stringstream; using std::endl; Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx) {- Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]"));+ Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]")); sig_parser.lex<Prelexer::identifier>(); std::string name(Util::normalize_underscores(sig_parser.lexed)); Parameters_Obj params = sig_parser.parse_parameters();@@ -58,7 +76,7 @@ using namespace Prelexer; const char* sig = sass_function_get_signature(c_func);- Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[c function]"));+ Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]")); // allow to overload generic callback plus @warn, @error and @debug with custom functions sig_parser.lex < alternatives < identifier, exactly <'*'>, exactly < Constants::warn_kwd >,@@ -84,31 +102,31 @@ namespace Functions { - inline void handle_utf8_error (const ParserState& pstate, Backtrace* backtrace)+ inline void handle_utf8_error (const ParserState& pstate, Backtraces traces) { try { throw; } catch (utf8::invalid_code_point) { std::string msg("utf8::invalid_code_point");- error(msg, pstate, backtrace);+ error(msg, pstate, traces); } catch (utf8::not_enough_room) { std::string msg("utf8::not_enough_room");- error(msg, pstate, backtrace);+ error(msg, pstate, traces); } catch (utf8::invalid_utf8) { std::string msg("utf8::invalid_utf8");- error(msg, pstate, backtrace);+ error(msg, pstate, traces); } catch (...) { throw; } } template <typename T>- T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)+ T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) { // Minimal error handling -- the expectation is that built-ins will be written correctly!- T* val = dynamic_cast<T*>(&env[argname]);+ T* val = Cast<T>(env[argname]); if (!val) { std::string msg("argument `"); msg += argname;@@ -116,74 +134,138 @@ msg += sig; msg += "` must be a "; msg += T::type_name();- error(msg, pstate, backtrace);+ error(msg, pstate, traces); } return val; } - Map_Ptr 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, Backtraces traces, Context& ctx) { // Minimal error handling -- the expectation is that built-ins will be written correctly!- Map_Ptr val = SASS_MEMORY_CAST(Map, env[argname]);+ Map_Ptr val = Cast<Map>(env[argname]); if (val) return val; - List_Ptr lval = SASS_MEMORY_CAST(List, env[argname]);+ List_Ptr lval = Cast<List>(env[argname]); if (lval && lval->length() == 0) return SASS_MEMORY_NEW(Map, pstate, 0); // fallback on get_arg for error handling- val = get_arg<Map>(argname, env, sig, pstate, backtrace);+ val = get_arg<Map>(argname, env, sig, pstate, traces); return val; } - Number_Ptr get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace)+ double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi) { // Minimal error handling -- the expectation is that built-ins will be written correctly!- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);- double v = val->value();+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ double v = tmpnr.value(); if (!(lo <= v && v <= hi)) { std::stringstream msg; msg << "argument `" << argname << "` of `" << sig << "` must be between "; msg << lo << " and " << hi;- error(msg.str(), pstate, backtrace);+ error(msg.str(), pstate, traces); }+ return v;+ }++ Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ // Minimal error handling -- the expectation is that built-ins will be written correctly!+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);+ val = SASS_MEMORY_COPY(val);+ val->reduce(); return val; } - #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, backtrace, ctx)+ double get_arg_v(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ // Minimal error handling -- the expectation is that built-ins will be written correctly!+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ /*+ if (tmpnr.unit() == "%") {+ tmpnr.value(tmpnr.value() / 100);+ tmpnr.numerators.clear();+ } else {+ if (!tmpnr.is_unitless()) error("argument " + argname + " of `" + std::string(sig) + "` must be unitless", pstate);+ }+ */+ return tmpnr.value();+ } + double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ // Minimal error handling -- the expectation is that built-ins will be written correctly!+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ return tmpnr.value();+ }++ double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ if (tmpnr.unit() == "%") {+ return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0);+ } else {+ return std::min(std::max(tmpnr.value(), 0.0), 255.0);+ }+ }+++ inline double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) {+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ if (tmpnr.unit() == "%") {+ return std::min(std::max(tmpnr.value(), 0.0), 100.0);+ } else {+ return std::min(std::max(tmpnr.value(), 0.0), 1.0);+ }+ }++ #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, traces, ctx)+ template <typename T>- T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx);+ T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); template <>- Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) { Expression_Obj exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";- error(msg.str(), pstate);+ error(msg.str(), pstate, traces); }- if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+ if (String_Constant_Ptr str = Cast<String_Constant>(exp)) { str->quote_mark(0); }- std::string exp_src = exp->to_string(ctx.c_options) + "{";- return Parser::parse_selector(exp_src.c_str(), ctx);+ std::string exp_src = exp->to_string(ctx.c_options);+ return Parser::parse_selector(exp_src.c_str(), ctx, traces); } template <>- Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) { Expression_Obj exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a string for `" << function_name(sig) << "'";- error(msg.str(), pstate);+ error(msg.str(), pstate, traces); }- if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+ if (String_Constant_Ptr str = Cast<String_Constant>(exp)) { str->quote_mark(0); }- std::string exp_src = exp->to_string(ctx.c_options) + "{";- Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx);- return (sel_list->length() > 0) ? &sel_list->first()->tail()->head() : 0;+ std::string exp_src = exp->to_string(ctx.c_options);+ Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces);+ if (sel_list->length() == 0) return NULL;+ Complex_Selector_Obj first = sel_list->first();+ if (!first->tail()) return first->head();+ return first->tail()->head(); } #ifdef __MINGW32__@@ -219,56 +301,110 @@ "global-variable-shadowing", "extend-selector-pseudoclass", "at-error",- "units-level-3"+ "units-level-3",+ "custom-property" }; //////////////// // RGB FUNCTIONS //////////////// - 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 {- return std::min(std::max(n->value(), 0.0), 255.0);- }- }-- inline double alpha_num(Number_Ptr n) {- if (n->unit() == "%") {- return std::min(std::max(n->value(), 0.0), 100.0);- } else {- return std::min(std::max(n->value(), 0.0), 1.0);+ inline bool special_number(String_Constant_Ptr s) {+ if (s) {+ std::string calc("calc(");+ std::string var("var(");+ std::string ss(s->value());+ return std::equal(calc.begin(), calc.end(), ss.begin()) ||+ std::equal(var.begin(), var.end(), ss.begin()); }+ return false; } Signature rgb_sig = "rgb($red, $green, $blue)"; BUILT_IN(rgb) {+ if (+ special_number(Cast<String_Constant>(env["$red"])) ||+ special_number(Cast<String_Constant>(env["$green"])) ||+ special_number(Cast<String_Constant>(env["$blue"]))+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("+ + env["$red"]->to_string()+ + ", "+ + env["$green"]->to_string()+ + ", "+ + env["$blue"]->to_string()+ + ")"+ );+ }+ return SASS_MEMORY_NEW(Color, pstate,- color_num(ARG("$red", Number)),- color_num(ARG("$green", Number)),- color_num(ARG("$blue", Number)));+ COLOR_NUM("$red"),+ COLOR_NUM("$green"),+ COLOR_NUM("$blue")); } Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; BUILT_IN(rgba_4) {+ if (+ special_number(Cast<String_Constant>(env["$red"])) ||+ special_number(Cast<String_Constant>(env["$green"])) ||+ special_number(Cast<String_Constant>(env["$blue"])) ||+ special_number(Cast<String_Constant>(env["$alpha"]))+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("+ + env["$red"]->to_string()+ + ", "+ + env["$green"]->to_string()+ + ", "+ + env["$blue"]->to_string()+ + ", "+ + env["$alpha"]->to_string()+ + ")"+ );+ }+ return SASS_MEMORY_NEW(Color, pstate,- color_num(ARG("$red", Number)),- color_num(ARG("$green", Number)),- color_num(ARG("$blue", Number)),- alpha_num(ARG("$alpha", Number)));+ COLOR_NUM("$red"),+ COLOR_NUM("$green"),+ COLOR_NUM("$blue"),+ ALPHA_NUM("$alpha")); } Signature rgba_2_sig = "rgba($color, $alpha)"; BUILT_IN(rgba_2) {+ if (+ special_number(Cast<String_Constant>(env["$color"]))+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("+ + env["$color"]->to_string()+ + ", "+ + env["$alpha"]->to_string()+ + ")"+ );+ }+ Color_Ptr c_arg = ARG("$color", Color);++ if (+ special_number(Cast<String_Constant>(env["$alpha"]))+ ) {+ std::stringstream strm;+ strm << "rgba("+ << (int)c_arg->r() << ", "+ << (int)c_arg->g() << ", "+ << (int)c_arg->b() << ", "+ << env["$alpha"]->to_string()+ << ")";+ return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());+ }+ Color_Ptr new_c = SASS_MEMORY_COPY(c_arg);- new_c->a(alpha_num(ARG("$alpha", Number)));+ new_c->a(ALPHA_NUM("$alpha")); new_c->disp(""); return new_c; }@@ -285,14 +421,8 @@ BUILT_IN(blue) { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); } - Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";- BUILT_IN(mix)- {- 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;+ Color* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) {+ double p = weight/100; double w = 2*p - 1; double a = color1->a() - color2->a(); @@ -307,6 +437,16 @@ color1->a()*p + color2->a()*(1-p)); } + Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";+ BUILT_IN(mix)+ {+ Color_Obj color1 = ARG("$color-1", Color);+ Color_Obj color2 = ARG("$color-2", Color);+ double weight = DARG_U_PRCT("$weight");+ return colormix(ctx, pstate, color1, color2, weight);++ }+ //////////////// // HSL FUNCTIONS ////////////////@@ -323,7 +463,9 @@ double min = std::min(r, std::min(g, b)); double delta = max - min; - double h = 0, s = 0, l = (max + min) / 2.0;+ double h = 0;+ double s;+ double l = (max + min) / 2.0; if (max == min) { h = s = 0; // achromatic@@ -390,9 +532,24 @@ Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; BUILT_IN(hsl) {- return hsla_impl(ARG("$hue", Number)->value(),- ARG("$saturation", Number)->value(),- ARG("$lightness", Number)->value(),+ if (+ special_number(Cast<String_Constant>(env["$hue"])) ||+ special_number(Cast<String_Constant>(env["$saturation"])) ||+ special_number(Cast<String_Constant>(env["$lightness"]))+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("+ + env["$hue"]->to_string()+ + ", "+ + env["$saturation"]->to_string()+ + ", "+ + env["$lightness"]->to_string()+ + ")"+ );+ }++ return hsla_impl(ARGVAL("$hue"),+ ARGVAL("$saturation"),+ ARGVAL("$lightness"), 1.0, ctx, pstate);@@ -401,10 +558,28 @@ Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; BUILT_IN(hsla) {- return hsla_impl(ARG("$hue", Number)->value(),- ARG("$saturation", Number)->value(),- ARG("$lightness", Number)->value(),- ARG("$alpha", Number)->value(),+ if (+ special_number(Cast<String_Constant>(env["$hue"])) ||+ special_number(Cast<String_Constant>(env["$saturation"])) ||+ special_number(Cast<String_Constant>(env["$lightness"])) ||+ special_number(Cast<String_Constant>(env["$alpha"]))+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("+ + env["$hue"]->to_string()+ + ", "+ + env["$saturation"]->to_string()+ + ", "+ + env["$lightness"]->to_string()+ + ", "+ + env["$alpha"]->to_string()+ + ")"+ );+ }++ return hsla_impl(ARGVAL("$hue"),+ ARGVAL("$saturation"),+ ARGVAL("$lightness"),+ ARGVAL("$alpha"), ctx, pstate); }@@ -443,11 +618,11 @@ BUILT_IN(adjust_hue) { Color_Ptr rgb_color = ARG("$color", Color);- Number_Ptr degrees = ARG("$degrees", Number);+ double degrees = ARGVAL("$degrees"); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());- return hsla_impl(hsl_color.h + degrees->value(),+ return hsla_impl(hsl_color.h + degrees, hsl_color.s, hsl_color.l, rgb_color->a(),@@ -459,7 +634,7 @@ BUILT_IN(lighten) { Color_Ptr rgb_color = ARG("$color", Color);- Number_Ptr amount = ARGR("$amount", Number, 0, 100);+ double amount = DARG_U_PRCT("$amount"); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -471,7 +646,7 @@ return hsla_impl(hsl_color.h, hsl_color.s,- hslcolorL + amount->value(),+ hslcolorL + amount, rgb_color->a(), ctx, pstate);@@ -481,7 +656,7 @@ BUILT_IN(darken) { Color_Ptr rgb_color = ARG("$color", Color);- Number_Ptr amount = ARGR("$amount", Number, 0, 100);+ double amount = DARG_U_PRCT("$amount"); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -494,7 +669,7 @@ return hsla_impl(hsl_color.h, hsl_color.s,- hslcolorL - amount->value(),+ hslcolorL - amount, rgb_color->a(), ctx, pstate);@@ -504,18 +679,17 @@ BUILT_IN(saturate) { // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$amount"]);- if (!amount) {+ if (!Cast<Number>(env["$amount"])) { return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); } - ARGR("$amount", Number, 0, 100);+ double amount = DARG_U_PRCT("$amount"); Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); - double hslcolorS = hsl_color.s + amount->value();+ double hslcolorS = hsl_color.s + amount; // Saturation cannot be below 0 or above 100 if (hslcolorS < 0) {@@ -537,12 +711,12 @@ BUILT_IN(desaturate) { Color_Ptr rgb_color = ARG("$color", Color);- Number_Ptr amount = ARGR("$amount", Number, 0, 100);+ double amount = DARG_U_PRCT("$amount"); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); - double hslcolorS = hsl_color.s - amount->value();+ double hslcolorS = hsl_color.s - amount; // Saturation cannot be below 0 or above 100 if (hslcolorS <= 0) {@@ -564,7 +738,7 @@ BUILT_IN(grayscale) { // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]);+ Number_Ptr amount = Cast<Number>(env["$color"]); if (amount) { return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); }@@ -596,22 +770,24 @@ pstate); } - Signature invert_sig = "invert($color)";+ Signature invert_sig = "invert($color, $weight: 100%)"; BUILT_IN(invert) { // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]);+ Number_Ptr amount = Cast<Number>(env["$color"]); if (amount) { return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); } + double weight = DARG_U_PRCT("$weight"); Color_Ptr rgb_color = ARG("$color", Color);- return SASS_MEMORY_NEW(Color,+ Color_Obj inv = SASS_MEMORY_NEW(Color, pstate, 255 - rgb_color->r(), 255 - rgb_color->g(), 255 - rgb_color->b(), rgb_color->a());+ return colormix(ctx, pstate, inv, rgb_color, weight); } ////////////////////@@ -621,13 +797,13 @@ Signature opacity_sig = "opacity($color)"; BUILT_IN(alpha) {- String_Constant_Ptr ie_kwd = SASS_MEMORY_CAST(String_Constant, env["$color"]);+ String_Constant_Ptr ie_kwd = Cast<String_Constant>(env["$color"]); if (ie_kwd) { return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")"); } // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]);+ Number_Ptr amount = Cast<Number>(env["$color"]); if (amount) { return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); }@@ -640,7 +816,7 @@ BUILT_IN(opacify) { Color_Ptr color = ARG("$color", Color);- double amount = ARGR("$amount", Number, 0, 1)->value();+ double amount = DARG_U_FACT("$amount"); double alpha = std::min(color->a() + amount, 1.0); return SASS_MEMORY_NEW(Color, pstate,@@ -655,7 +831,7 @@ BUILT_IN(transparentize) { Color_Ptr color = ARG("$color", Color);- double amount = ARGR("$amount", Number, 0, 1)->value();+ double amount = DARG_U_FACT("$amount"); double alpha = std::max(color->a() - amount, 0.0); return SASS_MEMORY_NEW(Color, pstate,@@ -673,25 +849,25 @@ BUILT_IN(adjust_color) { 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"]);+ Number_Ptr r = Cast<Number>(env["$red"]);+ Number_Ptr g = Cast<Number>(env["$green"]);+ Number_Ptr b = Cast<Number>(env["$blue"]);+ Number_Ptr h = Cast<Number>(env["$hue"]);+ Number_Ptr s = Cast<Number>(env["$saturation"]);+ Number_Ptr l = Cast<Number>(env["$lightness"]);+ Number_Ptr a = Cast<Number>(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) {- error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate);+ error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces); } 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;+ double rr = r ? DARG_R_BYTE("$red") : 0;+ double gg = g ? DARG_R_BYTE("$green") : 0;+ double bb = b ? DARG_R_BYTE("$blue") : 0;+ double aa = a ? DARG_R_FACT("$alpha") : 0; return SASS_MEMORY_NEW(Color, pstate, color->r() + rr,@@ -701,9 +877,9 @@ } if (hsl) { HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());- double ss = s ? ARGR("$saturation", Number, -100, 100)->value() : 0;- double ll = l ? ARGR("$lightness", Number, -100, 100)->value() : 0;- double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0;+ double ss = s ? DARG_R_PRCT("$saturation") : 0;+ double ll = l ? DARG_R_PRCT("$lightness") : 0;+ double aa = a ? DARG_R_FACT("$alpha") : 0; return hsla_impl(hsl_struct.h + (h ? h->value() : 0), hsl_struct.s + ss, hsl_struct.l + ll,@@ -719,7 +895,7 @@ color->b(), color->a() + (a ? a->value() : 0)); }- error("not enough arguments for `adjust-color'", pstate);+ error("not enough arguments for `adjust-color'", pstate, traces); // unreachable return color; }@@ -728,25 +904,25 @@ BUILT_IN(scale_color) { 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"]);+ Number_Ptr r = Cast<Number>(env["$red"]);+ Number_Ptr g = Cast<Number>(env["$green"]);+ Number_Ptr b = Cast<Number>(env["$blue"]);+ Number_Ptr h = Cast<Number>(env["$hue"]);+ Number_Ptr s = Cast<Number>(env["$saturation"]);+ Number_Ptr l = Cast<Number>(env["$lightness"]);+ Number_Ptr a = Cast<Number>(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) {- error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate);+ error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces); } 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;+ double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;+ double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;+ double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;+ double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; return SASS_MEMORY_NEW(Color, pstate, color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()),@@ -755,10 +931,10 @@ color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); } if (hsl) {- double hscale = (h ? ARGR("$hue", Number, -100.0, 100.0)->value() : 0.0) / 100.0;- double sscale = (s ? ARGR("$saturation", Number, -100.0, 100.0)->value() : 0.0) / 100.0;- double lscale = (l ? ARGR("$lightness", 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;+ double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;+ double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;+ double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;+ double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h); hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s);@@ -767,7 +943,7 @@ return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); } if (a) {- double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double ascale = (DARG_R_PRCT("$alpha")) / 100.0; return SASS_MEMORY_NEW(Color, pstate, color->r(),@@ -775,7 +951,7 @@ 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, traces); // unreachable return color; }@@ -784,38 +960,38 @@ BUILT_IN(change_color) { 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"]);+ Number_Ptr r = Cast<Number>(env["$red"]);+ Number_Ptr g = Cast<Number>(env["$green"]);+ Number_Ptr b = Cast<Number>(env["$blue"]);+ Number_Ptr h = Cast<Number>(env["$hue"]);+ Number_Ptr s = Cast<Number>(env["$saturation"]);+ Number_Ptr l = Cast<Number>(env["$lightness"]);+ Number_Ptr a = Cast<Number>(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) {- error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate);+ error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces); } if (rgb) { return SASS_MEMORY_NEW(Color, pstate,- r ? ARGR("$red", Number, 0, 255)->value() : color->r(),- g ? ARGR("$green", Number, 0, 255)->value() : color->g(),- b ? ARGR("$blue", Number, 0, 255)->value() : color->b(),- a ? ARGR("$alpha", Number, 0, 255)->value() : color->a());+ r ? DARG_U_BYTE("$red") : color->r(),+ g ? DARG_U_BYTE("$green") : color->g(),+ b ? DARG_U_BYTE("$blue") : color->b(),+ a ? DARG_U_BYTE("$alpha") : color->a()); } if (hsl) { HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); if (h) hsl_struct.h = std::fmod(h->value(), 360.0);- if (s) hsl_struct.s = 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();+ if (s) hsl_struct.s = DARG_U_PRCT("$saturation");+ if (l) hsl_struct.l = DARG_U_PRCT("$lightness");+ double alpha = a ? DARG_U_FACT("$alpha") : color->a(); return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); } if (a) {- double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();+ double alpha = DARG_U_FACT("$alpha"); return SASS_MEMORY_NEW(Color, pstate, color->r(),@@ -823,7 +999,7 @@ color->b(), alpha); }- error("not enough arguments for `change-color'", pstate);+ error("not enough arguments for `change-color'", pstate, traces); // unreachable return color; }@@ -866,25 +1042,26 @@ BUILT_IN(sass_unquote) { AST_Node_Obj arg = env["$string"];- if (String_Quoted_Ptr string_quoted = SASS_MEMORY_CAST(String_Quoted, arg)) {+ if (String_Quoted_Ptr string_quoted = Cast<String_Quoted>(arg)) { String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value()); // remember if the string was quoted (color tokens) result->is_delayed(true); // delay colors return result; }- else if (SASS_MEMORY_CAST(String_Constant, arg)) {- return (Expression_Ptr) &arg;+ else if (String_Constant_Ptr str = Cast<String_Constant>(arg)) {+ return str; }- else {+ else if (Expression_Ptr ex = Cast<Expression>(arg)) { Sass_Output_Style oldstyle = ctx.c_options.output_style; ctx.c_options.output_style = SASS_STYLE_NESTED; std::string val(arg->to_string(ctx.c_options));- val = SASS_MEMORY_CAST(Null, arg) ? "null" : val;+ val = Cast<Null>(arg) ? "null" : val; ctx.c_options.output_style = oldstyle; deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);- return (Expression_Ptr) &arg;+ return ex; }+ throw std::runtime_error("Invalid Data Type for unquote"); } Signature quote_sig = "quote($string)";@@ -892,7 +1069,7 @@ { 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)) {+ if (String_Quoted_Ptr qstr = Cast<String_Quoted>(arg)) { qstr->quote_mark('*'); return qstr; }@@ -915,7 +1092,7 @@ } // handle any invalid utf8 errors // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, backtrace); }+ catch (...) { handle_utf8_error(pstate, traces); } // return something even if we had an error (-1) return SASS_MEMORY_NEW(Number, pstate, (double)len); }@@ -931,8 +1108,7 @@ String_Constant_Ptr i = ARG("$insert", String_Constant); std::string ins = i->value(); ins = unquote(ins);- Number_Ptr ind = ARG("$index", Number);- double index = ind->value();+ double index = ARGVAL("$index"); size_t len = UTF_8::code_point_count(str, 0, str.size()); if (index > 0 && index <= len) {@@ -956,13 +1132,13 @@ str = ins + str; } - if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+ if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) { if (ss->quote_mark()) str = quote(str); } } // handle any invalid utf8 errors // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, backtrace); }+ catch (...) { handle_utf8_error(pstate, traces); } return SASS_MEMORY_NEW(String_Quoted, pstate, str); } @@ -986,7 +1162,7 @@ } // handle any invalid utf8 errors // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, backtrace); }+ catch (...) { handle_utf8_error(pstate, traces); } // return something even if we had an error (-1) return SASS_MEMORY_NEW(Number, pstate, (double)index); }@@ -997,15 +1173,15 @@ std::string newstr; try { 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);+ double start_at = ARGVAL("$start-at");+ double end_at = ARGVAL("$end-at");+ String_Quoted_Ptr ss = Cast<String_Quoted>(s); std::string str = unquote(s->value()); size_t size = utf8::distance(str.begin(), str.end()); - if (!SASS_MEMORY_CAST(Number, env["$end-at"])) {+ if (!Cast<Number>(env["$end-at"])) { end_at = -1; } @@ -1039,7 +1215,7 @@ } // handle any invalid utf8 errors // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, backtrace); }+ catch (...) { handle_utf8_error(pstate, traces); } return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); } @@ -1055,7 +1231,7 @@ } } - if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+ if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) { String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); cpy->value(str); return cpy;@@ -1076,7 +1252,7 @@ } } - if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+ if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) { String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); cpy->value(str); return cpy;@@ -1092,49 +1268,45 @@ Signature percentage_sig = "percentage($number)"; BUILT_IN(percentage) {- Number_Ptr n = ARG("$number", Number);- if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate);+ Number_Obj n = ARGN("$number");+ if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces); return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%"); } Signature round_sig = "round($number)"; BUILT_IN(round) {- Number_Ptr n = ARG("$number", Number);- Number_Ptr r = SASS_MEMORY_COPY(n);- r->pstate(pstate);+ Number_Obj r = ARGN("$number"); r->value(Sass::round(r->value(), ctx.c_options.precision));- return r;+ r->pstate(pstate);+ return r.detach(); } Signature ceil_sig = "ceil($number)"; BUILT_IN(ceil) {- Number_Ptr n = ARG("$number", Number);- Number_Ptr r = SASS_MEMORY_COPY(n);- r->pstate(pstate);+ Number_Obj r = ARGN("$number"); r->value(std::ceil(r->value()));- return r;+ r->pstate(pstate);+ return r.detach(); } Signature floor_sig = "floor($number)"; BUILT_IN(floor) {- Number_Ptr n = ARG("$number", Number);- Number_Ptr r = SASS_MEMORY_COPY(n);- r->pstate(pstate);+ Number_Obj r = ARGN("$number"); r->value(std::floor(r->value()));- return r;+ r->pstate(pstate);+ return r.detach(); } Signature abs_sig = "abs($number)"; BUILT_IN(abs) {- Number_Ptr n = ARG("$number", Number);- Number_Ptr r = SASS_MEMORY_COPY(n);- r->pstate(pstate);+ Number_Obj r = ARGN("$number"); r->value(std::abs(r->value()));- return r;+ r->pstate(pstate);+ return r.detach(); } Signature min_sig = "min($numbers...)";@@ -1144,9 +1316,9 @@ Number_Obj least = NULL; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression_Obj val = arglist->value_at_index(i);- Number_Obj xi = SASS_MEMORY_CAST(Number, val);+ Number_Obj xi = Cast<Number>(val); if (!xi) {- error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate);+ error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces); } if (least) { if (*xi < *least) least = xi;@@ -1162,9 +1334,9 @@ Number_Obj greatest = NULL; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression_Obj val = arglist->value_at_index(i);- Number_Obj xi = SASS_MEMORY_CAST(Number, val);+ Number_Obj xi = Cast<Number>(val); if (!xi) {- error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate);+ error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces); } if (greatest) { if (*greatest < *xi) greatest = xi;@@ -1177,23 +1349,23 @@ BUILT_IN(random) { 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);+ Value_Ptr v = Cast<Value>(arg);+ Number_Ptr l = Cast<Number>(arg);+ Boolean_Ptr b = Cast<Boolean>(arg); if (l) {- double v = l->value();- if (v < 1) {+ double lv = l->value();+ if (lv < 1) { stringstream err;- err << "$limit " << v << " must be greater than or equal to 1 for `random'";- error(err.str(), pstate);+ err << "$limit " << lv << " must be greater than or equal to 1 for `random'";+ error(err.str(), pstate, traces); }- bool eq_int = std::fabs(trunc(v) - v) < NUMBER_EPSILON;+ bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON; if (!eq_int) { stringstream err;- err << "Expected $limit to be an integer but got " << v << " for `random'";- error(err.str(), pstate);+ err << "Expected $limit to be an integer but got " << lv << " for `random'";+ error(err.str(), pstate, traces); }- std::uniform_real_distribution<> distributor(1, v + 1);+ std::uniform_real_distribution<> distributor(1, lv + 1); uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand)); return SASS_MEMORY_NEW(Number, pstate, (double)distributed); }@@ -1202,11 +1374,12 @@ double distributed = static_cast<double>(distributor(rand)); return SASS_MEMORY_NEW(Number, pstate, distributed); } else if (v) {- throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number", v);+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v); } else {- throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number");+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number"); }- return 0; } /////////////////@@ -1216,25 +1389,25 @@ Signature length_sig = "length($list)"; BUILT_IN(length) {- if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) {+ if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) { return SASS_MEMORY_NEW(Number, pstate, (double)sl->length()); } Expression_Ptr v = ARG("$list", Expression); if (v->concrete_type() == Expression::MAP) {- Map_Ptr map = SASS_MEMORY_CAST(Map, env["$list"]);+ Map_Ptr map = Cast<Map>(env["$list"]); return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1)); } if (v->concrete_type() == Expression::SELECTOR) {- if (Compound_Selector_Ptr h = dynamic_cast<Compound_Selector_Ptr>(v)) {+ if (Compound_Selector_Ptr h = Cast<Compound_Selector>(v)) { return SASS_MEMORY_NEW(Number, pstate, (double)h->length());- } else if (Selector_List_Ptr ls = SASS_MEMORY_CAST_PTR(Selector_List, v)) {+ } else if (Selector_List_Ptr ls = Cast<Selector_List>(v)) { return SASS_MEMORY_NEW(Number, pstate, (double)ls->length()); } else { return SASS_MEMORY_NEW(Number, pstate, 1); } } - List_Ptr list = SASS_MEMORY_CAST(List, env["$list"]);+ List_Ptr list = Cast<List>(env["$list"]); return SASS_MEMORY_NEW(Number, pstate, (double)(list ? list->size() : 1));@@ -1243,20 +1416,20 @@ Signature nth_sig = "nth($list, $n)"; BUILT_IN(nth) {- 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"])) {+ double nr = ARGVAL("$n");+ Map_Ptr m = Cast<Map>(env["$list"]);+ if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) { size_t len = m ? m->length() : sl->length(); bool empty = m ? m->empty() : sl->empty();- if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);- 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);+ if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+ double index = std::floor(nr < 0 ? len + nr : nr - 1);+ if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); // return (*sl)[static_cast<int>(index)]; Listize listize; return (*sl)[static_cast<int>(index)]->perform(&listize); }- List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);- if (n->value() == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate);+ List_Obj l = Cast<List>(env["$list"]);+ if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces); // if the argument isn't a list, then wrap it in a singleton list if (!m && !l) { l = SASS_MEMORY_NEW(List, pstate, 1);@@ -1264,9 +1437,9 @@ } size_t len = m ? m->length() : l->length(); bool empty = m ? m->empty() : l->empty();- if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);- 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);+ if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+ double index = std::floor(nr < 0 ? len + nr : nr - 1);+ if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces); if (m) { l = SASS_MEMORY_NEW(List, pstate, 1);@@ -1284,8 +1457,8 @@ Signature set_nth_sig = "set-nth($list, $n, $value)"; BUILT_IN(set_nth) {- Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);- List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ Map_Obj m = Cast<Map>(env["$list"]);+ List_Obj l = Cast<List>(env["$list"]); Number_Obj n = ARG("$n", Number); Expression_Obj v = ARG("$value", Expression); if (!l) {@@ -1293,12 +1466,12 @@ l->append(ARG("$list", Expression)); } if (m) {- l = m->to_list(ctx, pstate);+ l = m->to_list(pstate); }- if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);+ if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces); double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);- if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);- List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator());+ if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);+ List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { result->append(((i == index) ? v : (*l)[i])); }@@ -1308,67 +1481,75 @@ Signature index_sig = "index($list, $value)"; BUILT_IN(index) {- Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);- List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ Map_Obj m = Cast<Map>(env["$list"]);+ List_Obj l = Cast<List>(env["$list"]); Expression_Obj v = ARG("$value", Expression); if (!l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression)); } if (m) {- l = m->to_list(ctx, pstate);+ l = m->to_list(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(Number, pstate, (double)(i+1));+ if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1)); } return SASS_MEMORY_NEW(Null, pstate); } - Signature join_sig = "join($list1, $list2, $separator: auto)";+ Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)"; BUILT_IN(join) {- 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"]);+ Map_Obj m1 = Cast<Map>(env["$list1"]);+ Map_Obj m2 = Cast<Map>(env["$list2"]);+ List_Obj l1 = Cast<List>(env["$list1"]);+ List_Obj l2 = Cast<List>(env["$list2"]); String_Constant_Obj sep = ARG("$separator", String_Constant); enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);+ Value* bracketed = ARG("$bracketed", Value);+ bool is_bracketed = (l1 ? l1->is_bracketed() : false); if (!l1) { l1 = SASS_MEMORY_NEW(List, pstate, 1); l1->append(ARG("$list1", Expression)); sep_val = (l2 ? l2->separator() : SASS_SPACE);+ is_bracketed = (l2 ? l2->is_bracketed() : false); } if (!l2) { l2 = SASS_MEMORY_NEW(List, pstate, 1); l2->append(ARG("$list2", Expression)); } if (m1) {- l1 = m1->to_list(ctx, pstate);+ l1 = m1->to_list(pstate); sep_val = SASS_COMMA; } if (m2) {- l2 = m2->to_list(ctx, pstate);+ l2 = m2->to_list(pstate); } size_t len = l1->length() + l2->length(); std::string sep_str = unquote(sep->value()); if (sep_str == "space") sep_val = SASS_SPACE; else if (sep_str == "comma") sep_val = SASS_COMMA;- else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);- List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val);- result->concat(&l1);- result->concat(&l2);+ else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);+ String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);+ bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";+ if (!bracketed_is_auto) {+ is_bracketed = !bracketed->is_false();+ }+ List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed);+ result->concat(l1);+ result->concat(l2); return result.detach(); } Signature append_sig = "append($list, $val, $separator: auto)"; BUILT_IN(append) {- Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);- List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ Map_Obj m = Cast<Map>(env["$list"]);+ List_Obj l = Cast<List>(env["$list"]); Expression_Obj v = ARG("$val", Expression);- if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) {+ if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) { Listize listize;- l = SASS_MEMORY_CAST_PTR(List, sl->perform(&listize));+ l = Cast<List>(sl->perform(&listize)); } String_Constant_Obj sep = ARG("$separator", String_Constant); if (!l) {@@ -1376,14 +1557,14 @@ l->append(ARG("$list", Expression)); } if (m) {- l = m->to_list(ctx, pstate);+ l = m->to_list(pstate); } List_Ptr result = SASS_MEMORY_COPY(l); std::string sep_str(unquote(sep->value())); if (sep_str != "auto") { // check default first if (sep_str == "space") result->separator(SASS_SPACE); else if (sep_str == "comma") result->separator(SASS_COMMA);- else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);+ else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); } if (l->is_arglist()) { result->append(SASS_MEMORY_NEW(Argument,@@ -1405,20 +1586,20 @@ List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List)); size_t shortest = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- List_Obj ith = SASS_MEMORY_CAST(List, arglist->value_at_index(i));- Map_Obj mith = SASS_MEMORY_CAST(Map, arglist->value_at_index(i));+ List_Obj ith = Cast<List>(arglist->value_at_index(i));+ Map_Obj mith = Cast<Map>(arglist->value_at_index(i)); if (!ith) { if (mith) {- ith = mith->to_list(ctx, pstate);+ ith = mith->to_list(pstate); } else { ith = SASS_MEMORY_NEW(List, pstate, 1); ith->append(arglist->value_at_index(i)); } if (arglist->is_arglist()) {- Argument_Obj arg = (Argument_Ptr)(&arglist->at(i));- arg->value(&ith);+ Argument_Obj arg = (Argument_Ptr)(arglist->at(i).ptr()); // XXX+ arg->value(ith); } else {- (*arglist)[i] = &ith;+ (*arglist)[i] = ith; } } shortest = (i ? std::min(shortest, ith->length()) : ith->length());@@ -1428,7 +1609,7 @@ for (size_t i = 0; i < shortest; ++i) { List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L); for (size_t j = 0; j < L; ++j) {- zipper->append(SASS_MEMORY_CAST(List, arglist->value_at_index(j))->at(i));+ zipper->append(Cast<List>(arglist->value_at_index(j))->at(i)); } zippers->append(zipper); }@@ -1438,7 +1619,7 @@ Signature list_separator_sig = "list_separator($list)"; BUILT_IN(list_separator) {- List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ List_Obj l = Cast<List>(env["$list"]); if (!l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression));@@ -1460,8 +1641,10 @@ Map_Obj m = ARGM("$map", Map, ctx); Expression_Obj v = ARG("$key", Expression); try {- Expression_Obj val = m->at(v); // XXX- return val ? val.detach() : SASS_MEMORY_NEW(Null, pstate);+ Expression_Obj val = m->at(v);+ if (!val) return SASS_MEMORY_NEW(Null, pstate);+ val->set_delayed(false);+ return val.detach(); } catch (const std::out_of_range&) { return SASS_MEMORY_NEW(Null, pstate); }@@ -1507,8 +1690,8 @@ size_t len = m1->length() + m2->length(); Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len); // concat not implemented for maps- *result += &m1;- *result += &m2;+ *result += m1;+ *result += m2; return result; } @@ -1522,7 +1705,7 @@ for (auto key : m->keys()) { remove = false; for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {- remove = Eval::eq(key, arglist->value_at_index(j));+ remove = Operators::eq(key, arglist->value_at_index(j)); } if (!remove) *result << std::make_pair(key, m->at(key)); }@@ -1536,7 +1719,7 @@ Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1); for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { Expression_Obj obj = arglist->at(i);- Argument_Obj arg = (Argument_Ptr)&obj;+ Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX std::string name = std::string(arg->name()); name = name.erase(0, 1); // sanitize name (remove dollar sign) *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,@@ -1559,23 +1742,33 @@ Signature unit_sig = "unit($number)"; BUILT_IN(unit)- { return SASS_MEMORY_NEW(String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); }+ {+ Number_Obj arg = ARGN("$number");+ std::string str(quote(arg->unit(), '"'));+ return SASS_MEMORY_NEW(String_Quoted, pstate, str);+ } Signature unitless_sig = "unitless($number)"; BUILT_IN(unitless)- { return SASS_MEMORY_NEW(Boolean, pstate, ARG("$number", Number)->is_unitless()); }+ {+ Number_Obj arg = ARGN("$number");+ bool unitless = arg->is_unitless();+ return SASS_MEMORY_NEW(Boolean, pstate, unitless);+ } Signature comparable_sig = "comparable($number-1, $number-2)"; BUILT_IN(comparable) {- Number_Ptr n1 = ARG("$number-1", Number);- Number_Ptr n2 = ARG("$number-2", Number);+ Number_Obj n1 = ARGN("$number-1");+ Number_Obj n2 = ARGN("$number-2"); if (n1->is_unitless() || n2->is_unitless()) { return SASS_MEMORY_NEW(Boolean, pstate, true); }- Number tmp_n2(n2); // copy- tmp_n2.normalize(n1->find_convertible_unit());- return SASS_MEMORY_NEW(Boolean, pstate, n1->unit() == tmp_n2.unit());+ // normalize into main units+ n1->normalize(); n2->normalize();+ Units &lhs_unit = *n1, &rhs_unit = *n2;+ bool is_comparable = (lhs_unit == rhs_unit);+ return SASS_MEMORY_NEW(Boolean, pstate, is_comparable); } Signature variable_exists_sig = "variable-exists($name)";@@ -1607,9 +1800,14 @@ Signature function_exists_sig = "function-exists($name)"; BUILT_IN(function_exists) {- std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));+ String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);+ if (!ss) {+ error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);+ } - if(d_env.has_global(s+"[f]")) {+ std::string name = Util::normalize_underscores(unquote(ss->value()));++ if(d_env.has_global(name+"[f]")) { return SASS_MEMORY_NEW(Boolean, pstate, true); } else {@@ -1646,24 +1844,37 @@ Signature call_sig = "call($name, $args...)"; BUILT_IN(call) {- std::string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));+ std::string name;+ Function_Ptr ff = Cast<Function>(env["$name"]);+ String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);++ if (ss) {+ name = Util::normalize_underscores(unquote(ss->value()));+ std::cerr << "DEPRECATION WARNING: ";+ std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;+ std::cerr << "in Sass 4.0. Use call(get-function(" + quote(name) + ")) instead." << std::endl;+ std::cerr << std::endl;+ } else if (ff) {+ name = ff->name();+ }+ List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); // std::string full_name(name + "[f]");- // Definition_Ptr def = d_env.has(full_name) ? static_cast<Definition_Ptr>((d_env)[full_name]) : 0;+ // Definition_Ptr def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0; // Parameters_Ptr params = def ? def->parameters() : 0; // size_t param_size = params ? params->length() : 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression_Obj expr = arglist->value_at_index(i); // if (params && params->has_rest_parameter()) { // Parameter_Obj p = param_size > i ? (*params)[i] : 0;- // List_Ptr list = SASS_MEMORY_CAST(List, expr);+ // List_Ptr list = Cast<List>(expr); // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; // } if (arglist->is_arglist()) { Expression_Obj obj = arglist->at(i);- Argument_Obj arg = (Argument_Ptr)&obj;+ Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX args->append(SASS_MEMORY_NEW(Argument, pstate, expr,@@ -1674,11 +1885,11 @@ args->append(SASS_MEMORY_NEW(Argument, pstate, expr)); } }- Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, &args);- Expand expand(ctx, &d_env, backtrace, &selector_stack);+ Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args);+ Expand expand(ctx, &d_env, &selector_stack); func->via_call(true); // calc invoke is allowed+ if (ff) func->func(ff); return func->perform(&expand.eval);- } ////////////////////@@ -1696,24 +1907,13 @@ // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } BUILT_IN(sass_if) {- Expand expand(ctx, &d_env, backtrace, &selector_stack);+ Expand expand(ctx, &d_env, &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);+ Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression); res = res->perform(&expand.eval); res->set_delayed(false); // clone?- return res;- }-- ////////////////- // URL FUNCTIONS- ////////////////-- Signature image_url_sig = "image-url($path, $only-path: false, $cache-buster: false)";- BUILT_IN(image_url)- {- error("`image_url` has been removed from libsass because it's not part of the Sass spec", pstate);- return 0; // suppress warning, error will exit anyway+ return res.detach(); } //////////////////////////@@ -1754,24 +1954,24 @@ // Not enough parameters if( arglist->length() == 0 )- error("$selectors: At least one selector must be passed for `selector-nest'", pstate);+ error("$selectors: At least one selector must be passed for `selector-nest'", pstate, traces); // Parse args into vector of selectors std::vector<Selector_List_Obj> parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj exp = SASS_MEMORY_CAST(Expression, arglist->value_at_index(i));+ Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i)); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << "$selectors: null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for 'selector-nest'";- error(msg.str(), pstate);+ error(msg.str(), pstate, traces); }- if (String_Constant_Obj str = SASS_MEMORY_CAST(String_Constant, exp)) {+ if (String_Constant_Obj str = Cast<String_Constant>(exp)) { str->quote_mark(0); }- std::string exp_src = exp->to_string(ctx.c_options) + "{";- Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx);- parsedSelectors.push_back(&sel);+ std::string exp_src = exp->to_string(ctx.c_options);+ Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);+ parsedSelectors.push_back(sel); } // Nothing to do@@ -1787,8 +1987,8 @@ for(;itr != parsedSelectors.end(); ++itr) { Selector_List_Obj child = *itr; std::vector<Complex_Selector_Obj> exploded;- selector_stack.push_back(&result);- Selector_List_Obj rv = child->resolve_parent_refs(ctx, selector_stack);+ selector_stack.push_back(result);+ Selector_List_Obj rv = child->resolve_parent_refs(selector_stack, traces); selector_stack.pop_back(); for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) { exploded.push_back((*rv)[m]);@@ -1807,24 +2007,24 @@ // Not enough parameters if( arglist->length() == 0 )- error("$selectors: At least one selector must be passed for `selector-append'", pstate);+ error("$selectors: At least one selector must be passed for `selector-append'", pstate, traces); // Parse args into vector of selectors std::vector<Selector_List_Obj> parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj exp = SASS_MEMORY_CAST(Expression, arglist->value_at_index(i));+ Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i)); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << "$selectors: null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for 'selector-append'";- error(msg.str(), pstate);+ error(msg.str(), pstate, traces); }- if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+ if (String_Constant_Ptr str = Cast<String_Constant>(exp)) { str->quote_mark(0); }- std::string exp_src = exp->to_string() + "{";- Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx);- parsedSelectors.push_back(&sel);+ std::string exp_src = exp->to_string();+ Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);+ parsedSelectors.push_back(sel); } // Nothing to do@@ -1861,29 +2061,29 @@ msg += "\" to \""; msg += parentSeqClone->to_string(); msg += "\" for `selector-append'";- error(msg, pstate, backtrace);+ error(msg, pstate, traces); } // Cannot be a Universal selector- Element_Selector_Obj pType = SASS_MEMORY_CAST(Element_Selector, childSeq->head()->first());+ Element_Selector_Obj pType = Cast<Element_Selector>(childSeq->head()->first()); if(pType && pType->name() == "*") { std::string msg("Can't append \""); msg += childSeq->to_string(); msg += "\" to \""; msg += parentSeqClone->to_string(); msg += "\" for `selector-append'";- error(msg, pstate, backtrace);+ error(msg, pstate, traces); } // TODO: Add check for namespace stuff // append any selectors in childSeq's head- parentSeqClone->innermost()->head()->concat(&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); } } @@ -1900,7 +2100,7 @@ Selector_List_Obj selector1 = ARGSEL("$selector1", Selector_List_Obj, p_contextualize); Selector_List_Obj selector2 = ARGSEL("$selector2", Selector_List_Obj, p_contextualize); - Selector_List_Obj result = selector1->unify_with(&selector2, ctx);+ Selector_List_Obj result = selector1->unify_with(selector2); Listize listize; return result->perform(&listize); }@@ -1930,9 +2130,10 @@ Selector_List_Obj extender = ARGSEL("$extender", Selector_List_Obj, p_contextualize); Subset_Map subset_map;- extender->populate_extends(extendee, ctx, subset_map);+ extender->populate_extends(extendee, subset_map);+ Extend extend(subset_map); - Selector_List_Obj result = Extend::extendSelectorList(selector, ctx, subset_map, false);+ Selector_List_Obj result = extend.extendSelectorList(selector, false); Listize listize; return result->perform(&listize);@@ -1945,9 +2146,10 @@ 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);+ replacement->populate_extends(original, subset_map);+ Extend extend(subset_map); - Selector_List_Obj result = Extend::extendSelectorList(selector, ctx, subset_map, true);+ Selector_List_Obj result = extend.extendSelectorList(selector, true); Listize listize; return result->perform(&listize);@@ -1981,5 +2183,52 @@ return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str()); } + Signature is_bracketed_sig = "is-bracketed($list)";+ BUILT_IN(is_bracketed)+ {+ Value_Obj value = ARG("$list", Value);+ List_Obj list = Cast<List>(value);+ return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed());+ }++ Signature content_exists_sig = "content-exists()";+ BUILT_IN(content_exists)+ {+ if (!d_env.has_global("is_in_mixin")) {+ error("Cannot call content-exists() except within a mixin.", pstate, traces);+ }+ return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));+ }++ Signature get_function_sig = "get-function($name, $css: false)";+ BUILT_IN(get_function)+ {+ String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);+ if (!ss) {+ error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);+ }++ std::string name = Util::normalize_underscores(unquote(ss->value()));+ std::string full_name = name + "[f]";++ Boolean_Obj css = ARG("$css", Boolean);+ if (!css->is_false()) {+ Definition_Ptr def = SASS_MEMORY_NEW(Definition,+ pstate,+ name,+ SASS_MEMORY_NEW(Parameters, pstate),+ SASS_MEMORY_NEW(Block, pstate, 0, false),+ Definition::FUNCTION);+ return SASS_MEMORY_NEW(Function, pstate, def, true);+ }+++ if (!d_env.has_global(full_name)) {+ error("Function not found: " + name, pstate, traces);+ }++ Definition_Ptr def = Cast<Definition>(d_env[full_name]);+ return SASS_MEMORY_NEW(Function, pstate, def, false);+ } } }
libsass/src/functions.hpp view
@@ -8,13 +8,12 @@ #include "sass/functions.h" #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)+name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtraces traces, std::vector<Selector_List_Obj> selector_stack) namespace Sass { struct Backtrace;- typedef 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>);+ typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>); Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx); Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx);@@ -89,7 +88,6 @@ extern Signature call_sig; extern Signature not_sig; extern Signature if_sig;- extern Signature image_url_sig; extern Signature map_get_sig; extern Signature map_merge_sig; extern Signature map_remove_sig;@@ -107,6 +105,9 @@ extern Signature is_superselector_sig; extern Signature simple_selectors_sig; extern Signature selector_parse_sig;+ extern Signature is_bracketed_sig;+ extern Signature content_exists_sig;+ extern Signature get_function_sig; BUILT_IN(rgb); BUILT_IN(rgba_4);@@ -171,7 +172,6 @@ BUILT_IN(call); BUILT_IN(sass_not); BUILT_IN(sass_if);- BUILT_IN(image_url); BUILT_IN(map_get); BUILT_IN(map_merge); BUILT_IN(map_remove);@@ -189,6 +189,9 @@ BUILT_IN(is_superselector); BUILT_IN(simple_selectors); BUILT_IN(selector_parse);+ BUILT_IN(is_bracketed);+ BUILT_IN(content_exists);+ BUILT_IN(get_function); } }
libsass/src/inspect.cpp view
@@ -15,7 +15,7 @@ namespace Sass { - Inspect::Inspect(Emitter emi)+ Inspect::Inspect(const Emitter& emi) : Emitter(emi) { } Inspect::~Inspect() { }@@ -42,7 +42,9 @@ void Inspect::operator()(Ruleset_Ptr ruleset) { if (ruleset->selector()) {+ opt.in_selector = true; ruleset->selector()->perform(this);+ opt.in_selector = false; } if (ruleset->block()) { ruleset->block()->perform(this);@@ -90,7 +92,7 @@ append_token("@at-root ", at_root_block); append_mandatory_space(); if(at_root_block->expression()) at_root_block->expression()->perform(this);- at_root_block->block()->perform(this);+ if(at_root_block->block()) at_root_block->block()->perform(this); } void Inspect::operator()(Directive_Ptr at_rule)@@ -121,6 +123,8 @@ if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true;+ LOCAL_FLAG(in_custom_property, dec->is_custom_property());+ if (output_style() == NESTED) indentation += dec->tabs(); append_indentation();@@ -166,7 +170,7 @@ import->urls().front()->perform(this); if (import->urls().size() == 1) {- if (&import->import_queries()) {+ if (import->import_queries()) { append_mandatory_space(); import->import_queries()->perform(this); }@@ -179,7 +183,7 @@ import->urls()[i]->perform(this); if (import->urls().size() - 1 == i) {- if (&import->import_queries()) {+ if (import->import_queries()) { append_mandatory_space(); import->import_queries()->perform(this); }@@ -354,16 +358,27 @@ if (items_output) append_comma_separator(); key->perform(this); append_colon_separator();+ LOCAL_FLAG(in_space_array, true);+ LOCAL_FLAG(in_comma_array, true); map->at(key)->perform(this); items_output = true; } append_string(")"); } + std::string Inspect::lbracket(List_Ptr list) {+ return list->is_bracketed() ? "[" : "(";+ }++ std::string Inspect::rbracket(List_Ptr list) {+ return list->is_bracketed() ? "]" : ")";+ }+ void Inspect::operator()(List_Ptr list) {- if (output_style() == TO_SASS && list->empty()) {- append_string("()");+ if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) {+ append_string(lbracket(list));+ append_string(rbracket(list)); return; } std::string sep(list->separator() == SASS_SPACE ? " " : ",");@@ -374,19 +389,24 @@ bool was_space_array = in_space_array; bool was_comma_array = in_comma_array;+ // if the list is bracketed, always include the left bracket+ if (list->is_bracketed()) {+ append_string(lbracket(list));+ } // probably ruby sass eqivalent of element_needs_parens- if (output_style() == TO_SASS &&+ else 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("(");+ !Cast<List>(list->at(0)) &&+ !Cast<Selector_List>(list->at(0))+ ) {+ append_string(lbracket(list)); } else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) {- append_string("(");+ append_string(lbracket(list)); } if (list->separator() == SASS_SPACE) in_space_array = true;@@ -399,7 +419,7 @@ 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)) {+ if (!Cast<String_Constant>(list_item)) { continue; } }@@ -415,19 +435,29 @@ in_comma_array = was_comma_array; in_space_array = was_space_array;++ // if the list is bracketed, always include the right bracket+ if (list->is_bracketed()) {+ if (list->separator() == SASS_COMMA && list->size() == 1) {+ append_string(",");+ }+ append_string(rbracket(list));+ } // probably ruby sass eqivalent of element_needs_parens- if (output_style() == TO_SASS &&+ else 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(",)");+ !Cast<List>(list->at(0)) &&+ !Cast<Selector_List>(list->at(0))+ ) {+ append_string(",");+ append_string(rbracket(list)); } else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) {- append_string(")");+ append_string(rbracket(list)); } }@@ -443,7 +473,7 @@ expr->is_right_interpolant()) )) append_string(" ");- switch (expr->type()) {+ switch (expr->optype()) { case Sass_OP::AND: append_string("&&"); break; case Sass_OP::OR: append_string("||"); break; case Sass_OP::EQ: append_string("=="); break;@@ -471,8 +501,9 @@ void Inspect::operator()(Unary_Expression_Ptr expr) {- if (expr->type() == Unary_Expression::PLUS) append_string("+");- else append_string("-");+ if (expr->optype() == Unary_Expression::PLUS) append_string("+");+ else if (expr->optype() == Unary_Expression::SLASH) append_string("/");+ else append_string("-"); expr->operand()->perform(this); } @@ -493,16 +524,14 @@ append_token(var->name(), var); } - void Inspect::operator()(Textual_Ptr txt)- {- append_token(txt->value(), txt);- }- void Inspect::operator()(Number_Ptr n) { std::string res; + // reduce units+ n->reduce();+ // check if the fractional part of the value equals to zero // neat trick from http://stackoverflow.com/a/1521682/1550314 // double int_part; bool is_int = modf(value, &int_part) == 0.0;@@ -600,6 +629,11 @@ // maybe an unknown token std::string name = c->disp(); + if (opt.in_selector && name != "") {+ append_token(name, c);+ return;+ }+ // resolved color std::string res_name = name; @@ -648,7 +682,7 @@ ss << name; } else if (r == 0 && g == 0 && b == 0 && a == 0) {- ss << "transparent";+ ss << "transparent"; } else if (a >= 1) { if (res_name != "") {@@ -744,9 +778,9 @@ { 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_Ptr sd)@@ -766,7 +800,7 @@ 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);@@ -798,12 +832,22 @@ void Inspect::operator()(At_Root_Query_Ptr ae) {- append_string("(");- ae->feature()->perform(this);- if (ae->value()) {- append_colon_separator();- ae->value()->perform(this);+ if (ae->feature()) {+ append_string("(");+ ae->feature()->perform(this);+ if (ae->value()) {+ append_colon_separator();+ ae->value()->perform(this);+ }+ append_string(")"); }+ }++ void Inspect::operator()(Function_Ptr f)+ {+ append_token("get-function", f);+ append_string("(");+ append_string(quote(f->name())); append_string(")"); } @@ -851,7 +895,7 @@ return; } if (a->value()->concrete_type() == Expression::STRING) {- String_Constant_Ptr s = SASS_MEMORY_CAST(String_Constant, a->value());+ String_Constant_Ptr s = Cast<String_Constant>(a->value()); if (s) s->perform(this); } else { a->value()->perform(this);@@ -877,7 +921,9 @@ void Inspect::operator()(Selector_Schema_Ptr s) {+ opt.in_selector = true; s->contents()->perform(this);+ opt.in_selector = false; } void Inspect::operator()(Parent_Selector_Ptr p)@@ -919,11 +965,15 @@ append_token(s->ns_name(), s); if (!s->matcher().empty()) { append_string(s->matcher());- if (&s->value() && *s->value()) {+ if (s->value() && *s->value()) { s->value()->perform(this); } } add_close_mapping(s);+ if (s->modifier() != 0) {+ append_mandatory_space();+ append_char(s->modifier());+ } append_string("]"); } @@ -939,16 +989,20 @@ void Inspect::operator()(Wrapped_Selector_Ptr s) {- bool was = in_wrapped;- in_wrapped = true;- append_token(s->name(), s);- append_string("(");- bool was_comma_array = in_comma_array;- in_comma_array = false;- s->selector()->perform(this);- in_comma_array = was_comma_array;- append_string(")");- in_wrapped = was;+ if (s->name() == " ") {+ append_string("");+ } else {+ bool was = in_wrapped;+ in_wrapped = true;+ append_token(s->name(), s);+ append_string("(");+ bool was_comma_array = in_comma_array;+ in_comma_array = false;+ s->selector()->perform(this);+ in_comma_array = was_comma_array;+ append_string(")");+ in_wrapped = was;+ } } void Inspect::operator()(Compound_Selector_Ptr s)@@ -1014,6 +1068,7 @@ if (tail) append_mandatory_space(); else append_optional_space(); break;+ default: break; } if (tail && comb != Complex_Selector::ANCESTOR_OF) { if (c->has_line_break()) append_optional_linefeed();@@ -1040,8 +1095,8 @@ bool was_comma_array = 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]))) {+ (!Cast<List>((*g)[0]) &&+ !Cast<Selector_List>((*g)[0]))) { append_string("("); } else if (!in_declaration && in_comma_array) {@@ -1053,7 +1108,7 @@ 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());+ schedule_mapping(g->at(i)->last()); // add_open_mapping((*g)[i]->last()); (*g)[i]->perform(this); // add_close_mapping((*g)[i]->last());@@ -1066,8 +1121,8 @@ in_comma_array = was_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]))) {+ (!Cast<List>((*g)[0]) &&+ !Cast<Selector_List>((*g)[0]))) { append_string(",)"); } else if (!in_declaration && in_comma_array) {
libsass/src/inspect.hpp view
@@ -17,7 +17,7 @@ public: - Inspect(Emitter emi);+ Inspect(const Emitter& emi); virtual ~Inspect(); // statements@@ -48,6 +48,7 @@ virtual void operator()(Content_Ptr); // expressions virtual void operator()(Map_Ptr);+ virtual void operator()(Function_Ptr); virtual void operator()(List_Ptr); virtual void operator()(Binary_Expression_Ptr); virtual void operator()(Unary_Expression_Ptr);@@ -56,7 +57,6 @@ // 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);@@ -91,6 +91,9 @@ virtual void operator()(Compound_Selector_Ptr); virtual void operator()(Complex_Selector_Ptr); virtual void operator()(Selector_List_Ptr);++ virtual std::string lbracket(List_Ptr);+ virtual std::string rbracket(List_Ptr); // template <typename U> // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node_Ptr>(x)); }
libsass/src/lexer.cpp view
@@ -1,6 +1,5 @@ #include "sass.hpp" #include <cctype>-#include <cstddef> #include <iostream> #include <iomanip> #include "lexer.hpp"@@ -50,6 +49,12 @@ return unsigned(chr - '0') <= '9' - '0'; } + bool is_number(const char& chr)+ {+ // adapted the technique from is_alpha+ return is_digit(chr) || chr == '-' || chr == '+';+ }+ bool is_xdigit(const char& chr) { // adapted the technique from is_alpha@@ -80,10 +85,11 @@ // but with specific ranges (copied from Ruby Sass) bool is_nonascii(const char& chr) {+ unsigned int cmp = unsigned(chr); return (- (unsigned(chr) >= 128 && unsigned(chr) <= 15572911) ||- (unsigned(chr) >= 15630464 && unsigned(chr) <= 15712189) ||- (unsigned(chr) >= 4036001920)+ (cmp >= 128 && cmp <= 15572911) ||+ (cmp >= 15630464 && cmp <= 15712189) ||+ (cmp >= 4036001920) ); } @@ -91,15 +97,17 @@ // valid in a uri (copied from Ruby Sass) bool is_uri_character(const char& chr) {- return (unsigned(chr) > 41 && unsigned(chr) < 127) ||- unsigned(chr) == ':' || unsigned(chr) == '/';+ unsigned int cmp = unsigned(chr);+ return (cmp > 41 && cmp < 127) ||+ cmp == ':' || cmp == '/'; } // check if char is within a reduced ascii range // valid for escaping (copied from Ruby Sass) bool is_escapable_character(const char& chr) {- return unsigned(chr) > 31 && unsigned(chr) < 127;+ unsigned int cmp = unsigned(chr);+ return cmp > 31 && cmp < 127; } // Match word character (look ahead)
libsass/src/lexer.hpp view
@@ -29,6 +29,7 @@ bool is_alpha(const char& src); bool is_punct(const char& src); bool is_digit(const char& src);+ bool is_number(const char& src); bool is_alnum(const char& src); bool is_xdigit(const char& src); bool is_unicode(const char& src);@@ -96,9 +97,9 @@ // Regex equivalent: /(?:literal)/ template <const char* str> const char* exactly(const char* src) {- if (str == 0) return 0;+ if (str == NULL) return 0; const char* pre = str;- if (src == 0) return 0;+ if (src == NULL) return 0; // there is a small chance that the search string // is longer than the rest of the string to look at while (*pre && *src == *pre) {@@ -109,14 +110,22 @@ } + // Match a single character literal.+ // Regex equivalent: /(?:x)/i+ // only define lower case alpha chars+ template <char chr>+ const char* insensitive(const char* src) {+ return *src == chr || *src+32 == chr ? src + 1 : 0;+ }+ // Match the full string literal. // Regex equivalent: /(?:literal)/i // only define lower case alpha chars template <const char* str> const char* insensitive(const char* src) {- if (str == 0) return 0;+ if (str == NULL) return 0; const char* pre = str;- if (src == 0) return 0;+ if (src == NULL) return 0; // there is a small chance that the search string // is longer than the rest of the string to look at while (*pre && (*src == *pre || *src+32 == *pre)) {
libsass/src/listize.cpp view
@@ -64,13 +64,14 @@ break; case Complex_Selector::ANCESTOR_OF: break;+ default: break; } Complex_Selector_Obj tail = sel->tail(); if (tail) { Expression_Obj tt = tail->perform(this);- if (List_Ptr ls = SASS_MEMORY_CAST(List, tt))+ if (List_Ptr ls = Cast<List>(tt)) { l->concat(ls); } } if (l->length() == 0) return 0;@@ -79,7 +80,7 @@ Expression_Ptr Listize::fallback_impl(AST_Node_Ptr n) {- return dynamic_cast<Expression_Ptr>(n);+ return Cast<Expression>(n); } }
libsass/src/listize.hpp view
@@ -11,7 +11,6 @@ namespace Sass { - typedef Environment<AST_Node_Obj> Env; struct Backtrace; class Listize : public Operation_CRTP<Expression_Ptr, Listize> {
@@ -17,8 +17,8 @@ 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)) {+ for (SharedObj* var : all) {+ if (AST_Node_Ptr ast = dynamic_cast<AST_Node*>(var)) { debug_ast(ast); } else { std::cerr << "LEAKED " << var << "\n";@@ -37,22 +37,20 @@ , 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- };-+ 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) {@@ -62,7 +60,7 @@ #endif if (node->refcounter == 0) { #ifdef DEBUG_SHARED_PTR- AST_Node_Ptr ptr = SASS_MEMORY_CAST_PTR(AST_Node, node);+ // AST_Node_Ptr ast = dynamic_cast<AST_Node*>(node); if (node->dbg) std::cerr << "DELETE NODE " << node << "\n"; #endif if (!node->detached) {
@@ -17,7 +17,7 @@ #ifdef DEBUG_SHARED_PTR #define SASS_MEMORY_NEW(Class, ...) \- static_cast<Class##_Ptr>((new Class(__VA_ARGS__))->trace(__FILE__, __LINE__)) \+ ((Class*)(new Class(__VA_ARGS__))->trace(__FILE__, __LINE__)) \ #define SASS_MEMORY_COPY(obj) \ ((obj)->copy(__FILE__, __LINE__)) \@@ -38,12 +38,6 @@ #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;@@ -92,9 +86,9 @@ class SharedPtr {- private:+ protected: SharedObj* node;- private:+ protected: void decRefCount(); void incRefCount(); public:@@ -103,24 +97,21 @@ : 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();+ SharedPtr(SharedPtr&& obj);+ // copy assignment operator+ SharedPtr& operator=(const SharedPtr& obj);+ // move assignment operator+ SharedPtr& operator=(SharedPtr&& obj);+ // pure virtual destructor+ virtual ~SharedPtr() = 0; public:- SharedObj* obj () {- return node;- }; SharedObj* obj () const { return node; };- SharedObj* operator-> () {+ SharedObj* operator-> () const { return node; }; bool isNull () {@@ -129,68 +120,81 @@ 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 >+ template < class T > class SharedImpl : private SharedPtr { public: SharedImpl() : SharedPtr(NULL) {}; SharedImpl(T* node) : SharedPtr(node) {};+ template < class U >+ SharedImpl(SharedImpl<U> obj)+ : SharedPtr(static_cast<T*>(obj.ptr())) {} SharedImpl(T&& node) : SharedPtr(node) {}; SharedImpl(const T& node) : SharedPtr(node) {};+ // the copy constructor+ SharedImpl(const SharedImpl<T>& impl)+ : SharedPtr(impl.node) {};+ // the move constructor+ SharedImpl(SharedImpl<T>&& impl)+ : SharedPtr(impl.node) {};+ // copy assignment operator+ SharedImpl<T>& operator=(const SharedImpl<T>& rhs) {+ if (node) decRefCount();+ node = rhs.node;+ incRefCount();+ return *this;+ }+ // move assignment operator+ SharedImpl<T>& operator=(SharedImpl<T>&& rhs) {+ // don't move our self+ if (this != &rhs) {+ if (node) decRefCount();+ node = std::move(rhs.node);+ rhs.node = NULL;+ }+ return *this;+ } ~SharedImpl() {}; public:- T* operator& () {- return static_cast<T*>(this->obj());- };- T* operator& () const {+ operator T*() const { return static_cast<T*>(this->obj());- };- T& operator* () {+ }+ operator T&() const { 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 () {+ T* ptr () const { return static_cast<T*>(this->obj()); };- T* detach() {+ T* detach() const { if (this->obj() == NULL) return NULL; return static_cast<T*>(SharedPtr::detach()); }- bool isNull() {+ bool isNull() const { return this->obj() == NULL; }- operator bool() {- return this->obj() != NULL;+ bool operator<(const T& rhs) const {+ return *this->ptr() < rhs; }; operator bool() const { return this->obj() != NULL;
libsass/src/node.cpp view
@@ -14,15 +14,15 @@ } - Node Node::createSelector(Complex_Selector_Ptr pSelector, Context& ctx) {+ Node Node::createSelector(const Complex_Selector& pSelector) { NodeDequePtr null; - Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(pSelector);+ Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(&pSelector); pStripped->tail(NULL); pStripped->combinator(Complex_Selector::ANCESTOR_OF); Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/);- if (pSelector) n.got_line_feed = pSelector->has_line_feed();+ n.got_line_feed = pSelector.has_line_feed(); return n; } @@ -50,12 +50,12 @@ { if (pSelector) got_line_feed = pSelector->has_line_feed(); } - Node Node::klone(Context& ctx) const {+ Node Node::klone() const { NodeDequePtr pNewCollection = std::make_shared<NodeDeque>(); if (mpCollection) { for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toClone = *iter;- pNewCollection->push_back(toClone.klone(ctx));+ pNewCollection->push_back(toClone.klone()); } } @@ -65,13 +65,13 @@ } - bool Node::contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const {- bool found = false;+ bool Node::contains(const Node& potentialChild) const {+ bool found = false; for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toTest = *iter; - if (nodesEqual(toTest, potentialChild, simpleSelectorOrderDependent)) {+ if (toTest == potentialChild) { found = true; break; }@@ -82,37 +82,32 @@ bool Node::operator==(const Node& rhs) const {- return nodesEqual(*this, rhs, true /*simpleSelectorOrderDependent*/);- }--- bool nodesEqual(const Node& lhs, const Node& rhs, bool simpleSelectorOrderDependent) {- if (lhs.type() != rhs.type()) {+ if (this->type() != rhs.type()) { return false; } - if (lhs.isCombinator()) {+ if (this->isCombinator()) { - return lhs.combinator() == rhs.combinator();+ return this->combinator() == rhs.combinator(); - } else if (lhs.isNil()) {+ } else if (this->isNil()) { return true; // no state to check - } else if (lhs.isSelector()){+ } else if (this->isSelector()){ - return selectors_equal(*&lhs.selector(), *&rhs.selector(), simpleSelectorOrderDependent);+ return *this->selector() == *rhs.selector(); - } else if (lhs.isCollection()) {+ } else if (this->isCollection()) { - if (lhs.collection()->size() != rhs.collection()->size()) {+ if (this->collection()->size() != rhs.collection()->size()) { return false; } - for (NodeDeque::iterator lhsIter = lhs.collection()->begin(), lhsIterEnd = lhs.collection()->end(),+ for (NodeDeque::iterator lhsIter = this->collection()->begin(), lhsIterEnd = this->collection()->end(), rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) { - if (!nodesEqual(*lhsIter, *rhsIter, simpleSelectorOrderDependent)) {+ if (*lhsIter != *rhsIter) { return false; } @@ -128,10 +123,10 @@ void Node::plus(Node& rhs) {- if (!this->isCollection() || !rhs.isCollection()) {- throw "Both the current node and rhs must be collections.";+ if (!this->isCollection() || !rhs.isCollection()) {+ throw "Both the current node and rhs must be collections."; }- this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end());+ this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end()); } #ifdef DEBUG@@ -177,7 +172,7 @@ #endif - Node complexSelectorToNode(Complex_Selector_Ptr pToConvert, Context& ctx) {+ Node complexSelectorToNode(Complex_Selector_Ptr pToConvert) { if (pToConvert == NULL) { return Node::createNil(); }@@ -189,20 +184,22 @@ if (pToConvert->head() && pToConvert->head()->has_parent_ref()) { Complex_Selector_Obj tail = pToConvert->tail(); if (tail) tail->has_line_feed(pToConvert->has_line_feed());- pToConvert = &tail;+ pToConvert = tail; } while (pToConvert) { bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference(); - if (pToConvert->head() || empty_parent_ref) {- }- // the first Complex_Selector may contain a dummy head pointer, skip it. if (pToConvert->head() && !empty_parent_ref) {- node.collection()->push_back(Node::createSelector(pToConvert, ctx));+ node.collection()->push_back(Node::createSelector(*pToConvert)); if (has_lf) node.collection()->back().got_line_feed = has_lf;+ if (pToConvert->head() || empty_parent_ref) {+ if (pToConvert->tail()) {+ pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());+ }+ } has_lf = false; } @@ -216,14 +213,14 @@ // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed()); } - pToConvert = &pToConvert->tail();+ pToConvert = pToConvert->tail(); } return node; } - Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert, Context& ctx) {+ Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert) { if (toConvert.isNil()) { return NULL; }@@ -237,7 +234,6 @@ NodeDeque& childNodes = *toConvert.collection(); std::string noPath("");- Position noPosition(-1, -1, -1); Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL); Complex_Selector_Obj pCurrent = pFirst;@@ -254,7 +250,7 @@ // 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);@@ -265,7 +261,7 @@ if (nextNode.isCombinator()) { pCurrent->tail(SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL)); if (nextNode.got_line_feed) pCurrent->tail()->has_line_feed(nextNode.got_line_feed);- pCurrent = &pCurrent->tail();+ pCurrent = pCurrent->tail(); } } } else {@@ -285,7 +281,7 @@ // A very naive trim function, which removes duplicates in a node // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs- Node Node::naiveTrim(Node& seqses, Context& ctx) {+ Node Node::naiveTrim(Node& seqses) { std::vector<Node*> res; std::vector<Complex_Selector_Obj> known;
libsass/src/node.hpp view
@@ -61,15 +61,15 @@ static Node createCombinator(const Complex_Selector::Combinator& combinator); // This method will klone the selector, stripping off the tail and combinator- static Node createSelector(Complex_Selector_Ptr pSelector, Context& ctx);+ static Node createSelector(const Complex_Selector& pSelector); static Node createCollection(); static Node createCollection(const NodeDeque& values); static Node createNil();- static Node naiveTrim(Node& seqses, Context& ctx);+ static Node naiveTrim(Node& seqses); - Node klone(Context& ctx) const;+ Node klone() const; bool operator==(const Node& rhs) const; inline bool operator!=(const Node& rhs) const { return !(*this == rhs); }@@ -91,7 +91,7 @@ // potentialChild must be a node collection of selectors/combinators. this must be a collection // of collections of nodes/combinators. This method checks if potentialChild is a child of this // Node.- bool contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const;+ bool contains(const Node& potentialChild) const; private: // Private constructor; Use the static methods (like createCombinator and createSelector)@@ -110,10 +110,8 @@ #ifdef DEBUG std::ostream& operator<<(std::ostream& os, const Node& node); #endif- 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);+ Node complexSelectorToNode(Complex_Selector_Ptr pToConvert);+ Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert); }
libsass/src/operation.hpp view
@@ -40,6 +40,7 @@ // expressions virtual T operator()(List_Ptr x) = 0; virtual T operator()(Map_Ptr x) = 0;+ virtual T operator()(Function_Ptr x) = 0; virtual T operator()(Binary_Expression_Ptr x) = 0; virtual T operator()(Unary_Expression_Ptr x) = 0; virtual T operator()(Function_Call_Ptr x) = 0;@@ -47,7 +48,6 @@ 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;@@ -122,6 +122,7 @@ // expressions T operator()(List_Ptr x) { return static_cast<D*>(this)->fallback(x); } T operator()(Map_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Function_Ptr x) { return static_cast<D*>(this)->fallback(x); } T operator()(Binary_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); } T operator()(Unary_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); } T operator()(Function_Call_Ptr x) { return static_cast<D*>(this)->fallback(x); }@@ -129,7 +130,6 @@ 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); }
+ libsass/src/operators.cpp view
@@ -0,0 +1,240 @@+#include "sass.hpp"+#include "operators.hpp"++namespace Sass {++ namespace Operators {++ 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+ };++ /* static function, has no pstate or traces */+ bool eq(Expression_Obj lhs, Expression_Obj rhs)+ {+ // operation is undefined if one is not a number+ if (!lhs || !rhs) throw Exception::UndefinedOperation(lhs, rhs, Sass_OP::EQ);+ // use compare operator from ast node+ return *lhs == *rhs;+ }++ /* static function, throws OperationError, has no pstate or traces */+ bool cmp(Expression_Obj lhs, Expression_Obj rhs, const Sass_OP op)+ {+ // can only compare numbers!?+ Number_Obj l = Cast<Number>(lhs);+ Number_Obj r = Cast<Number>(rhs);+ // operation is undefined if one is not a number+ if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op);+ // use compare operator from ast node+ return *l < *r;+ }++ /* static functions, throws OperationError, has no pstate or traces */+ bool lt(Expression_Obj lhs, Expression_Obj rhs) { return cmp(lhs, rhs, Sass_OP::LT); }+ bool neq(Expression_Obj lhs, Expression_Obj rhs) { return eq(lhs, rhs) == false; }+ bool gt(Expression_Obj lhs, Expression_Obj rhs) { return !cmp(lhs, rhs, Sass_OP::GT) && neq(lhs, rhs); }+ bool lte(Expression_Obj lhs, Expression_Obj rhs) { return cmp(lhs, rhs, Sass_OP::LTE) || eq(lhs, rhs); }+ bool gte(Expression_Obj lhs, Expression_Obj rhs) { return !cmp(lhs, rhs, Sass_OP::GTE) || eq(lhs, rhs); }++ /* static function, throws OperationError, has no traces but optional pstate for returned value */+ Value_Ptr op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ {+ enum Sass_OP op = operand.operand;++ String_Quoted_Ptr lqstr = Cast<String_Quoted>(&lhs);+ String_Quoted_Ptr rqstr = Cast<String_Quoted>(&rhs);++ std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));+ std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));++ if (Cast<Null>(&lhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op);+ if (Cast<Null>(&rhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op);++ std::string sep;+ switch (op) {+ case Sass_OP::ADD: sep = ""; break;+ case Sass_OP::SUB: sep = "-"; break;+ case Sass_OP::DIV: 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:+ throw Exception::UndefinedOperation(&lhs, &rhs, op);+ break;+ }++ if (op == Sass_OP::ADD) {+ // create string that might be quoted on output (but do not unquote what we pass)+ return SASS_MEMORY_NEW(String_Quoted, pstate, lstr + rstr, 0, false, true);+ }++ // add whitespace around operator+ // but only if result is not delayed+ if (sep != "" && delayed == false) {+ 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, lstr + sep + rstr);+ }++ /* static function, throws OperationError, has no traces but optional pstate for returned value */+ Value_Ptr op_colors(enum Sass_OP op, const Color& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ {+ if (lhs.a() != rhs.a()) {+ throw Exception::AlphaChannelsNotEqual(&lhs, &rhs, op);+ }+ if (op == Sass_OP::DIV && (!rhs.r() || !rhs.g() || !rhs.b())) {+ throw Exception::ZeroDivisionError(lhs, rhs);+ }+ return SASS_MEMORY_NEW(Color,+ pstate,+ ops[op](lhs.r(), rhs.r()),+ ops[op](lhs.g(), rhs.g()),+ ops[op](lhs.b(), rhs.b()),+ lhs.a());+ }++ /* static function, throws OperationError, has no traces but optional pstate for returned value */+ Value_Ptr op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ {+ double lval = lhs.value();+ double rval = rhs.value();++ if (op == Sass_OP::DIV && rval == 0) {+ std::string result(lval ? "Infinity" : "NaN");+ return SASS_MEMORY_NEW(String_Quoted, pstate, result);+ }++ if (op == Sass_OP::MOD && rval == 0) {+ throw Exception::ZeroDivisionError(lhs, rhs);+ }++ size_t l_n_units = lhs.numerators.size();+ size_t l_d_units = lhs.numerators.size();+ size_t r_n_units = rhs.denominators.size();+ size_t r_d_units = rhs.denominators.size();+ // optimize out the most common and simplest case+ if (l_n_units == r_n_units && l_d_units == r_d_units) {+ if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) {+ if (lhs.numerators == rhs.numerators) {+ if (lhs.denominators == rhs.denominators) {+ Number_Ptr v = SASS_MEMORY_COPY(&lhs);+ v->value(ops[op](lval, rval));+ return v;+ }+ }+ }+ }++ Number_Obj v = SASS_MEMORY_COPY(&lhs);++ if (lhs.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {+ v->numerators = rhs.numerators;+ v->denominators = rhs.denominators;+ }++ if (op == Sass_OP::MUL) {+ v->value(ops[op](lval, rval));+ v->numerators.insert(v->numerators.end(),+ rhs.numerators.begin(), rhs.numerators.end()+ );+ v->denominators.insert(v->denominators.end(),+ rhs.denominators.begin(), rhs.denominators.end()+ );+ v->reduce();+ }+ else if (op == Sass_OP::DIV) {+ v->value(ops[op](lval, rval));+ v->numerators.insert(v->numerators.end(),+ rhs.denominators.begin(), rhs.denominators.end()+ );+ v->denominators.insert(v->denominators.end(),+ rhs.numerators.begin(), rhs.numerators.end()+ );+ v->reduce();+ }+ else {+ Number ln(lhs), rn(rhs);+ ln.reduce(); rn.reduce();+ double f(rn.convert_factor(ln));+ v->value(ops[op](lval, rn.value() * f));+ }++ v->pstate(pstate);+ return v.detach();+ }++ /* static function, throws OperationError, has no traces but optional pstate for returned value */+ Value_Ptr op_number_color(enum Sass_OP op, const Number& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ {+ double lval = lhs.value();+ switch (op) {+ case Sass_OP::ADD:+ case Sass_OP::MUL: {+ return SASS_MEMORY_NEW(Color,+ pstate,+ ops[op](lval, rhs.r()),+ ops[op](lval, rhs.g()),+ ops[op](lval, rhs.b()),+ rhs.a());+ }+ case Sass_OP::SUB:+ case Sass_OP::DIV: {+ std::string color(rhs.to_string(opt));+ return SASS_MEMORY_NEW(String_Quoted,+ pstate,+ lhs.to_string(opt)+ + sass_op_separator(op)+ + color);+ }+ default: break;+ }+ throw Exception::UndefinedOperation(&lhs, &rhs, op);+ }++ /* static function, throws OperationError, has no traces but optional pstate for returned value */+ Value_Ptr op_color_number(enum Sass_OP op, const Color& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ {+ double rval = rhs.value();+ if (op == Sass_OP::DIV && rval == 0) {+ // comparison of Fixnum with Float failed?+ throw Exception::ZeroDivisionError(lhs, rhs);+ }+ return SASS_MEMORY_NEW(Color,+ pstate,+ ops[op](lhs.r(), rval),+ ops[op](lhs.g(), rval),+ ops[op](lhs.b(), rval),+ lhs.a());+ }++ }++}
+ libsass/src/operators.hpp view
@@ -0,0 +1,30 @@+#ifndef SASS_OPERATORS_H+#define SASS_OPERATORS_H++#include "values.hpp"+#include "sass/values.h"++namespace Sass {++ namespace Operators {++ // equality operator using AST Node operator==+ bool eq(Expression_Obj, Expression_Obj);+ bool neq(Expression_Obj, Expression_Obj);+ // specific operators based on cmp and eq+ bool lt(Expression_Obj, Expression_Obj);+ bool gt(Expression_Obj, Expression_Obj);+ bool lte(Expression_Obj, Expression_Obj);+ bool gte(Expression_Obj, Expression_Obj);+ // arithmetic for all the combinations that matter+ Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);++ };++}++#endif
libsass/src/output.cpp view
@@ -19,13 +19,14 @@ void Output::operator()(Number_Ptr n) {- // use values to_string facility- std::string res = n->to_string(opt); // check for a valid unit here // includes result for reporting if (!n->is_valid_css_unit()) {- throw Exception::InvalidValue(*n);+ // should be handle in check_expression+ throw Exception::InvalidValue({}, *n); }+ // use values to_string facility+ std::string res = n->to_string(opt); // output the final token append_token(res, n); }@@ -37,8 +38,8 @@ void Output::operator()(Map_Ptr m) {- std::string dbg(m->to_string(opt));- error(dbg + " isn't a valid CSS value.", m->pstate());+ // should be handle in check_expression+ throw Exception::InvalidValue({}, *m); } OutputBuffer Output::get_buffer(void)@@ -116,8 +117,8 @@ if (!Util::isPrintable(r, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { const Statement_Obj& stm = b->at(i);- if (dynamic_cast<Has_Block_Ptr>(&stm)) {- if (!dynamic_cast<Declaration_Ptr>(&stm)) {+ if (Cast<Has_Block>(stm)) {+ if (!Cast<Declaration>(stm)) { stm->perform(this); } }@@ -134,28 +135,29 @@ append_string(ss.str()); append_optional_linefeed(); }+ scheduled_crutch = s; if (s) s->perform(this);- append_scope_opener(&b);+ 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())) {+ if (Declaration_Ptr dec = Cast<Declaration>(stm)) {+ if (String_Constant_Ptr valConst = Cast<String_Constant>(dec->value())) { std::string val(valConst->value());- if (String_Quoted_Ptr qstr = SASS_MEMORY_CAST_PTR(String_Quoted, valConst)) {+ if (String_Quoted_Ptr qstr = Cast<String_Quoted>(valConst)) { if (!qstr->quote_mark() && val.empty()) { bPrintExpression = false; } } }- else if (List_Ptr list = SASS_MEMORY_CAST(List, dec->value())) {+ else if (List_Ptr list = Cast<List>(dec->value())) { bool all_invisible = true; for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {- Expression_Ptr item = &list->at(list_i);+ Expression_Ptr item = list->at(list_i); if (!item->is_invisible()) all_invisible = false; }- if (all_invisible) bPrintExpression = false;+ if (all_invisible && !list->is_bracketed()) bPrintExpression = false; } } // Print if OK@@ -164,7 +166,7 @@ } } if (output_style() == NESTED) indentation -= r->tabs();- append_scope_closer(&b);+ append_scope_closer(b); } void Output::operator()(Keyframe_Rule_Ptr r)@@ -172,7 +174,7 @@ Block_Obj b = r->block(); Selector_Obj v = r->name(); - if (&v) {+ if (!v.isNull()) { v->perform(this); } @@ -201,7 +203,7 @@ if (!Util::isPrintable(f, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i);- if (dynamic_cast<Has_Block_Ptr>(&stm)) {+ if (Cast<Has_Block>(stm)) { stm->perform(this); } }@@ -237,7 +239,7 @@ if (!Util::isPrintable(m, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i);- if (dynamic_cast<Has_Block_Ptr>(&stm)) {+ if (Cast<Has_Block>(stm)) { stm->perform(this); } }@@ -282,7 +284,7 @@ if (v) { append_mandatory_space(); // ruby sass bug? should use options?- append_token(v->to_string(/* opt */), &v);+ append_token(v->to_string(/* opt */), v); } if (!b) { append_delimiter();@@ -324,7 +326,7 @@ if (s->can_compress_whitespace() && output_style() == COMPRESSED) { value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); }- if (!in_comment) {+ if (!in_comment && !in_custom_property) { append_token(string_to_output(value), s); } else { append_token(value, s);
libsass/src/parser.cpp view
@@ -1,2776 +1,3121 @@ #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 █- }-- // 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, ¶ms, &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 ↦- }-- // 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);+#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 <cstdlib>+#include <iostream>+#include <vector>+#include <typeinfo>++namespace Sass {+ using namespace Constants;+ using namespace Prelexer;++ Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+ {+ pstate.offset.column = 0;+ pstate.offset.line = 0;+ Parser p(ctx, pstate, traces);+ 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, Backtraces traces, ParserState pstate, const char* source)+ {+ pstate.offset.column = 0;+ pstate.offset.line = 0;+ Parser p(ctx, pstate, traces);+ 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, Backtraces traces, ParserState pstate, const char* source)+ {+ Parser p = Parser::from_c_str(beg, ctx, traces, 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, Backtraces traces, ParserState pstate, const char* source)+ {+ Parser p(ctx, pstate, traces);+ 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()+ {++ // consume unicode BOM+ read_bom();++ // scan the input to find invalid utf8 sequences+ const char* it = utf8::find_invalid(position, end);++ // report invalid utf8+ if (it != end) {+ pstate += Offset::init(position, it);+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidSass(pstate, traces, "Invalid UTF-8 sequence");+ }++ // create a block AST node to hold children+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true);++ // check seems a bit esoteric but works+ if (ctx.resources.size() == 1) {+ // apply headers only on very first include+ ctx.apply_custom_headers(root, path, pstate);+ }++ // parse children nodes+ block_stack.push_back(root);+ parse_block_nodes(true);+ block_stack.pop_back();++ // update final 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(is_root)) 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)+ {+ 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.");+ }+ }+ // this puts the parsed doc into sheets+ // import stub will fetch this in expand+ 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_List_Obj target;+ if (!lookahead.has_interpolants) {+ target = parse_selector_list(true);+ }+ else {+ target = SASS_MEMORY_NEW(Selector_List, pstate);+ target->schema(parse_selector_schema(lookahead.found, 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 &&+ !lookahead_result.is_custom_property+ )+ {+ block->append(parse_ruleset(lookahead_result));+ }++ // 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 && stack.back() != Scope::AtRoot /* && 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 quoted_url = parse_string();+ args->append(SASS_MEMORY_NEW(Argument, quoted_url->pstate(), quoted_url));+ }+ else if (String_Obj string_url = parse_url_function_argument()) {+ args->append(SASS_MEMORY_NEW(Argument, string_url->pstate(), string_url));+ }+ else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {+ Expression_Obj braced_url = parse_list(); // parse_interpolated_chunk(lexed);+ args->append(SASS_MEMORY_NEW(Argument, braced_url->pstate(), braced_url));+ }+ else {+ error("malformed URL");+ }+ if (!lex< exactly<')'> >()) error("URI is missing ')'");+ to_import.push_back(std::pair<std::string, Function_Call_Obj>("", result));+ }+ else {+ if (first) error("@import directive requires a url or quoted path");+ else error("expecting another url or quoted path in @import list");+ }+ 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);+ }+ // check if custom importers want to take over the handling+ else if (!ctx.call_importers(unquote(location.first), path, pstate, imp)) {+ // nobody wants it, so we do our import+ 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");+ std::string name(Util::normalize_underscores(lexed));+ if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))+ { error("Invalid function name \"" + name + "\"."); }+ 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()+ {+ Parameters_Obj params = SASS_MEMORY_NEW(Parameters, pstate);+ if (lex_css< exactly<'('> >()) {+ // if there's anything there at all+ if (!peek_css< exactly<')'> >()) {+ do {+ if (peek< exactly<')'> >()) break;+ params->append(parse_parameter());+ } while (lex_css< exactly<','> >());+ }+ if (!lex_css< exactly<')'> >()) {+ css_error("Invalid CSS", " after ", ": expected \")\", was ");+ }+ }+ return params;+ }++ Parameter_Obj Parser::parse_parameter()+ {+ if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) {+ css_error("Invalid CSS", " after ", ": expected variable (e.g. $foo), was ");+ }+ 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()+ {+ Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);+ if (lex_css< exactly<'('> >()) {+ // if there's anything there at all+ if (!peek_css< exactly<')'> >()) {+ do {+ if (peek< exactly<')'> >()) break;+ args->append(parse_argument());+ } while (lex_css< exactly<','> >());+ }+ if (!lex_css< exactly<')'> >()) {+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ }+ return args;+ }++ Argument_Obj Parser::parse_argument()+ {+ if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) {+ css_error("Invalid CSS", " after ", ": expected \")\", was ");+ }+ 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 = 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");+ 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)+ {+ NESTING_GUARD(nestings);+ // inherit is_root from parent block+ Block_Obj parent = block_stack.back();+ bool is_root = parent && parent->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(false));+ else {+ Selector_List_Obj list = SASS_MEMORY_NEW(Selector_List, pstate);+ list->schema(parse_selector_schema(lookahead.position, false));+ ruleset->selector(list);+ }+ // 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);+ // 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, bool chroot)+ {+ NESTING_GUARD(nestings);+ // 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_Obj selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema);+ selector_schema->connect_parent(chroot == false);+ selector_schema->media_block(last_media_block);++ // process until end+ while (i < end_of_selector) {+ // try to parse mutliple interpolants+ 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);+ }++ // 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);+ // check if the interpolation never ends of only contains white-space (error out)+ if (!j || peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) {+ position = p+2;+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ // 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, traces, 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.detach();+ }+ // 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 chroot)+ {+ bool reloop;+ bool had_linefeed = false;+ NESTING_GUARD(nestings);+ Complex_Selector_Obj sel;+ Selector_List_Obj group = SASS_MEMORY_NEW(Selector_List, pstate);+ group->media_block(last_media_block);++ if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) {+ css_error("Invalid CSS", " after ", ": expected selector, was ");+ }++ do {+ reloop = false;++ had_linefeed = had_linefeed || peek_newline();++ if (peek_css< alternatives < class_char < selector_list_delims > > >())+ break; // in case there are superfluous commas at the end++ // now parse the complex selector+ sel = parse_complex_selector(chroot);++ if (!sel) return group.detach();++ sel->has_line_feed(had_linefeed);++ had_linefeed = false;++ while (peek_css< exactly<','> >())+ {+ lex< css_comments >(false);+ // consume everything up and including the comma separator+ reloop = lex< exactly<','> >() != 0;+ // remember line break (also between some commas)+ had_linefeed = had_linefeed || peek_newline();+ // remember line break (also between some commas)+ }+ group->append(sel);+ }+ while (reloop);+ while (lex_css< kwd_optional >()) {+ group->is_optional(true);+ }+ // update for end position+ group->update_pstate(pstate);+ if (sel) sel->last()->has_line_break(false);+ return group.detach();+ }+ // EO parse_selector_list++ // a complex selector combines a compound selector with another+ // complex selector, with one of four combinator operations.+ // the compound selector (head) is optional, since the combinator+ // can come first in the whole selector sequence (like `> DIV').+ Complex_Selector_Obj Parser::parse_complex_selector(bool chroot)+ {++ NESTING_GUARD(nestings);+ String_Obj reference = 0;+ lex < block_comment >();+ advanceToNextToken();+ Complex_Selector_Obj sel = SASS_MEMORY_NEW(Complex_Selector, pstate);++ if (peek < end_of_file >()) return 0;++ // parse the left hand side+ Compound_Selector_Obj lhs;+ // special case if it starts with combinator ([+~>])+ if (!peek_css< class_char < selector_combinator_ops > >()) {+ // parse the left hand side+ lhs = parse_compound_selector();+ }+++ // parse combinator between lhs and rhs+ Complex_Selector::Combinator combinator = Complex_Selector::ANCESTOR_OF;+ if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO;+ else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES;+ else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF;+ else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) {+ // comments are allowed, but not spaces?+ combinator = Complex_Selector::REFERENCE;+ if (!lex < re_reference_combinator >()) return 0;+ reference = SASS_MEMORY_NEW(String_Constant, pstate, lexed);+ if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?+ }++ if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;++ // lex < block_comment >();+ sel->head(lhs);+ sel->combinator(combinator);+ sel->media_block(last_media_block);++ if (combinator == Complex_Selector::REFERENCE) sel->reference(reference);+ // has linfeed after combinator?+ sel->has_line_break(peek_newline());+ // sel->has_line_feed(has_line_feed);++ // check if we got the abort condition (ToDo: optimize)+ if (!peek_css< class_char < complex_selector_delims > >()) {+ // parse next selector in sequence+ sel->tail(parse_complex_selector(true));+ }++ // add a parent selector if we are not in a root+ // also skip adding parent ref if we only have refs+ if (!sel->has_parent_ref() && !chroot) {+ // create the objects to wrap parent selector reference+ Compound_Selector_Obj head = SASS_MEMORY_NEW(Compound_Selector, pstate);+ Parent_Selector_Ptr parent = SASS_MEMORY_NEW(Parent_Selector, pstate, false);+ parent->media_block(last_media_block);+ head->media_block(last_media_block);+ // add simple selector+ head->append(parent);+ // selector may not have any head yet+ if (!sel->head()) { sel->head(head); }+ // otherwise we need to create a new complex selector and set the old one as its tail+ else {+ sel = SASS_MEMORY_NEW(Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel);+ sel->media_block(last_media_block);+ }+ // peek for linefeed and remember result on head+ // if (peek_newline()) head->has_line_break(true);+ }++ sel->update_pstate(pstate);+ // complex selector+ return sel;+ }+ // EO parse_complex_selector++ // parse one compound selector, which is basically+ // a list of simple selectors (directly adjacent)+ // lex them exactly (without skipping white-space)+ Compound_Selector_Obj Parser::parse_compound_selector()+ {+ // init an empty compound selector wrapper+ Compound_Selector_Obj seq = SASS_MEMORY_NEW(Compound_Selector, pstate);+ seq->media_block(last_media_block);++ // skip initial white-space+ lex< css_whitespace >();++ // parse list+ while (true)+ {+ // remove all block comments (don't skip white-space)+ lex< delimited_by< slash_star, star_slash, false > >(false);+ // parse functional+ if (match < re_pseudo_selector >())+ {+ seq->append(parse_simple_selector());+ }+ // parse parent selector+ else if (lex< exactly<'&'> >(false))+ {+ // this produces a linefeed!?+ seq->has_parent_reference(true);+ seq->append(SASS_MEMORY_NEW(Parent_Selector, pstate));+ // parent selector only allowed at start+ // upcoming Sass may allow also trailing+ if (seq->length() > 1) {+ ParserState state(pstate);+ Simple_Selector_Obj cur = (*seq)[seq->length()-1];+ Simple_Selector_Obj prev = (*seq)[seq->length()-2];+ std::string sel(prev->to_string({ NESTED, 5 }));+ std::string found(cur->to_string({ NESTED, 5 }));+ if (lex < identifier >()) { found += std::string(lexed); }+ error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n"+ "\"" + found + "\" may only be used at the beginning of a compound selector.", state);+ }+ }+ // parse type selector+ else if (lex< re_type_selector >(false))+ {+ seq->append(SASS_MEMORY_NEW(Element_Selector, pstate, lexed));+ }+ // peek for abort conditions+ else if (peek< spaces >()) break;+ else if (peek< end_of_file >()) { break; }+ else if (peek_css < class_char < selector_combinator_ops > >()) break;+ else if (peek_css < class_char < complex_selector_delims > >()) break;+ // otherwise parse another simple selector+ else {+ Simple_Selector_Obj sel = parse_simple_selector();+ if (!sel) return 0;+ seq->append(sel);+ }+ }++ if (seq && !peek_css<alternatives<end_of_file,exactly<'{'>>>()) {+ seq->has_line_break(peek_newline());+ }++ // EO while true+ return seq;++ }+ // EO parse_compound_selector++ Simple_Selector_Obj Parser::parse_simple_selector()+ {+ 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< 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;+ }+ else {+ css_error("Invalid CSS", " after ", ": expected selector, was ");+ }+ // 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 ')'");+ }+ 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_Obj expr = SASS_MEMORY_NEW(String_Constant, pstate, lexed);+ if (lex_css< exactly<')'> >()) {+ expr->can_compress_whitespace(true);+ return SASS_MEMORY_NEW(Pseudo_Selector, p, name, expr);+ }+ }+ 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;+ }++ const char* Parser::re_attr_sensitive_close(const char* src)+ {+ return alternatives < exactly<']'>, exactly<'/'> >(src);+ }++ const char* Parser::re_attr_insensitive_close(const char* src)+ {+ return sequence < insensitive<'i'>, re_attr_sensitive_close >(src);+ }++ Attribute_Selector_Obj Parser::parse_attribute_selector()+ {+ ParserState p = pstate;+ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector");+ std::string name(lexed);+ if (lex_css< re_attr_sensitive_close >()) {+ return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, 0);+ }+ else if (lex_css< re_attr_insensitive_close >()) {+ char modifier = lexed.begin[0];+ return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, modifier);+ }+ if (!lex_css< alternatives< exact_match, class_match, dash_match,+ prefix_match, suffix_match, substring_match > >()) {+ error("invalid operator in attribute selector for " + name);+ }+ 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);+ }++ if (lex_css< re_attr_sensitive_close >()) {+ return SASS_MEMORY_NEW(Attribute_Selector, p, name, matcher, value, 0);+ }+ else if (lex_css< re_attr_insensitive_close >()) {+ char modifier = lexed.begin[0];+ return SASS_MEMORY_NEW(Attribute_Selector, p, name, matcher, value, modifier);+ }+ error("unterminated attribute selector for " + name);+ return NULL; // to satisfy compilers (error must not return)+ }++ /* 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, false);+ block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));+ }+ }++ Declaration_Obj Parser::parse_declaration() {+ String_Obj prop;+ bool is_custom_property = false;+ if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {+ const std::string property(lexed);+ is_custom_property = property.compare(0, 2, "--") == 0;+ prop = parse_identifier_schema();+ }+ else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {+ const std::string property(lexed);+ is_custom_property = property.compare(0, 2, "--") == 0;+ 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 \"" + escape_string(property) + "\" must be followed by a ':'");+ if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value");+ if (match< sequence< optional_css_comments, exactly<'{'> > >()) is_indented = false; // don't indent if value is empty+ if (is_custom_property) {+ return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_css_variable_value(), false, true);+ }+ lex < css_comments >(false);+ 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 = Cast<List>(value)) {+ if (!list->is_bracketed() && 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+ // this is never hit via spec tests+ bool Parser::parse_number_prefix()+ {+ bool positive = true;+ while(true) {+ if (lex < block_comment >()) continue;+ if (lex < number_prefix >()) continue;+ if (lex < exactly < '-' > >()) {+ positive = !positive;+ continue;+ }+ break;+ }+ return positive;+ }++ Expression_Obj Parser::parse_map()+ {+ NESTING_GUARD(nestings);+ 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; }++ List_Obj l = Cast<List>(key);+ if (l && l->separator() == SASS_COMMA) {+ css_error("Invalid CSS", " after ", ": expected \")\", was ");+ }++ 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; }++ key = parse_space_list();++ if (!(lex< exactly<':'> >()))+ { css_error("Invalid CSS", " after ", ": expected \":\", was "); }++ 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;+ }++ Expression_Obj Parser::parse_bracket_list()+ {+ NESTING_GUARD(nestings);+ // check if we have an empty list+ // return the empty list as such+ if (peek_css< list_terminator >(position))+ {+ // return an empty list (nothing to delay)+ return SASS_MEMORY_NEW(List, pstate, 0, SASS_SPACE, false, true);+ }++ bool has_paren = peek_css< exactly<'('> >() != NULL;++ // 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)) {+ List_Obj l = Cast<List>(list);+ if (!l || l->is_bracketed() || has_paren) {+ List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 1, SASS_SPACE, false, true);+ bracketed_list->append(list);+ return bracketed_list;+ }+ l->is_bracketed(true);+ return l;+ }++ // if we got so far, we actually do have a comma list+ List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA, false, true);+ // wrap the first expression+ bracketed_list->append(list);++ while (lex_css< exactly<','> >())+ {+ // check for abort condition+ if (peek_css< list_terminator >(position)+ ) { break; }+ // otherwise add another expression+ bracketed_list->append(parse_space_list());+ }+ // return the list+ return bracketed_list;+ }++ // 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)+ {+ NESTING_GUARD(nestings);+ return parse_comma_list(delayed);+ }++ // will return singletons unwrapped+ Expression_Obj Parser::parse_comma_list(bool delayed)+ {+ NESTING_GUARD(nestings);+ // check if we have an empty list+ // return the empty list as such+ if (peek_css< list_terminator >(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< list_terminator >(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()+ {+ NESTING_GUARD(nestings);+ Expression_Obj disj1 = parse_disjunction();+ // if it's a singleton, return it (don't wrap it)+ if (peek_css< space_list_terminator >(position)+ ) {+ return disj1; }++ List_Obj space_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_SPACE);+ space_list->append(disj1);++ while (+ !(peek_css< space_list_terminator >(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()+ {+ NESTING_GUARD(nestings);+ 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()+ {+ NESTING_GUARD(nestings);+ 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()+ {+ NESTING_GUARD(nestings);+ 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());+ }+ // 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()+ {+ NESTING_GUARD(nestings);+ 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()+ {+ NESTING_GUARD(nestings);+ 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");+ }+ 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()+ {+ NESTING_GUARD(nestings);+ 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");+ // expression can be evaluated+ return value;+ }+ else if (lex_css< exactly<'['> >()) {+ // explicit bracketed+ Expression_Obj value = parse_bracket_list();+ // lex the expected closing square bracket+ if (!lex_css< exactly<']'> >()) error("unclosed squared bracket");+ 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 = 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< exactly<'/'> >()) {+ Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor());+ if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());+ return ex;+ }+ else if (lex< sequence< kwd_not > >()) {+ Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());+ if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());+ return ex;+ }+ // this whole branch is never hit via spec tests+ else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {+ if (parse_number_prefix()) return parse_value(); // prefix is positive+ Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());+ if (ex->operand()) ex->is_delayed(ex->operand()->is_delayed());+ return ex;+ }+ else {+ return parse_value();+ }+ }++ bool number_has_zero(const std::string& parsed)+ {+ size_t L = parsed.length();+ return !( (L > 0 && parsed.substr(0, 1) == ".") ||+ (L > 1 && parsed.substr(0, 2) == "0.") ||+ (L > 1 && parsed.substr(0, 2) == "-.") ||+ (L > 2 && parsed.substr(0, 3) == "-0.") );+ }++ Number_Ptr Parser::lexed_number(const ParserState& pstate, const std::string& parsed)+ {+ Number_Ptr nr = SASS_MEMORY_NEW(Number,+ pstate,+ sass_strtod(parsed.c_str()),+ "",+ number_has_zero(parsed));+ nr->is_interpolant(false);+ nr->is_delayed(true);+ return nr;+ }++ Number_Ptr Parser::lexed_percentage(const ParserState& pstate, const std::string& parsed)+ {+ Number_Ptr nr = SASS_MEMORY_NEW(Number,+ pstate,+ sass_strtod(parsed.c_str()),+ "%",+ true);+ nr->is_interpolant(false);+ nr->is_delayed(true);+ return nr;+ }++ Number_Ptr Parser::lexed_dimension(const ParserState& pstate, const std::string& parsed)+ {+ size_t L = parsed.length();+ size_t num_pos = parsed.find_first_not_of(" \n\r\t");+ if (num_pos == std::string::npos) num_pos = L;+ size_t unit_pos = parsed.find_first_not_of("-+0123456789.", num_pos);+ if (parsed[unit_pos] == 'e' && is_number(parsed[unit_pos+1]) ) {+ unit_pos = parsed.find_first_not_of("-+0123456789.", ++ unit_pos);+ }+ if (unit_pos == std::string::npos) unit_pos = L;+ const std::string& num = parsed.substr(num_pos, unit_pos - num_pos);+ Number_Ptr nr = SASS_MEMORY_NEW(Number,+ pstate,+ sass_strtod(num.c_str()),+ Token(number(parsed.c_str())),+ number_has_zero(parsed));+ nr->is_interpolant(false);+ nr->is_delayed(true);+ return nr;+ }++ Expression_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed)+ {+ Color_Ptr color = NULL;+ if (parsed[0] != '#') {+ return SASS_MEMORY_NEW(String_Quoted, pstate, parsed);+ }+ // chop off the '#'+ std::string hext(parsed.substr(1));+ if (parsed.length() == 4) {+ std::string r(2, parsed[1]);+ std::string g(2, parsed[2]);+ std::string b(2, parsed[3]);+ color = SASS_MEMORY_NEW(Color,+ 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+ parsed);+ }+ else if (parsed.length() == 7) {+ std::string r(parsed.substr(1,2));+ std::string g(parsed.substr(3,2));+ std::string b(parsed.substr(5,2));+ color = SASS_MEMORY_NEW(Color,+ 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+ parsed);+ }+ else if (parsed.length() == 9) {+ std::string r(parsed.substr(1,2));+ std::string g(parsed.substr(3,2));+ std::string b(parsed.substr(5,2));+ std::string a(parsed.substr(7,2));+ color = SASS_MEMORY_NEW(Color,+ 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)),+ static_cast<double>(strtol(a.c_str(), NULL, 16)) / 255,+ parsed);+ }+ color->is_interpolant(false);+ color->is_delayed(false);+ return color;+ }++ // parse one value for a list+ Expression_Obj Parser::parse_value()+ {+ lex< css_comments >(false);+ if (lex< ampersand >())+ {+ if (match< ampersand >()) {+ warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate);+ }+ return SASS_MEMORY_NEW(Parent_Selector, pstate); }++ 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 lexed_percentage(lexed); }++ if (lex< sequence < number, lookahead< sequence < op, number > > > >())+ { return lexed_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 lexed_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 lexed_hex_color(lexed); }++ if (lex< hexa >())+ {+ std::string s = lexed.to_string();++ deprecated(+ "The value \""+s+"\" is currently parsed as a string, but it will be parsed as a color in",+ "future versions of Sass. Use \"unquote('"+s+"')\" to continue parsing it as a string.",+ true, pstate+ );++ return SASS_MEMORY_NEW(String_Quoted, pstate, 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 lexed_dimension(lexed); }++ if (lex< sequence< static_component, one_plus< strict_identifier > > >())+ { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); }++ if (lex< number >())+ { return lexed_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, bool css)+ {+ 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), 0, false, false, true, css);+ if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');+ return str_quoted;+ }++ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate, 0, css);+ 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), css));+ }+ // 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, traces, 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());+ }+ }+ 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), css));+ break;+ }+ ++ i;+ }++ return schema.detach();+ }++ String_Schema_Obj Parser::parse_css_variable_value(bool top_level)+ {+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);+ String_Schema_Obj tok;+ if (!(tok = parse_css_variable_value_token(top_level))) {+ return NULL;+ }++ schema->concat(tok);+ while ((tok = parse_css_variable_value_token(top_level))) {+ schema->concat(tok);+ }++ return schema.detach();+ }++ String_Schema_Obj Parser::parse_css_variable_value_token(bool top_level)+ {+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);+ if (+ (top_level && lex< css_variable_top_level_value >(false)) ||+ (!top_level && lex< css_variable_value >(false))+ ) {+ Token str(lexed);+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));+ }+ else if (Expression_Obj tok = lex_interpolation()) {+ if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {+ schema->concat(s);+ } else {+ schema->append(tok);+ }+ }+ else if (lex< quoted_string >()) {+ Expression_Obj tok = parse_string();+ if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {+ schema->concat(s);+ } else {+ schema->append(tok);+ }+ }+ else {+ if (peek< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {+ if (lex< exactly<'('> >()) {+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("(")));+ if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);+ if (!lex< exactly<')'> >()) css_error("Invalid CSS", " after ", ": expected \")\", was ");+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(")")));+ }+ else if (lex< exactly<'['> >()) {+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("[")));+ if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);+ if (!lex< exactly<']'> >()) css_error("Invalid CSS", " after ", ": expected \"]\", was ");+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("]")));+ }+ else if (lex< exactly<'{'> >()) {+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("{")));+ if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);+ if (!lex< exactly<'}'> >()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("}")));+ }+ }+ }++ return schema->length() > 0 ? schema.detach() : NULL;+ }++ 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;+ --after_token.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, traces, 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());+ }+ }+ 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 >()) {+ std::string parsed(lexed);+ Util::normalize_decimals(parsed);+ kwd_arg->append(lexed_number(parsed));+ }+ 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;+ 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;+ 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(lexed_percentage(lexed));+ }+ // lex dimension value+ else if (lex< dimension >()) {+ schema->append(lexed_dimension(lexed));+ }+ // lex number value+ else if (lex< number >()) {+ schema->append(lexed_number(lexed));+ }+ // lex hex color value+ else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {+ schema->append(lexed_hex_color(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, traces, 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());+ }+ }+ 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 = Cast<String_Schema>(url_string)) {+ String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate);+ res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix));+ res->append(schema);+ 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);++ if (Util::normalize_underscores(name) == "content-exists" && stack.back() != Scope::Mixin)+ { error("Cannot call content-exists() except within a mixin."); }++ 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");+ 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");+ 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");+ vars.push_back(Util::normalize_underscores(lexed));+ }+ if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive");+ 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();+ List_Obj l = Cast<List>(predicate);+ if (!predicate || (l && !l->length())) {+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ", false);+ }+ 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 '('");+ }+ Expression_Obj feature;+ if (peek_css< exactly<')'> >()) {+ error("media feature required in media query expression");+ }+ 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");+ }+ 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();+ if (!cond) {+ css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", false);+ }+ // 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;+ 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.isNull()) 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;+ // parse something declaration like+ Expression_Obj feature = parse_expression();+ Expression_Obj expression = 0;+ if (lex_css< exactly<':'> >()) {+ expression = parse_list(DELAYED);+ }+ if (!feature || !expression) error("@supports condition expected declaration");+ cond = SASS_MEMORY_NEW(Supports_Declaration,+ feature->pstate(),+ feature,+ expression);+ // 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");+ } else {+ cond = parse_supports_declaration();+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");+ }+ lex < css_whitespace >();+ return cond;+ }++ At_Root_Block_Obj Parser::parse_at_root_block()+ {+ stack.push_back(Scope::AtRoot);+ ParserState at_source_position = pstate;+ Block_Obj body = 0;+ At_Root_Query_Obj expr;+ Lookahead lookahead_result;+ 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);+ 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.isNull()) at_root->expression(expr);+ stack.pop_back();+ return at_root;+ }++ At_Root_Query_Obj Parser::parse_at_root_query()+ {+ if (peek< exactly<')'> >()) error("at-root feature required in at-root expression");++ 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");+ Expression_Obj expression = parse_list();+ List_Obj value = SASS_MEMORY_NEW(List, feature->pstate(), 1);++ if (expression->concrete_type() == Expression::LIST) {+ value = 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");+ return cond;+ }++ Directive_Obj Parser::parse_special_directive()+ {+ std::string kwd(lexed);++ if (lexed == "@else") error("Invalid CSS: @else must come after @if");++ // this whole branch is never hit via spec tests++ Directive_Ptr at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);+ Lookahead lookahead = lookahead_for_include(position);+ if (lookahead.found && !lookahead.has_interpolants) {+ at_rule->selector(parse_selector_list(false));+ }++ lex < css_comments >(false);++ if (lex < static_property >()) {+ at_rule->value(parse_interpolated_chunk(Token(lexed)));+ } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {+ at_rule->value(parse_list());+ }++ lex < css_comments >(false);++ if (peek< exactly<'{'> >()) {+ at_rule->block(parse_block());+ }++ return at_rule;+ }++ // this whole branch is never hit via spec tests+ Directive_Obj Parser::parse_prefixed_directive()+ {+ std::string kwd(lexed);++ if (lexed == "@else") error("Invalid CSS: @else must come after @if");++ Directive_Obj at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);+ Lookahead lookahead = lookahead_for_include(position);+ if (lookahead.found && !lookahead.has_interpolants) {+ at_rule->selector(parse_selector_list(false));+ }++ lex < css_comments >(false);++ if (lex < static_property >()) {+ at_rule->value(parse_interpolated_chunk(Token(lexed)));+ } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {+ at_rule->value(parse_list());+ }++ lex < css_comments >(false);++ if (peek< exactly<'{'> >()) {+ at_rule->block(parse_block());+ }++ return at_rule;+ }+++ Directive_Obj Parser::parse_directive()+ {+ Directive_Obj directive = SASS_MEMORY_NEW(Directive, pstate, lexed);+ 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;+ 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;+ 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.");+ }+ 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.");+ }+ 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.");+ }+ 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)+ ) {+ bool could_be_property = peek< sequence< exactly<'-'>, exactly<'-'> > >(p) != 0;+ while (p < q) {+ // did we have interpolations?+ if (*p == '#' && *(p+1) == '{') {+ rv.has_interpolants = true;+ p = q; break;+ }+ // A property that's ambiguous with a nested selector is interpreted as a+ // custom property.+ if (*p == ':') {+ rv.is_custom_property = could_be_property || p+1 == q || peek< space >(p+1);+ }+ ++ 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 < end_of_file >(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 <+ // end_of_file,+ 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;+ default: break;+ }+ if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding);+ 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 = Cast<String_Schema>(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 = Cast<String_Schema>(operands[i])) {+ if (schema->has_interpolants()) {+ if (i + 1 < S) {+ // this whole branch is never hit via spec tests+ 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 = Cast<Binary_Expression>(base.ptr());+ if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {+ base->is_delayed(true);+ }+ }+ // nested binary expression are never to be delayed+ if (Binary_Expression_Ptr b = Cast<Binary_Expression>(base)) {+ if (Cast<Binary_Expression>(b->left())) base->set_delayed(false);+ if (Cast<Binary_Expression>(b->right())) base->set_delayed(false);+ }+ return base;+ }++ void Parser::error(std::string msg, Position pos)+ {+ Position p(pos.line ? pos : before_token);+ ParserState pstate(path, source, p, Offset(0, 0));+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidSass(pstate, traces, msg);+ }++ void Parser::error(std::string msg)+ {+ error(msg, pstate);+ }++ // 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, const bool trim)+ {+ int max_len = 18;+ const char* end = this->end;+ while (*end != 0) ++ end;+ const char* pos = peek < optional_spaces >();+ if (!pos) pos = position;++ const char* last_pos(pos);+ if (last_pos > source) {+ utf8::prior(last_pos, source);+ }+ // backup position to last significant char+ while (trim && 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);++ if (*pos_left) utf8::next(pos_left, end);+ if (*end_left) 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;+ // Hotfix when source is null, probably due to interpolation parsing!?+ if (source == NULL || *source == 0) source = pstate.src;+ // now pass new message to the more generic error function+ error(msg + prefix + quote(left) + middle + quote(right)); } }
libsass/src/parser.hpp view
@@ -10,12 +10,22 @@ #include "position.hpp" #include "prelexer.hpp" +#ifndef MAX_NESTING+// Note that this limit is not an exact science+// it depends on various factors, which some are+// not under our control (compile time or even OS+// dependent settings on the available stack size)+// It should fix most common segfault cases though.+#define MAX_NESTING 512+#endif+ struct Lookahead { const char* found; const char* error; const char* position; bool parsable; bool has_interpolants;+ bool is_custom_property; }; namespace Sass {@@ -23,7 +33,7 @@ class Parser : public ParserState { public: - enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules };+ enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules, AtRoot }; Context& ctx; std::vector<Block_Obj> block_stack;@@ -35,23 +45,26 @@ Position before_token; Position after_token; ParserState pstate;- int indentation;-+ Backtraces traces;+ size_t indentation;+ size_t nestings; Token lexed;- bool in_at_root; - Parser(Context& ctx, const ParserState& pstate)+ Parser(Context& ctx, const ParserState& pstate, Backtraces traces) : 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(Scope::Root); }+ source(0), position(0), end(0), before_token(pstate), after_token(pstate),+ pstate(pstate), traces(traces), indentation(0), nestings(0)+ { + 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]"), 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);+ static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);+ static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);+ static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0); // special static parsers to convert strings into certain selectors- static Selector_List_Obj parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);+ static Selector_List_Obj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0); #ifdef __clang__ @@ -100,7 +113,7 @@ } - // peek will only skip over space, tabs and line comment+ // match will not 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)@@ -223,12 +236,14 @@ #endif + void error(std::string msg); void error(std::string msg, Position pos); // generate message with given and expected sample // text before and in the middle are configurable void css_error(const std::string& msg, const std::string& prefix = " after ",- const std::string& middle = ", was: ");+ const std::string& middle = ", was: ",+ const bool trim = true); void read_bom(); Block_Obj parse();@@ -240,10 +255,10 @@ 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);+ Ruleset_Obj parse_ruleset(Lookahead lookahead);+ Selector_List_Obj parse_selector_list(bool chroot);+ Complex_Selector_Obj parse_complex_selector(bool chroot);+ Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot); Compound_Selector_Obj parse_compound_selector(); Simple_Selector_Obj parse_simple_selector(); Wrapped_Selector_Obj parse_negated_selector();@@ -257,6 +272,7 @@ bool parse_number_prefix(); Declaration_Obj parse_declaration(); Expression_Obj parse_map();+ Expression_Obj parse_bracket_list(); Expression_Obj parse_list(bool delayed = false); Expression_Obj parse_comma_list(bool delayed = false); Expression_Obj parse_space_list();@@ -272,9 +288,11 @@ 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_interpolated_chunk(Token, bool constant = false, bool css = true); String_Obj parse_string(); String_Constant_Obj parse_static_value();+ String_Schema_Obj parse_css_variable_value(bool top_level = true);+ String_Schema_Obj parse_css_variable_value_token(bool top_level = true); String_Obj parse_ie_property(); String_Obj parse_ie_keyword_arg(); String_Schema_Obj parse_value_schema(const char* stop);@@ -340,15 +358,15 @@ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if (position[0] == '#' && position[1] == '{') { Expression_Obj itpl = lex_interpolation();- if (&itpl) schema->append(&itpl);+ if (!itpl.isNull()) 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);+ if (!itpl.isNull()) schema->append(itpl); } else {- return &schema;+ return schema; } } } else {@@ -357,6 +375,21 @@ } return 0; }++ public:+ static Number_Ptr lexed_number(const ParserState& pstate, const std::string& parsed);+ static Number_Ptr lexed_dimension(const ParserState& pstate, const std::string& parsed);+ static Number_Ptr lexed_percentage(const ParserState& pstate, const std::string& parsed);+ static Expression_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed);+ private:+ Number_Ptr lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); };+ Number_Ptr lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); };+ Number_Ptr lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); };+ Expression_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); };++ static const char* re_attr_sensitive_close(const char* src);+ static const char* re_attr_insensitive_close(const char* src);+ }; size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
libsass/src/plugins.cpp view
@@ -1,3 +1,8 @@+#include "sass.hpp"+#include <iostream>+#include "output.hpp"+#include "plugins.hpp"+ #ifdef _WIN32 #include <windows.h> #else@@ -7,15 +12,21 @@ #include <dlfcn.h> #endif -#include "sass.hpp"-#include <iostream>-#include "output.hpp"-#include "plugins.hpp"- namespace Sass { Plugins::Plugins(void) { }- Plugins::~Plugins(void) { }+ Plugins::~Plugins(void)+ {+ for (auto function : functions) {+ sass_delete_function(function);+ }+ for (auto importer : importers) {+ sass_delete_importer(importer);+ }+ for (auto header : headers) {+ sass_delete_importer(header);+ }+ } // check if plugin is compatible with this version // plugins may be linked static against libsass@@ -57,20 +68,23 @@ // try to get import address for "libsass_load_functions" if (LOAD_LIB_FN(__plugin_load_fns__, plugin_load_functions, "libsass_load_functions")) {- Sass_Function_List fns = plugin_load_functions();+ Sass_Function_List fns = plugin_load_functions(), _p = fns; while (fns && *fns) { functions.push_back(*fns); ++ fns; }+ sass_free_memory(_p); // only delete the container, items not yet } // try to get import address for "libsass_load_importers" if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_importers, "libsass_load_importers")) {- Sass_Importer_List imps = plugin_load_importers();+ Sass_Importer_List imps = plugin_load_importers(), _p = imps; while (imps && *imps) { importers.push_back(*imps); ++ imps; }+ sass_free_memory(_p); // only delete the container, items not yet } // try to get import address for "libsass_load_headers" if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_headers, "libsass_load_headers")) {- Sass_Importer_List imps = plugin_load_headers();+ Sass_Importer_List imps = plugin_load_headers(), _p = imps; while (imps && *imps) { headers.push_back(*imps); ++ imps; }+ sass_free_memory(_p); // only delete the container, items not yet } // success return true;@@ -153,7 +167,11 @@ struct dirent *dirp; if((dp = opendir(path.c_str())) == NULL) return -1; while ((dirp = readdir(dp)) != NULL) {- if (!ends_with(dirp->d_name, ".so")) continue;+ #if __APPLE__+ if (!ends_with(dirp->d_name, ".dylib")) continue;+ #else+ if (!ends_with(dirp->d_name, ".so")) continue;+ #endif if (load_plugin(path + dirp->d_name)) ++ loaded; } closedir(dp);
libsass/src/position.cpp view
@@ -4,6 +4,11 @@ namespace Sass { + Offset::Offset(const char chr)+ : line(chr == '\n' ? 1 : 0),+ column(chr == '\n' ? 0 : 1)+ {}+ Offset::Offset(const char* string) : line(0), column(0) {@@ -32,7 +37,6 @@ // increase offset by given string (mostly called by lexer) // increase line counter and count columns on the last line- // ToDo: make the col count utf8 aware Offset Offset::add(const char* begin, const char* end) { if (end == 0) return *this;@@ -42,9 +46,23 @@ // start new line column = 0; } else {- ++ column;+ // do not count any utf8 continuation bytes+ // https://stackoverflow.com/a/9356203/1550314+ // https://en.wikipedia.org/wiki/UTF-8#Description+ unsigned char chr = *begin;+ // skip over 10xxxxxx+ // is 1st bit not set+ if ((chr & 128) == 0) {+ // regular ascii char+ column += 1;+ }+ // is 2nd bit not set+ else if ((chr & 64) == 0) {+ // first utf8 byte+ column += 1;+ } }- ++begin;+ ++ begin; } return *this; }
libsass/src/position.hpp view
@@ -11,6 +11,7 @@ class Offset { public: // c-tor+ Offset(const char chr); Offset(const char* string); Offset(const std::string& text); Offset(const size_t line, const size_t column);@@ -85,7 +86,7 @@ size_t length() const { return end - begin; } std::string ws_before() const { return std::string(prefix, begin); }- std::string to_string() const { return std::string(begin, end); }+ const std::string to_string() const { return std::string(begin, end); } std::string time_wspace() const { std::string str(to_string()); std::string whitespaces(" \t\f\v\n\r");
libsass/src/prelexer.cpp view
@@ -1,6 +1,5 @@ #include "sass.hpp" #include <cctype>-#include <cstddef> #include <iostream> #include <iomanip> #include "util.hpp"@@ -438,6 +437,10 @@ optional < sequence < exactly <'/'>,+ negate < sequence <+ exactly < calc_fn_kwd >,+ exactly < '(' >+ > >, multiple_units > > >(src);@@ -577,7 +580,7 @@ const char* value_combinations(const char* src) { // `2px-2px` is invalid combo bool was_number = false;- const char* pos = src;+ const char* pos; while (src) { if ((pos = alternatives < quoted_string, identifier, percentage, hex >(src))) { was_number = false;@@ -643,10 +646,7 @@ >, sequence < negate <- sequence <- exactly < url_kwd >,- exactly <'('>- >+ uri_prefix >, neg_class_char < almost_any_value_class@@ -998,7 +998,17 @@ digits>(src); } const char* number(const char* src) {- return sequence< optional<sign>, unsigned_number>(src);+ return sequence<+ optional<sign>,+ unsigned_number,+ optional<+ sequence<+ exactly<'e'>,+ optional<sign>,+ unsigned_number+ >+ >+ >(src); } const char* coefficient(const char* src) { return alternatives< sequence< optional<sign>, digits >,@@ -1037,7 +1047,7 @@ const char* hexa(const char* src) { const char* p = sequence< exactly<'#'>, one_plus<xdigit> >(src); ptrdiff_t len = p - src;- return (len != 4 && len != 7 && len != 9) ? 0 : p;+ return (len != 5 && len != 9) ? 0 : p; } const char* hex0(const char* src) { const char* p = sequence< exactly<'0'>, exactly<'x'>, one_plus<xdigit> >(src);@@ -1047,7 +1057,7 @@ /* no longer used - remove? const char* rgb_prefix(const char* src) {- return word<rgb_kwd>(src);+ return word<rgb_fn_kwd>(src); }*/ // Match CSS uri specifiers. @@ -1161,7 +1171,7 @@ } // Match the CSS negation pseudo-class. const char* pseudo_not(const char* src) {- return word< pseudo_not_kwd >(src);+ return word< pseudo_not_fn_kwd >(src); } // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. const char* even(const char* src) {@@ -1269,7 +1279,7 @@ optional_css_whitespace, exactly<'='>, optional_css_whitespace,- alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >,+ alternatives< variable, identifier_schema, identifier, quoted_string, number, hex, hexa >, zero_plus< sequence< optional_css_whitespace, exactly<','>,@@ -1279,7 +1289,7 @@ optional_css_whitespace, exactly<'='>, optional_css_whitespace,- alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >+ alternatives< variable, identifier_schema, identifier, quoted_string, number, hex, hexa > > > > > >,@@ -1314,6 +1324,7 @@ identifier, quoted_string, number,+ hex, hexa, sequence < exactly < '(' >,@@ -1420,7 +1431,29 @@ >(src); } + const char* list_terminator(const char* src) {+ return alternatives <+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<']'>,+ exactly<':'>,+ end_of_file,+ exactly<ellipsis>,+ default_flag,+ global_flag+ >(src);+ }; + const char* space_list_terminator(const char* src) {+ return alternatives <+ exactly<','>,+ list_terminator+ >(src);+ };++ // const char* real_uri_prefix(const char* src) { // return alternatives< // exactly< url_kwd >,@@ -1428,6 +1461,16 @@ // >(src); // } + const char* real_uri(const char* src) {+ return sequence<+ exactly< url_kwd >,+ exactly< '(' >,+ W,+ real_uri_value,+ exactly< ')' >+ >(src);+ }+ const char* real_uri_suffix(const char* src) { return sequence< W, exactly< ')' > >(src); }@@ -1478,6 +1521,7 @@ static_string, percentage, hex,+ hexa, exactly<'|'>, // exactly<'+'>, sequence < number, unit_identifier >,@@ -1545,6 +1589,40 @@ >(src); } + extern const char css_variable_url_negates[] = "()[]{}\"'#/";+ const char* css_variable_value(const char* src) {+ return sequence<+ alternatives<+ sequence<+ negate< exactly< url_fn_kwd > >,+ one_plus< neg_class_char< css_variable_url_negates > >+ >,+ sequence< exactly<'#'>, negate< exactly<'{'> > >,+ sequence< exactly<'/'>, negate< exactly<'*'> > >,+ static_string,+ real_uri,+ block_comment+ >+ >(src);+ }++ extern const char css_variable_url_top_level_negates[] = "()[]{}\"'#/;!";+ const char* css_variable_top_level_value(const char* src) {+ return sequence<+ alternatives<+ sequence<+ negate< exactly< url_fn_kwd > >,+ one_plus< neg_class_char< css_variable_url_top_level_negates > >+ >,+ sequence< exactly<'#'>, negate< exactly<'{'> > >,+ sequence< exactly<'/'>, negate< exactly<'*'> > >,+ static_string,+ real_uri,+ block_comment+ >+ >(src);+ }+ const char* parenthese_scope(const char* src) { return sequence < exactly < '(' >,@@ -1578,7 +1656,7 @@ class_char < selector_lookahead_ops >, // match selector combinators /[>+~]/ class_char < selector_combinator_ops >,- // match attribute compare operators+ // match pseudo selectors sequence < exactly <'('>, optional_spaces,@@ -1586,6 +1664,7 @@ optional_spaces, exactly <')'> >,+ // match attribute compare operators alternatives < exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match@@ -1604,12 +1683,21 @@ // class match exactly <'.'>, // single or double colon- optional < pseudo_prefix >+ sequence <+ optional < pseudo_prefix >,+ // fix libsass issue 2376+ negate < uri_prefix >+ > >, // accept hypens in token one_plus < sequence < // can start with hyphens- zero_plus < exactly<'-'> >,+ zero_plus <+ sequence <+ exactly <'-'>,+ optional_spaces+ >+ >, // now the main token alternatives < kwd_optional,@@ -1636,10 +1724,7 @@ return sequence< optional<namespace_schema>, identifier>(src); } const char* re_type_selector(const char* src) {- return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src);- }- const char* re_type_selector2(const char* src) {- return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src);+ return alternatives< type_selector, universal, dimension, percentage, number, identifier_alnums >(src); } const char* re_static_expression(const char* src) { return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src);
libsass/src/prelexer.hpp view
@@ -46,7 +46,7 @@ src = exactly<beg>(src); if (!src) return 0; const char* stop;- while (1) {+ while (true) { if (!*src) return 0; stop = exactly<end>(src); if (stop && (!esc || *(src - 1) != '\\')) return stop;@@ -139,7 +139,7 @@ src = exactly<beg>(src); if (!src) return 0; const char* stop;- while (1) {+ while (true) { if (!*src) return 0; stop = exactly<end>(src); if (stop && (!esc || *(src - 1) != '\\')) return stop;@@ -264,7 +264,6 @@ const char* kwd_while_directive(const char* src); const char* re_nothing(const char* src);- const char* re_type_selector2(const char* src); const char* re_special_fun(const char* src); @@ -355,6 +354,10 @@ const char* ie_keyword_arg_value(const char* src); const char* ie_keyword_arg_property(const char* src); + // characters that terminate parsing of a list+ const char* list_terminator(const char* src);+ const char* space_list_terminator(const char* src);+ // match url() const char* H(const char* src); const char* W(const char* src);@@ -362,6 +365,7 @@ const char* UUNICODE(const char* src); const char* NONASCII(const char* src); const char* ESCAPE(const char* src);+ const char* real_uri(const char* src); const char* real_uri_suffix(const char* src); // const char* real_uri_prefix(const char* src); const char* real_uri_value(const char* src);@@ -375,6 +379,9 @@ const char* static_component(const char* src); const char* static_property(const char* src); const char* static_value(const char* src);++ const char* css_variable_value(const char* src);+ const char* css_variable_top_level_value(const char* src); // Utility functions for finding and counting characters in a string. template<char c>
libsass/src/remove_placeholders.cpp view
@@ -6,13 +6,12 @@ namespace Sass { - Remove_Placeholders::Remove_Placeholders(Context& ctx)- : ctx(ctx)+ Remove_Placeholders::Remove_Placeholders() { } void Remove_Placeholders::operator()(Block_Ptr b) { for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Ptr st = &b->at(i);+ Statement_Ptr st = b->at(i); st->perform(this); } }@@ -34,19 +33,19 @@ void Remove_Placeholders::operator()(Ruleset_Ptr r) { // Create a new selector group without placeholders- Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, r->selector());+ Selector_List_Obj sl = Cast<Selector_List>(r->selector()); if (sl) { // Set the new placeholder selector list- r->selector(remove_placeholders(&sl));+ 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);+ if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {+ if (Selector_List_Ptr wsl = Cast<Selector_List>(ws->selector())) {+ Selector_List_Ptr clean = remove_placeholders(wsl); // also clean superflous parent selectors // probably not really the correct place clean->remove_parent_selectors();@@ -72,10 +71,10 @@ } void Remove_Placeholders::operator()(Media_Block_Ptr m) {- operator()(&m->block());+ operator()(m->block()); } void Remove_Placeholders::operator()(Supports_Block_Ptr m) {- operator()(&m->block());+ operator()(m->block()); } void Remove_Placeholders::operator()(Directive_Ptr a) {
libsass/src/remove_placeholders.hpp view
@@ -9,19 +9,15 @@ namespace Sass { - class Context;- class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> { - Context& ctx;- void fallback_impl(AST_Node_Ptr n) {} public: Selector_List_Ptr remove_placeholders(Selector_List_Ptr); public:- Remove_Placeholders(Context&);+ Remove_Placeholders(); ~Remove_Placeholders() { } void operator()(Block_Ptr);
libsass/src/sass.cpp view
@@ -7,7 +7,24 @@ #include "sass.h" #include "file.hpp" #include "util.hpp"+#include "sass_context.hpp"+#include "sass_functions.hpp" +namespace Sass {++ // helper to convert string list to vector+ std::vector<std::string> list2vec(struct string_list* cur)+ {+ std::vector<std::string> list;+ while (cur) {+ list.push_back(cur->string);+ cur = cur->next;+ }+ return list;+ }++}+ extern "C" { using namespace Sass; @@ -49,11 +66,50 @@ return sass_copy_c_string(unquoted.c_str()); } + char* ADDCALL sass_compiler_find_include (const char* file, struct Sass_Compiler* compiler)+ {+ // get the last import entry to get current base directory+ Sass_Import_Entry import = sass_compiler_get_last_import(compiler);+ const std::vector<std::string>& incs = compiler->cpp_ctx->include_paths;+ // create the vector with paths to lookup+ std::vector<std::string> paths(1 + incs.size());+ paths.push_back(File::dir_name(import->abs_path));+ paths.insert( paths.end(), incs.begin(), incs.end() );+ // now resolve the file path relative to lookup paths+ std::string resolved(File::find_include(file, paths));+ return sass_copy_c_string(resolved.c_str());+ }++ char* ADDCALL sass_compiler_find_file (const char* file, struct Sass_Compiler* compiler)+ {+ // get the last import entry to get current base directory+ Sass_Import_Entry import = sass_compiler_get_last_import(compiler);+ const std::vector<std::string>& incs = compiler->cpp_ctx->include_paths;+ // create the vector with paths to lookup+ std::vector<std::string> paths(1 + incs.size());+ paths.push_back(File::dir_name(import->abs_path));+ paths.insert( paths.end(), incs.begin(), incs.end() );+ // now resolve the file path relative to lookup paths+ std::string resolved(File::find_file(file, paths));+ return sass_copy_c_string(resolved.c_str());+ }+ // Make sure to free the returned value! // Incs array has to be null terminated!- char* ADDCALL sass_resolve_file (const char* file, const char* paths[])+ // this has the original resolve logic for sass include+ char* ADDCALL sass_find_include (const char* file, struct Sass_Options* opt) {- std::string resolved(File::find_file(file, paths));+ std::vector<std::string> vec(list2vec(opt->include_paths));+ std::string resolved(File::find_include(file, vec));+ return sass_copy_c_string(resolved.c_str());+ }++ // Make sure to free the returned value!+ // Incs array has to be null terminated!+ char* ADDCALL sass_find_file (const char* file, struct Sass_Options* opt)+ {+ std::vector<std::string> vec(list2vec(opt->include_paths));+ std::string resolved(File::find_file(file, vec)); return sass_copy_c_string(resolved.c_str()); }
libsass/src/sass.hpp view
@@ -90,10 +90,13 @@ // Precision for fractional numbers int precision; + // Do not compress colors in selectors+ bool in_selector;+ // initialization list (constructor with defaults) Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED,- int precision = 5)- : output_style(style), precision(precision)+ int precision = 5, bool in_selector = false)+ : output_style(style), precision(precision), in_selector(in_selector) { } };
libsass/src/sass_context.cpp view
@@ -35,7 +35,6 @@ catch (Exception::Base& e) { std::stringstream msg_stream; std::string cwd(Sass::File::get_cwd());- std::string msg_prefix(e.errtype()); bool got_newline = false; msg_stream << msg_prefix << ": ";@@ -55,41 +54,49 @@ ++msg; } if (!got_newline) msg_stream << "\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 {++ if (e.traces.empty()) {+ // we normally should have some traces, still here as a fallback 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"; }+ else {+ std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));+ msg_stream << traces_to_string(e.traces, " ");+ } // 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;+ size_t lines = e.pstate.line; const char* line_beg = e.pstate.src;- while (line_beg && *line_beg && line) {- if (*line_beg == '\n') --line;- ++line_beg;+ // scan through src until target line+ // move line_beg pointer to line start+ while (line_beg && *line_beg && lines != 0) {+ if (*line_beg == '\n') --lines;+ utf8::unchecked::next(line_beg); } const char* line_end = line_beg;+ // move line_end before next newline character while (line_end && *line_end && *line_end != '\n') { if (*line_end == '\n') break; if (*line_end == '\r') break;- line_end++;+ utf8::unchecked::next(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;- msg_stream << ">> " << std::string(line_beg + move_in, line_end - shorten) << "\n";- msg_stream << " " << std::string(e.pstate.column - move_in, '-') << "^\n";+ if (line_end && *line_end != 0) ++ line_end;+ size_t line_len = line_end - line_beg;+ size_t move_in = 0; size_t shorten = 0;+ size_t left_chars = 42; size_t max_chars = 76;+ // reported excerpt should not exceed `max_chars` chars+ if (e.pstate.column > line_len) left_chars = e.pstate.column;+ if (e.pstate.column > left_chars) move_in = e.pstate.column - left_chars;+ if (line_len > max_chars + move_in) shorten = line_len - move_in - max_chars;+ utf8::advance(line_beg, move_in, line_end);+ utf8::retreat(line_end, shorten, line_beg);+ std::string sanitized; std::string marker(e.pstate.column - move_in, '-');+ utf8::replace_invalid(line_beg, line_end, std::back_inserter(sanitized));+ msg_stream << ">> " << sanitized << "\n";+ msg_stream << " " << marker << "^\n"; } JsonNode* json_err = json_mkobject();@@ -199,7 +206,6 @@ 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()@@ -272,10 +278,14 @@ #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); } \+ #define IMPLEMENT_SASS_OPTION_STRING_GETTER(type, option, def) \+ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); }+ #define IMPLEMENT_SASS_OPTION_STRING_SETTER(type, 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_OPTION_STRING_ACCESSOR(type, option, def) \+ IMPLEMENT_SASS_OPTION_STRING_GETTER(type, option, def) \+ IMPLEMENT_SASS_OPTION_STRING_SETTER(type, option, def) #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; }@@ -327,7 +337,9 @@ c_ctx->error_column = std::string::npos; // allocate a new compiler instance- Sass_Compiler* compiler = (struct Sass_Compiler*) calloc(1, sizeof(struct Sass_Compiler));+ void* ctxmem = calloc(1, sizeof(struct Sass_Compiler));+ if (ctxmem == 0) { std::cerr << "Error allocating memory for context" << std::endl; return 0; }+ Sass_Compiler* compiler = (struct Sass_Compiler*) ctxmem; compiler->state = SASS_COMPILER_CREATED; // store in sass compiler@@ -468,7 +480,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; }@@ -518,30 +530,10 @@ static void sass_clear_options (struct Sass_Options* options) { if (options == 0) return;- // Deallocate custom functions- if (options->c_functions) {- Sass_Function_List this_func_data = options->c_functions;- while (this_func_data && *this_func_data) {- free(*this_func_data);- ++this_func_data;- }- }- // Deallocate custom headers- if (options->c_headers) {- Sass_Importer_List this_head_data = options->c_headers;- while (this_head_data && *this_head_data) {- free(*this_head_data);- ++this_head_data;- }- }- // Deallocate custom importers- if (options->c_importers) {- Sass_Importer_List this_imp_data = options->c_importers;- while (this_imp_data && *this_imp_data) {- free(*this_imp_data);- ++this_imp_data;- }- }+ // Deallocate custom functions, headers and importes+ sass_delete_function_list(options->c_functions);+ sass_delete_importer_list(options->c_importers);+ sass_delete_importer_list(options->c_headers); // Deallocate inc paths if (options->plugin_paths) { struct string_list* cur;@@ -573,11 +565,6 @@ 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;@@ -674,6 +661,10 @@ size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.size(); } Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.back(); } Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx) { return compiler->cpp_ctx->import_stack[idx]; }+ // Getters for Sass_Compiler options (query function stack)+ size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->callee_stack.size(); }+ Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler) { return &compiler->cpp_ctx->callee_stack.back(); }+ Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx) { return &compiler->cpp_ctx->callee_stack[idx]; } // Calculate the size of the stored null terminated array size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx)@@ -693,10 +684,10 @@ IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed);+ IMPLEMENT_SASS_OPTION_STRING_SETTER(const char*, plugin_path, 0);+ IMPLEMENT_SASS_OPTION_STRING_SETTER(const char*, include_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0);- IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path, 0);- IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0); @@ -738,6 +729,23 @@ last->next = include_path; } + }++ // Push function for include paths (no manipulation support for now)+ size_t ADDCALL sass_option_get_include_path_size(struct Sass_Options* options)+ {+ size_t len = 0;+ struct string_list* cur = options->include_paths;+ while (cur) { len ++; cur = cur->next; }+ return len;+ }++ // Push function for include paths (no manipulation support for now)+ const char* ADDCALL sass_option_get_include_path(struct Sass_Options* options, size_t i)+ {+ struct string_list* cur = options->include_paths;+ while (i) { i--; cur = cur->next; }+ return cur->string; } // Push function for plugin paths (no manipulation support for now)
libsass/src/sass_context.hpp view
@@ -1,9 +1,8 @@ #ifndef SASS_SASS_CONTEXT_H #define SASS_SASS_CONTEXT_H -#include "sass.h"-#include "sass.hpp"-#include "context.hpp"+#include "sass/base.h"+#include "sass/context.h" #include "ast_fwd_decl.hpp" // sass config options structure@@ -33,7 +32,7 @@ char* input_path; // The output path is used for source map- // generation. Libsass will not write to+ // generation. LibSass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path;
libsass/src/sass_functions.cpp view
@@ -2,6 +2,7 @@ #include <cstring> #include "util.hpp" #include "context.hpp"+#include "values.hpp" #include "sass/functions.h" #include "sass_functions.hpp" @@ -17,12 +18,30 @@ { Sass_Function_Entry cb = (Sass_Function_Entry) calloc(1, sizeof(Sass_Function)); if (cb == 0) return 0;- cb->signature = signature;+ cb->signature = sass_copy_c_string(signature); cb->function = function; cb->cookie = cookie; return cb; } + void ADDCALL sass_delete_function(Sass_Function_Entry entry)+ {+ free(entry->signature);+ free(entry);+ }++ // Deallocator for the allocated memory+ void ADDCALL sass_delete_function_list(Sass_Function_List list)+ {+ Sass_Function_List it = list;+ if (list == 0) return;+ while(*list) {+ sass_delete_function(*list);+ ++list;+ }+ free(it);+ }+ // Setters and getters for callbacks on function lists Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos) { return list[pos]; } void sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb) { list[pos] = cb; }@@ -57,6 +76,18 @@ return (Sass_Importer_List) calloc(length + 1, sizeof(Sass_Importer_Entry)); } + // Deallocator for the allocated memory+ void ADDCALL sass_delete_importer_list(Sass_Importer_List list)+ {+ Sass_Importer_List it = list;+ if (list == 0) return;+ while(*list) {+ sass_delete_importer(*list);+ ++list;+ }+ free(it);+ }+ Sass_Importer_Entry ADDCALL sass_importer_get_list_entry(Sass_Importer_List list, size_t idx) { return list[idx]; } void ADDCALL sass_importer_set_list_entry(Sass_Importer_List list, size_t idx, Sass_Importer_Entry cb) { list[idx] = cb; } @@ -124,6 +155,37 @@ free(import->srcmap); free(import->error); free(import);+ }++ // Getter for callee entry+ const char* ADDCALL sass_callee_get_name(Sass_Callee_Entry entry) { return entry->name; }+ const char* ADDCALL sass_callee_get_path(Sass_Callee_Entry entry) { return entry->path; }+ size_t ADDCALL sass_callee_get_line(Sass_Callee_Entry entry) { return entry->line; }+ size_t ADDCALL sass_callee_get_column(Sass_Callee_Entry entry) { return entry->column; }+ enum Sass_Callee_Type ADDCALL sass_callee_get_type(Sass_Callee_Entry entry) { return entry->type; }+ Sass_Env_Frame ADDCALL sass_callee_get_env (Sass_Callee_Entry entry) { return &entry->env; }++ // Getters and Setters for environments (lexical, local and global)+ union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame env, const char* name) {+ Expression_Ptr ex = Cast<Expression>((*env->frame)[name]);+ return ex != NULL ? ast_node_to_sass_value(ex) : NULL;+ }+ void ADDCALL sass_env_set_lexical (Sass_Env_Frame env, const char* name, union Sass_Value* val) {+ (*env->frame)[name] = sass_value_to_ast_node(val);+ }+ union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame env, const char* name) {+ Expression_Ptr ex = Cast<Expression>(env->frame->get_local(name));+ return ex != NULL ? ast_node_to_sass_value(ex) : NULL;+ }+ void ADDCALL sass_env_set_local (Sass_Env_Frame env, const char* name, union Sass_Value* val) {+ env->frame->set_local(name, sass_value_to_ast_node(val));+ }+ union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame env, const char* name) {+ Expression_Ptr ex = Cast<Expression>(env->frame->get_global(name));+ return ex != NULL ? ast_node_to_sass_value(ex) : NULL;+ }+ void ADDCALL sass_env_set_global (Sass_Env_Frame env, const char* name, union Sass_Value* val) {+ env->frame->set_global(name, sass_value_to_ast_node(val)); } // Getter for import entry
libsass/src/sass_functions.hpp view
@@ -2,10 +2,12 @@ #define SASS_SASS_FUNCTIONS_H #include "sass.h"+#include "environment.hpp"+#include "functions.hpp" // Struct to hold custom function callback struct Sass_Function {- const char* signature;+ char* signature; Sass_Function_Fn function; void* cookie; };@@ -20,6 +22,22 @@ char* error; size_t line; size_t column;+};++// External environments+struct Sass_Env {+ // links to parent frames+ Sass::Env* frame;+};++// External call entry+struct Sass_Callee {+ const char* name;+ const char* path;+ size_t line;+ size_t column;+ enum Sass_Callee_Type type;+ struct Sass_Env env; }; // Struct to hold importer callback
libsass/src/sass_util.cpp view
@@ -37,7 +37,7 @@ end end */- Node paths(const Node& arrs, Context& ctx) {+ Node paths(const Node& arrs) { Node loopStart = Node::createCollection(); loopStart.collection()->push_back(Node::createCollection());@@ -108,7 +108,7 @@ return flattened end */- Node flatten(Node& arr, Context& ctx, int n) {+ Node flatten(Node& arr, int n) { if (n != -1 && n == 0) { return arr; }@@ -124,7 +124,7 @@ if (e.isCollection()) { // e.collection().got_line_feed = e.got_line_feed;- Node recurseFlattened = flatten(e, ctx, n - 1);+ Node recurseFlattened = flatten(e, n - 1); if(e.got_line_feed) { flattened.got_line_feed = e.got_line_feed;
libsass/src/sass_util.hpp view
@@ -28,7 +28,7 @@ # # [1, 4, 5], # # [2, 4, 5]] */- Node paths(const Node& arrs, Context& ctx);+ Node paths(const Node& arrs); /*@@ -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 (nodesEqual(one, two, true)) {+ if (one == two) { out = one; return true; }@@ -139,7 +139,7 @@ http://en.wikipedia.org/wiki/Longest_common_subsequence_problem */ template<typename ComparatorType>- Node lcs(Node& x, Node& y, const ComparatorType& comparator, Context& ctx) {+ Node lcs(Node& x, Node& y, const ComparatorType& comparator) { DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y) Node newX = Node::createCollection();@@ -169,7 +169,7 @@ # @param n [int] The number of levels to flatten # @return [NodeCollection] The flattened array */- Node flatten(Node& arr, Context& ctx, int n = -1);+ Node flatten(Node& arr, int n = -1); /*
libsass/src/sass_values.cpp view
@@ -4,6 +4,7 @@ #include "util.hpp" #include "eval.hpp" #include "values.hpp"+#include "operators.hpp" #include "sass/values.h" #include "sass_values.hpp" @@ -54,6 +55,8 @@ size_t ADDCALL sass_list_get_length(const union Sass_Value* v) { return v->list.length; } enum Sass_Separator ADDCALL sass_list_get_separator(const union Sass_Value* v) { return v->list.separator; } void ADDCALL sass_list_set_separator(union Sass_Value* v, enum Sass_Separator separator) { v->list.separator = separator; }+ bool ADDCALL sass_list_get_is_bracketed(const union Sass_Value* v) { return v->list.is_bracketed; }+ void ADDCALL sass_list_set_is_bracketed(union Sass_Value* v, bool is_bracketed) { v->list.is_bracketed = is_bracketed; } // Getters and setters for Sass_List values union Sass_Value* ADDCALL sass_list_get_value(const union Sass_Value* v, size_t i) { return v->list.values[i]; } void ADDCALL sass_list_set_value(union Sass_Value* v, size_t i, union Sass_Value* value) { v->list.values[i] = value; }@@ -130,13 +133,14 @@ return v; } - union Sass_Value* ADDCALL sass_make_list(size_t len, enum Sass_Separator sep)+ union Sass_Value* ADDCALL sass_make_list(size_t len, enum Sass_Separator sep, bool is_bracketed) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->list.tag = SASS_LIST; v->list.length = len; v->list.separator = sep;+ v->list.is_bracketed = is_bracketed; v->list.values = (union Sass_Value**) calloc(len, sizeof(union Sass_Value*)); if (v->list.values == 0) { free(v); return 0; } return v;@@ -218,6 +222,7 @@ case SASS_WARNING: { free(val->error.message); } break;+ default: break; } free(val);@@ -233,26 +238,26 @@ switch(val->unknown.tag) { case SASS_NULL: { return sass_make_null();- } break;+ } case SASS_BOOLEAN: { return sass_make_boolean(val->boolean.value);- } break;+ } case SASS_NUMBER: { return sass_make_number(val->number.value, val->number.unit);- } break;+ } case SASS_COLOR: { return sass_make_color(val->color.r, val->color.g, val->color.b, val->color.a);- } break;+ } case SASS_STRING: { return sass_string_is_quoted(val) ? sass_make_qstring(val->string.value) : sass_make_string(val->string.value);- } break;+ } case SASS_LIST: {- union Sass_Value* list = sass_make_list(val->list.length, val->list.separator);+ union Sass_Value* list = sass_make_list(val->list.length, val->list.separator, val->list.is_bracketed); for (i = 0; i < list->list.length; i++) { list->list.values[i] = sass_clone_value(val->list.values[i]); } return list;- } break;+ } case SASS_MAP: { union Sass_Value* map = sass_make_map(val->map.length); for (i = 0; i < val->map.length; i++) {@@ -260,13 +265,14 @@ map->map.pairs[i].value = sass_clone_value(val->map.pairs[i].value); } return map;- } break;+ } case SASS_ERROR: { return sass_make_error(val->error.message);- } break;+ } case SASS_WARNING: { return sass_make_warning(val->warning.message);- } break;+ }+ default: break; } return 0;@@ -284,7 +290,7 @@ union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b) { - Sass::Value_Ptr rv = 0;+ Sass::Value_Ptr rv; try { @@ -294,41 +300,41 @@ // 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, "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;+ case Sass_OP::EQ: return sass_make_boolean(Operators::eq(lhs, rhs));+ case Sass_OP::NEQ: return sass_make_boolean(Operators::neq(lhs, rhs));+ case Sass_OP::GT: return sass_make_boolean(Operators::gt(lhs, rhs));+ case Sass_OP::GTE: return sass_make_boolean(Operators::gte(lhs, rhs));+ case Sass_OP::LT: return sass_make_boolean(Operators::lt(lhs, rhs));+ case Sass_OP::LTE: return sass_make_boolean(Operators::lte(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)) {- 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);+ Number_Ptr_Const l_n = Cast<Number>(lhs);+ Number_Ptr_Const r_n = Cast<Number>(rhs);+ rv = Operators::op_numbers(op, *l_n, *r_n, options, l_n->pstate()); } else if (sass_value_is_number(a) && sass_value_is_color(a)) {- Number_Ptr_Const l_n = 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);+ Number_Ptr_Const l_n = Cast<Number>(lhs);+ Color_Ptr_Const r_c = Cast<Color>(rhs);+ rv = Operators::op_number_color(op, *l_n, *r_c, options, l_n->pstate()); } else if (sass_value_is_color(a) && sass_value_is_number(b)) {- Color_Ptr_Const l_c = 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);+ Color_Ptr_Const l_c = Cast<Color>(lhs);+ Number_Ptr_Const r_n = Cast<Number>(rhs);+ rv = Operators::op_color_number(op, *l_c, *r_n, options, l_c->pstate()); } else if (sass_value_is_color(a) && sass_value_is_color(b)) {- Color_Ptr_Const l_c = 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);+ Color_Ptr_Const l_c = Cast<Color>(lhs);+ Color_Ptr_Const r_c = Cast<Color>(rhs);+ rv = Operators::op_colors(op, *l_c, *r_c, options, l_c->pstate()); } else /* convert other stuff to string and apply operation */ {- Value_Ptr l_v = SASS_MEMORY_CAST(Value, lhs);- Value_Ptr r_v = SASS_MEMORY_CAST(Value, rhs);- rv = Eval::op_strings(op, *l_v, *r_v, options);+ Value_Ptr l_v = Cast<Value>(lhs);+ Value_Ptr r_v = Cast<Value>(rhs);+ rv = Operators::op_strings(op, *l_v, *r_v, options, l_v->pstate()); } // ToDo: maybe we should should return null value?@@ -346,9 +352,6 @@ catch (std::string& e) { return sass_make_error(e.c_str()); } catch (const char* e) { return sass_make_error(e); } catch (...) { return sass_make_error("unknown"); }-- return 0;- } }
libsass/src/sass_values.hpp view
@@ -35,6 +35,7 @@ struct Sass_List { enum Sass_Tag tag; enum Sass_Separator separator;+ bool is_bracketed; size_t length; // null terminated "array" union Sass_Value** values;
libsass/src/source_map.cpp view
@@ -2,7 +2,6 @@ #include <string> #include <sstream> #include <iostream>-#include <cstddef> #include <iomanip> #include "ast.hpp"@@ -25,9 +24,9 @@ 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);+ const char *file_name = file.c_str();+ JsonNode *json_file_name = json_mkstring(file_name);+ json_append_member(json_srcmap, "file", json_file_name); // pass-through sourceRoot option if (!ctx.source_map_root.empty()) {@@ -35,35 +34,34 @@ json_append_member(json_srcmap, "sourceRoot", root); } - JsonNode *json_includes = json_mkarray();+ JsonNode *json_sources = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) {- std::string include(links[source_index[i]]);+ std::string source(links[source_index[i]]); if (ctx.c_options.source_map_file_urls) {- include = File::rel2abs(include);+ source = File::rel2abs(source); // check for windows abs path- if (include[0] == '/') {+ if (source[0] == '/') { // ends up with three slashes- include = "file://" + include;+ source = "file://" + source; } else { // needs an additional slash- include = "file:///" + include;+ source = "file:///" + source; } }- const char* inc = include.c_str();- JsonNode *json_include = json_mkstring(inc);- json_append_element(json_includes, json_include);+ const char* source_name = source.c_str();+ JsonNode *json_source_name = json_mkstring(source_name);+ json_append_element(json_sources, json_source_name); }- json_append_member(json_srcmap, "sources", json_includes);+ json_append_member(json_srcmap, "sources", json_sources); - if (include_sources) {+ if (include_sources && source_index.size()) { JsonNode *json_contents = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { const Resource& resource(sources[source_index[i]]); JsonNode *json_content = json_mkstring(resource.contents); json_append_element(json_contents, json_content); }- if (json_contents->children.head)- json_append_member(json_srcmap, "sourcesContent", json_contents);+ json_append_member(json_srcmap, "sourcesContent", json_contents); } JsonNode *json_names = json_mkarray();@@ -138,7 +136,7 @@ } } }- // will adjust the offset+ // adjust the buffer offset prepend(Offset(out.buffer)); // now add the new mappings VECTOR_UNSHIFT(mappings, out.smap.mappings);
libsass/src/subset_map.cpp view
@@ -4,22 +4,20 @@ namespace Sass { - void Subset_Map::put(const Compound_Selector_Obj& sel, const Subset_Map_Val& value)+ void Subset_Map::put(const Compound_Selector_Obj& sel, const SubSetMapPair& value) { if (sel->empty()) throw std::runtime_error("internal error: subset map keys may not be empty"); size_t index = values_.size(); values_.push_back(value); for (size_t i = 0, S = sel->length(); i < S; ++i) {- hash_[(*sel)[i]].push_back(std::make_pair(&sel, index));+ 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<SubSetMapPair> 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());+ SimpleSelectorDict dict(sel->begin(), sel->end()); // XXX Set std::vector<size_t> indices; for (size_t i = 0, S = sel->length(); i < S; ++i) { if (!hash_.count((*sel)[i])) {@@ -42,14 +40,14 @@ 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;+ std::vector<SubSetMapPair> results; for (size_t i = 0, S = indices.size(); i < S; ++i) { results.push_back(values_[indices[i]]); } return results; } - std::vector<Subset_Map_Val> Subset_Map::get_v(const Compound_Selector_Obj& sel)+ std::vector<SubSetMapPair> Subset_Map::get_v(const Compound_Selector_Obj& sel) { return get_kv(sel); }
libsass/src/subset_map.hpp view
@@ -60,15 +60,15 @@ class Subset_Map { private:- std::vector<Subset_Map_Val> values_;- std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> > > hash_;+ std::vector<SubSetMapPair> values_;+ std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> >, OrderNodes > hash_; public:- void put(const Compound_Selector_Obj& sel, const 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);+ void put(const Compound_Selector_Obj& sel, const SubSetMapPair& value);+ std::vector<SubSetMapPair> get_kv(const Compound_Selector_Obj& s);+ std::vector<SubSetMapPair> get_v(const Compound_Selector_Obj& s); bool empty() { return values_.empty(); } void clear() { values_.clear(); hash_.clear(); }- const std::vector<Subset_Map_Val> values(void) { return values_; }+ const std::vector<SubSetMapPair> values(void) { return values_; } }; }
libsass/src/to_c.cpp view
@@ -36,7 +36,7 @@ union Sass_Value* To_C::operator()(List_Ptr l) {- union Sass_Value* v = sass_make_list(l->length(), l->separator());+ union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { sass_list_set_value(v, i, (*l)[i]->perform(this)); }@@ -57,7 +57,7 @@ union Sass_Value* To_C::operator()(Arguments_Ptr a) {- union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA);+ union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false); for (size_t i = 0, L = a->length(); i < L; ++i) { sass_list_set_value(v, i, (*a)[i]->perform(this)); }
libsass/src/to_value.cpp view
@@ -9,8 +9,6 @@ // throw a runtime error if this happens // we want a well defined set of possible nodes throw std::runtime_error("invalid node for to_value");- // mute warning- return 0; } // Custom_Error is a valid value@@ -62,7 +60,8 @@ l->pstate(), l->length(), l->separator(),- l->is_arglist());+ l->is_arglist(),+ l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { ll->append((*l)[i]->perform(this)); }@@ -77,6 +76,12 @@ // Null is a valid value Value_Ptr To_Value::operator()(Null_Ptr n)+ {+ return n;+ }++ // Function is a valid value+ Value_Ptr To_Value::operator()(Function_Ptr n) { return n; }
libsass/src/to_value.hpp view
@@ -34,6 +34,7 @@ Value_Ptr operator()(List_Ptr); Value_Ptr operator()(Map_Ptr); Value_Ptr operator()(Null_Ptr);+ Value_Ptr operator()(Function_Ptr); // convert to string via `To_String` Value_Ptr operator()(Selector_List_Ptr);
libsass/src/units.cpp view
@@ -1,6 +1,7 @@ #include "sass.hpp" #include <stdexcept> #include "units.hpp"+#include "error_handling.hpp" namespace Sass { @@ -53,12 +54,12 @@ { switch (unit & 0xFF00) {- 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;+ case UnitClass::LENGTH: return UnitClass::LENGTH;+ case UnitClass::ANGLE: return UnitClass::ANGLE;+ case UnitClass::TIME: return UnitClass::TIME;+ case UnitClass::FREQUENCY: return UnitClass::FREQUENCY;+ case UnitClass::RESOLUTION: return UnitClass::RESOLUTION;+ default: return UnitClass::INCOMMENSURABLE; } }; @@ -66,15 +67,28 @@ { 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;+ case UnitClass::LENGTH: return "LENGTH";+ case UnitClass::ANGLE: return "ANGLE";+ case UnitClass::TIME: return "TIME";+ case UnitClass::FREQUENCY: return "FREQUENCY";+ case UnitClass::RESOLUTION: return "RESOLUTION";+ default: return "INCOMMENSURABLE"; } }; + UnitType get_main_unit(const UnitClass unit)+ {+ switch (unit)+ {+ case UnitClass::LENGTH: return UnitType::PX;+ case UnitClass::ANGLE: return UnitType::DEG;+ case UnitClass::TIME: return UnitType::SEC;+ case UnitClass::FREQUENCY: return UnitType::HERTZ;+ case UnitClass::RESOLUTION: return UnitType::DPI;+ default: return UnitType::UNKNOWN;+ }+ };+ UnitType string_to_unit(const std::string& s) { // size units@@ -107,29 +121,29 @@ { switch (unit) { // size units- 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;+ case UnitType::PX: return "px";+ case UnitType::PT: return "pt";+ case UnitType::PC: return "pc";+ case UnitType::MM: return "mm";+ case UnitType::CM: return "cm";+ case UnitType::IN: return "in"; // angle units- case UnitType::DEG: return "deg"; break;- case UnitType::GRAD: return "grad"; break;- case UnitType::RAD: return "rad"; break;- case UnitType::TURN: return "turn"; break;+ case UnitType::DEG: return "deg";+ case UnitType::GRAD: return "grad";+ case UnitType::RAD: return "rad";+ case UnitType::TURN: return "turn"; // time units- case UnitType::SEC: return "s"; break;- case UnitType::MSEC: return "ms"; break;+ case UnitType::SEC: return "s";+ case UnitType::MSEC: return "ms"; // frequency units- case UnitType::HERTZ: return "Hz"; break;- case UnitType::KHERTZ: return "kHz"; break;+ case UnitType::HERTZ: return "Hz";+ case UnitType::KHERTZ: return "kHz"; // resolutions units- case UnitType::DPI: return "dpi"; break;- case UnitType::DPCM: return "dpcm"; break;- case UnitType::DPPX: return "dppx"; break;+ case UnitType::DPI: return "dpi";+ case UnitType::DPCM: return "dpcm";+ case UnitType::DPPX: return "dppx"; // for unknown units- default: return ""; break;+ default: return ""; } } @@ -161,7 +175,7 @@ } // throws incompatibleUnits exceptions- double conversion_factor(const std::string& s1, const std::string& s2, bool strict)+ double conversion_factor(const std::string& s1, const std::string& s2) { // assert for same units if (s1 == s2) return 1;@@ -171,27 +185,317 @@ // query unit group types UnitClass t1 = get_unit_type(u1); UnitClass t2 = get_unit_type(u2);+ // return the conversion factor+ return conversion_factor(u1, u2, t1, t2);+ }++ // throws incompatibleUnits exceptions+ double conversion_factor(UnitType u1, UnitType u2, UnitClass t1, UnitClass t2)+ {+ // can't convert between groups+ if (t1 != t2) return 0; // get absolute offset // used for array acces size_t i1 = u1 - t1; size_t i2 = u2 - t2;- // error if units are not of the same group- // don't error for multiplication and division- if (strict && t1 != t2) throw incompatibleUnits(u1, u2);- // only process known units- if (u1 != UNKNOWN && u2 != UNKNOWN) {- switch (t1) {- 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 UnitClass::INCOMMENSURABLE: return 0; break;- }+ // process known units+ switch (t1) {+ case LENGTH:+ return size_conversion_factors[i1][i2];+ case ANGLE:+ return angle_conversion_factors[i1][i2];+ case TIME:+ return time_conversion_factors[i1][i2];+ case FREQUENCY:+ return frequency_conversion_factors[i1][i2];+ case RESOLUTION:+ return resolution_conversion_factors[i1][i2];+ case INCOMMENSURABLE:+ return 0; } // fallback return 0;+ }++ double convert_units(const std::string& lhs, const std::string& rhs, int& lhsexp, int& rhsexp)+ {+ double f = 0;+ // do not convert same ones+ if (lhs == rhs) return 0;+ // skip already canceled out unit+ if (lhsexp == 0) return 0;+ if (rhsexp == 0) return 0;+ // check if it can be converted+ UnitType ulhs = string_to_unit(lhs);+ UnitType urhs = string_to_unit(rhs);+ // skip units we cannot convert+ if (ulhs == UNKNOWN) return 0;+ if (urhs == UNKNOWN) return 0;+ // query unit group types+ UnitClass clhs = get_unit_type(ulhs);+ UnitClass crhs = get_unit_type(urhs);+ // skip units we cannot convert+ if (clhs != crhs) return 0;+ // if right denominator is bigger than lhs, we want to keep it in rhs unit+ if (rhsexp < 0 && lhsexp > 0 && - rhsexp > lhsexp) {+ // get the conversion factor for units+ f = conversion_factor(urhs, ulhs, clhs, crhs);+ // left hand side has been consumned+ f = std::pow(f, lhsexp);+ rhsexp += lhsexp;+ lhsexp = 0;+ }+ else {+ // get the conversion factor for units+ f = conversion_factor(ulhs, urhs, clhs, crhs);+ // right hand side has been consumned+ f = std::pow(f, rhsexp);+ lhsexp += rhsexp;+ rhsexp = 0;+ }+ return f;+ }++ bool Units::operator< (const Units& rhs) const+ {+ return (numerators < rhs.numerators) &&+ (denominators < rhs.denominators);+ }+ bool Units::operator== (const Units& rhs) const+ {+ return (numerators == rhs.numerators) &&+ (denominators == rhs.denominators);+ }++ double Units::normalize()+ {++ size_t iL = numerators.size();+ size_t nL = denominators.size();++ // the final conversion factor+ double factor = 1;++ for (size_t i = 0; i < iL; i++) {+ std::string &lhs = numerators[i];+ UnitType ulhs = string_to_unit(lhs);+ if (ulhs == UNKNOWN) continue;+ UnitClass clhs = get_unit_type(ulhs);+ UnitType umain = get_main_unit(clhs);+ if (ulhs == umain) continue;+ double f(conversion_factor(umain, ulhs, clhs, clhs));+ if (f == 0) throw std::runtime_error("INVALID");+ numerators[i] = unit_to_string(umain);+ factor /= f;+ }++ for (size_t n = 0; n < nL; n++) {+ std::string &rhs = denominators[n];+ UnitType urhs = string_to_unit(rhs);+ if (urhs == UNKNOWN) continue;+ UnitClass crhs = get_unit_type(urhs);+ UnitType umain = get_main_unit(crhs);+ if (urhs == umain) continue;+ double f(conversion_factor(umain, urhs, crhs, crhs));+ if (f == 0) throw std::runtime_error("INVALID");+ denominators[n] = unit_to_string(umain);+ factor /= f;+ }++ std::sort (numerators.begin(), numerators.end());+ std::sort (denominators.begin(), denominators.end());++ // return for conversion+ return factor;+ }++ double Units::reduce()+ {++ size_t iL = numerators.size();+ size_t nL = denominators.size();++ // have less than two units?+ if (iL + nL < 2) return 1;++ // 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+ // this will already cancel out equivalent units (e.q. px/px)+ for (size_t i = 0; i < iL; i ++) exponents[numerators[i]] += 1;+ for (size_t n = 0; n < nL; n ++) exponents[denominators[n]] -= 1;++ // the final conversion factor+ double factor = 1;++ // convert between compatible units+ for (size_t i = 0; i < iL; i++) {+ for (size_t n = 0; n < nL; n++) {+ std::string &lhs = numerators[i], &rhs = denominators[n];+ int &lhsexp = exponents[lhs], &rhsexp = exponents[rhs];+ double f(convert_units(lhs, rhs, lhsexp, rhsexp));+ if (f == 0) continue;+ factor /= f;+ }+ }++ // now we can build up the new unit arrays+ numerators.clear();+ denominators.clear();++ // recreate sorted units vectors+ for (auto exp : exponents) {+ int &exponent = exp.second;+ while (exponent > 0 && exponent --)+ numerators.push_back(exp.first);+ while (exponent < 0 && exponent ++)+ denominators.push_back(exp.first);+ }++ // return for conversion+ return factor;++ }++ std::string Units::unit() const+ {+ std::string u;+ size_t iL = numerators.size();+ size_t nL = denominators.size();+ for (size_t i = 0; i < iL; i += 1) {+ if (i) u += '*';+ u += numerators[i];+ }+ if (nL != 0) u += '/';+ for (size_t n = 0; n < nL; n += 1) {+ if (n) u += '*';+ u += denominators[n];+ }+ return u;+ }++ bool Units::is_unitless() const+ {+ return numerators.empty() &&+ denominators.empty();+ }++ bool Units::is_valid_css_unit() const+ {+ return numerators.size() <= 1 &&+ denominators.size() == 0;+ }++ // this does not cover all cases (multiple prefered units)+ double Units::convert_factor(const Units& r) const+ {++ std::vector<std::string> miss_nums(0);+ std::vector<std::string> miss_dens(0);+ // create copy since we need these for state keeping+ std::vector<std::string> r_nums(r.numerators);+ std::vector<std::string> r_dens(r.denominators);++ auto l_num_it = numerators.begin();+ auto l_num_end = numerators.end();++ bool l_unitless = is_unitless();+ auto r_unitless = r.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 ++);++ auto r_num_it = r_nums.begin(), 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 conversion factor for units+ double conversion = conversion_factor(l_num, r_num);+ // 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) miss_nums.push_back(l_num);+ }++ auto l_den_it = denominators.begin();+ auto l_den_end = denominators.end();++ // process all left denominators+ while (l_den_it != l_den_end)+ {+ // get and increment afterwards+ const std::string l_den = *(l_den_it ++);++ auto r_den_it = r_dens.begin();+ auto 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);+ // 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) miss_dens.push_back(l_den);+ }++ // check left-overs (ToDo: might cancel out?)+ if (miss_nums.size() > 0 && !r_unitless) {+ throw Exception::IncompatibleUnits(r, *this);+ }+ else if (miss_dens.size() > 0 && !r_unitless) {+ throw Exception::IncompatibleUnits(r, *this);+ }+ else if (r_nums.size() > 0 && !l_unitless) {+ throw Exception::IncompatibleUnits(r, *this);+ }+ else if (r_dens.size() > 0 && !l_unitless) {+ throw Exception::IncompatibleUnits(r, *this);+ }++ return factor; } }
libsass/src/units.hpp view
@@ -4,6 +4,7 @@ #include <cmath> #include <string> #include <sstream>+#include <vector> namespace Sass { @@ -52,40 +53,56 @@ }; + class Units {+ public:+ std::vector<std::string> numerators;+ std::vector<std::string> denominators;+ public:+ // default constructor+ Units() :+ numerators(),+ denominators()+ { }+ // copy constructor+ Units(const Units* ptr) :+ numerators(ptr->numerators),+ denominators(ptr->denominators)+ { }+ // convert to string+ std::string unit() const;+ // get if units are empty+ bool is_unitless() const;+ // return if valid for css+ bool is_valid_css_unit() const;+ // reduce units for output+ // returns conversion factor+ double reduce();+ // normalize units for compare+ // returns conversion factor+ double normalize();+ // compare operations+ bool operator< (const Units& rhs) const;+ bool operator== (const Units& rhs) const;+ // factor to convert into given units+ double convert_factor(const Units&) const;+ };+ extern const double size_conversion_factors[6][6]; extern const double angle_conversion_factors[4][4]; extern const double time_conversion_factors[2][2]; extern const double frequency_conversion_factors[2][2]; extern const double resolution_conversion_factors[3][3]; + UnitType get_main_unit(const UnitClass 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);-- class incompatibleUnits: public std::exception- {- public:- const char* msg;- 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) << "'";- // hold on to string on stack!- std::string str(ss.str());- msg = str.c_str();- }- virtual const char* what() const throw()- {- return msg;- }- };+ double conversion_factor(const std::string&, const std::string&);+ double conversion_factor(UnitType, UnitType, UnitClass, UnitClass);+ double convert_units(const std::string&, const std::string&, int&, int&); }
libsass/src/utf8/checked.h view
@@ -193,6 +193,13 @@ utf8::next(it, end); } + template <typename octet_iterator, typename distance_type>+ void retreat (octet_iterator& it, distance_type n, octet_iterator start)+ {+ for (distance_type i = 0; i < n; ++i)+ utf8::prior(it, start);+ }+ template <typename octet_iterator> typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last)
libsass/src/utf8/unchecked.h view
@@ -116,6 +116,13 @@ utf8::unchecked::next(it); } + template <typename octet_iterator, typename distance_type>+ void retreat (octet_iterator& it, distance_type n)+ {+ for (distance_type i = 0; i < n; ++i)+ utf8::unchecked::prior(it);+ }+ template <typename octet_iterator> typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last)
libsass/src/utf8_string.cpp view
@@ -28,7 +28,7 @@ size_t offset_at_position(const string& str, size_t position) { string::const_iterator it = str.begin(); utf8::advance(it, position, str.end());- return distance(str.begin(), it);+ return std::distance(str.begin(), it); } // function that returns number of bytes in a character at offset
libsass/src/util.cpp view
@@ -9,11 +9,22 @@ #include <cmath> #include <stdint.h>+#if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64)+#include <mutex>+#endif namespace Sass { double round(double val, size_t precision) {+ // Disable FMA3-optimized implementation when compiling with VS2013 for x64 targets+ // See https://github.com/sass/node-sass/issues/1854 for details+ // FIXME: Remove this workaround when we switch to VS2015++ #if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64)+ static std::once_flag flag;+ std::call_once(flag, []() { _set_FMA3_enable(0); });+ #endif+ // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93 if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val); else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val);@@ -24,7 +35,7 @@ } /* Locale unspecific atof function. */- double sass_atof(const char *str)+ double sass_strtod(const char *str) { char separator = *(localeconv()->decimal_point); if(separator != '.'){@@ -37,13 +48,13 @@ // of the string. This is slower but it is thread safe. char *copy = sass_copy_c_string(str); *(copy + (found - str)) = separator;- double res = atof(copy);+ double res = strtod(copy, NULL); free(copy); return res; } } - return atof(str);+ return strtod(str, NULL); } // helper for safe access to c_ctx@@ -85,8 +96,9 @@ } // read css string (handle multiline DELIM)- std::string read_css_string(const std::string& str)+ std::string read_css_string(const std::string& str, bool css) {+ if (!css) return str; std::string out(""); bool esc = false; for (auto i : str) {@@ -169,6 +181,23 @@ return out; } + std::string escape_string(const std::string& str)+ {+ std::string out("");+ for (auto i : str) {+ if (i == '\n') {+ out += "\\n";+ } else if (i == '\r') {+ out += "\\r";+ } else if (i == '\t') {+ out += "\\t";+ } else {+ out += i;+ }+ }+ return out;+ }+ std::string comment_to_string(const std::string& text) { std::string str = "";@@ -221,6 +250,75 @@ return quote_mark; } + std::string read_hex_escapes(const std::string& s)+ {++ std::string result;+ bool skipped = false;++ for (size_t i = 0, L = s.length(); i < L; ++i) {++ // implement the same strange ruby sass behavior+ // an escape sequence can also mean a unicode char+ if (s[i] == '\\' && !skipped) {++ // remember+ skipped = 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;++ 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 (s[i + len] == ' ') ++ len;++ // assert invalid code points+ if (cp == 0) cp = 0xFFFD;+ // replace bell character+ // 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+ // 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; m < 5 && u[m]; m++) result.push_back(u[m]);++ // skip some more chars?+ i += len - 1; skipped = false;++ }++ else {++ skipped = false;++ result.push_back(s[i]);++ }++ }++ else {++ result.push_back(s[i]);++ }++ }++ return result;++ }+ std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences, bool strict) { @@ -281,7 +379,7 @@ // 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++) unq.push_back(u[m]);+ for(size_t m = 0; m < 5 && u[m]; m++) unq.push_back(u[m]); // skip some more chars? i += len - 1; skipped = false;@@ -445,7 +543,7 @@ Block_Obj b = r->block(); - Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, r->selector());+ Selector_List_Ptr sl = Cast<Selector_List>(r->selector()); bool hasSelectors = sl ? sl->length() > 0 : false; if (!hasSelectors) {@@ -456,16 +554,16 @@ bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i);- if (dynamic_cast<Directive_Ptr>(&stm)) {+ if (Cast<Directive>(stm)) { return true;- } else if (Declaration_Ptr d = dynamic_cast<Declaration_Ptr>(&stm)) {+ } else if (Declaration_Ptr d = Cast<Declaration>(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)) {+ } else if (Has_Block_Ptr p = Cast<Has_Block>(stm)) {+ Block_Obj pChildBlock = p->block();+ if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; }- } else if (Comment_Ptr c = dynamic_cast<Comment_Ptr>(&stm)) {+ } else if (Comment_Ptr c = Cast<Comment>(stm)) { // keep for uncompressed if (style != COMPRESSED) { hasDeclarations = true;@@ -499,8 +597,8 @@ bool isPrintable(Declaration_Ptr d, Sass_Output_Style 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);+ if (String_Quoted_Obj sq = Cast<String_Quoted>(val)) return isPrintable(sq.ptr(), style);+ if (String_Constant_Obj sc = Cast<String_Constant>(val)) return isPrintable(sc.ptr(), style); return true; } @@ -511,19 +609,19 @@ Block_Obj b = f->block(); -// 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_Obj stm = b->at(i);- if (dynamic_cast<Declaration_Ptr>(&stm) || dynamic_cast<Directive_Ptr>(&stm)) {+ if (Cast<Declaration>(stm) || Cast<Directive>(stm)) { hasDeclarations = true; }- else if (dynamic_cast<Has_Block_Ptr>(&stm)) {- Block_Obj pChildBlock = ((Has_Block_Ptr)&stm)->block();- if (isPrintable(&pChildBlock, style)) {- hasPrintableChildBlocks = true;+ else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+ Block_Obj pChildBlock = b->block();+ if (!b->is_invisible()) {+ if (isPrintable(pChildBlock, style)) {+ hasPrintableChildBlocks = true;+ } } } @@ -542,34 +640,32 @@ if (b == 0) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { 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 (Cast<Directive>(stm)) return true;+ else if (Cast<Declaration>(stm)) return true;+ else if (Comment_Ptr c = Cast<Comment>(stm)) { if (isPrintable(c, style)) { return true; } }- else if (dynamic_cast<Ruleset_Ptr>(&stm)) {- Ruleset_Ptr r = (Ruleset_Ptr) &stm;+ else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) { if (isPrintable(r, style)) { return true; } }- else if (dynamic_cast<Supports_Block_Ptr>(&stm)) {- Supports_Block_Ptr f = (Supports_Block_Ptr) &stm;+ else if (Supports_Block_Ptr f = Cast<Supports_Block>(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)) {+ else if (Media_Block_Ptr mb = Cast<Media_Block>(stm)) {+ if (isPrintable(mb, style)) { return true; } }- else if (dynamic_cast<Has_Block_Ptr>(&stm) && isPrintable(((Has_Block_Ptr)&stm)->block(), style)) {- return true;+ else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+ if (isPrintable(b->block(), style)) {+ return true;+ } } } return false;@@ -596,35 +692,33 @@ for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i);- if (dynamic_cast<Declaration_Ptr>(&stm) || dynamic_cast<Directive_Ptr>(&stm)) {+ if (Cast<Declaration>(stm) || Cast<Directive>(stm)) { return true; }- else if (dynamic_cast<Comment_Ptr>(&stm)) {- Comment_Ptr c = (Comment_Ptr) &stm;+ else if (Comment_Ptr c = Cast<Comment>(stm)) { if (isPrintable(c, style)) { return true; } }- else if (dynamic_cast<Ruleset_Ptr>(&stm)) {- Ruleset_Ptr r = (Ruleset_Ptr) &stm;+ else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) { if (isPrintable(r, style)) { return true; } }- else if (dynamic_cast<Supports_Block_Ptr>(&stm)) {- Supports_Block_Ptr f = (Supports_Block_Ptr) &stm;+ else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) { if (isPrintable(f, style)) { return true; } }- else if (dynamic_cast<Media_Block_Ptr>(&stm)) {- Media_Block_Ptr m = (Media_Block_Ptr) &stm;+ else if (Media_Block_Ptr m = Cast<Media_Block>(stm)) { if (isPrintable(m, style)) { return true; } }- else if (dynamic_cast<Has_Block_Ptr>(&stm) && isPrintable(((Has_Block_Ptr)&stm)->block(), style)) {- return true;+ else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+ if (isPrintable(b->block(), style)) {+ return true;+ } } }
libsass/src/util.hpp view
@@ -18,14 +18,16 @@ } while (0) double round(double val, size_t precision = 0);- double sass_atof(const char* str);+ double sass_strtod(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 read_css_string(const std::string& str);+ std::string read_css_string(const std::string& str, bool css = true); std::string evacuate_escapes(const std::string& str); std::string string_to_output(const std::string& str); std::string comment_to_string(const std::string& text);+ std::string read_hex_escapes(const std::string& str);+ std::string escape_string(const std::string& str); void newline_to_space(std::string& str); std::string quote(const std::string&, char q = 0);
libsass/src/values.cpp view
@@ -11,32 +11,32 @@ { if (val->concrete_type() == Expression::NUMBER) {- Number_Ptr_Const res = dynamic_cast<Number_Ptr_Const>(val);+ Number_Ptr_Const res = Cast<Number>(val); return sass_make_number(res->value(), res->unit().c_str()); } else if (val->concrete_type() == Expression::COLOR) {- Color_Ptr_Const col = dynamic_cast<Color_Ptr_Const>(val);+ Color_Ptr_Const col = Cast<Color>(val); return sass_make_color(col->r(), col->g(), col->b(), col->a()); } else if (val->concrete_type() == Expression::LIST) {- List_Ptr_Const l = dynamic_cast<List_Ptr_Const>(val);- union Sass_Value* list = sass_make_list(l->size(), l->separator());+ List_Ptr_Const l = Cast<List>(val);+ union Sass_Value* list = sass_make_list(l->size(), l->separator(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { Expression_Obj obj = l->at(i);- auto val = ast_node_to_sass_value(&obj);+ auto val = ast_node_to_sass_value(obj); sass_list_set_value(list, i, val); } return list; } else if (val->concrete_type() == Expression::MAP) {- Map_Ptr_Const m = dynamic_cast<Map_Ptr_Const>(val);+ Map_Ptr_Const m = Cast<Map>(val); union Sass_Value* map = sass_make_map(m->length()); size_t i = 0; for (Expression_Obj key : m->keys()) {- sass_map_set_key(map, i, ast_node_to_sass_value(&key));- sass_map_set_value(map, i, ast_node_to_sass_value(&m->at(key)));+ 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;@@ -47,16 +47,16 @@ } else if (val->concrete_type() == Expression::BOOLEAN) {- Boolean_Ptr_Const res = dynamic_cast<Boolean_Ptr_Const>(val);+ Boolean_Ptr_Const res = Cast<Boolean>(val); return sass_make_boolean(res->value()); } else if (val->concrete_type() == Expression::STRING) {- if (String_Quoted_Ptr_Const qstr = dynamic_cast<String_Quoted_Ptr_Const>(val))+ if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(val)) { return sass_make_qstring(qstr->value().c_str()); }- else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(val))+ else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(val)) { return sass_make_string(cstr->value().c_str()); }@@ -73,12 +73,10 @@ ParserState("[C-VALUE]"), sass_number_get_value(val), sass_number_get_unit(val));- break; case SASS_BOOLEAN: return SASS_MEMORY_NEW(Boolean, ParserState("[C-VALUE]"), sass_boolean_get_value(val));- break; case SASS_COLOR: return SASS_MEMORY_NEW(Color, ParserState("[C-VALUE]"),@@ -86,18 +84,15 @@ sass_color_get_g(val), sass_color_get_b(val), sass_color_get_a(val));- break; case SASS_STRING: if (sass_string_is_quoted(val)) { return SASS_MEMORY_NEW(String_Quoted, ParserState("[C-VALUE]"), sass_string_get_value(val));- } else {- return SASS_MEMORY_NEW(String_Constant,+ }+ return SASS_MEMORY_NEW(String_Constant, ParserState("[C-VALUE]"), sass_string_get_value(val));- }- break; case SASS_LIST: { List_Ptr l = SASS_MEMORY_NEW(List, ParserState("[C-VALUE]"),@@ -106,9 +101,9 @@ for (size_t i = 0, L = sass_list_get_length(val); i < L; ++i) { l->append(sass_value_to_ast_node(sass_list_get_value(val, i))); }+ l->is_bracketed(sass_list_get_is_bracketed(val)); return l; }- break; case SASS_MAP: { Map_Ptr m = SASS_MEMORY_NEW(Map, ParserState("[C-VALUE]")); for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) {@@ -118,20 +113,17 @@ } return m; }- break; case SASS_NULL: return SASS_MEMORY_NEW(Null, ParserState("[C-VALUE]"));- break; case SASS_ERROR: return SASS_MEMORY_NEW(Custom_Error, ParserState("[C-VALUE]"), sass_error_get_message(val));- break; case SASS_WARNING: return SASS_MEMORY_NEW(Custom_Warning, ParserState("[C-VALUE]"), sass_warning_get_message(val));- break;+ default: break; } return 0; }
tests/Main.hs view
@@ -35,7 +35,7 @@ #ifndef EXTERNAL_LIBSASS it "should report correct version" $ do str <- peekCString libsass_version- str `shouldBe` "3.4.3"+ str `shouldBe` "3.5.2" #endif it "should support quoted strings" $ withCString "sample" $ \cstr -> do