hlibsass 0.1.5.2 → 0.1.6.0
raw patch · 108 files changed
+16609/−12791 lines, 108 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Bindings.Libsass: libsass_version :: CString
+ Bindings.Libsass.Base: libsass_language_version :: CString
+ Bindings.Libsass.Base: libsass_version :: CString
+ Bindings.Libsass.Base: sass_alloc_memory :: CSize -> IO (Ptr ())
+ Bindings.Libsass.Base: sass_copy_c_string :: CString -> IO CString
+ Bindings.Libsass.Base: sass_free_memory :: Ptr () -> IO ()
+ Bindings.Libsass.Base: sass_resolve_file :: CString -> Ptr CString -> IO CString
+ Bindings.Libsass.Base: sass_string_quote :: CString -> CChar -> IO CString
+ Bindings.Libsass.Base: sass_string_unquote :: CString -> IO CString
+ Bindings.Libsass.Context: sass_compiler_get_options :: Ptr SassCompiler -> IO (Ptr SassOptions)
+ Bindings.Libsass.Context: sass_delete_options :: Ptr SassOptions -> IO ()
+ Bindings.Libsass.Context: sass_option_get_source_map_file_urls :: Ptr SassOptions -> IO Bool
+ Bindings.Libsass.Context: sass_option_set_source_map_file_urls :: Ptr SassOptions -> Bool -> IO ()
+ Bindings.Libsass.Types: SassSeparatorHash :: SassSeparator
+ Bindings.Libsass.Types: SassStyleInspect :: SassOutputStyle
+ Bindings.Libsass.Types: SassStyleToSass :: SassOutputStyle
Files
- Bindings/Libsass.hs +4/−7
- Bindings/Libsass/Base.hs +37/−0
- Bindings/Libsass/Context.hs +18/−2
- Bindings/Libsass/Types.hs +17/−8
- Bindings/Libsass/Values.hs +64/−64
- CHANGELOG.md +8/−0
- LICENSE +1/−1
- hlibsass.cabal +10/−5
- libsass/LICENSE +1/−1
- libsass/Makefile +35/−21
- libsass/Makefile.conf +29/−24
- libsass/VERSION +1/−1
- libsass/include/sass/base.h +18/−2
- libsass/include/sass/context.h +3/−0
- libsass/include/sass/interface.h +0/−105
- libsass/include/sass/values.h +30/−28
- libsass/include/sass/version.h +4/−0
- libsass/include/sass/version.h.in +4/−0
- libsass/include/sass2scss.h +1/−1
- libsass/src/ast.cpp +2451/−2134
- libsass/src/ast.hpp +3070/−2326
- libsass/src/ast_def_macros.hpp +5/−5
- libsass/src/ast_factory.hpp +0/−93
- libsass/src/ast_fwd_decl.hpp +315/−16
- libsass/src/base64vlq.cpp +1/−0
- libsass/src/bind.cpp +77/−89
- libsass/src/bind.hpp +3/−7
- libsass/src/check_nesting.cpp +376/−0
- libsass/src/check_nesting.hpp +64/−0
- libsass/src/color_maps.cpp +4/−3
- libsass/src/color_maps.hpp +3/−3
- libsass/src/constants.cpp +13/−11
- libsass/src/constants.hpp +7/−6
- libsass/src/context.cpp +96/−81
- libsass/src/context.hpp +24/−26
- libsass/src/cssize.cpp +255/−225
- libsass/src/cssize.hpp +49/−49
- libsass/src/debugger.hpp +349/−237
- libsass/src/emitter.cpp +40/−28
- libsass/src/emitter.hpp +12/−11
- libsass/src/environment.cpp +19/−8
- libsass/src/environment.hpp +5/−5
- libsass/src/error_handling.cpp +106/−16
- libsass/src/error_handling.hpp +129/−9
- libsass/src/eval.cpp +1709/−1527
- libsass/src/eval.hpp +66/−64
- libsass/src/expand.cpp +348/−248
- libsass/src/expand.hpp +44/−40
- libsass/src/extend.cpp +234/−181
- libsass/src/extend.hpp +21/−14
- libsass/src/file.cpp +38/−27
- libsass/src/file.hpp +4/−5
- libsass/src/functions.cpp +520/−458
- libsass/src/functions.hpp +8/−10
- libsass/src/inspect.cpp +401/−169
- libsass/src/inspect.hpp +69/−67
- libsass/src/json.cpp +85/−47
- libsass/src/json.hpp +5/−5
- libsass/src/lexer.cpp +9/−5
- libsass/src/lexer.hpp +21/−0
- libsass/src/listize.cpp +31/−33
- libsass/src/listize.hpp +9/−13
- libsass/src/memory/SharedPtr.cpp +116/−0
- libsass/src/memory/SharedPtr.hpp +202/−0
- libsass/src/memory_manager.cpp +0/−76
- libsass/src/memory_manager.hpp +0/−48
- libsass/src/node.cpp +31/−30
- libsass/src/node.hpp +9/−9
- libsass/src/operation.hpp +139/−138
- libsass/src/output.cpp +108/−167
- libsass/src/output.hpp +14/−16
- libsass/src/parser.cpp +2771/−2431
- libsass/src/parser.hpp +139/−85
- libsass/src/plugins.cpp +1/−0
- libsass/src/position.cpp +1/−0
- libsass/src/prelexer.cpp +607/−59
- libsass/src/prelexer.hpp +66/−5
- libsass/src/remove_placeholders.cpp +51/−21
- libsass/src/remove_placeholders.hpp +10/−8
- libsass/src/sass.cpp +55/−3
- libsass/src/sass.hpp +136/−0
- libsass/src/sass2scss.cpp +23/−4
- libsass/src/sass_context.cpp +223/−170
- libsass/src/sass_context.hpp +6/−32
- libsass/src/sass_functions.cpp +4/−3
- libsass/src/sass_interface.cpp +0/−216
- libsass/src/sass_util.cpp +1/−2
- libsass/src/sass_util.hpp +6/−6
- libsass/src/sass_values.cpp +36/−33
- libsass/src/source_map.cpp +26/−13
- libsass/src/source_map.hpp +2/−2
- libsass/src/subset_map.cpp +57/−0
- libsass/src/subset_map.hpp +8/−77
- libsass/src/to_c.cpp +14/−13
- libsass/src/to_c.hpp +15/−17
- libsass/src/to_string.cpp +0/−48
- libsass/src/to_string.hpp +0/−38
- libsass/src/to_value.cpp +22/−24
- libsass/src/to_value.hpp +20/−21
- libsass/src/units.cpp +99/−62
- libsass/src/units.hpp +13/−9
- libsass/src/utf8/unchecked.h +16/−16
- libsass/src/utf8_string.cpp +5/−0
- libsass/src/util.cpp +128/−269
- libsass/src/util.hpp +18/−20
- libsass/src/values.cpp +28/−26
- libsass/src/values.hpp +2/−2
- tests/Main.hs +1/−1
Bindings/Libsass.hs view
@@ -5,15 +5,12 @@ {-# LANGUAGE ForeignFunctionInterface #-} module Bindings.Libsass (- libsass_version- , module Libsass+ module Libsass ) where +import Bindings.Libsass.Types as Libsass+import Bindings.Libsass.Base as Libsass import Bindings.Libsass.Context as Libsass import Bindings.Libsass.Functions as Libsass-import Bindings.Libsass.Types as Libsass-import Bindings.Libsass.Wrappers as Libsass import Bindings.Libsass.Values as Libsass-import Foreign.C.String--foreign import ccall unsafe "sass/base.h" libsass_version :: CString+import Bindings.Libsass.Wrappers as Libsass
+ Bindings/Libsass/Base.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Bindings.Libsass.Base where++import Foreign+import Foreign.C++foreign import ccall unsafe "sass/base.h" sass_alloc_memory+ :: CSize+ -> IO (Ptr ())++foreign import ccall unsafe "sass/base.h" sass_copy_c_string+ :: CString+ -> IO CString++foreign import ccall unsafe "sass/base.h" sass_free_memory+ :: Ptr ()+ -> IO ()++foreign import ccall unsafe "sass/base.h" sass_string_quote+ :: CString+ -> CChar+ -> IO CString++foreign import ccall unsafe "sass/base.h" sass_string_unquote+ :: CString+ -> IO CString++foreign import ccall unsafe "sass/base.h" sass_resolve_file+ :: CString+ -> Ptr CString+ -> IO CString++foreign import ccall unsafe "sass/base.h" libsass_version+ :: CString++foreign import ccall unsafe "sass/base.h" libsass_language_version+ :: CString
Bindings/Libsass/Context.hs view
@@ -3,8 +3,7 @@ import Bindings.Libsass.Types import Foreign-import Foreign.C.String-import Foreign.C.Types+import Foreign.C foreign import ccall unsafe "sass/context.h" sass_make_options :: IO (Ptr SassOptions)@@ -50,6 +49,10 @@ :: Ptr SassCompiler -> IO () +foreign import ccall unsafe "sass/context.h" sass_delete_options+ :: Ptr SassOptions+ -> IO ()+ foreign import ccall unsafe "sass/context.h" sass_delete_file_context :: Ptr SassFileContext -> IO ()@@ -108,6 +111,10 @@ :: Ptr SassOptions -> IO Bool +foreign import ccall unsafe "sass/context.h" sass_option_get_source_map_file_urls+ :: Ptr SassOptions+ -> IO Bool+ foreign import ccall unsafe "sass/context.h" sass_option_get_omit_source_map_url :: Ptr SassOptions -> IO Bool@@ -185,6 +192,11 @@ -> Bool -> IO () +foreign import ccall unsafe "sass/context.h" sass_option_set_source_map_file_urls+ :: Ptr SassOptions+ -> Bool+ -> IO ()+ foreign import ccall unsafe "sass/context.h" sass_option_set_omit_source_map_url :: Ptr SassOptions -> Bool@@ -333,6 +345,10 @@ foreign import ccall unsafe "sass/context.h" sass_compiler_get_context :: Ptr SassCompiler -> IO (Ptr SassContext)++foreign import ccall unsafe "sass/context.h" sass_compiler_get_options+ :: Ptr SassCompiler+ -> IO (Ptr SassOptions) foreign import ccall unsafe "sass/context.h" sass_compiler_get_import_stack_size :: Ptr SassCompiler
Bindings/Libsass/Types.hs view
@@ -2,15 +2,17 @@ module Bindings.Libsass.Types where import Foreign-import Foreign.C.String+import Foreign.C --- From sass.h+-- From sass/base.h -- | Defines output style of compiled CSS. data SassOutputStyle = SassStyleNested | SassStyleExpanded | SassStyleCompact | SassStyleCompressed+ | SassStyleInspect -- ^ Marked as internal+ | SassStyleToSass -- ^ Marked as internal deriving (Eq, Show) instance Enum SassOutputStyle where@@ -18,14 +20,18 @@ fromEnum SassStyleExpanded = 1 fromEnum SassStyleCompact = 2 fromEnum SassStyleCompressed = 3+ fromEnum SassStyleInspect = 4+ fromEnum SassStyleToSass = 5 toEnum 0 = SassStyleNested toEnum 1 = SassStyleExpanded toEnum 2 = SassStyleCompact toEnum 3 = SassStyleCompressed+ toEnum 4 = SassStyleInspect+ toEnum 5 = SassStyleToSass toEnum u = error ("SassOutputStyle.toEnum: Cannot match " ++ show u) --- From sass_context.h+-- From sass/context.h data SassCompiler data SassOptions@@ -48,7 +54,7 @@ toEnum 2 = SassCompilerExecuted toEnum u = error ("SassCompilerState.toEnum: Cannot match " ++ show u) --- From sass_functions.h+-- From sass/functions.h data SassImport data SassImporter@@ -76,7 +82,7 @@ -> IO (Ptr SassValue) type SassFunctionFn = FunPtr SassFunctionFnType --- From sass_values.h+-- From sass/values.h data SassValue @@ -113,20 +119,23 @@ toEnum 8 = SassWarning toEnum u = error ("SassTag.toEnum: Cannot match " ++ show u) --- ^ Separator used in Sass lists.+-- | Separator used in Sass lists. data SassSeparator = SassSeparatorComma | SassSeparatorSpace+ | SassSeparatorHash -- ^ Marked as internal deriving (Eq, Show) instance Enum SassSeparator where fromEnum SassSeparatorComma = 0- fromEnum SassSeparatorSpace = 1+ fromEnum SassSeparatorSpace = 1+ fromEnum SassSeparatorHash = 2 toEnum 0 = SassSeparatorComma toEnum 1 = SassSeparatorSpace+ toEnum 2 = SassSeparatorHash toEnum u = error ("SassSeparator.toEnum: Cannot match " ++ show u) --- ^ Operator used to combine two 'SassValue's.+-- | Operator used to combine two 'SassValue's. data SassOp = SassAnd | SassOr | SassEq
Bindings/Libsass/Values.hs view
@@ -5,6 +5,70 @@ import Foreign import Foreign.C +foreign import ccall unsafe "sass/values.h" sass_make_null+ :: IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_boolean+ :: Bool+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_string+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_qstring+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_number+ :: CDouble+ -> CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_color+ :: CDouble+ -> CDouble+ -> CDouble+ -> CDouble+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_list+ :: CSize+ -> CInt -- ^ 'SassSeparator'+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_map+ :: CSize+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_error+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_make_warning+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_delete_value+ :: Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass/values.h" sass_clone_value+ :: Ptr SassValue+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_value_op+ :: CInt -- ^ 'SassOp'+ -> Ptr SassValue+ -> Ptr SassValue+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_value_stringify+ :: Ptr SassValue+ -> Bool+ -> CInt+ -> IO (Ptr SassValue)+ foreign import ccall unsafe "sass/values.h" sass_value_get_tag :: Ptr SassValue -> IO CInt -- ^ Returns 'SassTag'@@ -193,67 +257,3 @@ :: Ptr SassValue -> CString -> IO ()--foreign import ccall unsafe "sass/values.h" sass_make_null- :: IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_boolean- :: Bool- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_string- :: CString- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_qstring- :: CString- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_number- :: CDouble- -> CString- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_color- :: CDouble- -> CDouble- -> CDouble- -> CDouble- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_list- :: CSize- -> CInt -- ^ 'SassSeparator'- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_map- :: CSize- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_error- :: CString- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_make_warning- :: CString- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_delete_value- :: Ptr SassValue- -> IO ()--foreign import ccall unsafe "sass/values.h" sass_clone_value- :: Ptr SassValue- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_value_stringify- :: Ptr SassValue- -> Bool- -> CInt- -> IO (Ptr SassValue)--foreign import ccall unsafe "sass/values.h" sass_value_op- :: CInt -- ^ 'SassOp'- -> Ptr SassValue- -> Ptr SassValue- -> IO (Ptr SassValue)
CHANGELOG.md view
@@ -1,6 +1,13 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.1.6.0] - 2017-03-25+### Fixed+- `tools/libpath.sh` recognizes `stack`++### Changed+- Update to Libsass 3.4.3+ ## [0.1.5.2] - 2017-03-19 ### Fixed - Build process for dependant libraries works with Cabal 1.24 & Stack 1.4@@ -63,6 +70,7 @@ - Bindings to Libsass C API - Basic tests +[0.1.6.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.5.2...v0.1.6.0 [0.1.5.2]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.5.0...v0.1.5.2 [0.1.5.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.4.0...v0.1.5.0 [0.1.4.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.3.0...v0.1.4.0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015 Jakub Fijałkowski+Copyright (c) 2015-2017 Jakub Fijałkowski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
hlibsass.cabal view
@@ -1,5 +1,5 @@ name: hlibsass-version: 0.1.5.2+version: 0.1.6.0 license: MIT license-file: LICENSE author: Jakub Fijałkowski <fiolek94@gmail.com>@@ -26,6 +26,8 @@ libsass/src/b64/*.h libsass/src/utf8/*.h libsass/src/support/*.pc.in+ libsass/src/memory/*.hpp+ libsass/src/memory/*.cpp libsass/include/*.h libsass/include/sass/*.h libsass/include/sass/*.h.in@@ -54,6 +56,7 @@ library exposed-modules: Bindings.Libsass+ , Bindings.Libsass.Base , Bindings.Libsass.Types , Bindings.Libsass.Wrappers , Bindings.Libsass.Context@@ -64,10 +67,12 @@ default-language: Haskell2010 default-extensions: ForeignFunctionInterface ghc-options: -Wall- if flag(externalLibsass)- extra-libraries: sass- else- extra-libraries: sass, stdc+++ extra-libraries: sass+ if !flag(externalLibsass) && !flag(sharedLibsass)+ if os(darwin)+ extra-libraries: c+++ else+ extra-libraries: stdc++ test-suite tests hs-source-dirs: tests
libsass/LICENSE view
@@ -1,5 +1,5 @@ -Copyright (C) 2012 by Hampton Catlin+Copyright (C) 2012-2016 by the Sass Open Source Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in
libsass/Makefile view
@@ -1,3 +1,4 @@+OS ?= $(shell uname -s) CC ?= gcc CXX ?= g++ RM ?= rm -f@@ -5,6 +6,11 @@ MKDIR ?= mkdir RMDIR ?= rmdir WINDRES ?= windres+# Solaris/Illumos flavors+# ginstall from coreutils+ifeq ($(OS),SunOS)+INSTALL ?= ginstall+endif INSTALL ?= install CFLAGS ?= -Wall CXXFLAGS ?= -Wall@@ -20,14 +26,14 @@ ifneq (,$(findstring /cygdrive/,$(PATH))) UNAME := Cygwin else- ifneq (,$(findstring WINDOWS,$(PATH)))+ ifneq (,$(findstring Windows_NT,$(OS))) UNAME := Windows else ifneq (,$(findstring mingw32,$(MAKE)))- UNAME := MinGW+ UNAME := Windows else ifneq (,$(findstring MINGW32,$(shell uname -s)))- UNAME = MinGW+ UNAME = Windows else UNAME := $(shell uname -s) endif@@ -57,7 +63,7 @@ endif # enable mandatory flag-ifeq (MinGW,$(UNAME))+ifeq (Windows,$(UNAME)) ifneq ($(BUILD),shared) STATIC_ALL ?= 1 endif@@ -117,25 +123,33 @@ LDFLAGS += -stdlib=libc++ endif -ifneq (MinGW,$(UNAME))+ifneq (Windows,$(UNAME)) ifneq (FreeBSD,$(UNAME))- LDFLAGS += -ldl- LDLIBS += -ldl+ ifneq (OpenBSD,$(UNAME))+ LDFLAGS += -ldl+ LDLIBS += -ldl+ endif endif endif ifneq ($(BUILD),shared)- BUILD = static+ BUILD := static endif+ifeq ($(DEBUG),1)+ BUILD := debug-$(BUILD)+endif -ifeq (,$(PREFIX))- ifeq (,$(TRAVIS_BUILD_DIR))- PREFIX = /usr/local+ifeq (,$(TRAVIS_BUILD_DIR))+ ifeq ($(OS),SunOS)+ PREFIX ?= /opt/local else- PREFIX = $(TRAVIS_BUILD_DIR)+ PREFIX ?= /usr/local endif+else+ PREFIX ?= $(TRAVIS_BUILD_DIR) endif + SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec@@ -145,7 +159,7 @@ LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so -ifeq (MinGW,$(UNAME))+ifeq (Windows,$(UNAME)) ifeq (shared,$(BUILD)) CFLAGS += -D ADD_EXPORTS CXXFLAGS += -D ADD_EXPORTS@@ -159,9 +173,6 @@ endif endif -ifeq (MinGW,$(UNAME))- SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe-endif ifeq (Windows,$(UNAME)) SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe endif@@ -171,7 +182,7 @@ RESOURCES = STATICLIB = lib/libsass.a SHAREDLIB = lib/libsass.so-ifeq (MinGW,$(UNAME))+ifeq (Windows,$(UNAME)) RESOURCES += res/resource.rc SHAREDLIB = lib/libsass.dll ifeq (shared,$(BUILD))@@ -294,13 +305,16 @@ $(SASSC_BIN) -v test: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_build: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) -test_issues: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues+test_full: $(SASSC_BIN)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)++test_probe: $(SASSC_BIN)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.4 -c $(SASSC_BIN) --impl libsass --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) clean-objects: lib -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la
libsass/Makefile.conf view
@@ -1,47 +1,52 @@ # this is merely a common Makefile multiple implementers can use+# bigger files (in terms of compile time) tend to go to the top,+# so they don't end up as the last compile unit when compiling+# in parallel. But we also want to mix them a little too avoid+# heavy RAM usage peaks. Other than that the order is arbitrary. + SOURCES = \ ast.cpp \- base64vlq.cpp \- bind.cpp \- color_maps.cpp \- constants.cpp \+ node.cpp \ context.cpp \- cssize.cpp \- emitter.cpp \+ constants.cpp \+ functions.cpp \+ color_maps.cpp \ environment.cpp \- error_handling.cpp \- eval.cpp \- expand.cpp \- extend.cpp \+ bind.cpp \ file.cpp \- functions.cpp \- inspect.cpp \+ util.cpp \ json.cpp \- lexer.cpp \- listize.cpp \- memory_manager.cpp \- node.cpp \- output.cpp \- parser.cpp \+ units.cpp \+ values.cpp \ plugins.cpp \ position.cpp \+ lexer.cpp \+ parser.cpp \ prelexer.cpp \+ eval.cpp \+ expand.cpp \+ listize.cpp \+ cssize.cpp \+ extend.cpp \+ output.cpp \+ inspect.cpp \+ emitter.cpp \+ check_nesting.cpp \ remove_placeholders.cpp \ sass.cpp \ sass_util.cpp \ sass_values.cpp \ sass_context.cpp \ sass_functions.cpp \- sass_interface.cpp \ sass2scss.cpp \- source_map.cpp \ to_c.cpp \- to_string.cpp \ to_value.cpp \- units.cpp \+ source_map.cpp \+ subset_map.cpp \+ error_handling.cpp \+ memory/SharedPtr.cpp \ utf8_string.cpp \- values.cpp \- util.cpp+ base64vlq.cpp CSOURCES = cencode.c
libsass/VERSION view
@@ -1,1 +1,1 @@-3.3.2+3.4.3
libsass/include/sass/base.h view
@@ -1,6 +1,8 @@ #ifndef SASS_BASE_H #define SASS_BASE_H +// #define DEBUG_SHARED_PTR+ #ifdef _MSC_VER #pragma warning(disable : 4503) #ifndef _SCL_SECURE_NO_WARNINGS@@ -56,9 +58,19 @@ SASS_STYLE_NESTED, SASS_STYLE_EXPANDED, SASS_STYLE_COMPACT,- SASS_STYLE_COMPRESSED+ SASS_STYLE_COMPRESSED,+ // only used internaly+ SASS_STYLE_INSPECT,+ SASS_STYLE_TO_SASS }; +// to allocate buffer to be filled+ADDAPI void* ADDCALL sass_alloc_memory(size_t size);+// to allocate a buffer from existing string+ADDAPI char* ADDCALL sass_copy_c_string(const char* str);+// to free overtaken memory when done+ADDAPI void ADDCALL sass_free_memory(void* ptr);+ // Some convenient string helper function ADDAPI char* ADDCALL sass_string_quote (const char* str, const char quote_mark); ADDAPI char* ADDCALL sass_string_unquote (const char* str);@@ -66,8 +78,12 @@ // Resolve a file via the given include paths in the include char* array ADDAPI char* ADDCALL sass_resolve_file (const char* path, const char* incs[]); -// Get compiled libsass version+// Implemented sass language version+// Hardcoded version 3.4 for time being ADDAPI const char* ADDCALL libsass_version(void);++// Get compiled libsass language+ADDAPI const char* ADDCALL libsass_language_version(void); #ifdef __cplusplus } // __cplusplus defined.
libsass/include/sass/context.h view
@@ -50,6 +50,7 @@ // Release all memory allocated with the compiler // This does _not_ include any contexts or options ADDAPI void ADDCALL sass_delete_compiler(struct Sass_Compiler* compiler);+ADDAPI void ADDCALL sass_delete_options(struct Sass_Options* options); // Release all memory allocated and also ourself ADDAPI void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx);@@ -73,6 +74,7 @@ ADDAPI bool ADDCALL sass_option_get_source_comments (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_embed (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_contents (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_source_map_file_urls (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_omit_source_map_url (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_is_indented_syntax_src (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_indent (struct Sass_Options* options);@@ -93,6 +95,7 @@ ADDAPI void ADDCALL sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); ADDAPI void ADDCALL sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); ADDAPI void ADDCALL sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents);+ADDAPI void ADDCALL sass_option_set_source_map_file_urls (struct Sass_Options* options, bool source_map_file_urls); ADDAPI void ADDCALL sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); ADDAPI void ADDCALL sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); ADDAPI void ADDCALL sass_option_set_indent (struct Sass_Options* options, const char* indent);
− libsass/include/sass/interface.h
@@ -1,105 +0,0 @@-#ifndef SASS_C_INTERFACE_H-#define SASS_C_INTERFACE_H--// the API in this header has been deprecated-// please use the new API from sass/context.h--#include <stddef.h>-#include <stdbool.h>-#include <sass/base.h>--#ifdef __cplusplus-extern "C" {-#endif---// Please ensure there are no null values.-// Thar be dragons.-struct sass_options {- // Output style for the generated css code- // A value from above SASS_STYLE_* constants- int output_style;- // If you want inline source comments- bool source_comments;- // Path to source map file- // Enables the source map generating- // Used to create sourceMappingUrl- const char* source_map_file;- // Disable sourceMappingUrl in css output- bool omit_source_map_url;- // embed sourceMappingUrl as data uri- bool source_map_embed;- // embed include contents in maps- bool source_map_contents;- // Pass-through as sourceRoot property- const char* source_map_root;- // Treat source_string as sass (as opposed to scss)- bool is_indented_syntax_src;- // Colon-separated list of paths- // Semicolon-separated on Windows- const char* include_paths;- const char* plugin_paths;- // String to be used for indentation- const char* indent;- // String to be used to for line feeds- const char* linefeed;- // Precision for outputting fractional numbers- int precision;-};--struct sass_context {- const char* input_path;- const char* output_path;- char* source_string;- char* output_string;- char* source_map_string;- struct sass_options options;- int error_status;- char* error_message;- Sass_Function_List c_functions;- char** included_files;- int num_included_files;-};--struct sass_file_context {- const char* input_path;- const char* output_path;- char* output_string;- char* source_map_string;- struct sass_options options;- int error_status;- char* error_message;- Sass_Function_List c_functions;- char** included_files;- int num_included_files;-};--struct sass_folder_context {- const char* search_path;- const char* output_path;- struct sass_options options;- int error_status;- char* error_message;- Sass_Function_List c_functions;- char** included_files;- int num_included_files;-};--struct sass_context* sass_new_context (void);-struct sass_file_context* sass_new_file_context (void);-struct sass_folder_context* sass_new_folder_context (void);--void sass_free_context (struct sass_context* ctx);-void sass_free_file_context (struct sass_file_context* ctx);-void sass_free_folder_context(struct sass_folder_context* ctx);--int sass_compile (struct sass_context* ctx);-int sass_compile_file (struct sass_file_context* ctx);-int sass_compile_folder (struct sass_folder_context* ctx);---#ifdef __cplusplus-}-#endif--#endif
libsass/include/sass/values.h view
@@ -29,7 +29,10 @@ // Tags for denoting Sass list separators enum Sass_Separator { SASS_COMMA,- SASS_SPACE+ SASS_SPACE,+ // only used internally to represent a hash map before evaluation+ // otherwise we would be too early to check for duplicate keys+ SASS_HASH }; // Value Operators@@ -40,6 +43,32 @@ NUM_OPS // so we know how big to make the op table }; +// Creator functions for all value types+ADDAPI union Sass_Value* ADDCALL sass_make_null (void);+ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);+ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit);+ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a);+ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep);+ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len);+ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg);+ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);++// Generic destructor function for all types+// Will release memory of all associated Sass_Values+// Means we will delete recursively for lists and maps+ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);++// Make a deep cloned copy of the given sass value+ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);++// Execute an operation for two Sass_Values and return the result as a Sass_Value too+ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b);++// Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING)+ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision);+ // Return the sass tag for a generic sass value // Check is needed before accessing specific values! ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);@@ -106,33 +135,6 @@ // Getters and setters for Sass_Warning ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v); ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg);--// Creator functions for all value types-ADDAPI union Sass_Value* ADDCALL sass_make_null (void);-ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);-ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val);-ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);-ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit);-ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a);-ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep);-ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len);-ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg);-ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);--// Generic destructor function for all types-// Will release memory of all associated Sass_Values-// Means we will delete recursively for lists and maps-ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);--// Make a deep cloned copy of the given sass value-ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);--// Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING)-ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision);--// Execute an operation for two Sass_Values and return the result as a Sass_Value too-ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b);- #ifdef __cplusplus } // __cplusplus defined.
libsass/include/sass/version.h view
@@ -5,4 +5,8 @@ #define LIBSASS_VERSION "[NA]" #endif +#ifndef LIBSASS_LANGUAGE_VERSION+#define LIBSASS_LANGUAGE_VERSION "3.4"+#endif+ #endif
libsass/include/sass/version.h.in view
@@ -5,4 +5,8 @@ #define LIBSASS_VERSION "@PACKAGE_VERSION@" #endif +#ifndef LIBSASS_LANGUAGE_VERSION+#define LIBSASS_LANGUAGE_VERSION "3.4"+#endif+ #endif
libsass/include/sass2scss.h view
@@ -37,7 +37,7 @@ #ifndef SASS2SCSS_VERSION // Hardcode once the file is copied from // https://github.com/mgreter/sass2scss-#define SASS2SCSS_VERSION "1.0.5"+#define SASS2SCSS_VERSION "1.1.0" #endif // add namespace for c++
libsass/src/ast.cpp view
@@ -1,2134 +1,2451 @@-#include "ast.hpp"-#include "context.hpp"-#include "node.hpp"-#include "extend.hpp"-#include "to_string.hpp"-#include "color_maps.hpp"-#include <set>-#include <iomanip>-#include <algorithm>-#include <iostream>--namespace Sass {-- static Null sass_null(Sass::Null(ParserState("null")));-- bool Supports_Operator::needs_parens(Supports_Condition* cond) const {- return dynamic_cast<Supports_Negation*>(cond) ||- (dynamic_cast<Supports_Operator*>(cond) &&- dynamic_cast<Supports_Operator*>(cond)->operand() != operand());- }-- bool Supports_Negation::needs_parens(Supports_Condition* cond) const {- return dynamic_cast<Supports_Negation*>(cond) ||- dynamic_cast<Supports_Operator*>(cond);- }-- void AST_Node::update_pstate(const ParserState& pstate)- {- pstate_.offset += pstate - pstate_ + pstate.offset;- }-- inline bool is_ns_eq(const std::string& l, const std::string& r)- {- if (l.empty() && r.empty()) return true;- else if (l.empty() && r == "*") return true;- else if (r.empty() && l == "*") return true;- else return l == r;- }---- bool Compound_Selector::operator< (const Compound_Selector& rhs) const- {- size_t L = std::min(length(), rhs.length());- for (size_t i = 0; i < L; ++i)- {- Simple_Selector* l = (*this)[i];- Simple_Selector* r = rhs[i];- if (!l && !r) return false;- else if (!r) return false;- else if (!l) return true;- else if (*l != *r)- { return *l < *r; }- }- // just compare the length now- return length() < rhs.length();- }-- bool Compound_Selector::has_parent_ref()- {- return has_parent_reference();- }-- bool Complex_Selector::has_parent_ref()- {- return (head() && head()->has_parent_ref()) ||- (tail() && tail()->has_parent_ref());- }-- bool Complex_Selector::operator< (const Complex_Selector& rhs) const- {- // const iterators for tails- const Complex_Selector* l = this;- const Complex_Selector* r = &rhs;- Compound_Selector* l_h = l ? l->head() : 0;- Compound_Selector* r_h = r ? r->head() : 0;- // process all tails- while (true)- {- // skip empty ancestor first- if (l && l->is_empty_ancestor())- {- l = l->tail();- l_h = l ? l->head() : 0;- continue;- }- // skip empty ancestor first- if (r && r->is_empty_ancestor())- {- r = r->tail();- r_h = r ? r->head() : 0;- continue;- }- // check for valid selectors- if (!l) return !!r;- if (!r) return false;- // both are null- else if (!l_h && !r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() < r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next headers- l_h = l ? l->head() : 0;- r_h = r ? r->head() : 0;- }- // one side is null- else if (!r_h) return true;- else if (!l_h) return false;- // heads ok and equal- else if (*l_h == *r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() < r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next headers- l_h = l ? l->head() : 0;- r_h = r ? r->head() : 0;- }- // heads are not equal- else return *l_h < *r_h;- }- return true;- }-- bool Complex_Selector::operator== (const Complex_Selector& rhs) const- {- // const iterators for tails- const Complex_Selector* l = this;- const Complex_Selector* r = &rhs;- Compound_Selector* l_h = l ? l->head() : 0;- Compound_Selector* r_h = r ? r->head() : 0;- // process all tails- while (true)- {- // skip empty ancestor first- if (l && l->is_empty_ancestor())- {- l = l->tail();- l_h = l ? l->head() : 0;- continue;- }- // skip empty ancestor first- if (r && r->is_empty_ancestor())- {- r = r->tail();- r_h = r ? r->head() : 0;- continue;- }- // check the pointers- if (!r) return !l;- if (!l) return !r;- // both are null- if (!l_h && !r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() < r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next heads- l_h = l ? l->head() : 0;- r_h = r ? r->head() : 0;- }- // fail if only one is null- else if (!r_h) return !l_h;- else if (!l_h) return !r_h;- // heads ok and equal- else if (*l_h == *r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() == r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next heads- l_h = l ? l->head() : 0;- r_h = r ? r->head() : 0;- }- // abort- else break;- }- // unreachable- return false;- }-- Compound_Selector* Compound_Selector::unify_with(Compound_Selector* rhs, Context& ctx)- {- Compound_Selector* unified = rhs;- for (size_t i = 0, L = length(); i < L; ++i)- {- if (!unified) break;- unified = (*this)[i]->unify_with(unified, ctx);- }- return unified;- }-- bool Simple_Selector::operator== (const Simple_Selector& rhs) const- {- const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);- const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);- if (ll && rr) return *ll == *rr;-- if (is_ns_eq(ns(), rhs.ns()))- { return name() == rhs.name(); }- return ns() == rhs.ns();- }-- bool Simple_Selector::operator< (const Simple_Selector& rhs) const- {- const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);- const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);- if (ll && rr) return *ll < *rr;-- if (is_ns_eq(ns(), rhs.ns()))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Selector_List::operator== (const Selector& rhs) const- {- // solve the double dispatch problem by using RTTI information via dynamic cast- if (const Selector_List* ls = dynamic_cast<const Selector_List*>(&rhs)) { return *this == *ls; }- else if (const Complex_Selector* ls = dynamic_cast<const Complex_Selector*>(&rhs)) { return *this == *ls; }- else if (const Compound_Selector* ls = dynamic_cast<const Compound_Selector*>(&rhs)) { return *this == *ls; }- // no compare method- return this == &rhs;- }-- // Selector lists can be compared to comma lists- bool Selector_List::operator==(const Expression& rhs) const- {- // solve the double dispatch problem by using RTTI information via dynamic cast- if (const List* ls = dynamic_cast<const List*>(&rhs)) { return *this == *ls; }- if (const Selector* ls = dynamic_cast<const Selector*>(&rhs)) { return *this == *ls; }- // compare invalid (maybe we should error?)- return false;- }-- bool Selector_List::operator== (const Selector_List& rhs) const- {- // for array access- size_t i = 0, n = 0;- size_t iL = length();- size_t nL = rhs.length();- // create temporary vectors and sort them- std::vector<Complex_Selector*> l_lst = this->elements();- std::vector<Complex_Selector*> r_lst = rhs.elements();- std::sort(l_lst.begin(), l_lst.end(), cmp_complex_selector());- std::sort(r_lst.begin(), r_lst.end(), cmp_complex_selector());- // process loop- while (true)- {- // first check for valid index- if (i == iL) return iL == nL;- else if (n == nL) return iL == nL;- // the access the vector items- Complex_Selector* l = l_lst[i];- Complex_Selector* r = r_lst[n];- // skip nulls- if (!l) ++i;- else if (!r) ++n;- // do the check- else if (*l != *r)- { return false; }- // advance- ++i; ++n;- }- // no mismatch- return true;- }-- Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx)- {- To_String to_string(&ctx);- for (size_t i = 0, L = rhs->length(); i < L; ++i)- { if (perform(&to_string) == (*rhs)[i]->perform(&to_string)) return rhs; }-- // check for pseudo elements because they are always last- size_t i, L;- bool found = false;- if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector))- {- for (i = 0, L = rhs->length(); i < L; ++i)- {- if ((typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) && (*rhs)[L-1]->is_pseudo_element())- { found = true; break; }- }- }- else- {- for (i = 0, L = rhs->length(); i < L; ++i)- {- if (typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector))- { found = true; break; }- }- }- if (!found)- {- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);- (*cpy) << this;- return cpy;- }- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());- for (size_t j = 0; j < i; ++j)- { (*cpy) << (*rhs)[j]; }- (*cpy) << this;- for (size_t j = i; j < L; ++j)- { (*cpy) << (*rhs)[j]; }- return cpy;- }-- Simple_Selector* Type_Selector::unify_with(Simple_Selector* rhs, Context& ctx)- {- // check if ns can be extended- // true for no ns or universal- if (has_universal_ns())- {- // but dont extend with universal- // true for valid ns and universal- if (!rhs->is_universal_ns())- {- // creaty the copy inside (avoid unnecessary copies)- Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this);- // overwrite the name if star is given as name- if (ts->name() == "*") { ts->name(rhs->name()); }- // now overwrite the namespace name and flag- ts->ns(rhs->ns()); ts->has_ns(rhs->has_ns());- // return copy- return ts;- }- }- // namespace may changed, check the name now- // overwrite star (but not with another star)- if (name() == "*" && rhs->name() != "*")- {- // creaty the copy inside (avoid unnecessary copies)- Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this);- // simply set the new name- ts->name(rhs->name());- // return copy- return ts;- }- // return original- return this;- }-- Compound_Selector* Type_Selector::unify_with(Compound_Selector* rhs, Context& ctx)- {- // TODO: handle namespaces-- // if the rhs is empty, just return a copy of this- if (rhs->length() == 0) {- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());- (*cpy) << this;- return cpy;- }-- Simple_Selector* rhs_0 = (*rhs)[0];- // otherwise, this is a tag name- if (name() == "*")- {- if (typeid(*rhs_0) == typeid(Type_Selector))- {- // if rhs is universal, just return this tagname + rhs's qualifiers- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);- Type_Selector* ts = static_cast<Type_Selector*>(rhs_0);- (*cpy)[0] = this->unify_with(ts, ctx);- return cpy;- }- else if (dynamic_cast<Selector_Qualifier*>(rhs_0)) {- // qualifier is `.class`, so we can prefix with `ns|*.class`- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());- if (has_ns() && !rhs_0->has_ns()) {- if (ns() != "*") (*cpy) << this;- }- for (size_t i = 0, L = rhs->length(); i < L; ++i)- { (*cpy) << (*rhs)[i]; }- return cpy;- }--- return rhs;- }-- if (typeid(*rhs_0) == typeid(Type_Selector))- {- // if rhs is universal, just return this tagname + rhs's qualifiers- if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0;- // otherwise create new compound and unify first simple selector- Compound_Selector* copy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);- (*copy)[0] = this->unify_with(rhs_0, ctx);- return copy;-- }- // else it's a tag name and a bunch of qualifiers -- just append them- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());- if (name() != "*") (*cpy) << this;- (*cpy) += rhs;- return cpy;- }-- Compound_Selector* Selector_Qualifier::unify_with(Compound_Selector* rhs, Context& ctx)- {- if (name()[0] == '#')- {- for (size_t i = 0, L = rhs->length(); i < L; ++i)- {- Simple_Selector* rhs_i = (*rhs)[i];- if (typeid(*rhs_i) == typeid(Selector_Qualifier) &&- static_cast<Selector_Qualifier*>(rhs_i)->name()[0] == '#' &&- static_cast<Selector_Qualifier*>(rhs_i)->name() != name())- return 0;- }- }- rhs->has_line_break(has_line_break());- return Simple_Selector::unify_with(rhs, ctx);- }-- Compound_Selector* Pseudo_Selector::unify_with(Compound_Selector* rhs, Context& ctx)- {- if (is_pseudo_element())- {- for (size_t i = 0, L = rhs->length(); i < L; ++i)- {- Simple_Selector* rhs_i = (*rhs)[i];- if (typeid(*rhs_i) == typeid(Pseudo_Selector) &&- static_cast<Pseudo_Selector*>(rhs_i)->is_pseudo_element() &&- static_cast<Pseudo_Selector*>(rhs_i)->name() != name())- { return 0; }- }- }- return Simple_Selector::unify_with(rhs, ctx);- }-- bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const- {- if (is_ns_eq(ns(), rhs.ns())) {- if (name() == rhs.name()) {- if (matcher() == rhs.matcher()) {- return value() < rhs.value();- } else { return matcher() < rhs.matcher(); }- } else { return name() < rhs.name(); }- }- else return false;- }-- bool Attribute_Selector::operator< (const Simple_Selector& rhs) const- {- if (const Attribute_Selector* w = dynamic_cast<const Attribute_Selector*>(&rhs))- {- return *this < *w;- }- if (is_ns_eq(ns(), rhs.ns()))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const- {- if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())- { return matcher() == rhs.matcher() && value() == rhs.value(); }- else return false;- }-- bool Attribute_Selector::operator== (const Simple_Selector& rhs) const- {- if (const Attribute_Selector* w = dynamic_cast<const Attribute_Selector*>(&rhs))- {- return *this == *w;- }- if (is_ns_eq(ns(), rhs.ns()))- { return name() == rhs.name(); }- return ns() == rhs.ns();- }-- bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const- {- if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())- { return *(selector()) == *(rhs.selector()); }- else return false;- }-- bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const- {- if (const Wrapped_Selector* w = dynamic_cast<const Wrapped_Selector*>(&rhs))- {- return *this == *w;- }- if (is_ns_eq(ns(), rhs.ns()))- { return name() == rhs.name(); }- return ns() == rhs.ns();- }-- bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub)- {- if (this->name() != sub->name()) return false;- if (this->name() == ":current") return false;- if (Selector_List* rhs_list = dynamic_cast<Selector_List*>(sub->selector())) {- if (Selector_List* lhs_list = dynamic_cast<Selector_List*>(selector())) {- return lhs_list->is_superselector_of(rhs_list);- }- error("is_superselector expected a Selector_List", sub->pstate());- } else {- error("is_superselector expected a Selector_List", sub->pstate());- }- return false;- }-- bool Compound_Selector::is_superselector_of(Selector_List* rhs, std::string wrapped)- {- for (Complex_Selector* item : rhs->elements()) {- if (is_superselector_of(item, wrapped)) return true;- }- return false;- }-- bool Compound_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapped)- {- if (rhs->head()) return is_superselector_of(rhs->head(), wrapped);- return false;- }-- bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)- {- To_String to_string;-- Compound_Selector* lhs = this;- Simple_Selector* lbase = lhs->base();- Simple_Selector* rbase = rhs->base();-- // Check if pseudo-elements are the same between the selectors-- std::set<std::string> lpsuedoset, rpsuedoset;- for (size_t i = 0, L = length(); i < L; ++i)- {- if ((*this)[i]->is_pseudo_element()) {- std::string pseudo((*this)[i]->perform(&to_string));- pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving- lpsuedoset.insert(pseudo);- }- }- for (size_t i = 0, L = rhs->length(); i < L; ++i)- {- if ((*rhs)[i]->is_pseudo_element()) {- std::string pseudo((*rhs)[i]->perform(&to_string));- pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving- rpsuedoset.insert(pseudo);- }- }- if (lpsuedoset != rpsuedoset) {- return false;- }-- std::set<std::string> lset, rset;-- if (lbase && rbase)- {- if (lbase->perform(&to_string) == rbase->perform(&to_string)) {- for (size_t i = 1, L = length(); i < L; ++i)- { lset.insert((*this)[i]->perform(&to_string)); }- for (size_t i = 1, L = rhs->length(); i < L; ++i)- { rset.insert((*rhs)[i]->perform(&to_string)); }- return includes(rset.begin(), rset.end(), lset.begin(), lset.end());- }- return false;- }-- for (size_t i = 0, iL = length(); i < iL; ++i)- {- Selector* lhs = (*this)[i];- // very special case for wrapped matches selector- if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(lhs)) {- if (wrapped->name() == ":not") {- if (Selector_List* not_list = dynamic_cast<Selector_List*>(wrapped->selector())) {- if (not_list->is_superselector_of(rhs, wrapped->name())) return false;- } else {- throw std::runtime_error("wrapped not selector is not a list");- }- }- if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {- lhs = wrapped->selector();- if (Selector_List* list = dynamic_cast<Selector_List*>(wrapped->selector())) {- if (Compound_Selector* comp = dynamic_cast<Compound_Selector*>(rhs)) {- if (!wrapping.empty() && wrapping != wrapped->name()) return false;- if (wrapping.empty() || wrapping != wrapped->name()) {;- if (list->is_superselector_of(comp, wrapped->name())) return true;- }- }- }- }- Simple_Selector* rhs_sel = rhs->elements().size() > i ? (*rhs)[i] : 0;- if (Wrapped_Selector* wrapped_r = dynamic_cast<Wrapped_Selector*>(rhs_sel)) {- if (wrapped->name() == wrapped_r->name()) {- if (wrapped->is_superselector_of(wrapped_r)) {- continue;- rset.insert(lhs->perform(&to_string));-- }}- }- }- // match from here on as strings- lset.insert(lhs->perform(&to_string));- }-- for (size_t n = 0, nL = rhs->length(); n < nL; ++n)- {- auto r = (*rhs)[n];- if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(r)) {- if (wrapped->name() == ":not") {- if (Selector_List* ls = dynamic_cast<Selector_List*>(wrapped->selector())) {- ls->remove_parent_selectors();- if (is_superselector_of(ls, wrapped->name())) return false;- }- }- if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {- if (!wrapping.empty()) {- if (wrapping != wrapped->name()) return false;- }- if (Selector_List* ls = dynamic_cast<Selector_List*>(wrapped->selector())) {- ls->remove_parent_selectors();- return (is_superselector_of(ls, wrapped->name()));- }- }- }- rset.insert(r->perform(&to_string));- }-- //for (auto l : lset) { cerr << "l: " << l << endl; }- //for (auto r : rset) { cerr << "r: " << r << endl; }-- if (lset.empty()) return true;- // return true if rset contains all the elements of lset- return includes(rset.begin(), rset.end(), lset.begin(), lset.end());-- }-- // create complex selector (ancestor of) from compound selector- Complex_Selector* Compound_Selector::to_complex(Memory_Manager& mem)- {- // create an intermediate complex selector- return SASS_MEMORY_NEW(mem, Complex_Selector,- pstate(),- Complex_Selector::ANCESTOR_OF,- this,- 0);- }-- Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx)- {-- // get last tails (on the right side)- Complex_Selector* l_last = this->last();- Complex_Selector* r_last = other->last();-- // check valid pointers (assertion)- SASS_ASSERT(l_last, "lhs is null");- SASS_ASSERT(r_last, "rhs is null");-- // Not sure about this check, but closest way I could check- // was to see if this is a ruby 'SimpleSequence' equivalent.- // It seems to do the job correctly as some specs react to this- if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0;- if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0;-- // get the headers for the last tails- Compound_Selector* l_last_head = l_last->head();- Compound_Selector* r_last_head = r_last->head();-- // check valid head pointers (assertion)- SASS_ASSERT(l_last_head, "lhs head is null");- SASS_ASSERT(r_last_head, "rhs head is null");-- // get the unification of the last compound selectors- Compound_Selector* unified = r_last_head->unify_with(l_last_head, ctx);-- // abort if we could not unify heads- if (unified == 0) return 0;-- // check for universal (star: `*`) selector- bool is_universal = l_last_head->is_universal() ||- r_last_head->is_universal();-- if (is_universal)- {- // move the head- l_last->head(0);- r_last->head(unified);- }-- // create nodes from both selectors- Node lhsNode = complexSelectorToNode(this, ctx);- Node rhsNode = complexSelectorToNode(other, ctx);-- // overwrite universal base- if (!is_universal)- {- // create some temporaries to convert to node- Complex_Selector* fake = unified->to_complex(ctx.mem);- Node unified_node = complexSelectorToNode(fake, ctx);- // add to permutate the list?- rhsNode.plus(unified_node);- }-- // do some magic we inherit from node and extend- Node node = Extend::subweave(lhsNode, rhsNode, ctx);- Selector_List* result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- NodeDequePtr col = node.collection(); // move from collection to list- for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++)- { (*result) << nodeToComplexSelector(Node::naiveTrim(*it, ctx), ctx); }-- // only return if list has some entries- return result->length() ? result : 0;-- }-- bool Compound_Selector::operator== (const Compound_Selector& rhs) const- {- // for array access- size_t i = 0, n = 0;- size_t iL = length();- size_t nL = rhs.length();- // create temporary vectors and sort them- std::vector<Simple_Selector*> l_lst = this->elements();- std::vector<Simple_Selector*> r_lst = rhs.elements();- std::sort(l_lst.begin(), l_lst.end(), cmp_simple_selector());- std::sort(r_lst.begin(), r_lst.end(), cmp_simple_selector());- // process loop- while (true)- {- // first check for valid index- if (i == iL) return iL == nL;- else if (n == nL) return iL == nL;- // the access the vector items- Simple_Selector* l = l_lst[i];- Simple_Selector* r = r_lst[n];- // skip nulls- if (!l) ++i;- if (!r) ++n;- // do the check now- else if (*l != *r)- { return false; }- // advance now- ++i; ++n;- }- // no mismatch- return true;- }-- bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const {- return *pLeft < *pRight;- }-- bool Complex_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)- {- return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);- }-- bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapping)- {- Complex_Selector* lhs = this;- To_String to_string;- // check for selectors with leading or trailing combinators- if (!lhs->head() || !rhs->head())- { return false; }- const Complex_Selector* l_innermost = lhs->innermost();- if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)- { return false; }- const Complex_Selector* r_innermost = rhs->innermost();- if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF)- { return false; }- // more complex (i.e., longer) selectors are always more specific- size_t l_len = lhs->length(), r_len = rhs->length();- if (l_len > r_len)- { return false; }-- if (l_len == 1)- { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); }-- // we have to look one tail deeper, since we cary the- // combinator around for it (which is important here)- if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {- Complex_Selector* lhs_tail = lhs->tail();- Complex_Selector* rhs_tail = rhs->tail();- if (lhs_tail->combinator() != rhs_tail->combinator()) return false;- if (lhs_tail->head() && !rhs_tail->head()) return false;- if (!lhs_tail->head() && rhs_tail->head()) return false;- if (lhs_tail->head() && lhs_tail->head()) {- if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;- }- }-- bool found = false;- Complex_Selector* marker = rhs;- for (size_t i = 0, L = rhs->length(); i < L; ++i) {- if (i == L-1)- { return false; }- if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping))- { found = true; break; }- marker = marker->tail();- }- if (!found)- { return false; }-- /*- Hmm, I hope I have the logic right:-- if lhs has a combinator:- if !(marker has a combinator) return false- if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false- return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)- else if marker has a combinator:- if !(marker.combinator == ">") return false- return lhs.tail.is_superselector_of(marker.tail)- else- return lhs.tail.is_superselector_of(marker.tail)- */- if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)- {- if (marker->combinator() == Complex_Selector::ANCESTOR_OF)- { return false; }- if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))- { return false; }- return lhs->tail()->is_superselector_of(marker->tail());- }- else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)- {- if (marker->combinator() != Complex_Selector::PARENT_OF)- { return false; }- return lhs->tail()->is_superselector_of(marker->tail());- }- else- {- return lhs->tail()->is_superselector_of(marker->tail());- }- // catch-all- return false;- }-- size_t Complex_Selector::length() const- {- // TODO: make this iterative- if (!tail()) return 1;- return 1 + tail()->length();- }-- Complex_Selector* Complex_Selector::context(Context& ctx)- {- if (!tail()) return 0;- if (!head()) return tail()->context(ctx);- Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate(), combinator(), head(), tail()->context(ctx));- cpy->media_block(media_block());- return cpy;- }-- // append another complex selector at the end- // check if we need to append some headers- // then we need to check for the combinator- // only then we can safely set the new tail- void Complex_Selector::append(Context& ctx, Complex_Selector* ss)- {-- Complex_Selector* t = ss->tail();- Combinator c = ss->combinator();- String* r = ss->reference();- Compound_Selector* h = ss->head();-- if (ss->has_line_feed()) has_line_feed(true);- if (ss->has_line_break()) has_line_break(true);-- // append old headers- if (h && h->length()) {- if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {- error("Invalid parent selector", pstate_);- } else if (last()->head_ && last()->head_->length()) {- Compound_Selector* rh = last()->head();- size_t i = 0, L = h->length();- if (dynamic_cast<Type_Selector*>(h->first())) {- if (Selector_Qualifier* sq = dynamic_cast<Selector_Qualifier*>(rh->last())) {- Selector_Qualifier* sqs = new Selector_Qualifier(*sq);- sqs->name(sqs->name() + (*h)[0]->name());- (*rh)[rh->length()-1] = sqs;- for (i = 1; i < L; ++i) *rh << (*h)[i];- } else if (Type_Selector* ts = dynamic_cast<Type_Selector*>(rh->last())) {- Type_Selector* tss = new Type_Selector(*ts);- tss->name(tss->name() + (*h)[0]->name());- (*rh)[rh->length()-1] = tss;- for (i = 1; i < L; ++i) *rh << (*h)[i];- } else if (Selector_Placeholder* ps = dynamic_cast<Selector_Placeholder*>(rh->last())) {- Selector_Placeholder* pss = new Selector_Placeholder(*ps);- pss->name(pss->name() + (*h)[0]->name());- (*rh)[rh->length()-1] = pss;- for (i = 1; i < L; ++i) *rh << (*h)[i];- } else {- *last()->head_ += h;- }- } else {- *last()->head_ += h;- }- } else {- *last()->head_ += h;- }- } else {- // std::cerr << "has no or empty head\n";- }-- if (last()) {- if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {- Complex_Selector* inter = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate());- inter->reference(r);- inter->combinator(c);- inter->tail(t);- last()->tail(inter);- } else {- if (last()->combinator() == ANCESTOR_OF) {- last()->combinator(c);- last()->reference(r);- }- last()->tail(t);- }- }--- }-- Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx)- {- Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {- Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- *list << (*ps)[pi];- for (size_t si = 0, sL = this->length(); si < sL; ++si) {- *ss += (*this)[si]->parentize(list, ctx);- }- }- return ss;- }-- Selector_List* Complex_Selector::parentize(Selector_List* parents, Context& ctx)- {-- Complex_Selector* tail = this->tail();- Compound_Selector* head = this->head();-- // first parentize the tail (which may return an expanded list)- Selector_List* tails = tail ? tail->parentize(parents, ctx) : 0;-- if (head && head->length() > 0) {-- // we have a parent selector in a simple compound list- // mix parent complex selector into the compound list- if (dynamic_cast<Parent_Selector*>((*head)[0])) {- if (parents && parents->length()) {- Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- if (tails && tails->length() > 0) {- for (size_t n = 0, nL = tails->length(); n < nL; ++n) {- for (size_t i = 0, iL = parents->length(); i < iL; ++i) {- Complex_Selector* t = (*tails)[n];- Complex_Selector* parent = (*parents)[i];- Complex_Selector* s = parent->cloneFully(ctx);- Complex_Selector* ss = this->clone(ctx);- ss->tail(t ? t->clone(ctx) : 0);- Compound_Selector* h = head_->clone(ctx);- if (h->length()) h->erase(h->begin());- ss->head(h->length() ? h : 0);- s->append(ctx, ss);- *retval << s;- }- }- }- // have no tails but parents- // loop above is inside out- else {- for (size_t i = 0, iL = parents->length(); i < iL; ++i) {- Complex_Selector* parent = (*parents)[i];- Complex_Selector* s = parent->cloneFully(ctx);- Complex_Selector* ss = this->clone(ctx);- // this is only if valid if the parent has no trailing op- // otherwise we cannot append more simple selectors to head- if (parent->last()->combinator() != ANCESTOR_OF) {- throw Exception::InvalidParent(parent, ss);- }- ss->tail(tail ? tail->clone(ctx) : 0);- Compound_Selector* h = head_->clone(ctx);- if (h->length()) h->erase(h->begin());- ss->head(h->length() ? h : 0);- // \/ IMO ruby sass bug \/- ss->has_line_feed(false);- s->append(ctx, ss);- *retval << s;- }- }- return retval;- }- // have no parent but some tails- else {- Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- if (tails && tails->length() > 0) {- for (size_t n = 0, nL = tails->length(); n < nL; ++n) {- Complex_Selector* cpy = this->clone(ctx);- cpy->tail((*tails)[n]->cloneFully(ctx));- cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate()));- for (size_t i = 1, L = this->head()->length(); i < L; ++i)- *cpy->head() << (*this->head())[i];- if (!cpy->head()->length()) cpy->head(0);- *retval << cpy->skip_empty_reference();- }- }- // have no parent nor tails- else {- Complex_Selector* cpy = this->clone(ctx);- cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate()));- for (size_t i = 1, L = this->head()->length(); i < L; ++i)- *cpy->head() << (*this->head())[i];- if (!cpy->head()->length()) cpy->head(0);- *retval << cpy->skip_empty_reference();- }- return retval;- }- }- // no parent selector in head- else {- return this->tails(ctx, tails);- }-- }- // has no head- else {- return this->tails(ctx, tails);- }-- // unreachable- return 0;- }-- Selector_List* Complex_Selector::tails(Context& ctx, Selector_List* tails)- {- Selector_List* rv = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate_);- if (tails && tails->length()) {- for (size_t i = 0, iL = tails->length(); i < iL; ++i) {- Complex_Selector* pr = this->clone(ctx);- pr->tail((*tails)[i]);- *rv << pr;- }- }- else {- *rv << this;- }- return rv;- }-- // return the last tail that is defined- Complex_Selector* Complex_Selector::first()- {- // declare variables used in loop- Complex_Selector* cur = this->tail_;- const Compound_Selector* head = head_;- // processing loop- while (cur)- {- // get the head- head = cur->head_;- // check for single parent ref- if (head && head->length() == 1)- {- // abort (and return) if it is not a parent selector- if (!dynamic_cast<Parent_Selector*>((*head)[0])) break;- }- // advance to next- cur = cur->tail_;- }- // result- return cur;- }-- // return the last tail that is defined- const Complex_Selector* Complex_Selector::first() const- {- // declare variables used in loop- const Complex_Selector* cur = this->tail_;- const Compound_Selector* head = head_;- // processing loop- while (cur)- {- // get the head- head = cur->head_;- // check for single parent ref- if (head && head->length() == 1)- {- // abort (and return) if it is not a parent selector- if (!dynamic_cast<Parent_Selector*>((*head)[0])) break;- }- // advance to next- cur = cur->tail_;- }- // result- return cur;- }-- // return the last tail that is defined- Complex_Selector* Complex_Selector::last()- {- // ToDo: implement with a while loop- return tail_? tail_->last() : this;- }-- // return the last tail that is defined- const Complex_Selector* Complex_Selector::last() const- {- // ToDo: implement with a while loop- return tail_? tail_->last() : this;- }--- Complex_Selector::Combinator Complex_Selector::clear_innermost()- {- Combinator c;- if (!tail() || tail()->tail() == 0)- { c = combinator(); combinator(ANCESTOR_OF); tail(0); }- else- { c = tail()->clear_innermost(); }- return c;- }-- void Complex_Selector::set_innermost(Complex_Selector* val, Combinator c)- {- if (!tail())- { tail(val); combinator(c); }- else- { tail()->set_innermost(val, c); }- }-- Complex_Selector* Complex_Selector::clone(Context& ctx) const- {- Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);- cpy->media_block(this->media_block());- if (tail()) cpy->tail(tail()->clone(ctx));- return cpy;- }-- Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const- {- Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);-- if (head()) {- cpy->head(head()->clone(ctx));- }-- if (tail()) {- cpy->tail(tail()->cloneFully(ctx));- }-- return cpy;- }-- Compound_Selector* Compound_Selector::clone(Context& ctx) const- {- Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this);- cpy->media_block(this->media_block());- return cpy;- }-- Selector_List* Selector_List::clone(Context& ctx) const- {- Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this);- cpy->media_block(this->media_block());- return cpy;- }-- Selector_List* Selector_List::cloneFully(Context& ctx) const- {- Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- for (size_t i = 0, L = length(); i < L; ++i) {- *cpy << (*this)[i]->cloneFully(ctx);- }- return cpy;- }-- /* not used anymore - remove?- Selector_Placeholder* Selector::find_placeholder()- {- return 0;- }*/-- // remove parent selector references- // basically unwraps parsed selectors- void Selector_List::remove_parent_selectors()- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = length(); i < L; ++i) {- if (!(*this)[i]->head()) continue;- if ((*this)[i]->head()->is_empty_reference()) {- // simply move to the next tail if we have "no" combinator- if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) {- if ((*this)[i]->tail() && (*this)[i]->has_line_feed()) {- (*this)[i]->tail()->has_line_feed(true);- }- (*this)[i] = (*this)[i]->tail();- }- // otherwise remove the first item from head- else {- (*this)[i]->head()->erase((*this)[i]->head()->begin());- }- }- }- }-- void Selector_List::adjust_after_pushing(Complex_Selector* c)- {- if (c->has_reference()) has_reference(true);- }-- // it's a superselector if every selector of the right side- // list is a superselector of the given left side selector- bool Complex_Selector::is_superselector_of(Selector_List *sub, std::string wrapping)- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = sub->length(); i < L; ++i) {- if (!is_superselector_of((*sub)[i], wrapping)) return false;- }- return true;- }-- // it's a superselector if every selector of the right side- // list is a superselector of the given left side selector- bool Selector_List::is_superselector_of(Selector_List *sub, std::string wrapping)- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = sub->length(); i < L; ++i) {- if (!is_superselector_of((*sub)[i], wrapping)) return false;- }- return true;- }-- // it's a superselector if every selector on the right side- // is a superselector of any one of the left side selectors- bool Selector_List::is_superselector_of(Compound_Selector *sub, std::string wrapping)- {- // Check every lhs selector against right hand- for(size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_superselector_of(sub, wrapping)) return true;- }- return false;- }-- // it's a superselector if every selector on the right side- // is a superselector of any one of the left side selectors- bool Selector_List::is_superselector_of(Complex_Selector *sub, std::string wrapping)- {- // Check every lhs selector against right hand- for(size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_superselector_of(sub)) return true;- }- return false;- }-- Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) {- std::vector<Complex_Selector*> unified_complex_selectors;- // Unify all of children with RHS's children, storing the results in `unified_complex_selectors`- for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) {- Complex_Selector* seq1 = (*this)[lhs_i];- for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {- Complex_Selector* seq2 = (*rhs)[rhs_i];-- Selector_List* result = seq1->unify_with(seq2, ctx);- if( result ) {- for(size_t i = 0, L = result->length(); i < L; ++i) {- unified_complex_selectors.push_back( (*result)[i] );- }- }- }- }-- // Creates the final Selector_List by combining all the complex selectors- Selector_List* final_result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());- for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {- *final_result << *itr;- }- return final_result;- }-- void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) {- To_String to_string;-- Selector_List* extender = this;- for (auto complex_sel : extendee->elements()) {- Complex_Selector* c = complex_sel;--- // Ignore any parent selectors, until we find the first non Selector_Reference head- Compound_Selector* compound_sel = c->head();- Complex_Selector* pIter = complex_sel;- while (pIter) {- Compound_Selector* pHead = pIter->head();- if (pHead && dynamic_cast<Parent_Selector*>(pHead->elements()[0]) == NULL) {- compound_sel = pHead;- break;- }-- pIter = pIter->tail();- }-- if (!pIter->head() || pIter->tail()) {- error("nested selectors may not be extended", c->pstate());- }-- compound_sel->is_optional(extendee->is_optional());-- for (size_t i = 0, L = extender->length(); i < L; ++i) {- extends.put(compound_sel->to_str_vec(), std::make_pair((*extender)[i], compound_sel));- }- }- };-- std::vector<std::string> Compound_Selector::to_str_vec()- {- To_String to_string;- std::vector<std::string> result;- result.reserve(length());- for (size_t i = 0, L = length(); i < L; ++i)- { result.push_back((*this)[i]->perform(&to_string)); }- return result;- }-- Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx)- {- To_String to_string(&ctx);- Compound_Selector* result = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate());- // result->has_parent_reference(has_parent_reference());-- // not very efficient because it needs to preserve order- for (size_t i = 0, L = length(); i < L; ++i)- {- bool found = false;- std::string thisSelector((*this)[i]->perform(&to_string));- for (size_t j = 0, M = rhs->length(); j < M; ++j)- {- if (thisSelector == (*rhs)[j]->perform(&to_string))- {- found = true;- break;- }- }- if (!found) (*result) << (*this)[i];- }-- return result;- }-- void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx)- {- for (SourcesSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) {- this->sources_.insert((*iterator)->clone(ctx));- }- }-- void Arguments::adjust_after_pushing(Argument* a)- {- if (!a->name().empty()) {- if (/* has_rest_argument_ || */ has_keyword_argument_) {- error("named arguments must precede variable-length argument", a->pstate());- }- has_named_arguments_ = true;- }- else if (a->is_rest_argument()) {- if (has_rest_argument_) {- error("functions and mixins may only be called with one variable-length argument", a->pstate());- }- if (has_keyword_argument_) {- error("only keyword arguments may follow variable arguments", a->pstate());- }- has_rest_argument_ = true;- }- else if (a->is_keyword_argument()) {- if (has_keyword_argument_) {- error("functions and mixins may only be called with one keyword argument", a->pstate());- }- has_keyword_argument_ = true;- }- else {- if (has_rest_argument_) {- error("ordinal arguments must precede variable-length arguments", a->pstate());- }- if (has_named_arguments_) {- error("ordinal arguments must precede named arguments", a->pstate());- }- }- }-- bool Ruleset::is_invisible() const {- Selector_List* sl = static_cast<Selector_List*>(selector());- for (size_t i = 0, L = sl->length(); i < L; ++i)- if (!(*sl)[i]->has_placeholder()) return false;- return true;- }-- bool Media_Block::is_invisible() const {- for (size_t i = 0, L = block()->length(); i < L; ++i) {- if (!(*block())[i]->is_invisible()) return false;- }- return true;- }-- Number::Number(ParserState pstate, double val, std::string u, bool zero)- : Value(pstate),- value_(val),- zero_(zero),- numerator_units_(std::vector<std::string>()),- denominator_units_(std::vector<std::string>()),- hash_(0)- {- size_t l = 0, r = 0;- if (!u.empty()) {- bool nominator = true;- while (true) {- r = u.find_first_of("*/", l);- std::string unit(u.substr(l, r == std::string::npos ? r : r - l));- if (!unit.empty()) {- if (nominator) numerator_units_.push_back(unit);- else denominator_units_.push_back(unit);- }- if (r == std::string::npos) break;- // ToDo: should error for multiple slashes- // if (!nominator && u[r] == '/') error(...)- if (u[r] == '/')- nominator = false;- l = r + 1;- }- }- concrete_type(NUMBER);- }-- std::string Number::unit() const- {- std::string u;- for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {- if (i) u += '*';- u += numerator_units_[i];- }- if (!denominator_units_.empty()) u += '/';- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {- if (i) u += '*';- u += denominator_units_[i];- }- return u;- }-- bool Number::is_unitless()- { return numerator_units_.empty() && denominator_units_.empty(); }-- void Number::normalize(const std::string& prefered, bool strict)- {-- // first make sure same units cancel each other out- // it seems that a map table will fit nicely to do this- // we basically construct exponents for each unit- // has the advantage that they will be pre-sorted- std::map<std::string, int> exponents;-- // initialize by summing up occurences in unit vectors- for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];-- // the final conversion factor- double factor = 1;-- // get the first entry of numerators- // forward it when entry is converted- std::vector<std::string>::iterator nom_it = numerator_units_.begin();- std::vector<std::string>::iterator nom_end = numerator_units_.end();- std::vector<std::string>::iterator denom_it = denominator_units_.begin();- std::vector<std::string>::iterator denom_end = denominator_units_.end();-- // main normalization loop- // should be close to optimal- while (denom_it != denom_end)- {- // get and increment afterwards- const std::string denom = *(denom_it ++);- // skip already canceled out unit- if (exponents[denom] >= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(denom) == UNKNOWN) continue;- // now search for nominator- while (nom_it != nom_end)- {- // get and increment afterwards- const std::string nom = *(nom_it ++);- // skip already canceled out unit- if (exponents[nom] <= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(nom) == UNKNOWN) continue;- // we now have two convertable units- // add factor for current conversion- factor *= conversion_factor(nom, denom, strict);- // update nominator/denominator exponent- -- exponents[nom]; ++ exponents[denom];- // inner loop done- break;- }- }-- // now we can build up the new unit arrays- numerator_units_.clear();- denominator_units_.clear();-- // build them by iterating over the exponents- for (auto exp : exponents)- {- // maybe there is more effecient way to push- // the same item multiple times to a vector?- for(size_t i = 0, S = abs(exp.second); i < S; ++i)- {- // opted to have these switches in the inner loop- // makes it more readable and should not cost much- if (!exp.first.empty()) {- if (exp.second < 0) denominator_units_.push_back(exp.first);- else if (exp.second > 0) numerator_units_.push_back(exp.first);- }- }- }-- // apply factor to value_- // best precision this way- value_ *= factor;-- // maybe convert to other unit- // easier implemented on its own- try { convert(prefered, strict); }- catch (incompatibleUnits& err)- { error(err.what(), pstate()); }- catch (...) { throw; }-- }-- void Number::convert(const std::string& prefered, bool strict)- {- // abort if unit is empty- if (prefered.empty()) return;-- // first make sure same units cancel each other out- // it seems that a map table will fit nicely to do this- // we basically construct exponents for each unit- // has the advantage that they will be pre-sorted- std::map<std::string, int> exponents;-- // initialize by summing up occurences in unit vectors- for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];-- // the final conversion factor- double factor = 1;-- std::vector<std::string>::iterator denom_it = denominator_units_.begin();- std::vector<std::string>::iterator denom_end = denominator_units_.end();-- // main normalization loop- // should be close to optimal- while (denom_it != denom_end)- {- // get and increment afterwards- const std::string denom = *(denom_it ++);- // check if conversion is needed- if (denom == prefered) continue;- // skip already canceled out unit- if (exponents[denom] >= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(denom) == UNKNOWN) continue;- // we now have two convertable units- // add factor for current conversion- factor *= conversion_factor(denom, prefered, strict);- // update nominator/denominator exponent- ++ exponents[denom]; -- exponents[prefered];- }-- std::vector<std::string>::iterator nom_it = numerator_units_.begin();- std::vector<std::string>::iterator nom_end = numerator_units_.end();-- // now search for nominator- while (nom_it != nom_end)- {- // get and increment afterwards- const std::string nom = *(nom_it ++);- // check if conversion is needed- if (nom == prefered) continue;- // skip already canceled out unit- if (exponents[nom] <= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(nom) == UNKNOWN) continue;- // we now have two convertable units- // add factor for current conversion- factor *= conversion_factor(nom, prefered, strict);- // update nominator/denominator exponent- -- exponents[nom]; ++ exponents[prefered];- }-- // now we can build up the new unit arrays- numerator_units_.clear();- denominator_units_.clear();-- // build them by iterating over the exponents- for (auto exp : exponents)- {- // maybe there is more effecient way to push- // the same item multiple times to a vector?- for(size_t i = 0, S = abs(exp.second); i < S; ++i)- {- // opted to have these switches in the inner loop- // makes it more readable and should not cost much- if (!exp.first.empty()) {- if (exp.second < 0) denominator_units_.push_back(exp.first);- else if (exp.second > 0) numerator_units_.push_back(exp.first);- }- }- }-- // apply factor to value_- // best precision this way- value_ *= factor;-- }-- // useful for making one number compatible with another- std::string Number::find_convertible_unit() const- {- for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {- std::string u(numerator_units_[i]);- if (string_to_unit(u) != UNKNOWN) return u;- }- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {- std::string u(denominator_units_[i]);- if (string_to_unit(u) != UNKNOWN) return u;- }- return std::string();- }-- bool Custom_Warning::operator== (const Expression& rhs) const- {- if (const Custom_Warning* r = dynamic_cast<const Custom_Warning*>(&rhs)) {- return message() == r->message();- }- return false;- }-- bool Custom_Error::operator== (const Expression& rhs) const- {- if (const Custom_Error* r = dynamic_cast<const Custom_Error*>(&rhs)) {- return message() == r->message();- }- return false;- }-- bool Number::operator== (const Expression& rhs) const- {- if (const Number* r = dynamic_cast<const Number*>(&rhs)) {- return (numerator_units_ == r->numerator_units_) &&- (denominator_units_ == r->denominator_units_) &&- std::fabs(value() - r->value()) < NUMBER_EPSILON;- }- return false;- }-- bool Number::operator< (const Number& rhs) const- {- Number tmp_r(rhs);- tmp_r.normalize(find_convertible_unit());- std::string l_unit(unit());- std::string r_unit(tmp_r.unit());- if (!l_unit.empty() && !r_unit.empty() && unit() != tmp_r.unit()) {- error("cannot compare numbers with incompatible units", pstate());- }- return value() < tmp_r.value();- }-- bool String_Quoted::operator== (const Expression& rhs) const- {- if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(&rhs)) {- return (value() == qstr->value());- } else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(&rhs)) {- return (value() == cstr->value());- }- return false;- }-- bool String_Constant::operator== (const Expression& rhs) const- {- if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(&rhs)) {- return (value() == qstr->value());- } else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(&rhs)) {- return (value() == cstr->value());- }- return false;- }-- bool String_Schema::operator== (const Expression& rhs) const- {- if (const String_Schema* r = dynamic_cast<const String_Schema*>(&rhs)) {- if (length() != r->length()) return false;- for (size_t i = 0, L = length(); i < L; ++i) {- Expression* rv = (*r)[i];- Expression* lv = (*this)[i];- if (!lv || !rv) return false;- if (!(*lv == *rv)) return false;- }- return true;- }- return false;- }-- bool Boolean::operator== (const Expression& rhs) const- {- if (const Boolean* r = dynamic_cast<const Boolean*>(&rhs)) {- return (value() == r->value());- }- return false;- }-- bool Color::operator== (const Expression& rhs) const- {- if (const Color* r = dynamic_cast<const Color*>(&rhs)) {- return r_ == r->r() &&- g_ == r->g() &&- b_ == r->b() &&- a_ == r->a();- }- return false;- }-- bool List::operator== (const Expression& rhs) const- {- if (const List* r = dynamic_cast<const List*>(&rhs)) {- if (length() != r->length()) return false;- if (separator() != r->separator()) return false;- for (size_t i = 0, L = length(); i < L; ++i) {- Expression* rv = (*r)[i];- Expression* lv = (*this)[i];- if (!lv || !rv) return false;- if (!(*lv == *rv)) return false;- }- return true;- }- return false;- }-- bool Map::operator== (const Expression& rhs) const- {- if (const Map* r = dynamic_cast<const Map*>(&rhs)) {- if (length() != r->length()) return false;- for (auto key : keys()) {- Expression* lv = at(key);- Expression* rv = r->at(key);- if (!rv || !lv) return false;- if (!(*lv == *rv)) return false;- }- return true;- }- return false;- }-- bool Null::operator== (const Expression& rhs) const- {- return rhs.concrete_type() == NULL_VAL;- }-- size_t List::size() const {- if (!is_arglist_) return length();- // arglist expects a list of arguments- // so we need to break before keywords- for (size_t i = 0, L = length(); i < L; ++i) {- if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {- if (!arg->name().empty()) return i;- }- }- return length();- }-- Expression* Hashed::at(Expression* k) const- {- if (elements_.count(k))- { return elements_.at(k); }- else { return &sass_null; }- }-- std::string Map::to_string(bool compressed, int precision) const- {- std::string res("");- if (empty()) return res;- if (is_invisible()) return res;- bool items_output = false;- for (auto key : keys()) {- if (key->is_invisible()) continue;- if (at(key)->is_invisible()) continue;- if (items_output) res += compressed ? "," : ", ";- Value* v_key = dynamic_cast<Value*>(key);- Value* v_val = dynamic_cast<Value*>(at(key));- if (v_key) res += v_key->to_string(compressed, precision);- res += compressed ? ":" : ": ";- if (v_val) res += v_val->to_string(compressed, precision);- items_output = true;- }- return res;- }-- std::string List::to_string(bool compressed, int precision) const- {- std::string res("");- if (empty()) return res;- if (is_invisible()) return res;- bool items_output = false;- std::string sep = separator() == SASS_COMMA ? "," : " ";- if (!compressed && sep == ",") sep += " ";- for (size_t i = 0, L = size(); i < L; ++i) {- Expression* item = (*this)[i];- if (item->is_invisible()) continue;- if (items_output) res += sep;- if (Value* v_val = dynamic_cast<Value*>(item))- { res += v_val->to_string(compressed, precision); }- items_output = true;- }- return res;- }-- std::string String_Schema::to_string(bool compressed, int precision) const- {- std::string res("");- for (size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_interpolant()) res += "#{";- if (Value* val = dynamic_cast<Value*>((*this)[i]))- { res += val->to_string(compressed, precision); }- if ((*this)[i]->is_interpolant()) res += "}";- }- return res;- }-- std::string Null::to_string(bool compressed, int precision) const- {- return "null";- }-- std::string Boolean::to_string(bool compressed, int precision) const- {- return value_ ? "true" : "false";- }-- // helper function for serializing colors- template <size_t range>- static double cap_channel(double c) {- if (c > range) return range;- else if (c < 0) return 0;- else return c;- }-- std::string Color::to_string(bool compressed, int precision) const- {- std::stringstream ss;-- // original color name- // maybe an unknown token- std::string name = disp();-- // resolved color- std::string res_name = name;-- double r = Sass::round(cap_channel<0xff>(r_));- double g = Sass::round(cap_channel<0xff>(g_));- double b = Sass::round(cap_channel<0xff>(b_));- double a = cap_channel<1> (a_);-- // get color from given name (if one was given at all)- if (name != "" && name_to_color(name)) {- const Color* n = name_to_color(name);- r = Sass::round(cap_channel<0xff>(n->r()));- g = Sass::round(cap_channel<0xff>(n->g()));- b = Sass::round(cap_channel<0xff>(n->b()));- a = cap_channel<1> (n->a());- }- // otherwise get the possible resolved color name- else {- double numval = r * 0x10000 + g * 0x100 + b;- if (color_to_name(numval))- res_name = color_to_name(numval);- }-- std::stringstream hexlet;- hexlet << '#' << std::setw(1) << std::setfill('0');- // create a short color hexlet if there is any need for it- if (compressed && is_color_doublet(r, g, b) && a == 1) {- hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);- hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);- hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);- } else {- hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);- hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);- hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);- }-- if (compressed && !this->is_delayed()) name = "";-- // retain the originally specified color definition if unchanged- if (name != "") {- ss << name;- }- else if (r == 0 && g == 0 && b == 0 && a == 0) {- ss << "transparent";- }- else if (a >= 1) {- if (res_name != "") {- if (compressed && hexlet.str().size() < res_name.size()) {- ss << hexlet.str();- } else {- ss << res_name;- }- }- else {- ss << hexlet.str();- }- }- else {- ss << "rgba(";- ss << static_cast<unsigned long>(r) << ",";- if (!compressed) ss << " ";- ss << static_cast<unsigned long>(g) << ",";- if (!compressed) ss << " ";- ss << static_cast<unsigned long>(b) << ",";- if (!compressed) ss << " ";- ss << a << ')';- }-- return ss.str();-- }-- std::string Number::to_string(bool compressed, int precision) const- {-- std::string res;-- // check if the fractional part of the value equals to zero- // neat trick from http://stackoverflow.com/a/1521682/1550314- // double int_part; bool is_int = modf(value, &int_part) == 0.0;-- // this all cannot be done with one run only, since fixed- // output differs from normal output and regular output- // can contain scientific notation which we do not want!-- // first sample- std::stringstream ss;- ss.precision(12);- ss << value_;-- // check if we got scientific notation in result- if (ss.str().find_first_of("e") != std::string::npos) {- ss.clear(); ss.str(std::string());- ss.precision(std::max(12, precision));- ss << std::fixed << value_;- }-- std::string tmp = ss.str();- size_t pos_point = tmp.find_first_of(".,");- size_t pos_fract = tmp.find_last_not_of("0");- bool is_int = pos_point == pos_fract ||- pos_point == std::string::npos;-- // reset stream for another run- ss.clear(); ss.str(std::string());-- // take a shortcut for integers- if (is_int)- {- ss.precision(0);- ss << std::fixed << value_;- res = std::string(ss.str());- }- // process floats- else- {- // do we have have too much precision?- if (pos_fract < precision + pos_point)- { precision = (int)(pos_fract - pos_point); }- // round value again- ss.precision(precision);- ss << std::fixed << value_;- res = std::string(ss.str());- // maybe we truncated up to decimal point- size_t pos = res.find_last_not_of("0");- bool at_dec_point = res[pos] == '.' ||- res[pos] == ',';- // don't leave a blank point- if (at_dec_point) ++ pos;- res.resize (pos + 1);- }-- // some final cosmetics- if (res == "-0.0") res.erase(0, 1);- else if (res == "-0") res.erase(0, 1);- else if (res == "") res = "0";-- // add unit now- res += unit();-- // and return- return res;-- }-- std::string String_Quoted::to_string(bool compressed, int precision) const- {- return quote_mark_ ? quote(value_, quote_mark_, true) : value_;- }-- std::string String_Constant::to_string(bool compressed, int precision) const- {- return quote_mark_ ? quote(value_, quote_mark_, true) : value_;- }-- std::string Custom_Error::to_string(bool compressed, int precision) const- {- return message();- }- std::string Custom_Warning::to_string(bool compressed, int precision) const- {- return message();- }-- std::string Selector_List::to_string(bool compressed, int precision) const- {- std::string str("");- auto end = this->end();- auto start = this->begin();- while (start < end && *start) {- Complex_Selector* sel = *start;- if (!str.empty()) str += ", ";- str += sel->to_string(compressed, precision);- ++ start;- }- return str;- }-- std::string Compound_Selector::to_string(bool compressed, int precision) const- {- std::string str("");- auto end = this->end();- auto start = this->begin();- while (start < end && *start) {- Simple_Selector* sel = *start;- str += sel->to_string(compressed, precision);- ++ start;- }- return str;- }-- std::string Complex_Selector::to_string(bool compressed, int precision) const- {- // first render head and tail if they are available- std::string str_head(head() ? head()->to_string(compressed, precision) : "");- std::string str_tail(tail() ? tail()->to_string(compressed, precision) : "");- std::string str_ref(reference() ? reference()->to_string(compressed, precision) : "");- // combinator in between- std::string str_op("");- // use a switch statement- switch (combinator()) {- case ANCESTOR_OF: str_op = " "; break;- case PARENT_OF: str_op = ">"; break;- case PRECEDES: str_op = "~"; break;- case ADJACENT_TO: str_op = "+"; break;- case REFERENCE: str_op = "/" + str_ref + "/"; break;- }- // prettify for non ancestors- if (combinator() != ANCESTOR_OF) {- // no spaces needed for compressed- if (compressed == false) {- // make sure we add some spaces where needed- if (str_tail != "") str_op += " ";- if (str_head != "") str_head += " ";- }- }- // is ancestor with no tail- else if (str_tail == "") {- str_op = ""; // superflous- }- // now build the final result- return str_head + str_op + str_tail;- }-- std::string Selector_Schema::to_string(bool compressed, int precision) const- {- return contents()->to_string(compressed, precision);- }-- std::string Parent_Selector::to_string(bool compressed, int precision) const- {- return "&";- }-- std::string Attribute_Selector::to_string(bool compressed, int precision) const- {- std::string val(value() ? value()->to_string(compressed, precision) : "");- return "[" + this->ns_name() + this->matcher() + val + "]";- }-- std::string Wrapped_Selector::to_string(bool compressed, int precision) const- {- // first render the- std::string main(this->Simple_Selector::to_string(compressed, precision));- std::string wrapped(selector() ? selector()->to_string(compressed, precision) : "");- // now build the final result- return main + "(" + wrapped + ")";- }-- //////////////////////////////////////////////////////////////////////////////////////////- // Additional method on Lists to retrieve values directly or from an encompassed Argument.- //////////////////////////////////////////////////////////////////////////////////////////- Expression* List::value_at_index(size_t i) {- if (is_arglist_) {- if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {- return arg->value();- } else {- return (*this)[i];- }- } else {- return (*this)[i];- }- }--}-+#include "sass.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "node.hpp"+#include "extend.hpp"+#include "emitter.hpp"+#include "color_maps.hpp"+#include "ast_fwd_decl.hpp"+#include <set>+#include <iomanip>+#include <iostream>+#include <algorithm>+#include <functional>+#include <cctype>+#include <locale>++namespace Sass {++ static Null sass_null(ParserState("null"));++ bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const {+ if (Supports_Operator_Obj op = SASS_MEMORY_CAST(Supports_Operator, cond)) {+ return op->operand() != operand();+ }+ return SASS_MEMORY_CAST(Supports_Negation, cond) != NULL;+ }++ bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const {+ return SASS_MEMORY_CAST(Supports_Negation, cond) ||+ SASS_MEMORY_CAST(Supports_Operator, cond);+ }++ size_t HashExpression::operator() (Expression_Obj ex) const {+ return ex ? ex->hash() : 0;+ }++ size_t HashSimpleSelector::operator() (Simple_Selector_Obj ex) const {+ return ex ? ex->hash() : 0;+ }+++ bool CompareExpression::operator()(const Expression_Obj& lhs, const Expression_Obj& rhs) const {+ return lhs && rhs && lhs->eq(*rhs);+ }++ bool CompareSimpleSelector::operator()(Simple_Selector_Obj lhs, Simple_Selector_Obj rhs) const {+ return &lhs && &rhs && *lhs == *rhs;+ }++ std::string & str_ltrim(std::string & str)+ {+ auto it2 = std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );+ str.erase( str.begin() , it2);+ return str;+ }++ std::string & str_rtrim(std::string & str)+ {+ auto it1 = std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );+ str.erase( it1.base() , str.end() );+ return str;+ }++ void String_Constant::rtrim()+ {+ value_ = str_rtrim(value_);+ }+ void String_Constant::ltrim()+ {+ value_ = str_ltrim(value_);+ }+ void String_Constant::trim()+ {+ rtrim();+ ltrim();+ }++ void String_Schema::rtrim()+ {+ if (!empty()) {+ if (String_Ptr str = SASS_MEMORY_CAST(String, last())) str->rtrim();+ }+ }+ void String_Schema::ltrim()+ {+ if (!empty()) {+ if (String_Ptr str = SASS_MEMORY_CAST(String, first())) str->ltrim();+ }+ }+ void String_Schema::trim()+ {+ rtrim();+ ltrim();+ }++ void Argument::set_delayed(bool delayed)+ {+ if (value_) value_->set_delayed(delayed);+ is_delayed(delayed);+ }++ void Arguments::set_delayed(bool delayed)+ {+ for (Argument_Obj arg : elements()) {+ if (arg) arg->set_delayed(delayed);+ }+ is_delayed(delayed);+ }+++ bool At_Root_Query::exclude(std::string str)+ {+ bool with = feature() && unquote(feature()->to_string()).compare("with") == 0;+ List_Ptr l = static_cast<List_Ptr>(&value());+ std::string v;++ if (with)+ {+ if (!l || l->length() == 0) return str.compare("rule") != 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->to_string());+ if (v.compare("all") == 0 || v == str) return false;+ }+ return true;+ }+ else+ {+ if (!l || !l->length()) return str.compare("rule") == 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->to_string());+ if (v.compare("all") == 0 || v == str) return true;+ }+ return false;+ }+ }++ void AST_Node::update_pstate(const ParserState& pstate)+ {+ pstate_.offset += pstate - pstate_ + pstate.offset;+ }++ void AST_Node::set_pstate_offset(const Offset& offset)+ {+ pstate_.offset = offset;+ }++ inline bool is_ns_eq(const std::string& l, const std::string& r)+ {+ if (l.empty() && r.empty()) return true;+ else if (l.empty() && r == "*") return true;+ else if (r.empty() && l == "*") return true;+ else return l == r;+ }++++ bool Compound_Selector::operator< (const Compound_Selector& rhs) const+ {+ size_t L = std::min(length(), rhs.length());+ for (size_t i = 0; i < L; ++i)+ {+ Simple_Selector_Obj l = (*this)[i];+ Simple_Selector_Obj r = rhs[i];+ if (!l && !r) return false;+ else if (!r) return false;+ else if (!l) return true;+ else if (*l != *r)+ { return *l < *r; }+ }+ // just compare the length now+ return length() < rhs.length();+ }++ bool Compound_Selector::has_parent_ref()+ {+ for (Simple_Selector_Obj s : *this) {+ if (s && s->has_parent_ref()) return true;+ }+ return false;+ }++ bool Compound_Selector::has_real_parent_ref()+ {+ for (Simple_Selector_Obj s : *this) {+ if (s && s->has_real_parent_ref()) return true;+ }+ return false;+ }++ bool Complex_Selector::has_parent_ref()+ {+ return (head() && head()->has_parent_ref()) ||+ (tail() && tail()->has_parent_ref());+ }++ bool Complex_Selector::has_real_parent_ref()+ {+ return (head() && head()->has_real_parent_ref()) ||+ (tail() && tail()->has_real_parent_ref());+ }++ bool Complex_Selector::operator< (const Complex_Selector& rhs) const+ {+ // const iterators for tails+ Complex_Selector_Ptr_Const l = this;+ Complex_Selector_Ptr_Const r = &rhs;+ Compound_Selector_Ptr l_h = l ? &l->head() : 0;+ Compound_Selector_Ptr r_h = r ? &r->head() : 0;+ // process all tails+ while (true)+ {+ // skip empty ancestor first+ if (l && l->is_empty_ancestor())+ {+ l = &l->tail();+ l_h = l ? &l->head() : 0;+ continue;+ }+ // skip empty ancestor first+ if (r && r->is_empty_ancestor())+ {+ r = &r->tail();+ r_h = r ? &r->head() : 0;+ continue;+ }+ // check for valid selectors+ if (!l) return !!r;+ if (!r) return false;+ // both are null+ else if (!l_h && !r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = &l->tail();+ r = &r->tail();+ // fetch the next headers+ l_h = l ? &l->head() : 0;+ r_h = r ? &r->head() : 0;+ }+ // one side is null+ else if (!r_h) return true;+ else if (!l_h) return false;+ // heads ok and equal+ else if (*l_h == *r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = &l->tail();+ r = &r->tail();+ // fetch the next headers+ l_h = l ? &l->head() : 0;+ r_h = r ? &r->head() : 0;+ }+ // heads are not equal+ else return *l_h < *r_h;+ }+ return true;+ }++ bool Complex_Selector::operator== (const Complex_Selector& rhs) const+ {+ // const iterators for tails+ Complex_Selector_Ptr_Const l = this;+ Complex_Selector_Ptr_Const r = &rhs;+ Compound_Selector_Ptr l_h = l ? &l->head() : 0;+ Compound_Selector_Ptr r_h = r ? &r->head() : 0;+ // process all tails+ while (true)+ {+ // skip empty ancestor first+ if (l && l->is_empty_ancestor())+ {+ l = &l->tail();+ l_h = l ? &l->head() : 0;+ continue;+ }+ // skip empty ancestor first+ if (r && r->is_empty_ancestor())+ {+ r = &r->tail();+ r_h = r ? &r->head() : 0;+ continue;+ }+ // check the pointers+ if (!r) return !l;+ if (!l) return !r;+ // both are null+ if (!l_h && !r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = &l->tail();+ r = &r->tail();+ // fetch the next heads+ l_h = l ? &l->head() : 0;+ r_h = r ? &r->head() : 0;+ }+ // equals if other head is empty+ else if ((!l_h && !r_h) ||+ (!l_h && r_h->empty()) ||+ (!r_h && l_h->empty()) ||+ (*l_h == *r_h))+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() == r->combinator(); }+ // advance to next tails+ l = &l->tail();+ r = &r->tail();+ // fetch the next heads+ l_h = l ? &l->head() : 0;+ r_h = r ? &r->head() : 0;+ }+ // abort+ else break;+ }+ // unreachable+ return false;+ }++ Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+ {+ if (empty()) return rhs;+ Compound_Selector_Obj unified = SASS_MEMORY_COPY(rhs);+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if (unified.isNull()) break;+ unified = at(i)->unify_with(&unified, ctx);+ }+ return unified.detach();+ }++ bool Selector::operator== (const Selector& rhs) const+ {+ if (Selector_List_Ptr_Const sl = dynamic_cast<Selector_List_Ptr_Const>(this)) return *sl == rhs;+ if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(this)) return *sp == rhs;+ throw std::runtime_error("invalid selector base classes to compare");+ return false;+ }++ bool Selector::operator< (const Selector& rhs) const+ {+ if (Selector_List_Ptr_Const sl = dynamic_cast<Selector_List_Ptr_Const>(this)) return *sl < rhs;+ if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(this)) return *sp < rhs;+ throw std::runtime_error("invalid selector base classes to compare");+ return false;+ }++ bool Simple_Selector::operator== (const Selector& rhs) const+ {+ if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(&rhs)) return *this == *sp;+ return false;+ }++ bool Simple_Selector::operator< (const Selector& rhs) const+ {+ if (Simple_Selector_Ptr_Const sp = dynamic_cast<Simple_Selector_Ptr_Const>(&rhs)) return *this < *sp;+ return false;+ }++ bool Simple_Selector::operator== (const Simple_Selector& rhs) const+ {+ Simple_Type type = simple_type();+ // dynamic cast is a bottleneck - use concrete type as types are final+ if (type == PSEUDO_SEL /* Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this) */) {+ return *static_cast<Pseudo_Selector_Ptr_Const>(this) == rhs;+ }+ else if (type == WRAPPED_SEL /* Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this) */) {+ return *static_cast<Wrapped_Selector_Ptr_Const>(this) == rhs;+ }+ else if (type == ATTR_SEL /* Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this) */) {+ return *static_cast<Attribute_Selector_Ptr_Const>(this) == rhs;+ }+ else if (name_ == rhs.name_)+ { return is_ns_eq(ns_, rhs.ns_); }+ else return false;+ }++ bool Simple_Selector::operator< (const Simple_Selector& rhs) const+ {+ Simple_Type type = simple_type();+ // dynamic cast is a bottleneck - use concrete type as types are final+ if (type == PSEUDO_SEL /* Pseudo_Selector_Ptr_Const lp = dynamic_cast<Pseudo_Selector_Ptr_Const>(this) */) {+ return *static_cast<Pseudo_Selector_Ptr_Const>(this) < rhs;+ }+ else if (type == WRAPPED_SEL /* Wrapped_Selector_Ptr_Const lw = dynamic_cast<Wrapped_Selector_Ptr_Const>(this) */) {+ return *static_cast<Wrapped_Selector_Ptr_Const>(this) < rhs;+ }+ else if (type == ATTR_SEL /* Attribute_Selector_Ptr_Const la = dynamic_cast<Attribute_Selector_Ptr_Const>(this) */) {+ return *static_cast<Attribute_Selector_Ptr_Const>(this) < rhs;+ }+ if (is_ns_eq(ns_, rhs.ns_))+ { return name_ < rhs.name_; }+ return ns_ < rhs.ns_;+ }++ bool Selector_List::operator== (const Selector& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (Selector_List_Ptr_Const ls = dynamic_cast<Selector_List_Ptr_Const>(&rhs)) { return *this == *ls; }+ else if (Complex_Selector_Ptr_Const ls = dynamic_cast<Complex_Selector_Ptr_Const>(&rhs)) { return *this == *ls; }+ else if (Compound_Selector_Ptr_Const ls = dynamic_cast<Compound_Selector_Ptr_Const>(&rhs)) { return *this == *ls; }+ // no compare method+ return this == &rhs;+ }++ // Selector lists can be compared to comma lists+ bool Selector_List::operator==(const Expression& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (List_Ptr_Const ls = dynamic_cast<List_Ptr_Const>(&rhs)) { return *this == *ls; }+ if (Selector_Ptr_Const ls = dynamic_cast<Selector_Ptr_Const>(&rhs)) { return *this == *ls; }+ // compare invalid (maybe we should error?)+ return false;+ }++ bool Selector_List::operator== (const Selector_List& rhs) const+ {+ // for array access+ size_t i = 0, n = 0;+ size_t iL = length();+ size_t nL = rhs.length();+ // create temporary vectors and sort them+ std::vector<Complex_Selector_Obj> l_lst = this->elements();+ std::vector<Complex_Selector_Obj> r_lst = rhs.elements();+ std::sort(l_lst.begin(), l_lst.end(), cmp_complex_selector());+ std::sort(r_lst.begin(), r_lst.end(), cmp_complex_selector());+ // process loop+ while (true)+ {+ // first check for valid index+ if (i == iL) return iL == nL;+ else if (n == nL) return iL == nL;+ // the access the vector items+ Complex_Selector_Obj l = l_lst[i];+ Complex_Selector_Obj r = r_lst[n];+ // skip nulls+ if (!l) ++i;+ else if (!r) ++n;+ // do the check+ else if (*l != *r)+ { return false; }+ // advance+ ++i; ++n;+ }+ // no mismatch+ return true;+ }++ bool Selector_List::operator< (const Selector& rhs) const+ {+ if (Selector_List_Ptr_Const sp = dynamic_cast<Selector_List_Ptr_Const>(&rhs)) return *this < *sp;+ return false;+ }++ bool Selector_List::operator< (const Selector_List& rhs) const+ {+ if (this->length() != rhs.length()) return false;+ for (size_t i = 0; i < rhs.length(); i ++) {+ if (!(*at(i) < *rhs.at(i))) return false;+ }+ return true;+ }++ Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ { if (to_string(ctx.c_options) == rhs->at(i)->to_string(ctx.c_options)) return rhs; }++ // check for pseudo elements because they are always last+ size_t i, L;+ bool found = false;+ if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector))+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((SASS_MEMORY_CAST(Pseudo_Selector, (*rhs)[i]) || SASS_MEMORY_CAST(Wrapped_Selector, (*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element())+ { found = true; break; }+ }+ }+ else+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if (SASS_MEMORY_CAST(Pseudo_Selector, (*rhs)[i]) || SASS_MEMORY_CAST(Wrapped_Selector, (*rhs)[i]))+ { found = true; break; }+ }+ }+ if (!found)+ {+ rhs->append(this);+ return rhs;+ }+ rhs->elements().insert(rhs->elements().begin() + i, this);+ return rhs;+ }++ Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs, Context& ctx)+ {+ // check if ns can be extended+ // true for no ns or universal+ if (has_universal_ns())+ {+ // but dont extend with universal+ // true for valid ns and universal+ if (!rhs->is_universal_ns())+ {+ // overwrite the name if star is given as name+ if (this->name() == "*") { this->name(rhs->name()); }+ // now overwrite the namespace name and flag+ this->ns(rhs->ns()); this->has_ns(rhs->has_ns());+ // return copy+ return this;+ }+ }+ // namespace may changed, check the name now+ // overwrite star (but not with another star)+ if (name() == "*" && rhs->name() != "*")+ {+ // simply set the new name+ this->name(rhs->name());+ // return copy+ return this;+ }+ // return original+ return this;+ }++ Compound_Selector_Ptr Element_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+ {+ // TODO: handle namespaces++ // if the rhs is empty, just return a copy of this+ if (rhs->length() == 0) {+ rhs->append(this);+ return rhs;+ }++ Simple_Selector_Ptr rhs_0 = &rhs->at(0);+ // otherwise, this is a tag name+ if (name() == "*")+ {+ if (typeid(*rhs_0) == typeid(Element_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ Element_Selector_Ptr ts = SASS_MEMORY_CAST_PTR(Element_Selector, rhs_0);+ rhs->at(0) = this->unify_with(ts, ctx);+ return rhs;+ }+ else if (SASS_MEMORY_CAST_PTR(Class_Selector, rhs_0) || SASS_MEMORY_CAST_PTR(Id_Selector, rhs_0)) {+ // qualifier is `.class`, so we can prefix with `ns|*.class`+ if (has_ns() && !rhs_0->has_ns()) {+ if (ns() != "*") rhs->elements().insert(rhs->begin(), this);+ }+ return rhs;+ }+++ return rhs;+ }++ if (typeid(*rhs_0) == typeid(Element_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0;+ // otherwise create new compound and unify first simple selector+ rhs->at(0) = this->unify_with(rhs_0, ctx);+ return rhs;++ }+ // else it's a tag name and a bunch of qualifiers -- just append them+ if (name() != "*") rhs->elements().insert(rhs->begin(), this);+ return rhs;+ }++ Compound_Selector_Ptr Class_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+ {+ rhs->has_line_break(has_line_break());+ return Simple_Selector::unify_with(rhs, ctx);+ }++ Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if (Id_Selector_Ptr sel = SASS_MEMORY_CAST(Id_Selector, rhs->at(i))) {+ if (sel->name() != name()) return 0;+ }+ }+ rhs->has_line_break(has_line_break());+ return Simple_Selector::unify_with(rhs, ctx);+ }++ Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs, Context& ctx)+ {+ if (is_pseudo_element())+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if (Pseudo_Selector_Ptr sel = SASS_MEMORY_CAST(Pseudo_Selector, rhs->at(i))) {+ if (sel->is_pseudo_element() && sel->name() != name()) return 0;+ }+ }+ }+ return Simple_Selector::unify_with(rhs, ctx);+ }++ bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns())) {+ if (name() == rhs.name()) {+ if (matcher() == rhs.matcher()) {+ bool no_lhs_val = value().isNull();+ bool no_rhs_val = rhs.value().isNull();+ if (no_lhs_val && no_rhs_val) {+ return true;+ }+ if (!no_lhs_val && !no_rhs_val) {+ return *value() < *rhs.value();+ }+ } else { return matcher() < rhs.matcher(); }+ } else { return name() < rhs.name(); }+ }+ return false;+ }++ bool Attribute_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Attribute_Selector_Ptr_Const w = dynamic_cast<Attribute_Selector_Ptr_Const>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const+ {+ // get optional value state+ bool no_lhs_val = value().isNull();+ bool no_rhs_val = rhs.value().isNull();+ // both are null, therefore equal+ if (no_lhs_val && no_rhs_val) {+ return (name() == rhs.name())+ && (matcher() == rhs.matcher())+ && (is_ns_eq(ns(), rhs.ns()));+ }+ // both are defined, evaluate+ if (no_lhs_val == no_rhs_val) {+ return (name() == rhs.name())+ && (matcher() == rhs.matcher())+ && (is_ns_eq(ns(), rhs.ns()))+ && (*value() == *rhs.value());+ }+ // not equal+ return false;++ }++ bool Attribute_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Attribute_Selector_Ptr_Const w = dynamic_cast<Attribute_Selector_Ptr_Const>(&rhs))+ {+ return *this == *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() == rhs.name(); }+ return ns() == rhs.ns();+ }++ bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+ {+ String_Obj lhs_ex = expression();+ String_Obj rhs_ex = rhs.expression();+ if (rhs_ex && lhs_ex) return *lhs_ex == *rhs_ex;+ else return lhs_ex == rhs_ex;+ }+ else return false;+ }++ bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Pseudo_Selector_Ptr_Const w = dynamic_cast<Pseudo_Selector_Ptr_Const>(&rhs))+ {+ return *this == *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() == rhs.name(); }+ return ns() == rhs.ns();+ }++ bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+ {+ String_Obj lhs_ex = expression();+ String_Obj rhs_ex = rhs.expression();+ if (rhs_ex && lhs_ex) return *lhs_ex < *rhs_ex;+ else return lhs_ex < rhs_ex;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Pseudo_Selector_Ptr_Const w = dynamic_cast<Pseudo_Selector_Ptr_Const>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+ { return *(selector()) == *(rhs.selector()); }+ else return false;+ }++ bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (Wrapped_Selector_Ptr_Const w = dynamic_cast<Wrapped_Selector_Ptr_Const>(&rhs))+ {+ return *this == *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() == rhs.name(); }+ return ns() == rhs.ns();+ }++ bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+ { return *(selector()) < *(rhs.selector()); }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (Wrapped_Selector_Ptr_Const w = dynamic_cast<Wrapped_Selector_Ptr_Const>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Obj sub)+ {+ if (this->name() != sub->name()) return false;+ if (this->name() == ":current") return false;+ if (Selector_List_Obj rhs_list = SASS_MEMORY_CAST(Selector_List, sub->selector())) {+ if (Selector_List_Obj lhs_list = SASS_MEMORY_CAST(Selector_List, selector())) {+ return lhs_list->is_superselector_of(rhs_list);+ }+ error("is_superselector expected a Selector_List", sub->pstate());+ } else {+ error("is_superselector expected a Selector_List", sub->pstate());+ }+ return false;+ }++ bool Compound_Selector::is_superselector_of(Selector_List_Obj rhs, std::string wrapped)+ {+ for (Complex_Selector_Obj item : rhs->elements()) {+ if (is_superselector_of(&item, wrapped)) return true;+ }+ return false;+ }++ bool Compound_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapped)+ {+ if (rhs->head()) return is_superselector_of(&rhs->head(), wrapped);+ return false;+ }++ bool Compound_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)+ {+ Compound_Selector_Ptr lhs = this;+ Simple_Selector_Ptr lbase = lhs->base();+ Simple_Selector_Ptr rbase = rhs->base();++ // Check if pseudo-elements are the same between the selectors++ std::set<std::string> lpsuedoset, rpsuedoset;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if ((*this)[i]->is_pseudo_element()) {+ std::string pseudo((*this)[i]->to_string());+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ lpsuedoset.insert(pseudo);+ }+ }+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((*rhs)[i]->is_pseudo_element()) {+ std::string pseudo((*rhs)[i]->to_string());+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ rpsuedoset.insert(pseudo);+ }+ }+ if (lpsuedoset != rpsuedoset) {+ return false;+ }++ std::set<std::string> lset, rset;++ if (lbase && rbase)+ {+ if (lbase->to_string() == rbase->to_string()) {+ for (size_t i = 1, L = length(); i < L; ++i)+ { lset.insert((*this)[i]->to_string()); }+ for (size_t i = 1, L = rhs->length(); i < L; ++i)+ { rset.insert((*rhs)[i]->to_string()); }+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());+ }+ return false;+ }++ for (size_t i = 0, iL = length(); i < iL; ++i)+ {+ Selector_Obj lhs = &(*this)[i];+ // very special case for wrapped matches selector+ if (Wrapped_Selector_Obj wrapped = SASS_MEMORY_CAST(Wrapped_Selector, lhs)) {+ if (wrapped->name() == ":not") {+ if (Selector_List_Obj not_list = SASS_MEMORY_CAST(Selector_List, wrapped->selector())) {+ if (not_list->is_superselector_of(rhs, wrapped->name())) return false;+ } else {+ throw std::runtime_error("wrapped not selector is not a list");+ }+ }+ if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {+ lhs = wrapped->selector();+ if (Selector_List_Obj list = SASS_MEMORY_CAST(Selector_List, wrapped->selector())) {+ if (Compound_Selector_Obj comp = SASS_MEMORY_CAST(Compound_Selector, rhs)) {+ if (!wrapping.empty() && wrapping != wrapped->name()) return false;+ if (wrapping.empty() || wrapping != wrapped->name()) {;+ if (list->is_superselector_of(comp, wrapped->name())) return true;+ }+ }+ }+ }+ Simple_Selector_Ptr rhs_sel = rhs->elements().size() > i ? &(*rhs)[i] : 0;+ if (Wrapped_Selector_Ptr wrapped_r = dynamic_cast<Wrapped_Selector_Ptr>(rhs_sel)) {+ if (wrapped->name() == wrapped_r->name()) {+ if (wrapped->is_superselector_of(wrapped_r)) {+ continue;+ rset.insert(lhs->to_string());++ }}+ }+ }+ // match from here on as strings+ lset.insert(lhs->to_string());+ }++ for (size_t n = 0, nL = rhs->length(); n < nL; ++n)+ {+ Selector_Obj r = &(*rhs)[n];+ if (Wrapped_Selector_Obj wrapped = SASS_MEMORY_CAST(Wrapped_Selector, r)) {+ if (wrapped->name() == ":not") {+ if (Selector_List_Obj ls = SASS_MEMORY_CAST(Selector_List, wrapped->selector())) {+ ls->remove_parent_selectors();+ if (is_superselector_of(ls, wrapped->name())) return false;+ }+ }+ if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {+ if (!wrapping.empty()) {+ if (wrapping != wrapped->name()) return false;+ }+ if (Selector_List_Obj ls = SASS_MEMORY_CAST(Selector_List, wrapped->selector())) {+ ls->remove_parent_selectors();+ return (is_superselector_of(ls, wrapped->name()));+ }+ }+ }+ rset.insert(r->to_string());+ }++ //for (auto l : lset) { cerr << "l: " << l << endl; }+ //for (auto r : rset) { cerr << "r: " << r << endl; }++ if (lset.empty()) return true;+ // return true if rset contains all the elements of lset+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());++ }++ // create complex selector (ancestor of) from compound selector+ Complex_Selector_Obj Compound_Selector::to_complex()+ {+ // create an intermediate complex selector+ return SASS_MEMORY_NEW(Complex_Selector,+ pstate(),+ Complex_Selector::ANCESTOR_OF,+ this,+ 0);+ }++ Selector_List_Ptr Complex_Selector::unify_with(Complex_Selector_Ptr other, Context& ctx)+ {++ // get last tails (on the right side)+ Complex_Selector_Obj l_last = this->last();+ Complex_Selector_Obj r_last = other->last();++ // check valid pointers (assertion)+ SASS_ASSERT(l_last, "lhs is null");+ SASS_ASSERT(r_last, "rhs is null");++ // Not sure about this check, but closest way I could check+ // was to see if this is a ruby 'SimpleSequence' equivalent.+ // It seems to do the job correctly as some specs react to this+ if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0;+ if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0;++ // get the headers for the last tails+ Compound_Selector_Obj l_last_head = l_last->head();+ Compound_Selector_Obj r_last_head = r_last->head();++ // check valid head pointers (assertion)+ SASS_ASSERT(l_last_head, "lhs head is null");+ SASS_ASSERT(r_last_head, "rhs head is null");++ // get the unification of the last compound selectors+ Compound_Selector_Obj unified = r_last_head->unify_with(&l_last_head, ctx);++ // abort if we could not unify heads+ if (unified == 0) return 0;++ // check for universal (star: `*`) selector+ bool is_universal = l_last_head->is_universal() ||+ r_last_head->is_universal();++ if (is_universal)+ {+ // move the head+ l_last->head(0);+ r_last->head(unified);+ }++ // create nodes from both selectors+ Node lhsNode = complexSelectorToNode(this, ctx);+ Node rhsNode = complexSelectorToNode(other, ctx);++ // overwrite universal base+ if (!is_universal)+ {+ // create some temporaries to convert to node+ Complex_Selector_Obj fake = unified->to_complex();+ Node unified_node = complexSelectorToNode(&fake, ctx);+ // add to permutate the list?+ rhsNode.plus(unified_node);+ }++ // do some magic we inherit from node and extend+ Node node = Extend::subweave(lhsNode, rhsNode, ctx);+ Selector_List_Ptr result = SASS_MEMORY_NEW(Selector_List, pstate());+ NodeDequePtr col = node.collection(); // move from collection to list+ for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++)+ { result->append(nodeToComplexSelector(Node::naiveTrim(*it, ctx), ctx)); }++ // only return if list has some entries+ return result->length() ? result : 0;++ }++ bool Compound_Selector::operator== (const Compound_Selector& rhs) const+ {+ // for array access+ size_t i = 0, n = 0;+ size_t iL = length();+ size_t nL = rhs.length();+ // create temporary vectors and sort them+ std::vector<Simple_Selector_Obj> l_lst = this->elements();+ std::vector<Simple_Selector_Obj> r_lst = rhs.elements();+ std::sort(l_lst.begin(), l_lst.end(), cmp_simple_selector());+ std::sort(r_lst.begin(), r_lst.end(), cmp_simple_selector());+ // process loop+ while (true)+ {+ // first check for valid index+ if (i == iL) return iL == nL;+ else if (n == nL) return iL == nL;+ // the access the vector items+ Simple_Selector_Obj l = l_lst[i];+ Simple_Selector_Obj r = r_lst[n];+ // skip nulls+ if (!l) ++i;+ if (!r) ++n;+ // do the check now+ else if (*l != *r)+ { return false; }+ // advance now+ ++i; ++n;+ }+ // no mismatch+ return true;+ }++ bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector_Obj& pLeft, const Complex_Selector_Obj& pRight) const {+ return *pLeft < *pRight;+ }++ bool Complex_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)+ {+ return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);+ }++ bool Complex_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapping)+ {+ Complex_Selector_Ptr lhs = this;+ // check for selectors with leading or trailing combinators+ if (!lhs->head() || !rhs->head())+ { return false; }+ Complex_Selector_Obj l_innermost = lhs->innermost();+ if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)+ { return false; }+ Complex_Selector_Obj r_innermost = rhs->innermost();+ if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF)+ { return false; }+ // more complex (i.e., longer) selectors are always more specific+ size_t l_len = lhs->length(), r_len = rhs->length();+ if (l_len > r_len)+ { return false; }++ if (l_len == 1)+ { return lhs->head()->is_superselector_of(&rhs->last()->head(), wrapping); }++ // we have to look one tail deeper, since we cary the+ // combinator around for it (which is important here)+ if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {+ Complex_Selector_Obj lhs_tail = lhs->tail();+ Complex_Selector_Obj rhs_tail = rhs->tail();+ if (lhs_tail->combinator() != rhs_tail->combinator()) return false;+ if (lhs_tail->head() && !rhs_tail->head()) return false;+ if (!lhs_tail->head() && rhs_tail->head()) return false;+ if (lhs_tail->head() && rhs_tail->head()) {+ if (!lhs_tail->head()->is_superselector_of(&rhs_tail->head())) return false;+ }+ }++ bool found = false;+ Complex_Selector_Obj marker = rhs;+ for (size_t i = 0, L = rhs->length(); i < L; ++i) {+ if (i == L-1)+ { return false; }+ if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(&marker->head(), wrapping))+ { found = true; break; }+ marker = marker->tail();+ }+ if (!found)+ { return false; }++ /*+ Hmm, I hope I have the logic right:++ if lhs has a combinator:+ if !(marker has a combinator) return false+ if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false+ return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)+ else if marker has a combinator:+ if !(marker.combinator == ">") return false+ return lhs.tail.is_superselector_of(marker.tail)+ else+ return lhs.tail.is_superselector_of(marker.tail)+ */+ if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() == Complex_Selector::ANCESTOR_OF)+ { return false; }+ if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))+ { return false; }+ return lhs->tail()->is_superselector_of(&marker->tail());+ }+ else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() != Complex_Selector::PARENT_OF)+ { return false; }+ return lhs->tail()->is_superselector_of(&marker->tail());+ }+ else+ {+ return lhs->tail()->is_superselector_of(&marker->tail());+ }+ // catch-all+ return false;+ }++ size_t Complex_Selector::length() const+ {+ // TODO: make this iterative+ if (!tail()) return 1;+ return 1 + tail()->length();+ }++ Complex_Selector_Obj Complex_Selector::context(Context& ctx)+ {+ if (!tail()) return 0;+ if (!head()) return tail()->context(ctx);+ Complex_Selector_Obj cpy = SASS_MEMORY_NEW(Complex_Selector, pstate(), combinator(), head(), tail()->context(ctx));+ cpy->media_block(media_block());+ return cpy;+ }++ // append another complex selector at the end+ // check if we need to append some headers+ // then we need to check for the combinator+ // only then we can safely set the new tail+ void Complex_Selector::append(Context& ctx, Complex_Selector_Obj ss)+ {++ Complex_Selector_Obj t = ss->tail();+ Combinator c = ss->combinator();+ String_Obj r = ss->reference();+ Compound_Selector_Obj h = ss->head();++ if (ss->has_line_feed()) has_line_feed(true);+ if (ss->has_line_break()) has_line_break(true);++ // append old headers+ if (h && h->length()) {+ if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {+ error("Invalid parent selector", pstate_);+ } else if (last()->head_ && last()->head_->length()) {+ Compound_Selector_Obj rh = last()->head();+ size_t i = 0, L = h->length();+ if (SASS_MEMORY_CAST(Element_Selector, h->first())) {+ if (Class_Selector_Ptr sq = SASS_MEMORY_CAST(Class_Selector, rh->last())) {+ Class_Selector_Ptr sqs = SASS_MEMORY_COPY(sq);+ sqs->name(sqs->name() + (*h)[0]->name());+ sqs->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = sqs;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append(&(*h)[i]);+ } else if (Id_Selector_Ptr sq = SASS_MEMORY_CAST(Id_Selector, rh->last())) {+ Id_Selector_Ptr sqs = SASS_MEMORY_COPY(sq);+ sqs->name(sqs->name() + (*h)[0]->name());+ sqs->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = sqs;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append(&(*h)[i]);+ } else if (Element_Selector_Ptr ts = SASS_MEMORY_CAST(Element_Selector, rh->last())) {+ Element_Selector_Ptr tss = SASS_MEMORY_COPY(ts);+ tss->name(tss->name() + (*h)[0]->name());+ tss->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = tss;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append(&(*h)[i]);+ } else if (Placeholder_Selector_Ptr ps = SASS_MEMORY_CAST(Placeholder_Selector, rh->last())) {+ Placeholder_Selector_Ptr pss = SASS_MEMORY_COPY(ps);+ pss->name(pss->name() + (*h)[0]->name());+ pss->pstate((*h)[0]->pstate());+ (*rh)[rh->length()-1] = pss;+ rh->pstate(h->pstate());+ for (i = 1; i < L; ++i) rh->append(&(*h)[i]);+ } else {+ last()->head_->concat(&h);+ }+ } else {+ last()->head_->concat(&h);+ }+ } else {+ last()->head_->concat(&h);+ }+ } else {+ // std::cerr << "has no or empty head\n";+ }++ if (last()) {+ if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {+ Complex_Selector_Ptr inter = SASS_MEMORY_NEW(Complex_Selector, pstate());+ inter->reference(r);+ inter->combinator(c);+ inter->tail(t);+ last()->tail(inter);+ } else {+ if (last()->combinator() == ANCESTOR_OF) {+ last()->combinator(c);+ last()->reference(r);+ }+ last()->tail(t);+ }+ }+++ }++ Selector_List_Ptr Selector_List::resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, bool implicit_parent)+ {+ if (!this->has_parent_ref()) return this;+ Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());+ Selector_List_Ptr ps = &pstack.back();+ for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {+ for (size_t si = 0, sL = this->length(); si < sL; ++si) {+ Selector_List_Obj rv = at(si)->resolve_parent_refs(ctx, pstack, implicit_parent);+ ss->concat(&rv);+ }+ }+ return ss;+ }++ Selector_List_Ptr Complex_Selector::resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, bool implicit_parent)+ {+ Complex_Selector_Obj tail = this->tail();+ Compound_Selector_Obj head = this->head();+ Selector_List_Ptr parents = &pstack.back();++ if (!this->has_real_parent_ref() && !implicit_parent) {+ Selector_List_Ptr retval = SASS_MEMORY_NEW(Selector_List, pstate());+ retval->append(this);+ return retval;+ }++ // first resolve_parent_refs the tail (which may return an expanded list)+ Selector_List_Obj tails = tail ? tail->resolve_parent_refs(ctx, pstack, implicit_parent) : 0;++ if (head && head->length() > 0) {++ Selector_List_Obj retval;+ // we have a parent selector in a simple compound list+ // mix parent complex selector into the compound list+ if (SASS_MEMORY_CAST(Parent_Selector, (*head)[0])) {+ retval = SASS_MEMORY_NEW(Selector_List, pstate());++ // it turns out that real parent references reach+ // across @at-root rules, which comes unexpected+ if (parents == NULL && head->has_real_parent_ref()) {+ int i = pstack.size() - 1;+ while (!parents && i > -1) {+ parents = &pstack.at(i--);+ }+ }++ if (parents && parents->length()) {+ if (tails && tails->length() > 0) {+ for (size_t n = 0, nL = tails->length(); n < nL; ++n) {+ for (size_t i = 0, iL = parents->length(); i < iL; ++i) {+ Complex_Selector_Obj t = (*tails)[n];+ Complex_Selector_Obj parent = (*parents)[i];+ Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);+ Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);+ ss->tail(t ? SASS_MEMORY_CLONE(t) : 0);+ Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);+ // remove parent selector from sequence+ if (h->length()) h->erase(h->begin());+ ss->head(h->length() ? &h : 0);+ // adjust for parent selector (1 char)+ if (h->length()) {+ ParserState state(h->at(0)->pstate());+ state.offset.column += 1;+ state.column -= 1;+ (*h)[0]->pstate(state);+ }+ // keep old parser state+ s->pstate(pstate());+ // append new tail+ s->append(ctx, ss);+ retval->append(s);+ }+ }+ }+ // have no tails but parents+ // loop above is inside out+ else {+ for (size_t i = 0, iL = parents->length(); i < iL; ++i) {+ Complex_Selector_Obj parent = (*parents)[i];+ Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);+ Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);+ // this is only if valid if the parent has no trailing op+ // otherwise we cannot append more simple selectors to head+ if (parent->last()->combinator() != ANCESTOR_OF) {+ throw Exception::InvalidParent(&parent, &ss);+ }+ ss->tail(tail ? SASS_MEMORY_CLONE(tail) : 0);+ Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);+ // remove parent selector from sequence+ if (h->length()) h->erase(h->begin());+ ss->head(h->length() ? &h : 0);+ // \/ IMO ruby sass bug \/+ ss->has_line_feed(false);+ // adjust for parent selector (1 char)+ if (h->length()) {+ ParserState state(h->at(0)->pstate());+ state.offset.column += 1;+ state.column -= 1;+ (*h)[0]->pstate(state);+ }+ // keep old parser state+ s->pstate(pstate());+ // append new tail+ s->append(ctx, &ss);+ retval->append(s);+ }+ }+ }+ // have no parent but some tails+ else {+ if (tails && tails->length() > 0) {+ for (size_t n = 0, nL = tails->length(); n < nL; ++n) {+ Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);+ cpy->tail(SASS_MEMORY_CLONE(tails->at(n)));+ cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));+ for (size_t i = 1, L = this->head()->length(); i < L; ++i)+ cpy->head()->append(&(*this->head())[i]);+ if (!cpy->head()->length()) cpy->head(0);+ retval->append(cpy->skip_empty_reference());+ }+ }+ // have no parent nor tails+ else {+ Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);+ cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));+ for (size_t i = 1, L = this->head()->length(); i < L; ++i)+ cpy->head()->append(&(*this->head())[i]);+ if (!cpy->head()->length()) cpy->head(0);+ retval->append(cpy->skip_empty_reference());+ }+ }+ }+ // no parent selector in head+ else {+ retval = this->tails(ctx, &tails);+ }++ for (Simple_Selector_Obj ss : head->elements()) {+ if (Wrapped_Selector_Ptr ws = SASS_MEMORY_CAST(Wrapped_Selector, ss)) {+ if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, ws->selector())) {+ if (parents) ws->selector(sl->resolve_parent_refs(ctx, pstack, implicit_parent));+ }+ }+ }++ return retval.detach();++ }+ // has no head+ else {+ return this->tails(ctx, &tails);+ }++ // unreachable+ return 0;+ }++ Selector_List_Ptr Complex_Selector::tails(Context& ctx, Selector_List_Ptr tails)+ {+ Selector_List_Ptr rv = SASS_MEMORY_NEW(Selector_List, pstate_);+ if (tails && tails->length()) {+ for (size_t i = 0, iL = tails->length(); i < iL; ++i) {+ Complex_Selector_Obj pr = SASS_MEMORY_CLONE(this);+ pr->tail(tails->at(i));+ rv->append(pr);+ }+ }+ else {+ rv->append(this);+ }+ return rv;+ }++ // return the last tail that is defined+ Complex_Selector_Obj Complex_Selector::first()+ {+ // declare variables used in loop+ Complex_Selector_Obj cur = this;+ Compound_Selector_Obj head;+ // processing loop+ while (cur)+ {+ // get the head+ head = cur->head_;+ // abort (and return) if it is not a parent selector+ if (!head || head->length() != 1 || !SASS_MEMORY_CAST(Parent_Selector, (*head)[0])) {+ break;+ }+ // advance to next+ cur = cur->tail_;+ }+ // result+ return &cur;+ }++ // return the last tail that is defined+ Complex_Selector_Obj Complex_Selector::last()+ {+ // ToDo: implement with a while loop+ return tail_? tail_->last() : this;+ }++ Complex_Selector::Combinator Complex_Selector::clear_innermost()+ {+ Combinator c;+ if (!tail() || tail()->tail() == 0)+ { c = combinator(); combinator(ANCESTOR_OF); tail(0); }+ else+ { c = tail()->clear_innermost(); }+ return c;+ }++ void Complex_Selector::set_innermost(Complex_Selector_Obj val, Combinator c)+ {+ if (!tail())+ { tail(val); combinator(c); }+ else+ { tail()->set_innermost(val, c); }+ }++ void Complex_Selector::cloneChildren()+ {+ if (head()) head(SASS_MEMORY_CLONE(head()));+ if (tail()) tail(SASS_MEMORY_CLONE(tail()));+ }++ void Compound_Selector::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ void Selector_List::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ void Wrapped_Selector::cloneChildren()+ {+ selector(SASS_MEMORY_CLONE(selector()));+ }++ // remove parent selector references+ // basically unwraps parsed selectors+ void Selector_List::remove_parent_selectors()+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = length(); i < L; ++i) {+ if (!(*this)[i]->head()) continue;+ if ((*this)[i]->head()->is_empty_reference()) {+ // simply move to the next tail if we have "no" combinator+ if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) {+ if ((*this)[i]->tail()) {+ if ((*this)[i]->has_line_feed()) {+ (*this)[i]->tail()->has_line_feed(true);+ }+ (*this)[i] = (*this)[i]->tail();+ }+ }+ // otherwise remove the first item from head+ else {+ (*this)[i]->head()->erase((*this)[i]->head()->begin());+ }+ }+ }+ }++ bool Selector_List::has_parent_ref()+ {+ for (Complex_Selector_Obj s : elements()) {+ if (s && s->has_parent_ref()) return true;+ }+ return false;+ }++ bool Selector_List::has_real_parent_ref()+ {+ for (Complex_Selector_Obj s : elements()) {+ if (s && s->has_real_parent_ref()) return true;+ }+ return false;+ }++ bool Selector_Schema::has_parent_ref()+ {+ if (String_Schema_Obj schema = SASS_MEMORY_CAST(String_Schema, contents())) {+ return schema->length() > 0 && SASS_MEMORY_CAST(Parent_Selector, schema->at(0)) != NULL;+ }+ return false;+ }++ bool Selector_Schema::has_real_parent_ref()+ {+ if (String_Schema_Obj schema = SASS_MEMORY_CAST(String_Schema, contents())) {+ Parent_Selector_Obj p = SASS_MEMORY_CAST(Parent_Selector, schema->at(0));+ return schema->length() > 0 && p && p->is_real_parent_ref();+ }+ return false;+ }++ void Selector_List::adjust_after_pushing(Complex_Selector_Obj c)+ {+ // if (c->has_reference()) has_reference(true);+ }++ // it's a superselector if every selector of the right side+ // list is a superselector of the given left side selector+ bool Complex_Selector::is_superselector_of(Selector_List_Obj sub, std::string wrapping)+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = sub->length(); i < L; ++i) {+ if (!is_superselector_of(&(*sub)[i], wrapping)) return false;+ }+ return true;+ }++ // it's a superselector if every selector of the right side+ // list is a superselector of the given left side selector+ bool Selector_List::is_superselector_of(Selector_List_Obj sub, std::string wrapping)+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = sub->length(); i < L; ++i) {+ if (!is_superselector_of(&(*sub)[i], wrapping)) return false;+ }+ return true;+ }++ // it's a superselector if every selector on the right side+ // is a superselector of any one of the left side selectors+ bool Selector_List::is_superselector_of(Compound_Selector_Obj sub, std::string wrapping)+ {+ // Check every lhs selector against right hand+ for(size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_superselector_of(sub, wrapping)) return true;+ }+ return false;+ }++ // it's a superselector if every selector on the right side+ // is a superselector of any one of the left side selectors+ bool Selector_List::is_superselector_of(Complex_Selector_Obj sub, std::string wrapping)+ {+ // Check every lhs selector against right hand+ for(size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_superselector_of(sub)) return true;+ }+ return false;+ }++ Selector_List_Ptr Selector_List::unify_with(Selector_List_Ptr rhs, Context& ctx) {+ std::vector<Complex_Selector_Obj> unified_complex_selectors;+ // Unify all of children with RHS's children, storing the results in `unified_complex_selectors`+ for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) {+ Complex_Selector_Obj seq1 = (*this)[lhs_i];+ for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {+ Complex_Selector_Ptr seq2 = &rhs->at(rhs_i);++ Selector_List_Obj result = seq1->unify_with(seq2, ctx);+ if( result ) {+ for(size_t i = 0, L = result->length(); i < L; ++i) {+ unified_complex_selectors.push_back( &(*result)[i] );+ }+ }+ }+ }++ // Creates the final Selector_List by combining all the complex selectors+ Selector_List_Ptr final_result = SASS_MEMORY_NEW(Selector_List, pstate());+ for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {+ final_result->append(*itr);+ }+ return final_result;+ }++ void Selector_List::populate_extends(Selector_List_Obj extendee, Context& ctx, Subset_Map& extends)+ {++ Selector_List_Ptr extender = this;+ for (auto complex_sel : extendee->elements()) {+ Complex_Selector_Obj c = complex_sel;+++ // Ignore any parent selectors, until we find the first non Selectorerence head+ Compound_Selector_Obj compound_sel = c->head();+ Complex_Selector_Obj pIter = complex_sel;+ while (pIter) {+ Compound_Selector_Obj pHead = pIter->head();+ if (pHead && SASS_MEMORY_CAST(Parent_Selector, pHead->elements()[0]) == NULL) {+ compound_sel = pHead;+ break;+ }++ pIter = pIter->tail();+ }++ if (!pIter->head() || pIter->tail()) {+ error("nested selectors may not be extended", c->pstate());+ }++ compound_sel->is_optional(extendee->is_optional());++ for (size_t i = 0, L = extender->length(); i < L; ++i) {+ extends.put(compound_sel, std::make_pair(&(*extender)[i], &compound_sel));+ }+ }+ };++ std::vector<std::string> Compound_Selector::to_str_vec()+ {+ std::vector<std::string> result(length());+ for (size_t i = 0, L = length(); i < L; ++i)+ { result.push_back((*this)[i]->to_string()); }+ return result;+ }++ void Compound_Selector::append(Simple_Selector_Ptr element)+ {+ Vectorized<Simple_Selector_Obj>::append(element);+ pstate_.offset += element->pstate().offset;+ }++ Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs, Context& ctx)+ {+ Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate());+ // result->has_parent_reference(has_parent_reference());++ // not very efficient because it needs to preserve order+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ bool found = false;+ std::string thisSelector((*this)[i]->to_string(ctx.c_options));+ for (size_t j = 0, M = rhs->length(); j < M; ++j)+ {+ if (thisSelector == (*rhs)[j]->to_string(ctx.c_options))+ {+ found = true;+ break;+ }+ }+ if (!found) result->append(&(*this)[i]);+ }++ return result;+ }++ void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx)+ {+ for (SourcesSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) {+ this->sources_.insert(SASS_MEMORY_CLONE(*iterator));+ }+ }++ Argument_Obj Arguments::get_rest_argument()+ {+ if (this->has_rest_argument()) {+ for (Argument_Obj arg : this->elements()) {+ if (arg->is_rest_argument()) {+ return arg;+ }+ }+ }+ return NULL;+ }++ Argument_Obj Arguments::get_keyword_argument()+ {+ if (this->has_keyword_argument()) {+ for (Argument_Obj arg : this->elements()) {+ if (arg->is_keyword_argument()) {+ return arg;+ }+ }+ }+ return NULL;+ }++ void Arguments::adjust_after_pushing(Argument_Obj a)+ {+ if (!a->name().empty()) {+ if (/* has_rest_argument_ || */ has_keyword_argument_) {+ error("named arguments must precede variable-length argument", a->pstate());+ }+ has_named_arguments_ = true;+ }+ else if (a->is_rest_argument()) {+ if (has_rest_argument_) {+ error("functions and mixins may only be called with one variable-length argument", a->pstate());+ }+ if (has_keyword_argument_) {+ error("only keyword arguments may follow variable arguments", a->pstate());+ }+ has_rest_argument_ = true;+ }+ else if (a->is_keyword_argument()) {+ if (has_keyword_argument_) {+ error("functions and mixins may only be called with one keyword argument", a->pstate());+ }+ has_keyword_argument_ = true;+ }+ else {+ if (has_rest_argument_) {+ error("ordinal arguments must precede variable-length arguments", a->pstate());+ }+ if (has_named_arguments_) {+ error("ordinal arguments must precede named arguments", a->pstate());+ }+ }+ }++ bool Ruleset::is_invisible() const {+ if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, selector())) {+ for (size_t i = 0, L = sl->length(); i < L; ++i)+ if (!(*sl)[i]->has_placeholder()) return false;+ }+ return true;+ }++ bool Media_Block::is_invisible() const {+ for (size_t i = 0, L = block()->length(); i < L; ++i) {+ Statement_Obj stm = block()->at(i);+ if (!stm->is_invisible()) return false;+ }+ return true;+ }++ Number::Number(ParserState pstate, double val, std::string u, bool zero)+ : Value(pstate),+ value_(val),+ zero_(zero),+ numerator_units_(std::vector<std::string>()),+ denominator_units_(std::vector<std::string>()),+ hash_(0)+ {+ size_t l = 0, r = 0;+ if (!u.empty()) {+ bool nominator = true;+ while (true) {+ r = u.find_first_of("*/", l);+ std::string unit(u.substr(l, r == std::string::npos ? r : r - l));+ if (!unit.empty()) {+ if (nominator) numerator_units_.push_back(unit);+ else denominator_units_.push_back(unit);+ }+ if (r == std::string::npos) break;+ // ToDo: should error for multiple slashes+ // if (!nominator && u[r] == '/') error(...)+ if (u[r] == '/')+ nominator = false;+ l = r + 1;+ }+ }+ concrete_type(NUMBER);+ }++ std::string Number::unit() const+ {+ std::string u;+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ if (i) u += '*';+ u += numerator_units_[i];+ }+ if (!denominator_units_.empty()) u += '/';+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ if (i) u += '*';+ u += denominator_units_[i];+ }+ return u;+ }++ bool Number::is_valid_css_unit() const+ {+ return numerator_units().size() <= 1 &&+ denominator_units().size() == 0;+ }++ bool Number::is_unitless() const+ { return numerator_units_.empty() && denominator_units_.empty(); }++ void Number::normalize(const std::string& prefered, bool strict)+ {++ // first make sure same units cancel each other out+ // it seems that a map table will fit nicely to do this+ // we basically construct exponents for each unit+ // has the advantage that they will be pre-sorted+ std::map<std::string, int> exponents;++ // initialize by summing up occurences in unit vectors+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];++ // the final conversion factor+ double factor = 1;++ // get the first entry of numerators+ // forward it when entry is converted+ std::vector<std::string>::iterator nom_it = numerator_units_.begin();+ std::vector<std::string>::iterator nom_end = numerator_units_.end();+ std::vector<std::string>::iterator denom_it = denominator_units_.begin();+ std::vector<std::string>::iterator denom_end = denominator_units_.end();++ // main normalization loop+ // should be close to optimal+ while (denom_it != denom_end)+ {+ // get and increment afterwards+ const std::string denom = *(denom_it ++);+ // skip already canceled out unit+ if (exponents[denom] >= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(denom) == UNKNOWN) continue;+ // now search for nominator+ while (nom_it != nom_end)+ {+ // get and increment afterwards+ const std::string nom = *(nom_it ++);+ // skip already canceled out unit+ if (exponents[nom] <= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(nom) == UNKNOWN) continue;+ // we now have two convertable units+ // add factor for current conversion+ factor *= conversion_factor(nom, denom, strict);+ // update nominator/denominator exponent+ -- exponents[nom]; ++ exponents[denom];+ // inner loop done+ break;+ }+ }++ // now we can build up the new unit arrays+ numerator_units_.clear();+ denominator_units_.clear();++ // build them by iterating over the exponents+ for (auto exp : exponents)+ {+ // maybe there is more effecient way to push+ // the same item multiple times to a vector?+ for(size_t i = 0, S = abs(exp.second); i < S; ++i)+ {+ // opted to have these switches in the inner loop+ // makes it more readable and should not cost much+ if (!exp.first.empty()) {+ if (exp.second < 0) denominator_units_.push_back(exp.first);+ else if (exp.second > 0) numerator_units_.push_back(exp.first);+ }+ }+ }++ // apply factor to value_+ // best precision this way+ value_ *= factor;++ // maybe convert to other unit+ // easier implemented on its own+ try { convert(prefered, strict); }+ catch (incompatibleUnits& err)+ { error(err.what(), pstate()); }+ catch (...) { throw; }++ }++ // this does not cover all cases (multiple prefered units)+ double Number::convert_factor(const Number& n) const+ {++ // first make sure same units cancel each other out+ // it seems that a map table will fit nicely to do this+ // we basically construct exponents for each unit class+ // std::map<std::string, int> exponents;+ // initialize by summing up occurences in unit vectors+ // for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[unit_to_class(numerator_units_[i])];+ // for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[unit_to_class(denominator_units_[i])];++ std::vector<std::string> l_miss_nums(0);+ std::vector<std::string> l_miss_dens(0);+ // create copy since we need these for state keeping+ std::vector<std::string> r_nums(n.numerator_units_);+ std::vector<std::string> r_dens(n.denominator_units_);++ std::vector<std::string>::const_iterator l_num_it = numerator_units_.begin();+ std::vector<std::string>::const_iterator l_num_end = numerator_units_.end();++ bool l_unitless = is_unitless();+ bool r_unitless = n.is_unitless();++ // overall conversion+ double factor = 1;++ // process all left numerators+ while (l_num_it != l_num_end)+ {+ // get and increment afterwards+ const std::string l_num = *(l_num_it ++);++ std::vector<std::string>::iterator r_num_it = r_nums.begin();+ std::vector<std::string>::iterator r_num_end = r_nums.end();++ bool found = false;+ // search for compatible numerator+ while (r_num_it != r_num_end)+ {+ // get and increment afterwards+ const std::string r_num = *(r_num_it);+ // get possible converstion factor for units+ double conversion = conversion_factor(l_num, r_num, false);+ // skip incompatible numerator+ if (conversion == 0) {+ ++ r_num_it;+ continue;+ }+ // apply to global factor+ factor *= conversion;+ // remove item from vector+ r_nums.erase(r_num_it);+ // found numerator+ found = true;+ break;+ }+ // maybe we did not find any+ // left numerator is leftover+ if (!found) l_miss_nums.push_back(l_num);+ }++ std::vector<std::string>::const_iterator l_den_it = denominator_units_.begin();+ std::vector<std::string>::const_iterator l_den_end = denominator_units_.end();++ // process all left denominators+ while (l_den_it != l_den_end)+ {+ // get and increment afterwards+ const std::string l_den = *(l_den_it ++);++ std::vector<std::string>::iterator r_den_it = r_dens.begin();+ std::vector<std::string>::iterator r_den_end = r_dens.end();++ bool found = false;+ // search for compatible denominator+ while (r_den_it != r_den_end)+ {+ // get and increment afterwards+ const std::string r_den = *(r_den_it);+ // get possible converstion factor for units+ double conversion = conversion_factor(l_den, r_den, false);+ // skip incompatible denominator+ if (conversion == 0) {+ ++ r_den_it;+ continue;+ }+ // apply to global factor+ factor *= conversion;+ // remove item from vector+ r_dens.erase(r_den_it);+ // found denominator+ found = true;+ break;+ }+ // maybe we did not find any+ // left denominator is leftover+ if (!found) l_miss_dens.push_back(l_den);+ }++ // check left-overs (ToDo: might cancel out)+ if (l_miss_nums.size() > 0 && !r_unitless) {+ throw Exception::IncompatibleUnits(n, *this);+ }+ if (l_miss_dens.size() > 0 && !r_unitless) {+ throw Exception::IncompatibleUnits(n, *this);+ }+ if (r_nums.size() > 0 && !l_unitless) {+ throw Exception::IncompatibleUnits(n, *this);+ }+ if (r_dens.size() > 0 && !l_unitless) {+ throw Exception::IncompatibleUnits(n, *this);+ }++ return factor;+ }++ // this does not cover all cases (multiple prefered units)+ bool Number::convert(const std::string& prefered, bool strict)+ {+ // no conversion if unit is empty+ if (prefered.empty()) return true;++ // first make sure same units cancel each other out+ // it seems that a map table will fit nicely to do this+ // we basically construct exponents for each unit+ // has the advantage that they will be pre-sorted+ std::map<std::string, int> exponents;++ // initialize by summing up occurences in unit vectors+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];++ // the final conversion factor+ double factor = 1;++ std::vector<std::string>::iterator denom_it = denominator_units_.begin();+ std::vector<std::string>::iterator denom_end = denominator_units_.end();++ // main normalization loop+ // should be close to optimal+ while (denom_it != denom_end)+ {+ // get and increment afterwards+ const std::string denom = *(denom_it ++);+ // check if conversion is needed+ if (denom == prefered) continue;+ // skip already canceled out unit+ if (exponents[denom] >= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(denom) == UNKNOWN) continue;+ // we now have two convertable units+ // add factor for current conversion+ factor *= conversion_factor(denom, prefered, strict);+ // update nominator/denominator exponent+ ++ exponents[denom]; -- exponents[prefered];+ }++ std::vector<std::string>::iterator nom_it = numerator_units_.begin();+ std::vector<std::string>::iterator nom_end = numerator_units_.end();++ // now search for nominator+ while (nom_it != nom_end)+ {+ // get and increment afterwards+ const std::string nom = *(nom_it ++);+ // check if conversion is needed+ if (nom == prefered) continue;+ // skip already canceled out unit+ if (exponents[nom] <= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(nom) == UNKNOWN) continue;+ // we now have two convertable units+ // add factor for current conversion+ factor *= conversion_factor(nom, prefered, strict);+ // update nominator/denominator exponent+ -- exponents[nom]; ++ exponents[prefered];+ }++ // now we can build up the new unit arrays+ numerator_units_.clear();+ denominator_units_.clear();++ // build them by iterating over the exponents+ for (auto exp : exponents)+ {+ // maybe there is more effecient way to push+ // the same item multiple times to a vector?+ for(size_t i = 0, S = abs(exp.second); i < S; ++i)+ {+ // opted to have these switches in the inner loop+ // makes it more readable and should not cost much+ if (!exp.first.empty()) {+ if (exp.second < 0) denominator_units_.push_back(exp.first);+ else if (exp.second > 0) numerator_units_.push_back(exp.first);+ }+ }+ }++ // apply factor to value_+ // best precision this way+ value_ *= factor;++ // success?+ return true;++ }++ // useful for making one number compatible with another+ std::string Number::find_convertible_unit() const+ {+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ std::string u(numerator_units_[i]);+ if (string_to_unit(u) != UNKNOWN) return u;+ }+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ std::string u(denominator_units_[i]);+ if (string_to_unit(u) != UNKNOWN) return u;+ }+ return std::string();+ }++ bool Custom_Warning::operator== (const Expression& rhs) const+ {+ if (Custom_Warning_Ptr_Const r = dynamic_cast<Custom_Warning_Ptr_Const>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ bool Custom_Error::operator== (const Expression& rhs) const+ {+ if (Custom_Error_Ptr_Const r = dynamic_cast<Custom_Error_Ptr_Const>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ bool Number::eq (const Expression& rhs) const+ {+ if (Number_Ptr_Const r = dynamic_cast<Number_Ptr_Const>(&rhs)) {+ size_t lhs_units = numerator_units_.size() + denominator_units_.size();+ size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size();+ if (!lhs_units && !rhs_units) {+ return std::fabs(value() - r->value()) < NUMBER_EPSILON;+ }+ return (numerator_units_ == r->numerator_units_) &&+ (denominator_units_ == r->denominator_units_) &&+ std::fabs(value() - r->value()) < NUMBER_EPSILON;+ }+ return false;+ }++ bool Number::operator== (const Expression& rhs) const+ {+ if (Number_Ptr_Const r = dynamic_cast<Number_Ptr_Const>(&rhs)) {+ size_t lhs_units = numerator_units_.size() + denominator_units_.size();+ size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size();+ // unitless and only having one unit seems equivalent (will change in future)+ if (!lhs_units || !rhs_units) {+ return std::fabs(value() - r->value()) < NUMBER_EPSILON;+ }+ return (numerator_units_ == r->numerator_units_) &&+ (denominator_units_ == r->denominator_units_) &&+ std::fabs(value() - r->value()) < NUMBER_EPSILON;+ }+ return false;+ }++ bool Number::operator< (const Number& rhs) const+ {+ size_t lhs_units = numerator_units_.size() + denominator_units_.size();+ size_t rhs_units = rhs.numerator_units_.size() + rhs.denominator_units_.size();+ // unitless and only having one unit seems equivalent (will change in future)+ if (!lhs_units || !rhs_units) {+ return value() < rhs.value();+ }++ Number tmp_r(&rhs); // copy+ tmp_r.normalize(find_convertible_unit());+ std::string l_unit(unit());+ std::string r_unit(tmp_r.unit());+ if (unit() != tmp_r.unit()) {+ error("cannot compare numbers with incompatible units", pstate());+ }+ return value() < tmp_r.value();+ }++ bool String_Quoted::operator== (const Expression& rhs) const+ {+ if (String_Quoted_Ptr_Const qstr = dynamic_cast<String_Quoted_Ptr_Const>(&rhs)) {+ return (value() == qstr->value());+ } else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(&rhs)) {+ return (value() == cstr->value());+ }+ return false;+ }++ bool String_Constant::is_invisible() const {+ return value_.empty() && quote_mark_ == 0;+ }++ bool String_Constant::operator== (const Expression& rhs) const+ {+ if (String_Quoted_Ptr_Const qstr = dynamic_cast<String_Quoted_Ptr_Const>(&rhs)) {+ return (value() == qstr->value());+ } else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(&rhs)) {+ return (value() == cstr->value());+ }+ return false;+ }++ bool String_Schema::is_left_interpolant(void) const+ {+ return length() && first()->is_left_interpolant();+ }+ bool String_Schema::is_right_interpolant(void) const+ {+ return length() && last()->is_right_interpolant();+ }++ bool String_Schema::operator== (const Expression& rhs) const+ {+ if (String_Schema_Ptr_Const r = dynamic_cast<String_Schema_Ptr_Const>(&rhs)) {+ if (length() != r->length()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj rv = (*r)[i];+ Expression_Obj lv = (*this)[i];+ if (!lv || !rv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Boolean::operator== (const Expression& rhs) const+ {+ if (Boolean_Ptr_Const r = dynamic_cast<Boolean_Ptr_Const>(&rhs)) {+ return (value() == r->value());+ }+ return false;+ }++ bool Color::operator== (const Expression& rhs) const+ {+ if (Color_Ptr_Const r = dynamic_cast<Color_Ptr_Const>(&rhs)) {+ return r_ == r->r() &&+ g_ == r->g() &&+ b_ == r->b() &&+ a_ == r->a();+ }+ return false;+ }++ bool List::operator== (const Expression& rhs) const+ {+ if (List_Ptr_Const r = dynamic_cast<List_Ptr_Const>(&rhs)) {+ if (length() != r->length()) return false;+ if (separator() != r->separator()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj rv = r->at(i);+ Expression_Obj lv = this->at(i);+ if (!lv || !rv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Map::operator== (const Expression& rhs) const+ {+ if (Map_Ptr_Const r = dynamic_cast<Map_Ptr_Const>(&rhs)) {+ if (length() != r->length()) return false;+ for (auto key : keys()) {+ Expression_Obj lv = at(key);+ Expression_Obj rv = r->at(key);+ if (!rv || !lv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Null::operator== (const Expression& rhs) const+ {+ return rhs.concrete_type() == NULL_VAL;+ }++ size_t List::size() const {+ if (!is_arglist_) return length();+ // arglist expects a list of arguments+ // so we need to break before keywords+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj obj = this->at(i);+ if (Argument* arg = dynamic_cast<Argument*>(&obj)) {+ if (!arg->name().empty()) return i;+ }+ }+ return length();+ }++ Expression_Obj Hashed::at(Expression_Obj k) const+ {+ if (elements_.count(k))+ { return elements_.at(k); }+ else { return NULL; }+ }++ bool Binary_Expression::is_left_interpolant(void) const+ {+ return is_interpolant() || (left() && left()->is_left_interpolant());+ }+ bool Binary_Expression::is_right_interpolant(void) const+ {+ return is_interpolant() || (right() && right()->is_right_interpolant());+ }++ std::string AST_Node::to_string(Sass_Inspect_Options opt) const+ {+ Sass_Output_Options out(opt);+ Emitter emitter(out);+ Inspect i(emitter);+ i.in_declaration = true;+ // ToDo: inspect should be const+ const_cast<AST_Node_Ptr>(this)->perform(&i);+ return i.get_buffer();+ }++ std::string AST_Node::to_string() const+ {+ return to_string({ NESTED, 5 });+ }++ std::string String_Quoted::inspect() const+ {+ return quote(value_, '*');+ }++ std::string String_Constant::inspect() const+ {+ return quote(value_, '*');+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Additional method on Lists to retrieve values directly or from an encompassed Argument.+ //////////////////////////////////////////////////////////////////////////////////////////+ Expression_Obj List::value_at_index(size_t i) {+ Expression_Obj obj = this->at(i);+ if (is_arglist_) {+ if (Argument* arg = dynamic_cast<Argument*>(&obj)) {+ return arg->value();+ } else {+ return &obj;+ }+ } else {+ return &obj;+ }+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Convert map to (key, value) list.+ //////////////////////////////////////////////////////////////////////////////////////////+ List_Obj Map::to_list(Context& ctx, ParserState& pstate) {+ List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA);++ for (auto key : keys()) {+ List_Obj l = SASS_MEMORY_NEW(List, pstate, 2);+ l->append(&key);+ l->append(at(key));+ ret->append(&l);+ }++ return ret;+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Copy implementations+ //////////////////////////////////////////////////////////////////////////////////////////++ #ifdef DEBUG_SHARED_PTR++ #define IMPLEMENT_AST_OPERATORS(klass) \+ klass##_Ptr klass::copy(std::string file, size_t line) const { \+ klass##_Ptr cpy = new klass(this); \+ cpy->trace(file, line); \+ return cpy; \+ } \+ klass##_Ptr klass::clone(std::string file, size_t line) const { \+ klass##_Ptr cpy = copy(file, line); \+ cpy->cloneChildren(); \+ return cpy; \+ } \++ #else++ #define IMPLEMENT_AST_OPERATORS(klass) \+ klass##_Ptr klass::copy() const { \+ return new klass(this); \+ } \+ klass##_Ptr klass::clone() const { \+ klass##_Ptr cpy = copy(); \+ cpy->cloneChildren(); \+ return cpy; \+ } \++ #endif++ IMPLEMENT_AST_OPERATORS(Supports_Operator);+ IMPLEMENT_AST_OPERATORS(Supports_Negation);+ IMPLEMENT_AST_OPERATORS(Compound_Selector);+ IMPLEMENT_AST_OPERATORS(Complex_Selector);+ IMPLEMENT_AST_OPERATORS(Element_Selector);+ IMPLEMENT_AST_OPERATORS(Class_Selector);+ IMPLEMENT_AST_OPERATORS(Id_Selector);+ IMPLEMENT_AST_OPERATORS(Pseudo_Selector);+ IMPLEMENT_AST_OPERATORS(Wrapped_Selector);+ IMPLEMENT_AST_OPERATORS(Selector_List);+ IMPLEMENT_AST_OPERATORS(Ruleset);+ IMPLEMENT_AST_OPERATORS(Media_Block);+ IMPLEMENT_AST_OPERATORS(Custom_Warning);+ IMPLEMENT_AST_OPERATORS(Custom_Error);+ IMPLEMENT_AST_OPERATORS(List);+ IMPLEMENT_AST_OPERATORS(Map);+ IMPLEMENT_AST_OPERATORS(Number);+ IMPLEMENT_AST_OPERATORS(Binary_Expression);+ IMPLEMENT_AST_OPERATORS(String_Schema);+ IMPLEMENT_AST_OPERATORS(String_Constant);+ IMPLEMENT_AST_OPERATORS(String_Quoted);+ IMPLEMENT_AST_OPERATORS(Boolean);+ IMPLEMENT_AST_OPERATORS(Color);+ IMPLEMENT_AST_OPERATORS(Null);+ IMPLEMENT_AST_OPERATORS(Parent_Selector);+ IMPLEMENT_AST_OPERATORS(Import);+ IMPLEMENT_AST_OPERATORS(Import_Stub);+ IMPLEMENT_AST_OPERATORS(Function_Call);+ IMPLEMENT_AST_OPERATORS(Directive);+ IMPLEMENT_AST_OPERATORS(At_Root_Block);+ IMPLEMENT_AST_OPERATORS(Supports_Block);+ IMPLEMENT_AST_OPERATORS(While);+ IMPLEMENT_AST_OPERATORS(Each);+ IMPLEMENT_AST_OPERATORS(For);+ IMPLEMENT_AST_OPERATORS(If);+ IMPLEMENT_AST_OPERATORS(Mixin_Call);+ IMPLEMENT_AST_OPERATORS(Extension);+ IMPLEMENT_AST_OPERATORS(Media_Query);+ IMPLEMENT_AST_OPERATORS(Media_Query_Expression);+ IMPLEMENT_AST_OPERATORS(Debug);+ IMPLEMENT_AST_OPERATORS(Error);+ IMPLEMENT_AST_OPERATORS(Warning);+ IMPLEMENT_AST_OPERATORS(Assignment);+ IMPLEMENT_AST_OPERATORS(Return);+ IMPLEMENT_AST_OPERATORS(At_Root_Query);+ IMPLEMENT_AST_OPERATORS(Variable);+ IMPLEMENT_AST_OPERATORS(Comment);+ IMPLEMENT_AST_OPERATORS(Attribute_Selector);+ IMPLEMENT_AST_OPERATORS(Supports_Interpolation);+ IMPLEMENT_AST_OPERATORS(Supports_Declaration);+ IMPLEMENT_AST_OPERATORS(Supports_Condition);+ IMPLEMENT_AST_OPERATORS(Parameters);+ IMPLEMENT_AST_OPERATORS(Parameter);+ IMPLEMENT_AST_OPERATORS(Arguments);+ IMPLEMENT_AST_OPERATORS(Argument);+ IMPLEMENT_AST_OPERATORS(Unary_Expression);+ IMPLEMENT_AST_OPERATORS(Function_Call_Schema);+ IMPLEMENT_AST_OPERATORS(Block);+ IMPLEMENT_AST_OPERATORS(Content);+ IMPLEMENT_AST_OPERATORS(Textual);+ IMPLEMENT_AST_OPERATORS(Trace);+ IMPLEMENT_AST_OPERATORS(Keyframe_Rule);+ IMPLEMENT_AST_OPERATORS(Bubble);+ IMPLEMENT_AST_OPERATORS(Selector_Schema);+ IMPLEMENT_AST_OPERATORS(Placeholder_Selector);+ IMPLEMENT_AST_OPERATORS(Definition);+ IMPLEMENT_AST_OPERATORS(Declaration);++}
libsass/src/ast.hpp view
@@ -9,2332 +9,3076 @@ #include <iostream> #include <typeinfo> #include <algorithm>-#include <unordered_map>-#include "sass/base.h"--#ifdef __clang__--/*- * There are some overloads used here that trigger the clang overload- * hiding warning. Specifically:- *- * Type type() which hides string type() from Expression- *- * and- *- * Block* block() which hides virtual Block* block() from Statement- *- */--#pragma clang diagnostic push-#pragma clang diagnostic ignored "-Woverloaded-virtual"--#endif--#include "util.hpp"-#include "units.hpp"-#include "context.hpp"-#include "position.hpp"-#include "constants.hpp"-#include "operation.hpp"-#include "position.hpp"-#include "inspect.hpp"-#include "source_map.hpp"-#include "environment.hpp"-#include "error_handling.hpp"-#include "ast_def_macros.hpp"-#include "ast_fwd_decl.hpp"-#include "to_string.hpp"-#include "source_map.hpp"--#include "sass.h"--namespace Sass {-- const double NUMBER_EPSILON = 0.00000000000001;-- // from boost (functional/hash):- // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html- // Boost Software License - Version 1.0- // http://www.boost.org/users/license.html- template <typename T>- void hash_combine (std::size_t& seed, const T& val)- {- seed ^= std::hash<T>()(val) + 0x9e3779b9- + (seed<<6) + (seed>>2);- }-- //////////////////////////////////////////////////////////- // Abstract base class for all abstract syntax tree nodes.- //////////////////////////////////////////////////////////- class AST_Node : public Memory_Object {- ADD_PROPERTY(ParserState, pstate)- public:- AST_Node(ParserState pstate)- : pstate_(pstate)- { }- virtual ~AST_Node() = 0;- // virtual Block* block() { return 0; }- public:- void update_pstate(const ParserState& pstate);- public:- Offset off() { return pstate(); }- Position pos() { return pstate(); }- ATTACH_OPERATIONS()- };- inline AST_Node::~AST_Node() { }--- //////////////////////////////////////////////////////////////////////- // Abstract base class for expressions. This side of the AST hierarchy- // represents elements in value contexts, which exist primarily to be- // evaluated and returned.- //////////////////////////////////////////////////////////////////////- class Expression : public AST_Node {- public:- enum Concrete_Type {- NONE,- BOOLEAN,- NUMBER,- COLOR,- STRING,- LIST,- MAP,- SELECTOR,- NULL_VAL,- C_WARNING,- C_ERROR,- NUM_TYPES- };- private:- // expressions in some contexts shouldn't be evaluated- ADD_PROPERTY(bool, is_delayed)- ADD_PROPERTY(bool, is_expanded)- ADD_PROPERTY(bool, is_interpolant)- ADD_PROPERTY(Concrete_Type, concrete_type)- public:- Expression(ParserState pstate,- bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)- : AST_Node(pstate),- is_delayed_(d),- is_expanded_(d),- is_interpolant_(i),- concrete_type_(ct)- { }- virtual operator bool() { return true; }- virtual ~Expression() { }- virtual std::string type() { return ""; /* TODO: raise an error? */ }- virtual bool is_invisible() const { return false; }- static std::string type_name() { return ""; }- virtual bool is_false() { return false; }- virtual bool operator== (const Expression& rhs) const { return false; }- virtual void set_delayed(bool delayed) { is_delayed(delayed); }- virtual size_t hash() { return 0; }- };-- //////////////////////////////////////////////////////////////////////- // base class for values that support operations- //////////////////////////////////////////////////////////////////////- class Value : public Expression {- public:- Value(ParserState pstate,- bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)- : Expression(pstate, d, e, i, ct)- { }- virtual bool operator== (const Expression& rhs) const = 0;- virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;- };-}--/////////////////////////////////////////////////////////////////////////////////////-// Hash method specializations for std::unordered_map to work with Sass::Expression-/////////////////////////////////////////////////////////////////////////////////////--namespace std {- template<>- struct hash<Sass::Expression*>- {- size_t operator()(Sass::Expression* s) const- {- return s->hash();- }- };- template<>- struct equal_to<Sass::Expression*>- {- bool operator()( Sass::Expression* lhs, Sass::Expression* rhs) const- {- return lhs->hash() == rhs->hash();- }- };-}--namespace Sass {-- /////////////////////////////////////////////////////////////////////////////- // Mixin class for AST nodes that should behave like vectors. Uses the- // "Template Method" design pattern to allow subclasses to adjust their flags- // when certain objects are pushed.- /////////////////////////////////////////////////////////////////////////////- template <typename T>- class Vectorized {- std::vector<T> elements_;- protected:- size_t hash_;- void reset_hash() { hash_ = 0; }- virtual void adjust_after_pushing(T element) { }- public:- Vectorized(size_t s = 0) : elements_(std::vector<T>())- { elements_.reserve(s); }- virtual ~Vectorized() = 0;- size_t length() const { return elements_.size(); }- bool empty() const { return elements_.empty(); }- T last() { return elements_.back(); }- T first() { return elements_.front(); }- T& operator[](size_t i) { return elements_[i]; }- virtual const T& at(size_t i) const { return elements_.at(i); }- const T& operator[](size_t i) const { return elements_[i]; }- Vectorized& operator<<(T element)- {- if (!element) return *this;- reset_hash();- elements_.push_back(element);- adjust_after_pushing(element);- return *this;- }- Vectorized& operator+=(Vectorized* v)- {- for (size_t i = 0, L = v->length(); i < L; ++i) *this << (*v)[i];- return *this;- }- Vectorized& unshift(T element)- {- elements_.insert(elements_.begin(), element);- return *this;- }- std::vector<T>& elements() { return elements_; }- const std::vector<T>& elements() const { return elements_; }- std::vector<T>& elements(std::vector<T>& e) { elements_ = e; return elements_; }-- typename std::vector<T>::iterator end() { return elements_.end(); }- typename std::vector<T>::iterator begin() { return elements_.begin(); }- typename std::vector<T>::const_iterator end() const { return elements_.end(); }- typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }- typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { return elements_.erase(el); }- typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { return elements_.erase(el); }-- };- template <typename T>- inline Vectorized<T>::~Vectorized() { }-- /////////////////////////////////////////////////////////////////////////////- // Mixin class for AST nodes that should behave like a hash table. Uses an- // extra <std::vector> internally to maintain insertion order for interation.- /////////////////////////////////////////////////////////////////////////////- class Hashed {- private:- std::unordered_map<Expression*, Expression*> elements_;- std::vector<Expression*> list_;- protected:- size_t hash_;- Expression* duplicate_key_;- void reset_hash() { hash_ = 0; }- void reset_duplicate_key() { duplicate_key_ = 0; }- virtual void adjust_after_pushing(std::pair<Expression*, Expression*> p) { }- public:- Hashed(size_t s = 0) : elements_(std::unordered_map<Expression*, Expression*>(s)), list_(std::vector<Expression*>())- { elements_.reserve(s); list_.reserve(s); reset_duplicate_key(); }- virtual ~Hashed();- size_t length() const { return list_.size(); }- bool empty() const { return list_.empty(); }- bool has(Expression* k) const { return elements_.count(k) == 1; }- Expression* at(Expression* k) const;- bool has_duplicate_key() const { return duplicate_key_ != 0; }- Expression* get_duplicate_key() const { return duplicate_key_; }- const std::unordered_map<Expression*, Expression*> elements() { return elements_; }- Hashed& operator<<(std::pair<Expression*, Expression*> p)- {- reset_hash();-- if (!has(p.first)) list_.push_back(p.first);- else if (!duplicate_key_) duplicate_key_ = p.first;-- elements_[p.first] = p.second;-- adjust_after_pushing(p);- return *this;- }- Hashed& operator+=(Hashed* h)- {- if (length() == 0) {- this->elements_ = h->elements_;- this->list_ = h->list_;- return *this;- }-- for (auto key : h->keys()) {- *this << std::make_pair(key, h->at(key));- }-- reset_duplicate_key();- return *this;- }- const std::unordered_map<Expression*, Expression*>& pairs() const { return elements_; }- const std::vector<Expression*>& keys() const { return list_; }-- std::unordered_map<Expression*, Expression*>::iterator end() { return elements_.end(); }- std::unordered_map<Expression*, Expression*>::iterator begin() { return elements_.begin(); }- std::unordered_map<Expression*, Expression*>::const_iterator end() const { return elements_.end(); }- std::unordered_map<Expression*, Expression*>::const_iterator begin() const { return elements_.begin(); }-- };- inline Hashed::~Hashed() { }--- /////////////////////////////////////////////////////////////////////////- // Abstract base class for statements. This side of the AST hierarchy- // represents elements in expansion contexts, which exist primarily to be- // rewritten and macro-expanded.- /////////////////////////////////////////////////////////////////////////- class Statement : public AST_Node {- public:- enum Statement_Type {- NONE,- RULESET,- MEDIA,- DIRECTIVE,- SUPPORTS,- ATROOT,- BUBBLE,- CONTENT,- KEYFRAMERULE,- DECLARATION,- ASSIGNMENT,- IMPORT_STUB,- IMPORT,- COMMENT,- WARNING,- RETURN,- EXTEND,- ERROR,- DEBUGSTMT,- WHILE,- EACH,- FOR,- IF- };- private:- ADD_PROPERTY(Block*, block)- ADD_PROPERTY(Statement_Type, statement_type)- ADD_PROPERTY(size_t, tabs)- ADD_PROPERTY(bool, group_end)- public:- Statement(ParserState pstate, Statement_Type st = NONE, size_t t = 0)- : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)- { }- virtual ~Statement() = 0;- // needed for rearranging nested rulesets during CSS emission- virtual bool is_hoistable() { return false; }- virtual bool is_invisible() const { return false; }- virtual bool bubbles() { return false; }- virtual Block* block() { return 0; }- virtual bool has_content()- {- return statement_type_ == CONTENT;- }- };- inline Statement::~Statement() { }-- ////////////////////////- // Blocks of statements.- ////////////////////////- class Block : public Statement, public Vectorized<Statement*> {- ADD_PROPERTY(bool, is_root)- ADD_PROPERTY(bool, is_at_root);- // needed for properly formatted CSS emission- ADD_PROPERTY(bool, has_hoistable)- ADD_PROPERTY(bool, has_non_hoistable)- protected:- void adjust_after_pushing(Statement* s)- {- if (s->is_hoistable()) has_hoistable_ = true;- else has_non_hoistable_ = true;- }- public:- Block(ParserState pstate, size_t s = 0, bool r = false)- : Statement(pstate),- Vectorized<Statement*>(s),- is_root_(r),- is_at_root_(false),- has_hoistable_(false),- has_non_hoistable_(false)- { }- virtual bool has_content()- {- for (size_t i = 0, L = elements().size(); i < L; ++i) {- if (elements()[i]->has_content()) return true;- }- return Statement::has_content();- }- Block* block() { return this; }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Abstract base class for statements that contain blocks of statements.- ////////////////////////////////////////////////////////////////////////- class Has_Block : public Statement {- ADD_PROPERTY(Block*, block)- public:- Has_Block(ParserState pstate, Block* b)- : Statement(pstate), block_(b)- { }- virtual bool has_content()- {- return (block_ && block_->has_content()) || Statement::has_content();- }- virtual ~Has_Block() = 0;- };- inline Has_Block::~Has_Block() { }-- /////////////////////////////////////////////////////////////////////////////- // Rulesets (i.e., sets of styles headed by a selector and containing a block- // of style declarations.- /////////////////////////////////////////////////////////////////////////////- class Ruleset : public Has_Block {- ADD_PROPERTY(Selector*, selector)- ADD_PROPERTY(bool, at_root);- ADD_PROPERTY(bool, is_root);- public:- Ruleset(ParserState pstate, Selector* s = 0, Block* b = 0)- : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false)- { statement_type(RULESET); }- bool is_invisible() const;- // nested rulesets need to be hoisted out of their enclosing blocks- bool is_hoistable() { return true; }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////- // Nested declaration sets (i.e., namespaced properties).- /////////////////////////////////////////////////////////- class Propset : public Has_Block {- ADD_PROPERTY(String*, property_fragment)- public:- Propset(ParserState pstate, String* pf, Block* b = 0)- : Has_Block(pstate, b), property_fragment_(pf)- { }- ATTACH_OPERATIONS()- };-- /////////////////- // Bubble.- /////////////////- class Bubble : public Statement {- ADD_PROPERTY(Statement*, node)- ADD_PROPERTY(bool, group_end)- public:- Bubble(ParserState pstate, Statement* n, Statement* g = 0, size_t t = 0)- : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0)- { }- bool bubbles() { return true; }- ATTACH_OPERATIONS()- };-- /////////////////- // Media queries.- /////////////////- class Media_Block : public Has_Block {- ADD_PROPERTY(List*, media_queries)- public:- Media_Block(ParserState pstate, List* mqs, Block* b)- : Has_Block(pstate, b), media_queries_(mqs)- { statement_type(MEDIA); }- Media_Block(ParserState pstate, List* mqs, Block* b, Selector* s)- : Has_Block(pstate, b), media_queries_(mqs)- { statement_type(MEDIA); }- bool bubbles() { return true; }- bool is_hoistable() { return true; }- bool is_invisible() const;- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // At-rules -- arbitrary directives beginning with "@" that may have an- // optional statement block.- ///////////////////////////////////////////////////////////////////////- class At_Rule : public Has_Block {- ADD_PROPERTY(std::string, keyword)- ADD_PROPERTY(Selector*, selector)- ADD_PROPERTY(Expression*, value)- public:- At_Rule(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0)- : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed- { statement_type(DIRECTIVE); }- bool bubbles() { return is_keyframes() || is_media(); }- bool is_media() {- return keyword_.compare("@-webkit-media") == 0 ||- keyword_.compare("@-moz-media") == 0 ||- keyword_.compare("@-o-media") == 0 ||- keyword_.compare("@media") == 0;- }- bool is_keyframes() {- return keyword_.compare("@-webkit-keyframes") == 0 ||- keyword_.compare("@-moz-keyframes") == 0 ||- keyword_.compare("@-o-keyframes") == 0 ||- keyword_.compare("@keyframes") == 0;- }- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // Keyframe-rules -- the child blocks of "@keyframes" nodes.- ///////////////////////////////////////////////////////////////////////- class Keyframe_Rule : public Has_Block {- ADD_PROPERTY(Selector*, selector)- public:- Keyframe_Rule(ParserState pstate, Block* b)- : Has_Block(pstate, b), selector_(0)- { statement_type(KEYFRAMERULE); }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Declarations -- style rules consisting of a property name and values.- ////////////////////////////////////////////////////////////////////////- class Declaration : public Statement {- ADD_PROPERTY(String*, property)- ADD_PROPERTY(Expression*, value)- ADD_PROPERTY(bool, is_important)- ADD_PROPERTY(bool, is_indented)- public:- Declaration(ParserState pstate,- String* prop, Expression* val, bool i = false)- : Statement(pstate), property_(prop), value_(val), is_important_(i), is_indented_(false)- { statement_type(DECLARATION); }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////- // Assignments -- variable and value.- /////////////////////////////////////- class Assignment : public Statement {- ADD_PROPERTY(std::string, variable)- ADD_PROPERTY(Expression*, value)- ADD_PROPERTY(bool, is_default)- ADD_PROPERTY(bool, is_global)- public:- Assignment(ParserState pstate,- std::string var, Expression* val,- bool is_default = false,- bool is_global = false)- : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)- { statement_type(ASSIGNMENT); }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // Import directives. CSS and Sass import lists can be intermingled, so it's- // necessary to store a list of each in an Import node.- ////////////////////////////////////////////////////////////////////////////- class Import : public Statement {- std::vector<Expression*> urls_;- std::vector<Include> incs_;- ADD_PROPERTY(List*, media_queries);- public:- Import(ParserState pstate)- : Statement(pstate),- urls_(std::vector<Expression*>()),- incs_(std::vector<Include>()),- media_queries_(0)- { statement_type(IMPORT); }- std::vector<Expression*>& urls() { return urls_; }- std::vector<Include>& incs() { return incs_; }- ATTACH_OPERATIONS()- };-- // not yet resolved single import- // so far we only know requested name- class Import_Stub : public Statement {- Include resource_;- public:- std::string abs_path() { return resource_.abs_path; };- std::string imp_path() { return resource_.imp_path; };- Include resource() { return resource_; };-- Import_Stub(ParserState pstate, Include res)- : Statement(pstate), resource_(res)- { statement_type(IMPORT_STUB); }- ATTACH_OPERATIONS()- };-- //////////////////////////////- // The Sass `@warn` directive.- //////////////////////////////- class Warning : public Statement {- ADD_PROPERTY(Expression*, message)- public:- Warning(ParserState pstate, Expression* msg)- : Statement(pstate), message_(msg)- { statement_type(WARNING); }- ATTACH_OPERATIONS()- };-- ///////////////////////////////- // The Sass `@error` directive.- ///////////////////////////////- class Error : public Statement {- ADD_PROPERTY(Expression*, message)- public:- Error(ParserState pstate, Expression* msg)- : Statement(pstate), message_(msg)- { statement_type(ERROR); }- ATTACH_OPERATIONS()- };-- ///////////////////////////////- // The Sass `@debug` directive.- ///////////////////////////////- class Debug : public Statement {- ADD_PROPERTY(Expression*, value)- public:- Debug(ParserState pstate, Expression* val)- : Statement(pstate), value_(val)- { statement_type(DEBUGSTMT); }- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////- // CSS comments. These may be interpolated.- ///////////////////////////////////////////- class Comment : public Statement {- ADD_PROPERTY(String*, text)- ADD_PROPERTY(bool, is_important)- public:- Comment(ParserState pstate, String* txt, bool is_important)- : Statement(pstate), text_(txt), is_important_(is_important)- { statement_type(COMMENT); }- virtual bool is_invisible() const- { return is_important() == false; }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////- // The Sass `@if` control directive.- ////////////////////////////////////- class If : public Has_Block {- ADD_PROPERTY(Expression*, predicate)- ADD_PROPERTY(Block*, alternative)- public:- If(ParserState pstate, Expression* pred, Block* con, Block* alt = 0)- : Has_Block(pstate, con), predicate_(pred), alternative_(alt)- { statement_type(IF); }- virtual bool has_content()- {- return Has_Block::has_content() || (alternative_ && alternative_->has_content());- }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////- // The Sass `@for` control directive.- /////////////////////////////////////- class For : public Has_Block {- ADD_PROPERTY(std::string, variable)- ADD_PROPERTY(Expression*, lower_bound)- ADD_PROPERTY(Expression*, upper_bound)- ADD_PROPERTY(bool, is_inclusive)- public:- For(ParserState pstate,- std::string var, Expression* lo, Expression* hi, Block* b, bool inc)- : Has_Block(pstate, b),- variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)- { statement_type(FOR); }- ATTACH_OPERATIONS()- };-- //////////////////////////////////////- // The Sass `@each` control directive.- //////////////////////////////////////- class Each : public Has_Block {- ADD_PROPERTY(std::vector<std::string>, variables)- ADD_PROPERTY(Expression*, list)- public:- Each(ParserState pstate, std::vector<std::string> vars, Expression* lst, Block* b)- : Has_Block(pstate, b), variables_(vars), list_(lst)- { statement_type(EACH); }- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////- // The Sass `@while` control directive.- ///////////////////////////////////////- class While : public Has_Block {- ADD_PROPERTY(Expression*, predicate)- public:- While(ParserState pstate, Expression* pred, Block* b)- : Has_Block(pstate, b), predicate_(pred)- { statement_type(WHILE); }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////- // The @return directive for use inside SassScript functions.- /////////////////////////////////////////////////////////////- class Return : public Statement {- ADD_PROPERTY(Expression*, value)- public:- Return(ParserState pstate, Expression* val)- : Statement(pstate), value_(val)- { statement_type(RETURN); }- ATTACH_OPERATIONS()- };-- ////////////////////////////////- // The Sass `@extend` directive.- ////////////////////////////////- class Extension : public Statement {- ADD_PROPERTY(Selector*, selector)- public:- Extension(ParserState pstate, Selector* s)- : Statement(pstate), selector_(s)- { statement_type(EXTEND); }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////////////- // Definitions for both mixins and functions. The two cases are distinguished- // by a type tag.- /////////////////////////////////////////////////////////////////////////////- struct Backtrace;- typedef Environment<AST_Node*> Env;- typedef const char* Signature;- typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);- typedef const char* Signature;- class Definition : public Has_Block {- public:- enum Type { MIXIN, FUNCTION };- ADD_PROPERTY(std::string, name)- ADD_PROPERTY(Parameters*, parameters)- ADD_PROPERTY(Env*, environment)- ADD_PROPERTY(Type, type)- ADD_PROPERTY(Native_Function, native_function)- ADD_PROPERTY(Sass_Function_Entry, c_function)- ADD_PROPERTY(void*, cookie)- ADD_PROPERTY(bool, is_overload_stub)- ADD_PROPERTY(Signature, signature)- public:- Definition(ParserState pstate,- std::string n,- Parameters* params,- Block* b,- Type t)- : Has_Block(pstate, b),- name_(n),- parameters_(params),- environment_(0),- type_(t),- native_function_(0),- c_function_(0),- cookie_(0),- is_overload_stub_(false),- signature_(0)- { }- Definition(ParserState pstate,- Signature sig,- std::string n,- Parameters* params,- Native_Function func_ptr,- bool overload_stub = false)- : Has_Block(pstate, 0),- name_(n),- parameters_(params),- environment_(0),- type_(FUNCTION),- native_function_(func_ptr),- c_function_(0),- cookie_(0),- is_overload_stub_(overload_stub),- signature_(sig)- { }- Definition(ParserState pstate,- Signature sig,- std::string n,- Parameters* params,- Sass_Function_Entry c_func,- bool whatever,- bool whatever2)- : Has_Block(pstate, 0),- name_(n),- parameters_(params),- environment_(0),- type_(FUNCTION),- native_function_(0),- c_function_(c_func),- cookie_(sass_function_get_cookie(c_func)),- is_overload_stub_(false),- signature_(sig)- { }- ATTACH_OPERATIONS()- };-- //////////////////////////////////////- // Mixin calls (i.e., `@include ...`).- //////////////////////////////////////- class Mixin_Call : public Has_Block {- ADD_PROPERTY(std::string, name)- ADD_PROPERTY(Arguments*, arguments)- public:- Mixin_Call(ParserState pstate, std::string n, Arguments* args, Block* b = 0)- : Has_Block(pstate, b), name_(n), arguments_(args)- { }- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////- // The @content directive for mixin content blocks.- ///////////////////////////////////////////////////- class Content : public Statement {- public:- Content(ParserState pstate) : Statement(pstate)- { statement_type(CONTENT); }- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // Lists of values, both comma- and space-separated (distinguished by a- // type-tag.) Also used to represent variable-length argument lists.- ///////////////////////////////////////////////////////////////////////- class List : public Value, public Vectorized<Expression*> {- void adjust_after_pushing(Expression* e) { is_expanded(false); }- private:- ADD_PROPERTY(enum Sass_Separator, separator)- ADD_PROPERTY(bool, is_arglist)- public:- List(ParserState pstate,- size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false)- : Value(pstate),- Vectorized<Expression*>(size),- separator_(sep), is_arglist_(argl)- { concrete_type(LIST); }- std::string type() { return is_arglist_ ? "arglist" : "list"; }- static std::string type_name() { return "list"; }- const char* sep_string(bool compressed = false) const {- return separator() == SASS_COMMA ?- (compressed ? "," : ", ") : " ";- }- bool is_invisible() const { return empty(); }- Expression* value_at_index(size_t i);-- virtual size_t size() const;-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(sep_string());- for (size_t i = 0, L = length(); i < L; ++i)- hash_combine(hash_, (elements()[i])->hash());- }- return hash_;- }-- virtual void set_delayed(bool delayed)- {- for (size_t i = 0, L = length(); i < L; ++i)- (elements()[i])->set_delayed(delayed);- is_delayed(delayed);- }-- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // Key value paris.- ///////////////////////////////////////////////////////////////////////- class Map : public Value, public Hashed {- void adjust_after_pushing(std::pair<Expression*, Expression*> p) { is_expanded(false); }- public:- Map(ParserState pstate,- size_t size = 0)- : Value(pstate),- Hashed(size)- { concrete_type(MAP); }- std::string type() { return "map"; }- static std::string type_name() { return "map"; }- bool is_invisible() const { return empty(); }-- virtual size_t hash()- {- if (hash_ == 0) {- for (auto key : keys()) {- hash_combine(hash_, key->hash());- hash_combine(hash_, at(key)->hash());- }- }-- return hash_;- }-- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- //////////////////////////////////////////////////////////////////////////- // Binary expressions. Represents logical, relational, and arithmetic- // operations. Templatized to avoid large switch statements and repetitive- // subclassing.- //////////////////////////////////////////////////////////////////////////- class Binary_Expression : public Expression {- private:- ADD_HASHED(enum Sass_OP, type)- ADD_HASHED(Expression*, left)- ADD_HASHED(Expression*, right)- size_t hash_;- public:- Binary_Expression(ParserState pstate,- enum Sass_OP t, Expression* lhs, Expression* rhs)- : Expression(pstate), type_(t), left_(lhs), right_(rhs), hash_(0)- { }- const std::string type_name() {- switch (type_) {- case AND: return "and"; break;- case OR: return "or"; break;- case EQ: return "eq"; break;- case NEQ: return "neq"; break;- case GT: return "gt"; break;- case GTE: return "gte"; break;- case LT: return "lt"; break;- case LTE: return "lte"; break;- case ADD: return "add"; break;- case SUB: return "sub"; break;- case MUL: return "mul"; break;- case DIV: return "div"; break;- case MOD: return "mod"; break;- case NUM_OPS: return "num_ops"; break;- default: return "invalid"; break;- }- }- virtual void set_delayed(bool delayed)- {- right()->set_delayed(delayed);- left()->set_delayed(delayed);- is_delayed(delayed);- }- virtual bool operator==(const Expression& rhs) const- {- try- {- const Binary_Expression* m = dynamic_cast<const Binary_Expression*>(&rhs);- if (m == 0) return false;- return type() == m->type() &&- left() == m->left() &&- right() == m->right();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<size_t>()(type_);- hash_combine(hash_, left()->hash());- hash_combine(hash_, right()->hash());- }- return hash_;- }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // Arithmetic negation (logical negation is just an ordinary function call).- ////////////////////////////////////////////////////////////////////////////- class Unary_Expression : public Expression {- public:- enum Type { PLUS, MINUS, NOT };- private:- ADD_HASHED(Type, type)- ADD_HASHED(Expression*, operand)- size_t hash_;- public:- Unary_Expression(ParserState pstate, Type t, Expression* o)- : Expression(pstate), type_(t), operand_(o), hash_(0)- { }- const std::string type_name() {- switch (type_) {- case PLUS: return "plus"; break;- case MINUS: return "minus"; break;- case NOT: return "not"; break;- default: return "invalid"; break;- }- }- virtual bool operator==(const Expression& rhs) const- {- try- {- const Unary_Expression* m = dynamic_cast<const Unary_Expression*>(&rhs);- if (m == 0) return false;- return type() == m->type() &&- operand() == m->operand();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<size_t>()(type_);- hash_combine(hash_, operand()->hash());- };- return hash_;- }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////- // Individual argument objects for mixin and function calls.- ////////////////////////////////////////////////////////////- class Argument : public Expression {- ADD_HASHED(Expression*, value)- ADD_HASHED(std::string, name)- ADD_PROPERTY(bool, is_rest_argument)- ADD_PROPERTY(bool, is_keyword_argument)- size_t hash_;- public:- Argument(ParserState pstate, Expression* val, std::string n = "", bool rest = false, bool keyword = false)- : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)- {- if (!name_.empty() && is_rest_argument_) {- error("variable-length argument may not be passed by name", pstate);- }- }-- virtual bool operator==(const Expression& rhs) const- {- try- {- const Argument* m = dynamic_cast<const Argument*>(&rhs);- if (!(m && name() == m->name())) return false;- return *value() == *m->value();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(name());- hash_combine(hash_, value()->hash());- }- return hash_;- }-- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Argument lists -- in their own class to facilitate context-sensitive- // error checking (e.g., ensuring that all ordinal arguments precede all- // named arguments).- ////////////////////////////////////////////////////////////////////////- class Arguments : public Expression, public Vectorized<Argument*> {- ADD_PROPERTY(bool, has_named_arguments)- ADD_PROPERTY(bool, has_rest_argument)- ADD_PROPERTY(bool, has_keyword_argument)- protected:- void adjust_after_pushing(Argument* a);- public:- Arguments(ParserState pstate)- : Expression(pstate),- Vectorized<Argument*>(),- has_named_arguments_(false),- has_rest_argument_(false),- has_keyword_argument_(false)- { }- ATTACH_OPERATIONS()- };-- //////////////////- // Function calls.- //////////////////- class Function_Call : public Expression {- ADD_HASHED(std::string, name)- ADD_HASHED(Arguments*, arguments)- ADD_PROPERTY(void*, cookie)- size_t hash_;- public:- Function_Call(ParserState pstate, std::string n, Arguments* args, void* cookie)- : Expression(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0)- { concrete_type(STRING); }- Function_Call(ParserState pstate, std::string n, Arguments* args)- : Expression(pstate), name_(n), arguments_(args), cookie_(0), hash_(0)- { concrete_type(STRING); }-- virtual bool operator==(const Expression& rhs) const- {- try- {- const Function_Call* m = dynamic_cast<const Function_Call*>(&rhs);- if (!(m && name() == m->name())) return false;- if (!(m && arguments()->length() == m->arguments()->length())) return false;- for (size_t i =0, L = arguments()->length(); i < L; ++i)- if (!((*arguments())[i] == (*m->arguments())[i])) return false;- return true;- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(name());- for (auto argument : arguments()->elements())- hash_combine(hash_, argument->hash());- }- return hash_;- }-- ATTACH_OPERATIONS()- };-- /////////////////////////- // Function call schemas.- /////////////////////////- class Function_Call_Schema : public Expression {- ADD_PROPERTY(String*, name)- ADD_PROPERTY(Arguments*, arguments)- public:- Function_Call_Schema(ParserState pstate, String* n, Arguments* args)- : Expression(pstate), name_(n), arguments_(args)- { concrete_type(STRING); }- ATTACH_OPERATIONS()- };-- ///////////////////////- // Variable references.- ///////////////////////- class Variable : public Expression {- ADD_PROPERTY(std::string, name)- public:- Variable(ParserState pstate, std::string n)- : Expression(pstate), name_(n)- { }-- virtual bool operator==(const Expression& rhs) const- {- try- {- const Variable* e = dynamic_cast<const Variable*>(&rhs);- return e && name() == e->name();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- return std::hash<std::string>()(name());- }-- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // Textual (i.e., unevaluated) numeric data. Variants are distinguished with- // a type tag.- ////////////////////////////////////////////////////////////////////////////- class Textual : public Expression {- public:- enum Type { NUMBER, PERCENTAGE, DIMENSION, HEX };- private:- ADD_HASHED(Type, type)- ADD_HASHED(std::string, value)- size_t hash_;- public:- Textual(ParserState pstate, Type t, std::string val)- : Expression(pstate, true), type_(t), value_(val),- hash_(0)- { }-- virtual bool operator==(const Expression& rhs) const- {- try- {- const Textual* e = dynamic_cast<const Textual*>(&rhs);- return e && value() == e->value() && type() == e->type();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(value_);- hash_combine(hash_, std::hash<int>()(type_));- }- return hash_;- }-- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////- // Numbers, percentages, dimensions, and colors.- ////////////////////////////////////////////////- class Number : public Value {- ADD_HASHED(double, value)- ADD_PROPERTY(bool, zero)- std::vector<std::string> numerator_units_;- std::vector<std::string> denominator_units_;- size_t hash_;- public:- Number(ParserState pstate, double val, std::string u = "", bool zero = true);- bool zero() { return zero_; }- std::vector<std::string>& numerator_units() { return numerator_units_; }- std::vector<std::string>& denominator_units() { return denominator_units_; }- const std::vector<std::string>& numerator_units() const { return numerator_units_; }- const std::vector<std::string>& denominator_units() const { return denominator_units_; }- std::string type() { return "number"; }- static std::string type_name() { return "number"; }- std::string unit() const;-- bool is_unitless();- void convert(const std::string& unit = "", bool strict = false);- void normalize(const std::string& unit = "", bool strict = false);- // useful for making one number compatible with another- std::string find_convertible_unit() const;-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<double>()(value_);- for (const auto numerator : numerator_units())- hash_combine(hash_, std::hash<std::string>()(numerator));- for (const auto denominator : denominator_units())- hash_combine(hash_, std::hash<std::string>()(denominator));- }- return hash_;- }-- virtual bool operator< (const Number& rhs) const;- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- //////////- // Colors.- //////////- class Color : public Value {- ADD_HASHED(double, r)- ADD_HASHED(double, g)- ADD_HASHED(double, b)- ADD_HASHED(double, a)- ADD_PROPERTY(bool, sixtuplet)- ADD_PROPERTY(std::string, disp)- size_t hash_;- public:- Color(ParserState pstate, double r, double g, double b, double a = 1, bool sixtuplet = true, const std::string disp = "")- : Value(pstate), r_(r), g_(g), b_(b), a_(a), sixtuplet_(sixtuplet), disp_(disp),- hash_(0)- { concrete_type(COLOR); }- std::string type() { return "color"; }- static std::string type_name() { return "color"; }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<double>()(a_);- hash_combine(hash_, std::hash<double>()(r_));- hash_combine(hash_, std::hash<double>()(g_));- hash_combine(hash_, std::hash<double>()(b_));- }- return hash_;- }-- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- //////////////////////////////- // Errors from Sass_Values.- //////////////////////////////- class Custom_Error : public Value {- ADD_PROPERTY(std::string, message)- public:- Custom_Error(ParserState pstate, std::string msg)- : Value(pstate), message_(msg)- { concrete_type(C_ERROR); }- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- //////////////////////////////- // Warnings from Sass_Values.- //////////////////////////////- class Custom_Warning : public Value {- ADD_PROPERTY(std::string, message)- public:- Custom_Warning(ParserState pstate, std::string msg)- : Value(pstate), message_(msg)- { concrete_type(C_WARNING); }- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- ////////////- // Booleans.- ////////////- class Boolean : public Value {- ADD_HASHED(bool, value)- size_t hash_;- public:- Boolean(ParserState pstate, bool val)- : Value(pstate), value_(val),- hash_(0)- { concrete_type(BOOLEAN); }- virtual operator bool() { return value_; }- std::string type() { return "bool"; }- static std::string type_name() { return "bool"; }- virtual bool is_false() { return !value_; }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<bool>()(value_);- }- return hash_;- }-- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Abstract base class for Sass string values. Includes interpolated and- // "flat" strings.- ////////////////////////////////////////////////////////////////////////- class String : public Value {- ADD_PROPERTY(bool, sass_fix_1291)- public:- String(ParserState pstate, bool delayed = false, bool sass_fix_1291 = false)- : Value(pstate, delayed), sass_fix_1291_(sass_fix_1291)- { concrete_type(STRING); }- static std::string type_name() { return "string"; }- virtual ~String() = 0;- virtual bool operator==(const Expression& rhs) const = 0;- virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;- ATTACH_OPERATIONS()- };- inline String::~String() { };-- ///////////////////////////////////////////////////////////////////////- // Interpolated strings. Meant to be reduced to flat strings during the- // evaluation phase.- ///////////////////////////////////////////////////////////////////////- class String_Schema : public String, public Vectorized<Expression*> {- ADD_PROPERTY(bool, has_interpolants)- size_t hash_;- public:- String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)- : String(pstate), Vectorized<Expression*>(size), has_interpolants_(has_interpolants), hash_(0)- { concrete_type(STRING); }- std::string type() { return "string"; }- static std::string type_name() { return "string"; }-- virtual size_t hash()- {- if (hash_ == 0) {- for (auto string : elements())- hash_combine(hash_, string->hash());- }- return hash_;- }-- virtual bool operator==(const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////- // Flat strings -- the lowest level of raw textual data.- ////////////////////////////////////////////////////////- class String_Constant : public String {- ADD_PROPERTY(char, quote_mark)- ADD_PROPERTY(bool, can_compress_whitespace)- ADD_HASHED(std::string, value)- protected:- size_t hash_;- public:- String_Constant(ParserState pstate, std::string val)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)- { }- String_Constant(ParserState pstate, const char* beg)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0)- { }- String_Constant(ParserState pstate, const char* beg, const char* end)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0)- { }- String_Constant(ParserState pstate, const Token& tok)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)- { }- std::string type() { return "string"; }- static std::string type_name() { return "string"; }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(value_);- }- return hash_;- }-- virtual bool operator==(const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- // static char auto_quote() { return '*'; }- static char double_quote() { return '"'; }- static char single_quote() { return '\''; }-- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////- // Possibly quoted string (unquote on instantiation)- ////////////////////////////////////////////////////////- class String_Quoted : public String_Constant {- public:- String_Quoted(ParserState pstate, std::string val, char q = 0, bool keep_utf8_escapes = false)- : String_Constant(pstate, val)- {- value_ = unquote(value_, "e_mark_, keep_utf8_escapes);- if (q && quote_mark_) quote_mark_ = q;- }- virtual bool operator==(const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- /////////////////- // Media queries.- /////////////////- class Media_Query : public Expression,- public Vectorized<Media_Query_Expression*> {- ADD_PROPERTY(String*, media_type)- ADD_PROPERTY(bool, is_negated)- ADD_PROPERTY(bool, is_restricted)- public:- Media_Query(ParserState pstate,- String* t = 0, size_t s = 0, bool n = false, bool r = false)- : Expression(pstate), Vectorized<Media_Query_Expression*>(s),- media_type_(t), is_negated_(n), is_restricted_(r)- { }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////- // Media expressions (for use inside media queries).- ////////////////////////////////////////////////////- class Media_Query_Expression : public Expression {- ADD_PROPERTY(Expression*, feature)- ADD_PROPERTY(Expression*, value)- ADD_PROPERTY(bool, is_interpolated)- public:- Media_Query_Expression(ParserState pstate,- Expression* f, Expression* v, bool i = false)- : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)- { }- ATTACH_OPERATIONS()- };-- ////////////////////- // `@supports` rule.- ////////////////////- class Supports_Block : public Has_Block {- ADD_PROPERTY(Supports_Condition*, condition)- public:- Supports_Block(ParserState pstate, Supports_Condition* condition, Block* block = 0)- : Has_Block(pstate, block), condition_(condition)- { statement_type(SUPPORTS); }- bool is_hoistable() { return true; }- bool bubbles() { return true; }- ATTACH_OPERATIONS()- };-- //////////////////////////////////////////////////////- // The abstract superclass of all Supports conditions.- //////////////////////////////////////////////////////- class Supports_Condition : public Expression {- public:- Supports_Condition(ParserState pstate)- : Expression(pstate)- { }- virtual bool needs_parens(Supports_Condition* cond) const { return false; }- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////- // An operator condition (e.g. `CONDITION1 and CONDITION2`).- ////////////////////////////////////////////////////////////- class Supports_Operator : public Supports_Condition {- public:- enum Operand { AND, OR };- private:- ADD_PROPERTY(Supports_Condition*, left);- ADD_PROPERTY(Supports_Condition*, right);- ADD_PROPERTY(Operand, operand);- public:- Supports_Operator(ParserState pstate, Supports_Condition* l, Supports_Condition* r, Operand o)- : Supports_Condition(pstate), left_(l), right_(r), operand_(o)- { }- virtual bool needs_parens(Supports_Condition* cond) const;- ATTACH_OPERATIONS()- };-- //////////////////////////////////////////- // A negation condition (`not CONDITION`).- //////////////////////////////////////////- class Supports_Negation : public Supports_Condition {- private:- ADD_PROPERTY(Supports_Condition*, condition);- public:- Supports_Negation(ParserState pstate, Supports_Condition* c)- : Supports_Condition(pstate), condition_(c)- { }- virtual bool needs_parens(Supports_Condition* cond) const;- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////- // A declaration condition (e.g. `(feature: value)`).- /////////////////////////////////////////////////////- class Supports_Declaration : public Supports_Condition {- private:- ADD_PROPERTY(Expression*, feature);- ADD_PROPERTY(Expression*, value);- public:- Supports_Declaration(ParserState pstate, Expression* f, Expression* v)- : Supports_Condition(pstate), feature_(f), value_(v)- { }- virtual bool needs_parens(Supports_Condition* cond) const { return false; }- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////- // An interpolation condition (e.g. `#{$var}`).- ///////////////////////////////////////////////- class Supports_Interpolation : public Supports_Condition {- private:- ADD_PROPERTY(Expression*, value);- public:- Supports_Interpolation(ParserState pstate, Expression* v)- : Supports_Condition(pstate), value_(v)- { }- virtual bool needs_parens(Supports_Condition* cond) const { return false; }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////- // At root expressions (for use inside @at-root).- /////////////////////////////////////////////////- class At_Root_Expression : public Expression {- private:- ADD_PROPERTY(String*, feature)- ADD_PROPERTY(Expression*, value)- ADD_PROPERTY(bool, is_interpolated)- public:- At_Root_Expression(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false)- : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)- { }- bool exclude(std::string str)- {- To_String to_string;- bool with = feature() && unquote(feature()->perform(&to_string)).compare("with") == 0;- List* l = static_cast<List*>(value());- std::string v;-- if (with)- {- if (!l || l->length() == 0) return str.compare("rule") != 0;- for (size_t i = 0, L = l->length(); i < L; ++i)- {- v = unquote((*l)[i]->perform(&to_string));- if (v.compare("all") == 0 || v == str) return false;- }- return true;- }- else- {- if (!l || !l->length()) return str.compare("rule") == 0;- for (size_t i = 0, L = l->length(); i < L; ++i)- {- v = unquote((*l)[i]->perform(&to_string));- if (v.compare("all") == 0 || v == str) return true;- }- return false;- }- }- ATTACH_OPERATIONS()- };-- ///////////- // At-root.- ///////////- class At_Root_Block : public Has_Block {- ADD_PROPERTY(At_Root_Expression*, expression)- public:- At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Expression* e = 0)- : Has_Block(pstate, b), expression_(e)- { statement_type(ATROOT); }- bool is_hoistable() { return true; }- bool bubbles() { return true; }- bool exclude_node(Statement* s) {- if (s->statement_type() == Statement::DIRECTIVE)- {- return expression()->exclude(static_cast<At_Rule*>(s)->keyword().erase(0, 1));- }- if (s->statement_type() == Statement::MEDIA)- {- return expression()->exclude("media");- }- if (s->statement_type() == Statement::RULESET)- {- return expression()->exclude("rule");- }- if (s->statement_type() == Statement::SUPPORTS)- {- return expression()->exclude("supports");- }- if (static_cast<At_Rule*>(s)->is_keyframes())- {- return expression()->exclude("keyframes");- }- return false;- }- ATTACH_OPERATIONS()- };-- //////////////////- // The null value.- //////////////////- class Null : public Value {- public:- Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); }- std::string type() { return "null"; }- static std::string type_name() { return "null"; }- bool is_invisible() const { return true; }- operator bool() { return false; }- bool is_false() { return true; }-- virtual size_t hash()- {- return -1;- }-- virtual bool operator== (const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;-- ATTACH_OPERATIONS()- };-- /////////////////////////////////- // Thunks for delayed evaluation.- /////////////////////////////////- class Thunk : public Expression {- ADD_PROPERTY(Expression*, expression)- ADD_PROPERTY(Env*, environment)- public:- Thunk(ParserState pstate, Expression* exp, Env* env = 0)- : Expression(pstate), expression_(exp), environment_(env)- { }- };-- /////////////////////////////////////////////////////////- // Individual parameter objects for mixins and functions.- /////////////////////////////////////////////////////////- class Parameter : public AST_Node {- ADD_PROPERTY(std::string, name)- ADD_PROPERTY(Expression*, default_value)- ADD_PROPERTY(bool, is_rest_parameter)- public:- Parameter(ParserState pstate,- std::string n, Expression* def = 0, bool rest = false)- : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)- {- if (default_value_ && is_rest_parameter_) {- error("variable-length parameter may not have a default value", pstate);- }- }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////////- // Parameter lists -- in their own class to facilitate context-sensitive- // error checking (e.g., ensuring that all optional parameters follow all- // required parameters).- /////////////////////////////////////////////////////////////////////////- class Parameters : public AST_Node, public Vectorized<Parameter*> {- ADD_PROPERTY(bool, has_optional_parameters)- ADD_PROPERTY(bool, has_rest_parameter)- protected:- void adjust_after_pushing(Parameter* p)- {- if (p->default_value()) {- if (has_rest_parameter_) {- error("optional parameters may not be combined with variable-length parameters", p->pstate());- }- has_optional_parameters_ = true;- }- else if (p->is_rest_parameter()) {- if (has_rest_parameter_) {- error("functions and mixins cannot have more than one variable-length parameter", p->pstate());- }- has_rest_parameter_ = true;- }- else {- if (has_rest_parameter_) {- error("required parameters must precede variable-length parameters", p->pstate());- }- if (has_optional_parameters_) {- error("required parameters must precede optional parameters", p->pstate());- }- }- }- public:- Parameters(ParserState pstate)- : AST_Node(pstate),- Vectorized<Parameter*>(),- has_optional_parameters_(false),- has_rest_parameter_(false)- { }- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////- // Abstract base class for CSS selectors.- /////////////////////////////////////////- class Selector : public Expression {- ADD_PROPERTY(bool, has_reference)- ADD_PROPERTY(bool, has_placeholder)- // line break before list separator- ADD_PROPERTY(bool, has_line_feed)- // line break after list separator- ADD_PROPERTY(bool, has_line_break)- // maybe we have optional flag- ADD_PROPERTY(bool, is_optional)- // parent block pointers- ADD_PROPERTY(Media_Block*, media_block)- public:- Selector(ParserState pstate, bool r = false, bool h = false)- : Expression(pstate),- has_reference_(r),- has_placeholder_(h),- has_line_feed_(false),- has_line_break_(false),- is_optional_(false),- media_block_(0)- { concrete_type(SELECTOR); }- virtual ~Selector() = 0;- virtual unsigned long specificity() {- return Constants::Specificity_Universal;- }- virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;- };- inline Selector::~Selector() { }-- /////////////////////////////////////////////////////////////////////////- // Interpolated selectors -- the interpolated String will be expanded and- // re-parsed into a normal selector class.- /////////////////////////////////////////////////////////////////////////- class Selector_Schema : public Selector {- ADD_PROPERTY(String*, contents)- ADD_PROPERTY(bool, at_root);- public:- Selector_Schema(ParserState pstate, String* c)- : Selector(pstate), contents_(c), at_root_(false)- { }- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////- // Abstract base class for simple selectors.- ////////////////////////////////////////////- class Simple_Selector : public Selector {- ADD_PROPERTY(std::string, ns);- ADD_PROPERTY(std::string, name)- ADD_PROPERTY(bool, has_ns)- public:- Simple_Selector(ParserState pstate, std::string n = "")- : Selector(pstate), ns_(""), name_(n), has_ns_(false)- {- size_t pos = n.find('|');- // found some namespace- if (pos != std::string::npos) {- has_ns_ = true;- ns_ = n.substr(0, pos);- name_ = n.substr(pos + 1);- }- }- virtual std::string ns_name() const- {- std::string name("");- if (has_ns_)- name += ns_ + "|";- return name + name_;- }- // namespace query functions- bool is_universal_ns() const- {- return has_ns_ && ns_ == "*";- }- bool has_universal_ns() const- {- return !has_ns_ || ns_ == "*";- }- bool is_empty_ns() const- {- return !has_ns_ || ns_ == "";- }- bool has_empty_ns() const- {- return has_ns_ && ns_ == "";- }- bool has_qualified_ns() const- {- return has_ns_ && ns_ != "" && ns_ != "*";- }- // name query functions- bool is_universal() const- {- return name_ == "*";- }-- virtual ~Simple_Selector() = 0;- virtual Compound_Selector* unify_with(Compound_Selector*, Context&);- virtual bool has_parent_ref() { return false; };- virtual bool is_pseudo_element() { return false; }- virtual bool is_pseudo_class() { return false; }-- virtual bool is_superselector_of(Compound_Selector* sub) { return false; }-- bool operator==(const Simple_Selector& rhs) const;- inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); }-- bool operator<(const Simple_Selector& rhs) const;- // default implementation should work for most of the simple selectors (otherwise overload)- virtual std::string to_string(bool compressed = false, int precision = 5) const { return this->ns_name(); };- ATTACH_OPERATIONS();- };- inline Simple_Selector::~Simple_Selector() { }--- //////////////////////////////////- // The Parent Selector Expression.- //////////////////////////////////- // parent selectors can occur in selectors but also- // inside strings in declarations (Compound_Selector).- // only one simple parent selector means the first case.- class Parent_Selector : public Simple_Selector {- public:- Parent_Selector(ParserState pstate)- : Simple_Selector(pstate, "&")- { has_reference(true); }- virtual bool has_parent_ref() { return true; };- virtual unsigned long specificity()- {- return 0;- }- std::string type() { return "selector"; }- static std::string type_name() { return "selector"; }- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////////- // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.- /////////////////////////////////////////////////////////////////////////- class Selector_Placeholder : public Simple_Selector {- public:- Selector_Placeholder(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { has_placeholder(true); }- // virtual Selector_Placeholder* find_placeholder();- virtual ~Selector_Placeholder() {};- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////- // Type selectors (and the universal selector) -- e.g., div, span, *.- /////////////////////////////////////////////////////////////////////- class Type_Selector : public Simple_Selector {- public:- Type_Selector(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { }- virtual unsigned long specificity()- {- // ToDo: What is the specificity of the star selector?- if (name() == "*") return Constants::Specificity_Universal;- else return Constants::Specificity_Type;- }- virtual Simple_Selector* unify_with(Simple_Selector*, Context&);- virtual Compound_Selector* unify_with(Compound_Selector*, Context&);- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////- // Selector qualifiers -- i.e., classes and ids.- ////////////////////////////////////////////////- class Selector_Qualifier : public Simple_Selector {- public:- Selector_Qualifier(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { }- virtual unsigned long specificity()- {- if (name()[0] == '#') return Constants::Specificity_ID;- if (name()[0] == '.') return Constants::Specificity_Class;- else return Constants::Specificity_Type;- }- virtual Compound_Selector* unify_with(Compound_Selector*, Context&);- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////- // Attribute selectors -- e.g., [src*=".jpg"], etc.- ///////////////////////////////////////////////////- class Attribute_Selector : public Simple_Selector {- ADD_PROPERTY(std::string, matcher)- ADD_PROPERTY(String*, value) // might be interpolated- public:- Attribute_Selector(ParserState pstate, std::string n, std::string m, String* v)- : Simple_Selector(pstate, n), matcher_(m), value_(v)- { }- virtual unsigned long specificity()- {- return Constants::Specificity_Attr;- }- bool operator==(const Simple_Selector& rhs) const;- bool operator==(const Attribute_Selector& rhs) const;- bool operator<(const Simple_Selector& rhs) const;- bool operator<(const Attribute_Selector& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- //////////////////////////////////////////////////////////////////- // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.- //////////////////////////////////////////////////////////////////- /* '::' starts a pseudo-element, ':' a pseudo-class */- /* Except :first-line, :first-letter, :before and :after */- /* Note that pseudo-elements are restricted to one per selector */- /* and occur only in the last simple_selector_sequence. */- inline bool is_pseudo_class_element(const std::string& name)- {- return name == ":before" ||- name == ":after" ||- name == ":first-line" ||- name == ":first-letter";- }-- class Pseudo_Selector : public Simple_Selector {- ADD_PROPERTY(String*, expression)- public:- Pseudo_Selector(ParserState pstate, std::string n, String* expr = 0)- : Simple_Selector(pstate, n), expression_(expr)- { }-- // A pseudo-class always consists of a "colon" (:) followed by the name- // of the pseudo-class and optionally by a value between parentheses.- virtual bool is_pseudo_class()- {- return (name_[0] == ':' && name_[1] != ':')- && ! is_pseudo_class_element(name_);- }-- // A pseudo-element is made of two colons (::) followed by the name.- // The `::` notation is introduced by the current document in order to- // establish a discrimination between pseudo-classes and pseudo-elements.- // For compatibility with existing style sheets, user agents must also- // accept the previous one-colon notation for pseudo-elements introduced- // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and- // :after). This compatibility is not allowed for the new pseudo-elements- // introduced in this specification.- virtual bool is_pseudo_element()- {- return (name_[0] == ':' && name_[1] == ':')- || is_pseudo_class_element(name_);- }- virtual unsigned long specificity()- {- if (is_pseudo_element())- return Constants::Specificity_Type;- return Constants::Specificity_Pseudo;- }- virtual Compound_Selector* unify_with(Compound_Selector*, Context&);- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////- // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir)- /////////////////////////////////////////////////- class Wrapped_Selector : public Simple_Selector {- ADD_PROPERTY(Selector*, selector)- public:- Wrapped_Selector(ParserState pstate, std::string n, Selector* sel)- : Simple_Selector(pstate, n), selector_(sel)- { }- virtual bool is_superselector_of(Wrapped_Selector* sub);- // Selectors inside the negation pseudo-class are counted like any- // other, but the negation itself does not count as a pseudo-class.- virtual unsigned long specificity()- {- return selector_ ? selector_->specificity() : 0;- }- bool operator==(const Simple_Selector& rhs) const;- bool operator==(const Wrapped_Selector& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- struct Complex_Selector_Pointer_Compare {- bool operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const;- };-- ////////////////////////////////////////////////////////////////////////////- // Simple selector sequences. Maintains flags indicating whether it contains- // any parent references or placeholders, to simplify expansion.- ////////////////////////////////////////////////////////////////////////////- typedef std::set<Complex_Selector*, Complex_Selector_Pointer_Compare> SourcesSet;- class Compound_Selector : public Selector, public Vectorized<Simple_Selector*> {- private:- SourcesSet sources_;- ADD_PROPERTY(bool, has_parent_reference);- protected:- void adjust_after_pushing(Simple_Selector* s)- {- if (s->has_reference()) has_reference(true);- if (s->has_placeholder()) has_placeholder(true);- }- public:- Compound_Selector(ParserState pstate, size_t s = 0)- : Selector(pstate),- Vectorized<Simple_Selector*>(s),- has_parent_reference_(false)- { }- bool contains_placeholder() {- for (size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->has_placeholder()) return true;- }- return false;- };-- bool is_universal() const- {- return length() == 1 && (*this)[0]->is_universal();- }-- Complex_Selector* to_complex(Memory_Manager& mem);- Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx);- // virtual Selector_Placeholder* find_placeholder();- virtual bool has_parent_ref();- Simple_Selector* base()- {- // Implement non-const in terms of const. Safe to const_cast since this method is non-const- return const_cast<Simple_Selector*>(static_cast<const Compound_Selector*>(this)->base());- }- const Simple_Selector* base() const {- if (length() == 0) return 0;- if (typeid(*(*this)[0]) == typeid(Type_Selector))- return (*this)[0];-// else cerr << "SERIOUSELY " << "\n";- return 0;- }- virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapped = "");- virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapped = "");- virtual bool is_superselector_of(Selector_List* sub, std::string wrapped = "");- virtual unsigned long specificity()- {- int sum = 0;- for (size_t i = 0, L = length(); i < L; ++i)- { sum += (*this)[i]->specificity(); }- return sum;- }-- bool is_empty_reference()- {- return length() == 1 &&- typeid(*(*this)[0]) == typeid(Parent_Selector);- }- std::vector<std::string> to_str_vec(); // sometimes need to convert to a flat "by-value" data structure-- bool operator<(const Compound_Selector& rhs) const;-- bool operator==(const Compound_Selector& rhs) const;- inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); }-- SourcesSet& sources() { return sources_; }- void clearSources() { sources_.clear(); }- void mergeSources(SourcesSet& sources, Context& ctx);-- Compound_Selector* clone(Context&) const; // does not clone the Simple_Selector*s-- Compound_Selector* minus(Compound_Selector* rhs, Context& ctx);- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // General selectors -- i.e., simple sequences combined with one of the four- // CSS selector combinators (">", "+", "~", and whitespace). Essentially a- // linked list.- ////////////////////////////////////////////////////////////////////////////- class Complex_Selector : public Selector {- public:- enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE };- private:- ADD_PROPERTY(Combinator, combinator)- ADD_PROPERTY(Compound_Selector*, head)- ADD_PROPERTY(Complex_Selector*, tail)- ADD_PROPERTY(String*, reference);- public:- bool contains_placeholder() {- if (head() && head()->contains_placeholder()) return true;- if (tail() && tail()->contains_placeholder()) return true;- return false;- };- Complex_Selector(ParserState pstate,- Combinator c = ANCESTOR_OF,- Compound_Selector* h = 0,- Complex_Selector* t = 0,- String* r = 0)- : Selector(pstate),- combinator_(c),- head_(h), tail_(t),- reference_(r)- {- if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true);- if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true);- }- virtual bool has_parent_ref();-- Complex_Selector* skip_empty_reference()- {- if ((!head_ || !head_->length() || head_->is_empty_reference()) &&- combinator() == Combinator::ANCESTOR_OF)- {- if (!tail_) return 0;- tail_->has_line_feed_ = this->has_line_feed_;- // tail_->has_line_break_ = this->has_line_break_;- return tail_->skip_empty_reference();- }- return this;- }-- // can still have a tail- bool is_empty_ancestor() const- {- return (!head() || head()->length() == 0) &&- combinator() == Combinator::ANCESTOR_OF;- }-- Complex_Selector* context(Context&);--- // front returns the first real tail- // skips over parent and empty ones- const Complex_Selector* first() const;-- // last returns the last real tail- const Complex_Selector* last() const;-- Selector_List* tails(Context& ctx, Selector_List* tails);-- // unconstant accessors- Complex_Selector* first();- Complex_Selector* last();-- // some shortcuts that should be removed- const Complex_Selector* innermost() const { return last(); };- Complex_Selector* innermost() { return last(); };-- size_t length() const;- Selector_List* parentize(Selector_List* parents, Context& ctx);- virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");- virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");- virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");- // virtual Selector_Placeholder* find_placeholder();- Selector_List* unify_with(Complex_Selector* rhs, Context& ctx);- Combinator clear_innermost();- void append(Context&, Complex_Selector*);- void set_innermost(Complex_Selector*, Combinator);- virtual unsigned long specificity() const- {- int sum = 0;- if (head()) sum += head()->specificity();- if (tail()) sum += tail()->specificity();- return sum;- }- bool operator<(const Complex_Selector& rhs) const;- bool operator==(const Complex_Selector& rhs) const;- inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }- SourcesSet sources()- {- //s = Set.new- //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}- //s-- SourcesSet srcs;-- Compound_Selector* pHead = head();- Complex_Selector* pTail = tail();-- if (pHead) {- SourcesSet& headSources = pHead->sources();- srcs.insert(headSources.begin(), headSources.end());- }-- if (pTail) {- SourcesSet tailSources = pTail->sources();- srcs.insert(tailSources.begin(), tailSources.end());- }-- return srcs;- }- void addSources(SourcesSet& sources, Context& ctx) {- // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}- Complex_Selector* pIter = this;- while (pIter) {- Compound_Selector* pHead = pIter->head();-- if (pHead) {- pHead->mergeSources(sources, ctx);- }-- pIter = pIter->tail();- }- }- void clearSources() {- Complex_Selector* pIter = this;- while (pIter) {- Compound_Selector* pHead = pIter->head();-- if (pHead) {- pHead->clearSources();- }-- pIter = pIter->tail();- }- }- Complex_Selector* clone(Context&) const; // does not clone Compound_Selector*s- Complex_Selector* cloneFully(Context&) const; // clones Compound_Selector*s- // std::vector<Compound_Selector*> to_vector();- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- typedef std::deque<Complex_Selector*> ComplexSelectorDeque;- typedef Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;-- ///////////////////////////////////- // Comma-separated selector groups.- ///////////////////////////////////- class Selector_List : public Selector, public Vectorized<Complex_Selector*> {- ADD_PROPERTY(std::vector<std::string>, wspace)- protected:- void adjust_after_pushing(Complex_Selector* c);- public:- Selector_List(ParserState pstate, size_t s = 0)- : Selector(pstate), Vectorized<Complex_Selector*>(s), wspace_(0)- { }- std::string type() { return "list"; }- // remove parent selector references- // basically unwraps parsed selectors- void remove_parent_selectors();- // virtual Selector_Placeholder* find_placeholder();- Selector_List* parentize(Selector_List* parents, Context& ctx);- virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");- virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");- virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");- Selector_List* unify_with(Selector_List*, Context&);- void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&);- virtual unsigned long specificity()- {- unsigned long sum = 0;- unsigned long specificity = 0;- for (size_t i = 0, L = length(); i < L; ++i)- {- specificity = (*this)[i]->specificity();- if (sum < specificity) sum = specificity;- }- return sum;- }- Selector_List* clone(Context&) const; // does not clone Compound_Selector*s- Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s- virtual bool operator==(const Selector& rhs) const;- virtual bool operator==(const Selector_List& rhs) const;- // Selector Lists can be compared to comma lists- virtual bool operator==(const Expression& rhs) const;- virtual std::string to_string(bool compressed = false, int precision = 5) const;- ATTACH_OPERATIONS()- };-- template<typename SelectorType>- bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) {- // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector- // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but- // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting- // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the- // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the- // members of a SimpleSequence (Compound_Selector)).- //- // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for- // use with Compound_Selector and Complex_Selector objects.- if (simpleSelectorOrderDependent) {- return !(one < two) && !(two < one);- } else {- return one == two;- }- }-- // compare function for sorting and probably other other uses- struct cmp_complex_selector { inline bool operator() (const Complex_Selector* l, const Complex_Selector* r) { return (*l < *r); } };- struct cmp_compound_selector { inline bool operator() (const Compound_Selector* l, const Compound_Selector* r) { return (*l < *r); } };- struct cmp_simple_selector { inline bool operator() (const Simple_Selector* l, const Simple_Selector* r) { return (*l < *r); } };+#include "sass/base.h"+#include "ast_fwd_decl.hpp"++#ifdef DEBUG_SHARED_PTR++#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \+ virtual klass##_Ptr copy(std::string, size_t) const = 0; \+ virtual klass##_Ptr clone(std::string, size_t) const = 0; \++#define ATTACH_AST_OPERATIONS(klass) \+ virtual klass##_Ptr copy(std::string, size_t) const; \+ virtual klass##_Ptr clone(std::string, size_t) const; \++#else++#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \+ virtual klass##_Ptr copy() const = 0; \+ virtual klass##_Ptr clone() const = 0; \++#define ATTACH_AST_OPERATIONS(klass) \+ virtual klass##_Ptr copy() const; \+ virtual klass##_Ptr clone() const; \++#endif++#ifdef __clang__++/*+ * There are some overloads used here that trigger the clang overload+ * hiding warning. Specifically:+ *+ * Type type() which hides string type() from Expression+ *+ */+#pragma clang diagnostic push+#pragma clang diagnostic ignored "-Woverloaded-virtual"++#endif++#include "util.hpp"+#include "units.hpp"+#include "context.hpp"+#include "position.hpp"+#include "constants.hpp"+#include "operation.hpp"+#include "position.hpp"+#include "inspect.hpp"+#include "source_map.hpp"+#include "environment.hpp"+#include "error_handling.hpp"+#include "ast_def_macros.hpp"+#include "ast_fwd_decl.hpp"+#include "source_map.hpp"++#include "sass.h"++namespace Sass {++ // easier to search with name+ const bool DELAYED = true;++ // ToDo: should this really be hardcoded+ // Note: most methods follow precision option+ const double NUMBER_EPSILON = 0.00000000000001;++ // ToDo: where does this fit best?+ // We don't share this with C-API?+ class Operand {+ public:+ Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false)+ : operand(operand), ws_before(ws_before), ws_after(ws_after)+ { }+ public:+ enum Sass_OP operand;+ bool ws_before;+ bool ws_after;+ };++ //////////////////////////////////////////////////////////+ // `hash_combine` comes from boost (functional/hash):+ // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html+ // Boost Software License - Version 1.0+ // http://www.boost.org/users/license.html+ template <typename T>+ void hash_combine (std::size_t& seed, const T& val)+ {+ seed ^= std::hash<T>()(val) + 0x9e3779b9+ + (seed<<6) + (seed>>2);+ }+ //////////////////////////////////////////////////////////++ //////////////////////////////////////////////////////////+ // Abstract base class for all abstract syntax tree nodes.+ //////////////////////////////////////////////////////////+ class AST_Node : public SharedObj {+ ADD_PROPERTY(ParserState, pstate)+ public:+ AST_Node(ParserState pstate)+ : pstate_(pstate)+ { }+ AST_Node(const AST_Node* ptr)+ : pstate_(ptr->pstate_)+ { }++ // AST_Node(AST_Node& ptr) = delete;++ virtual ~AST_Node() = 0;+ virtual size_t hash() { return 0; }+ ATTACH_VIRTUAL_AST_OPERATIONS(AST_Node);+ virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }+ virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }+ virtual std::string to_string(Sass_Inspect_Options opt) const;+ virtual std::string to_string() const;+ virtual void cloneChildren() {};+ public:+ void update_pstate(const ParserState& pstate);+ void set_pstate_offset(const Offset& offset);+ public:+ Offset off() { return pstate(); }+ Position pos() { return pstate(); }+ ATTACH_OPERATIONS()+ };+ inline AST_Node::~AST_Node() { }+++ //////////////////////////////////////////////////////////////////////+ // Abstract base class for expressions. This side of the AST hierarchy+ // represents elements in value contexts, which exist primarily to be+ // evaluated and returned.+ //////////////////////////////////////////////////////////////////////+ class Expression : public AST_Node {+ public:+ enum Concrete_Type {+ NONE,+ BOOLEAN,+ NUMBER,+ COLOR,+ STRING,+ LIST,+ MAP,+ SELECTOR,+ NULL_VAL,+ C_WARNING,+ C_ERROR,+ FUNCTION,+ NUM_TYPES+ };+ enum Simple_Type {+ SIMPLE,+ ATTR_SEL,+ PSEUDO_SEL,+ WRAPPED_SEL,+ };+ private:+ // expressions in some contexts shouldn't be evaluated+ ADD_PROPERTY(bool, is_delayed)+ ADD_PROPERTY(bool, is_expanded)+ ADD_PROPERTY(bool, is_interpolant)+ ADD_PROPERTY(Concrete_Type, concrete_type)+ public:+ Expression(ParserState pstate,+ bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)+ : AST_Node(pstate),+ is_delayed_(d),+ is_expanded_(e),+ is_interpolant_(i),+ concrete_type_(ct)+ { }+ Expression(const Expression* ptr)+ : AST_Node(ptr),+ is_delayed_(ptr->is_delayed_),+ is_expanded_(ptr->is_expanded_),+ is_interpolant_(ptr->is_interpolant_),+ concrete_type_(ptr->concrete_type_)+ { }+ virtual operator bool() { return true; }+ virtual ~Expression() { }+ virtual std::string type() { return ""; /* TODO: raise an error? */ }+ virtual bool is_invisible() const { return false; }+ static std::string type_name() { return ""; }+ virtual bool is_false() { return false; }+ // virtual bool is_true() { return !is_false(); }+ virtual bool operator== (const Expression& rhs) const { return false; }+ virtual bool eq(const Expression& rhs) const { return *this == rhs; };+ virtual void set_delayed(bool delayed) { is_delayed(delayed); }+ virtual bool has_interpolant() const { return is_interpolant(); }+ virtual bool is_left_interpolant() const { return is_interpolant(); }+ virtual bool is_right_interpolant() const { return is_interpolant(); }+ virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }+ virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }+ ATTACH_VIRTUAL_AST_OPERATIONS(Expression);+ virtual size_t hash() { return 0; }+ };++ //////////////////////////////////////////////////////////////////////+ // Still just an expression, but with a to_string method+ //////////////////////////////////////////////////////////////////////+ class PreValue : public Expression {+ public:+ PreValue(ParserState pstate,+ bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)+ : Expression(pstate, d, e, i, ct)+ { }+ PreValue(const PreValue* ptr)+ : Expression(ptr)+ { }+ ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);+ virtual ~PreValue() { }+ };++ //////////////////////////////////////////////////////////////////////+ // base class for values that support operations+ //////////////////////////////////////////////////////////////////////+ class Value : public Expression {+ public:+ Value(ParserState pstate,+ bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)+ : Expression(pstate, d, e, i, ct)+ { }+ Value(const Value* ptr)+ : Expression(ptr)+ { }+ ATTACH_VIRTUAL_AST_OPERATIONS(Value);+ virtual bool operator== (const Expression& rhs) const = 0;+ };+}++/////////////////////////////////////////////////////////////////////////////////////+// Hash method specializations for std::unordered_map to work with Sass::Expression+/////////////////////////////////////////////////////////////////////////////////////++namespace std {+ template<>+ struct hash<Sass::Expression_Obj>+ {+ size_t operator()(Sass::Expression_Obj s) const+ {+ return s->hash();+ }+ };+ template<>+ struct equal_to<Sass::Expression_Obj>+ {+ bool operator()( Sass::Expression_Obj lhs, Sass::Expression_Obj rhs) const+ {+ return lhs->hash() == rhs->hash();+ }+ };+}++namespace Sass {++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like vectors. Uses the+ // "Template Method" design pattern to allow subclasses to adjust their flags+ // when certain objects are pushed.+ /////////////////////////////////////////////////////////////////////////////+ template <typename T>+ class Vectorized {+ std::vector<T> elements_;+ protected:+ size_t hash_;+ void reset_hash() { hash_ = 0; }+ virtual void adjust_after_pushing(T element) { }+ public:+ Vectorized(size_t s = 0) : elements_(std::vector<T>()), hash_(0)+ { elements_.reserve(s); }+ virtual ~Vectorized() = 0;+ size_t length() const { return elements_.size(); }+ bool empty() const { return elements_.empty(); }+ void clear() { return elements_.clear(); }+ T last() const { return elements_.back(); }+ T first() const { return elements_.front(); }+ T& operator[](size_t i) { return elements_[i]; }+ virtual const T& at(size_t i) const { return elements_.at(i); }+ virtual T& at(size_t i) { return elements_.at(i); }+ const T& operator[](size_t i) const { return elements_[i]; }+ virtual void append(T element)+ {+ if (element) {+ reset_hash();+ elements_.push_back(element);+ adjust_after_pushing(element);+ }+ }+ virtual void concat(Vectorized* v)+ {+ for (size_t i = 0, L = v->length(); i < L; ++i) this->append((*v)[i]);+ }+ Vectorized& unshift(T element)+ {+ elements_.insert(elements_.begin(), element);+ return *this;+ }+ std::vector<T>& elements() { return elements_; }+ const std::vector<T>& elements() const { return elements_; }+ std::vector<T>& elements(std::vector<T>& e) { elements_ = e; return elements_; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ for (T& el : elements_) {+ hash_combine(hash_, el->hash());+ }+ }+ return hash_;+ }++ typename std::vector<T>::iterator end() { return elements_.end(); }+ typename std::vector<T>::iterator begin() { return elements_.begin(); }+ typename std::vector<T>::const_iterator end() const { return elements_.end(); }+ typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }+ typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { return elements_.erase(el); }+ typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { return elements_.erase(el); }++ };+ template <typename T>+ inline Vectorized<T>::~Vectorized() { }++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like a hash table. Uses an+ // extra <std::vector> internally to maintain insertion order for interation.+ /////////////////////////////////////////////////////////////////////////////+ class Hashed {+ private:+ ExpressionMap elements_;+ std::vector<Expression_Obj> list_;+ protected:+ size_t hash_;+ Expression_Obj duplicate_key_;+ void reset_hash() { hash_ = 0; }+ void reset_duplicate_key() { duplicate_key_ = 0; }+ virtual void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) { }+ public:+ Hashed(size_t s = 0) : elements_(ExpressionMap(s)), list_(std::vector<Expression_Obj>())+ { elements_.reserve(s); list_.reserve(s); reset_duplicate_key(); }+ virtual ~Hashed();+ size_t length() const { return list_.size(); }+ bool empty() const { return list_.empty(); }+ bool has(Expression_Obj k) const { return elements_.count(k) == 1; }+ Expression_Obj at(Expression_Obj k) const;+ bool has_duplicate_key() const { return duplicate_key_ != 0; }+ Expression_Obj get_duplicate_key() const { return duplicate_key_; }+ const ExpressionMap elements() { return elements_; }+ Hashed& operator<<(std::pair<Expression_Obj, Expression_Obj> p)+ {+ reset_hash();++ if (!has(p.first)) list_.push_back(p.first);+ else if (!duplicate_key_) duplicate_key_ = p.first;++ elements_[p.first] = p.second;++ adjust_after_pushing(p);+ return *this;+ }+ Hashed& operator+=(Hashed* h)+ {+ if (length() == 0) {+ this->elements_ = h->elements_;+ this->list_ = h->list_;+ return *this;+ }++ for (auto key : h->keys()) {+ *this << std::make_pair(key, h->at(key));+ }++ reset_duplicate_key();+ return *this;+ }+ const ExpressionMap& pairs() const { return elements_; }+ const std::vector<Expression_Obj>& keys() const { return list_; }++// std::unordered_map<Expression_Obj, Expression_Obj>::iterator end() { return elements_.end(); }+// std::unordered_map<Expression_Obj, Expression_Obj>::iterator begin() { return elements_.begin(); }+// std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator end() const { return elements_.end(); }+// std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator begin() const { return elements_.begin(); }++ };+ inline Hashed::~Hashed() { }+++ /////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements. This side of the AST hierarchy+ // represents elements in expansion contexts, which exist primarily to be+ // rewritten and macro-expanded.+ /////////////////////////////////////////////////////////////////////////+ class Statement : public AST_Node {+ public:+ enum Statement_Type {+ NONE,+ RULESET,+ MEDIA,+ DIRECTIVE,+ SUPPORTS,+ ATROOT,+ BUBBLE,+ CONTENT,+ KEYFRAMERULE,+ DECLARATION,+ ASSIGNMENT,+ IMPORT_STUB,+ IMPORT,+ COMMENT,+ WARNING,+ RETURN,+ EXTEND,+ ERROR,+ DEBUGSTMT,+ WHILE,+ EACH,+ FOR,+ IF+ };+ private:+ ADD_PROPERTY(Statement_Type, statement_type)+ ADD_PROPERTY(size_t, tabs)+ ADD_PROPERTY(bool, group_end)+ public:+ Statement(ParserState pstate, Statement_Type st = NONE, size_t t = 0)+ : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)+ { }+ Statement(const Statement* ptr)+ : AST_Node(ptr),+ statement_type_(ptr->statement_type_),+ tabs_(ptr->tabs_),+ group_end_(ptr->group_end_)+ { }+ virtual ~Statement() = 0;+ // needed for rearranging nested rulesets during CSS emission+ virtual bool is_invisible() const { return false; }+ virtual bool bubbles() { return false; }+ virtual bool has_content()+ {+ return statement_type_ == CONTENT;+ }+ };+ inline Statement::~Statement() { }++ ////////////////////////+ // Blocks of statements.+ ////////////////////////+ class Block : public Statement, public Vectorized<Statement_Obj> {+ ADD_PROPERTY(bool, is_root)+ ADD_PROPERTY(bool, is_at_root);+ // needed for properly formatted CSS emission+ protected:+ void adjust_after_pushing(Statement_Obj s)+ {+ }+ public:+ Block(ParserState pstate, size_t s = 0, bool r = false)+ : Statement(pstate),+ Vectorized<Statement_Obj>(s),+ is_root_(r),+ is_at_root_(false)+ { }+ Block(const Block* ptr)+ : Statement(ptr),+ Vectorized<Statement_Obj>(*ptr),+ is_root_(ptr->is_root_),+ is_at_root_(ptr->is_at_root_)+ { }+ virtual bool has_content()+ {+ for (size_t i = 0, L = elements().size(); i < L; ++i) {+ if (elements()[i]->has_content()) return true;+ }+ return Statement::has_content();+ }+ ATTACH_AST_OPERATIONS(Block)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements that contain blocks of statements.+ ////////////////////////////////////////////////////////////////////////+ class Has_Block : public Statement {+ ADD_PROPERTY(Block_Obj, block)+ public:+ Has_Block(ParserState pstate, Block_Obj b)+ : Statement(pstate), block_(b)+ { }+ Has_Block(const Has_Block* ptr)+ : Statement(ptr), block_(ptr->block_)+ { }+ virtual bool has_content()+ {+ return (block_ && block_->has_content()) || Statement::has_content();+ }+ virtual ~Has_Block() = 0;+ };+ inline Has_Block::~Has_Block() { }++ /////////////////////////////////////////////////////////////////////////////+ // Rulesets (i.e., sets of styles headed by a selector and containing a block+ // of style declarations.+ /////////////////////////////////////////////////////////////////////////////+ class Ruleset : public Has_Block {+ ADD_PROPERTY(Selector_Obj, selector)+ ADD_PROPERTY(bool, at_root);+ ADD_PROPERTY(bool, is_root);+ public:+ Ruleset(ParserState pstate, Selector_Obj s = 0, Block_Obj b = 0)+ : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false)+ { statement_type(RULESET); }+ Ruleset(const Ruleset* ptr)+ : Has_Block(ptr),+ selector_(ptr->selector_),+ at_root_(ptr->at_root_),+ is_root_(ptr->is_root_)+ { statement_type(RULESET); }+ bool is_invisible() const;+ ATTACH_AST_OPERATIONS(Ruleset)+ ATTACH_OPERATIONS()+ };++ /////////////////+ // Bubble.+ /////////////////+ class Bubble : public Statement {+ ADD_PROPERTY(Statement_Obj, node)+ ADD_PROPERTY(bool, group_end)+ public:+ Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = 0, size_t t = 0)+ : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0)+ { }+ Bubble(const Bubble* ptr)+ : Statement(ptr),+ node_(ptr->node_),+ group_end_(ptr->group_end_)+ { }+ bool bubbles() { return true; }+ ATTACH_AST_OPERATIONS(Bubble)+ ATTACH_OPERATIONS()+ };++ /////////////////+ // Trace.+ /////////////////+ class Trace : public Has_Block {+ ADD_PROPERTY(std::string, name)+ public:+ Trace(ParserState pstate, std::string n, Block_Obj b = 0)+ : Has_Block(pstate, b), name_(n)+ { }+ Trace(const Trace* ptr)+ : Has_Block(ptr),+ name_(ptr->name_)+ { }+ ATTACH_AST_OPERATIONS(Trace)+ ATTACH_OPERATIONS()+ };++ /////////////////+ // Media queries.+ /////////////////+ class Media_Block : public Has_Block {+ ADD_PROPERTY(List_Obj, media_queries)+ public:+ Media_Block(ParserState pstate, List_Obj mqs, Block_Obj b)+ : Has_Block(pstate, b), media_queries_(mqs)+ { statement_type(MEDIA); }+ Media_Block(ParserState pstate, List_Obj mqs, Block_Obj b, Selector_Obj s)+ : Has_Block(pstate, b), media_queries_(mqs)+ { statement_type(MEDIA); }+ Media_Block(const Media_Block* ptr)+ : Has_Block(ptr), media_queries_(ptr->media_queries_)+ { statement_type(MEDIA); }+ bool bubbles() { return true; }+ bool is_invisible() const;+ ATTACH_AST_OPERATIONS(Media_Block)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // At-rules -- arbitrary directives beginning with "@" that may have an+ // optional statement block.+ ///////////////////////////////////////////////////////////////////////+ class Directive : public Has_Block {+ ADD_PROPERTY(std::string, keyword)+ ADD_PROPERTY(Selector_Obj, selector)+ ADD_PROPERTY(Expression_Obj, value)+ public:+ Directive(ParserState pstate, std::string kwd, Selector_Obj sel = 0, Block_Obj b = 0, Expression_Obj val = 0)+ : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed+ { statement_type(DIRECTIVE); }+ Directive(const Directive* ptr)+ : Has_Block(ptr),+ keyword_(ptr->keyword_),+ selector_(ptr->selector_),+ value_(ptr->value_) // set value manually if needed+ { statement_type(DIRECTIVE); }+ bool bubbles() { return is_keyframes() || is_media(); }+ bool is_media() {+ return keyword_.compare("@-webkit-media") == 0 ||+ keyword_.compare("@-moz-media") == 0 ||+ keyword_.compare("@-o-media") == 0 ||+ keyword_.compare("@media") == 0;+ }+ bool is_keyframes() {+ return keyword_.compare("@-webkit-keyframes") == 0 ||+ keyword_.compare("@-moz-keyframes") == 0 ||+ keyword_.compare("@-o-keyframes") == 0 ||+ keyword_.compare("@keyframes") == 0;+ }+ ATTACH_AST_OPERATIONS(Directive)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Keyframe-rules -- the child blocks of "@keyframes" nodes.+ ///////////////////////////////////////////////////////////////////////+ class Keyframe_Rule : public Has_Block {+ // according to css spec, this should be <keyframes-name>+ // <keyframes-name> = <custom-ident> | <string>+ ADD_PROPERTY(Selector_Obj, name)+ public:+ Keyframe_Rule(ParserState pstate, Block_Obj b)+ : Has_Block(pstate, b), name_()+ { statement_type(KEYFRAMERULE); }+ Keyframe_Rule(const Keyframe_Rule* ptr)+ : Has_Block(ptr), name_(ptr->name_)+ { statement_type(KEYFRAMERULE); }+ ATTACH_AST_OPERATIONS(Keyframe_Rule)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Declarations -- style rules consisting of a property name and values.+ ////////////////////////////////////////////////////////////////////////+ class Declaration : public Has_Block {+ ADD_PROPERTY(String_Obj, property)+ ADD_PROPERTY(Expression_Obj, value)+ ADD_PROPERTY(bool, is_important)+ ADD_PROPERTY(bool, is_indented)+ public:+ Declaration(ParserState pstate,+ String_Obj prop, Expression_Obj val, bool i = false, Block_Obj b = 0)+ : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_indented_(false)+ { statement_type(DECLARATION); }+ Declaration(const Declaration* ptr)+ : Has_Block(ptr),+ property_(ptr->property_),+ value_(ptr->value_),+ is_important_(ptr->is_important_),+ is_indented_(ptr->is_indented_)+ { statement_type(DECLARATION); }+ ATTACH_AST_OPERATIONS(Declaration)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////+ // Assignments -- variable and value.+ /////////////////////////////////////+ class Assignment : public Statement {+ ADD_PROPERTY(std::string, variable)+ ADD_PROPERTY(Expression_Obj, value)+ ADD_PROPERTY(bool, is_default)+ ADD_PROPERTY(bool, is_global)+ public:+ Assignment(ParserState pstate,+ std::string var, Expression_Obj val,+ bool is_default = false,+ bool is_global = false)+ : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)+ { statement_type(ASSIGNMENT); }+ Assignment(const Assignment* ptr)+ : Statement(ptr),+ variable_(ptr->variable_),+ value_(ptr->value_),+ is_default_(ptr->is_default_),+ is_global_(ptr->is_global_)+ { statement_type(ASSIGNMENT); }+ ATTACH_AST_OPERATIONS(Assignment)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Import directives. CSS and Sass import lists can be intermingled, so it's+ // necessary to store a list of each in an Import node.+ ////////////////////////////////////////////////////////////////////////////+ class Import : public Statement {+ std::vector<Expression_Obj> urls_;+ std::vector<Include> incs_;+ ADD_PROPERTY(List_Obj, import_queries);+ public:+ Import(ParserState pstate)+ : Statement(pstate),+ urls_(std::vector<Expression_Obj>()),+ incs_(std::vector<Include>()),+ import_queries_()+ { statement_type(IMPORT); }+ Import(const Import* ptr)+ : Statement(ptr),+ urls_(ptr->urls_),+ incs_(ptr->incs_),+ import_queries_(ptr->import_queries_)+ { statement_type(IMPORT); }+ std::vector<Expression_Obj>& urls() { return urls_; }+ std::vector<Include>& incs() { return incs_; }+ ATTACH_AST_OPERATIONS(Import)+ ATTACH_OPERATIONS()+ };++ // not yet resolved single import+ // so far we only know requested name+ class Import_Stub : public Statement {+ Include resource_;+ public:+ std::string abs_path() { return resource_.abs_path; };+ std::string imp_path() { return resource_.imp_path; };+ Include resource() { return resource_; };++ Import_Stub(ParserState pstate, Include res)+ : Statement(pstate), resource_(res)+ { statement_type(IMPORT_STUB); }+ Import_Stub(const Import_Stub* ptr)+ : Statement(ptr), resource_(ptr->resource_)+ { statement_type(IMPORT_STUB); }+ ATTACH_AST_OPERATIONS(Import_Stub)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////+ // The Sass `@warn` directive.+ //////////////////////////////+ class Warning : public Statement {+ ADD_PROPERTY(Expression_Obj, message)+ public:+ Warning(ParserState pstate, Expression_Obj msg)+ : Statement(pstate), message_(msg)+ { statement_type(WARNING); }+ Warning(const Warning* ptr)+ : Statement(ptr), message_(ptr->message_)+ { statement_type(WARNING); }+ ATTACH_AST_OPERATIONS(Warning)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////+ // The Sass `@error` directive.+ ///////////////////////////////+ class Error : public Statement {+ ADD_PROPERTY(Expression_Obj, message)+ public:+ Error(ParserState pstate, Expression_Obj msg)+ : Statement(pstate), message_(msg)+ { statement_type(ERROR); }+ Error(const Error* ptr)+ : Statement(ptr), message_(ptr->message_)+ { statement_type(ERROR); }+ ATTACH_AST_OPERATIONS(Error)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////+ // The Sass `@debug` directive.+ ///////////////////////////////+ class Debug : public Statement {+ ADD_PROPERTY(Expression_Obj, value)+ public:+ Debug(ParserState pstate, Expression_Obj val)+ : Statement(pstate), value_(val)+ { statement_type(DEBUGSTMT); }+ Debug(const Debug* ptr)+ : Statement(ptr), value_(ptr->value_)+ { statement_type(DEBUGSTMT); }+ ATTACH_AST_OPERATIONS(Debug)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////+ // CSS comments. These may be interpolated.+ ///////////////////////////////////////////+ class Comment : public Statement {+ ADD_PROPERTY(String_Obj, text)+ ADD_PROPERTY(bool, is_important)+ public:+ Comment(ParserState pstate, String_Obj txt, bool is_important)+ : Statement(pstate), text_(txt), is_important_(is_important)+ { statement_type(COMMENT); }+ Comment(const Comment* ptr)+ : Statement(ptr),+ text_(ptr->text_),+ is_important_(ptr->is_important_)+ { statement_type(COMMENT); }+ virtual bool is_invisible() const+ { return /* is_important() == */ false; }+ ATTACH_AST_OPERATIONS(Comment)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////+ // The Sass `@if` control directive.+ ////////////////////////////////////+ class If : public Has_Block {+ ADD_PROPERTY(Expression_Obj, predicate)+ ADD_PROPERTY(Block_Obj, alternative)+ public:+ If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt = 0)+ : Has_Block(pstate, &con), predicate_(pred), alternative_(alt)+ { statement_type(IF); }+ If(const If* ptr)+ : Has_Block(ptr),+ predicate_(ptr->predicate_),+ alternative_(ptr->alternative_)+ { statement_type(IF); }+ virtual bool has_content()+ {+ return Has_Block::has_content() || (alternative_ && alternative_->has_content());+ }+ ATTACH_AST_OPERATIONS(If)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////+ // The Sass `@for` control directive.+ /////////////////////////////////////+ class For : public Has_Block {+ ADD_PROPERTY(std::string, variable)+ ADD_PROPERTY(Expression_Obj, lower_bound)+ ADD_PROPERTY(Expression_Obj, upper_bound)+ ADD_PROPERTY(bool, is_inclusive)+ public:+ For(ParserState pstate,+ std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc)+ : Has_Block(pstate, b),+ variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)+ { statement_type(FOR); }+ For(const For* ptr)+ : Has_Block(ptr),+ variable_(ptr->variable_),+ lower_bound_(ptr->lower_bound_),+ upper_bound_(ptr->upper_bound_),+ is_inclusive_(ptr->is_inclusive_)+ { statement_type(FOR); }+ ATTACH_AST_OPERATIONS(For)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////+ // The Sass `@each` control directive.+ //////////////////////////////////////+ class Each : public Has_Block {+ ADD_PROPERTY(std::vector<std::string>, variables)+ ADD_PROPERTY(Expression_Obj, list)+ public:+ Each(ParserState pstate, std::vector<std::string> vars, Expression_Obj lst, Block_Obj b)+ : Has_Block(pstate, b), variables_(vars), list_(lst)+ { statement_type(EACH); }+ Each(const Each* ptr)+ : Has_Block(ptr), variables_(ptr->variables_), list_(ptr->list_)+ { statement_type(EACH); }+ ATTACH_AST_OPERATIONS(Each)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////+ // The Sass `@while` control directive.+ ///////////////////////////////////////+ class While : public Has_Block {+ ADD_PROPERTY(Expression_Obj, predicate)+ public:+ While(ParserState pstate, Expression_Obj pred, Block_Obj b)+ : Has_Block(pstate, b), predicate_(pred)+ { statement_type(WHILE); }+ While(const While* ptr)+ : Has_Block(ptr), predicate_(ptr->predicate_)+ { statement_type(WHILE); }+ ATTACH_AST_OPERATIONS(While)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////+ // The @return directive for use inside SassScript functions.+ /////////////////////////////////////////////////////////////+ class Return : public Statement {+ ADD_PROPERTY(Expression_Obj, value)+ public:+ Return(ParserState pstate, Expression_Obj val)+ : Statement(pstate), value_(val)+ { statement_type(RETURN); }+ Return(const Return* ptr)+ : Statement(ptr), value_(ptr->value_)+ { statement_type(RETURN); }+ ATTACH_AST_OPERATIONS(Return)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////+ // The Sass `@extend` directive.+ ////////////////////////////////+ class Extension : public Statement {+ ADD_PROPERTY(Selector_Obj, selector)+ public:+ Extension(ParserState pstate, Selector_Obj s)+ : Statement(pstate), selector_(s)+ { statement_type(EXTEND); }+ Extension(const Extension* ptr)+ : Statement(ptr), selector_(ptr->selector_)+ { statement_type(EXTEND); }+ ATTACH_AST_OPERATIONS(Extension)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////////////+ // Definitions for both mixins and functions. The two cases are distinguished+ // by a type tag.+ /////////////////////////////////////////////////////////////////////////////+ struct Backtrace;+ typedef Environment<AST_Node_Obj> Env;+ typedef const char* Signature;+ typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*, std::vector<Selector_List_Obj>);+ class Definition : public Has_Block {+ public:+ enum Type { MIXIN, FUNCTION };+ ADD_PROPERTY(std::string, name)+ ADD_PROPERTY(Parameters_Obj, parameters)+ ADD_PROPERTY(Env*, environment)+ ADD_PROPERTY(Type, type)+ ADD_PROPERTY(Native_Function, native_function)+ ADD_PROPERTY(Sass_Function_Entry, c_function)+ ADD_PROPERTY(void*, cookie)+ ADD_PROPERTY(bool, is_overload_stub)+ ADD_PROPERTY(Signature, signature)+ public:+ Definition(const Definition* ptr)+ : Has_Block(ptr),+ name_(ptr->name_),+ parameters_(ptr->parameters_),+ environment_(ptr->environment_),+ type_(ptr->type_),+ native_function_(ptr->native_function_),+ c_function_(ptr->c_function_),+ cookie_(ptr->cookie_),+ is_overload_stub_(ptr->is_overload_stub_),+ signature_(ptr->signature_)+ { }++ Definition(ParserState pstate,+ std::string n,+ Parameters_Obj params,+ Block_Obj b,+ Type t)+ : Has_Block(pstate, b),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(t),+ native_function_(0),+ c_function_(0),+ cookie_(0),+ is_overload_stub_(false),+ signature_(0)+ { }+ Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters_Obj params,+ Native_Function func_ptr,+ bool overload_stub = false)+ : Has_Block(pstate, 0),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(func_ptr),+ c_function_(0),+ cookie_(0),+ is_overload_stub_(overload_stub),+ signature_(sig)+ { }+ Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters_Obj params,+ Sass_Function_Entry c_func,+ bool whatever,+ bool whatever2)+ : Has_Block(pstate, 0),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(0),+ c_function_(c_func),+ cookie_(sass_function_get_cookie(c_func)),+ is_overload_stub_(false),+ signature_(sig)+ { }+ ATTACH_AST_OPERATIONS(Definition)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////+ // Mixin calls (i.e., `@include ...`).+ //////////////////////////////////////+ class Mixin_Call : public Has_Block {+ ADD_PROPERTY(std::string, name)+ ADD_PROPERTY(Arguments_Obj, arguments)+ public:+ Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Block_Obj b = 0)+ : Has_Block(pstate, b), name_(n), arguments_(args)+ { }+ Mixin_Call(const Mixin_Call* ptr)+ : Has_Block(ptr),+ name_(ptr->name_),+ arguments_(ptr->arguments_)+ { }+ ATTACH_AST_OPERATIONS(Mixin_Call)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////+ // The @content directive for mixin content blocks.+ ///////////////////////////////////////////////////+ class Content : public Statement {+ ADD_PROPERTY(Media_Block_Obj, media_block)+ public:+ Content(ParserState pstate) : Statement(pstate)+ { statement_type(CONTENT); }+ Content(const Content* ptr) : Statement(ptr)+ { statement_type(CONTENT); }+ ATTACH_AST_OPERATIONS(Content)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Lists of values, both comma- and space-separated (distinguished by a+ // type-tag.) Also used to represent variable-length argument lists.+ ///////////////////////////////////////////////////////////////////////+ class List : public Value, public Vectorized<Expression_Obj> {+ void adjust_after_pushing(Expression_Obj e) { is_expanded(false); }+ private:+ ADD_PROPERTY(enum Sass_Separator, separator)+ ADD_PROPERTY(bool, is_arglist)+ ADD_PROPERTY(bool, from_selector)+ public:+ List(ParserState pstate,+ size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false)+ : Value(pstate),+ Vectorized<Expression_Obj>(size),+ separator_(sep),+ is_arglist_(argl),+ from_selector_(false)+ { concrete_type(LIST); }+ List(const List* ptr)+ : Value(ptr),+ Vectorized<Expression_Obj>(*ptr),+ separator_(ptr->separator_),+ is_arglist_(ptr->is_arglist_),+ from_selector_(ptr->from_selector_)+ { concrete_type(LIST); }+ std::string type() { return is_arglist_ ? "arglist" : "list"; }+ static std::string type_name() { return "list"; }+ const char* sep_string(bool compressed = false) const {+ return separator() == SASS_SPACE ?+ " " : (compressed ? "," : ", ");+ }+ bool is_invisible() const { return empty(); }+ Expression_Obj value_at_index(size_t i);++ virtual size_t size() const;++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(sep_string());+ for (size_t i = 0, L = length(); i < L; ++i)+ hash_combine(hash_, (elements()[i])->hash());+ }+ return hash_;+ }++ virtual void set_delayed(bool delayed)+ {+ is_delayed(delayed);+ // don't set children+ }++ virtual bool operator== (const Expression& rhs) const;++ ATTACH_AST_OPERATIONS(List)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Key value paris.+ ///////////////////////////////////////////////////////////////////////+ class Map : public Value, public Hashed {+ void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) { is_expanded(false); }+ public:+ Map(ParserState pstate,+ size_t size = 0)+ : Value(pstate),+ Hashed(size)+ { concrete_type(MAP); }+ Map(const Map* ptr)+ : Value(ptr),+ Hashed(*ptr)+ { concrete_type(MAP); }+ std::string type() { return "map"; }+ static std::string type_name() { return "map"; }+ bool is_invisible() const { return empty(); }+ List_Obj to_list(Context& ctx, ParserState& pstate);++ virtual size_t hash()+ {+ if (hash_ == 0) {+ for (auto key : keys()) {+ hash_combine(hash_, key->hash());+ hash_combine(hash_, at(key)->hash());+ }+ }++ return hash_;+ }++ virtual bool operator== (const Expression& rhs) const;++ ATTACH_AST_OPERATIONS(Map)+ ATTACH_OPERATIONS()+ };++ inline static const std::string sass_op_to_name(enum Sass_OP op) {+ switch (op) {+ case AND: return "and"; break;+ case OR: return "or"; break;+ case EQ: return "eq"; break;+ case NEQ: return "neq"; break;+ case GT: return "gt"; break;+ case GTE: return "gte"; break;+ case LT: return "lt"; break;+ case LTE: return "lte"; break;+ case ADD: return "plus"; break;+ case SUB: return "sub"; break;+ case MUL: return "times"; break;+ case DIV: return "div"; break;+ case MOD: return "mod"; break;+ // this is only used internally!+ case NUM_OPS: return "[OPS]"; break;+ default: return "invalid"; break;+ }+ }++ //////////////////////////////////////////////////////////////////////////+ // Binary expressions. Represents logical, relational, and arithmetic+ // operations. Templatized to avoid large switch statements and repetitive+ // subclassing.+ //////////////////////////////////////////////////////////////////////////+ class Binary_Expression : public PreValue {+ private:+ ADD_HASHED(Operand, op)+ ADD_HASHED(Expression_Obj, left)+ ADD_HASHED(Expression_Obj, right)+ size_t hash_;+ public:+ Binary_Expression(ParserState pstate,+ Operand op, Expression_Obj lhs, Expression_Obj rhs)+ : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0)+ { }+ Binary_Expression(const Binary_Expression* ptr)+ : PreValue(ptr),+ op_(ptr->op_),+ left_(ptr->left_),+ right_(ptr->right_),+ hash_(ptr->hash_)+ { }+ const std::string type_name() {+ switch (type()) {+ case AND: return "and"; break;+ case OR: return "or"; break;+ case EQ: return "eq"; break;+ case NEQ: return "neq"; break;+ case GT: return "gt"; break;+ case GTE: return "gte"; break;+ case LT: return "lt"; break;+ case LTE: return "lte"; break;+ case ADD: return "add"; break;+ case SUB: return "sub"; break;+ case MUL: return "mul"; break;+ case DIV: return "div"; break;+ case MOD: return "mod"; break;+ // this is only used internally!+ case NUM_OPS: return "[OPS]"; break;+ default: return "invalid"; break;+ }+ }+ const std::string separator() {+ switch (type()) {+ case AND: return "&&"; break;+ case OR: return "||"; break;+ case EQ: return "=="; break;+ case NEQ: return "!="; break;+ case GT: return ">"; break;+ case GTE: return ">="; break;+ case LT: return "<"; break;+ case LTE: return "<="; break;+ case ADD: return "+"; break;+ case SUB: return "-"; break;+ case MUL: return "*"; break;+ case DIV: return "/"; break;+ case MOD: return "%"; break;+ // this is only used internally!+ case NUM_OPS: return "[OPS]"; break;+ default: return "invalid"; break;+ }+ }+ bool is_left_interpolant(void) const;+ bool is_right_interpolant(void) const;+ bool has_interpolant() const+ {+ return is_left_interpolant() ||+ is_right_interpolant();+ }+ virtual void set_delayed(bool delayed)+ {+ right()->set_delayed(delayed);+ left()->set_delayed(delayed);+ is_delayed(delayed);+ }+ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ Binary_Expression_Ptr_Const m = dynamic_cast<Binary_Expression_Ptr_Const>(&rhs);+ if (m == 0) return false;+ return type() == m->type() &&+ left() == m->left() &&+ right() == m->right();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<size_t>()(type());+ hash_combine(hash_, left()->hash());+ hash_combine(hash_, right()->hash());+ }+ return hash_;+ }+ enum Sass_OP type() const { return op_.operand; }+ ATTACH_AST_OPERATIONS(Binary_Expression)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Arithmetic negation (logical negation is just an ordinary function call).+ ////////////////////////////////////////////////////////////////////////////+ class Unary_Expression : public Expression {+ public:+ enum Type { PLUS, MINUS, NOT };+ private:+ ADD_HASHED(Type, type)+ ADD_HASHED(Expression_Obj, operand)+ size_t hash_;+ public:+ Unary_Expression(ParserState pstate, Type t, Expression_Obj o)+ : Expression(pstate), type_(t), operand_(o), hash_(0)+ { }+ Unary_Expression(const Unary_Expression* ptr)+ : Expression(ptr),+ type_(ptr->type_),+ operand_(ptr->operand_),+ hash_(ptr->hash_)+ { }+ const std::string type_name() {+ switch (type_) {+ case PLUS: return "plus"; break;+ case MINUS: return "minus"; break;+ case NOT: return "not"; break;+ default: return "invalid"; break;+ }+ }+ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ Unary_Expression_Ptr_Const m = dynamic_cast<Unary_Expression_Ptr_Const>(&rhs);+ if (m == 0) return false;+ return type() == m->type() &&+ operand() == m->operand();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<size_t>()(type_);+ hash_combine(hash_, operand()->hash());+ };+ return hash_;+ }+ ATTACH_AST_OPERATIONS(Unary_Expression)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////+ // Individual argument objects for mixin and function calls.+ ////////////////////////////////////////////////////////////+ class Argument : public Expression {+ ADD_HASHED(Expression_Obj, value)+ ADD_HASHED(std::string, name)+ ADD_PROPERTY(bool, is_rest_argument)+ ADD_PROPERTY(bool, is_keyword_argument)+ size_t hash_;+ public:+ Argument(ParserState pstate, Expression_Obj val, std::string n = "", bool rest = false, bool keyword = false)+ : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)+ {+ if (!name_.empty() && is_rest_argument_) {+ error("variable-length argument may not be passed by name", pstate_);+ }+ }+ Argument(const Argument* ptr)+ : Expression(ptr),+ value_(ptr->value_),+ name_(ptr->name_),+ is_rest_argument_(ptr->is_rest_argument_),+ is_keyword_argument_(ptr->is_keyword_argument_),+ hash_(ptr->hash_)+ {+ if (!name_.empty() && is_rest_argument_) {+ error("variable-length argument may not be passed by name", pstate_);+ }+ }++ virtual void set_delayed(bool delayed);+ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ Argument_Ptr_Const m = dynamic_cast<Argument_Ptr_Const>(&rhs);+ if (!(m && name() == m->name())) return false;+ return *value() == *m->value();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(name());+ hash_combine(hash_, value()->hash());+ }+ return hash_;+ }++ ATTACH_AST_OPERATIONS(Argument)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Argument lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all ordinal arguments precede all+ // named arguments).+ ////////////////////////////////////////////////////////////////////////+ class Arguments : public Expression, public Vectorized<Argument_Obj> {+ ADD_PROPERTY(bool, has_named_arguments)+ ADD_PROPERTY(bool, has_rest_argument)+ ADD_PROPERTY(bool, has_keyword_argument)+ protected:+ void adjust_after_pushing(Argument_Obj a);+ public:+ Arguments(ParserState pstate)+ : Expression(pstate),+ Vectorized<Argument_Obj>(),+ has_named_arguments_(false),+ has_rest_argument_(false),+ has_keyword_argument_(false)+ { }+ Arguments(const Arguments* ptr)+ : Expression(ptr),+ Vectorized<Argument_Obj>(*ptr),+ has_named_arguments_(ptr->has_named_arguments_),+ has_rest_argument_(ptr->has_rest_argument_),+ has_keyword_argument_(ptr->has_keyword_argument_)+ { }++ virtual void set_delayed(bool delayed);++ Argument_Obj get_rest_argument();+ Argument_Obj get_keyword_argument();++ ATTACH_AST_OPERATIONS(Arguments)+ ATTACH_OPERATIONS()+ };++ //////////////////+ // Function calls.+ //////////////////+ class Function_Call : public PreValue {+ ADD_HASHED(std::string, name)+ ADD_HASHED(Arguments_Obj, arguments)+ ADD_PROPERTY(bool, via_call)+ ADD_PROPERTY(void*, cookie)+ size_t hash_;+ public:+ Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie)+ : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(cookie), hash_(0)+ { concrete_type(FUNCTION); }+ Function_Call(ParserState pstate, std::string n, Arguments_Obj args)+ : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0)+ { concrete_type(FUNCTION); }+ Function_Call(const Function_Call* ptr)+ : PreValue(ptr),+ name_(ptr->name_),+ arguments_(ptr->arguments_),+ via_call_(ptr->via_call_),+ cookie_(ptr->cookie_),+ hash_(ptr->hash_)+ { concrete_type(FUNCTION); }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ Function_Call_Ptr_Const m = dynamic_cast<Function_Call_Ptr_Const>(&rhs);+ if (!(m && name() == m->name())) return false;+ if (!(m && arguments()->length() == m->arguments()->length())) return false;+ for (size_t i =0, L = arguments()->length(); i < L; ++i)+ if (!((*arguments())[i] == (*m->arguments())[i])) return false;+ return true;+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(name());+ for (auto argument : arguments()->elements())+ hash_combine(hash_, argument->hash());+ }+ return hash_;+ }+ ATTACH_AST_OPERATIONS(Function_Call)+ ATTACH_OPERATIONS()+ };++ /////////////////////////+ // Function call schemas.+ /////////////////////////+ class Function_Call_Schema : public Expression {+ ADD_PROPERTY(String_Obj, name)+ ADD_PROPERTY(Arguments_Obj, arguments)+ public:+ Function_Call_Schema(ParserState pstate, String_Obj n, Arguments_Obj args)+ : Expression(pstate), name_(n), arguments_(args)+ { concrete_type(STRING); }+ Function_Call_Schema(const Function_Call_Schema* ptr)+ : Expression(ptr),+ name_(ptr->name_),+ arguments_(ptr->arguments_)+ { concrete_type(STRING); }+ ATTACH_AST_OPERATIONS(Function_Call_Schema)+ ATTACH_OPERATIONS()+ };++ ///////////////////////+ // Variable references.+ ///////////////////////+ class Variable : public PreValue {+ ADD_PROPERTY(std::string, name)+ public:+ Variable(ParserState pstate, std::string n)+ : PreValue(pstate), name_(n)+ { }+ Variable(const Variable* ptr)+ : PreValue(ptr), name_(ptr->name_)+ { }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ Variable_Ptr_Const e = dynamic_cast<Variable_Ptr_Const>(&rhs);+ return e && name() == e->name();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ return std::hash<std::string>()(name());+ }++ ATTACH_AST_OPERATIONS(Variable)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Textual (i.e., unevaluated) numeric data. Variants are distinguished with+ // a type tag.+ ////////////////////////////////////////////////////////////////////////////+ class Textual : public Expression {+ public:+ enum Type { NUMBER, PERCENTAGE, DIMENSION, HEX };+ private:+ ADD_HASHED(Type, type)+ ADD_HASHED(std::string, value)+ size_t hash_;+ public:+ Textual(ParserState pstate, Type t, std::string val)+ : Expression(pstate, DELAYED), type_(t), value_(val),+ hash_(0)+ { }+ Textual(const Textual* ptr)+ : Expression(ptr),+ type_(ptr->type_),+ value_(ptr->value_),+ hash_(ptr->hash_)+ { }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ Textual_Ptr_Const e = dynamic_cast<Textual_Ptr_Const>(&rhs);+ return e && value() == e->value() && type() == e->type();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(value_);+ hash_combine(hash_, std::hash<int>()(type_));+ }+ return hash_;+ }++ ATTACH_AST_OPERATIONS(Textual)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////+ // Numbers, percentages, dimensions, and colors.+ ////////////////////////////////////////////////+ class Number : public Value {+ ADD_HASHED(double, value)+ ADD_PROPERTY(bool, zero)+ std::vector<std::string> numerator_units_;+ std::vector<std::string> denominator_units_;+ size_t hash_;+ public:+ Number(ParserState pstate, double val, std::string u = "", bool zero = true);++ Number(const Number* ptr)+ : Value(ptr),+ value_(ptr->value_), zero_(ptr->zero_),+ numerator_units_(ptr->numerator_units_),+ denominator_units_(ptr->denominator_units_),+ hash_(ptr->hash_)+ { concrete_type(NUMBER); }++ bool zero() { return zero_; }+ bool is_valid_css_unit() const;+ std::vector<std::string>& numerator_units() { return numerator_units_; }+ std::vector<std::string>& denominator_units() { return denominator_units_; }+ const std::vector<std::string>& numerator_units() const { return numerator_units_; }+ const std::vector<std::string>& denominator_units() const { return denominator_units_; }+ std::string type() { return "number"; }+ static std::string type_name() { return "number"; }+ std::string unit() const;++ bool is_unitless() const;+ double convert_factor(const Number&) const;+ bool convert(const std::string& unit = "", bool strict = false);+ void normalize(const std::string& unit = "", bool strict = false);+ // useful for making one number compatible with another+ std::string find_convertible_unit() const;++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<double>()(value_);+ for (const auto numerator : numerator_units())+ hash_combine(hash_, std::hash<std::string>()(numerator));+ for (const auto denominator : denominator_units())+ hash_combine(hash_, std::hash<std::string>()(denominator));+ }+ return hash_;+ }++ virtual bool operator< (const Number& rhs) const;+ virtual bool operator== (const Expression& rhs) const;+ virtual bool eq(const Expression& rhs) const;+ ATTACH_AST_OPERATIONS(Number)+ ATTACH_OPERATIONS()+ };++ //////////+ // Colors.+ //////////+ class Color : public Value {+ ADD_HASHED(double, r)+ ADD_HASHED(double, g)+ ADD_HASHED(double, b)+ ADD_HASHED(double, a)+ ADD_PROPERTY(std::string, disp)+ size_t hash_;+ public:+ Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "")+ : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp),+ hash_(0)+ { concrete_type(COLOR); }+ Color(const Color* ptr)+ : Value(ptr),+ r_(ptr->r_),+ g_(ptr->g_),+ b_(ptr->b_),+ a_(ptr->a_),+ disp_(ptr->disp_),+ hash_(ptr->hash_)+ { concrete_type(COLOR); }+ std::string type() { return "color"; }+ static std::string type_name() { return "color"; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<double>()(a_);+ hash_combine(hash_, std::hash<double>()(r_));+ hash_combine(hash_, std::hash<double>()(g_));+ hash_combine(hash_, std::hash<double>()(b_));+ }+ return hash_;+ }++ virtual bool operator== (const Expression& rhs) const;++ ATTACH_AST_OPERATIONS(Color)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////+ // Errors from Sass_Values.+ //////////////////////////////+ class Custom_Error : public Value {+ ADD_PROPERTY(std::string, message)+ public:+ Custom_Error(ParserState pstate, std::string msg)+ : Value(pstate), message_(msg)+ { concrete_type(C_ERROR); }+ Custom_Error(const Custom_Error* ptr)+ : Value(ptr), message_(ptr->message_)+ { concrete_type(C_ERROR); }+ virtual bool operator== (const Expression& rhs) const;+ ATTACH_AST_OPERATIONS(Custom_Error)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////+ // Warnings from Sass_Values.+ //////////////////////////////+ class Custom_Warning : public Value {+ ADD_PROPERTY(std::string, message)+ public:+ Custom_Warning(ParserState pstate, std::string msg)+ : Value(pstate), message_(msg)+ { concrete_type(C_WARNING); }+ Custom_Warning(const Custom_Warning* ptr)+ : Value(ptr), message_(ptr->message_)+ { concrete_type(C_WARNING); }+ virtual bool operator== (const Expression& rhs) const;+ ATTACH_AST_OPERATIONS(Custom_Warning)+ ATTACH_OPERATIONS()+ };++ ////////////+ // Booleans.+ ////////////+ class Boolean : public Value {+ ADD_HASHED(bool, value)+ size_t hash_;+ public:+ Boolean(ParserState pstate, bool val)+ : Value(pstate), value_(val),+ hash_(0)+ { concrete_type(BOOLEAN); }+ Boolean(const Boolean* ptr)+ : Value(ptr),+ value_(ptr->value_),+ hash_(ptr->hash_)+ { concrete_type(BOOLEAN); }+ virtual operator bool() { return value_; }+ std::string type() { return "bool"; }+ static std::string type_name() { return "bool"; }+ virtual bool is_false() { return !value_; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<bool>()(value_);+ }+ return hash_;+ }++ virtual bool operator== (const Expression& rhs) const;++ ATTACH_AST_OPERATIONS(Boolean)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for Sass string values. Includes interpolated and+ // "flat" strings.+ ////////////////////////////////////////////////////////////////////////+ class String : public Value {+ public:+ String(ParserState pstate, bool delayed = false)+ : Value(pstate, delayed)+ { concrete_type(STRING); }+ String(const String* ptr)+ : Value(ptr)+ { concrete_type(STRING); }+ static std::string type_name() { return "string"; }+ virtual ~String() = 0;+ virtual void rtrim() = 0;+ virtual void ltrim() = 0;+ virtual void trim() = 0;+ virtual bool operator==(const Expression& rhs) const = 0;+ virtual bool operator<(const Expression& rhs) const {+ return this->to_string() < rhs.to_string();+ };+ ATTACH_VIRTUAL_AST_OPERATIONS(String);+ ATTACH_OPERATIONS()+ };+ inline String::~String() { };++ ///////////////////////////////////////////////////////////////////////+ // Interpolated strings. Meant to be reduced to flat strings during the+ // evaluation phase.+ ///////////////////////////////////////////////////////////////////////+ class String_Schema : public String, public Vectorized<Expression_Obj> {+ // ADD_PROPERTY(bool, has_interpolants)+ size_t hash_;+ public:+ String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)+ : String(pstate), Vectorized<Expression_Obj>(size), hash_(0)+ { concrete_type(STRING); }+ String_Schema(const String_Schema* ptr)+ : String(ptr),+ Vectorized<Expression_Obj>(*ptr),+ hash_(ptr->hash_)+ { concrete_type(STRING); }++ std::string type() { return "string"; }+ static std::string type_name() { return "string"; }++ bool is_left_interpolant(void) const;+ bool is_right_interpolant(void) const;+ // void has_interpolants(bool tc) { }+ bool has_interpolants() {+ for (auto el : elements()) {+ if (el->is_interpolant()) return true;+ }+ return false;+ }+ virtual void rtrim();+ virtual void ltrim();+ virtual void trim();++ virtual size_t hash()+ {+ if (hash_ == 0) {+ for (auto string : elements())+ hash_combine(hash_, string->hash());+ }+ return hash_;+ }++ virtual void set_delayed(bool delayed) {+ is_delayed(delayed);+ }++ virtual bool operator==(const Expression& rhs) const;+ ATTACH_AST_OPERATIONS(String_Schema)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////+ // Flat strings -- the lowest level of raw textual data.+ ////////////////////////////////////////////////////////+ class String_Constant : public String {+ ADD_PROPERTY(char, quote_mark)+ ADD_PROPERTY(bool, can_compress_whitespace)+ ADD_HASHED(std::string, value)+ protected:+ size_t hash_;+ public:+ String_Constant(const String_Constant* ptr)+ : String(ptr),+ quote_mark_(ptr->quote_mark_),+ can_compress_whitespace_(ptr->can_compress_whitespace_),+ value_(ptr->value_),+ hash_(ptr->hash_)+ { }+ String_Constant(ParserState pstate, std::string val)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)+ { }+ String_Constant(ParserState pstate, const char* beg)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0)+ { }+ String_Constant(ParserState pstate, const char* beg, const char* end)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0)+ { }+ String_Constant(ParserState pstate, const Token& tok)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)+ { }+ std::string type() { return "string"; }+ static std::string type_name() { return "string"; }+ virtual bool is_invisible() const;+ virtual void rtrim();+ virtual void ltrim();+ virtual void trim();++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(value_);+ }+ return hash_;+ }++ virtual bool operator==(const Expression& rhs) const;+ virtual std::string inspect() const; // quotes are forced on inspection++ // static char auto_quote() { return '*'; }+ static char double_quote() { return '"'; }+ static char single_quote() { return '\''; }++ ATTACH_AST_OPERATIONS(String_Constant)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////+ // Possibly quoted string (unquote on instantiation)+ ////////////////////////////////////////////////////////+ class String_Quoted : public String_Constant {+ public:+ String_Quoted(ParserState pstate, std::string val, char q = 0,+ bool keep_utf8_escapes = false, bool skip_unquoting = false,+ bool strict_unquoting = true)+ : String_Constant(pstate, val)+ {+ if (skip_unquoting == false) {+ value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting);+ }+ if (q && quote_mark_) quote_mark_ = q;+ }+ String_Quoted(const String_Quoted* ptr)+ : String_Constant(ptr)+ { }+ virtual bool operator==(const Expression& rhs) const;+ virtual std::string inspect() const; // quotes are forced on inspection+ ATTACH_AST_OPERATIONS(String_Quoted)+ ATTACH_OPERATIONS()+ };++ /////////////////+ // Media queries.+ /////////////////+ class Media_Query : public Expression,+ public Vectorized<Media_Query_Expression_Obj> {+ ADD_PROPERTY(String_Obj, media_type)+ ADD_PROPERTY(bool, is_negated)+ ADD_PROPERTY(bool, is_restricted)+ public:+ Media_Query(ParserState pstate,+ String_Obj t = 0, size_t s = 0, bool n = false, bool r = false)+ : Expression(pstate), Vectorized<Media_Query_Expression_Obj>(s),+ media_type_(t), is_negated_(n), is_restricted_(r)+ { }+ Media_Query(const Media_Query* ptr)+ : Expression(ptr),+ Vectorized<Media_Query_Expression_Obj>(*ptr),+ media_type_(ptr->media_type_),+ is_negated_(ptr->is_negated_),+ is_restricted_(ptr->is_restricted_)+ { }+ ATTACH_AST_OPERATIONS(Media_Query)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////+ // Media expressions (for use inside media queries).+ ////////////////////////////////////////////////////+ class Media_Query_Expression : public Expression {+ ADD_PROPERTY(Expression_Obj, feature)+ ADD_PROPERTY(Expression_Obj, value)+ ADD_PROPERTY(bool, is_interpolated)+ public:+ Media_Query_Expression(ParserState pstate,+ Expression_Obj f, Expression_Obj v, bool i = false)+ : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)+ { }+ Media_Query_Expression(const Media_Query_Expression* ptr)+ : Expression(ptr),+ feature_(ptr->feature_),+ value_(ptr->value_),+ is_interpolated_(ptr->is_interpolated_)+ { }+ ATTACH_AST_OPERATIONS(Media_Query_Expression)+ ATTACH_OPERATIONS()+ };++ ////////////////////+ // `@supports` rule.+ ////////////////////+ class Supports_Block : public Has_Block {+ ADD_PROPERTY(Supports_Condition_Obj, condition)+ public:+ Supports_Block(ParserState pstate, Supports_Condition_Obj condition, Block_Obj block = 0)+ : Has_Block(pstate, block), condition_(condition)+ { statement_type(SUPPORTS); }+ Supports_Block(const Supports_Block* ptr)+ : Has_Block(ptr), condition_(ptr->condition_)+ { statement_type(SUPPORTS); }+ bool bubbles() { return true; }+ ATTACH_AST_OPERATIONS(Supports_Block)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////////////////+ // The abstract superclass of all Supports conditions.+ //////////////////////////////////////////////////////+ class Supports_Condition : public Expression {+ public:+ Supports_Condition(ParserState pstate)+ : Expression(pstate)+ { }+ Supports_Condition(const Supports_Condition* ptr)+ : Expression(ptr)+ { }+ virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; }+ ATTACH_AST_OPERATIONS(Supports_Condition)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////+ // An operator condition (e.g. `CONDITION1 and CONDITION2`).+ ////////////////////////////////////////////////////////////+ class Supports_Operator : public Supports_Condition {+ public:+ enum Operand { AND, OR };+ private:+ ADD_PROPERTY(Supports_Condition_Obj, left);+ ADD_PROPERTY(Supports_Condition_Obj, right);+ ADD_PROPERTY(Operand, operand);+ public:+ Supports_Operator(ParserState pstate, Supports_Condition_Obj l, Supports_Condition_Obj r, Operand o)+ : Supports_Condition(pstate), left_(l), right_(r), operand_(o)+ { }+ Supports_Operator(const Supports_Operator* ptr)+ : Supports_Condition(ptr),+ left_(ptr->left_),+ right_(ptr->right_),+ operand_(ptr->operand_)+ { }+ virtual bool needs_parens(Supports_Condition_Obj cond) const;+ ATTACH_AST_OPERATIONS(Supports_Operator)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////+ // A negation condition (`not CONDITION`).+ //////////////////////////////////////////+ class Supports_Negation : public Supports_Condition {+ private:+ ADD_PROPERTY(Supports_Condition_Obj, condition);+ public:+ Supports_Negation(ParserState pstate, Supports_Condition_Obj c)+ : Supports_Condition(pstate), condition_(c)+ { }+ Supports_Negation(const Supports_Negation* ptr)+ : Supports_Condition(ptr), condition_(ptr->condition_)+ { }+ virtual bool needs_parens(Supports_Condition_Obj cond) const;+ ATTACH_AST_OPERATIONS(Supports_Negation)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////+ // A declaration condition (e.g. `(feature: value)`).+ /////////////////////////////////////////////////////+ class Supports_Declaration : public Supports_Condition {+ private:+ ADD_PROPERTY(Expression_Obj, feature);+ ADD_PROPERTY(Expression_Obj, value);+ public:+ Supports_Declaration(ParserState pstate, Expression_Obj f, Expression_Obj v)+ : Supports_Condition(pstate), feature_(f), value_(v)+ { }+ Supports_Declaration(const Supports_Declaration* ptr)+ : Supports_Condition(ptr),+ feature_(ptr->feature_),+ value_(ptr->value_)+ { }+ virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; }+ ATTACH_AST_OPERATIONS(Supports_Declaration)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////+ // An interpolation condition (e.g. `#{$var}`).+ ///////////////////////////////////////////////+ class Supports_Interpolation : public Supports_Condition {+ private:+ ADD_PROPERTY(Expression_Obj, value);+ public:+ Supports_Interpolation(ParserState pstate, Expression_Obj v)+ : Supports_Condition(pstate), value_(v)+ { }+ Supports_Interpolation(const Supports_Interpolation* ptr)+ : Supports_Condition(ptr),+ value_(ptr->value_)+ { }+ virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; }+ ATTACH_AST_OPERATIONS(Supports_Interpolation)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////+ // At root expressions (for use inside @at-root).+ /////////////////////////////////////////////////+ class At_Root_Query : public Expression {+ private:+ ADD_PROPERTY(Expression_Obj, feature)+ ADD_PROPERTY(Expression_Obj, value)+ public:+ At_Root_Query(ParserState pstate, Expression_Obj f = 0, Expression_Obj v = 0, bool i = false)+ : Expression(pstate), feature_(f), value_(v)+ { }+ At_Root_Query(const At_Root_Query* ptr)+ : Expression(ptr),+ feature_(ptr->feature_),+ value_(ptr->value_)+ { }+ bool exclude(std::string str);+ ATTACH_AST_OPERATIONS(At_Root_Query)+ ATTACH_OPERATIONS()+ };++ ///////////+ // At-root.+ ///////////+ class At_Root_Block : public Has_Block {+ ADD_PROPERTY(At_Root_Query_Obj, expression)+ public:+ At_Root_Block(ParserState pstate, Block_Obj b = 0, At_Root_Query_Obj e = 0)+ : Has_Block(pstate, b), expression_(e)+ { statement_type(ATROOT); }+ At_Root_Block(const At_Root_Block* ptr)+ : Has_Block(ptr), expression_(ptr->expression_)+ { statement_type(ATROOT); }+ bool bubbles() { return true; }+ bool exclude_node(Statement_Obj s) {+ if (expression() == 0)+ {+ return s->statement_type() == Statement::RULESET;+ }++ if (s->statement_type() == Statement::DIRECTIVE)+ {+ if (Directive_Obj dir = SASS_MEMORY_CAST(Directive, s))+ {+ std::string keyword(dir->keyword());+ if (keyword.length() > 0) keyword.erase(0, 1);+ return expression()->exclude(keyword);+ }+ }+ if (s->statement_type() == Statement::MEDIA)+ {+ return expression()->exclude("media");+ }+ if (s->statement_type() == Statement::RULESET)+ {+ return expression()->exclude("rule");+ }+ if (s->statement_type() == Statement::SUPPORTS)+ {+ return expression()->exclude("supports");+ }+ if (Directive_Obj dir = SASS_MEMORY_CAST(Directive, s))+ {+ if (dir->is_keyframes()) return expression()->exclude("keyframes");+ }+ return false;+ }+ ATTACH_AST_OPERATIONS(At_Root_Block)+ ATTACH_OPERATIONS()+ };++ //////////////////+ // The null value.+ //////////////////+ class Null : public Value {+ public:+ Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); }+ Null(const Null* ptr) : Value(ptr) { concrete_type(NULL_VAL); }+ std::string type() { return "null"; }+ static std::string type_name() { return "null"; }+ bool is_invisible() const { return true; }+ operator bool() { return false; }+ bool is_false() { return true; }++ virtual size_t hash()+ {+ return -1;+ }++ virtual bool operator== (const Expression& rhs) const;++ ATTACH_AST_OPERATIONS(Null)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////+ // Thunks for delayed evaluation.+ /////////////////////////////////+ class Thunk : public Expression {+ ADD_PROPERTY(Expression_Obj, expression)+ ADD_PROPERTY(Env*, environment)+ public:+ Thunk(ParserState pstate, Expression_Obj exp, Env* env = 0)+ : Expression(pstate), expression_(exp), environment_(env)+ { }+ };++ /////////////////////////////////////////////////////////+ // Individual parameter objects for mixins and functions.+ /////////////////////////////////////////////////////////+ class Parameter : public AST_Node {+ ADD_PROPERTY(std::string, name)+ ADD_PROPERTY(Expression_Obj, default_value)+ ADD_PROPERTY(bool, is_rest_parameter)+ public:+ Parameter(ParserState pstate,+ std::string n, Expression_Obj def = 0, bool rest = false)+ : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)+ {+ if (default_value_ && is_rest_parameter_) {+ error("variable-length parameter may not have a default value", pstate_);+ }+ }+ Parameter(const Parameter* ptr)+ : AST_Node(ptr),+ name_(ptr->name_),+ default_value_(ptr->default_value_),+ is_rest_parameter_(ptr->is_rest_parameter_)+ {+ if (default_value_ && is_rest_parameter_) {+ error("variable-length parameter may not have a default value", pstate_);+ }+ }+ ATTACH_AST_OPERATIONS(Parameter)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////////+ // Parameter lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all optional parameters follow all+ // required parameters).+ /////////////////////////////////////////////////////////////////////////+ class Parameters : public AST_Node, public Vectorized<Parameter_Obj> {+ ADD_PROPERTY(bool, has_optional_parameters)+ ADD_PROPERTY(bool, has_rest_parameter)+ protected:+ void adjust_after_pushing(Parameter_Obj p)+ {+ if (p->default_value()) {+ if (has_rest_parameter_) {+ error("optional parameters may not be combined with variable-length parameters", p->pstate());+ }+ has_optional_parameters_ = true;+ }+ else if (p->is_rest_parameter()) {+ if (has_rest_parameter_) {+ error("functions and mixins cannot have more than one variable-length parameter", p->pstate());+ }+ has_rest_parameter_ = true;+ }+ else {+ if (has_rest_parameter_) {+ error("required parameters must precede variable-length parameters", p->pstate());+ }+ if (has_optional_parameters_) {+ error("required parameters must precede optional parameters", p->pstate());+ }+ }+ }+ public:+ Parameters(ParserState pstate)+ : AST_Node(pstate),+ Vectorized<Parameter_Obj>(),+ has_optional_parameters_(false),+ has_rest_parameter_(false)+ { }+ Parameters(const Parameters* ptr)+ : AST_Node(ptr),+ Vectorized<Parameter_Obj>(*ptr),+ has_optional_parameters_(ptr->has_optional_parameters_),+ has_rest_parameter_(ptr->has_rest_parameter_)+ { }+ ATTACH_AST_OPERATIONS(Parameters)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////+ // Abstract base class for CSS selectors.+ /////////////////////////////////////////+ class Selector : public Expression {+ // ADD_PROPERTY(bool, has_reference)+ // line break before list separator+ ADD_PROPERTY(bool, has_line_feed)+ // line break after list separator+ ADD_PROPERTY(bool, has_line_break)+ // maybe we have optional flag+ ADD_PROPERTY(bool, is_optional)+ // parent block pointers++ // must not be a reference counted object+ // otherwise we create circular references+ ADD_PROPERTY(Media_Block_Ptr, media_block)+ protected:+ size_t hash_;+ public:+ Selector(ParserState pstate, bool r = false, bool h = false)+ : Expression(pstate),+ // has_reference_(r),+ has_line_feed_(false),+ has_line_break_(false),+ is_optional_(false),+ media_block_(0),+ hash_(0)+ { concrete_type(SELECTOR); }+ Selector(const Selector* ptr)+ : Expression(ptr),+ // has_reference_(ptr->has_reference_),+ has_line_feed_(ptr->has_line_feed_),+ has_line_break_(ptr->has_line_break_),+ is_optional_(ptr->is_optional_),+ media_block_(ptr->media_block_),+ hash_(ptr->hash_)+ { concrete_type(SELECTOR); }+ virtual ~Selector() = 0;+ virtual size_t hash() = 0;+ virtual unsigned long specificity() {+ return 0;+ }+ virtual void set_media_block(Media_Block_Ptr mb) {+ media_block(mb);+ }+ virtual bool has_parent_ref() {+ return false;+ }+ virtual bool has_real_parent_ref() {+ return false;+ }+ // dispatch to correct handlers+ virtual bool operator<(const Selector& rhs) const;+ virtual bool operator==(const Selector& rhs) const;+ ATTACH_VIRTUAL_AST_OPERATIONS(Selector);+ };+ inline Selector::~Selector() { }++ /////////////////////////////////////////////////////////////////////////+ // Interpolated selectors -- the interpolated String will be expanded and+ // re-parsed into a normal selector class.+ /////////////////////////////////////////////////////////////////////////+ class Selector_Schema : public Selector {+ ADD_PROPERTY(String_Obj, contents)+ ADD_PROPERTY(bool, at_root);+ public:+ Selector_Schema(ParserState pstate, String_Obj c)+ : Selector(pstate), contents_(c), at_root_(false)+ { }+ Selector_Schema(const Selector_Schema* ptr)+ : Selector(ptr),+ contents_(ptr->contents_),+ at_root_(ptr->at_root_)+ { }+ virtual bool has_parent_ref();+ virtual bool has_real_parent_ref();+ virtual size_t hash() {+ if (hash_ == 0) {+ hash_combine(hash_, contents_->hash());+ }+ return hash_;+ }+ ATTACH_AST_OPERATIONS(Selector_Schema)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////+ // Abstract base class for simple selectors.+ ////////////////////////////////////////////+ class Simple_Selector : public Selector {+ ADD_PROPERTY(std::string, ns)+ ADD_PROPERTY(std::string, name)+ ADD_PROPERTY(Simple_Type, simple_type)+ ADD_PROPERTY(bool, has_ns)+ public:+ Simple_Selector(ParserState pstate, std::string n = "")+ : Selector(pstate), ns_(""), name_(n), has_ns_(false)+ {+ simple_type(SIMPLE);+ size_t pos = n.find('|');+ // found some namespace+ if (pos != std::string::npos) {+ has_ns_ = true;+ ns_ = n.substr(0, pos);+ name_ = n.substr(pos + 1);+ }+ }+ Simple_Selector(const Simple_Selector* ptr)+ : Selector(ptr),+ ns_(ptr->ns_),+ name_(ptr->name_),+ has_ns_(ptr->has_ns_)+ { simple_type(SIMPLE); }+ virtual bool unique() const+ {+ return false;+ }+ virtual std::string ns_name() const+ {+ std::string name("");+ if (has_ns_)+ name += ns_ + "|";+ return name + name_;+ }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_combine(hash_, std::hash<int>()(SELECTOR));+ hash_combine(hash_, std::hash<std::string>()(ns()));+ hash_combine(hash_, std::hash<std::string>()(name()));+ }+ return hash_;+ }+ // namespace query functions+ bool is_universal_ns() const+ {+ return has_ns_ && ns_ == "*";+ }+ bool has_universal_ns() const+ {+ return !has_ns_ || ns_ == "*";+ }+ bool is_empty_ns() const+ {+ return !has_ns_ || ns_ == "";+ }+ bool has_empty_ns() const+ {+ return has_ns_ && ns_ == "";+ }+ bool has_qualified_ns() const+ {+ return has_ns_ && ns_ != "" && ns_ != "*";+ }+ // name query functions+ bool is_universal() const+ {+ return name_ == "*";+ }++ virtual bool has_placeholder() {+ return false;+ }++ virtual ~Simple_Selector() = 0;+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ virtual bool has_parent_ref() { return false; };+ virtual bool has_real_parent_ref() { return false; };+ virtual bool is_pseudo_element() { return false; }+ virtual bool is_pseudo_class() { return false; }++ virtual bool is_superselector_of(Compound_Selector_Obj sub) { return false; }++ virtual bool operator==(const Selector& rhs) const;+ virtual bool operator==(const Simple_Selector& rhs) const;+ inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); }++ bool operator<(const Selector& rhs) const;+ bool operator<(const Simple_Selector& rhs) const;+ // default implementation should work for most of the simple selectors (otherwise overload)+ ATTACH_VIRTUAL_AST_OPERATIONS(Simple_Selector);+ ATTACH_OPERATIONS();+ };+ inline Simple_Selector::~Simple_Selector() { }+++ //////////////////////////////////+ // The Parent Selector Expression.+ //////////////////////////////////+ // parent selectors can occur in selectors but also+ // inside strings in declarations (Compound_Selector).+ // only one simple parent selector means the first case.+ class Parent_Selector : public Simple_Selector {+ ADD_PROPERTY(bool, real)+ public:+ Parent_Selector(ParserState pstate, bool r = true)+ : Simple_Selector(pstate, "&"), real_(r)+ { /* has_reference(true); */ }+ Parent_Selector(const Parent_Selector* ptr)+ : Simple_Selector(ptr), real_(ptr->real_)+ { /* has_reference(true); */ }+ bool is_real_parent_ref() { return real(); };+ virtual bool has_parent_ref() { return true; };+ virtual bool has_real_parent_ref() { return is_real_parent_ref(); };+ virtual unsigned long specificity()+ {+ return 0;+ }+ std::string type() { return "selector"; }+ static std::string type_name() { return "selector"; }+ ATTACH_AST_OPERATIONS(Parent_Selector)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////////+ // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.+ /////////////////////////////////////////////////////////////////////////+ class Placeholder_Selector : public Simple_Selector {+ public:+ Placeholder_Selector(ParserState pstate, std::string n)+ : Simple_Selector(pstate, n)+ { }+ Placeholder_Selector(const Placeholder_Selector* ptr)+ : Simple_Selector(ptr)+ { }+ virtual unsigned long specificity()+ {+ return Constants::Specificity_Base;+ }+ virtual bool has_placeholder() {+ return true;+ }+ virtual ~Placeholder_Selector() {};+ ATTACH_AST_OPERATIONS(Placeholder_Selector)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////+ // Element selectors (and the universal selector) -- e.g., div, span, *.+ /////////////////////////////////////////////////////////////////////+ class Element_Selector : public Simple_Selector {+ public:+ Element_Selector(ParserState pstate, std::string n)+ : Simple_Selector(pstate, n)+ { }+ Element_Selector(const Element_Selector* ptr)+ : Simple_Selector(ptr)+ { }+ virtual unsigned long specificity()+ {+ if (name() == "*") return 0;+ else return Constants::Specificity_Element;+ }+ virtual Simple_Selector_Ptr unify_with(Simple_Selector_Ptr, Context&);+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ ATTACH_AST_OPERATIONS(Element_Selector)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////+ // Class selectors -- i.e., .foo.+ ////////////////////////////////////////////////+ class Class_Selector : public Simple_Selector {+ public:+ Class_Selector(ParserState pstate, std::string n)+ : Simple_Selector(pstate, n)+ { }+ Class_Selector(const Class_Selector* ptr)+ : Simple_Selector(ptr)+ { }+ virtual bool unique() const+ {+ return false;+ }+ virtual unsigned long specificity()+ {+ return Constants::Specificity_Class;+ }+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ ATTACH_AST_OPERATIONS(Class_Selector)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////+ // ID selectors -- i.e., #foo.+ ////////////////////////////////////////////////+ class Id_Selector : public Simple_Selector {+ public:+ Id_Selector(ParserState pstate, std::string n)+ : Simple_Selector(pstate, n)+ { }+ Id_Selector(const Id_Selector* ptr)+ : Simple_Selector(ptr)+ { }+ virtual bool unique() const+ {+ return true;+ }+ virtual unsigned long specificity()+ {+ return Constants::Specificity_ID;+ }+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ ATTACH_AST_OPERATIONS(Id_Selector)+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////+ // Attribute selectors -- e.g., [src*=".jpg"], etc.+ ///////////////////////////////////////////////////+ class Attribute_Selector : public Simple_Selector {+ ADD_PROPERTY(std::string, matcher)+ // this cannot be changed to obj atm!!!!!!????!!!!!!!+ ADD_PROPERTY(String_Obj, value) // might be interpolated+ public:+ Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v)+ : Simple_Selector(pstate, n), matcher_(m), value_(v)+ { simple_type(ATTR_SEL); }+ Attribute_Selector(const Attribute_Selector* ptr)+ : Simple_Selector(ptr),+ matcher_(ptr->matcher_),+ value_(ptr->value_)+ { simple_type(ATTR_SEL); }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_combine(hash_, Simple_Selector::hash());+ hash_combine(hash_, std::hash<std::string>()(matcher()));+ if (value_) hash_combine(hash_, value_->hash());+ }+ return hash_;+ }+ virtual unsigned long specificity()+ {+ return Constants::Specificity_Attr;+ }+ virtual bool operator==(const Simple_Selector& rhs) const;+ virtual bool operator==(const Attribute_Selector& rhs) const;+ virtual bool operator<(const Simple_Selector& rhs) const;+ virtual bool operator<(const Attribute_Selector& rhs) const;+ ATTACH_AST_OPERATIONS(Attribute_Selector)+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////////////////////////////+ // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.+ //////////////////////////////////////////////////////////////////+ /* '::' starts a pseudo-element, ':' a pseudo-class */+ /* Except :first-line, :first-letter, :before and :after */+ /* Note that pseudo-elements are restricted to one per selector */+ /* and occur only in the last simple_selector_sequence. */+ inline bool is_pseudo_class_element(const std::string& name)+ {+ return name == ":before" ||+ name == ":after" ||+ name == ":first-line" ||+ name == ":first-letter";+ }++ // Pseudo Selector cannot have any namespace?+ class Pseudo_Selector : public Simple_Selector {+ ADD_PROPERTY(String_Obj, expression)+ public:+ Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = 0)+ : Simple_Selector(pstate, n), expression_(expr)+ { simple_type(PSEUDO_SEL); }+ Pseudo_Selector(const Pseudo_Selector* ptr)+ : Simple_Selector(ptr), expression_(ptr->expression_)+ { simple_type(PSEUDO_SEL); }++ // A pseudo-class always consists of a "colon" (:) followed by the name+ // of the pseudo-class and optionally by a value between parentheses.+ virtual bool is_pseudo_class()+ {+ return (name_[0] == ':' && name_[1] != ':')+ && ! is_pseudo_class_element(name_);+ }++ // A pseudo-element is made of two colons (::) followed by the name.+ // The `::` notation is introduced by the current document in order to+ // establish a discrimination between pseudo-classes and pseudo-elements.+ // For compatibility with existing style sheets, user agents must also+ // accept the previous one-colon notation for pseudo-elements introduced+ // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and+ // :after). This compatibility is not allowed for the new pseudo-elements+ // introduced in this specification.+ virtual bool is_pseudo_element()+ {+ return (name_[0] == ':' && name_[1] == ':')+ || is_pseudo_class_element(name_);+ }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_combine(hash_, Simple_Selector::hash());+ if (expression_) hash_combine(hash_, expression_->hash());+ }+ return hash_;+ }+ virtual unsigned long specificity()+ {+ if (is_pseudo_element())+ return Constants::Specificity_Element;+ return Constants::Specificity_Pseudo;+ }+ virtual bool operator==(const Simple_Selector& rhs) const;+ virtual bool operator==(const Pseudo_Selector& rhs) const;+ virtual bool operator<(const Simple_Selector& rhs) const;+ virtual bool operator<(const Pseudo_Selector& rhs) const;+ virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr, Context&);+ ATTACH_AST_OPERATIONS(Pseudo_Selector)+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////+ // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir)+ /////////////////////////////////////////////////+ class Wrapped_Selector : public Simple_Selector {+ ADD_PROPERTY(Selector_Obj, selector)+ public:+ Wrapped_Selector(ParserState pstate, std::string n, Selector_Obj sel)+ : Simple_Selector(pstate, n), selector_(sel)+ { simple_type(WRAPPED_SEL); }+ Wrapped_Selector(const Wrapped_Selector* ptr)+ : Simple_Selector(ptr), selector_(ptr->selector_)+ { simple_type(WRAPPED_SEL); }+ virtual bool is_superselector_of(Wrapped_Selector_Obj sub);+ // Selectors inside the negation pseudo-class are counted like any+ // other, but the negation itself does not count as a pseudo-class.+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_combine(hash_, Simple_Selector::hash());+ if (selector_) hash_combine(hash_, selector_->hash());+ }+ return hash_;+ }+ virtual bool has_parent_ref() {+ // if (has_reference()) return true;+ if (!selector()) return false;+ return selector()->has_parent_ref();+ }+ virtual bool has_real_parent_ref() {+ // if (has_reference()) return true;+ if (!selector()) return false;+ return selector()->has_real_parent_ref();+ }+ virtual unsigned long specificity()+ {+ return selector_ ? selector_->specificity() : 0;+ }+ virtual bool operator==(const Simple_Selector& rhs) const;+ virtual bool operator==(const Wrapped_Selector& rhs) const;+ virtual bool operator<(const Simple_Selector& rhs) const;+ virtual bool operator<(const Wrapped_Selector& rhs) const;+ virtual void cloneChildren();+ ATTACH_AST_OPERATIONS(Wrapped_Selector)+ ATTACH_OPERATIONS()+ };++ struct Complex_Selector_Pointer_Compare {+ bool operator() (const Complex_Selector_Obj& pLeft, const Complex_Selector_Obj& pRight) const;+ };++ ////////////////////////////////////////////////////////////////////////////+ // Simple selector sequences. Maintains flags indicating whether it contains+ // any parent references or placeholders, to simplify expansion.+ ////////////////////////////////////////////////////////////////////////////+ typedef std::set<Complex_Selector_Obj, Complex_Selector_Pointer_Compare> SourcesSet;+ class Compound_Selector : public Selector, public Vectorized<Simple_Selector_Obj> {+ private:+ SourcesSet sources_;+ ADD_PROPERTY(bool, extended);+ ADD_PROPERTY(bool, has_parent_reference);+ protected:+ void adjust_after_pushing(Simple_Selector_Obj s)+ {+ // if (s->has_reference()) has_reference(true);+ // if (s->has_placeholder()) has_placeholder(true);+ }+ public:+ Compound_Selector(ParserState pstate, size_t s = 0)+ : Selector(pstate),+ Vectorized<Simple_Selector_Obj>(s),+ extended_(false),+ has_parent_reference_(false)+ { }+ Compound_Selector(const Compound_Selector* ptr)+ : Selector(ptr),+ Vectorized<Simple_Selector_Obj>(*ptr),+ extended_(ptr->extended_),+ has_parent_reference_(ptr->has_parent_reference_)+ { }+ bool contains_placeholder() {+ for (size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->has_placeholder()) return true;+ }+ return false;+ };++ void append(Simple_Selector_Ptr element);++ bool is_universal() const+ {+ return length() == 1 && (*this)[0]->is_universal();+ }++ Complex_Selector_Obj to_complex();+ Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs, Context& ctx);+ // virtual Placeholder_Selector_Ptr find_placeholder();+ virtual bool has_parent_ref();+ virtual bool has_real_parent_ref();+ Simple_Selector_Ptr base()+ {+ // Implement non-const in terms of const. Safe to const_cast since this method is non-const+ return const_cast<Simple_Selector_Ptr>(static_cast<Compound_Selector_Ptr_Const>(this)->base());+ }+ Simple_Selector_Ptr_Const base() const {+ if (length() == 0) return 0;+ // ToDo: why is this needed?+ if (SASS_MEMORY_CAST(Element_Selector, (*this)[0]))+ return &(*this)[0];+ return 0;+ }+ virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapped = "");+ virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapped = "");+ virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapped = "");+ virtual size_t hash()+ {+ if (Selector::hash_ == 0) {+ hash_combine(Selector::hash_, std::hash<int>()(SELECTOR));+ if (length()) hash_combine(Selector::hash_, Vectorized::hash());+ }+ return Selector::hash_;+ }+ virtual unsigned long specificity()+ {+ int sum = 0;+ for (size_t i = 0, L = length(); i < L; ++i)+ { sum += (*this)[i]->specificity(); }+ return sum;+ }++ virtual bool has_placeholder()+ {+ if (length() == 0) return false;+ if (Simple_Selector_Obj ss = elements().front()) {+ if (ss->has_placeholder()) return true;+ }+ return false;+ }++ bool is_empty_reference()+ {+ return length() == 1 &&+ SASS_MEMORY_CAST(Parent_Selector, (*this)[0]);+ }+ std::vector<Subset_Map_Key> to_str_vec(); // sometimes need to convert to a flat "by-value" data structure++ virtual bool operator<(const Compound_Selector& rhs) const;+ virtual bool operator==(const Compound_Selector& rhs) const;+ inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); }++ SourcesSet& sources() { return sources_; }+ void clearSources() { sources_.clear(); }+ void mergeSources(SourcesSet& sources, Context& ctx);++ Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs, Context& ctx);+ virtual void cloneChildren();+ ATTACH_AST_OPERATIONS(Compound_Selector)+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // General selectors -- i.e., simple sequences combined with one of the four+ // CSS selector combinators (">", "+", "~", and whitespace). Essentially a+ // linked list.+ ////////////////////////////////////////////////////////////////////////////+ class Complex_Selector : public Selector {+ public:+ enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE };+ private:+ ADD_PROPERTY(Combinator, combinator)+ ADD_PROPERTY(Compound_Selector_Obj, head)+ ADD_PROPERTY(Complex_Selector_Obj, tail)+ ADD_PROPERTY(String_Obj, reference);+ public:+ bool contains_placeholder() {+ if (head() && head()->contains_placeholder()) return true;+ if (tail() && tail()->contains_placeholder()) return true;+ return false;+ };+ Complex_Selector(ParserState pstate,+ Combinator c = ANCESTOR_OF,+ Compound_Selector_Obj h = 0,+ Complex_Selector_Obj t = 0,+ String_Obj r = 0)+ : Selector(pstate),+ combinator_(c),+ head_(h), tail_(t),+ reference_(r)+ {}+ Complex_Selector(const Complex_Selector* ptr)+ : Selector(ptr),+ combinator_(ptr->combinator_),+ head_(ptr->head_), tail_(ptr->tail_),+ reference_(ptr->reference_)+ {};+ virtual bool has_parent_ref();+ virtual bool has_real_parent_ref();++ Complex_Selector_Obj skip_empty_reference()+ {+ if ((!head_ || !head_->length() || head_->is_empty_reference()) &&+ combinator() == Combinator::ANCESTOR_OF)+ {+ if (!tail_) return 0;+ tail_->has_line_feed_ = this->has_line_feed_;+ // tail_->has_line_break_ = this->has_line_break_;+ return tail_->skip_empty_reference();+ }+ return this;+ }++ // can still have a tail+ bool is_empty_ancestor() const+ {+ return (!head() || head()->length() == 0) &&+ combinator() == Combinator::ANCESTOR_OF;+ }++ Complex_Selector_Obj context(Context&);+++ Selector_List_Ptr tails(Context& ctx, Selector_List_Ptr tails);++ // front returns the first real tail+ // skips over parent and empty ones+ Complex_Selector_Obj first();+ // last returns the last real tail+ Complex_Selector_Obj last();++ // some shortcuts that should be removed+ Complex_Selector_Obj innermost() { return last(); };++ size_t length() const;+ Selector_List_Ptr resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);+ virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");+ virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");+ virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");+ Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs, Context& ctx);+ Combinator clear_innermost();+ void append(Context&, Complex_Selector_Obj);+ void set_innermost(Complex_Selector_Obj, Combinator);+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_combine(hash_, std::hash<int>()(SELECTOR));+ hash_combine(hash_, std::hash<int>()(combinator_));+ if (head_) hash_combine(hash_, head_->hash());+ if (tail_) hash_combine(hash_, tail_->hash());+ }+ return hash_;+ }+ virtual unsigned long specificity() const+ {+ int sum = 0;+ if (head()) sum += head()->specificity();+ if (tail()) sum += tail()->specificity();+ return sum;+ }+ virtual void set_media_block(Media_Block_Ptr mb) {+ media_block(mb);+ if (tail_) tail_->set_media_block(mb);+ if (head_) head_->set_media_block(mb);+ }+ virtual bool has_placeholder() {+ if (head_ && head_->has_placeholder()) return true;+ if (tail_ && tail_->has_placeholder()) return true;+ return false;+ }+ virtual bool operator<(const Complex_Selector& rhs) const;+ virtual bool operator==(const Complex_Selector& rhs) const;+ inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }+ SourcesSet sources()+ {+ //s = Set.new+ //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}+ //s++ SourcesSet srcs;++ Compound_Selector_Obj pHead = head();+ Complex_Selector_Obj pTail = tail();++ if (pHead) {+ SourcesSet& headSources = pHead->sources();+ srcs.insert(headSources.begin(), headSources.end());+ }++ if (pTail) {+ SourcesSet tailSources = pTail->sources();+ srcs.insert(tailSources.begin(), tailSources.end());+ }++ return srcs;+ }+ void addSources(SourcesSet& sources, Context& ctx) {+ // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}+ Complex_Selector_Ptr pIter = this;+ while (pIter) {+ Compound_Selector_Ptr pHead = &pIter->head();++ if (pHead) {+ pHead->mergeSources(sources, ctx);+ }++ pIter = &pIter->tail();+ }+ }+ void clearSources() {+ Complex_Selector_Ptr pIter = this;+ while (pIter) {+ Compound_Selector_Ptr pHead = &pIter->head();++ if (pHead) {+ pHead->clearSources();+ }++ pIter = &pIter->tail();+ }+ }++ virtual void cloneChildren();+ ATTACH_AST_OPERATIONS(Complex_Selector)+ ATTACH_OPERATIONS()+ };++ typedef std::deque<Complex_Selector_Obj> ComplexSelectorDeque;++ ///////////////////////////////////+ // Comma-separated selector groups.+ ///////////////////////////////////+ class Selector_List : public Selector, public Vectorized<Complex_Selector_Obj> {+ ADD_PROPERTY(std::vector<std::string>, wspace)+ protected:+ void adjust_after_pushing(Complex_Selector_Obj c);+ public:+ Selector_List(ParserState pstate, size_t s = 0)+ : Selector(pstate), Vectorized<Complex_Selector_Obj>(s), wspace_(0)+ { }+ Selector_List(const Selector_List* ptr)+ : Selector(ptr),+ Vectorized<Complex_Selector_Obj>(*ptr),+ wspace_(ptr->wspace_)+ { }+ std::string type() { return "list"; }+ // remove parent selector references+ // basically unwraps parsed selectors+ virtual bool has_parent_ref();+ virtual bool has_real_parent_ref();+ void remove_parent_selectors();+ Selector_List_Ptr resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);+ virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");+ virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");+ virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");+ Selector_List_Ptr unify_with(Selector_List_Ptr, Context&);+ void populate_extends(Selector_List_Obj, Context&, Subset_Map&);+ virtual size_t hash()+ {+ if (Selector::hash_ == 0) {+ hash_combine(Selector::hash_, std::hash<int>()(SELECTOR));+ hash_combine(Selector::hash_, Vectorized::hash());+ }+ return Selector::hash_;+ }+ virtual unsigned long specificity()+ {+ unsigned long sum = 0;+ unsigned long specificity = 0;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ specificity = (*this)[i]->specificity();+ if (sum < specificity) sum = specificity;+ }+ return sum;+ }+ virtual void set_media_block(Media_Block_Ptr mb) {+ media_block(mb);+ for (Complex_Selector_Obj cs : elements()) {+ cs->set_media_block(mb);+ }+ }+ virtual bool has_placeholder() {+ for (Complex_Selector_Obj cs : elements()) {+ if (cs->has_placeholder()) return true;+ }+ return false;+ }+ virtual bool operator<(const Selector& rhs) const;+ virtual bool operator==(const Selector& rhs) const;+ virtual bool operator<(const Selector_List& rhs) const;+ virtual bool operator==(const Selector_List& rhs) const;+ // Selector Lists can be compared to comma lists+ virtual bool operator==(const Expression& rhs) const;+ virtual void cloneChildren();+ ATTACH_AST_OPERATIONS(Selector_List)+ ATTACH_OPERATIONS()+ };++ template<typename SelectorType>+ bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) {+ // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector+ // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but+ // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting+ // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the+ // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the+ // members of a SimpleSequence (Compound_Selector)).+ //+ // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for+ // use with Compound_Selector and Complex_Selector objects.+ if (simpleSelectorOrderDependent) {+ return !(one < two) && !(two < one);+ } else {+ return one == two;+ }+ }++ // compare function for sorting and probably other other uses+ struct cmp_complex_selector { inline bool operator() (const Complex_Selector_Obj l, const Complex_Selector_Obj r) { return (*l < *r); } };+ struct cmp_compound_selector { inline bool operator() (const Compound_Selector_Obj l, const Compound_Selector_Obj r) { return (*l < *r); } };+ struct cmp_simple_selector { inline bool operator() (const Simple_Selector_Obj l, const Simple_Selector_Obj r) { return (*l < *r); } }; }
libsass/src/ast_def_macros.hpp view
@@ -28,13 +28,13 @@ #define ATTACH_OPERATIONS()\ virtual void perform(Operation<void>* op) { (*op)(this); }\-virtual AST_Node* perform(Operation<AST_Node*>* op) { return (*op)(this); }\-virtual Statement* perform(Operation<Statement*>* op) { return (*op)(this); }\-virtual Expression* perform(Operation<Expression*>* op) { return (*op)(this); }\-virtual Selector* perform(Operation<Selector*>* op) { return (*op)(this); }\+virtual AST_Node_Ptr perform(Operation<AST_Node_Ptr>* op) { return (*op)(this); }\+virtual Statement_Ptr perform(Operation<Statement_Ptr>* op) { return (*op)(this); }\+virtual Expression_Ptr perform(Operation<Expression_Ptr>* op) { return (*op)(this); }\+virtual Selector_Ptr perform(Operation<Selector_Ptr>* op) { return (*op)(this); }\ virtual std::string perform(Operation<std::string>* op) { return (*op)(this); }\ virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) { return (*op)(this); }\-virtual Value* perform(Operation<Value*>* op) { return (*op)(this); }+virtual Value_Ptr perform(Operation<Value_Ptr>* op) { return (*op)(this); } #define ADD_PROPERTY(type, name)\ protected:\
− libsass/src/ast_factory.hpp
@@ -1,93 +0,0 @@-#ifndef SASS_AST_FACTORY_H-#define SASS_AST_FACTORY_H--#include <vector>--#include "ast.hpp"--namespace Sass {-- class AST_Factory {- std::vector<AST_Node*> nodes;- public:- // statements- Block* new_Block(std::string p, size_t l, size_t s = 0, bool r = false);- Ruleset* new_Ruleset(std::string p, size_t l, Selector* s, Block* b);- Propset* new_Propset(std::string p, size_t l, String* pf, Block* b);- Supports_Query* new_Supports_Query(std::string p, size_t l, Supports_Query* f, Block* b);- Media_Query* new_Media_Query(std::string p, size_t l, List* q, Block* b);- At_Root_Block* new_At_Root_Block(std::string p, size_t l, Selector* sel, Block* b);- At_Rule* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b);- Keyframe_Rule* new_Keyframe_Rule(std::string p, size_t l, Block* b);- Declaration* new_Declaration(std::string p, size_t l, String* prop, List* vals);- Assignment* new_Assignment(std::string p, size_t l, std::string var, Expression* val, bool guarded = false);- Import<Function_Call*>* new_CSS_Import(std::string p, size_t l, Function_Call* loc);- Import<String*>* new_SASS_Import(std::string p, size_t l, String* loc);- Custom_Warning* new_Custom_Warning(std::string msg, size_t l, std::string msg);- Custom_Error* new_Custom_Error(std::string p, size_t l, std::string msg);- Warning* new_Warning(std::string p, size_t l, Expression* msg);- Error* new_Error(std::string p, size_t l, Expression* msg);- Debug* new_Debug(std::string p, size_t l, Expression* val);- Comment* new_Comment(std::string p, size_t l, String* txt);- If* new_If(std::string p, size_t l, Expression* pred, Block* con, Block* alt = 0);- For* new_For(std::string p, size_t l, std::string var, Expression* lo, Expression* hi, Block* b, bool inc);- Each* new_Each(std::string p, size_t l, std::vector<std::string> vars, Expression* lst, Block* b);- While* new_While(std::string p, size_t l, Expression* pred, Block* b);- Extension* new_Extension(std::string p, size_t l, Selector* s);- Definition<MIXIN>* new_Mixin_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b);- Definition<FUNCTION>* new_Function_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b);- Mixin_Call* new_Mixin_Call(std::string p, size_t l, std::string n, Arguments* args, Block* b = 0);- // expressions- List* new_List(std::string p, size_t l, size_t size = 0, enum Sass_Separator sep = List::space, bool argl = false);- Map* new_Map(std::string p, size_t l, size_t size = 0);- Binary_Expression<AND>* new_And(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<OR>* new_Or(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<EQ>* new_Eq(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<NEQ>* new_Neq(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<GT>* new_Gt(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<GTE>* new_Gte(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<LT>* new_Lt(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<LTE>* new_Lte(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<ADD>* new_Add(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<SUB>* new_Sub(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<MUL>* new_Mul(std::string p, size_t l, Expression* lhs, Expression* rhs);- Binary_Expression<DIV>* new_Div(std::string p, size_t l, Expression* lhs, Expression* rhs);- Negation* new_Negation(std::string p, size_t l, Expression* o);- Function_Call* new_Function_Call(std::string p, size_t l, String* n, Arguments* args);- Variable* new_Variable(std::string p, size_t l, std::string n);- Textual<NUMBER>* new_Textual_Number(std::string p, size_t l, std::string val);- Textual<PERCENTAGE>* new_Textual_Percentage(std::string p, size_t l, std::string val);- Textual<DIMENSION>* new_Textual_Dimension(std::string p, size_t l, std::string val);- Textual<HEX>* new_Textual_Hex(std::string p, size_t l, std::string val);- Number* new_Number(std::string p, size_t l, double val);- Percentage* new_Percentage(std::string p, size_t l, double val);- Dimension* new_Dimension(std::string p, size_t l, double val, std::string unit);- Color* new_Color(std::string p, size_t l, double r, double g, double b, double a = 1, std::string disp = "");- Boolean* new_Boolean(std::string p, size_t l, bool val);- String_Schema* new_String_Schema(std::string p, size_t l, size_t size = 0);- String_Constant* new_String_Constant(std::string p, size_t l, std::string val);- String_Constant* new_String_Constant(std::string p, size_t l, const char* beg);- String_Constant* new_String_Constant(std::string p, size_t l, const char* beg, const char* end);- Supports_Condition* new_Supports_Condition(std::string p, size_t l, String* f, Expression* v);- Media_Expression* new_Media_Expression(std::string p, size_t l, String* f, Expression* v);- Parent_Selector* new_Parent_Selector(std::string p, size_t l, Selector* s);- // parameters and arguments- Parameter* new_Parameter(std::string p, size_t l, std::string n, Expression* def = 0, bool rest = false);- Parameters* new_Parameters(std::string p, size_t l);- Argument* new_Argument(std::string p, size_t l, Expression* val, std::string n = "", bool rest = false);- Arguments* new_Arguments(std::string p, size_t l);- // selectors- Selector_Schema* new_Selector_Schema(std::string p, size_t l, String* c);- Attribute_Selector* new_Attribute_Selector(std::string p, size_t l, std::string n, std::string m, String* v);- Simple_Selector* new_Simple_Selector(std::string p, size_t l, std::string c);- Reference_Selector* new_Reference_Selector(std::string p, size_t l);- Placeholder_Selector* new_Placeholder_Selector(std::string p, size_t l, std::string n);- Pseudo_Selector* new_Pseudo_Selector(std::string p, size_t l, std::string n, Expression* expr = 0);- Wrapped_Selector* new_Wrapped_Selector(std::string p, size_t l, std::string n, Simple_Base* sel);- Compound_Selector* new_Compound_Selector(std::string p, size_t l, size_t s = 0);- Complex_Selector* new_Complex_Selector(std::string p, size_t l, Complex_Selector::Combinator c, Complex_Selector* ctx, Compound_Selector* sel);- Selector_List* new_Selector_List(std::string p, size_t l, size_t s = 0);- };-}--#endif
libsass/src/ast_fwd_decl.hpp view
@@ -1,90 +1,389 @@ #ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H +#include <vector>+#include <iostream>+#include <algorithm>+#include <unordered_map>+#include <unordered_set>+#include "memory/SharedPtr.hpp"+ ///////////////////////////////////////////// // Forward declarations for the AST visitors. ///////////////////////////////////////////// namespace Sass { class AST_Node;- // statements- class Statement;+ typedef AST_Node* AST_Node_Ptr;+ typedef AST_Node const* AST_Node_Ptr_Const;++ class Has_Block;+ typedef Has_Block* Has_Block_Ptr;+ typedef Has_Block const* Has_Block_Ptr_Const;++ class Simple_Selector;+ typedef Simple_Selector* Simple_Selector_Ptr;+ typedef Simple_Selector const* Simple_Selector_Ptr_Const;++ class PreValue;+ typedef PreValue* PreValue_Ptr;+ typedef PreValue const* PreValue_Ptr_Const;+ class Thunk;+ typedef Thunk* Thunk_Ptr;+ typedef Thunk const* Thunk_Ptr_Const; class Block;+ typedef Block* Block_Ptr;+ typedef Block const* Block_Ptr_Const;+ class Expression;+ typedef Expression* Expression_Ptr;+ typedef Expression const* Expression_Ptr_Const;+ class Statement;+ typedef Statement* Statement_Ptr;+ typedef Statement const* Statement_Ptr_Const;+ class Value;+ typedef Value* Value_Ptr;+ typedef Value const* Value_Ptr_Const;+ class Declaration;+ typedef Declaration* Declaration_Ptr;+ typedef Declaration const* Declaration_Ptr_Const; class Ruleset;- class Propset;+ typedef Ruleset* Ruleset_Ptr;+ typedef Ruleset const* Ruleset_Ptr_Const; class Bubble;+ typedef Bubble* Bubble_Ptr;+ typedef Bubble const* Bubble_Ptr_Const;+ class Trace;+ typedef Trace* Trace_Ptr;+ typedef Trace const* Trace_Ptr_Const;+ class Media_Block;+ typedef Media_Block* Media_Block_Ptr;+ typedef Media_Block const* Media_Block_Ptr_Const; class Supports_Block;- class At_Rule;+ typedef Supports_Block* Supports_Block_Ptr;+ typedef Supports_Block const* Supports_Block_Ptr_Const;+ class Directive;+ typedef Directive* Directive_Ptr;+ typedef Directive const* Directive_Ptr_Const;++ class Keyframe_Rule;+ typedef Keyframe_Rule* Keyframe_Rule_Ptr;+ typedef Keyframe_Rule const* Keyframe_Rule_Ptr_Const; class At_Root_Block;- class Declaration;+ typedef At_Root_Block* At_Root_Block_Ptr;+ typedef At_Root_Block const* At_Root_Block_Ptr_Const; class Assignment;+ typedef Assignment* Assignment_Ptr;+ typedef Assignment const* Assignment_Ptr_Const;+ class Import;+ typedef Import* Import_Ptr;+ typedef Import const* Import_Ptr_Const; class Import_Stub;+ typedef Import_Stub* Import_Stub_Ptr;+ typedef Import_Stub const* Import_Stub_Ptr_Const; class Warning;+ typedef Warning* Warning_Ptr;+ typedef Warning const* Warning_Ptr_Const;+ class Error;+ typedef Error* Error_Ptr;+ typedef Error const* Error_Ptr_Const; class Debug;+ typedef Debug* Debug_Ptr;+ typedef Debug const* Debug_Ptr_Const; class Comment;+ typedef Comment* Comment_Ptr;+ typedef Comment const* Comment_Ptr_Const;+ class If;+ typedef If* If_Ptr;+ typedef If const* If_Ptr_Const; class For;+ typedef For* For_Ptr;+ typedef For const* For_Ptr_Const; class Each;+ typedef Each* Each_Ptr;+ typedef Each const* Each_Ptr_Const; class While;+ typedef While* While_Ptr;+ typedef While const* While_Ptr_Const; class Return;+ typedef Return* Return_Ptr;+ typedef Return const* Return_Ptr_Const; class Content;+ typedef Content* Content_Ptr;+ typedef Content const* Content_Ptr_Const; class Extension;+ typedef Extension* Extension_Ptr;+ typedef Extension const* Extension_Ptr_Const; class Definition;- class Mixin_Call;- // expressions- class Value;- class Expression;+ typedef Definition* Definition_Ptr;+ typedef Definition const* Definition_Ptr_Const;+ class List;+ typedef List* List_Ptr;+ typedef List const* List_Ptr_Const; class Map;+ typedef Map* Map_Ptr;+ typedef Map const* Map_Ptr_Const;++ class Mixin_Call;+ typedef Mixin_Call* Mixin_Call_Ptr;+ typedef Mixin_Call const* Mixin_Call_Ptr_Const; class Binary_Expression;+ typedef Binary_Expression* Binary_Expression_Ptr;+ typedef Binary_Expression const* Binary_Expression_Ptr_Const; class Unary_Expression;+ typedef Unary_Expression* Unary_Expression_Ptr;+ typedef Unary_Expression const* Unary_Expression_Ptr_Const; class Function_Call;+ typedef Function_Call* Function_Call_Ptr;+ typedef Function_Call const* Function_Call_Ptr_Const; class Function_Call_Schema;+ typedef Function_Call_Schema* Function_Call_Schema_Ptr;+ typedef Function_Call_Schema const* Function_Call_Schema_Ptr_Const; class Custom_Warning;+ typedef Custom_Warning* Custom_Warning_Ptr;+ typedef Custom_Warning const* Custom_Warning_Ptr_Const; class Custom_Error;+ typedef Custom_Error* Custom_Error_Ptr;+ typedef Custom_Error const* Custom_Error_Ptr_Const;+ class Variable;+ typedef Variable* Variable_Ptr;+ typedef Variable const* Variable_Ptr_Const; class Textual;+ typedef Textual* Textual_Ptr;+ typedef Textual const* Textual_Ptr_Const; class Number;+ typedef Number* Number_Ptr;+ typedef Number const* Number_Ptr_Const; class Color;+ typedef Color* Color_Ptr;+ typedef Color const* Color_Ptr_Const; class Boolean;- class String_Schema;+ typedef Boolean* Boolean_Ptr;+ typedef Boolean const* Boolean_Ptr_Const; class String;+ typedef String* String_Ptr;+ typedef String const* String_Ptr_Const;++ class String_Schema;+ typedef String_Schema* String_Schema_Ptr;+ typedef String_Schema const* String_Schema_Ptr_Const; class String_Constant;+ typedef String_Constant* String_Constant_Ptr;+ typedef String_Constant const* String_Constant_Ptr_Const; class String_Quoted;+ typedef String_Quoted* String_Quoted_Ptr;+ typedef String_Quoted const* String_Quoted_Ptr_Const;+ class Media_Query;+ typedef Media_Query* Media_Query_Ptr;+ typedef Media_Query const* Media_Query_Ptr_Const; class Media_Query_Expression;+ typedef Media_Query_Expression* Media_Query_Expression_Ptr;+ typedef Media_Query_Expression const* Media_Query_Expression_Ptr_Const; class Supports_Condition;+ typedef Supports_Condition* Supports_Condition_Ptr;+ typedef Supports_Condition const* Supports_Condition_Ptr_Const; class Supports_Operator;+ typedef Supports_Operator* Supports_Operator_Ptr;+ typedef Supports_Operator const* Supports_Operator_Ptr_Const; class Supports_Negation;+ typedef Supports_Negation* Supports_Negation_Ptr;+ typedef Supports_Negation const* Supports_Negation_Ptr_Const; class Supports_Declaration;+ typedef Supports_Declaration* Supports_Declaration_Ptr;+ typedef Supports_Declaration const* Supports_Declaration_Ptr_Const; class Supports_Interpolation;- class At_Root_Expression;+ typedef Supports_Interpolation* Supports_Interpolation_Ptr;+ typedef Supports_Interpolation const* Supports_Interpolation_Ptr_Const;++ class Null;+ typedef Null* Null_Ptr;+ typedef Null const* Null_Ptr_Const;++ class At_Root_Query;+ typedef At_Root_Query* At_Root_Query_Ptr;+ typedef At_Root_Query const* At_Root_Query_Ptr_Const; class Parent_Selector;- // parameters and arguments+ typedef Parent_Selector* Parent_Selector_Ptr;+ typedef Parent_Selector const* Parent_Selector_Ptr_Const; class Parameter;+ typedef Parameter* Parameter_Ptr;+ typedef Parameter const* Parameter_Ptr_Const; class Parameters;+ typedef Parameters* Parameters_Ptr;+ typedef Parameters const* Parameters_Ptr_Const; class Argument;+ typedef Argument* Argument_Ptr;+ typedef Argument const* Argument_Ptr_Const; class Arguments;- // selectors+ typedef Arguments* Arguments_Ptr;+ typedef Arguments const* Arguments_Ptr_Const; class Selector;+ typedef Selector* Selector_Ptr;+ typedef Selector const* Selector_Ptr_Const;++ class Selector_Schema;- class Selector_Placeholder;- class Type_Selector;- class Selector_Qualifier;+ typedef Selector_Schema* Selector_Schema_Ptr;+ typedef Selector_Schema const* Selector_Schema_Ptr_Const;+ class Placeholder_Selector;+ typedef Placeholder_Selector* Placeholder_Selector_Ptr;+ typedef Placeholder_Selector const* Placeholder_Selector_Ptr_Const;+ class Element_Selector;+ typedef Element_Selector* Element_Selector_Ptr;+ typedef Element_Selector const* Element_Selector_Ptr_Const;+ class Class_Selector;+ typedef Class_Selector* Class_Selector_Ptr;+ typedef Class_Selector const* Class_Selector_Ptr_Const;+ class Id_Selector;+ typedef Id_Selector* Id_Selector_Ptr;+ typedef Id_Selector const* Id_Selector_Ptr_Const; class Attribute_Selector;+ typedef Attribute_Selector* Attribute_Selector_Ptr;+ typedef Attribute_Selector const* Attribute_Selector_Ptr_Const;+ class Pseudo_Selector;+ typedef Pseudo_Selector* Pseudo_Selector_Ptr;+ typedef Pseudo_Selector const * Pseudo_Selector_Ptr_Const; class Wrapped_Selector;+ typedef Wrapped_Selector* Wrapped_Selector_Ptr;+ typedef Wrapped_Selector const * Wrapped_Selector_Ptr_Const; class Compound_Selector;+ typedef Compound_Selector* Compound_Selector_Ptr;+ typedef Compound_Selector const * Compound_Selector_Ptr_Const; class Complex_Selector;+ typedef Complex_Selector* Complex_Selector_Ptr;+ typedef Complex_Selector const * Complex_Selector_Ptr_Const; class Selector_List;+ typedef Selector_List* Selector_List_Ptr;+ typedef Selector_List const * Selector_List_Ptr_Const; + // common classes class Context;+ class Expand;+ class Eval;++ // declare classes that are instances of memory nodes+ // #define IMPL_MEM_OBJ(type) using type##_Obj = SharedImpl<type>+ #define IMPL_MEM_OBJ(type) typedef SharedImpl<type> type##_Obj++ IMPL_MEM_OBJ(AST_Node);+ IMPL_MEM_OBJ(Statement);+ IMPL_MEM_OBJ(Block);+ IMPL_MEM_OBJ(Ruleset);+ IMPL_MEM_OBJ(Bubble);+ IMPL_MEM_OBJ(Trace);+ IMPL_MEM_OBJ(Media_Block);+ IMPL_MEM_OBJ(Supports_Block);+ IMPL_MEM_OBJ(Directive);+ IMPL_MEM_OBJ(Keyframe_Rule);+ IMPL_MEM_OBJ(At_Root_Block);+ IMPL_MEM_OBJ(Declaration);+ IMPL_MEM_OBJ(Assignment);+ IMPL_MEM_OBJ(Import);+ IMPL_MEM_OBJ(Import_Stub);+ IMPL_MEM_OBJ(Warning);+ IMPL_MEM_OBJ(Error);+ IMPL_MEM_OBJ(Debug);+ IMPL_MEM_OBJ(Comment);+ IMPL_MEM_OBJ(PreValue);+ IMPL_MEM_OBJ(Has_Block);+ IMPL_MEM_OBJ(Thunk);+ IMPL_MEM_OBJ(If);+ IMPL_MEM_OBJ(For);+ IMPL_MEM_OBJ(Each);+ IMPL_MEM_OBJ(While);+ IMPL_MEM_OBJ(Return);+ IMPL_MEM_OBJ(Content);+ IMPL_MEM_OBJ(Extension);+ IMPL_MEM_OBJ(Definition);+ IMPL_MEM_OBJ(Mixin_Call);+ IMPL_MEM_OBJ(Value);+ IMPL_MEM_OBJ(Expression);+ IMPL_MEM_OBJ(List);+ IMPL_MEM_OBJ(Map);+ IMPL_MEM_OBJ(Binary_Expression);+ IMPL_MEM_OBJ(Unary_Expression);+ IMPL_MEM_OBJ(Function_Call);+ IMPL_MEM_OBJ(Function_Call_Schema);+ IMPL_MEM_OBJ(Custom_Warning);+ IMPL_MEM_OBJ(Custom_Error);+ IMPL_MEM_OBJ(Variable);+ IMPL_MEM_OBJ(Textual);+ IMPL_MEM_OBJ(Number);+ IMPL_MEM_OBJ(Color);+ IMPL_MEM_OBJ(Boolean);+ IMPL_MEM_OBJ(String_Schema);+ IMPL_MEM_OBJ(String);+ IMPL_MEM_OBJ(String_Constant);+ IMPL_MEM_OBJ(String_Quoted);+ IMPL_MEM_OBJ(Media_Query);+ IMPL_MEM_OBJ(Media_Query_Expression);+ IMPL_MEM_OBJ(Supports_Condition);+ IMPL_MEM_OBJ(Supports_Operator);+ IMPL_MEM_OBJ(Supports_Negation);+ IMPL_MEM_OBJ(Supports_Declaration);+ IMPL_MEM_OBJ(Supports_Interpolation);+ IMPL_MEM_OBJ(At_Root_Query);+ IMPL_MEM_OBJ(Null);+ IMPL_MEM_OBJ(Parent_Selector);+ IMPL_MEM_OBJ(Parameter);+ IMPL_MEM_OBJ(Parameters);+ IMPL_MEM_OBJ(Argument);+ IMPL_MEM_OBJ(Arguments);+ IMPL_MEM_OBJ(Selector);+ IMPL_MEM_OBJ(Selector_Schema);+ IMPL_MEM_OBJ(Simple_Selector);+ IMPL_MEM_OBJ(Placeholder_Selector);+ IMPL_MEM_OBJ(Element_Selector);+ IMPL_MEM_OBJ(Class_Selector);+ IMPL_MEM_OBJ(Id_Selector);+ IMPL_MEM_OBJ(Attribute_Selector);+ IMPL_MEM_OBJ(Pseudo_Selector);+ IMPL_MEM_OBJ(Wrapped_Selector);+ IMPL_MEM_OBJ(Compound_Selector);+ IMPL_MEM_OBJ(Complex_Selector);+ IMPL_MEM_OBJ(Selector_List);+++ struct HashExpression {+ size_t operator() (Expression_Obj ex) const;+ };+ struct CompareExpression {+ bool operator()(const Expression_Obj& lhs, const Expression_Obj& rhs) const;+ };++ struct HashSimpleSelector {+ size_t operator() (Simple_Selector_Obj ex) const;+ };++ struct CompareSimpleSelector {+ bool operator()(Simple_Selector_Obj lhs, Simple_Selector_Obj rhs) const;+ };++ typedef std::unordered_map<+ Expression_Obj, // key+ Expression_Obj, // value+ HashExpression, // hasher+ CompareExpression // compare+ > ExpressionMap;+ typedef std::unordered_set<+ Expression_Obj, // value+ HashExpression, // hasher+ CompareExpression // compare+ > ExpressionSet;++ typedef std::string Subset_Map_Key;+ typedef std::vector<size_t> Subset_Map_Arr;+ typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> Subset_Map_Val; }
libsass/src/base64vlq.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "base64vlq.hpp" namespace Sass {
libsass/src/bind.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "bind.hpp" #include "ast.hpp" #include "context.hpp"@@ -5,22 +6,20 @@ #include <map> #include <iostream> #include <sstream>-#include "to_string.hpp" namespace Sass { - void bind(std::string type, std::string name, Parameters* ps, Arguments* as, Context* ctx, Env* env, Eval* eval)+ void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval) { std::string callee(type + " " + name);- Listize listize(*ctx);- std::map<std::string, Parameter*> param_map; + std::map<std::string, Parameter_Obj> param_map;+ for (size_t i = 0, L = as->length(); i < L; ++i) {- if (auto str = dynamic_cast<String_Quoted*>((*as)[i]->value())) {+ if (auto str = SASS_MEMORY_CAST(String_Quoted, (*as)[i]->value())) { // force optional quotes (only if needed) if (str->quote_mark()) { str->quote_mark('*');- str->is_delayed(true); } } }@@ -28,7 +27,7 @@ // Set up a map to ensure named arguments refer to actual parameters. Also // eval each default value left-to-right, wrt env, populating env as we go. for (size_t i = 0, L = ps->length(); i < L; ++i) {- Parameter* p = (*ps)[i];+ Parameter_Obj p = ps->at(i); param_map[p->name()] = p; // if (p->default_value()) { // env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env));@@ -39,11 +38,11 @@ size_t ip = 0, LP = ps->length(); size_t ia = 0, LA = as->length(); while (ia < LA) {- Argument* a = (*as)[ia];+ Argument_Obj a = as->at(ia); if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) {- if (List* l = dynamic_cast<List*>(a->value())) {+ if (List_Obj l = SASS_MEMORY_CAST(List, a->value())) { if (l->length() == 0) { ++ ia; continue; }@@ -54,43 +53,37 @@ msg << " for `" << name << "'"; return error(msg.str(), as->pstate()); }- Parameter* p = (*ps)[ip];+ Parameter_Obj p = ps->at(ip); // If the current parameter is the rest parameter, process and break the loop if (p->is_rest_parameter()) { // The next argument by coincidence provides a rest argument if (a->is_rest_argument()) {+ // We should always get a list for rest arguments- if (List* rest = dynamic_cast<List*>(a->value())) {- // arg contains a list- List* args = rest;- // make sure it's an arglist- if (rest->is_arglist()) {- // can pass it through as it was- env->local_frame()[p->name()] = args;- }- // create a new list and wrap each item as an argument- // otherwise we will not be able to fetch it again- else {+ if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) { // create a new list object for wrapped items- List* arglist = SASS_MEMORY_NEW(ctx->mem, List,+ List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, rest->separator(), true); // wrap each item from list as an argument- for (Expression* item : rest->elements()) {- (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,- item->pstate(),- item,- "",- false,- false);+ for (Expression_Obj item : rest->elements()) {+ if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, item)) {+ arglist->append(SASS_MEMORY_COPY(arg)); // copy+ } else {+ arglist->append(SASS_MEMORY_NEW(Argument,+ item->pstate(),+ &item,+ "",+ false,+ false));+ } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; }- } // invalid state else { throw std::runtime_error("invalid state");@@ -98,23 +91,23 @@ } else if (a->is_keyword_argument()) { // expand keyword arguments into their parameters- List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), 0, SASS_COMMA, true);+ List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); env->local_frame()[p->name()] = arglist;- Map* argmap = static_cast<Map*>(a->value());+ Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value()); for (auto key : argmap->keys()) {- std::string name = unquote(static_cast<String_Constant*>(key)->value());- (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,- key->pstate(),- argmap->at(key),- "$" + name,- false,- false);+ std::string name = unquote(SASS_MEMORY_CAST(String_Constant, key)->value());+ arglist->append(SASS_MEMORY_NEW(Argument,+ key->pstate(),+ argmap->at(key),+ "$" + name,+ false,+ false)); } } else { // create a new list object for wrapped items- List* arglist = SASS_MEMORY_NEW(ctx->mem, List,+ List_Obj arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA,@@ -124,52 +117,45 @@ // get and post inc a = (*as)[ia++]; // maybe we have another list as argument- List* ls = dynamic_cast<List*>(a->value());+ List_Obj ls = SASS_MEMORY_CAST(List, a->value()); // skip any list completely if empty if (ls && ls->empty() && a->is_rest_argument()) continue;- // flatten all nested arglists- if (ls && ls->is_arglist()) {- for (size_t i = 0, L = ls->size(); i < L; ++i) {- // already have a wrapped argument- if (Argument* arg = dynamic_cast<Argument*>((*ls)[i])) {- (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);- }- // wrap all other value types into Argument- else {- (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,- (*ls)[i]->pstate(),- (*ls)[i],++ Expression_Obj value = a->value();+ if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, value)) {+ arglist->append(&arg);+ }+ // check if we have rest argument+ else if (a->is_rest_argument()) {+ // preserve the list separator from rest args+ if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) {+ arglist->separator(rest->separator());++ for (size_t i = 0, L = rest->size(); i < L; ++i) {+ Expression_Obj obj = rest->at(i);+ arglist->append(SASS_MEMORY_NEW(Argument,+ obj->pstate(),+ &obj, "", false,- false);+ false)); } }- }- // already have a wrapped argument- else if (Argument* arg = dynamic_cast<Argument*>(a->value())) {- (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);+ // no more arguments+ break; } // wrap all other value types into Argument else {- (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,+ arglist->append(SASS_MEMORY_NEW(Argument, a->pstate(), a->value(), a->name(), false,- false);- }- // check if we have rest argument- if (a->is_rest_argument()) {- // preserve the list separator from rest args- if (List* rest = dynamic_cast<List*>(a->value())) {- arglist->separator(rest->separator());- }- // no more arguments- break;+ false)); } } // assign new arglist to environment- env->local_frame()[p->name()] = arglist;+ env->local_frame()[p->name()] = &arglist; } // consumed parameter ++ip;@@ -180,25 +166,30 @@ // If the current argument is the rest argument, extract a value for processing else if (a->is_rest_argument()) { // normal param and rest arg- List* arglist = static_cast<List*>(a->value());+ List_Obj arglist = SASS_MEMORY_CAST(List, a->value()); // empty rest arg - treat all args as default values if (!arglist->length()) { break; } else {- if (arglist->length() + ia > LP && !ps->has_rest_parameter()) {- int arg_count = (arglist->length() + LA - 1);+ if (arglist->length() > LP - ip && !ps->has_rest_parameter()) {+ size_t arg_count = (arglist->length() + LA - 1); std::stringstream msg; msg << callee << " takes " << LP; msg << (LP == 1 ? " argument" : " arguments"); msg << " but " << arg_count; msg << (arg_count == 1 ? " was passed" : " were passed."); deprecated_bind(msg.str(), as->pstate());++ while (arglist->length() > LP - ip) {+ arglist->elements().erase(arglist->elements().end() - 1);+ } } } // otherwise move one of the rest args into the param, converting to argument if necessary- if (!(a = dynamic_cast<Argument*>((*arglist)[0]))) {- Expression* a_to_convert = (*arglist)[0];- a = SASS_MEMORY_NEW(ctx->mem, Argument,+ Expression_Obj obj = arglist->at(0);+ if (!(a = SASS_MEMORY_CAST(Argument, obj))) {+ Expression_Ptr a_to_convert = &obj;+ a = SASS_MEMORY_NEW(Argument, a_to_convert->pstate(), a_to_convert, "",@@ -209,18 +200,19 @@ if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { ++ia; }+ } else if (a->is_keyword_argument()) {- Map* argmap = static_cast<Map*>(a->value());+ Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value()); for (auto key : argmap->keys()) {- std::string name = "$" + unquote(static_cast<String_Constant*>(key)->value());+ std::string name = "$" + unquote(SASS_MEMORY_CAST(String_Constant, key)->value()); if (!param_map.count(name)) { std::stringstream msg; msg << callee << " has no parameter named " << name; error(msg.str(), a->pstate()); }- env->local_frame()[name] = argmap->at(key);+ env->local_frame()[name] = &argmap->at(&key); } ++ia; continue;@@ -236,7 +228,7 @@ error(msg.str(), a->pstate()); } // ordinal arg -- bind it to the next param- env->local_frame()[p->name()] = a->value();+ env->local_frame()[p->name()] = &a->value(); ++ip; } else {@@ -258,7 +250,7 @@ << "provided more than once in call to " << callee; error(msg.str(), a->pstate()); }- env->local_frame()[a->name()] = a->value();+ env->local_frame()[a->name()] = &a->value(); } } // EO while ia@@ -267,29 +259,25 @@ // That's only okay if they have default values, or were already bound by // named arguments, or if it's a single rest-param. for (size_t i = ip; i < LP; ++i) {- To_String to_string(ctx);- Parameter* leftover = (*ps)[i];+ Parameter_Obj leftover = ps->at(i); // cerr << "env for default params:" << endl; // env->print(); // cerr << "********" << endl; if (!env->has_local(leftover->name())) { if (leftover->is_rest_parameter()) {- env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(ctx->mem, List,+ env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(List, leftover->pstate(), 0, SASS_COMMA, true); } else if (leftover->default_value()) {- Expression* dv = leftover->default_value()->perform(eval);+ Expression_Ptr dv = leftover->default_value()->perform(eval); env->local_frame()[leftover->name()] = dv; } else { // param is unbound and has no default value -- error- std::stringstream msg;- msg << "required parameter " << leftover->name()- << " is missing in call to " << callee;- error(msg.str(), as->pstate());+ throw Exception::MissingArgument(as->pstate(), name, leftover->name(), type); } } }
libsass/src/bind.hpp view
@@ -3,16 +3,12 @@ #include <string> #include "environment.hpp"+#include "ast_fwd_decl.hpp" namespace Sass {- class AST_Node;- class Parameters;- class Arguments;- class Context;- class Eval;- typedef Environment<AST_Node*> Env;+ typedef Environment<AST_Node_Obj> Env; - void bind(std::string type, std::string name, Parameters*, Arguments*, Context*, Env*, Eval*);+ void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Context*, Env*, Eval*); } #endif
+ libsass/src/check_nesting.cpp view
@@ -0,0 +1,376 @@+#include "sass.hpp"+#include <vector>++#include "check_nesting.hpp"++namespace Sass {++ CheckNesting::CheckNesting()+ : parents(std::vector<Statement_Ptr>()),+ parent(0),+ current_mixin_definition(0)+ { }++ Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)+ {+ Statement_Ptr old_parent = this->parent;++ if (At_Root_Block_Ptr root = SASS_MEMORY_CAST_PTR(At_Root_Block, parent)) {+ std::vector<Statement_Ptr> old_parents = this->parents;+ std::vector<Statement_Ptr> new_parents;++ for (size_t i = 0, L = this->parents.size(); i < L; i++) {+ Statement_Ptr p = this->parents.at(i);+ if (!root->exclude_node(p)) {+ new_parents.push_back(p);+ }+ }+ this->parents = new_parents;++ for (size_t i = this->parents.size(); i > 0; i--) {+ Statement_Ptr p = 0;+ Statement_Ptr gp = 0;+ if (i > 0) p = this->parents.at(i - 1);+ if (i > 1) gp = this->parents.at(i - 2);++ if (!this->is_transparent_parent(p, gp)) {+ this->parent = p;+ break;+ }+ }++ At_Root_Block_Ptr ar = SASS_MEMORY_CAST_PTR(At_Root_Block, parent);+ Statement_Ptr ret = this->visit_children(&ar->block());++ this->parent = old_parent;+ this->parents = old_parents;++ return ret;+ }++ if (!this->is_transparent_parent(parent, old_parent)) {+ this->parent = parent;+ }++ this->parents.push_back(parent);++ Block_Ptr b = SASS_MEMORY_CAST_PTR(Block, parent);++ if (!b) {+ if (Has_Block_Ptr bb = SASS_MEMORY_CAST(Has_Block, *parent)) {+ b = &bb->block();+ }+ }++ if (b) {+ for (auto n : b->elements()) {+ n->perform(this);+ }+ }+ this->parent = old_parent;+ this->parents.pop_back();++ return b;+ }+++ Statement_Ptr CheckNesting::operator()(Block_Ptr b)+ {+ return this->visit_children(b);+ }++ Statement_Ptr CheckNesting::operator()(Definition_Ptr n)+ {+ if (!this->should_visit(n)) return NULL;+ if (!is_mixin(n)) {+ visit_children(n);+ return n;+ }++ Definition_Ptr old_mixin_definition = this->current_mixin_definition;+ this->current_mixin_definition = n;++ visit_children(n);++ this->current_mixin_definition = old_mixin_definition;++ return n;+ }++ Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)+ {+ Block_Ptr b1 = SASS_MEMORY_CAST_PTR(Block, s);+ Has_Block_Ptr b2 = SASS_MEMORY_CAST_PTR(Has_Block, s);+ return b1 || b2 ? visit_children(s) : s;+ }++ bool CheckNesting::should_visit(Statement_Ptr node)+ {+ if (!this->parent) return true;++ if (SASS_MEMORY_CAST_PTR(Content, node))+ { this->invalid_content_parent(this->parent); }++ if (is_charset(node))+ { this->invalid_charset_parent(this->parent); }++ if (SASS_MEMORY_CAST_PTR(Extension, node))+ { this->invalid_extend_parent(this->parent); }++ // if (SASS_MEMORY_CAST(Import, node))+ // { this->invalid_import_parent(this->parent); }++ if (this->is_mixin(node))+ { this->invalid_mixin_definition_parent(this->parent); }++ if (this->is_function(node))+ { this->invalid_function_parent(this->parent); }++ if (this->is_function(this->parent))+ { this->invalid_function_child(node); }++ if (SASS_MEMORY_CAST_PTR(Declaration, node))+ { this->invalid_prop_parent(this->parent); }++ if (SASS_MEMORY_CAST_PTR(Declaration, this->parent))+ { this->invalid_prop_child(node); }++ if (SASS_MEMORY_CAST_PTR(Return, node))+ { this->invalid_return_parent(this->parent); }++ return true;+ }++ void CheckNesting::invalid_content_parent(Statement_Ptr parent)+ {+ if (!this->current_mixin_definition) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "@content may only be used within a mixin."+ );+ }+ }++ void CheckNesting::invalid_charset_parent(Statement_Ptr parent)+ {+ if (!(+ is_root_node(parent)+ )) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "@charset may only be used at the root of a document."+ );+ }+ }++ void CheckNesting::invalid_extend_parent(Statement_Ptr parent)+ {+ if (!(+ SASS_MEMORY_CAST_PTR(Ruleset, parent) ||+ SASS_MEMORY_CAST_PTR(Mixin_Call, parent) ||+ is_mixin(parent)+ )) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "Extend directives may only be used within rules."+ );+ }+ }++ // void CheckNesting::invalid_import_parent(Statement_Ptr parent)+ // {+ // for (auto pp : this->parents) {+ // if (+ // SASS_MEMORY_CAST(Each, pp) ||+ // SASS_MEMORY_CAST(For, pp) ||+ // SASS_MEMORY_CAST(If, pp) ||+ // SASS_MEMORY_CAST(While, pp) ||+ // SASS_MEMORY_CAST(Trace, pp) ||+ // SASS_MEMORY_CAST(Mixin_Call, pp) ||+ // is_mixin(pp)+ // ) {+ // throw Exception::InvalidSass(+ // parent->pstate(),+ // "Import directives may not be defined within control directives or other mixins."+ // );+ // }+ // }++ // if (this->is_root_node(parent)) {+ // return;+ // }++ // if (false/*n.css_import?*/) {+ // throw Exception::InvalidSass(+ // parent->pstate(),+ // "CSS import directives may only be used at the root of a document."+ // );+ // }+ // }++ void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent)+ {+ for (Statement_Ptr pp : this->parents) {+ if (+ SASS_MEMORY_CAST_PTR(Each, pp) ||+ SASS_MEMORY_CAST_PTR(For, pp) ||+ SASS_MEMORY_CAST_PTR(If, pp) ||+ SASS_MEMORY_CAST_PTR(While, pp) ||+ SASS_MEMORY_CAST_PTR(Trace, pp) ||+ SASS_MEMORY_CAST_PTR(Mixin_Call, pp) ||+ is_mixin(pp)+ ) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "Mixins may not be defined within control directives or other mixins."+ );+ }+ }+ }++ void CheckNesting::invalid_function_parent(Statement_Ptr parent)+ {+ for (Statement_Ptr pp : this->parents) {+ if (+ SASS_MEMORY_CAST_PTR(Each, pp) ||+ SASS_MEMORY_CAST_PTR(For, pp) ||+ SASS_MEMORY_CAST_PTR(If, pp) ||+ SASS_MEMORY_CAST_PTR(While, pp) ||+ SASS_MEMORY_CAST_PTR(Trace, pp) ||+ SASS_MEMORY_CAST_PTR(Mixin_Call, pp) ||+ is_mixin(pp)+ ) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "Functions may not be defined within control directives or other mixins."+ );+ }+ }+ }++ void CheckNesting::invalid_function_child(Statement_Ptr child)+ {+ if (!(+ SASS_MEMORY_CAST_PTR(Each, child) ||+ SASS_MEMORY_CAST_PTR(For, child) ||+ SASS_MEMORY_CAST_PTR(If, child) ||+ SASS_MEMORY_CAST_PTR(While, child) ||+ SASS_MEMORY_CAST_PTR(Trace, child) ||+ SASS_MEMORY_CAST_PTR(Comment, child) ||+ SASS_MEMORY_CAST_PTR(Debug, child) ||+ SASS_MEMORY_CAST_PTR(Return, child) ||+ SASS_MEMORY_CAST_PTR(Variable, child) ||+ // Ruby Sass doesn't distinguish variables and assignments+ SASS_MEMORY_CAST_PTR(Assignment, child) ||+ SASS_MEMORY_CAST_PTR(Warning, child) ||+ SASS_MEMORY_CAST_PTR(Error, child)+ )) {+ throw Exception::InvalidSass(+ child->pstate(),+ "Functions can only contain variable declarations and control directives."+ );+ }+ }++ void CheckNesting::invalid_prop_child(Statement_Ptr child)+ {+ if (!(+ SASS_MEMORY_CAST_PTR(Each, child) ||+ SASS_MEMORY_CAST_PTR(For, child) ||+ SASS_MEMORY_CAST_PTR(If, child) ||+ SASS_MEMORY_CAST_PTR(While, child) ||+ SASS_MEMORY_CAST_PTR(Trace, child) ||+ SASS_MEMORY_CAST_PTR(Comment, child) ||+ SASS_MEMORY_CAST_PTR(Declaration, child) ||+ SASS_MEMORY_CAST_PTR(Mixin_Call, child)+ )) {+ throw Exception::InvalidSass(+ child->pstate(),+ "Illegal nesting: Only properties may be nested beneath properties."+ );+ }+ }++ void CheckNesting::invalid_prop_parent(Statement_Ptr parent)+ {+ if (!(+ is_mixin(parent) ||+ is_directive_node(parent) ||+ SASS_MEMORY_CAST_PTR(Ruleset, parent) ||+ SASS_MEMORY_CAST_PTR(Keyframe_Rule, parent) ||+ SASS_MEMORY_CAST_PTR(Declaration, parent) ||+ SASS_MEMORY_CAST_PTR(Mixin_Call, parent)+ )) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "Properties are only allowed within rules, directives, mixin includes, or other properties."+ );+ }+ }++ void CheckNesting::invalid_return_parent(Statement_Ptr parent)+ {+ if (!this->is_function(parent)) {+ throw Exception::InvalidSass(+ parent->pstate(),+ "@return may only be used within a function."+ );+ }+ }++ bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)+ {+ bool parent_bubbles = parent && parent->bubbles();++ bool valid_bubble_node = parent_bubbles &&+ !is_root_node(grandparent) &&+ !is_at_root_node(grandparent);++ return SASS_MEMORY_CAST_PTR(Import, parent) ||+ SASS_MEMORY_CAST_PTR(Each, parent) ||+ SASS_MEMORY_CAST_PTR(For, parent) ||+ SASS_MEMORY_CAST_PTR(If, parent) ||+ SASS_MEMORY_CAST_PTR(While, parent) ||+ SASS_MEMORY_CAST_PTR(Trace, parent) ||+ valid_bubble_node;+ }++ bool CheckNesting::is_charset(Statement_Ptr n)+ {+ Directive_Ptr d = SASS_MEMORY_CAST_PTR(Directive, n);+ return d && d->keyword() == "charset";+ }++ bool CheckNesting::is_mixin(Statement_Ptr n)+ {+ Definition_Ptr def = SASS_MEMORY_CAST_PTR(Definition, n);+ return def && def->type() == Definition::MIXIN;+ }++ bool CheckNesting::is_function(Statement_Ptr n)+ {+ Definition_Ptr def = SASS_MEMORY_CAST_PTR(Definition, n);+ return def && def->type() == Definition::FUNCTION;+ }++ bool CheckNesting::is_root_node(Statement_Ptr n)+ {+ if (SASS_MEMORY_CAST_PTR(Ruleset, n)) return false;++ Block_Ptr b = SASS_MEMORY_CAST_PTR(Block, n);+ return b && b->is_root();+ }++ bool CheckNesting::is_at_root_node(Statement_Ptr n)+ {+ return SASS_MEMORY_CAST_PTR(At_Root_Block, n) != NULL;+ }++ bool CheckNesting::is_directive_node(Statement_Ptr n)+ {+ return SASS_MEMORY_CAST_PTR(Directive, n) ||+ SASS_MEMORY_CAST_PTR(Import, n) ||+ SASS_MEMORY_CAST_PTR(Media_Block, n) ||+ SASS_MEMORY_CAST_PTR(Supports_Block, n);+ }+}
+ libsass/src/check_nesting.hpp view
@@ -0,0 +1,64 @@+#ifndef SASS_CHECK_NESTING_H+#define SASS_CHECK_NESTING_H++#include "ast.hpp"+#include "operation.hpp"++namespace Sass {++ typedef Environment<AST_Node_Obj> Env;++ class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> {++ std::vector<Statement_Ptr> parents;+ Statement_Ptr parent;+ Definition_Ptr current_mixin_definition;++ Statement_Ptr fallback_impl(Statement_Ptr);+ Statement_Ptr before(Statement_Ptr);+ Statement_Ptr visit_children(Statement_Ptr);++ public:+ CheckNesting();+ ~CheckNesting() { }++ Statement_Ptr operator()(Block_Ptr);+ Statement_Ptr operator()(Definition_Ptr);++ template <typename U>+ Statement_Ptr fallback(U x) {+ Statement_Ptr n = SASS_MEMORY_CAST_PTR(Statement, x);+ if (this->should_visit(n)) {+ return fallback_impl(n);+ }+ return NULL;+ }++ private:+ void invalid_content_parent(Statement_Ptr);+ void invalid_charset_parent(Statement_Ptr);+ void invalid_extend_parent(Statement_Ptr);+ // void invalid_import_parent(Statement_Ptr);+ void invalid_mixin_definition_parent(Statement_Ptr);+ void invalid_function_parent(Statement_Ptr);++ void invalid_function_child(Statement_Ptr);+ void invalid_prop_child(Statement_Ptr);+ void invalid_prop_parent(Statement_Ptr);+ void invalid_return_parent(Statement_Ptr);++ bool is_transparent_parent(Statement_Ptr, Statement_Ptr);++ bool should_visit(Statement_Ptr);++ bool is_charset(Statement_Ptr);+ bool is_mixin(Statement_Ptr);+ bool is_function(Statement_Ptr);+ bool is_root_node(Statement_Ptr);+ bool is_at_root_node(Statement_Ptr);+ bool is_directive_node(Statement_Ptr);+ };++}++#endif
libsass/src/color_maps.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "ast.hpp" #include "color_maps.hpp" @@ -451,7 +452,7 @@ { 102 * 0x10000 + 51 * 0x100 + 153, ColorNames::rebeccapurple } }; - const std::map<const char*, const Color*, map_cmp_str> names_to_colors+ const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors { { ColorNames::aliceblue, &Colors::aliceblue }, { ColorNames::antiquewhite, &Colors::antiquewhite },@@ -604,7 +605,7 @@ { ColorNames::transparent, &Colors::transparent } }; - const Color* name_to_color(const char* key)+ Color_Ptr_Const name_to_color(const char* key) { auto p = names_to_colors.find(key); if (p != names_to_colors.end()) {@@ -613,7 +614,7 @@ return 0; } - const Color* name_to_color(const std::string& key)+ Color_Ptr_Const name_to_color(const std::string& key) { return name_to_color(key.c_str()); }
libsass/src/color_maps.hpp view
@@ -320,10 +320,10 @@ } extern const std::map<const int, const char*> colors_to_names;- extern const std::map<const char*, const Color*, map_cmp_str> names_to_colors;+ extern const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors; - extern const Color* name_to_color(const char*);- extern const Color* name_to_color(const std::string&);+ extern Color_Ptr_Const name_to_color(const char*);+ extern Color_Ptr_Const name_to_color(const std::string&); extern const char* color_to_name(const int); extern const char* color_to_name(const Color&); extern const char* color_to_name(const double);
libsass/src/constants.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "constants.hpp" namespace Sass {@@ -9,12 +10,13 @@ // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity // https://github.com/sass/sass/issues/1495#issuecomment-61189114 extern const unsigned long Specificity_Star = 0;- extern const unsigned long Specificity_Universal = 1 << 0;- extern const unsigned long Specificity_Type = 1 << 8;- extern const unsigned long Specificity_Class = 1 << 16;- extern const unsigned long Specificity_Attr = 1 << 16;- extern const unsigned long Specificity_Pseudo = 1 << 16;- extern const unsigned long Specificity_ID = 1 << 24;+ extern const unsigned long Specificity_Universal = 0;+ extern const unsigned long Specificity_Element = 1;+ extern const unsigned long Specificity_Base = 1000;+ extern const unsigned long Specificity_Class = 1000;+ extern const unsigned long Specificity_Attr = 1000;+ extern const unsigned long Specificity_Pseudo = 1000;+ extern const unsigned long Specificity_ID = 1000000; // sass keywords extern const char at_root_kwd[] = "@at-root";@@ -78,7 +80,7 @@ extern const char keyframes_kwd[] = "keyframes"; extern const char only_kwd[] = "only"; extern const char rgb_kwd[] = "rgb(";- extern const char url_kwd[] = "url(";+ extern const char url_kwd[] = "url"; // extern const char url_prefix_kwd[] = "url-prefix("; extern const char important_kwd[] = "important"; extern const char pseudo_not_kwd[] = ":not(";@@ -86,11 +88,10 @@ extern const char odd_kwd[] = "odd"; extern const char progid_kwd[] = "progid"; extern const char expression_kwd[] = "expression";- extern const char calc_kwd[] = "calc(";- extern const char moz_calc_kwd[] = "-moz-calc(";- extern const char webkit_calc_kwd[] = "-webkit-calc(";- extern const char ms_calc_kwd[] = "-ms-calc(";+ extern const char calc_fn_kwd[] = "calc"; + extern const char almost_any_value_class[] = "\"'#!;{}";+ // css selector keywords extern const char sel_deep_kwd[] = "/deep/"; @@ -124,6 +125,7 @@ extern const char rbrace[] = "}"; extern const char rparen[] = ")"; extern const char sign_chars[] = "-+";+ extern const char op_chars[] = "-+"; extern const char hyphen[] = "-"; extern const char ellipsis[] = "..."; // extern const char url_space_chars[] = " \t\r\n\f";
libsass/src/constants.hpp view
@@ -11,7 +11,8 @@ // The following list of selectors is by increasing specificity: extern const unsigned long Specificity_Star; extern const unsigned long Specificity_Universal;- extern const unsigned long Specificity_Type;+ extern const unsigned long Specificity_Element;+ extern const unsigned long Specificity_Base; extern const unsigned long Specificity_Class; extern const unsigned long Specificity_Attr; extern const unsigned long Specificity_Pseudo;@@ -81,18 +82,17 @@ extern const char rgb_kwd[]; extern const char url_kwd[]; // extern const char url_prefix_kwd[];- extern const char image_url_kwd[]; extern const char important_kwd[]; extern const char pseudo_not_kwd[]; extern const char even_kwd[]; extern const char odd_kwd[]; extern const char progid_kwd[]; extern const char expression_kwd[];- extern const char calc_kwd[];- extern const char moz_calc_kwd[];- extern const char webkit_calc_kwd[];- extern const char ms_calc_kwd[];+ extern const char calc_fn_kwd[]; + // char classes for "regular expressions"+ extern const char almost_any_value_class[];+ // css selector keywords extern const char sel_deep_kwd[]; @@ -126,6 +126,7 @@ extern const char rbrace[]; extern const char rparen[]; extern const char sign_chars[];+ extern const char op_chars[]; extern const char hyphen[]; extern const char ellipsis[]; // extern const char url_space_chars[];
libsass/src/context.cpp view
@@ -1,9 +1,4 @@-#ifdef _WIN32-#define PATH_SEP ';'-#else-#define PATH_SEP ':'-#endif-+#include "sass.hpp" #include <string> #include <cstdlib> #include <cstring>@@ -23,11 +18,13 @@ #include "output.hpp" #include "expand.hpp" #include "eval.hpp"+#include "check_nesting.hpp" #include "cssize.hpp" #include "listize.hpp" #include "extend.hpp" #include "remove_placeholders.hpp" #include "functions.hpp"+#include "sass_functions.hpp" #include "backtrace.hpp" #include "sass2scss.h" #include "prelexer.hpp"@@ -62,13 +59,13 @@ return safe_path == "" ? "stdout" : safe_path; } - Context::Context(struct Sass_Context* c_ctx)+ Context::Context(struct Sass_Context& c_ctx) : CWD(File::get_cwd()),+ c_options(c_ctx), entry_path(""), head_imports(0),- mem(Memory_Manager()), plugins(),- emitter(this),+ emitter(c_options), strings(), resources(),@@ -76,19 +73,17 @@ subset_map(), import_stack(), - c_options (c_ctx),- c_headers (std::vector<Sass_Importer_Entry>()), c_importers (std::vector<Sass_Importer_Entry>()), c_functions (std::vector<Sass_Function_Entry>()), - indent (safe_str(c_options->indent, " ")),- linefeed (safe_str(c_options->linefeed, "\n")),+ indent (safe_str(c_options.indent, " ")),+ linefeed (safe_str(c_options.linefeed, "\n")), - input_path (make_canonical_path(safe_input(c_options->input_path))),- output_path (make_canonical_path(safe_output(c_options->output_path, input_path))),- source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))),- source_map_root (make_canonical_path(safe_str(c_options->source_map_root, "")))+ input_path (make_canonical_path(safe_input(c_options.input_path))),+ output_path (make_canonical_path(safe_output(c_options.output_path, input_path))),+ source_map_file (make_canonical_path(safe_str(c_options.source_map_file, ""))),+ source_map_root (make_canonical_path(safe_str(c_options.source_map_root, ""))) { @@ -96,10 +91,10 @@ include_paths.push_back(CWD); // collect more paths from different options- collect_include_paths(sass_option_get_include_path(c_options));- // collect_include_paths(initializers.include_paths_array());- collect_plugin_paths(sass_option_get_plugin_path(c_options));- // collect_plugin_paths(initializers.plugin_paths_array());+ collect_include_paths(c_options.include_path);+ collect_include_paths(c_options.include_paths);+ collect_plugin_paths(c_options.plugin_path);+ collect_plugin_paths(c_options.plugin_paths); // load plugins and register custom behaviors for(auto plug : plugin_paths) plugins.load_plugins(plug);@@ -150,6 +145,7 @@ } // clear inner structures (vectors) and input source resources.clear(); import_stack.clear();+ subset_map.clear(), sheets.clear(); } Data_Context::~Data_Context()@@ -167,7 +163,6 @@ void Context::collect_include_paths(const char* paths_str) {- if (paths_str) { const char* beg = paths_str; const char* end = Prelexer::find_first<PATH_SEP>(beg);@@ -190,17 +185,17 @@ } } - void Context::collect_include_paths(const char** paths_array)+ void Context::collect_include_paths(string_list* paths_array) {- if (!paths_array) return;- for (size_t i = 0; paths_array[i]; i++) {- collect_include_paths(paths_array[i]);+ while (paths_array)+ {+ collect_include_paths(paths_array->string);+ paths_array = paths_array->next; } } void Context::collect_plugin_paths(const char* paths_str) {- if (paths_str) { const char* beg = paths_str; const char* end = Prelexer::find_first<PATH_SEP>(beg);@@ -223,15 +218,15 @@ } } - void Context::collect_plugin_paths(const char** paths_array)+ void Context::collect_plugin_paths(string_list* paths_array) {- if (!paths_array) return;- for (size_t i = 0; paths_array[i]; i++) {- collect_plugin_paths(paths_array[i]);+ while (paths_array)+ {+ collect_plugin_paths(paths_array->string);+ paths_array = paths_array->next; } } - // resolve the imp_path in base_path or include_paths // looks for alternatives and returns a list from one directory std::vector<Include> Context::find_includes(const Importer& import)@@ -254,7 +249,7 @@ // register include with resolved path and its content // memory of the resources will be freed by us on exit- void Context::register_resource(const Include& inc, const Resource& res)+ void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate) { // do not parse same resource twice@@ -294,22 +289,40 @@ const char* contents = resources[idx].contents; // keep a copy of the path around (for parserstates) // ToDo: we clean it, but still not very elegant!?- strings.push_back(sass_strdup(inc.abs_path.c_str()));+ strings.push_back(sass_copy_c_string(inc.abs_path.c_str())); // create the initial parser state from resource ParserState pstate(strings.back(), contents, idx);++ // check existing import stack for possible recursion+ for (size_t i = 0; i < import_stack.size() - 2; ++i) {+ auto parent = import_stack[i];+ if (std::strcmp(parent->abs_path, import->abs_path) == 0) {+ std::string stack("An @import loop has been found:");+ for (size_t n = 1; n < i + 2; ++n) {+ stack += "\n " + std::string(import_stack[n]->imp_path) ++ " imports " + std::string(import_stack[n+1]->imp_path);+ }+ // implement error throw directly until we+ // decided how to handle full stack traces+ ParserState state = prstate ? *prstate : pstate;+ throw Exception::InvalidSyntax(state, stack, &import_stack);+ // error(stack, prstate ? *prstate : pstate, import_stack);+ }+ }+ // create a parser instance from the given c_str buffer Parser p(Parser::from_c_str(contents, *this, pstate)); // do not yet dispose these buffers sass_import_take_source(import); sass_import_take_srcmap(import); // then parse the root block- Block* root = p.parse();+ Block_Obj root = p.parse(); // delete memory of current stack frame sass_delete_import(import_stack.back()); // remove current stack frame import_stack.pop_back(); // create key/value pair for ast node- std::pair<const std::string, const StyleSheet>+ std::pair<const std::string, StyleSheet> ast_pair(inc.abs_path, { res, root }); // register resulting resource sheets.insert(ast_pair);@@ -338,13 +351,14 @@ // process the resolved entry else if (resolved.size() == 1) {+ bool use_cache = c_importers.size() == 0; // use cache for the resource loading- if (sheets.count(resolved[0].abs_path)) return resolved[0];+ if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0]; // try to read the content of the resolved file entry // the memory buffer returned must be freed by us! if (char* contents = read_file(resolved[0].abs_path)) { // register the newly resolved file resource- register_resource(resolved[0], { contents, 0 });+ register_resource(resolved[0], { contents, 0 }, &pstate); // return resolved entry return resolved[0]; }@@ -355,7 +369,7 @@ } - void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) {+ void Context::import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path) { ParserState pstate(imp->pstate()); std::string imp_path(unquote(load_path));@@ -365,30 +379,27 @@ if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { protocol = std::string(imp_path.c_str(), proto - 3);- // std::cerr << "==================== " << protocol << "\n";- if (protocol.compare("file") && true) {-- }+ // if (protocol.compare("file") && true) { } } // add urls (protocol other than file) and urls without procotol to `urls` member // ToDo: if ctx_path is already a file resource, we should not add it here?- if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {- imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path));+ if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {+ imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path)); } else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {- String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path));- Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc);- Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate);- (*loc_args) << loc_arg;- Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args);+ String_Constant_Ptr loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));+ Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);+ Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);+ loc_args->append(&loc_arg);+ Function_Call_Ptr new_url = SASS_MEMORY_NEW(Function_Call, pstate, "url", &loc_args); imp->urls().push_back(new_url); } else { const Importer importer(imp_path, ctx_path); Include include(load_import(importer, pstate)); if (include.abs_path.empty()) {- error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate);+ error("File to import not found or unreadable: " + imp_path + ".\nParent style sheet: " + ctx_path, pstate); } imp->incs().push_back(include); }@@ -397,7 +408,7 @@ // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet- bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one)+ bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one) { // unique counter size_t count = 0;@@ -426,14 +437,14 @@ // query data from the current include Sass_Import_Entry include = *it_includes; char* source = sass_import_take_source(include);- char* srcmap = sass_import_take_source(include);+ char* srcmap = sass_import_take_srcmap(include); size_t line = sass_import_get_error_line(include); size_t column = sass_import_get_error_column(include); const char *abs_path = sass_import_get_abs_path(include); // handle error message passed back from custom importer // it may (or may not) override the line and column info if (const char* err_message = sass_import_get_error_message(include)) {- if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });+ if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate); if (line == std::string::npos && column == std::string::npos) error(err_message, pstate); else error(err_message, ParserState(ctx_path, source, Position(line, column))); }@@ -447,7 +458,7 @@ // attach information to AST node imp->incs().push_back(include); // register the resource buffers- register_resource(include, { source, srcmap });+ register_resource(include, { source, srcmap }, &pstate); } // only a path was retuned // try to load it like normal@@ -483,7 +494,7 @@ void register_c_functions(Context&, Env* env, Sass_Function_List); void register_c_function(Context&, Env* env, Sass_Function_Entry); - char* Context::render(Block* root)+ char* Context::render(Block_Obj root) { // check for valid block if (!root) return 0;@@ -494,9 +505,9 @@ // get the resulting buffer from stream OutputBuffer emitted = emitter.get_buffer(); // should we append a source map url?- if (!c_options->omit_source_map_url) {+ if (!c_options.omit_source_map_url) { // generate an embeded source map- if (c_options->source_map_embed) {+ if (c_options.source_map_embed) { emitted.buffer += linefeed; emitted.buffer += format_embedded_source_map(); }@@ -508,27 +519,27 @@ } // create a copy of the resulting buffer string // this must be freed or taken over by implementor- return sass_strdup(emitted.buffer.c_str());+ return sass_copy_c_string(emitted.buffer.c_str()); } - void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate)+ void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, ParserState pstate) { // create a custom import to resolve headers- Import* imp = SASS_MEMORY_NEW(mem, Import, pstate);+ Import_Obj imp = SASS_MEMORY_NEW(Import, pstate); // dispatch headers which will add custom functions // custom headers are added to the import instance- call_headers(entry_path, ctx_path, pstate, imp);+ call_headers(entry_path, ctx_path, pstate, &imp); // increase head count to skip later head_imports += resources.size() - 1; // add the statement if we have urls- if (!imp->urls().empty()) (*root) << imp;+ if (!imp->urls().empty()) root->append(&imp); // process all other resources (add Import_Stub nodes) for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {- (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]);+ root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i])); } } - Block* File_Context::parse()+ Block_Obj File_Context::parse() { // check if entry file is given@@ -551,7 +562,7 @@ } // abort early if no content could be loaded (various reasons)- if (!contents) throw "File to read not found or unreadable: " + input_path;+ if (!contents) throw std::runtime_error("File to read not found or unreadable: " + input_path); // store entry path entry_path = abs_path;@@ -574,14 +585,14 @@ } - Block* Data_Context::parse()+ Block_Obj Data_Context::parse() { // check if source string is given if (!source_c_str) return 0; // convert indented sass syntax- if(c_options->is_indented_syntax_src) {+ if(c_options.is_indented_syntax_src) { // call sass2scss to convert the string char * converted = sass2scss(source_c_str, // preserve the structure as much as possible@@ -595,7 +606,7 @@ // ToDo: this may be resolved via custom importers std::string abs_path(rel2abs(entry_path));- char* abs_path_c_str = sass_strdup(abs_path.c_str());+ char* abs_path_c_str = sass_copy_c_string(abs_path.c_str()); strings.push_back(abs_path_c_str); // create entry only for the import stack@@ -618,15 +629,14 @@ // parse root block from includes- Block* Context::compile()+ Block_Obj Context::compile() { // abort if there is no data if (resources.size() == 0) return 0; // get root block from the first style sheet- Block* root = sheets.at(entry_path).root;+ Block_Obj root = sheets.at(entry_path).root; // abort on invalid root- if (root == 0) return 0;-+ if (root.isNull()) return 0; Env global; // create root environment // register built-in functions on env register_built_in_functions(*this, &global);@@ -638,16 +648,21 @@ // create crtp visitor objects Expand expand(*this, &global, &backtrace); Cssize cssize(*this, &backtrace);+ CheckNesting check_nesting;+ // check nesting+ check_nesting(&root); // expand and eval the tree- root = root->perform(&expand)->block();+ root = expand(&root);+ // check nesting+ check_nesting(&root); // merge and bubble certain rules- root = root->perform(&cssize)->block();+ root = cssize(&root); // should we extend something? if (!subset_map.empty()) { // create crtp visitor object Extend extend(*this, subset_map); // extend tree nodes- root->perform(&extend);+ extend(&root); } // clean up by removing empty placeholders@@ -682,7 +697,7 @@ if (source_map_file == "") return 0; char* result = 0; std::string map = emitter.render_srcmap(*this);- result = sass_strdup(map.c_str());+ result = sass_copy_c_string(map.c_str()); return result; } @@ -703,14 +718,14 @@ void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) {- Definition* def = make_native_function(sig, f, ctx);+ Definition_Ptr def = make_native_function(sig, f, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; } void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env) {- Definition* def = make_native_function(sig, f, ctx);+ Definition_Ptr def = make_native_function(sig, f, ctx); std::stringstream ss; ss << def->name() << "[f]" << arity; def->environment(env);@@ -719,7 +734,7 @@ void register_overload_stub(Context& ctx, std::string name, Env* env) {- Definition* stub = SASS_MEMORY_NEW(ctx.mem, Definition,+ Definition_Ptr stub = SASS_MEMORY_NEW(Definition, ParserState("[built-in function]"), 0, name,@@ -840,7 +855,7 @@ } void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr) {- Definition* def = make_c_function(descr, ctx);+ Definition_Ptr def = make_c_function(descr, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; }
libsass/src/context.hpp view
@@ -10,7 +10,6 @@ #include "ast_fwd_decl.hpp" #include "kwd_arg_macros.hpp"-#include "memory_manager.hpp" #include "ast_fwd_decl.hpp" #include "sass_context.hpp" #include "environment.hpp"@@ -27,20 +26,20 @@ class Context { public:- void import_url (Import* imp, std::string load_path, const std::string& ctx_path);- bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp)+ void import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path);+ bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp) { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); };- bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp)+ bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp) { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); }; private:- bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);+ bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true); public: const std::string CWD;+ struct Sass_Options& c_options; std::string entry_path; size_t head_imports;- Memory_Manager mem; Plugins plugins; Output emitter; @@ -48,12 +47,11 @@ // these are guaranteed to be freed std::vector<char*> strings; std::vector<Resource> resources;- std::map<const std::string, const StyleSheet> sheets;- Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > subset_map;+ std::map<const std::string, StyleSheet> sheets;+ Subset_Map subset_map; std::vector<Sass_Import_Entry> import_stack; struct Sass_Compiler* c_compiler;- struct Sass_Options* c_options; // absolute paths to includes std::vector<std::string> included_files;@@ -68,7 +66,7 @@ - void apply_custom_headers(Block* root, const char* path, ParserState pstate);+ void apply_custom_headers(Block_Obj root, const char* path, ParserState pstate); std::vector<Sass_Importer_Entry> c_headers; std::vector<Sass_Importer_Entry> c_importers;@@ -86,24 +84,24 @@ const std::string source_map_root; // path for sourceRoot property (pass-through) virtual ~Context();- Context(struct Sass_Context*);- virtual Block* parse() = 0;- virtual Block* compile();- virtual char* render(Block* root);+ Context(struct Sass_Context&);+ virtual Block_Obj parse() = 0;+ virtual Block_Obj compile();+ virtual char* render(Block_Obj root); virtual char* render_srcmap(); - void register_resource(const Include&, const Resource&);+ void register_resource(const Include&, const Resource&, ParserState* = 0); std::vector<Include> find_includes(const Importer& import); Include load_import(const Importer&, ParserState pstate); - Sass_Output_Style output_style() { return c_options->output_style; };+ Sass_Output_Style output_style() { return c_options.output_style; }; std::vector<std::string> get_included_files(bool skip = false, size_t headers = 0); private: void collect_plugin_paths(const char* paths_str);- void collect_plugin_paths(const char** paths_array);+ void collect_plugin_paths(string_list* paths_array); void collect_include_paths(const char* paths_str);- void collect_include_paths(const char** paths_array);+ void collect_include_paths(string_list* paths_array); std::string format_embedded_source_map(); std::string format_source_mapping_url(const std::string& out_path); @@ -119,27 +117,27 @@ class File_Context : public Context { public:- File_Context(struct Sass_File_Context* ctx)+ File_Context(struct Sass_File_Context& ctx) : Context(ctx) { } virtual ~File_Context();- virtual Block* parse();+ virtual Block_Obj parse(); }; class Data_Context : public Context { public: char* source_c_str; char* srcmap_c_str;- Data_Context(struct Sass_Data_Context* ctx)+ Data_Context(struct Sass_Data_Context& ctx) : Context(ctx) {- source_c_str = ctx->source_string;- srcmap_c_str = ctx->srcmap_string;- ctx->source_string = 0; // passed away- ctx->srcmap_string = 0; // passed away+ source_c_str = ctx.source_string;+ srcmap_c_str = ctx.srcmap_string;+ ctx.source_string = 0; // passed away+ ctx.srcmap_string = 0; // passed away } virtual ~Data_Context();- virtual Block* parse();+ virtual Block_Obj parse(); }; }
libsass/src/cssize.cpp view
@@ -1,9 +1,9 @@+#include "sass.hpp" #include <iostream> #include <typeinfo> #include <vector> #include "cssize.hpp"-#include "to_string.hpp" #include "context.hpp" #include "backtrace.hpp" @@ -11,157 +11,224 @@ Cssize::Cssize(Context& ctx, Backtrace* bt) : ctx(ctx),- block_stack(std::vector<Block*>()),- p_stack(std::vector<Statement*>()),+ block_stack(std::vector<Block_Ptr>()),+ p_stack(std::vector<Statement_Ptr>()), backtrace(bt)- { }+ { } - Statement* Cssize::parent()+ Statement_Ptr Cssize::parent() { return p_stack.size() ? p_stack.back() : block_stack.front(); } - Statement* Cssize::operator()(Block* b)+ Block_Ptr Cssize::operator()(Block_Ptr b) {- Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, b->pstate(), b->length(), b->is_root());+ Block_Ptr bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); // bb->tabs(b->tabs()); block_stack.push_back(bb);- append_block(b);+ append_block(b, bb); block_stack.pop_back(); return bb; } - Statement* Cssize::operator()(At_Rule* r)+ Statement_Ptr Cssize::operator()(Trace_Ptr t) {+ return t->block()->perform(this);+ }++ Statement_Ptr Cssize::operator()(Declaration_Ptr d)+ {+ String_Obj property = SASS_MEMORY_CAST(String, d->property());++ if (Declaration_Ptr dd = dynamic_cast<Declaration_Ptr>(parent())) {+ String_Obj parent_property = SASS_MEMORY_CAST(String, dd->property());+ property = SASS_MEMORY_NEW(String_Constant,+ d->property()->pstate(),+ parent_property->to_string() + "-" + property->to_string());+ if (!dd->value()) {+ d->tabs(dd->tabs() + 1);+ }+ }++ Declaration_Obj dd = SASS_MEMORY_NEW(Declaration,+ d->pstate(),+ property,+ d->value(),+ d->is_important());+ dd->is_indented(d->is_indented());+ dd->tabs(d->tabs());++ p_stack.push_back(&dd);+ Block_Obj bb = d->block() ? operator()(&d->block()) : NULL;+ p_stack.pop_back();++ if (bb && bb->length()) {+ if (dd->value() && !dd->value()->is_invisible()) {+ bb->unshift(&dd);+ }+ return bb.detach();+ }+ else if (dd->value() && !dd->value()->is_invisible()) {+ return dd.detach();+ }++ return 0;+ }++ Statement_Ptr Cssize::operator()(Directive_Ptr r)+ { if (!r->block() || !r->block()->length()) return r; if (parent()->statement_type() == Statement::RULESET) {- return (r->is_keyframes()) ? SASS_MEMORY_NEW(ctx.mem, Bubble, r->pstate(), r) : bubble(r);+ return (r->is_keyframes()) ? SASS_MEMORY_NEW(Bubble, r->pstate(), r) : bubble(r); } p_stack.push_back(r);- At_Rule* rr = SASS_MEMORY_NEW(ctx.mem, At_Rule,+ Directive_Obj rr = SASS_MEMORY_NEW(Directive, r->pstate(), r->keyword(), r->selector(),- r->block() ? r->block()->perform(this)->block() : 0);+ r->block() ? operator()(&r->block()) : 0); if (r->value()) rr->value(r->value()); p_stack.pop_back(); bool directive_exists = false; size_t L = rr->block() ? rr->block()->length() : 0; for (size_t i = 0; i < L && !directive_exists; ++i) {- Statement* s = (*r->block())[i];+ Statement_Obj s = r->block()->at(i); if (s->statement_type() != Statement::BUBBLE) directive_exists = true; else {- s = static_cast<Bubble*>(s)->node();+ Bubble_Obj s_obj = SASS_MEMORY_CAST(Bubble, s);+ s = s_obj->node(); if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false;- else directive_exists = (static_cast<At_Rule*>(s)->keyword() == rr->keyword());+ else directive_exists = (static_cast<Directive_Ptr>(&s)->keyword() == rr->keyword()); } } - Block* result = SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate());+ Block_Ptr result = SASS_MEMORY_NEW(Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) {- At_Rule* empty_node = static_cast<At_Rule*>(rr);- empty_node->block(SASS_MEMORY_NEW(ctx.mem, Block, rr->block() ? rr->block()->pstate() : rr->pstate()));- *result << empty_node;+ Directive_Ptr empty_node = SASS_MEMORY_CAST(Directive, rr);+ empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate()));+ result->append(empty_node); } - Statement* ss = debubble(rr->block() ? rr->block() : SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate()), rr);- for (size_t i = 0, L = ss->block()->length(); i < L; ++i) {- *result << (*ss->block())[i];+ Block_Obj ss = debubble(rr->block() ? &rr->block() : SASS_MEMORY_NEW(Block, rr->pstate()), &rr);+ for (size_t i = 0, L = ss->length(); i < L; ++i) {+ result->append(ss->at(i)); } return result; } - Statement* Cssize::operator()(Keyframe_Rule* r)+ Statement_Ptr Cssize::operator()(Keyframe_Rule_Ptr r) { if (!r->block() || !r->block()->length()) return r; - Keyframe_Rule* rr = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule,+ Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(),- r->block()->perform(this)->block());- if (r->selector()) rr->selector(r->selector());+ operator()(&r->block()));+ if (&r->name()) rr->name(r->name()); - return debubble(rr->block(), rr)->block();+ return debubble(&rr->block(), &rr); } - Statement* Cssize::operator()(Ruleset* r)+ Statement_Ptr Cssize::operator()(Ruleset_Ptr r) { p_stack.push_back(r);- Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset,+ // this can return a string schema+ // string schema is not a statement!+ // r->block() is already a string schema+ // and that is comming from propset expand+ Block_Ptr bb = operator()(&r->block());+ // this should protect us (at least a bit) from our mess+ // fixing this properly is harder that it should be ...+ if (dynamic_cast<Statement_Ptr>(bb) == NULL) {+ error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate());+ }+ Ruleset_Obj rr = SASS_MEMORY_NEW(Ruleset, r->pstate(), r->selector(),- r->block()->perform(this)->block());+ bb);++ rr->is_root(r->is_root()); // rr->tabs(r->block()->tabs()); p_stack.pop_back(); - Block* props = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());- Block* rules = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());+ if (!rr->block()) {+ error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate());+ }++ Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());+ Block_Ptr rules = SASS_MEMORY_NEW(Block, rr->block()->pstate()); for (size_t i = 0, L = rr->block()->length(); i < L; i++) {- Statement* s = (*rr->block())[i];- if (bubblable(s)) *rules << s;- if (!bubblable(s)) *props << s;+ Statement_Ptr s = &rr->block()->at(i);+ if (bubblable(s)) rules->append(s);+ if (!bubblable(s)) props->append(s); } if (props->length()) {- Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());- *bb += props;+ Block_Obj bb = SASS_MEMORY_NEW(Block, rr->block()->pstate());+ bb->concat(&props); rr->block(bb); for (size_t i = 0, L = rules->length(); i < L; i++) {- (*rules)[i]->tabs((*rules)[i]->tabs() + 1);+ Statement_Ptr stm = &rules->at(i);+ stm->tabs(stm->tabs() + 1); } - rules->unshift(rr);+ rules->unshift(&rr); } - rules = debubble(rules)->block();+ Block_Ptr ptr = rules;+ rules = debubble(rules);+ void* lp = ptr;+ void* rp = rules;+ if (lp != rp) {+ Block_Obj obj = ptr;+ } if (!(!rules->length() ||- !bubblable(rules->last()) ||+ !bubblable(&rules->last()) || parent()->statement_type() == Statement::RULESET)) { rules->last()->group_end(true); }- return rules; } - Statement* Cssize::operator()(Null* m)+ Statement_Ptr Cssize::operator()(Null_Ptr m) { return 0; } - Statement* Cssize::operator()(Media_Block* m)+ Statement_Ptr Cssize::operator()(Media_Block_Ptr m) { if (parent()->statement_type() == Statement::RULESET) { return bubble(m); } if (parent()->statement_type() == Statement::MEDIA)- { return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m); }+ { return SASS_MEMORY_NEW(Bubble, m->pstate(), m); } p_stack.push_back(m); - Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+ Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block, m->pstate(),- m->media_queries(),- m->block()->perform(this)->block());+ &m->media_queries(),+ operator()(&m->block())); mm->tabs(m->tabs()); p_stack.pop_back(); - return debubble(mm->block(), mm)->block();+ return debubble(&mm->block(), &mm); } - Statement* Cssize::operator()(Supports_Block* m)+ Statement_Ptr Cssize::operator()(Supports_Block_Ptr m) { if (!m->block()->length()) { return m; }@@ -171,339 +238,302 @@ p_stack.push_back(m); - Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+ Supports_Block_Obj mm = SASS_MEMORY_NEW(Supports_Block, m->pstate(),- m->condition(),- m->block()->perform(this)->block());+ &m->condition(),+ operator()(&m->block())); mm->tabs(m->tabs()); p_stack.pop_back(); - return debubble(mm->block(), mm)->block();+ return debubble(&mm->block(), &mm); } - Statement* Cssize::operator()(At_Root_Block* m)+ Statement_Ptr Cssize::operator()(At_Root_Block_Ptr m) { bool tmp = false; for (size_t i = 0, L = p_stack.size(); i < L; ++i) {- Statement* s = p_stack[i];+ Statement_Ptr s = p_stack[i]; tmp |= m->exclude_node(s); } if (!tmp) {- Block* bb = m->block()->perform(this)->block();+ Block_Ptr bb = operator()(&m->block()); for (size_t i = 0, L = bb->length(); i < L; ++i) { // (bb->elements())[i]->tabs(m->tabs());- if (bubblable((*bb)[i])) (*bb)[i]->tabs((*bb)[i]->tabs() + m->tabs());+ Statement_Obj stm = bb->at(i);+ if (bubblable(&stm)) stm->tabs(stm->tabs() + m->tabs()); }- if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end());+ if (bb->length() && bubblable(&bb->last())) bb->last()->group_end(m->group_end()); return bb; } if (m->exclude_node(parent())) {- return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m);+ return SASS_MEMORY_NEW(Bubble, m->pstate(), m); } return bubble(m); } - Statement* Cssize::bubble(At_Rule* m)+ Statement_Ptr Cssize::bubble(Directive_Ptr m) {- Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate());- Has_Block* new_rule = static_cast<Has_Block*>(shallow_copy(this->parent()));+ Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+ Has_Block_Obj new_rule = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs());-- size_t L = m->block() ? m->block()->length() : 0;- for (size_t i = 0; i < L; ++i) {- *new_rule->block() << (*m->block())[i];- }+ new_rule->block()->concat(&m->block()); - Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block() ? m->block()->pstate() : m->pstate());- *wrapper_block << new_rule;- At_Rule* mm = SASS_MEMORY_NEW(ctx.mem, At_Rule,+ Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate());+ wrapper_block->append(&new_rule);+ Directive_Obj mm = SASS_MEMORY_NEW(Directive, m->pstate(), m->keyword(), m->selector(), wrapper_block); if (m->value()) mm->value(m->value()); - Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+ Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), &mm); return bubble; } - Statement* Cssize::bubble(At_Root_Block* m)+ Statement_Ptr Cssize::bubble(At_Root_Block_Ptr m) {- Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate());- Has_Block* new_rule = static_cast<Has_Block*>(shallow_copy(this->parent()));+ Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+ Has_Block_Obj new_rule = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs());-- for (size_t i = 0, L = m->block()->length(); i < L; ++i) {- *new_rule->block() << (*m->block())[i];- }+ new_rule->block()->concat(&m->block()); - Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());- *wrapper_block << new_rule;- At_Root_Block* mm = SASS_MEMORY_NEW(ctx.mem, At_Root_Block,+ Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+ wrapper_block->append(&new_rule);+ At_Root_Block_Ptr mm = SASS_MEMORY_NEW(At_Root_Block, m->pstate(), wrapper_block, m->expression());- Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+ Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } - Statement* Cssize::bubble(Supports_Block* m)+ Statement_Ptr Cssize::bubble(Supports_Block_Ptr m) {- Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));+ Ruleset_Obj parent = static_cast<Ruleset_Ptr>(SASS_MEMORY_COPY(this->parent())); - Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate());- Ruleset* new_rule = SASS_MEMORY_NEW(ctx.mem, Ruleset,+ Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+ Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs());-- for (size_t i = 0, L = m->block()->length(); i < L; ++i) {- *new_rule->block() << (*m->block())[i];- }+ new_rule->block()->concat(&m->block()); - Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());- *wrapper_block << new_rule;- Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+ Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+ wrapper_block->append(new_rule);+ Supports_Block_Ptr mm = SASS_MEMORY_NEW(Supports_Block, m->pstate(),- m->condition(),+ &m->condition(), wrapper_block); mm->tabs(m->tabs()); - Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+ Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } - Statement* Cssize::bubble(Media_Block* m)+ Statement_Ptr Cssize::bubble(Media_Block_Ptr m) {- Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));+ Ruleset_Obj parent = static_cast<Ruleset_Ptr>(SASS_MEMORY_COPY(this->parent())); - Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate());- Ruleset* new_rule = SASS_MEMORY_NEW(ctx.mem, Ruleset,+ Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+ Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs());-- for (size_t i = 0, L = m->block()->length(); i < L; ++i) {- *new_rule->block() << (*m->block())[i];- }+ new_rule->block()->concat(&m->block()); - Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());- *wrapper_block << new_rule;- Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+ Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+ wrapper_block->append(new_rule);+ Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block, m->pstate(),- m->media_queries(),+ &m->media_queries(), wrapper_block, 0); mm->tabs(m->tabs()); - Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);-- return bubble;+ return SASS_MEMORY_NEW(Bubble, mm->pstate(), &mm); } - bool Cssize::bubblable(Statement* s)+ bool Cssize::bubblable(Statement_Ptr s) {- return s->statement_type() == Statement::RULESET || s->bubbles();+ return dynamic_cast<Ruleset_Ptr>(s) || s->bubbles(); } - Statement* Cssize::flatten(Statement* s)+ Block_Ptr Cssize::flatten(Block_Ptr b) {- Block* bb = s->block();- Block* result = SASS_MEMORY_NEW(ctx.mem, Block, bb->pstate(), 0, bb->is_root());- for (size_t i = 0, L = bb->length(); i < L; ++i) {- Statement* ss = (*bb)[i];- if (ss->block()) {- ss = flatten(ss);- for (size_t j = 0, K = ss->block()->length(); j < K; ++j) {- *result << (*ss->block())[j];+ Block_Ptr result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root());+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement_Ptr ss = &b->at(i);+ if (Block_Ptr bb = SASS_MEMORY_CAST_PTR(Block, ss)) {+ Block_Obj bs = flatten(bb);+ for (size_t j = 0, K = bs->length(); j < K; ++j) {+ result->append(bs->at(j)); } } else {- *result << ss;+ result->append(ss); } } return result; } - std::vector<std::pair<bool, Block*>> Cssize::slice_by_bubble(Statement* b)+ std::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block_Ptr b) {- std::vector<std::pair<bool, Block*>> results;- for (size_t i = 0, L = b->block()->length(); i < L; ++i) {- Statement* value = (*b->block())[i];- bool key = value->statement_type() == Statement::BUBBLE;+ std::vector<std::pair<bool, Block_Obj>> results; + for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement_Obj value = b->at(i);+ bool key = dynamic_cast<Bubble_Ptr>(&value) != NULL;+ if (!results.empty() && results.back().first == key) {- Block* wrapper_block = results.back().second;- *wrapper_block << value;+ Block_Obj wrapper_block = results.back().second;+ wrapper_block->append(value); } else {- Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, value->pstate());- *wrapper_block << value;+ Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());+ wrapper_block->append(value); results.push_back(std::make_pair(key, wrapper_block)); } } return results; } - Statement* Cssize::shallow_copy(Statement* s)- {- switch (s->statement_type())- {- case Statement::RULESET:- return SASS_MEMORY_NEW(ctx.mem, Ruleset, *static_cast<Ruleset*>(s));- case Statement::MEDIA:- return SASS_MEMORY_NEW(ctx.mem, Media_Block, *static_cast<Media_Block*>(s));- case Statement::BUBBLE:- return SASS_MEMORY_NEW(ctx.mem, Bubble, *static_cast<Bubble*>(s));- case Statement::DIRECTIVE:- return SASS_MEMORY_NEW(ctx.mem, At_Rule, *static_cast<At_Rule*>(s));- case Statement::SUPPORTS:- return SASS_MEMORY_NEW(ctx.mem, Supports_Block, *static_cast<Supports_Block*>(s));- case Statement::ATROOT:- return SASS_MEMORY_NEW(ctx.mem, At_Root_Block, *static_cast<At_Root_Block*>(s));- case Statement::KEYFRAMERULE:- return SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, *static_cast<Keyframe_Rule*>(s));- case Statement::NONE:- default:- error("unknown internal error; please contact the LibSass maintainers", s->pstate(), backtrace);- String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), std::string("`CSSize` can't clone ") + typeid(*s).name());- return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg);- }- }-- Statement* Cssize::debubble(Block* children, Statement* parent)+ Block_Ptr Cssize::debubble(Block_Ptr children, Statement_Ptr parent) {- Has_Block* previous_parent = 0;- std::vector<std::pair<bool, Block*>> baz = slice_by_bubble(children);- Block* result = SASS_MEMORY_NEW(ctx.mem, Block, children->pstate());+ Has_Block_Obj previous_parent = 0;+ std::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children);+ Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate()); for (size_t i = 0, L = baz.size(); i < L; ++i) { bool is_bubble = baz[i].first;- Block* slice = baz[i].second;+ Block_Obj slice = baz[i].second; if (!is_bubble) { if (!parent) {- *result << slice;+ result->append(&slice); } else if (previous_parent) {- *previous_parent->block() += slice;+ previous_parent->block()->concat(&slice); } else {- previous_parent = static_cast<Has_Block*>(shallow_copy(parent));+ previous_parent = static_cast<Has_Block_Ptr>(SASS_MEMORY_COPY(parent));+ previous_parent->block(slice); previous_parent->tabs(parent->tabs()); - Has_Block* new_parent = static_cast<Has_Block*>(shallow_copy(parent));- new_parent->block(slice);- new_parent->tabs(parent->tabs());-- *result << new_parent;+ result->append(&previous_parent); } continue; } - Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block,- children->block()->pstate(),- children->block()->length(),- children->block()->is_root());- for (size_t j = 0, K = slice->length(); j < K; ++j) {- Statement* ss = 0;- Bubble* b = static_cast<Bubble*>((*slice)[j]);-+ Statement_Ptr ss = NULL;+ Statement_Obj stm = slice->at(j);+ // this has to go now here (too bad)+ Bubble_Obj node = SASS_MEMORY_CAST(Bubble, stm);+ Media_Block_Ptr m1 = NULL;+ Media_Block_Ptr m2 = NULL;+ if (parent) m1 = SASS_MEMORY_CAST(Media_Block, *parent);+ if (node) m2 = SASS_MEMORY_CAST(Media_Block, node->node()); if (!parent || parent->statement_type() != Statement::MEDIA ||- b->node()->statement_type() != Statement::MEDIA ||- static_cast<Media_Block*>(b->node())->media_queries() == static_cast<Media_Block*>(parent)->media_queries())+ node->node()->statement_type() != Statement::MEDIA ||+ (m1 && m2 && &m1->media_queries() == &m2->media_queries())+ ) {- ss = b->node();+ ss = &node->node(); } else {- List* mq = merge_media_queries(static_cast<Media_Block*>(b->node()), static_cast<Media_Block*>(parent));+ List_Obj mq = merge_media_queries(static_cast<Media_Block_Ptr>(&node->node()), static_cast<Media_Block_Ptr>(parent)); if (!mq->length()) continue;- static_cast<Media_Block*>(b->node())->media_queries(mq);- ss = b->node();+ static_cast<Media_Block_Ptr>(&node->node())->media_queries(mq);+ ss = &node->node(); } if (!ss) continue; - ss->tabs(ss->tabs() + b->tabs());- ss->group_end(b->group_end());+ ss->tabs(ss->tabs() + node->tabs());+ ss->group_end(node->group_end()); if (!ss) continue; - Block* bb = SASS_MEMORY_NEW(ctx.mem, Block,- children->block()->pstate(),- children->block()->length(),- children->block()->is_root());- *bb << ss->perform(this);- Statement* wrapper = flatten(bb);- *wrapper_block << wrapper;+ Block_Obj bb = SASS_MEMORY_NEW(Block,+ children->pstate(),+ children->length(),+ children->is_root());+ bb->append(ss->perform(this)); - if (wrapper->block()->length()) {- previous_parent = 0;+ Block_Obj wrapper_block = SASS_MEMORY_NEW(Block,+ children->pstate(),+ children->length(),+ children->is_root());++ Block_Ptr wrapper = flatten(&bb);+ wrapper_block->append(wrapper);++ if (wrapper->length()) {+ previous_parent = NULL; }- } - if (wrapper_block) {- *result << flatten(wrapper_block);+ if (wrapper_block) {+ result->append(&wrapper_block);+ } } } - return flatten(result);+ return flatten(&result); } - Statement* Cssize::fallback_impl(AST_Node* n)+ Statement_Ptr Cssize::fallback_impl(AST_Node_Ptr n) {- return static_cast<Statement*>(n);+ return static_cast<Statement_Ptr>(n); } - void Cssize::append_block(Block* b)+ void Cssize::append_block(Block_Ptr b, Block_Ptr cur) {- Block* current_block = block_stack.back();- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* ith = (*b)[i]->perform(this);- if (ith && ith->block()) {- for (size_t j = 0, K = ith->block()->length(); j < K; ++j) {- *current_block << (*ith->block())[j];+ Statement_Obj ith = b->at(i)->perform(this);+ if (Block_Ptr bb = SASS_MEMORY_CAST(Block, ith)) {+ for (size_t j = 0, K = bb->length(); j < K; ++j) {+ cur->append(bb->at(j)); } } else if (ith) {- *current_block << ith;+ cur->append(ith); } } } - List* Cssize::merge_media_queries(Media_Block* m1, Media_Block* m2)+ List_Ptr Cssize::merge_media_queries(Media_Block_Ptr m1, Media_Block_Ptr m2) {- List* qq = SASS_MEMORY_NEW(ctx.mem, List,+ List_Ptr qq = SASS_MEMORY_NEW(List, m1->media_queries()->pstate(), m1->media_queries()->length(), SASS_COMMA); for (size_t i = 0, L = m1->media_queries()->length(); i < L; i++) { for (size_t j = 0, K = m2->media_queries()->length(); j < K; j++) {- Media_Query* mq1 = static_cast<Media_Query*>((*m1->media_queries())[i]);- Media_Query* mq2 = static_cast<Media_Query*>((*m2->media_queries())[j]);- Media_Query* mq = merge_media_query(mq1, mq2);-- if (mq) *qq << mq;+ Expression_Obj l1 = m1->media_queries()->at(i);+ Expression_Obj l2 = m2->media_queries()->at(j);+ Media_Query_Ptr mq1 = SASS_MEMORY_CAST(Media_Query, l1);+ Media_Query_Ptr mq2 = SASS_MEMORY_CAST(Media_Query, l2);+ Media_Query_Ptr mq = merge_media_query(mq1, mq2);+ if (mq) qq->append(mq); } } @@ -511,17 +541,16 @@ } - Media_Query* Cssize::merge_media_query(Media_Query* mq1, Media_Query* mq2)+ Media_Query_Ptr Cssize::merge_media_query(Media_Query_Ptr mq1, Media_Query_Ptr mq2) {- To_String to_string(&ctx); std::string type; std::string mod; std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");- std::string t1 = mq1->media_type() ? mq1->media_type()->perform(&to_string) : "";+ std::string t1 = &mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : ""; std::string m2 = std::string(mq2->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");- std::string t2 = mq2->media_type() ? mq2->media_type()->perform(&to_string) : "";+ std::string t2 = &mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : ""; if (t1.empty()) t1 = t2;@@ -548,18 +577,19 @@ mod = m1.empty() ? m2 : m1; } - Media_Query* mm = SASS_MEMORY_NEW(ctx.mem, Media_Query,+ Media_Query_Ptr mm = SASS_MEMORY_NEW(Media_Query, mq1->pstate(), 0, mq1->length() + mq2->length(), mod == "not", mod == "only" ); if (!type.empty()) {- mm->media_type(SASS_MEMORY_NEW(ctx.mem, String_Quoted, mq1->pstate(), type));+ mm->media_type(SASS_MEMORY_NEW(String_Quoted, mq1->pstate(), type)); } - *mm += mq2;- *mm += mq1;+ mm->concat(mq2);+ mm->concat(mq1);+ return mm; } }
libsass/src/cssize.hpp view
@@ -8,69 +8,69 @@ namespace Sass { - typedef Environment<AST_Node*> Env;+ typedef Environment<AST_Node_Obj> Env; struct Backtrace; - class Cssize : public Operation_CRTP<Statement*, Cssize> {+ class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> { - Context& ctx;- std::vector<Block*> block_stack;- std::vector<Statement*> p_stack;- Backtrace* backtrace;+ Context& ctx;+ std::vector<Block_Ptr> block_stack;+ std::vector<Statement_Ptr> p_stack;+ Backtrace* backtrace; - Statement* fallback_impl(AST_Node* n);+ Statement_Ptr fallback_impl(AST_Node_Ptr n); public: Cssize(Context&, Backtrace*);- virtual ~Cssize() { }+ ~Cssize() { } - using Operation<Statement*>::operator();+ Selector_List_Ptr selector(); - Statement* operator()(Block*);- Statement* operator()(Ruleset*);- // Statement* operator()(Propset*);- // Statement* operator()(Bubble*);- Statement* operator()(Media_Block*);- Statement* operator()(Supports_Block*);- Statement* operator()(At_Root_Block*);- Statement* operator()(At_Rule*);- Statement* operator()(Keyframe_Rule*);- // Statement* operator()(Declaration*);- // Statement* operator()(Assignment*);- // Statement* operator()(Import*);- // Statement* operator()(Import_Stub*);- // Statement* operator()(Warning*);- // Statement* operator()(Error*);- // Statement* operator()(Comment*);- // Statement* operator()(If*);- // Statement* operator()(For*);- // Statement* operator()(Each*);- // Statement* operator()(While*);- // Statement* operator()(Return*);- // Statement* operator()(Extension*);- // Statement* operator()(Definition*);- // Statement* operator()(Mixin_Call*);- // Statement* operator()(Content*);- Statement* operator()(Null*);+ Block_Ptr operator()(Block_Ptr);+ Statement_Ptr operator()(Ruleset_Ptr);+ // Statement_Ptr operator()(Bubble_Ptr);+ Statement_Ptr operator()(Media_Block_Ptr);+ Statement_Ptr operator()(Supports_Block_Ptr);+ Statement_Ptr operator()(At_Root_Block_Ptr);+ Statement_Ptr operator()(Directive_Ptr);+ Statement_Ptr operator()(Keyframe_Rule_Ptr);+ Statement_Ptr operator()(Trace_Ptr);+ Statement_Ptr operator()(Declaration_Ptr);+ // Statement_Ptr operator()(Assignment_Ptr);+ // Statement_Ptr operator()(Import_Ptr);+ // Statement_Ptr operator()(Import_Stub_Ptr);+ // Statement_Ptr operator()(Warning_Ptr);+ // Statement_Ptr operator()(Error_Ptr);+ // Statement_Ptr operator()(Comment_Ptr);+ // Statement_Ptr operator()(If_Ptr);+ // Statement_Ptr operator()(For_Ptr);+ // Statement_Ptr operator()(Each_Ptr);+ // Statement_Ptr operator()(While_Ptr);+ // Statement_Ptr operator()(Return_Ptr);+ // Statement_Ptr operator()(Extension_Ptr);+ // Statement_Ptr operator()(Definition_Ptr);+ // Statement_Ptr operator()(Mixin_Call_Ptr);+ // Statement_Ptr operator()(Content_Ptr);+ Statement_Ptr operator()(Null_Ptr); - Statement* parent();- std::vector<std::pair<bool, Block*>> slice_by_bubble(Statement*);- Statement* bubble(At_Rule*);- Statement* bubble(At_Root_Block*);- Statement* bubble(Media_Block*);- Statement* bubble(Supports_Block*);- Statement* shallow_copy(Statement*);- Statement* debubble(Block* children, Statement* parent = 0);- Statement* flatten(Statement*);- bool bubblable(Statement*);+ Statement_Ptr parent();+ std::vector<std::pair<bool, Block_Obj>> slice_by_bubble(Block_Ptr);+ Statement_Ptr bubble(Directive_Ptr);+ Statement_Ptr bubble(At_Root_Block_Ptr);+ Statement_Ptr bubble(Media_Block_Ptr);+ Statement_Ptr bubble(Supports_Block_Ptr); - List* merge_media_queries(Media_Block*, Media_Block*);- Media_Query* merge_media_query(Media_Query*, Media_Query*);+ Block_Ptr debubble(Block_Ptr children, Statement_Ptr parent = 0);+ Block_Ptr flatten(Block_Ptr);+ bool bubblable(Statement_Ptr); + List_Ptr merge_media_queries(Media_Block_Ptr, Media_Block_Ptr);+ Media_Query_Ptr merge_media_query(Media_Query_Ptr, Media_Query_Ptr);+ template <typename U>- Statement* fallback(U x) { return fallback_impl(x); }+ Statement_Ptr fallback(U x) { return fallback_impl(x); } - void append_block(Block*);+ void append_block(Block_Ptr, Block_Ptr); }; }
libsass/src/debugger.hpp view
@@ -8,13 +8,13 @@ using namespace Sass; -inline void debug_ast(AST_Node* node, std::string ind = "", Env* env = 0);+inline void debug_ast(AST_Node_Ptr node, std::string ind = "", Env* env = 0); inline void debug_sources_set(SourcesSet& set, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &pair : set) {- debug_ast(pair, ind + "");+ debug_ast(&pair, ind + ""); // debug_ast(set[pair], ind + "first: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";@@ -44,7 +44,7 @@ return is.str(); } -inline std::string pstate_source_position(AST_Node* node)+inline std::string pstate_source_position(AST_Node_Ptr node) { std::stringstream str; Position start(node->pstate());@@ -52,58 +52,82 @@ str << (start.file == std::string::npos ? -1 : start.file) << "@[" << start.line << ":" << start.column << "]" << "-[" << end.line << ":" << end.column << "]";+#ifdef DEBUG_SHARED_PTR+ str << "x" << node->getRefCount() << ""+ << " " << node->getDbgFile()+ << "@" << node->getDbgLine();+#endif return str.str(); } -inline void debug_ast(AST_Node* node, std::string ind, Env* env)+inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) { if (node == 0) return; if (ind == "") std::cerr << "####################################################################\n";- if (dynamic_cast<Bubble*>(node)) {- Bubble* bubble = dynamic_cast<Bubble*>(node);+ if (dynamic_cast<Bubble_Ptr>(node)) {+ Bubble_Ptr bubble = dynamic_cast<Bubble_Ptr>(node); std::cerr << ind << "Bubble " << bubble; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl;- } else if (dynamic_cast<At_Root_Block*>(node)) {- At_Root_Block* root_block = dynamic_cast<At_Root_Block*>(node);+ debug_ast(&bubble->node(), ind + " ", env);+ } else if (dynamic_cast<Trace_Ptr>(node)) {+ Trace_Ptr trace = dynamic_cast<Trace_Ptr>(node);+ std::cerr << ind << "Trace " << trace;+ std::cerr << " (" << pstate_source_position(node) << ")"+ << " [name:" << trace->name() << "]"+ << std::endl;+ debug_ast(&trace->block(), ind + " ", env);+ } else if (dynamic_cast<At_Root_Block_Ptr>(node)) {+ At_Root_Block_Ptr root_block = dynamic_cast<At_Root_Block_Ptr>(node); std::cerr << ind << "At_Root_Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << root_block->tabs(); std::cerr << std::endl;- debug_ast(root_block->expression(), ind + ":", env);- debug_ast(root_block->block(), ind + " ", env);- } else if (dynamic_cast<Selector_List*>(node)) {- Selector_List* selector = dynamic_cast<Selector_List*>(node);+ debug_ast(&root_block->expression(), ind + ":", env);+ debug_ast(&root_block->block(), ind + " ", env);+ } else if (dynamic_cast<Selector_List_Ptr>(node)) {+ Selector_List_Ptr selector = dynamic_cast<Selector_List_Ptr>(node); std::cerr << ind << "Selector_List " << selector; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <" << selector->hash() << ">"; std::cerr << " [@media:" << selector->media_block() << "]";+ std::cerr << (selector->is_invisible() ? " [INVISIBLE]": " -");+ std::cerr << (selector->has_placeholder() ? " [PLACEHOLDER]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl; - for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }+ for(const Complex_Selector_Obj& i : selector->elements()) { debug_ast(&i, ind + " ", env); } -// } else if (dynamic_cast<Expression*>(node)) {-// Expression* expression = dynamic_cast<Expression*>(node);+// } else if (dynamic_cast<Expression_Ptr>(node)) {+// Expression_Ptr expression = dynamic_cast<Expression_Ptr>(node); // std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl; - } else if (dynamic_cast<Parent_Selector*>(node)) {- Parent_Selector* selector = dynamic_cast<Parent_Selector*>(node);+ } else if (dynamic_cast<Parent_Selector_Ptr>(node)) {+ Parent_Selector_Ptr selector = dynamic_cast<Parent_Selector_Ptr>(node); std::cerr << ind << "Parent_Selector " << selector; // if (selector->not_selector()) cerr << " [in_declaration]"; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " [" << (selector->is_real_parent_ref() ? "REAL" : "FAKE") << "]"; std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; // debug_ast(selector->selector(), ind + "->", env); - } else if (dynamic_cast<Complex_Selector*>(node)) {- Complex_Selector* selector = dynamic_cast<Complex_Selector*>(node);+ } else if (dynamic_cast<Complex_Selector_Ptr>(node)) {+ Complex_Selector_Ptr selector = dynamic_cast<Complex_Selector_Ptr>(node); std::cerr << ind << "Complex_Selector " << selector << " (" << pstate_source_position(node) << ")"+ << " <" << selector->hash() << ">"+ << " [length:" << longToHex(selector->length()) << "]" << " [weight:" << longToHex(selector->specificity()) << "]" << " [@media:" << selector->media_block() << "]"+ << (selector->is_invisible() ? " [INVISIBLE]": " -")+ << (selector->has_placeholder() ? " [PLACEHOLDER]": " -") << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_parent_ref() ? " [has parent]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << (selector->has_line_break() ? " [line-break]": " -") << " -- ";@@ -117,64 +141,104 @@ } // if (del = "/") del += selector->reference()->perform(&to_string) + "/"; std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;- debug_ast(selector->head(), ind + " " /* + "[" + del + "]" */, env);+ debug_ast(&selector->head(), ind + " " /* + "[" + del + "]" */, env); if (selector->tail()) {- debug_ast(selector->tail(), ind + "{" + del + "}", env);+ debug_ast(&selector->tail(), ind + "{" + del + "}", env); } else if(del != " ") { std::cerr << ind << " |" << del << "| {trailing op}" << std::endl; } SourcesSet set = selector->sources(); // debug_sources_set(set, ind + " @--> ");- } else if (dynamic_cast<Compound_Selector*>(node)) {- Compound_Selector* selector = dynamic_cast<Compound_Selector*>(node);+ } else if (dynamic_cast<Compound_Selector_Ptr>(node)) {+ Compound_Selector_Ptr selector = dynamic_cast<Compound_Selector_Ptr>(node); std::cerr << ind << "Compound_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <" << selector->hash() << ">"; std::cerr << " [weight:" << longToHex(selector->specificity()) << "]"; std::cerr << " [@media:" << selector->media_block() << "]";+ std::cerr << (selector->extended() ? " [extended]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;- for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Propset*>(node)) {- Propset* selector = dynamic_cast<Propset*>(node);- std::cerr << ind << "Propset " << selector;- std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " " << selector->tabs() << std::endl;- if (selector->block()) for(auto i : selector->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Wrapped_Selector*>(node)) {- Wrapped_Selector* selector = dynamic_cast<Wrapped_Selector*>(node);+ for(const Simple_Selector_Obj& i : selector->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Wrapped_Selector_Ptr>(node)) {+ Wrapped_Selector_Ptr selector = dynamic_cast<Wrapped_Selector_Ptr>(node); std::cerr << ind << "Wrapped_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;- debug_ast(selector->selector(), ind + " () ", env);- } else if (dynamic_cast<Pseudo_Selector*>(node)) {- Pseudo_Selector* selector = dynamic_cast<Pseudo_Selector*>(node);+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " <<" << selector->ns_name() << ">>";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << std::endl;+ debug_ast(&selector->selector(), ind + " () ", env);+ } else if (dynamic_cast<Pseudo_Selector_Ptr>(node)) {+ Pseudo_Selector_Ptr selector = dynamic_cast<Pseudo_Selector_Ptr>(node); std::cerr << ind << "Pseudo_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;- debug_ast(selector->expression(), ind + " <= ", env);- } else if (dynamic_cast<Attribute_Selector*>(node)) {- Attribute_Selector* selector = dynamic_cast<Attribute_Selector*>(node);+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " <<" << selector->ns_name() << ">>";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << std::endl;+ debug_ast(&selector->expression(), ind + " <= ", env);+ } else if (dynamic_cast<Attribute_Selector_Ptr>(node)) {+ Attribute_Selector_Ptr selector = dynamic_cast<Attribute_Selector_Ptr>(node); std::cerr << ind << "Attribute_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;- debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);- } else if (dynamic_cast<Selector_Qualifier*>(node)) {- Selector_Qualifier* selector = dynamic_cast<Selector_Qualifier*>(node);- std::cerr << ind << "Selector_Qualifier " << selector;+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " <<" << selector->ns_name() << ">>";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << std::endl;+ debug_ast(&selector->value(), ind + "[" + selector->matcher() + "] ", env);+ } else if (dynamic_cast<Class_Selector_Ptr>(node)) {+ Class_Selector_Ptr selector = dynamic_cast<Class_Selector_Ptr>(node);+ std::cerr << ind << "Class_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;- } else if (dynamic_cast<Type_Selector*>(node)) {- Type_Selector* selector = dynamic_cast<Type_Selector*>(node);- std::cerr << ind << "Type_Selector " << selector;+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " <<" << selector->ns_name() << ">>";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << std::endl;+ } else if (dynamic_cast<Id_Selector_Ptr>(node)) {+ Id_Selector_Ptr selector = dynamic_cast<Id_Selector_Ptr>(node);+ std::cerr << ind << "Id_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") <<- " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;- } else if (dynamic_cast<Selector_Placeholder*>(node)) {+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " <<" << selector->ns_name() << ">>";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << std::endl;+ } else if (dynamic_cast<Element_Selector_Ptr>(node)) {+ Element_Selector_Ptr selector = dynamic_cast<Element_Selector_Ptr>(node);+ std::cerr << ind << "Element_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <" << selector->hash() << ">";+ std::cerr << " <<" << selector->ns_name() << ">>";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">";+ std::cerr << std::endl;+ } else if (dynamic_cast<Placeholder_Selector_Ptr>(node)) { - Selector_Placeholder* selector = dynamic_cast<Selector_Placeholder*>(node);- std::cerr << ind << "Selector_Placeholder [" << selector->ns_name() << "] " << selector+ Placeholder_Selector_Ptr selector = dynamic_cast<Placeholder_Selector_Ptr>(node);+ std::cerr << ind << "Placeholder_Selector [" << selector->ns_name() << "] " << selector;+ std::cerr << " (" << pstate_source_position(selector) << ")"+ << " <" << selector->hash() << ">" << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_line_break() ? " [line-break]": " -")@@ -187,8 +251,8 @@ std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; - } else if (dynamic_cast<Selector_Schema*>(node)) {- Selector_Schema* selector = dynamic_cast<Selector_Schema*>(node);+ } else if (dynamic_cast<Selector_Schema_Ptr>(node)) {+ Selector_Schema_Ptr selector = dynamic_cast<Selector_Schema_Ptr>(node); std::cerr << ind << "Selector_Schema " << selector; std::cerr << " (" << pstate_source_position(node) << ")" << (selector->at_root() && selector->at_root() ? " [@ROOT]" : "")@@ -197,183 +261,196 @@ << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; - debug_ast(selector->contents(), ind + " ");+ debug_ast(&selector->contents(), ind + " "); // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } - } else if (dynamic_cast<Selector*>(node)) {- Selector* selector = dynamic_cast<Selector*>(node);+ } else if (dynamic_cast<Selector_Ptr>(node)) {+ Selector_Ptr selector = dynamic_cast<Selector_Ptr>(node); std::cerr << ind << "Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; - } else if (dynamic_cast<Media_Query_Expression*>(node)) {- Media_Query_Expression* block = dynamic_cast<Media_Query_Expression*>(node);+ } else if (dynamic_cast<Media_Query_Expression_Ptr>(node)) {+ Media_Query_Expression_Ptr block = dynamic_cast<Media_Query_Expression_Ptr>(node); std::cerr << ind << "Media_Query_Expression " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -") << std::endl;- debug_ast(block->feature(), ind + " feature) ");- debug_ast(block->value(), ind + " value) ");+ debug_ast(&block->feature(), ind + " feature) ");+ debug_ast(&block->value(), ind + " value) "); - } else if (dynamic_cast<Media_Query*>(node)) {- Media_Query* block = dynamic_cast<Media_Query*>(node);+ } else if (dynamic_cast<Media_Query_Ptr>(node)) {+ Media_Query_Ptr block = dynamic_cast<Media_Query_Ptr>(node); std::cerr << ind << "Media_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_negated() ? " [is_negated]": " -") << (block->is_restricted() ? " [is_restricted]": " -") << std::endl;- debug_ast(block->media_type(), ind + " ");- for(auto i : block->elements()) { debug_ast(i, ind + " ", env); }+ debug_ast(&block->media_type(), ind + " ");+ for(const auto& i : block->elements()) { debug_ast(&i, ind + " ", env); } - } else if (dynamic_cast<Media_Block*>(node)) {- Media_Block* block = dynamic_cast<Media_Block*>(node);+ } else if (dynamic_cast<Media_Block_Ptr>(node)) {+ Media_Block_Ptr block = dynamic_cast<Media_Block_Ptr>(node); std::cerr << ind << "Media_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->media_queries(), ind + " =@ ");- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Supports_Block*>(node)) {- Supports_Block* block = dynamic_cast<Supports_Block*>(node);+ debug_ast(&block->media_queries(), ind + " =@ ");+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Supports_Block_Ptr>(node)) {+ Supports_Block_Ptr block = dynamic_cast<Supports_Block_Ptr>(node); std::cerr << ind << "Supports_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->condition(), ind + " =@ ");- } else if (dynamic_cast<Supports_Operator*>(node)) {- Supports_Operator* block = dynamic_cast<Supports_Operator*>(node);+ debug_ast(&block->condition(), ind + " =@ ");+ debug_ast(&block->block(), ind + " <>");+ } else if (dynamic_cast<Supports_Operator_Ptr>(node)) {+ Supports_Operator_Ptr block = dynamic_cast<Supports_Operator_Ptr>(node); std::cerr << ind << "Supports_Operator " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl;- debug_ast(block->left(), ind + " left) ");- debug_ast(block->right(), ind + " right) ");- } else if (dynamic_cast<Supports_Negation*>(node)) {- Supports_Negation* block = dynamic_cast<Supports_Negation*>(node);+ debug_ast(&block->left(), ind + " left) ");+ debug_ast(&block->right(), ind + " right) ");+ } else if (dynamic_cast<Supports_Negation_Ptr>(node)) {+ Supports_Negation_Ptr block = dynamic_cast<Supports_Negation_Ptr>(node); std::cerr << ind << "Supports_Negation " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl;- debug_ast(block->condition(), ind + " condition) ");- } else if (dynamic_cast<Supports_Declaration*>(node)) {- Supports_Declaration* block = dynamic_cast<Supports_Declaration*>(node);+ debug_ast(&block->condition(), ind + " condition) ");+ } else if (dynamic_cast<At_Root_Query_Ptr>(node)) {+ At_Root_Query_Ptr block = dynamic_cast<At_Root_Query_Ptr>(node);+ std::cerr << ind << "At_Root_Query " << block;+ std::cerr << " (" << pstate_source_position(node) << ")"+ << std::endl;+ debug_ast(&block->feature(), ind + " feature) ");+ debug_ast(&block->value(), ind + " value) ");+ } else if (dynamic_cast<Supports_Declaration_Ptr>(node)) {+ Supports_Declaration_Ptr block = dynamic_cast<Supports_Declaration_Ptr>(node); std::cerr << ind << "Supports_Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl;- debug_ast(block->feature(), ind + " feature) ");- debug_ast(block->value(), ind + " value) ");- } else if (dynamic_cast<Block*>(node)) {- Block* root_block = dynamic_cast<Block*>(node);+ debug_ast(&block->feature(), ind + " feature) ");+ debug_ast(&block->value(), ind + " value) ");+ } else if (dynamic_cast<Block_Ptr>(node)) {+ Block_Ptr root_block = dynamic_cast<Block_Ptr>(node); std::cerr << ind << "Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; if (root_block->is_root()) std::cerr << " [root]"; std::cerr << " " << root_block->tabs() << std::endl;- if (root_block->block()) for(auto i : root_block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Warning*>(node)) {- Warning* block = dynamic_cast<Warning*>(node);+ for(const Statement_Obj& i : root_block->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Warning_Ptr>(node)) {+ Warning_Ptr block = dynamic_cast<Warning_Ptr>(node); std::cerr << ind << "Warning " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Error*>(node)) {- Error* block = dynamic_cast<Error*>(node);+ debug_ast(&block->message(), ind + " : ");+ } else if (dynamic_cast<Error_Ptr>(node)) {+ Error_Ptr block = dynamic_cast<Error_Ptr>(node); std::cerr << ind << "Error " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Debug*>(node)) {- Debug* block = dynamic_cast<Debug*>(node);+ } else if (dynamic_cast<Debug_Ptr>(node)) {+ Debug_Ptr block = dynamic_cast<Debug_Ptr>(node); std::cerr << ind << "Debug " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Comment*>(node)) {- Comment* block = dynamic_cast<Comment*>(node);+ debug_ast(&block->value(), ind + " ");+ } else if (dynamic_cast<Comment_Ptr>(node)) {+ Comment_Ptr block = dynamic_cast<Comment_Ptr>(node); std::cerr << ind << "Comment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl;- debug_ast(block->text(), ind + "// ", env);- } else if (dynamic_cast<If*>(node)) {- If* block = dynamic_cast<If*>(node);+ debug_ast(&block->text(), ind + "// ", env);+ } else if (dynamic_cast<If_Ptr>(node)) {+ If_Ptr block = dynamic_cast<If_Ptr>(node); std::cerr << ind << "If " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->predicate(), ind + " = ");- debug_ast(block->block(), ind + " <>");- debug_ast(block->alternative(), ind + " ><");- } else if (dynamic_cast<Return*>(node)) {- Return* block = dynamic_cast<Return*>(node);+ debug_ast(&block->predicate(), ind + " = ");+ debug_ast(&block->block(), ind + " <>");+ debug_ast(&block->alternative(), ind + " ><");+ } else if (dynamic_cast<Return_Ptr>(node)) {+ Return_Ptr block = dynamic_cast<Return_Ptr>(node); std::cerr << ind << "Return " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Extension*>(node)) {- Extension* block = dynamic_cast<Extension*>(node);+ } else if (dynamic_cast<Extension_Ptr>(node)) {+ Extension_Ptr block = dynamic_cast<Extension_Ptr>(node); std::cerr << ind << "Extension " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->selector(), ind + "-> ", env);- } else if (dynamic_cast<Content*>(node)) {- Content* block = dynamic_cast<Content*>(node);+ debug_ast(&block->selector(), ind + "-> ", env);+ } else if (dynamic_cast<Content_Ptr>(node)) {+ Content_Ptr block = dynamic_cast<Content_Ptr>(node); std::cerr << ind << "Content " << block; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [@media:" << block->media_block() << "]"; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Import_Stub*>(node)) {- Import_Stub* block = dynamic_cast<Import_Stub*>(node);+ } else if (dynamic_cast<Import_Stub_Ptr>(node)) {+ Import_Stub_Ptr block = dynamic_cast<Import_Stub_Ptr>(node); std::cerr << ind << "Import_Stub " << block; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << block->imp_path() << "] "; std::cerr << " " << block->tabs() << std::endl;- } else if (dynamic_cast<Import*>(node)) {- Import* block = dynamic_cast<Import*>(node);+ } else if (dynamic_cast<Import_Ptr>(node)) {+ Import_Ptr block = dynamic_cast<Import_Ptr>(node); std::cerr << ind << "Import " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->media_queries(), ind + " @ "); // std::vector<std::string> files_;- for (auto imp : block->urls()) debug_ast(imp, "@ ", env);- } else if (dynamic_cast<Assignment*>(node)) {- Assignment* block = dynamic_cast<Assignment*>(node);+ for (auto imp : block->urls()) debug_ast(&imp, ind + "@: ", env);+ debug_ast(&block->import_queries(), ind + "@@ ");+ } else if (dynamic_cast<Assignment_Ptr>(node)) {+ Assignment_Ptr block = dynamic_cast<Assignment_Ptr>(node); std::cerr << ind << "Assignment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <<" << block->variable() << ">> " << block->tabs() << std::endl;- debug_ast(block->value(), ind + "=", env);- } else if (dynamic_cast<Declaration*>(node)) {- Declaration* block = dynamic_cast<Declaration*>(node);+ debug_ast(&block->value(), ind + "=", env);+ } else if (dynamic_cast<Declaration_Ptr>(node)) {+ Declaration_Ptr block = dynamic_cast<Declaration_Ptr>(node); std::cerr << ind << "Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->property(), ind + " prop: ", env);- debug_ast(block->value(), ind + " value: ", env);- } else if (dynamic_cast<Keyframe_Rule*>(node)) {- Keyframe_Rule* has_block = dynamic_cast<Keyframe_Rule*>(node);+ debug_ast(&block->property(), ind + " prop: ", env);+ debug_ast(&block->value(), ind + " value: ", env);+ debug_ast(&block->block(), ind + " ", env);+ } else if (dynamic_cast<Keyframe_Rule_Ptr>(node)) {+ Keyframe_Rule_Ptr has_block = dynamic_cast<Keyframe_Rule_Ptr>(node); std::cerr << ind << "Keyframe_Rule " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl;- if (has_block->selector()) debug_ast(has_block->selector(), ind + "@");- if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<At_Rule*>(node)) {- At_Rule* block = dynamic_cast<At_Rule*>(node);- std::cerr << ind << "At_Rule " << block;+ if (has_block->name()) debug_ast(&has_block->name(), ind + "@");+ if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Directive_Ptr>(node)) {+ Directive_Ptr block = dynamic_cast<Directive_Ptr>(node);+ std::cerr << ind << "Directive " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl;- debug_ast(block->selector(), ind + "~", env);- debug_ast(block->value(), ind + "+", env);- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Each*>(node)) {- Each* block = dynamic_cast<Each*>(node);+ debug_ast(&block->selector(), ind + "~", env);+ debug_ast(&block->value(), ind + "+", env);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Each_Ptr>(node)) {+ Each_Ptr block = dynamic_cast<Each_Ptr>(node); std::cerr << ind << "Each " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<For*>(node)) {- For* block = dynamic_cast<For*>(node);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<For_Ptr>(node)) {+ For_Ptr block = dynamic_cast<For_Ptr>(node); std::cerr << ind << "For " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<While*>(node)) {- While* block = dynamic_cast<While*>(node);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<While_Ptr>(node)) {+ While_Ptr block = dynamic_cast<While_Ptr>(node); std::cerr << ind << "While " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Definition*>(node)) {- Definition* block = dynamic_cast<Definition*>(node);+ if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Definition_Ptr>(node)) {+ Definition_Ptr block = dynamic_cast<Definition_Ptr>(node); std::cerr << ind << "Definition " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << block->name() << "] ";@@ -382,62 +459,73 @@ // std::cerr << " [signature: " << block->signature() << "] "; std::cerr << " [native: " << block->native_function() << "] "; std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->parameters(), ind + " params: ", env);- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Mixin_Call*>(node)) {- Mixin_Call* block = dynamic_cast<Mixin_Call*>(node);+ debug_ast(&block->parameters(), ind + " params: ", env);+ if (block->block()) debug_ast(&block->block(), ind + " ", env);+ } else if (dynamic_cast<Mixin_Call_Ptr>(node)) {+ Mixin_Call_Ptr block = dynamic_cast<Mixin_Call_Ptr>(node); std::cerr << ind << "Mixin_Call " << block << " " << block->tabs();+ std::cerr << " (" << pstate_source_position(block) << ")"; std::cerr << " [" << block->name() << "]"; std::cerr << " [has_content: " << block->has_content() << "] " << std::endl;- debug_ast(block->arguments(), ind + " args: ");- if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (Ruleset* ruleset = dynamic_cast<Ruleset*>(node)) {+ debug_ast(&block->arguments(), ind + " args: ");+ if (block->block()) debug_ast(&block->block(), ind + " ", env);+ } else if (Ruleset_Ptr ruleset = dynamic_cast<Ruleset_Ptr>(node)) { std::cerr << ind << "Ruleset " << ruleset; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [indent: " << ruleset->tabs() << "]";+ std::cerr << (ruleset->is_invisible() ? " [INVISIBLE]" : ""); std::cerr << (ruleset->at_root() ? " [@ROOT]" : ""); std::cerr << (ruleset->is_root() ? " [root]" : ""); std::cerr << std::endl;- debug_ast(ruleset->selector(), ind + ">");- debug_ast(ruleset->block(), ind + " ");- } else if (dynamic_cast<Block*>(node)) {- Block* block = dynamic_cast<Block*>(node);+ debug_ast(&ruleset->selector(), ind + ">");+ debug_ast(&ruleset->block(), ind + " ");+ } else if (dynamic_cast<Block_Ptr>(node)) {+ Block_Ptr block = dynamic_cast<Block_Ptr>(node); std::cerr << ind << "Block " << block; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << (block->is_invisible() ? " [INVISIBLE]" : ""); std::cerr << " [indent: " << block->tabs() << "]" << std::endl;- for(auto i : block->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Textual*>(node)) {- Textual* expression = dynamic_cast<Textual*>(node);- std::cerr << ind << "Textual ";+ for(const Statement_Obj& i : block->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Textual_Ptr>(node)) {+ Textual_Ptr expression = dynamic_cast<Textual_Ptr>(node);+ std::cerr << ind << "Textual " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->type() == Textual::NUMBER) std::cerr << " [NUMBER]"; else if (expression->type() == Textual::PERCENTAGE) std::cerr << " [PERCENTAGE]"; else if (expression->type() == Textual::DIMENSION) std::cerr << " [DIMENSION]"; else if (expression->type() == Textual::HEX) std::cerr << " [HEX]";- std::cerr << expression << " [" << expression->value() << "]";+ std::cerr << " [" << expression->value() << "]";+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << std::endl;- } else if (dynamic_cast<Variable*>(node)) {- Variable* expression = dynamic_cast<Variable*>(node);+ } else if (dynamic_cast<Variable_Ptr>(node)) {+ Variable_Ptr expression = dynamic_cast<Variable_Ptr>(node); std::cerr << ind << "Variable " << expression;+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]" << std::endl; std::string name(expression->name());- if (env && env->has(name)) debug_ast(static_cast<Expression*>((*env)[name]), ind + " -> ", env);- } else if (dynamic_cast<Function_Call_Schema*>(node)) {- Function_Call_Schema* expression = dynamic_cast<Function_Call_Schema*>(node);+ if (env && env->has(name)) debug_ast(SASS_MEMORY_CAST(Expression, (*env)[name]), ind + " -> ", env);+ } else if (dynamic_cast<Function_Call_Schema_Ptr>(node)) {+ Function_Call_Schema_Ptr expression = dynamic_cast<Function_Call_Schema_Ptr>(node); std::cerr << ind << "Function_Call_Schema " << expression;+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << "" << std::endl;- debug_ast(expression->name(), ind + "name: ", env);- debug_ast(expression->arguments(), ind + " args: ", env);- } else if (dynamic_cast<Function_Call*>(node)) {- Function_Call* expression = dynamic_cast<Function_Call*>(node);+ debug_ast(&expression->name(), ind + "name: ", env);+ debug_ast(&expression->arguments(), ind + " args: ", env);+ } else if (dynamic_cast<Function_Call_Ptr>(node)) {+ Function_Call_Ptr expression = dynamic_cast<Function_Call_Ptr>(node); std::cerr << ind << "Function_Call " << expression;+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " [" << expression->name() << "]" << std::endl;- debug_ast(expression->arguments(), ind + " args: ", env);- } else if (dynamic_cast<Arguments*>(node)) {- Arguments* expression = dynamic_cast<Arguments*>(node);+ std::cerr << " [" << expression->name() << "]";+ if (expression->is_delayed()) std::cerr << " [delayed]";+ if (expression->is_interpolant()) std::cerr << " [interpolant]";+ std::cerr << std::endl;+ debug_ast(&expression->arguments(), ind + " args: ", env);+ } else if (dynamic_cast<Arguments_Ptr>(node)) {+ Arguments_Ptr expression = dynamic_cast<Arguments_Ptr>(node); std::cerr << ind << "Arguments " << expression; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << " (" << pstate_source_position(node) << ")";@@ -445,100 +533,120 @@ if (expression->has_rest_argument()) std::cerr << " [has_rest_argument]"; if (expression->has_keyword_argument()) std::cerr << " [has_keyword_argument]"; std::cerr << std::endl;- for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Argument*>(node)) {- Argument* expression = dynamic_cast<Argument*>(node);+ for(const Argument_Obj& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Argument_Ptr>(node)) {+ Argument_Ptr expression = dynamic_cast<Argument_Ptr>(node); std::cerr << ind << "Argument " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->value() << "]"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [rest: " << expression->is_rest_argument() << "] "; std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl;- debug_ast(expression->value(), ind + " value: ", env);- } else if (dynamic_cast<Parameters*>(node)) {- Parameters* expression = dynamic_cast<Parameters*>(node);+ debug_ast(&expression->value(), ind + " value: ", env);+ } else if (dynamic_cast<Parameters_Ptr>(node)) {+ Parameters_Ptr expression = dynamic_cast<Parameters_Ptr>(node); std::cerr << ind << "Parameters " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] "; std::cerr << " [has_rest: " << expression->has_rest_parameter() << "] "; std::cerr << std::endl;- for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Parameter*>(node)) {- Parameter* expression = dynamic_cast<Parameter*>(node);+ for(const Parameter_Obj& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Parameter_Ptr>(node)) {+ Parameter_Ptr expression = dynamic_cast<Parameter_Ptr>(node); std::cerr << ind << "Parameter " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [default: " << expression->default_value() << "] "; std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl;- } else if (dynamic_cast<Unary_Expression*>(node)) {- Unary_Expression* expression = dynamic_cast<Unary_Expression*>(node);+ } else if (dynamic_cast<Unary_Expression_Ptr>(node)) {+ Unary_Expression_Ptr expression = dynamic_cast<Unary_Expression_Ptr>(node); std::cerr << ind << "Unary_Expression " << expression;+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";+ std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type() << "]" << std::endl;- debug_ast(expression->operand(), ind + " operand: ", env);- } else if (dynamic_cast<Binary_Expression*>(node)) {- Binary_Expression* expression = dynamic_cast<Binary_Expression*>(node);+ debug_ast(&expression->operand(), ind + " operand: ", env);+ } else if (dynamic_cast<Binary_Expression_Ptr>(node)) {+ Binary_Expression_Ptr expression = dynamic_cast<Binary_Expression_Ptr>(node); std::cerr << ind << "Binary_Expression " << expression;+ if (expression->is_interpolant()) std::cerr << " [is interpolant] ";+ if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";+ if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " [delayed: " << expression->is_delayed() << "] ";+ std::cerr << " [ws_before: " << expression->op().ws_before << "] ";+ std::cerr << " [ws_after: " << expression->op().ws_after << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type_name() << "]" << std::endl;- debug_ast(expression->left(), ind + " left: ", env);- debug_ast(expression->right(), ind + " right: ", env);- } else if (dynamic_cast<Map*>(node)) {- Map* expression = dynamic_cast<Map*>(node);+ debug_ast(&expression->left(), ind + " left: ", env);+ debug_ast(&expression->right(), ind + " right: ", env);+ } else if (dynamic_cast<Map_Ptr>(node)) {+ Map_Ptr expression = dynamic_cast<Map_Ptr>(node); std::cerr << ind << "Map " << expression;+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [Hashed]" << std::endl;- for (auto i : expression->elements()) {- debug_ast(i.first, ind + " key: ");- debug_ast(i.second, ind + " val: ");+ for (const auto& i : expression->elements()) {+ debug_ast(&i.first, ind + " key: ");+ debug_ast(&i.second, ind + " val: "); }- } else if (dynamic_cast<List*>(node)) {- List* expression = dynamic_cast<List*>(node);+ } else if (dynamic_cast<List_Ptr>(node)) {+ List_Ptr expression = dynamic_cast<List_Ptr>(node); std::cerr << ind << "List " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " <<- (expression->separator() == SASS_COMMA ? "Comma " : "Space ") <<+ (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map" : "Space ") << " [delayed: " << expression->is_delayed() << "] " << " [interpolant: " << expression->is_interpolant() << "] " <<+ " [listized: " << expression->from_selector() << "] " << " [arglist: " << expression->is_arglist() << "] " << " [hash: " << expression->hash() << "] " << std::endl;- for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Content*>(node)) {- Content* expression = dynamic_cast<Content*>(node);+ for(const auto& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Content_Ptr>(node)) {+ Content_Ptr expression = dynamic_cast<Content_Ptr>(node); std::cerr << ind << "Content " << expression; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [@media:" << expression->media_block() << "]"; std::cerr << " [Statement]" << std::endl;- } else if (dynamic_cast<Boolean*>(node)) {- Boolean* expression = dynamic_cast<Boolean*>(node);+ } else if (dynamic_cast<Boolean_Ptr>(node)) {+ Boolean_Ptr expression = dynamic_cast<Boolean_Ptr>(node); std::cerr << ind << "Boolean " << expression; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << "]" << std::endl;- } else if (dynamic_cast<Color*>(node)) {- Color* expression = dynamic_cast<Color*>(node);+ } else if (dynamic_cast<Color_Ptr>(node)) {+ Color_Ptr expression = dynamic_cast<Color_Ptr>(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [delayed: " << expression->is_delayed() << "] ";+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;- } else if (dynamic_cast<Number*>(node)) {- Number* expression = dynamic_cast<Number*>(node);+ } else if (dynamic_cast<Number_Ptr>(node)) {+ Number_Ptr expression = dynamic_cast<Number_Ptr>(node); std::cerr << ind << "Number " << expression; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << expression->unit() << "]" << " [hash: " << expression->hash() << "] " << std::endl;- } else if (dynamic_cast<String_Quoted*>(node)) {- String_Quoted* expression = dynamic_cast<String_Quoted*>(node);+ } else if (dynamic_cast<Null_Ptr>(node)) {+ Null_Ptr expression = dynamic_cast<Null_Ptr>(node);+ std::cerr << ind << "Null " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] "+ // " [hash: " << expression->hash() << "] "+ << std::endl;+ } else if (dynamic_cast<String_Quoted_Ptr>(node)) {+ String_Quoted_Ptr expression = dynamic_cast<String_Quoted_Ptr>(node); std::cerr << ind << "String_Quoted " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]";- if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;- } else if (dynamic_cast<String_Constant*>(node)) {- String_Constant* expression = dynamic_cast<String_Constant*>(node);+ } else if (dynamic_cast<String_Constant_Ptr>(node)) {+ String_Constant_Ptr expression = dynamic_cast<String_Constant_Ptr>(node); std::cerr << ind << "String_Constant " << expression; if (expression->concrete_type()) { std::cerr << " " << expression->concrete_type();@@ -546,28 +654,30 @@ std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]";- if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;- } else if (dynamic_cast<String_Schema*>(node)) {- String_Schema* expression = dynamic_cast<String_Schema*>(node);+ } else if (dynamic_cast<String_Schema_Ptr>(node)) {+ String_Schema_Ptr expression = dynamic_cast<String_Schema_Ptr>(node); std::cerr << ind << "String_Schema " << expression;+ std::cerr << " (" << pstate_source_position(expression) << ")"; std::cerr << " " << expression->concrete_type();+ std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_delayed()) std::cerr << " [delayed]";- if (expression->has_interpolants()) std::cerr << " [has_interpolants]";- if (expression->is_interpolant()) std::cerr << " [interpolant]";+ if (expression->is_interpolant()) std::cerr << " [is interpolant]";+ if (expression->has_interpolant()) std::cerr << " [has interpolant]";+ if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";+ if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;- for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<String*>(node)) {- String* expression = dynamic_cast<String*>(node);+ for(const auto& i : expression->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<String_Ptr>(node)) {+ String_Ptr expression = dynamic_cast<String_Ptr>(node); std::cerr << ind << "String " << expression; std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")";- if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;- } else if (dynamic_cast<Expression*>(node)) {- Expression* expression = dynamic_cast<Expression*>(node);+ } else if (dynamic_cast<Expression_Ptr>(node)) {+ Expression_Ptr expression = dynamic_cast<Expression_Ptr>(node); std::cerr << ind << "Expression " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; switch (expression->concrete_type()) {@@ -582,17 +692,18 @@ case Expression::Concrete_Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break; case Expression::Concrete_Type::C_WARNING: std::cerr << " [C_WARNING]"; break; case Expression::Concrete_Type::C_ERROR: std::cerr << " [C_ERROR]"; break;+ case Expression::Concrete_Type::FUNCTION: std::cerr << " [FUNCTION]"; break; case Expression::Concrete_Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break; } std::cerr << std::endl;- } else if (dynamic_cast<Has_Block*>(node)) {- Has_Block* has_block = dynamic_cast<Has_Block*>(node);+ } else if (dynamic_cast<Has_Block_Ptr>(node)) {+ Has_Block_Ptr has_block = dynamic_cast<Has_Block_Ptr>(node); std::cerr << ind << "Has_Block " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl;- if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Statement*>(node)) {- Statement* statement = dynamic_cast<Statement*>(node);+ if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(&i, ind + " ", env); }+ } else if (dynamic_cast<Statement_Ptr>(node)) {+ Statement_Ptr statement = dynamic_cast<Statement_Ptr>(node); std::cerr << ind << "Statement " << statement; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << statement->tabs() << std::endl;@@ -624,7 +735,7 @@ std::cerr << node << " "; if (node->got_line_feed) std::cerr << "[LF] "; std::cerr << std::endl;- debug_ast(node->selector(), ind + " ");+ debug_ast(&node->selector(), ind + " "); } else if (node->isCollection()) { std::cerr << ind; std::cerr << "Collection ";@@ -650,35 +761,36 @@ if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; } -inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0)+/*+inline void debug_ast(const AST_Node_Ptr node, std::string ind = "", Env* env = 0) {- debug_ast(const_cast<AST_Node*>(node), ind, env);+ debug_ast(const_cast<AST_Node_Ptr>(node), ind, env); }-+*/ inline void debug_node(const Node* node, std::string ind = "") { debug_node(const_cast<Node*>(node), ind); } -inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, std::string ind = "")+inline void debug_subset_map(Sass::Subset_Map& map, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";- for(auto const &it : map->values()) {- debug_ast(it.first, ind + "first: ");- debug_ast(it.second, ind + "second: ");+ for(auto const &it : map.values()) {+ debug_ast(&it.first, ind + "first: ");+ debug_ast(&it.second, ind + "second: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; } -typedef std::pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> ExtensionPair; typedef std::vector<ExtensionPair> SubsetMapEntries; inline void debug_subset_entries(SubsetMapEntries* entries, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &pair : *entries) {- debug_ast(pair.first, ind + "first: ");- debug_ast(pair.second, ind + "second: ");+ debug_ast(&pair.first, ind + "first: ");+ debug_ast(&pair.second, ind + "second: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; }
libsass/src/emitter.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "util.hpp" #include "context.hpp" #include "output.hpp"@@ -6,20 +7,20 @@ namespace Sass { - Emitter::Emitter(Context* ctx)+ Emitter::Emitter(struct Sass_Output_Options& opt) : wbuf(),- ctx(ctx),+ opt(opt), indentation(0), scheduled_space(0), scheduled_linefeed(0), scheduled_delimiter(false),+ scheduled_mapping(0), in_comment(false), in_wrapped(false), in_media_block(false), in_declaration(false), in_space_array(false),- in_comma_array(false),- in_debug(false)+ in_comma_array(false) { } // return buffer as string@@ -28,9 +29,9 @@ return wbuf.buffer; } - Sass_Output_Style Emitter::output_style(void)+ Sass_Output_Style Emitter::output_style(void) const {- return ctx ? ctx->output_style() : SASS_STYLE_COMPRESSED;+ return opt.output_style; } // PROXY METHODS FOR SOURCE MAPS@@ -44,9 +45,11 @@ void Emitter::set_filename(const std::string& str) { wbuf.smap.file = str; } - void Emitter::add_open_mapping(AST_Node* node)+ void Emitter::schedule_mapping(const AST_Node_Ptr node)+ { scheduled_mapping = node; }+ void Emitter::add_open_mapping(const AST_Node_Ptr node) { wbuf.smap.add_open_mapping(node); }- void Emitter::add_close_mapping(AST_Node* node)+ void Emitter::add_close_mapping(const AST_Node_Ptr node) { wbuf.smap.add_close_mapping(node); } ParserState Emitter::remap(const ParserState& pstate) { return wbuf.smap.remap(pstate); }@@ -54,9 +57,11 @@ // MAIN BUFFER MANIPULATION // add outstanding delimiter- void Emitter::finalize(void)+ void Emitter::finalize(bool final) { scheduled_space = 0;+ if (output_style() == SASS_STYLE_COMPRESSED)+ if (final) scheduled_delimiter = false; if (scheduled_linefeed) scheduled_linefeed = 1; flush_schedules();@@ -70,7 +75,7 @@ std::string linefeeds = ""; for (size_t i = 0; i < scheduled_linefeed; i++)- linefeeds += ctx ? ctx->linefeed : "\n";+ linefeeds += opt.linefeed; scheduled_space = 0; scheduled_linefeed = 0; append_string(linefeeds);@@ -103,10 +108,11 @@ // append some text or token to the buffer void Emitter::append_string(const std::string& text) {+ // write space/lf flush_schedules(); - if (in_comment && output_style() == SASS_STYLE_COMPACT) {+ if (in_comment && output_style() == COMPACT) { // unescape comment nodes std::string out = comment_to_string(text); // add to buffer@@ -133,10 +139,16 @@ // append some text or token to the buffer // this adds source-mappings for node start and end- void Emitter::append_token(const std::string& text, AST_Node* node)+ void Emitter::append_token(const std::string& text, const AST_Node_Ptr node) { flush_schedules(); add_open_mapping(node);+ // hotfix for browser issues+ // this is pretty ugly indeed+ if (scheduled_mapping) {+ add_open_mapping(scheduled_mapping);+ scheduled_mapping = 0;+ } append_string(text); add_close_mapping(node); }@@ -145,27 +157,27 @@ void Emitter::append_indentation() {- if (output_style() == SASS_STYLE_COMPRESSED) return;- if (output_style() == SASS_STYLE_COMPACT) return;+ if (output_style() == COMPRESSED) return;+ if (output_style() == COMPACT) return; if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; std::string indent = ""; for (size_t i = 0; i < indentation; i++)- indent += ctx ? ctx->indent : " ";+ indent += opt.indent; append_string(indent); } void Emitter::append_delimiter() { scheduled_delimiter = true;- if (output_style() == SASS_STYLE_COMPACT) {+ if (output_style() == COMPACT) { if (indentation == 0) { append_mandatory_linefeed(); } else { append_mandatory_space(); }- } else if (output_style() != SASS_STYLE_COMPRESSED) {+ } else if (output_style() != COMPRESSED) { append_optional_linefeed(); } }@@ -191,8 +203,8 @@ void Emitter::append_optional_space() {- if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && buffer().size()) {- char lst = buffer().at(buffer().length() - 1);+ if ((output_style() != COMPRESSED) && buffer().size()) {+ unsigned char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) { append_mandatory_space(); }@@ -201,17 +213,17 @@ void Emitter::append_special_linefeed() {- if (output_style() == SASS_STYLE_COMPACT) {+ if (output_style() == COMPACT) { append_mandatory_linefeed(); for (size_t p = 0; p < indentation; p++)- append_string(ctx ? ctx->indent : " ");+ append_string(opt.indent); } } void Emitter::append_optional_linefeed() { if (in_declaration && in_comma_array) return;- if (output_style() == SASS_STYLE_COMPACT) {+ if (output_style() == COMPACT) { append_mandatory_space(); } else { append_mandatory_linefeed();@@ -220,14 +232,14 @@ void Emitter::append_mandatory_linefeed() {- if (output_style() != SASS_STYLE_COMPRESSED) {+ if (output_style() != COMPRESSED) { scheduled_linefeed = 1; scheduled_space = 0; // flush_schedules(); } } - void Emitter::append_scope_opener(AST_Node* node)+ void Emitter::append_scope_opener(AST_Node_Ptr node) { scheduled_linefeed = 0; append_optional_space();@@ -238,13 +250,13 @@ // append_optional_space(); ++ indentation; }- void Emitter::append_scope_closer(AST_Node* node)+ void Emitter::append_scope_closer(AST_Node_Ptr node) { -- indentation; scheduled_linefeed = 0;- if (output_style() == SASS_STYLE_COMPRESSED)+ if (output_style() == COMPRESSED) scheduled_delimiter = false;- if (output_style() == SASS_STYLE_EXPANDED) {+ if (output_style() == EXPANDED) { append_optional_linefeed(); append_indentation(); } else {@@ -254,7 +266,7 @@ if (node) add_close_mapping(node); append_optional_linefeed(); if (indentation != 0) return;- if (output_style() != SASS_STYLE_COMPRESSED)+ if (output_style() != COMPRESSED) scheduled_linefeed = 2; }
libsass/src/emitter.hpp view
@@ -2,6 +2,7 @@ #define SASS_EMITTER_H #include <string>+#include "sass.hpp" #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp"@@ -12,7 +13,7 @@ class Emitter { public:- Emitter(Context* ctx);+ Emitter(struct Sass_Output_Options& opt); virtual ~Emitter() { } protected:@@ -24,17 +25,19 @@ // proxy methods for source maps void add_source_index(size_t idx); void set_filename(const std::string& str);- void add_open_mapping(AST_Node* node);- void add_close_mapping(AST_Node* node);+ void add_open_mapping(const AST_Node_Ptr node);+ void add_close_mapping(const AST_Node_Ptr node);+ void schedule_mapping(const AST_Node_Ptr node); std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate); public:- Context* ctx;+ struct Sass_Output_Options& opt; size_t indentation; size_t scheduled_space; size_t scheduled_linefeed; bool scheduled_delimiter;+ AST_Node_Ptr scheduled_mapping; public: // output strings different in comments@@ -48,16 +51,14 @@ // nested lists need parentheses bool in_space_array; bool in_comma_array;- // list separators don't get compressed in @debug- bool in_debug; public: // return buffer as std::string std::string get_buffer(void); // flush scheduled space/linefeed- Sass_Output_Style output_style(void);+ Sass_Output_Style output_style(void) const; // add outstanding linefeed- void finalize(void);+ void finalize(bool final = true); // flush scheduled space/linefeed void flush_schedules(void); // prepend some text or token to the buffer@@ -69,7 +70,7 @@ void append_wspace(const std::string& text); // append some text or token to the buffer // this adds source-mappings for node start and end- void append_token(const std::string& text, AST_Node* node);+ void append_token(const std::string& text, const AST_Node_Ptr node); public: // syntax sugar void append_indentation();@@ -78,8 +79,8 @@ void append_special_linefeed(void); void append_optional_linefeed(void); void append_mandatory_linefeed(void);- void append_scope_opener(AST_Node* node = 0);- void append_scope_closer(AST_Node* node = 0);+ void append_scope_opener(AST_Node_Ptr node = 0);+ void append_scope_closer(AST_Node_Ptr node = 0); void append_comma_separator(void); void append_colon_separator(void); void append_delimiter(void);
libsass/src/environment.cpp view
@@ -1,14 +1,24 @@+#include "sass.hpp" #include "ast.hpp" #include "environment.hpp" namespace Sass { template <typename T>- Environment<T>::Environment() : local_frame_(std::map<std::string, T>()), parent_(0) { }+ Environment<T>::Environment(bool is_shadow)+ : local_frame_(std::map<std::string, T>()),+ parent_(0), is_shadow_(false)+ { } template <typename T>- Environment<T>::Environment(Environment<T>* env) : local_frame_(std::map<std::string, T>()), parent_(env) { }+ Environment<T>::Environment(Environment<T>* env, bool is_shadow)+ : local_frame_(std::map<std::string, T>()),+ parent_(env), is_shadow_(is_shadow)+ { } template <typename T>- Environment<T>::Environment(Environment<T>& env) : local_frame_(std::map<std::string, T>()), parent_(&env) { }+ Environment<T>::Environment(Environment<T>& env, bool is_shadow)+ : local_frame_(std::map<std::string, T>()),+ parent_(&env), is_shadow_(is_shadow)+ { } // link parent to create a stack template <typename T>@@ -118,12 +128,13 @@ template <typename T> void Environment<T>::set_lexical(const std::string& key, T val) {- auto cur = this;- while (cur->is_lexical()) {+ auto cur = this; bool shadow = false;+ while (cur->is_lexical() || shadow) { if (cur->has_local(key)) { cur->set_local(key, val); return; }+ shadow = cur->is_shadow(); cur = cur->parent_; } set_local(key, val);@@ -168,8 +179,8 @@ for (typename std::map<std::string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) { std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second;- if (Value* val = dynamic_cast<Value*>(i->second))- { std::cerr << " : " << val->to_string(true, 5); }+ if (Value_Ptr val = SASS_MEMORY_CAST_PTR(Value, i->second))+ { std::cerr << " : " << val->to_string(); } std::cerr << std::endl; } }@@ -178,7 +189,7 @@ #endif // compile implementation for AST_Node- template class Environment<AST_Node*>;+ template class Environment<AST_Node_Obj>; }
libsass/src/environment.hpp view
@@ -6,7 +6,6 @@ #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp"-#include "memory_manager.hpp" namespace Sass { @@ -15,12 +14,12 @@ // TODO: test with map std::map<std::string, T> local_frame_; ADD_PROPERTY(Environment*, parent)+ ADD_PROPERTY(bool, is_shadow) public:- Memory_Manager mem;- Environment();- Environment(Environment* env);- Environment(Environment& env);+ Environment(bool is_shadow = false);+ Environment(Environment* env, bool is_shadow = false);+ Environment(Environment& env, bool is_shadow = false); // link parent to create a stack void link(Environment& env);@@ -87,6 +86,7 @@ #endif };+ } #endif
libsass/src/error_handling.cpp view
@@ -1,7 +1,7 @@+#include "sass.hpp" #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp"-#include "to_string.hpp" #include "error_handling.hpp" #include <iostream>@@ -10,43 +10,133 @@ namespace Exception { - Base::Base(ParserState pstate, std::string msg)- : std::runtime_error(msg),- msg(msg), pstate(pstate)+ Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)+ : std::runtime_error(msg), msg(msg),+ prefix("Error"), pstate(pstate),+ import_stack(import_stack) { } - const char* Base::what() const throw()- {- return msg.c_str();- }- InvalidSass::InvalidSass(ParserState pstate, std::string msg) : Base(pstate, msg) { } - InvalidParent::InvalidParent(Selector* parent, Selector* selector)+ InvalidParent::InvalidParent(Selector_Ptr parent, Selector_Ptr selector) : Base(selector->pstate()), parent(parent), selector(selector) { msg = "Invalid parent selector for \"";- msg += selector->to_string(false);+ msg += selector->to_string(Sass_Inspect_Options()); msg += "\": \"";- msg += parent->to_string(false);;+ msg += parent->to_string(Sass_Inspect_Options()); msg += "\""; } - InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value)+ InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value) : Base(pstate), fn(fn), arg(arg), type(type), value(value) { msg = arg + ": \"";- msg += value->to_string(true, 5);+ if (value) msg += value->to_string(Sass_Inspect_Options()); msg += "\" is not a " + type; msg += " for `" + fn + "'"; } - InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg)- : Base(pstate, msg)+ MissingArgument::MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype)+ : Base(pstate), fn(fn), arg(arg), fntype(fntype)+ {+ msg = fntype + " " + fn;+ msg += " is missing argument ";+ msg += arg + ".";+ }++ InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)+ : Base(pstate, msg, import_stack) { }++ UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)+ : lhs(lhs), rhs(rhs), op(op)+ {+ msg = def_op_msg + ": \"";+ msg += lhs->to_string({ NESTED, 5 });+ msg += " " + op + " ";+ msg += rhs->to_string({ TO_SASS, 5 });+ msg += "\".";+ }++ InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)+ : UndefinedOperation(lhs, rhs, op)+ {+ msg = def_op_null_msg + ": \"";+ msg += lhs->inspect();+ msg += " " + op + " ";+ msg += rhs->inspect();+ msg += "\".";+ }++ ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)+ : lhs(lhs), rhs(rhs)+ {+ msg = "divided by 0";+ }++ DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org)+ : Base(org.pstate()), dup(dup), org(org)+ {+ msg = "Duplicate key ";+ msg += dup.get_duplicate_key()->inspect();+ msg += " in map (";+ msg += org.inspect();+ msg += ").";+ }++ TypeMismatch::TypeMismatch(const Expression& var, const std::string type)+ : Base(var.pstate()), var(var), type(type)+ {+ msg = var.to_string();+ msg += " is not an ";+ msg += type;+ msg += ".";+ }++ InvalidValue::InvalidValue(const Expression& val)+ : Base(val.pstate()), val(val)+ {+ msg = val.to_string();+ msg += " isn't a valid CSS value.";+ }++ StackError::StackError(const AST_Node& node)+ : Base(node.pstate()), node(node)+ {+ msg = "stack level too deep";+ }++ IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs)+ : lhs(lhs), rhs(rhs)+ {+ msg = "Incompatible units: '";+ msg += rhs.unit();+ msg += "' and '";+ msg += lhs.unit();+ msg += "'.";+ }++ AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)+ : lhs(lhs), rhs(rhs), op(op)+ {+ msg = "Alpha channels must be equal: ";+ msg += lhs->to_string({ NESTED, 5 });+ msg += " " + op + " ";+ msg += rhs->to_string({ NESTED, 5 });+ msg += ".";+ }+++ SassValueError::SassValueError(ParserState pstate, OperationError& err)+ : Base(pstate, err.what())+ {+ msg = err.what();+ prefix = err.errtype();+ } }
libsass/src/error_handling.hpp view
@@ -12,16 +12,21 @@ namespace Exception { - const std::string def_msg = "Invalid sass";+ const std::string def_msg = "Invalid sass detected";+ const std::string def_op_msg = "Undefined operation";+ const std::string def_op_null_msg = "Invalid null operation"; class Base : public std::runtime_error { protected: std::string msg;+ std::string prefix; public: ParserState pstate;+ std::vector<Sass_Import_Entry>* import_stack; public:- Base(ParserState pstate, std::string msg = def_msg);- virtual const char* what() const throw();+ Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0);+ virtual const char* errtype() const { return prefix.c_str(); }+ virtual const char* what() const throw() { return msg.c_str(); } virtual ~Base() throw() {}; }; @@ -33,28 +38,143 @@ class InvalidParent : public Base { protected:- Selector* parent;- Selector* selector;+ Selector_Ptr parent;+ Selector_Ptr selector; public:- InvalidParent(Selector* parent, Selector* selector);+ InvalidParent(Selector_Ptr parent, Selector_Ptr selector); virtual ~InvalidParent() throw() {}; }; + class MissingArgument : public Base {+ protected:+ std::string fn;+ std::string arg;+ std::string fntype;+ public:+ MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype);+ virtual ~MissingArgument() throw() {};+ };+ class InvalidArgumentType : public Base { protected: std::string fn; std::string arg; std::string type;- const Value* value;+ const Value_Ptr value; public:- InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value = 0);+ InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0); virtual ~InvalidArgumentType() throw() {}; }; class InvalidSyntax : public Base { public:- InvalidSyntax(ParserState pstate, std::string msg);+ InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0); virtual ~InvalidSyntax() throw() {};+ };++ /* common virtual base class (has no pstate) */+ class OperationError : public std::runtime_error {+ protected:+ std::string msg;+ public:+ OperationError(std::string msg = def_op_msg)+ : std::runtime_error(msg), msg(msg)+ {};+ public:+ virtual const char* errtype() const { return "Error"; }+ virtual const char* what() const throw() { return msg.c_str(); }+ virtual ~OperationError() throw() {};+ };++ class ZeroDivisionError : public OperationError {+ protected:+ const Expression& lhs;+ const Expression& rhs;+ public:+ ZeroDivisionError(const Expression& lhs, const Expression& rhs);+ virtual const char* errtype() const { return "ZeroDivisionError"; }+ virtual ~ZeroDivisionError() throw() {};+ };++ class DuplicateKeyError : public Base {+ protected:+ const Map& dup;+ const Expression& org;+ public:+ DuplicateKeyError(const Map& dup, const Expression& org);+ virtual const char* errtype() const { return "Error"; }+ virtual ~DuplicateKeyError() throw() {};+ };++ class TypeMismatch : public Base {+ protected:+ const Expression& var;+ const std::string type;+ public:+ TypeMismatch(const Expression& var, const std::string type);+ virtual const char* errtype() const { return "Error"; }+ virtual ~TypeMismatch() throw() {};+ };++ class InvalidValue : public Base {+ protected:+ const Expression& val;+ public:+ InvalidValue(const Expression& val);+ virtual const char* errtype() const { return "Error"; }+ virtual ~InvalidValue() throw() {};+ };++ class StackError : public Base {+ protected:+ const AST_Node& node;+ public:+ StackError(const AST_Node& node);+ virtual const char* errtype() const { return "SystemStackError"; }+ virtual ~StackError() throw() {};+ };++ class IncompatibleUnits : public OperationError {+ protected:+ const Number& lhs;+ const Number& rhs;+ public:+ IncompatibleUnits(const Number& lhs, const Number& rhs);+ virtual ~IncompatibleUnits() throw() {};+ };++ class UndefinedOperation : public OperationError {+ protected:+ Expression_Ptr_Const lhs;+ Expression_Ptr_Const rhs;+ const std::string op;+ public:+ UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+ // virtual const char* errtype() const { return "Error"; }+ virtual ~UndefinedOperation() throw() {};+ };++ class InvalidNullOperation : public UndefinedOperation {+ public:+ InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+ virtual ~InvalidNullOperation() throw() {};+ };++ class AlphaChannelsNotEqual : public OperationError {+ protected:+ Expression_Ptr_Const lhs;+ Expression_Ptr_Const rhs;+ const std::string op;+ public:+ AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op);+ // virtual const char* errtype() const { return "Error"; }+ virtual ~AlphaChannelsNotEqual() throw() {};+ };++ class SassValueError : public Base {+ public:+ SassValueError(ParserState pstate, OperationError& err);+ virtual ~SassValueError() throw() {}; }; }
libsass/src/eval.cpp view
@@ -1,1530 +1,1712 @@-#include <cstdlib>-#include <cmath>-#include <iostream>-#include <sstream>-#include <iomanip>-#include <typeinfo>--#include "file.hpp"-#include "eval.hpp"-#include "ast.hpp"-#include "bind.hpp"-#include "util.hpp"-#include "to_string.hpp"-#include "inspect.hpp"-#include "environment.hpp"-#include "position.hpp"-#include "sass/values.h"-#include "to_value.hpp"-#include "to_c.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "lexer.hpp"-#include "prelexer.hpp"-#include "parser.hpp"-#include "expand.hpp"-#include "color_maps.hpp"--namespace Sass {-- inline double add(double x, double y) { return x + y; }- inline double sub(double x, double y) { return x - y; }- inline double mul(double x, double y) { return x * y; }- inline double div(double x, double y) { return x / y; } // x/0 checked by caller- inline double mod(double x, double y) { return std::abs(std::fmod(x, y)); } // x/0 checked by caller- typedef double (*bop)(double, double);- bop ops[Sass_OP::NUM_OPS] = {- 0, 0, // and, or- 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte- add, sub, mul, div, mod- };-- Eval::Eval(Expand& exp)- : exp(exp),- ctx(exp.ctx),- listize(exp.ctx)- { }- Eval::~Eval() { }-- Context& Eval::context()- {- return ctx;- }-- Env* Eval::environment()- {- return exp.environment();- }-- Selector_List* Eval::selector()- {- return exp.selector();- }-- Backtrace* Eval::backtrace()- {- return exp.backtrace();- }-- Expression* Eval::operator()(Block* b)- {- Expression* val = 0;- for (size_t i = 0, L = b->length(); i < L; ++i) {- val = (*b)[i]->perform(this);- if (val) return val;- }- return val;- }-- Expression* Eval::operator()(Assignment* a)- {- Env* env = exp.environment();- std::string var(a->variable());- if (a->is_global()) {- if (a->is_default()) {- if (env->has_global(var)) {- Expression* e = dynamic_cast<Expression*>(env->get_global(var));- if (!e || e->concrete_type() == Expression::NULL_VAL) {- env->set_global(var, a->value()->perform(this));- }- }- else {- env->set_global(var, a->value()->perform(this));- }- }- else {- env->set_global(var, a->value()->perform(this));- }- }- else if (a->is_default()) {- if (env->has_lexical(var)) {- auto cur = env;- while (cur && cur->is_lexical()) {- if (cur->has_local(var)) {- if (AST_Node* node = cur->get_local(var)) {- Expression* e = dynamic_cast<Expression*>(node);- if (!e || e->concrete_type() == Expression::NULL_VAL) {- cur->set_local(var, a->value()->perform(this));- }- }- else {- throw std::runtime_error("Env not in sync");- }- return 0;- }- cur = cur->parent();- }- throw std::runtime_error("Env not in sync");- }- else if (env->has_global(var)) {- if (AST_Node* node = env->get_global(var)) {- Expression* e = dynamic_cast<Expression*>(node);- if (!e || e->concrete_type() == Expression::NULL_VAL) {- env->set_global(var, a->value()->perform(this));- }- }- }- else if (env->is_lexical()) {- env->set_local(var, a->value()->perform(this));- }- else {- env->set_local(var, a->value()->perform(this));- }- }- else {- env->set_lexical(var, a->value()->perform(this));- }- return 0;- }-- Expression* Eval::operator()(If* i)- {- Expression* rv = 0;- Env env(exp.environment());- exp.env_stack.push_back(&env);- if (*i->predicate()->perform(this)) {- rv = i->block()->perform(this);- }- else {- Block* alt = i->alternative();- if (alt) rv = alt->perform(this);- }- exp.env_stack.pop_back();- return rv;- }-- // For does not create a new env scope- // But iteration vars are reset afterwards- Expression* Eval::operator()(For* f)- {- std::string variable(f->variable());- Expression* low = f->lower_bound()->perform(this);- if (low->concrete_type() != Expression::NUMBER) {- error("lower bound of `@for` directive must be numeric", low->pstate());- }- Expression* high = f->upper_bound()->perform(this);- if (high->concrete_type() != Expression::NUMBER) {- error("upper bound of `@for` directive must be numeric", high->pstate());- }- Number* sass_start = static_cast<Number*>(low);- Number* sass_end = static_cast<Number*>(high);- // check if units are valid for sequence- if (sass_start->unit() != sass_end->unit()) {- std::stringstream msg; msg << "Incompatible units: '"- << sass_start->unit() << "' and '"- << sass_end->unit() << "'.";- error(msg.str(), low->pstate(), backtrace());- }- double start = sass_start->value();- double end = sass_end->value();- // only create iterator once in this environment- Env* env = exp.environment();- Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());- AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;- env->set_local(variable, it);- Block* body = f->block();- Expression* val = 0;- if (start < end) {- if (f->is_inclusive()) ++end;- for (double i = start;- i < end;- ++i) {- it->value(i);- env->set_local(variable, it);- val = body->perform(this);- if (val) break;- }- } else {- if (f->is_inclusive()) --end;- for (double i = start;- i > end;- --i) {- it->value(i);- env->set_local(variable, it);- val = body->perform(this);- if (val) break;- }- }- // restore original environment- if (!old_var) env->del_local(variable);- else env->set_local(variable, old_var);- return val;- }-- // Eval does not create a new env scope- // But iteration vars are reset afterwards- Expression* Eval::operator()(Each* e)- {- std::vector<std::string> variables(e->variables());- Expression* expr = e->list()->perform(this);- Env* env = exp.environment();- List* list = 0;- Map* map = 0;- if (expr->concrete_type() == Expression::MAP) {- map = static_cast<Map*>(expr);- }- else if (expr->concrete_type() != Expression::LIST) {- list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);- *list << expr;- }- else {- list = static_cast<List*>(expr);- }- // remember variables and then reset them- std::vector<AST_Node*> old_vars(variables.size());- for (size_t i = 0, L = variables.size(); i < L; ++i) {- old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;- env->set_local(variables[i], 0);- }- Block* body = e->block();- Expression* val = 0;-- if (map) {- for (auto key : map->keys()) {- Expression* value = map->at(key);-- if (variables.size() == 1) {- List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE);- *variable << key;- *variable << value;- env->set_local(variables[0], variable);- } else {- env->set_local(variables[0], key);- env->set_local(variables[1], value);- }-- val = body->perform(this);- if (val) break;- }- }- else {- for (size_t i = 0, L = list->length(); i < L; ++i) {- Expression* e = (*list)[i];- // unwrap value if the expression is an argument- if (Argument* arg = dynamic_cast<Argument*>(e)) e = arg->value();- // check if we got passed a list of args (investigate)- if (List* scalars = dynamic_cast<List*>(e)) {- if (variables.size() == 1) {- Expression* var = scalars;- env->set_local(variables[0], var);- } else {- for (size_t j = 0, K = variables.size(); j < K; ++j) {- Expression* res = j >= scalars->length()- ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())- : (*scalars)[j];- env->set_local(variables[j], res);- }- }- } else {- if (variables.size() > 0) {- env->set_local(variables[0], e);- for (size_t j = 1, K = variables.size(); j < K; ++j) {- Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());- env->set_local(variables[j], res);- }- }- }- val = body->perform(this);- if (val) break;- }- }- // restore original environment- for (size_t j = 0, K = variables.size(); j < K; ++j) {- if(!old_vars[j]) env->del_local(variables[j]);- else env->set_local(variables[j], old_vars[j]);- }- return val;- }-- Expression* Eval::operator()(While* w)- {- Expression* pred = w->predicate();- Block* body = w->block();- while (*pred->perform(this)) {- Expression* val = body->perform(this);- if (val) return val;- }- return 0;- }-- Expression* Eval::operator()(Return* r)- {- return r->value()->perform(this);- }-- Expression* Eval::operator()(Warning* w)- {- Expression* message = w->message()->perform(this);- To_String to_string(&ctx);- Env* env = exp.environment();-- // try to use generic function- if (env->has("@warn[f]")) {-- Definition* def = static_cast<Definition*>((*env)["@warn[f]"]);- // Block* body = def->block();- // Native_Function func = def->native_function();- Sass_Function_Entry c_function = def->c_function();- Sass_Function_Fn c_func = sass_function_get_function(c_function);-- To_C to_c;- union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);- sass_list_set_value(c_args, 0, message->perform(&to_c));- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- sass_delete_value(c_args);- sass_delete_value(c_val);- return 0;-- }-- std::string result(unquote(message->perform(&to_string)));- Backtrace top(backtrace(), w->pstate(), "");- std::cerr << "WARNING: " << result;- std::cerr << top.to_string(true);- std::cerr << std::endl << std::endl;- return 0;- }-- Expression* Eval::operator()(Error* e)- {- Expression* message = e->message()->perform(this);- To_String to_string(&ctx);- Env* env = exp.environment();-- // try to use generic function- if (env->has("@error[f]")) {-- Definition* def = static_cast<Definition*>((*env)["@error[f]"]);- // Block* body = def->block();- // Native_Function func = def->native_function();- Sass_Function_Entry c_function = def->c_function();- Sass_Function_Fn c_func = sass_function_get_function(c_function);-- To_C to_c;- union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);- sass_list_set_value(c_args, 0, message->perform(&to_c));- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- sass_delete_value(c_args);- sass_delete_value(c_val);- return 0;-- }-- std::string result(unquote(message->perform(&to_string)));- error(result, e->pstate());- return 0;- }-- Expression* Eval::operator()(Debug* d)- {- Expression* message = d->value()->perform(this);- To_String to_string(&ctx, false, true);- Env* env = exp.environment();-- // try to use generic function- if (env->has("@debug[f]")) {-- Definition* def = static_cast<Definition*>((*env)["@debug[f]"]);- // Block* body = def->block();- // Native_Function func = def->native_function();- Sass_Function_Entry c_function = def->c_function();- Sass_Function_Fn c_func = sass_function_get_function(c_function);-- To_C to_c;- union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);- sass_list_set_value(c_args, 0, message->perform(&to_c));- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- sass_delete_value(c_args);- sass_delete_value(c_val);- return 0;-- }-- std::string cwd(ctx.cwd());- std::string result(unquote(message->perform(&to_string)));- std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));- std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));- std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));-- std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;- std::cerr << std::endl;- return 0;- }-- Expression* Eval::operator()(List* l)- {- if (l->is_expanded()) return l;- List* ll = SASS_MEMORY_NEW(ctx.mem, List,- l->pstate(),- l->length(),- l->separator(),- l->is_arglist());- for (size_t i = 0, L = l->length(); i < L; ++i) {- *ll << (*l)[i]->perform(this);- }- ll->is_expanded(true);- return ll;- }-- Expression* Eval::operator()(Map* m)- {- if (m->is_expanded()) return m;-- // make sure we're not starting with duplicate keys.- // the duplicate key state will have been set in the parser phase.- if (m->has_duplicate_key()) {- To_String to_string(&ctx);- error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());- }-- Map* mm = SASS_MEMORY_NEW(ctx.mem, Map,- m->pstate(),- m->length());- for (auto key : m->keys()) {- Expression* ex_key = key->perform(this);- Expression* ex_val = m->at(key)->perform(this);- *mm << std::make_pair(ex_key, ex_val);- }-- // check the evaluated keys aren't duplicates.- if (mm->has_duplicate_key()) {- To_String to_string(&ctx);- error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", mm->pstate());- }-- mm->is_expanded(true);- return mm;- }-- Expression* Eval::operator()(Binary_Expression* b)- {- enum Sass_OP op_type = b->type();- // don't eval delayed expressions (the '/' when used as a separator)- if (op_type == Sass_OP::DIV && b->is_delayed()) return b;- b->is_delayed(false);- // if one of the operands is a '/' then make sure it's evaluated- Expression* lhs = b->left()->perform(this);- lhs->is_delayed(false);- while (typeid(*lhs) == typeid(Binary_Expression)) {- Binary_Expression* lhs_ex = static_cast<Binary_Expression*>(lhs);- if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break;- lhs = Eval::operator()(lhs_ex);- }-- switch (op_type) {- case Sass_OP::AND:- return *lhs ? b->right()->perform(this) : lhs;- break;-- case Sass_OP::OR:- return *lhs ? lhs : b->right()->perform(this);- break;-- default:- break;- }- // not a logical connective, so go ahead and eval the rhs- Expression* rhs = b->right()->perform(this);- // maybe fully evaluate structure- if (op_type == Sass_OP::EQ ||- op_type == Sass_OP::NEQ ||- op_type == Sass_OP::GT ||- op_type == Sass_OP::GTE ||- op_type == Sass_OP::LT ||- op_type == Sass_OP::LTE)- {- rhs->is_expanded(false);- rhs->set_delayed(false);- rhs = rhs->perform(this);- }- else- {- // rhs->set_delayed(false);- // rhs = rhs->perform(this);- }-- // upgrade string to number if possible (issue #948)- if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL) {- if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {- std::string value(str->value());- const char* start = value.c_str();- if (Prelexer::sequence < Prelexer::number >(start) != 0) {- rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value());- rhs->is_delayed(false); rhs = rhs->perform(this);- }- }- }-- // see if it's a relational expression- switch(op_type) {- case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs));- case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs));- case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs) && !eq(lhs, rhs));- case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs));- case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs));- case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs) || eq(lhs, rhs));-- default: break;- }--- Expression::Concrete_Type l_type = lhs->concrete_type();- Expression::Concrete_Type r_type = rhs->concrete_type();-- // Is one of the operands an interpolant?- String_Schema* s1 = dynamic_cast<String_Schema*>(b->left());- String_Schema* s2 = dynamic_cast<String_Schema*>(b->right());-- if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants())) {- std::string sep;- switch (op_type) {- case Sass_OP::SUB: sep = "-"; break;- case Sass_OP::DIV: sep = "/"; break;- case Sass_OP::ADD: sep = "+"; break;- case Sass_OP::MUL: sep = "*"; break;- default: break;- }-- // If possible upgrade LHS to a number- if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB) {- if (String_Constant* str = dynamic_cast<String_Constant*>(lhs)) {- std::string value(str->value());- const char* start = value.c_str();- if (Prelexer::sequence < Prelexer::number >(start) != 0) {- lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value());- lhs->is_delayed(false); lhs = lhs->perform(this);- }- }- if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {- std::string value(str->value());- const char* start = value.c_str();- if (Prelexer::sequence < Prelexer::number >(start) != 0) {- rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value());- rhs->is_delayed(false); rhs = rhs->perform(this);- }- }- }-- To_Value to_value(ctx, ctx.mem);- Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));- Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));- Expression::Concrete_Type l_type = lhs->concrete_type();- Expression::Concrete_Type r_type = rhs->concrete_type();-- if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {- return SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(),- v_l->to_string() + " " + sep + " " + v_r->to_string());- }- }-- // ToDo: throw error in op functions- // ToDo: then catch and re-throw them- ParserState pstate(b->pstate());- int precision = (int)ctx.c_options->precision;- bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED;- if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {- const Number* l_n = dynamic_cast<const Number*>(lhs);- const Number* r_n = dynamic_cast<const Number*>(rhs);- return op_numbers(ctx.mem, op_type, *l_n, *r_n, compressed, precision, &pstate);- }- if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {- const Number* l_n = dynamic_cast<const Number*>(lhs);- const Color* r_c = dynamic_cast<const Color*>(rhs);- return op_number_color(ctx.mem, op_type, *l_n, *r_c, compressed, precision, &pstate);- }- if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {- const Color* l_c = dynamic_cast<const Color*>(lhs);- const Number* r_n = dynamic_cast<const Number*>(rhs);- return op_color_number(ctx.mem, op_type, *l_c, *r_n, compressed, precision, &pstate);- }- if (l_type == Expression::COLOR && r_type == Expression::COLOR) {- const Color* l_c = dynamic_cast<const Color*>(lhs);- const Color* r_c = dynamic_cast<const Color*>(rhs);- return op_colors(ctx.mem, op_type, *l_c, *r_c, compressed, precision, &pstate);- }-- To_Value to_value(ctx, ctx.mem);- Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));- Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));- Value* ex = op_strings(ctx.mem, op_type, *v_l, *v_r, compressed, precision, &pstate);- if (String_Constant* str = dynamic_cast<String_Constant*>(ex))- {- if (str->concrete_type() != Expression::STRING) return ex;- String_Constant* lstr = dynamic_cast<String_Constant*>(lhs);- String_Constant* rstr = dynamic_cast<String_Constant*>(rhs);- if (String_Constant* org = lstr ? lstr : rstr)- { str->quote_mark(org->quote_mark()); }- }- return ex;-- }-- Expression* Eval::operator()(Unary_Expression* u)- {- Expression* operand = u->operand()->perform(this);- if (u->type() == Unary_Expression::NOT) {- Boolean* result = SASS_MEMORY_NEW(ctx.mem, Boolean, u->pstate(), (bool)*operand);- result->value(!result->value());- return result;- }- else if (operand->concrete_type() == Expression::NUMBER) {- Number* result = SASS_MEMORY_NEW(ctx.mem, Number, *static_cast<Number*>(operand));- result->value(u->type() == Unary_Expression::MINUS- ? -result->value()- : result->value());- return result;- }- else {- To_String to_string(&ctx);- // Special cases: +/- variables which evaluate to null ouput just +/-,- // but +/- null itself outputs the string- if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) {- u->operand(SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), ""));- }- else u->operand(operand);- String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted,- u->pstate(),- u->perform(&to_string));- return result;- }- // unreachable- return u;- }-- Expression* Eval::operator()(Function_Call* c)- {- if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {- std::ostringstream stm;- stm << "Stack depth exceeded max of " << Constants::MaxCallStack;- error(stm.str(), c->pstate(), backtrace());- }- std::string name(Util::normalize_underscores(c->name()));- std::string full_name(name + "[f]");- Arguments* args = c->arguments();- if (full_name != "if[f]") {- args = static_cast<Arguments*>(args->perform(this));- }-- Env* env = environment();- if (!env->has(full_name)) {- if (!env->has("*[f]")) {- // just pass it through as a literal- Function_Call* lit = SASS_MEMORY_NEW(ctx.mem, Function_Call,- c->pstate(),- c->name(),- args);- To_String to_string(&ctx);- if (args->has_named_arguments()) {- error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());- }- return SASS_MEMORY_NEW(ctx.mem, String_Quoted,- c->pstate(),- lit->perform(&to_string));- } else {- // call generic function- full_name = "*[f]";- }- }-- Definition* def = static_cast<Definition*>((*env)[full_name]);-- if (def->is_overload_stub()) {- std::stringstream ss;- ss << full_name- << args->length();- full_name = ss.str();- std::string resolved_name(full_name);- if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());- def = static_cast<Definition*>((*env)[resolved_name]);- }-- Expression* result = c;- Block* body = def->block();- Native_Function func = def->native_function();- Sass_Function_Entry c_function = def->c_function();-- Parameters* params = def->parameters();- Env fn_env(def->environment());- exp.env_stack.push_back(&fn_env);-- if (func || body) {- bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);- Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");- exp.backtrace_stack.push_back(&here);- // if it's user-defined, eval the body- if (body) result = body->perform(this);- // if it's native, invoke the underlying CPP function- else result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace());- if (!result) error(std::string("Function ") + c->name() + " did not return a value", c->pstate());- exp.backtrace_stack.pop_back();- }-- // else if it's a user-defined c function- // convert call into C-API compatible form- else if (c_function) {- Sass_Function_Fn c_func = sass_function_get_function(c_function);- if (full_name == "*[f]") {- String_Quoted *str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, c->pstate(), c->name());- Arguments* new_args = SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate());- *new_args << SASS_MEMORY_NEW(ctx.mem, Argument, c->pstate(), str);- *new_args += args;- args = new_args;- }-- // populates env with default values for params- std::string ff(c->name());- bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);-- Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");- exp.backtrace_stack.push_back(&here);-- To_C to_c;- union Sass_Value* c_args = sass_make_list(params[0].length(), SASS_COMMA);- for(size_t i = 0; i < params[0].length(); i++) {- std::string key = params[0][i]->name();- AST_Node* node = fn_env.get_local(key);- Expression* arg = static_cast<Expression*>(node);- sass_list_set_value(c_args, i, arg->perform(&to_c));- }- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- if (sass_value_get_tag(c_val) == SASS_ERROR) {- error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace());- } else if (sass_value_get_tag(c_val) == SASS_WARNING) {- error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace());- }- result = cval_to_astnode(ctx.mem, c_val, ctx, backtrace(), c->pstate());-- exp.backtrace_stack.pop_back();- sass_delete_value(c_args);- if (c_val != c_args)- sass_delete_value(c_val);- }-- // link back to function definition- // only do this for custom functions- if (result->pstate().file == std::string::npos)- result->pstate(c->pstate());-- result->is_delayed(result->concrete_type() == Expression::STRING);- if (!result->is_delayed()) result = result->perform(this);- exp.env_stack.pop_back();- return result;- }-- Expression* Eval::operator()(Function_Call_Schema* s)- {- Expression* evaluated_name = s->name()->perform(this);- Expression* evaluated_args = s->arguments()->perform(this);- String_Schema* ss = SASS_MEMORY_NEW(ctx.mem, String_Schema, s->pstate(), 2);- (*ss) << evaluated_name << evaluated_args;- return ss->perform(this);- }-- Expression* Eval::operator()(Variable* v)- {- To_String to_string(&ctx);- std::string name(v->name());- Expression* value = 0;- Env* env = environment();- if (env->has(name)) value = static_cast<Expression*>((*env)[name]);- else error("Undefined variable: \"" + v->name() + "\".", v->pstate());- // std::cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << std::endl;- if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value();-- // behave according to as ruby sass (add leading zero)- if (value->concrete_type() == Expression::NUMBER) {- value = SASS_MEMORY_NEW(ctx.mem, Number, *static_cast<Number*>(value));- static_cast<Number*>(value)->zero(true);- }- else if (value->concrete_type() == Expression::STRING) {- if (auto str = dynamic_cast<String_Quoted*>(value)) {- value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *str);- } else if (auto str = dynamic_cast<String_Constant*>(value)) {- value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->perform(&to_string));- }- }- else if (value->concrete_type() == Expression::LIST) {- value = SASS_MEMORY_NEW(ctx.mem, List, *static_cast<List*>(value));- }- else if (value->concrete_type() == Expression::MAP) {- value = SASS_MEMORY_NEW(ctx.mem, Map, *static_cast<Map*>(value));- }- else if (value->concrete_type() == Expression::BOOLEAN) {- value = SASS_MEMORY_NEW(ctx.mem, Boolean, *static_cast<Boolean*>(value));- }- else if (value->concrete_type() == Expression::COLOR) {- value = SASS_MEMORY_NEW(ctx.mem, Color, *static_cast<Color*>(value));- }- else if (value->concrete_type() == Expression::NULL_VAL) {- value = SASS_MEMORY_NEW(ctx.mem, Null, value->pstate());- }- else if (value->concrete_type() == Expression::SELECTOR) {- value = value->perform(this); // ->perform(&listize);- }-- // std::cerr << "\ttype is now: " << typeid(*value).name() << std::endl << std::endl;- return value;- }-- Expression* Eval::operator()(Textual* t)- {- using Prelexer::number;- Expression* result = 0;- bool zero = !( t->value().substr(0, 1) == "." ||- t->value().substr(0, 2) == "-." );-- const std::string& text = t->value();- size_t num_pos = text.find_first_not_of(" \n\r\t");- if (num_pos == std::string::npos) num_pos = text.length();- size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);- if (unit_pos == std::string::npos) unit_pos = text.length();- const std::string& num = text.substr(num_pos, unit_pos - num_pos);-- switch (t->type())- {- case Textual::NUMBER:- result = SASS_MEMORY_NEW(ctx.mem, Number,- t->pstate(),- sass_atof(num.c_str()),- "",- zero);- break;- case Textual::PERCENTAGE:- result = SASS_MEMORY_NEW(ctx.mem, Number,- t->pstate(),- sass_atof(num.c_str()),- "%",- zero);- break;- case Textual::DIMENSION:- result = SASS_MEMORY_NEW(ctx.mem, Number,- t->pstate(),- sass_atof(num.c_str()),- Token(number(text.c_str())),- zero);- break;- case Textual::HEX: {- if (t->value().substr(0, 1) != "#") {- result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, t->pstate(), t->value());- break;- }- std::string hext(t->value().substr(1)); // chop off the '#'- if (hext.length() == 6) {- std::string r(hext.substr(0,2));- std::string g(hext.substr(2,2));- std::string b(hext.substr(4,2));- result = SASS_MEMORY_NEW(ctx.mem, Color,- t->pstate(),- static_cast<double>(strtol(r.c_str(), NULL, 16)),- static_cast<double>(strtol(g.c_str(), NULL, 16)),- static_cast<double>(strtol(b.c_str(), NULL, 16)),- 1, true,- t->value());- }- else {- result = SASS_MEMORY_NEW(ctx.mem, Color,- t->pstate(),- static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),- static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),- static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),- 1, false,- t->value());- }- } break;- }- return result;- }-- Expression* Eval::operator()(Number* n)- {- return n;- }-- Expression* Eval::operator()(Boolean* b)- {- return b;- }-- char is_quoted(std::string str)- {- size_t len = str.length();- if (len < 2) return 0;- if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) {- return str[0];- }- else {- return 0;- }- }-- std::string Eval::interpolation(Expression* s, bool into_quotes) {- Env* env = environment();- if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) {- if (str_quoted->quote_mark()) {- if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) {- return evacuate_escapes(str_quoted->value());- } else {- return string_escape(quote(str_quoted->value(), str_quoted->quote_mark()));- }- } else {- return evacuate_escapes(str_quoted->value());- }- } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {- if (into_quotes && !str_constant->is_interpolant()) return str_constant->value();- return evacuate_escapes(str_constant->value());- } else if (dynamic_cast<Parent_Selector*>(s)) {- To_String to_string(&ctx);- Expression* sel = s->perform(this);- return evacuate_quotes(sel ? sel->perform(&to_string) : "");-- } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) {- // To_String to_string(&ctx);- // return evacuate_quotes(str_schema->perform(&to_string));-- std::string res = "";- for(auto i : str_schema->elements())- res += (interpolation(i));- //ToDo: do this in one step- auto esc = evacuate_escapes(res);- auto unq = unquote(esc);- if (unq == esc) {- return string_to_output(res);- } else {- return evacuate_quotes(unq);- }- } else if (List* list = dynamic_cast<List*>(s)) {- std::string acc = ""; // ToDo: different output styles- std::string sep = list->separator() == SASS_COMMA ? "," : " ";- if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " ";- bool initial = false;- for(auto item : list->elements()) {- if (item->concrete_type() != Expression::NULL_VAL) {- if (initial) acc += sep;- acc += interpolation(item);- initial = true;- }- }- return evacuate_quotes(acc);- } else if (Variable* var = dynamic_cast<Variable*>(s)) {- std::string name(var->name());- if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate());- Expression* value = static_cast<Expression*>((*env)[name]);- return evacuate_quotes(interpolation(value));- } else if (dynamic_cast<Binary_Expression*>(s)) {- Expression* ex = s->perform(this);- // avoid recursive calls if same object gets returned- // since we will call interpolate again for the result- if (ex == s) {- To_String to_string(&ctx);- return evacuate_quotes(s->perform(&to_string));- }- return evacuate_quotes(interpolation(ex));- } else if (dynamic_cast<Function_Call*>(s)) {- Expression* ex = s->perform(this);- return evacuate_quotes(unquote(interpolation(ex)));- } else if (dynamic_cast<Unary_Expression*>(s)) {- Expression* ex = s->perform(this);- return evacuate_quotes(interpolation(ex));- } else if (dynamic_cast<Map*>(s)) {- To_String to_string(&ctx);- std::string dbg(s->perform(&to_string));- error(dbg + " isn't a valid CSS value.", s->pstate());- return dbg;- } else {- To_String to_string(&ctx);- return evacuate_quotes(s->perform(&to_string));- }- }-- Expression* Eval::operator()(String_Schema* s)- {- std::string acc;- bool into_quotes = false;- size_t L = s->length();- if (L > 1) {- if (String_Constant* l = dynamic_cast<String_Constant*>((*s)[0])) {- if (String_Constant* r = dynamic_cast<String_Constant*>((*s)[L - 1])) {- if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true;- if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;- }- }- }- for (size_t i = 0; i < L; ++i) {- // really a very special fix, but this is the logic I got from- // analyzing the ruby sass behavior and it actually seems to work- // https://github.com/sass/libsass/issues/1333- if (i == 0 && L > 1 && dynamic_cast<Function_Call*>((*s)[i])) {- Expression* ex = (*s)[i]->perform(this);- if (auto sq = dynamic_cast<String_Quoted*>(ex)) {- if (sq->is_delayed() && ! s->has_interpolants()) {- acc += string_escape(quote(sq->value(), sq->quote_mark()));- } else {- acc += interpolation((*s)[i], into_quotes);- }- } else if (ex) {- acc += interpolation((*s)[i], into_quotes);- }- } else if ((*s)[i]) {- acc += interpolation((*s)[i], into_quotes);- }- }- String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), acc);- if (!str->quote_mark()) {- str->value(string_unescape(str->value()));- } else if (str->quote_mark()) {- str->quote_mark('*');- }- str->is_delayed(true);- return str;- }-- Expression* Eval::operator()(String_Constant* s)- {- if (!s->is_delayed() && name_to_color(s->value())) {- Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *name_to_color(s->value()));- c->pstate(s->pstate());- c->disp(s->value());- return c;- }- return s;- }-- Expression* Eval::operator()(String_Quoted* s)- {- return s;- }-- Expression* Eval::operator()(Supports_Operator* c)- {- Expression* left = c->left()->perform(this);- Expression* right = c->right()->perform(this);- Supports_Operator* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Operator,- c->pstate(),- static_cast<Supports_Condition*>(left),- static_cast<Supports_Condition*>(right),- c->operand());- return cc;- }-- Expression* Eval::operator()(Supports_Negation* c)- {- Expression* condition = c->condition()->perform(this);- Supports_Negation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Negation,- c->pstate(),- static_cast<Supports_Condition*>(condition));- return cc;- }-- Expression* Eval::operator()(Supports_Declaration* c)- {- Expression* feature = c->feature()->perform(this);- Expression* value = c->value()->perform(this);- Supports_Declaration* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,- c->pstate(),- feature,- value);- return cc;- }-- Expression* Eval::operator()(Supports_Interpolation* c)- {- Expression* value = c->value()->perform(this);- Supports_Interpolation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation,- c->pstate(),- value);- return cc;- }-- Expression* Eval::operator()(At_Root_Expression* e)- {- Expression* feature = e->feature();- feature = (feature ? feature->perform(this) : 0);- Expression* value = e->value();- value = (value ? value->perform(this) : 0);- Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression,- e->pstate(),- static_cast<String*>(feature),- value,- e->is_interpolated());- return ee;- }-- Expression* Eval::operator()(Media_Query* q)- {- To_String to_string(&ctx);- String* t = q->media_type();- t = static_cast<String*>(t ? t->perform(this) : 0);- Media_Query* qq = SASS_MEMORY_NEW(ctx.mem, Media_Query,- q->pstate(),- t,- q->length(),- q->is_negated(),- q->is_restricted());- for (size_t i = 0, L = q->length(); i < L; ++i) {- *qq << static_cast<Media_Query_Expression*>((*q)[i]->perform(this));- }- return qq;- }-- Expression* Eval::operator()(Media_Query_Expression* e)- {- Expression* feature = e->feature();- feature = (feature ? feature->perform(this) : 0);- if (feature && dynamic_cast<String_Quoted*>(feature)) {- feature = SASS_MEMORY_NEW(ctx.mem, String_Quoted,- feature->pstate(),- dynamic_cast<String_Quoted*>(feature)->value());- }- Expression* value = e->value();- value = (value ? value->perform(this) : 0);- if (value && dynamic_cast<String_Quoted*>(value)) {- value = SASS_MEMORY_NEW(ctx.mem, String_Quoted,- value->pstate(),- dynamic_cast<String_Quoted*>(value)->value());- }- return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression,- e->pstate(),- feature,- value,- e->is_interpolated());- }-- Expression* Eval::operator()(Null* n)- {- return n;- }-- Expression* Eval::operator()(Argument* a)- {- Expression* val = a->value();- val->is_delayed(false);- val = val->perform(this);- val->is_delayed(false);-- bool is_rest_argument = a->is_rest_argument();- bool is_keyword_argument = a->is_keyword_argument();-- if (a->is_rest_argument()) {- if (val->concrete_type() == Expression::MAP) {- is_rest_argument = false;- is_keyword_argument = true;- }- else if(val->concrete_type() != Expression::LIST) {- List* wrapper = SASS_MEMORY_NEW(ctx.mem, List,- val->pstate(),- 0,- SASS_COMMA,- true);- *wrapper << val;- val = wrapper;- }- }- return SASS_MEMORY_NEW(ctx.mem, Argument,- a->pstate(),- val,- a->name(),- is_rest_argument,- is_keyword_argument);- }-- Expression* Eval::operator()(Arguments* a)- {- Arguments* aa = SASS_MEMORY_NEW(ctx.mem, Arguments, a->pstate());- for (size_t i = 0, L = a->length(); i < L; ++i) {- *aa << static_cast<Argument*>((*a)[i]->perform(this));- }- return aa;- }-- Expression* Eval::operator()(Comment* c)- {- return 0;- }-- inline Expression* Eval::fallback_impl(AST_Node* n)- {- return static_cast<Expression*>(n);- }-- // All the binary helpers.-- bool Eval::eq(Expression* lhs, Expression* rhs)- {- // use compare operator from ast node- return lhs && rhs && *lhs == *rhs;- }-- bool Eval::lt(Expression* lhs, Expression* rhs)- {- Number* l = dynamic_cast<Number*>(lhs);- Number* r = dynamic_cast<Number*>(rhs);- if (!l) error("may only compare numbers", lhs->pstate());- if (!r) error("may only compare numbers", rhs->pstate());- // use compare operator from ast node- return *l < *r;- }-- Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, bool compressed, int precision, ParserState* pstate)- {- double lv = l.value();- double rv = r.value();- if (op == Sass_OP::DIV && !rv) {- return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), "Infinity");- }- if (op == Sass_OP::MOD && !rv) {- error("division by zero", pstate ? *pstate : r.pstate());- }-- Number tmp(r);- bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;- tmp.normalize(l.find_convertible_unit(), strict);- std::string l_unit(l.unit());- std::string r_unit(tmp.unit());- if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() &&- (op == Sass_OP::ADD || op == Sass_OP::SUB)) {- error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", pstate ? *pstate : r.pstate());- }- Number* v = SASS_MEMORY_NEW(mem, Number, l);- v->pstate(pstate ? *pstate : l.pstate());- if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {- v->numerator_units() = r.numerator_units();- v->denominator_units() = r.denominator_units();- }-- if (op == Sass_OP::MUL) {- v->value(ops[op](lv, rv));- for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {- v->numerator_units().push_back(r.numerator_units()[i]);- }- for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {- v->denominator_units().push_back(r.denominator_units()[i]);- }- }- else if (op == Sass_OP::DIV) {- v->value(ops[op](lv, rv));- for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {- v->denominator_units().push_back(r.numerator_units()[i]);- }- for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {- v->numerator_units().push_back(r.denominator_units()[i]);- }- } else {- v->value(ops[op](lv, tmp.value()));- }- v->normalize();- return v;- }-- Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, bool compressed, int precision, ParserState* pstate)- {- Color r(rh);- r.disp("");- double lv = l.value();- switch (op) {- case Sass_OP::ADD:- case Sass_OP::MUL: {- return SASS_MEMORY_NEW(mem, Color,- pstate ? *pstate : l.pstate(),- ops[op](lv, r.r()),- ops[op](lv, r.g()),- ops[op](lv, r.b()),- r.a());- } break;- case Sass_OP::SUB:- case Sass_OP::DIV: {- std::string sep(op == Sass_OP::SUB ? "-" : "/");- std::string color(r.to_string(compressed||!r.sixtuplet(), precision));- return SASS_MEMORY_NEW(mem, String_Quoted,- pstate ? *pstate : l.pstate(),- l.to_string(compressed, precision)- + sep- + color);- } break;- case Sass_OP::MOD: {- error("cannot divide a number by a color", pstate ? *pstate : r.pstate());- } break;- default: break; // caller should ensure that we don't get here- }- // unreachable- return SASS_MEMORY_NEW(mem, Color, rh);- }-- Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, bool compressed, int precision, ParserState* pstate)- {- double rv = r.value();- if (op == Sass_OP::DIV && !rv) error("division by zero", pstate ? *pstate : r.pstate());- return SASS_MEMORY_NEW(mem, Color,- pstate ? *pstate : l.pstate(),- ops[op](l.r(), rv),- ops[op](l.g(), rv),- ops[op](l.b(), rv),- l.a());- }-- Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, bool compressed, int precision, ParserState* pstate)- {- if (l.a() != r.a()) {- error("alpha channels must be equal when combining colors", pstate ? *pstate : r.pstate());- }- if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {- error("division by zero", pstate ? *pstate : r.pstate());- }- return SASS_MEMORY_NEW(mem, Color,- pstate ? *pstate : l.pstate(),- ops[op](l.r(), r.r()),- ops[op](l.g(), r.g()),- ops[op](l.b(), r.b()),- l.a());- }-- Value* Eval::op_strings(Memory_Manager& mem, enum Sass_OP op, Value& lhs, Value& rhs, bool compressed, int precision, ParserState* pstate)- {- Expression::Concrete_Type ltype = lhs.concrete_type();- Expression::Concrete_Type rtype = rhs.concrete_type();-- String_Quoted* lqstr = dynamic_cast<String_Quoted*>(&lhs);- String_Quoted* rqstr = dynamic_cast<String_Quoted*>(&rhs);-- std::string lstr(lqstr ? lqstr->value() : lhs.to_string(compressed, precision));- std::string rstr(rqstr ? rqstr->value() : rhs.to_string(compressed, precision));-- bool l_str_quoted = ((Sass::String*)&lhs) && ((Sass::String*)&lhs)->sass_fix_1291();- bool r_str_quoted = ((Sass::String*)&rhs) && ((Sass::String*)&rhs)->sass_fix_1291();- bool l_str_color = ltype == Expression::STRING && name_to_color(lstr) && !l_str_quoted;- bool r_str_color = rtype == Expression::STRING && name_to_color(rstr) && !r_str_quoted;-- if (l_str_color && r_str_color) {- const Color* c_l = name_to_color(lstr);- const Color* c_r = name_to_color(rstr);- return op_colors(mem, op,*c_l, *c_r, compressed, precision);- }- else if (l_str_color && rtype == Expression::COLOR) {- const Color* c_l = name_to_color(lstr);- const Color* c_r = dynamic_cast<const Color*>(&rhs);- return op_colors(mem, op, *c_l, *c_r, compressed, precision);- }- else if (ltype == Expression::COLOR && r_str_color) {- const Color* c_l = dynamic_cast<const Color*>(&lhs);- const Color* c_r = name_to_color(rstr);- return op_colors(mem, op, *c_l, *c_r, compressed, precision);- }- else if (l_str_color && rtype == Expression::NUMBER) {- const Color* c_l = name_to_color(lstr);- const Number* n_r = dynamic_cast<const Number*>(&rhs);- return op_color_number(mem, op, *c_l, *n_r, compressed, precision);- }- else if (ltype == Expression::NUMBER && r_str_color) {- const Number* n_l = dynamic_cast<const Number*>(&lhs);- const Color* c_r = name_to_color(rstr);- return op_number_color(mem, op, *n_l, *c_r, compressed, precision);- }- if (op == Sass_OP::MUL) error("invalid operands for multiplication", lhs.pstate());- if (op == Sass_OP::MOD) error("invalid operands for modulo", lhs.pstate());- std::string sep;- switch (op) {- case Sass_OP::SUB: sep = "-"; break;- case Sass_OP::DIV: sep = "/"; break;- default: break;- }- if (ltype == Expression::NULL_VAL) error("Invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs.pstate());- if (rtype == Expression::NULL_VAL) error("Invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", rhs.pstate());-- if (ltype == Expression::NUMBER && sep == "/" && rtype == Expression::STRING)- {- return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(),- lhs.to_string() + sep + rhs.to_string());- }-- if ( (ltype == Expression::STRING || sep == "") &&- (sep != "/" || !rqstr || !rqstr->quote_mark())- ) {- char quote_mark = 0;- std::string unq(unquote(lstr + sep + rstr, "e_mark, true));- if (quote_mark && quote_mark != '*') {- return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), quote_mark + unq + quote_mark);- }- return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + sep + rstr);- }- return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + quote(rstr));- }-- Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate)- {- using std::strlen;- using std::strcpy;- Expression* e = 0;- switch (sass_value_get_tag(v)) {- case SASS_BOOLEAN: {- e = SASS_MEMORY_NEW(mem, Boolean, pstate, !!sass_boolean_get_value(v));- } break;- case SASS_NUMBER: {- e = SASS_MEMORY_NEW(mem, Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));- } break;- case SASS_COLOR: {- e = SASS_MEMORY_NEW(mem, Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));- } break;- case SASS_STRING: {- if (sass_string_is_quoted(v))- e = SASS_MEMORY_NEW(mem, String_Quoted, pstate, sass_string_get_value(v));- else {- e = SASS_MEMORY_NEW(mem, String_Constant, pstate, sass_string_get_value(v));- }- } break;- case SASS_LIST: {- List* l = SASS_MEMORY_NEW(mem, List, pstate, sass_list_get_length(v), sass_list_get_separator(v));- for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {- *l << cval_to_astnode(mem, sass_list_get_value(v, i), ctx, backtrace, pstate);- }- e = l;- } break;- case SASS_MAP: {- Map* m = SASS_MEMORY_NEW(mem, Map, pstate);- for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {- *m << std::make_pair(- cval_to_astnode(mem, sass_map_get_key(v, i), ctx, backtrace, pstate),- cval_to_astnode(mem, sass_map_get_value(v, i), ctx, backtrace, pstate));- }- e = m;- } break;- case SASS_NULL: {- e = SASS_MEMORY_NEW(mem, Null, pstate);- } break;- case SASS_ERROR: {- error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace);- } break;- case SASS_WARNING: {- error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);- } break;- }- return e;- }-- Selector_List* Eval::operator()(Selector_List* s)- {- std::vector<Selector_List*> rv;- Selector_List* sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate());- sl->media_block(s->media_block());- for (size_t i = 0, iL = s->length(); i < iL; ++i) {- rv.push_back(operator()((*s)[i]));- }-- // we should actually permutate parent first- // but here we have permutated the selector first- size_t round = 0;- while (round != std::string::npos) {- bool abort = true;- for (size_t i = 0, iL = rv.size(); i < iL; ++i) {- if (rv[i]->length() > round) {- *sl << (*rv[i])[round];- abort = false;- }- }- if (abort) {- round = std::string::npos;- } else {- ++ round;- }-- }- return sl;- }--- Selector_List* Eval::operator()(Complex_Selector* s)- {- return s->parentize(selector(), ctx);-- }-- Attribute_Selector* Eval::operator()(Attribute_Selector* s)- {- String* attr = s->value();- if (attr) { attr = static_cast<String*>(attr->perform(this)); }- Attribute_Selector* ss = SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, *s);- ss->value(attr);- return ss;- }-- Selector_List* Eval::operator()(Selector_Schema* s)- {- To_String to_string;- // the parser will look for a brace to end the selector- std::string result_str(s->contents()->perform(this)->perform(&to_string));- result_str = unquote(Util::rtrim(result_str)) + "{";- Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());- return operator()(p.parse_selector_list(exp.block_stack.back()->is_root()));- }-- Expression* Eval::operator()(Parent_Selector* p)- {- Selector_List* pr = selector();- if (pr) {- exp.selector_stack.pop_back();- pr = operator()(pr);- exp.selector_stack.push_back(pr);- return pr;- } else {- return SASS_MEMORY_NEW(ctx.mem, Null, p->pstate());+#include "sass.hpp"+#include <cstdlib>+#include <cmath>+#include <iostream>+#include <sstream>+#include <iomanip>+#include <typeinfo>++#include "file.hpp"+#include "eval.hpp"+#include "ast.hpp"+#include "bind.hpp"+#include "util.hpp"+#include "inspect.hpp"+#include "environment.hpp"+#include "position.hpp"+#include "sass/values.h"+#include "to_value.hpp"+#include "to_c.hpp"+#include "context.hpp"+#include "backtrace.hpp"+#include "lexer.hpp"+#include "prelexer.hpp"+#include "parser.hpp"+#include "expand.hpp"+#include "color_maps.hpp"++namespace Sass {++ inline double add(double x, double y) { return x + y; }+ inline double sub(double x, double y) { return x - y; }+ inline double mul(double x, double y) { return x * y; }+ inline double div(double x, double y) { return x / y; } // x/0 checked by caller+ inline double mod(double x, double y) { // x/0 checked by caller+ if ((x > 0 && y < 0) || (x < 0 && y > 0)) {+ double ret = std::fmod(x, y);+ return ret ? ret + y : ret;+ } else {+ return std::fmod(x, y);+ }+ }+ typedef double (*bop)(double, double);+ bop ops[Sass_OP::NUM_OPS] = {+ 0, 0, // and, or+ 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte+ add, sub, mul, div, mod+ };++ Eval::Eval(Expand& exp)+ : exp(exp),+ ctx(exp.ctx),+ force(false),+ is_in_comment(false)+ { }+ Eval::~Eval() { }++ Env* Eval::environment()+ {+ return exp.environment();+ }++ Selector_List_Obj Eval::selector()+ {+ return exp.selector();+ }++ Backtrace* Eval::backtrace()+ {+ return exp.backtrace();+ }++ Expression_Ptr Eval::operator()(Block_Ptr b)+ {+ Expression_Ptr val = 0;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ val = b->at(i)->perform(this);+ if (val) return val;+ }+ return val;+ }++ Expression_Ptr Eval::operator()(Assignment_Ptr a)+ {+ Env* env = exp.environment();+ std::string var(a->variable());+ if (a->is_global()) {+ if (a->is_default()) {+ if (env->has_global(var)) {+ Expression_Ptr e = SASS_MEMORY_CAST(Expression, env->get_global(var));+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(this));+ }+ }+ else {+ env->set_global(var, a->value()->perform(this));+ }+ }+ else {+ env->set_global(var, a->value()->perform(this));+ }+ }+ else if (a->is_default()) {+ if (env->has_lexical(var)) {+ auto cur = env;+ while (cur && cur->is_lexical()) {+ if (cur->has_local(var)) {+ if (AST_Node_Obj node = cur->get_local(var)) {+ Expression_Ptr e = SASS_MEMORY_CAST(Expression, node);+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ cur->set_local(var, a->value()->perform(this));+ }+ }+ else {+ throw std::runtime_error("Env not in sync");+ }+ return 0;+ }+ cur = cur->parent();+ }+ throw std::runtime_error("Env not in sync");+ }+ else if (env->has_global(var)) {+ if (AST_Node_Obj node = env->get_global(var)) {+ Expression_Ptr e = SASS_MEMORY_CAST(Expression, node);+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(this));+ }+ }+ }+ else if (env->is_lexical()) {+ env->set_local(var, a->value()->perform(this));+ }+ else {+ env->set_local(var, a->value()->perform(this));+ }+ }+ else {+ env->set_lexical(var, a->value()->perform(this));+ }+ return 0;+ }++ Expression_Ptr Eval::operator()(If_Ptr i)+ {+ Expression_Obj rv = 0;+ Env env(exp.environment());+ exp.env_stack.push_back(&env);+ Expression_Obj cond = i->predicate()->perform(this);+ if (!cond->is_false()) {+ rv = i->block()->perform(this);+ }+ else {+ Block_Obj alt = i->alternative();+ if (alt) rv = alt->perform(this);+ }+ exp.env_stack.pop_back();+ return rv.detach();+ }++ // For does not create a new env scope+ // But iteration vars are reset afterwards+ Expression_Ptr Eval::operator()(For_Ptr f)+ {+ std::string variable(f->variable());+ Expression_Obj low = f->lower_bound()->perform(this);+ if (low->concrete_type() != Expression::NUMBER) {+ throw Exception::TypeMismatch(*low, "integer");+ }+ Expression_Obj high = f->upper_bound()->perform(this);+ if (high->concrete_type() != Expression::NUMBER) {+ throw Exception::TypeMismatch(*high, "integer");+ }+ Number_Obj sass_start = SASS_MEMORY_CAST(Number, low);+ Number_Obj sass_end = SASS_MEMORY_CAST(Number, high);+ // check if units are valid for sequence+ if (sass_start->unit() != sass_end->unit()) {+ std::stringstream msg; msg << "Incompatible units: '"+ << sass_end->unit() << "' and '"+ << sass_start->unit() << "'.";+ error(msg.str(), low->pstate(), backtrace());+ }+ double start = sass_start->value();+ double end = sass_end->value();+ // only create iterator once in this environment+ Env env(environment(), true);+ exp.env_stack.push_back(&env);+ Number_Ptr it = SASS_MEMORY_NEW(Number, low->pstate(), start, sass_end->unit());+ env.set_local(variable, it);+ Block_Obj body = f->block();+ Expression_Ptr val = 0;+ if (start < end) {+ if (f->is_inclusive()) ++end;+ for (double i = start;+ i < end;+ ++i) {+ it->value(i);+ env.set_local(variable, it);+ val = body->perform(this);+ if (val) break;+ }+ } else {+ if (f->is_inclusive()) --end;+ for (double i = start;+ i > end;+ --i) {+ it->value(i);+ env.set_local(variable, it);+ val = body->perform(this);+ if (val) break;+ }+ }+ exp.env_stack.pop_back();+ return val;+ }++ // Eval does not create a new env scope+ // But iteration vars are reset afterwards+ Expression_Ptr Eval::operator()(Each_Ptr e)+ {+ std::vector<std::string> variables(e->variables());+ Expression_Obj expr = e->list()->perform(this);+ Env env(environment(), true);+ exp.env_stack.push_back(&env);+ List_Obj list = 0;+ Map_Ptr map = 0;+ if (expr->concrete_type() == Expression::MAP) {+ map = SASS_MEMORY_CAST(Map, expr);+ }+ else if (Selector_List_Ptr ls = SASS_MEMORY_CAST(Selector_List, expr)) {+ Listize listize;+ Expression_Obj rv = ls->perform(&listize);+ list = dynamic_cast<List_Ptr>(&rv);+ }+ else if (expr->concrete_type() != Expression::LIST) {+ list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);+ list->append(expr);+ }+ else {+ list = SASS_MEMORY_CAST(List, expr);+ }++ Block_Obj body = e->block();+ Expression_Obj val = 0;++ if (map) {+ for (Expression_Obj key : map->keys()) {+ Expression_Obj value = map->at(key);++ if (variables.size() == 1) {+ List_Ptr variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);+ variable->append(key);+ variable->append(value);+ env.set_local(variables[0], variable);+ } else {+ env.set_local(variables[0], &key);+ env.set_local(variables[1], &value);+ }++ val = body->perform(this);+ if (val) break;+ }+ }+ else {+ if (list->length() == 1 && SASS_MEMORY_CAST(Selector_List, list)) {+ list = SASS_MEMORY_CAST(List, list);+ }+ for (size_t i = 0, L = list->length(); i < L; ++i) {+ Expression_Ptr e = &list->at(i);+ // unwrap value if the expression is an argument+ if (Argument_Ptr arg = SASS_MEMORY_CAST_PTR(Argument, e)) e = &arg->value();+ // check if we got passed a list of args (investigate)+ if (List_Ptr scalars = SASS_MEMORY_CAST_PTR(List, e)) {+ if (variables.size() == 1) {+ Expression_Ptr var = scalars;+ env.set_local(variables[0], var);+ } else {+ // XXX: this is never hit via spec tests+ for (size_t j = 0, K = variables.size(); j < K; ++j) {+ Expression_Ptr res = j >= scalars->length()+ ? SASS_MEMORY_NEW(Null, expr->pstate())+ : &scalars->at(j);+ env.set_local(variables[j], res);+ }+ }+ } else {+ if (variables.size() > 0) {+ env.set_local(variables.at(0), e);+ for (size_t j = 1, K = variables.size(); j < K; ++j) {+ // XXX: this is never hit via spec tests+ Expression_Ptr res = SASS_MEMORY_NEW(Null, expr->pstate());+ env.set_local(variables[j], res);+ }+ }+ }+ val = body->perform(this);+ if (val) break;+ }+ }+ exp.env_stack.pop_back();+ return val.detach();+ }++ Expression_Ptr Eval::operator()(While_Ptr w)+ {+ Expression_Obj pred = w->predicate();+ Block_Obj body = w->block();+ Env env(environment(), true);+ exp.env_stack.push_back(&env);+ Expression_Obj cond = pred->perform(this);+ while (!cond->is_false()) {+ Expression_Obj val = body->perform(this);+ if (val) {+ exp.env_stack.pop_back();+ return val.detach();+ }+ cond = pred->perform(this);+ }+ exp.env_stack.pop_back();+ return 0;+ }++ Expression_Ptr Eval::operator()(Return_Ptr r)+ {+ return r->value()->perform(this);+ }++ Expression_Ptr Eval::operator()(Warning_Ptr w)+ {+ Sass_Output_Style outstyle = ctx.c_options.output_style;+ ctx.c_options.output_style = NESTED;+ Expression_Obj message = w->message()->perform(this);+ Env* env = exp.environment();++ // try to use generic function+ if (env->has("@warn[f]")) {++ Definition_Ptr def = SASS_MEMORY_CAST(Definition, (*env)["@warn[f]"]);+ // Block_Obj body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ ctx.c_options.output_style = outstyle;+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::string result(unquote(message->to_sass()));+ Backtrace top(backtrace(), w->pstate(), "");+ std::cerr << "WARNING: " << result;+ std::cerr << top.to_string();+ std::cerr << std::endl << std::endl;+ ctx.c_options.output_style = outstyle;+ return 0;+ }++ Expression_Ptr Eval::operator()(Error_Ptr e)+ {+ Sass_Output_Style outstyle = ctx.c_options.output_style;+ ctx.c_options.output_style = NESTED;+ Expression_Obj message = e->message()->perform(this);+ Env* env = exp.environment();++ // try to use generic function+ if (env->has("@error[f]")) {++ Definition_Ptr def = SASS_MEMORY_CAST(Definition, (*env)["@error[f]"]);+ // Block_Obj body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ ctx.c_options.output_style = outstyle;+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::string result(unquote(message->to_sass()));+ ctx.c_options.output_style = outstyle;+ error(result, e->pstate());+ return 0;+ }++ Expression_Ptr Eval::operator()(Debug_Ptr d)+ {+ Sass_Output_Style outstyle = ctx.c_options.output_style;+ ctx.c_options.output_style = NESTED;+ Expression_Obj message = d->value()->perform(this);+ Env* env = exp.environment();++ // try to use generic function+ if (env->has("@debug[f]")) {++ Definition_Ptr def = SASS_MEMORY_CAST(Definition, (*env)["@debug[f]"]);+ // Block_Obj body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ ctx.c_options.output_style = outstyle;+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::string cwd(ctx.cwd());+ std::string result(unquote(message->to_sass()));+ std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));+ std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));+ std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));+ ctx.c_options.output_style = outstyle;++ std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;+ std::cerr << std::endl;+ return 0;+ }++ Expression_Ptr Eval::operator()(List_Ptr l)+ {+ // special case for unevaluated map+ if (l->separator() == SASS_HASH) {+ Map_Obj lm = SASS_MEMORY_NEW(Map,+ l->pstate(),+ l->length() / 2);+ for (size_t i = 0, L = l->length(); i < L; i += 2)+ {+ Expression_Ptr key = (*l)[i+0]->perform(this);+ Expression_Ptr val = (*l)[i+1]->perform(this);+ // make sure the color key never displays its real name+ key->is_delayed(true); // verified+ *lm << std::make_pair(key, val);+ }+ if (lm->has_duplicate_key()) {+ throw Exception::DuplicateKeyError(*lm, *l);+ }++ lm->is_interpolant(l->is_interpolant());+ return lm->perform(this);+ }+ // check if we should expand it+ if (l->is_expanded()) return l;+ // regular case for unevaluated lists+ List_Obj ll = SASS_MEMORY_NEW(List,+ l->pstate(),+ l->length(),+ l->separator(),+ l->is_arglist());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ ll->append((*l)[i]->perform(this));+ }+ ll->is_interpolant(l->is_interpolant());+ ll->from_selector(l->from_selector());+ ll->is_expanded(true);+ return ll.detach();+ }++ Expression_Ptr Eval::operator()(Map_Ptr m)+ {+ if (m->is_expanded()) return m;++ // make sure we're not starting with duplicate keys.+ // the duplicate key state will have been set in the parser phase.+ if (m->has_duplicate_key()) {+ throw Exception::DuplicateKeyError(*m, *m);+ }++ Map_Obj mm = SASS_MEMORY_NEW(Map,+ m->pstate(),+ m->length());+ for (auto key : m->keys()) {+ Expression_Ptr ex_key = key->perform(this);+ Expression_Ptr ex_val = m->at(key)->perform(this);+ *mm << std::make_pair(ex_key, ex_val);+ }++ // check the evaluated keys aren't duplicates.+ if (mm->has_duplicate_key()) {+ throw Exception::DuplicateKeyError(*mm, *m);+ }++ mm->is_expanded(true);+ return mm.detach();+ }++ Expression_Ptr Eval::operator()(Binary_Expression_Ptr b_in)+ {++ String_Schema_Obj ret_schema;+ Binary_Expression_Obj b = b_in;+ enum Sass_OP op_type = b->type();++ // only the last item will be used to eval the binary expression+ if (String_Schema_Ptr s_l = SASS_MEMORY_CAST(String_Schema, b->left())) {+ if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) {+ ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());+ Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),+ b->op(), &s_l->last(), b->right());+ bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // unverified+ for (size_t i = 0; i < s_l->length() - 1; ++i) {+ ret_schema->append(s_l->at(i)->perform(this));+ }+ ret_schema->append(bin_ex->perform(this));+ return ret_schema->perform(this);+ }+ }+ if (String_Schema_Ptr s_r = SASS_MEMORY_CAST(String_Schema, b->right())) {++ if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) {+ ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());+ Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),+ b->op(), b->left(), &s_r->first());+ bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // verified+ ret_schema->append(bin_ex->perform(this));+ for (size_t i = 1; i < s_r->length(); ++i) {+ ret_schema->append(s_r->at(i)->perform(this));+ }+ return ret_schema->perform(this);+ }+ }++ // don't eval delayed expressions (the '/' when used as a separator)+ if (!force && op_type == Sass_OP::DIV && b->is_delayed()) {+ b->right(b->right()->perform(this));+ b->left(b->left()->perform(this));+ return b.detach();+ }++ Expression_Obj lhs = b->left();+ Expression_Obj rhs = b->right();++ // fully evaluate their values+ if (op_type == Sass_OP::EQ ||+ op_type == Sass_OP::NEQ ||+ op_type == Sass_OP::GT ||+ op_type == Sass_OP::GTE ||+ op_type == Sass_OP::LT ||+ op_type == Sass_OP::LTE)+ {+ LOCAL_FLAG(force, true);+ lhs->is_expanded(false);+ lhs->set_delayed(false);+ lhs = lhs->perform(this);+ rhs->is_expanded(false);+ rhs->set_delayed(false);+ rhs = rhs->perform(this);+ }+ else {+ lhs = lhs->perform(this);+ }++ Binary_Expression_Obj u3 = b;+ switch (op_type) {+ case Sass_OP::AND: {+ return *lhs ? b->right()->perform(this) : lhs.detach();+ } break;++ case Sass_OP::OR: {+ return *lhs ? lhs.detach() : b->right()->perform(this);+ } break;++ default:+ break;+ }+ // not a logical connective, so go ahead and eval the rhs+ rhs = rhs->perform(this);+ AST_Node_Obj lu = &lhs;+ AST_Node_Obj ru = &rhs;++ Expression::Concrete_Type l_type = lhs->concrete_type();+ Expression::Concrete_Type r_type = rhs->concrete_type();++ // Is one of the operands an interpolant?+ String_Schema_Obj s1 = SASS_MEMORY_CAST(String_Schema, b->left());+ String_Schema_Obj s2 = SASS_MEMORY_CAST(String_Schema, b->right());+ Binary_Expression_Obj b1 = SASS_MEMORY_CAST(Binary_Expression, b->left());+ Binary_Expression_Obj b2 = SASS_MEMORY_CAST(Binary_Expression, b->right());++ bool schema_op = false;++ bool force_delay = (s2 && s2->is_left_interpolant()) ||+ (s1 && s1->is_right_interpolant()) ||+ (b1 && b1->is_right_interpolant()) ||+ (b2 && b2->is_left_interpolant());++ if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay)+ {+ if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB ||+ op_type == Sass_OP::EQ) {+ // If possible upgrade LHS to a number (for number to string compare)+ if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, lhs)) {+ std::string value(str->value());+ const char* start = value.c_str();+ if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) {+ Textual_Obj l = SASS_MEMORY_NEW(Textual, b->pstate(), Textual::DIMENSION, str->value());+ lhs = l->perform(this);+ }+ }+ // If possible upgrade RHS to a number (for string to number compare)+ if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, rhs)) {+ std::string value(str->value());+ const char* start = value.c_str();+ if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) {+ Textual_Obj r = SASS_MEMORY_NEW(Textual, b->pstate(), Textual::DIMENSION, str->value());+ rhs = r->perform(this);+ }+ }+ }++ To_Value to_value(ctx);+ Value_Obj v_l = dynamic_cast<Value_Ptr>(lhs->perform(&to_value));+ Value_Obj v_r = dynamic_cast<Value_Ptr>(rhs->perform(&to_value));+ l_type = lhs->concrete_type();+ r_type = rhs->concrete_type();++ if (s2 && s2->has_interpolants() && s2->length()) {+ Textual_Obj front = SASS_MEMORY_CAST(Textual, s2->elements().front());+ if (front && !front->is_interpolant())+ {+ // XXX: this is never hit via spec tests+ schema_op = true;+ rhs = front->perform(this);+ }+ }++ if (force_delay) {+ std::string str("");+ str += v_l->to_string(ctx.c_options);+ if (b->op().ws_before) str += " ";+ str += b->separator();+ if (b->op().ws_after) str += " ";+ str += v_r->to_string(ctx.c_options);+ String_Constant_Ptr val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str);+ val->is_interpolant(b->left()->has_interpolant());+ return val;+ }+ }++ // see if it's a relational expression+ try {+ switch(op_type) {+ case Sass_OP::EQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), eq(lhs, rhs));+ case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), !eq(lhs, rhs));+ case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs));+ case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gte"));+ case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lt"));+ case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs));+ default: break;+ }+ }+ catch (Exception::OperationError& err)+ {+ // throw Exception::Base(b->pstate(), err.what());+ throw Exception::SassValueError(b->pstate(), err);+ }++ l_type = lhs->concrete_type();+ r_type = rhs->concrete_type();++ // ToDo: throw error in op functions+ // ToDo: then catch and re-throw them+ Expression_Obj rv = 0;+ try {+ ParserState pstate(b->pstate());+ if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {+ Number_Ptr l_n = SASS_MEMORY_CAST(Number, lhs);+ Number_Ptr r_n = SASS_MEMORY_CAST(Number, rhs);+ rv = op_numbers(op_type, *l_n, *r_n, ctx.c_options, &pstate);+ }+ else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {+ Number_Ptr l_n = SASS_MEMORY_CAST(Number, lhs);+ Color_Ptr r_c = SASS_MEMORY_CAST(Color, rhs);+ rv = op_number_color(op_type, *l_n, *r_c, ctx.c_options, &pstate);+ }+ else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {+ Color_Ptr l_c = SASS_MEMORY_CAST(Color, lhs);+ Number_Ptr r_n = SASS_MEMORY_CAST(Number, rhs);+ rv = op_color_number(op_type, *l_c, *r_n, ctx.c_options, &pstate);+ }+ else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {+ Color_Ptr l_c = SASS_MEMORY_CAST(Color, lhs);+ Color_Ptr r_c = SASS_MEMORY_CAST(Color, rhs);+ rv = op_colors(op_type, *l_c, *r_c, ctx.c_options, &pstate);+ }+ else {+ To_Value to_value(ctx);+ // this will leak if perform does not return a value!+ Value_Obj v_l = SASS_MEMORY_CAST_PTR(Value, lhs->perform(&to_value));+ Value_Obj v_r = SASS_MEMORY_CAST_PTR(Value, rhs->perform(&to_value));+ bool interpolant = b->is_right_interpolant() ||+ b->is_left_interpolant() ||+ b->is_interpolant();+ if (op_type == Sass_OP::SUB) interpolant = false;+ // if (op_type == Sass_OP::DIV) interpolant = true;+ // check for type violations+ if (l_type == Expression::MAP) {+ throw Exception::InvalidValue(*v_l);+ }+ if (r_type == Expression::MAP) {+ throw Exception::InvalidValue(*v_r);+ }+ Value_Ptr ex = op_strings(b->op(), *v_l, *v_r, ctx.c_options, &pstate, !interpolant); // pass true to compress+ if (String_Constant_Ptr str = dynamic_cast<String_Constant_Ptr>(ex))+ {+ if (str->concrete_type() == Expression::STRING)+ {+ String_Constant_Ptr lstr = SASS_MEMORY_CAST(String_Constant, lhs);+ String_Constant_Ptr rstr = SASS_MEMORY_CAST(String_Constant, rhs);+ if (op_type != Sass_OP::SUB) {+ if (String_Constant_Ptr org = lstr ? lstr : rstr)+ { str->quote_mark(org->quote_mark()); }+ }+ }+ }+ ex->is_interpolant(b->is_interpolant());+ rv = ex;+ }+ }+ catch (Exception::OperationError& err)+ {+ // throw Exception::Base(b->pstate(), err.what());+ throw Exception::SassValueError(b->pstate(), err);+ }++ if (rv) {+ if (schema_op) {+ // XXX: this is never hit via spec tests+ (*s2)[0] = rv;+ rv = s2->perform(this);+ }+ }++ return rv.detach();++ }++ Expression_Ptr Eval::operator()(Unary_Expression_Ptr u)+ {+ Expression_Obj operand = u->operand()->perform(this);+ if (u->type() == Unary_Expression::NOT) {+ Boolean_Ptr result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);+ result->value(!result->value());+ return result;+ }+ else if (operand->concrete_type() == Expression::NUMBER) {+ Number_Ptr result = SASS_MEMORY_NEW(Number, static_cast<Number_Ptr>(&operand));+ result->value(u->type() == Unary_Expression::MINUS+ ? -result->value()+ : result->value());+ return result;+ }+ else {+ // Special cases: +/- variables which evaluate to null ouput just +/-,+ // but +/- null itself outputs the string+ if (operand->concrete_type() == Expression::NULL_VAL && SASS_MEMORY_CAST(Variable, u->operand())) {+ u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), ""));+ }+ // Never apply unary opertions on colors @see #2140+ else if (operand->concrete_type() == Expression::COLOR) {+ Color_Ptr c = dynamic_cast<Color_Ptr>(&operand);++ // Use the color name if this was eval with one+ if (c->disp().length() > 0) {+ operand = SASS_MEMORY_NEW(String_Constant, operand->pstate(), c->disp());+ u->operand(operand);+ }+ }+ else {+ u->operand(operand);+ }++ String_Constant_Ptr result = SASS_MEMORY_NEW(String_Quoted,+ u->pstate(),+ u->inspect());+ return result;+ }+ // unreachable+ return u;+ }++ Expression_Ptr Eval::operator()(Function_Call_Ptr c)+ {+ if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {+ // XXX: this is never hit via spec tests+ std::ostringstream stm;+ stm << "Stack depth exceeded max of " << Constants::MaxCallStack;+ error(stm.str(), c->pstate(), backtrace());+ }+ std::string name(Util::normalize_underscores(c->name()));+ std::string full_name(name + "[f]");+ // we make a clone here, need to implement that further+ Arguments_Obj args = c->arguments();++ Env* env = environment();+ if (!env->has(full_name) || (!c->via_call() && Prelexer::re_special_fun(name.c_str()))) {+ if (!env->has("*[f]")) {+ for (Argument_Obj arg : args->elements()) {+ if (List_Obj ls = SASS_MEMORY_CAST(List, arg->value())) {+ if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate());+ }+ }+ args = SASS_MEMORY_CAST_PTR(Arguments, args->perform(this));+ Function_Call_Obj lit = SASS_MEMORY_NEW(Function_Call,+ c->pstate(),+ c->name(),+ &args);+ if (args->has_named_arguments()) {+ error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());+ }+ String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted,+ c->pstate(),+ lit->to_string(ctx.c_options));+ str->is_interpolant(c->is_interpolant());+ return str;+ } else {+ // call generic function+ full_name = "*[f]";+ }+ }++ // further delay for calls+ if (full_name != "call[f]") {+ args->set_delayed(false); // verified+ }+ if (full_name != "if[f]") {+ args = SASS_MEMORY_CAST_PTR(Arguments, args->perform(this));+ }+ Definition_Ptr def = SASS_MEMORY_CAST(Definition, (*env)[full_name]);++ if (def->is_overload_stub()) {+ std::stringstream ss;+ size_t L = args->length();+ // account for rest arguments+ if (args->has_rest_argument() && args->length() > 0) {+ // get the rest arguments list+ List_Ptr rest = SASS_MEMORY_CAST(List, args->last()->value());+ // arguments before rest argument plus rest+ if (rest) L += rest->length() - 1;+ }+ ss << full_name << L;+ full_name = ss.str();+ std::string resolved_name(full_name);+ if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());+ def = SASS_MEMORY_CAST(Definition, (*env)[resolved_name]);+ }++ Expression_Obj result = c;+ Block_Obj body = def->block();+ Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();++ Parameters_Obj params = def->parameters();+ Env fn_env(def->environment());+ exp.env_stack.push_back(&fn_env);++ if (func || body) {+ bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+ Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");+ exp.backtrace_stack.push_back(&here);+ // eval the body if user-defined or special, invoke underlying CPP function if native+ if (body /* && !Prelexer::re_special_fun(name.c_str()) */) {+ result = body->perform(this);+ }+ else if (func) {+ result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace(), exp.selector_stack);+ }+ if (!result) {+ error(std::string("Function ") + c->name() + " did not return a value", c->pstate());+ }+ exp.backtrace_stack.pop_back();+ }++ // else if it's a user-defined c function+ // convert call into C-API compatible form+ else if (c_function) {+ Sass_Function_Fn c_func = sass_function_get_function(c_function);+ if (full_name == "*[f]") {+ String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), c->name());+ Arguments_Obj new_args = SASS_MEMORY_NEW(Arguments, c->pstate());+ new_args->append(SASS_MEMORY_NEW(Argument, c->pstate(), &str));+ new_args->concat(&args);+ args = new_args;+ }++ // populates env with default values for params+ std::string ff(c->name());+ bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);++ Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");+ exp.backtrace_stack.push_back(&here);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA);+ for(size_t i = 0; i < params->length(); i++) {+ Parameter_Obj param = params->at(i);+ std::string key = param->name();+ AST_Node_Obj node = fn_env.get_local(key);+ Expression_Obj arg = SASS_MEMORY_CAST(Expression, node);+ sass_list_set_value(c_args, i, arg->perform(&to_c));+ }+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ if (sass_value_get_tag(c_val) == SASS_ERROR) {+ error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace());+ } else if (sass_value_get_tag(c_val) == SASS_WARNING) {+ error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace());+ }+ result = cval_to_astnode(c_val, backtrace(), c->pstate());++ exp.backtrace_stack.pop_back();+ sass_delete_value(c_args);+ if (c_val != c_args)+ sass_delete_value(c_val);+ }++ // link back to function definition+ // only do this for custom functions+ if (result->pstate().file == std::string::npos)+ result->pstate(c->pstate());++ result = result->perform(this);+ result->is_interpolant(c->is_interpolant());+ exp.env_stack.pop_back();+ return result.detach();+ }++ Expression_Ptr Eval::operator()(Function_Call_Schema_Ptr s)+ {+ Expression_Ptr evaluated_name = s->name()->perform(this);+ Expression_Ptr evaluated_args = s->arguments()->perform(this);+ String_Schema_Obj ss = SASS_MEMORY_NEW(String_Schema, s->pstate(), 2);+ ss->append(evaluated_name);+ ss->append(evaluated_args);+ return ss->perform(this);+ }++ Expression_Ptr Eval::operator()(Variable_Ptr v)+ {+ std::string name(v->name());+ Expression_Obj value = 0;+ Env* env = environment();+ if (env->has(name)) {+ value = SASS_MEMORY_CAST(Expression, (*env)[name]);+ }+ else error("Undefined variable: \"" + v->name() + "\".", v->pstate());+ if (typeid(*value) == typeid(Argument)) {+ value = SASS_MEMORY_CAST(Argument, value)->value();+ }++ // behave according to as ruby sass (add leading zero)+ if (Number_Ptr nr = SASS_MEMORY_CAST(Number, value)) {+ nr->zero(true);+ }++ value->is_interpolant(v->is_interpolant());+ if (force) value->is_expanded(false);+ value->set_delayed(false); // verified+ value = value->perform(this);+ if(!force) (*env)[name] = &value;+ return value.detach();+ }++ Expression_Ptr Eval::operator()(Textual_Ptr t)+ {+ using Prelexer::number;+ Expression_Obj result = 0;+ size_t L = t->value().length();+ bool zero = !( (L > 0 && t->value().substr(0, 1) == ".") ||+ (L > 1 && t->value().substr(0, 2) == "0.") ||+ (L > 1 && t->value().substr(0, 2) == "-.") ||+ (L > 2 && t->value().substr(0, 3) == "-0.")+ );++ const std::string& text = t->value();+ size_t num_pos = text.find_first_not_of(" \n\r\t");+ if (num_pos == std::string::npos) num_pos = text.length();+ size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);+ if (unit_pos == std::string::npos) unit_pos = text.length();+ const std::string& num = text.substr(num_pos, unit_pos - num_pos);++ switch (t->type())+ {+ case Textual::NUMBER:+ result = SASS_MEMORY_NEW(Number,+ t->pstate(),+ sass_atof(num.c_str()),+ "",+ zero);+ break;+ case Textual::PERCENTAGE:+ result = SASS_MEMORY_NEW(Number,+ t->pstate(),+ sass_atof(num.c_str()),+ "%",+ true);+ break;+ case Textual::DIMENSION:+ result = SASS_MEMORY_NEW(Number,+ t->pstate(),+ sass_atof(num.c_str()),+ Token(number(text.c_str())),+ zero);+ break;+ case Textual::HEX: {+ if (t->value().substr(0, 1) != "#") {+ result = SASS_MEMORY_NEW(String_Quoted, t->pstate(), t->value());+ break;+ }+ std::string hext(t->value().substr(1)); // chop off the '#'+ if (hext.length() == 6) {+ std::string r(hext.substr(0,2));+ std::string g(hext.substr(2,2));+ std::string b(hext.substr(4,2));+ result = SASS_MEMORY_NEW(Color,+ t->pstate(),+ static_cast<double>(strtol(r.c_str(), NULL, 16)),+ static_cast<double>(strtol(g.c_str(), NULL, 16)),+ static_cast<double>(strtol(b.c_str(), NULL, 16)),+ 1, // alpha channel+ t->value());+ }+ else {+ result = SASS_MEMORY_NEW(Color,+ t->pstate(),+ static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),+ static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),+ static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),+ 1, // alpha channel+ t->value());+ }+ } break;+ }+ result->is_interpolant(t->is_interpolant());+ return result.detach();+ }++ Expression_Ptr Eval::operator()(Color_Ptr c)+ {+ return c;+ }++ Expression_Ptr Eval::operator()(Number_Ptr n)+ {+ return n;+ }++ Expression_Ptr Eval::operator()(Boolean_Ptr b)+ {+ return b;+ }++ void Eval::interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl) {++ bool needs_closing_brace = false;++ if (Arguments_Ptr args = SASS_MEMORY_CAST(Arguments, ex)) {+ List_Ptr ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA);+ for(auto arg : args->elements()) {+ ll->append(arg->value());+ }+ ll->is_interpolant(args->is_interpolant());+ needs_closing_brace = true;+ res += "(";+ ex = ll;+ }+ if (Number_Ptr nr = SASS_MEMORY_CAST(Number, ex)) {+ if (!nr->is_valid_css_unit()) {+ throw Exception::InvalidValue(*nr);+ }+ }+ if (Argument_Ptr arg = SASS_MEMORY_CAST(Argument, ex)) {+ ex = &arg->value();+ }+ if (String_Quoted_Ptr sq = SASS_MEMORY_CAST(String_Quoted, ex)) {+ if (was_itpl) {+ bool was_interpolant = ex->is_interpolant();+ ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value());+ ex->is_interpolant(was_interpolant);+ }+ }++ if (SASS_MEMORY_CAST(Null, ex)) { return; }++ // parent selector needs another go+ if (SASS_MEMORY_CAST(Parent_Selector, ex)) {+ // XXX: this is never hit via spec tests+ ex = ex->perform(this);+ }++ if (List_Ptr l = SASS_MEMORY_CAST(List, ex)) {+ List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator());+ // this fixes an issue with bourbon sample, not really sure why+ // if (l->size() && dynamic_cast<Null_Ptr>((*l)[0])) { res += ""; }+ for(Expression_Obj item : *l) {+ item->is_interpolant(l->is_interpolant());+ std::string rl(""); interpolation(ctx, rl, &item, into_quotes, l->is_interpolant());+ bool is_null = dynamic_cast<Null_Ptr>(&item) != 0; // rl != ""+ if (!is_null) ll->append(SASS_MEMORY_NEW(String_Quoted, item->pstate(), rl));+ }+ // Check indicates that we probably should not get a list+ // here. Normally single list items are already unwrapped.+ if (l->size() > 1) {+ // string_to_output would fail "#{'_\a' '_\a'}";+ std::string str(ll->to_string(ctx.c_options));+ newline_to_space(str); // replace directly+ res += str; // append to result string+ } else {+ res += (ll->to_string(ctx.c_options));+ }+ ll->is_interpolant(l->is_interpolant());+ }++ // Value+ // Textual+ // Function_Call+ // Selector_List+ // String_Quoted+ // String_Constant+ // Parent_Selector+ // Binary_Expression+ else {+ // ex = ex->perform(this);+ if (into_quotes && ex->is_interpolant()) {+ res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : "");+ } else {+ res += ex ? ex->to_string(ctx.c_options) : "";+ }+ }++ if (needs_closing_brace) res += ")";++ }++ Expression_Ptr Eval::operator()(String_Schema_Ptr s)+ {+ size_t L = s->length();+ bool into_quotes = false;+ if (L > 1) {+ if (!SASS_MEMORY_CAST(String_Quoted, (*s)[0]) && !SASS_MEMORY_CAST(String_Quoted, (*s)[L - 1])) {+ if (String_Constant_Ptr l = SASS_MEMORY_CAST(String_Constant, (*s)[0])) {+ if (String_Constant_Ptr r = SASS_MEMORY_CAST(String_Constant, (*s)[L - 1])) {+ if (r->value().size() > 0) {+ if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true;+ if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;+ }+ }+ }+ }+ }+ bool was_quoted = false;+ bool was_interpolant = false;+ std::string res("");+ for (size_t i = 0; i < L; ++i) {+ bool is_quoted = SASS_MEMORY_CAST(String_Quoted, (*s)[i]) != NULL;+ if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; }+ else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; }+ Expression_Obj ex = (*s)[i]->perform(this);+ interpolation(ctx, res, ex, into_quotes, ex->is_interpolant());+ was_quoted = SASS_MEMORY_CAST(String_Quoted, (*s)[i]) != NULL;+ was_interpolant = (*s)[i]->is_interpolant();++ }+ if (!s->is_interpolant()) {+ if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(Null, s->pstate());+ return SASS_MEMORY_NEW(String_Constant, s->pstate(), res);+ }+ // string schema seems to have a special unquoting behavior (also handles "nested" quotes)+ String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false);+ // if (s->is_interpolant()) str->quote_mark(0);+ // String_Constant_Ptr str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);+ if (str->quote_mark()) str->quote_mark('*');+ else if (!is_in_comment) str->value(string_to_output(str->value()));+ str->is_interpolant(s->is_interpolant());+ return str.detach();+ }+++ Expression_Ptr Eval::operator()(String_Constant_Ptr s)+ {+ if (!s->is_delayed() && name_to_color(s->value())) {+ Color_Ptr c = SASS_MEMORY_COPY(name_to_color(s->value())); // copy+ c->pstate(s->pstate());+ c->disp(s->value());+ c->is_delayed(true);+ return c;+ }+ return s;+ }++ Expression_Ptr Eval::operator()(String_Quoted_Ptr s)+ {+ String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), "");+ str->value(s->value());+ str->quote_mark(s->quote_mark());+ str->is_interpolant(s->is_interpolant());+ return str;+ }++ Expression_Ptr Eval::operator()(Supports_Operator_Ptr c)+ {+ Expression_Ptr left = c->left()->perform(this);+ Expression_Ptr right = c->right()->perform(this);+ Supports_Operator_Ptr cc = SASS_MEMORY_NEW(Supports_Operator,+ c->pstate(),+ dynamic_cast<Supports_Condition_Ptr>(left),+ dynamic_cast<Supports_Condition_Ptr>(right),+ c->operand());+ return cc;+ }++ Expression_Ptr Eval::operator()(Supports_Negation_Ptr c)+ {+ Expression_Ptr condition = c->condition()->perform(this);+ Supports_Negation_Ptr cc = SASS_MEMORY_NEW(Supports_Negation,+ c->pstate(),+ dynamic_cast<Supports_Condition_Ptr>(condition));+ return cc;+ }++ Expression_Ptr Eval::operator()(Supports_Declaration_Ptr c)+ {+ Expression_Ptr feature = c->feature()->perform(this);+ Expression_Ptr value = c->value()->perform(this);+ Supports_Declaration_Ptr cc = SASS_MEMORY_NEW(Supports_Declaration,+ c->pstate(),+ feature,+ value);+ return cc;+ }++ Expression_Ptr Eval::operator()(Supports_Interpolation_Ptr c)+ {+ Expression_Ptr value = c->value()->perform(this);+ Supports_Interpolation_Ptr cc = SASS_MEMORY_NEW(Supports_Interpolation,+ c->pstate(),+ value);+ return cc;+ }++ Expression_Ptr Eval::operator()(At_Root_Query_Ptr e)+ {+ Expression_Obj feature = e->feature();+ feature = (feature ? feature->perform(this) : 0);+ Expression_Obj value = e->value();+ value = (value ? value->perform(this) : 0);+ Expression_Ptr ee = SASS_MEMORY_NEW(At_Root_Query,+ e->pstate(),+ dynamic_cast<String_Ptr>(&feature),+ value);+ return ee;+ }++ Expression_Ptr Eval::operator()(Media_Query_Ptr q)+ {+ String_Obj t = q->media_type();+ t = static_cast<String_Ptr>(&t ? t->perform(this) : 0);+ Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query,+ q->pstate(),+ &t,+ q->length(),+ q->is_negated(),+ q->is_restricted());+ for (size_t i = 0, L = q->length(); i < L; ++i) {+ qq->append(static_cast<Media_Query_Expression_Ptr>((*q)[i]->perform(this)));+ }+ return qq.detach();+ }++ Expression_Ptr Eval::operator()(Media_Query_Expression_Ptr e)+ {+ Expression_Obj feature = e->feature();+ feature = (feature ? feature->perform(this) : 0);+ if (feature && SASS_MEMORY_CAST(String_Quoted, feature)) {+ feature = SASS_MEMORY_NEW(String_Quoted,+ feature->pstate(),+ SASS_MEMORY_CAST(String_Quoted, feature)->value());+ }+ Expression_Obj value = e->value();+ value = (value ? value->perform(this) : 0);+ if (value && SASS_MEMORY_CAST(String_Quoted, value)) {+ // XXX: this is never hit via spec tests+ value = SASS_MEMORY_NEW(String_Quoted,+ value->pstate(),+ SASS_MEMORY_CAST(String_Quoted, value)->value());+ }+ return SASS_MEMORY_NEW(Media_Query_Expression,+ e->pstate(),+ feature,+ value,+ e->is_interpolated());+ }++ Expression_Ptr Eval::operator()(Null_Ptr n)+ {+ return n;+ }++ Expression_Ptr Eval::operator()(Argument_Ptr a)+ {+ Expression_Obj val = a->value()->perform(this);+ bool is_rest_argument = a->is_rest_argument();+ bool is_keyword_argument = a->is_keyword_argument();++ if (a->is_rest_argument()) {+ if (val->concrete_type() == Expression::MAP) {+ is_rest_argument = false;+ is_keyword_argument = true;+ }+ else if(val->concrete_type() != Expression::LIST) {+ List_Obj wrapper = SASS_MEMORY_NEW(List,+ val->pstate(),+ 0,+ SASS_COMMA,+ true);+ wrapper->append(val);+ val = &wrapper;+ }+ }+ return SASS_MEMORY_NEW(Argument,+ a->pstate(),+ val,+ a->name(),+ is_rest_argument,+ is_keyword_argument);+ }++ Expression_Ptr Eval::operator()(Arguments_Ptr a)+ {+ Arguments_Obj aa = SASS_MEMORY_NEW(Arguments, a->pstate());+ if (a->length() == 0) return aa.detach();+ for (size_t i = 0, L = a->length(); i < L; ++i) {+ Expression_Obj rv = (*a)[i]->perform(this);+ Argument_Ptr arg = SASS_MEMORY_CAST(Argument, rv);+ if (!(arg->is_rest_argument() || arg->is_keyword_argument())) {+ aa->append(arg);+ }+ }++ if (a->has_rest_argument()) {+ Expression_Obj rest = a->get_rest_argument()->perform(this);+ Expression_Obj splat = SASS_MEMORY_CAST(Argument, rest)->value()->perform(this);++ Sass_Separator separator = SASS_COMMA;+ List_Ptr ls = SASS_MEMORY_CAST(List, splat);+ Map_Ptr ms = SASS_MEMORY_CAST(Map, splat);++ List_Obj arglist = SASS_MEMORY_NEW(List,+ splat->pstate(),+ 0,+ ls ? ls->separator() : separator,+ true);++ if (ls && ls->is_arglist()) {+ arglist->concat(ls);+ } else if (ms) {+ aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), ms, "", false, true));+ } else if (ls) {+ arglist->concat(ls);+ } else {+ arglist->append(splat);+ }+ if (arglist->length()) {+ aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), &arglist, "", true));+ }+ }++ if (a->has_keyword_argument()) {+ Expression_Obj rv = a->get_keyword_argument()->perform(this);+ Argument_Ptr rvarg = SASS_MEMORY_CAST(Argument, rv);+ Expression_Obj kwarg = rvarg->value()->perform(this);++ aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "", false, true));+ }+ return aa.detach();+ }++ Expression_Ptr Eval::operator()(Comment_Ptr c)+ {+ return 0;+ }++ inline Expression_Ptr Eval::fallback_impl(AST_Node_Ptr n)+ {+ return static_cast<Expression_Ptr>(n);+ }++ // All the binary helpers.++ bool Eval::eq(Expression_Obj lhs, Expression_Obj rhs)+ {+ // use compare operator from ast node+ return lhs && rhs && *lhs == *rhs;+ }++ bool Eval::lt(Expression_Obj lhs, Expression_Obj rhs, std::string op)+ {+ Number_Obj l = SASS_MEMORY_CAST(Number, lhs);+ Number_Obj r = SASS_MEMORY_CAST(Number, rhs);+ // use compare operator from ast node+ if (!l || !r) throw Exception::UndefinedOperation(&lhs, &rhs, op);+ // use compare operator from ast node+ return *l < *r;+ }++ Value_Ptr Eval::op_numbers(enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)+ {+ double lv = l.value();+ double rv = r.value();+ if (op == Sass_OP::DIV && rv == 0) {+ // XXX: this is never hit via spec tests+ return SASS_MEMORY_NEW(String_Quoted, pstate ? *pstate : l.pstate(), lv ? "Infinity" : "NaN");+ }+ if (op == Sass_OP::MOD && !rv) {+ // XXX: this is never hit via spec tests+ throw Exception::ZeroDivisionError(l, r);+ }++ Number tmp(&r); // copy+ bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;+ tmp.normalize(l.find_convertible_unit(), strict);+ std::string l_unit(l.unit());+ std::string r_unit(tmp.unit());+ Number_Obj v = SASS_MEMORY_COPY(&l); // copy+ v->pstate(pstate ? *pstate : l.pstate());+ if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {+ v->numerator_units() = r.numerator_units();+ v->denominator_units() = r.denominator_units();+ }++ if (op == Sass_OP::MUL) {+ v->value(ops[op](lv, rv));+ for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {+ v->numerator_units().push_back(r.numerator_units()[i]);+ }+ for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {+ v->denominator_units().push_back(r.denominator_units()[i]);+ }+ }+ else if (op == Sass_OP::DIV) {+ v->value(ops[op](lv, rv));+ for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) {+ v->denominator_units().push_back(r.numerator_units()[i]);+ }+ for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) {+ v->numerator_units().push_back(r.denominator_units()[i]);+ }+ } else {+ v->value(ops[op](lv, r.value() * r.convert_factor(l)));+ // v->normalize();+ return v.detach();++ v->value(ops[op](lv, tmp.value()));+ }+ v->normalize();+ return v.detach();+ }++ Value_Ptr Eval::op_number_color(enum Sass_OP op, const Number& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)+ {+ double lv = l.value();+ switch (op) {+ case Sass_OP::ADD:+ case Sass_OP::MUL: {+ return SASS_MEMORY_NEW(Color,+ pstate ? *pstate : l.pstate(),+ ops[op](lv, r.r()),+ ops[op](lv, r.g()),+ ops[op](lv, r.b()),+ r.a());+ } break;+ case Sass_OP::SUB:+ case Sass_OP::DIV: {+ std::string sep(op == Sass_OP::SUB ? "-" : "/");+ std::string color(r.to_string(opt));+ return SASS_MEMORY_NEW(String_Quoted,+ pstate ? *pstate : l.pstate(),+ l.to_string(opt)+ + sep+ + color);+ } break;+ case Sass_OP::MOD: {+ throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op));+ } break;+ default: break; // caller should ensure that we don't get here+ }+ // unreachable+ return NULL;+ }++ Value_Ptr Eval::op_color_number(enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)+ {+ double rv = r.value();+ if (op == Sass_OP::DIV && !rv) {+ // comparison of Fixnum with Float failed?+ throw Exception::ZeroDivisionError(l, r);+ }+ return SASS_MEMORY_NEW(Color,+ pstate ? *pstate : l.pstate(),+ ops[op](l.r(), rv),+ ops[op](l.g(), rv),+ ops[op](l.b(), rv),+ l.a());+ }++ Value_Ptr Eval::op_colors(enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)+ {+ if (l.a() != r.a()) {+ throw Exception::AlphaChannelsNotEqual(&l, &r, "+");+ }+ if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {+ // comparison of Fixnum with Float failed?+ throw Exception::ZeroDivisionError(l, r);+ }+ return SASS_MEMORY_NEW(Color,+ pstate ? *pstate : l.pstate(),+ ops[op](l.r(), r.r()),+ ops[op](l.g(), r.g()),+ ops[op](l.b(), r.b()),+ l.a());+ }++ Value_Ptr Eval::op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, ParserState* pstate, bool delayed)+ {+ Expression::Concrete_Type ltype = lhs.concrete_type();+ Expression::Concrete_Type rtype = rhs.concrete_type();+ enum Sass_OP op = operand.operand;++ String_Quoted_Ptr lqstr = dynamic_cast<String_Quoted_Ptr>(&lhs);+ String_Quoted_Ptr rqstr = dynamic_cast<String_Quoted_Ptr>(&rhs);++ std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));+ std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));++ if (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));+ if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));+ if (op == Sass_OP::MOD) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));+ if (op == Sass_OP::MUL) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));+ std::string sep;+ switch (op) {+ case Sass_OP::SUB: sep = "-"; break;+ case Sass_OP::DIV: sep = "/"; break;+ case Sass_OP::MUL: sep = "*"; break;+ case Sass_OP::MOD: sep = "%"; break;+ case Sass_OP::EQ: sep = "=="; break;+ case Sass_OP::NEQ: sep = "!="; break;+ case Sass_OP::LT: sep = "<"; break;+ case Sass_OP::GT: sep = ">"; break;+ case Sass_OP::LTE: sep = "<="; break;+ case Sass_OP::GTE: sep = ">="; break;+ default: break;+ }++ if ( (sep == "") /* &&+ (sep != "/" || !rqstr || !rqstr->quote_mark()) */+ ) {+ // create a new string that might be quoted on output (but do not unquote what we pass)+ return SASS_MEMORY_NEW(String_Quoted, pstate ? *pstate : lhs.pstate(), lstr + rstr, 0, false, true);+ }++ if (sep != "" && !delayed) {+ if (operand.ws_before) sep = " " + sep;+ if (operand.ws_after) sep = sep + " ";+ }++ if (op == Sass_OP::SUB || op == Sass_OP::DIV) {+ if (lqstr && lqstr->quote_mark()) lstr = quote(lstr);+ if (rqstr && rqstr->quote_mark()) rstr = quote(rstr);+ }++ return SASS_MEMORY_NEW(String_Constant, pstate ? *pstate : lhs.pstate(), lstr + sep + rstr);+ }++ Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate)+ {+ using std::strlen;+ using std::strcpy;+ Expression_Ptr e = NULL;+ switch (sass_value_get_tag(v)) {+ case SASS_BOOLEAN: {+ e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));+ } break;+ case SASS_NUMBER: {+ e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));+ } break;+ case SASS_COLOR: {+ e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));+ } break;+ case SASS_STRING: {+ if (sass_string_is_quoted(v))+ e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));+ else {+ e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));+ }+ } break;+ case SASS_LIST: {+ List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));+ for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {+ l->append(cval_to_astnode(sass_list_get_value(v, i), backtrace, pstate));+ }+ e = l;+ } break;+ case SASS_MAP: {+ Map_Ptr m = SASS_MEMORY_NEW(Map, pstate);+ for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {+ *m << std::make_pair(+ cval_to_astnode(sass_map_get_key(v, i), backtrace, pstate),+ cval_to_astnode(sass_map_get_value(v, i), backtrace, pstate));+ }+ e = m;+ } break;+ case SASS_NULL: {+ e = SASS_MEMORY_NEW(Null, pstate);+ } break;+ case SASS_ERROR: {+ error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace);+ } break;+ case SASS_WARNING: {+ error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);+ } break;+ }+ return e;+ }++ Selector_List_Ptr Eval::operator()(Selector_List_Ptr s)+ {+ std::vector<Selector_List_Obj> rv;+ Selector_List_Obj sl = SASS_MEMORY_NEW(Selector_List, s->pstate());+ sl->is_optional(s->is_optional());+ sl->media_block(s->media_block());+ sl->is_optional(s->is_optional());+ for (size_t i = 0, iL = s->length(); i < iL; ++i) {+ rv.push_back(operator()(&(*s)[i]));+ }++ // we should actually permutate parent first+ // but here we have permutated the selector first+ size_t round = 0;+ while (round != std::string::npos) {+ bool abort = true;+ for (size_t i = 0, iL = rv.size(); i < iL; ++i) {+ if (rv[i]->length() > round) {+ sl->append((*rv[i])[round]);+ abort = false;+ }+ }+ if (abort) {+ round = std::string::npos;+ } else {+ ++ round;+ }++ }+ return sl.detach();+ }+++ Selector_List_Ptr Eval::operator()(Complex_Selector_Ptr s)+ {+ bool implicit_parent = !exp.old_at_root_without_rule;+ return s->resolve_parent_refs(ctx, exp.selector_stack, implicit_parent);+ }++ // XXX: this is never hit via spec tests+ Attribute_Selector_Ptr Eval::operator()(Attribute_Selector_Ptr s)+ {+ String_Obj attr = s->value();+ if (attr) { attr = static_cast<String_Ptr>(attr->perform(this)); }+ Attribute_Selector_Ptr ss = SASS_MEMORY_COPY(s);+ ss->value(attr);+ return ss;+ }++ Selector_List_Ptr Eval::operator()(Selector_Schema_Ptr s)+ {+ // the parser will look for a brace to end the selector+ Expression_Obj sel = s->contents()->perform(this);+ std::string result_str(sel->to_string(ctx.c_options));+ result_str = unquote(Util::rtrim(result_str)) + "\n{";+ Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());+ p.last_media_block = s->media_block();+ Selector_List_Obj sl = p.parse_selector_list(exp.block_stack.back()->is_root());+ if (s->has_parent_ref()) sl->remove_parent_selectors();+ return operator()(&sl);+ }++ Expression_Ptr Eval::operator()(Parent_Selector_Ptr p)+ {+ if (Selector_List_Obj pr = selector()) {+ exp.selector_stack.pop_back();+ Selector_List_Obj rv = operator()(&pr);+ exp.selector_stack.push_back(rv);+ return rv.detach();+ } else {+ return SASS_MEMORY_NEW(Null, p->pstate()); } }
libsass/src/eval.hpp view
@@ -1,106 +1,108 @@ #ifndef SASS_EVAL_H #define SASS_EVAL_H +#include "ast.hpp" #include "context.hpp" #include "listize.hpp" #include "operation.hpp"+#include "environment.hpp" namespace Sass { class Expand; class Context;- class Listize; - class Eval : public Operation_CRTP<Expression*, Eval> {+ class Eval : public Operation_CRTP<Expression_Ptr, Eval> { private:- Expression* fallback_impl(AST_Node* n);+ Expression_Ptr fallback_impl(AST_Node_Ptr n); public: Expand& exp; Context& ctx;- Listize listize; Eval(Expand& exp);- virtual ~Eval();+ ~Eval(); + bool force;+ bool is_in_comment;+ Env* environment();- Context& context();- Selector_List* selector(); Backtrace* backtrace();-- using Operation<Expression*>::operator();+ Selector_List_Obj selector(); // for evaluating function bodies- Expression* operator()(Block*);- Expression* operator()(Assignment*);- Expression* operator()(If*);- Expression* operator()(For*);- Expression* operator()(Each*);- Expression* operator()(While*);- Expression* operator()(Return*);- Expression* operator()(Warning*);- Expression* operator()(Error*);- Expression* operator()(Debug*);+ Expression_Ptr operator()(Block_Ptr);+ Expression_Ptr operator()(Assignment_Ptr);+ Expression_Ptr operator()(If_Ptr);+ Expression_Ptr operator()(For_Ptr);+ Expression_Ptr operator()(Each_Ptr);+ Expression_Ptr operator()(While_Ptr);+ Expression_Ptr operator()(Return_Ptr);+ Expression_Ptr operator()(Warning_Ptr);+ Expression_Ptr operator()(Error_Ptr);+ Expression_Ptr operator()(Debug_Ptr); - Expression* operator()(List*);- Expression* operator()(Map*);- Expression* operator()(Binary_Expression*);- Expression* operator()(Unary_Expression*);- Expression* operator()(Function_Call*);- Expression* operator()(Function_Call_Schema*);- Expression* operator()(Variable*);- Expression* operator()(Textual*);- Expression* operator()(Number*);- Expression* operator()(Boolean*);- Expression* operator()(String_Schema*);- Expression* operator()(String_Quoted*);- Expression* operator()(String_Constant*);- // Expression* operator()(Selector_List*);- Expression* operator()(Media_Query*);- Expression* operator()(Media_Query_Expression*);- Expression* operator()(At_Root_Expression*);- Expression* operator()(Supports_Operator*);- Expression* operator()(Supports_Negation*);- Expression* operator()(Supports_Declaration*);- Expression* operator()(Supports_Interpolation*);- Expression* operator()(Null*);- Expression* operator()(Argument*);- Expression* operator()(Arguments*);- Expression* operator()(Comment*);+ Expression_Ptr operator()(List_Ptr);+ Expression_Ptr operator()(Map_Ptr);+ Expression_Ptr operator()(Binary_Expression_Ptr);+ Expression_Ptr operator()(Unary_Expression_Ptr);+ Expression_Ptr operator()(Function_Call_Ptr);+ Expression_Ptr operator()(Function_Call_Schema_Ptr);+ Expression_Ptr operator()(Variable_Ptr);+ Expression_Ptr operator()(Textual_Ptr);+ Expression_Ptr operator()(Number_Ptr);+ Expression_Ptr operator()(Color_Ptr);+ Expression_Ptr operator()(Boolean_Ptr);+ Expression_Ptr operator()(String_Schema_Ptr);+ Expression_Ptr operator()(String_Quoted_Ptr);+ Expression_Ptr operator()(String_Constant_Ptr);+ // Expression_Ptr operator()(Selector_List_Ptr);+ Expression_Ptr operator()(Media_Query_Ptr);+ Expression_Ptr operator()(Media_Query_Expression_Ptr);+ Expression_Ptr operator()(At_Root_Query_Ptr);+ Expression_Ptr operator()(Supports_Operator_Ptr);+ Expression_Ptr operator()(Supports_Negation_Ptr);+ Expression_Ptr operator()(Supports_Declaration_Ptr);+ Expression_Ptr operator()(Supports_Interpolation_Ptr);+ Expression_Ptr operator()(Null_Ptr);+ Expression_Ptr operator()(Argument_Ptr);+ Expression_Ptr operator()(Arguments_Ptr);+ Expression_Ptr operator()(Comment_Ptr); // these will return selectors- Selector_List* operator()(Selector_List*);- Selector_List* operator()(Complex_Selector*);- Attribute_Selector* operator()(Attribute_Selector*);+ Selector_List_Ptr operator()(Selector_List_Ptr);+ Selector_List_Ptr operator()(Complex_Selector_Ptr);+ Attribute_Selector_Ptr operator()(Attribute_Selector_Ptr); // they don't have any specific implementatio (yet)- Type_Selector* operator()(Type_Selector* s) { return s; };- Pseudo_Selector* operator()(Pseudo_Selector* s) { return s; };- Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; };- Selector_Qualifier* operator()(Selector_Qualifier* s) { return s; };- Selector_Placeholder* operator()(Selector_Placeholder* s) { return s; };+ Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; };+ Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; };+ Wrapped_Selector_Ptr operator()(Wrapped_Selector_Ptr s) { return s; };+ Class_Selector_Ptr operator()(Class_Selector_Ptr s) { return s; };+ Id_Selector_Ptr operator()(Id_Selector_Ptr s) { return s; };+ Placeholder_Selector_Ptr operator()(Placeholder_Selector_Ptr s) { return s; }; // actual evaluated selectors- Selector_List* operator()(Selector_Schema*);- Expression* operator()(Parent_Selector*);+ Selector_List_Ptr operator()(Selector_Schema_Ptr);+ Expression_Ptr operator()(Parent_Selector_Ptr); template <typename U>- Expression* fallback(U x) { return fallback_impl(x); }+ Expression_Ptr fallback(U x) { return fallback_impl(x); } // -- only need to define two comparisons, and the rest can be implemented in terms of them- static bool eq(Expression*, Expression*);- static bool lt(Expression*, Expression*);+ static bool eq(Expression_Obj, Expression_Obj);+ static bool lt(Expression_Obj, Expression_Obj, std::string op); // -- arithmetic on the combinations that matter- static Value* op_numbers(Memory_Manager&, enum Sass_OP, const Number&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0);- static Value* op_number_color(Memory_Manager&, enum Sass_OP, const Number&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0);- static Value* op_color_number(Memory_Manager&, enum Sass_OP, const Color&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0);- static Value* op_colors(Memory_Manager&, enum Sass_OP, const Color&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0);- static Value* op_strings(Memory_Manager&, enum Sass_OP, Value&, Value&, bool compressed = false, int precision = 5, ParserState* pstate = 0);+ static Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+ static Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+ static Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+ static Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0);+ static Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, ParserState* pstate = 0, bool interpolant = false); private:- std::string interpolation(Expression* s, bool into_quotes = false);+ void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false); }; - Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate = ParserState("[AST]"));+ Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate = ParserState("[AST]")); }
libsass/src/expand.cpp view
@@ -1,36 +1,41 @@-#ifdef _MSC_VER-#pragma warning(disable : 4503)-#endif-+#include "sass.hpp" #include <iostream> #include <typeinfo> +#include "ast.hpp" #include "expand.hpp" #include "bind.hpp" #include "eval.hpp"-#include "to_string.hpp" #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp" namespace Sass { - Expand::Expand(Context& ctx, Env* env, Backtrace* bt)+ // simple endless recursion protection+ const size_t maxRecursion = 500;++ Expand::Expand(Context& ctx, Env* env, Backtrace* bt, std::vector<Selector_List_Obj>* stack) : ctx(ctx), eval(Eval(*this)),+ recursions(0),+ in_keyframes(false),+ at_root_without_rule(false),+ old_at_root_without_rule(false), env_stack(std::vector<Env*>()),- block_stack(std::vector<Block*>()),- property_stack(std::vector<String*>()),- selector_stack(std::vector<Selector_List*>()),- backtrace_stack(std::vector<Backtrace*>()),- in_keyframes(false)+ block_stack(std::vector<Block_Ptr>()),+ call_stack(std::vector<AST_Node_Obj>()),+ selector_stack(std::vector<Selector_List_Obj>()),+ media_block_stack(std::vector<Media_Block_Ptr>()),+ backtrace_stack(std::vector<Backtrace*>()) { env_stack.push_back(0); env_stack.push_back(env); block_stack.push_back(0);- // import_stack.push_back(0);- property_stack.push_back(0);- selector_stack.push_back(0);+ call_stack.push_back(0);+ if (stack == NULL) { selector_stack.push_back(0); }+ else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); }+ media_block_stack.push_back(0); backtrace_stack.push_back(0); backtrace_stack.push_back(bt); }@@ -47,7 +52,7 @@ return 0; } - Selector_List* Expand::selector()+ Selector_List_Obj Expand::selector() { if (selector_stack.size() > 0) return selector_stack.back();@@ -62,161 +67,167 @@ } // blocks create new variable scopes- Statement* Expand::operator()(Block* b)+ Block_Ptr Expand::operator()(Block_Ptr b) { // create new local environment // set the current env as parent Env env(environment()); // copy the block object (add items later)- Block* bb = SASS_MEMORY_NEW(ctx.mem, Block,+ Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); // setup block and env stack- this->block_stack.push_back(bb);+ this->block_stack.push_back(&bb); this->env_stack.push_back(&env); // operate on block+ // this may throw up! this->append_block(b); // revert block and env stack this->block_stack.pop_back(); this->env_stack.pop_back(); // return copy- return bb;+ return bb.detach(); } - Statement* Expand::operator()(Ruleset* r)+ Statement_Ptr Expand::operator()(Ruleset_Ptr r) {- // reset when leaving scope+ LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule); if (in_keyframes) {- Keyframe_Rule* k = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, r->pstate(), r->block()->perform(this)->block());+ Block_Ptr bb = operator()(&r->block());+ Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb); if (r->selector()) { selector_stack.push_back(0);- k->selector(static_cast<Selector_List*>(r->selector()->perform(&eval)));+ k->name(SASS_MEMORY_CAST_PTR(Selector_List, r->selector()->perform(&eval))); selector_stack.pop_back(); }- return k;+ return k.detach(); } + // reset when leaving scope+ LOCAL_FLAG(at_root_without_rule, false);+ // do some special checks for the base level rules if (r->is_root()) {- if (Selector_List* selector_list = dynamic_cast<Selector_List*>(r->selector())) {- for (Complex_Selector* complex_selector : selector_list->elements()) {- Complex_Selector* tail = complex_selector;+ if (Selector_List_Ptr selector_list = SASS_MEMORY_CAST(Selector_List, r->selector())) {+ for (Complex_Selector_Obj complex_selector : selector_list->elements()) {+ Complex_Selector_Ptr tail = &complex_selector; while (tail) {- if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) {- if (dynamic_cast<Parent_Selector*>(header) == NULL) continue; // skip all others- To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string));+ if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {+ if (SASS_MEMORY_CAST(Parent_Selector, header) == NULL) continue; // skip all others+ std::string sel_str(complex_selector->to_string(ctx.c_options)); error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace()); }- tail = tail->tail();+ tail = &tail->tail(); } } } } - Expression* ex = r->selector()->perform(&eval);- Selector_List* sel = dynamic_cast<Selector_List*>(ex);+ Expression_Obj ex = 0;+ if (r->selector()) ex = r->selector()->perform(&eval);+ Selector_List_Obj sel = SASS_MEMORY_CAST(Selector_List, ex); if (sel == 0) throw std::runtime_error("Expanded null selector"); - selector_stack.push_back(sel);- Block* blk = r->block()->perform(this)->block();- Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset,+ if (sel->length() == 0 || sel->has_parent_ref()) {+ bool has_parent_selector = false;+ for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) {+ Selector_List_Obj ll = selector_stack.at(i);+ has_parent_selector = ll != 0 && ll->length() > 0;+ }+ if (sel->has_real_parent_ref() && !has_parent_selector) {+ error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), backtrace());+ }+ }++ selector_stack.push_back(&sel);+ Env env(environment());+ if (block_stack.back()->is_root()) {+ env_stack.push_back(&env);+ }+ sel->set_media_block(media_block_stack.back());+ Block_Obj blk = 0;+ if (r->block()) blk = operator()(&r->block());+ Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset, r->pstate(),- sel,+ &sel, blk); selector_stack.pop_back();- rr->tabs(r->tabs());-- return rr;- }-- Statement* Expand::operator()(Propset* p)- {- property_stack.push_back(p->property_fragment());- Block* expanded_block = p->block()->perform(this)->block();-- for (size_t i = 0, L = expanded_block->length(); i < L; ++i) {- Statement* stm = (*expanded_block)[i];- if (Declaration* dec = static_cast<Declaration*>(stm)) {- String_Schema* combined_prop = SASS_MEMORY_NEW(ctx.mem, String_Schema, p->pstate());- if (!property_stack.empty()) {- *combined_prop << property_stack.back()->perform(&eval)- << SASS_MEMORY_NEW(ctx.mem, String_Quoted,- p->pstate(), "-")- << dec->property(); // TODO: eval the prop into a string constant- }- else {- *combined_prop << dec->property();- }- dec->property(combined_prop);- *block_stack.back() << dec;- }- else if (typeid(*stm) == typeid(Comment)) {- // drop comments in propsets- }- else {- error("contents of namespaced properties must result in style declarations only", stm->pstate(), backtrace());- }+ if (block_stack.back()->is_root()) {+ env_stack.pop_back(); } - property_stack.pop_back();+ rr->is_root(r->is_root());+ rr->tabs(r->tabs()); - return 0;+ return rr; } - Statement* Expand::operator()(Supports_Block* f)+ Statement_Ptr Expand::operator()(Supports_Block_Ptr f) {- Expression* condition = f->condition()->perform(&eval);- Supports_Block* ff = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+ Expression_Obj condition = f->condition()->perform(&eval);+ Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block, f->pstate(),- static_cast<Supports_Condition*>(condition),- f->block()->perform(this)->block());- return ff;+ SASS_MEMORY_CAST(Supports_Condition, condition),+ operator()(&f->block()));+ return ff.detach(); } - Statement* Expand::operator()(Media_Block* m)+ Statement_Ptr Expand::operator()(Media_Block_Ptr m) {- To_String to_string(&ctx);- Expression* mq = m->media_queries()->perform(&eval);- mq = Parser::from_c_str(mq->perform(&to_string).c_str(), ctx, mq->pstate()).parse_media_queries();- Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+ media_block_stack.push_back(m);+ Expression_Obj mq = m->media_queries()->perform(&eval);+ std::string str_mq(mq->to_string(ctx.c_options));+ char* str = sass_copy_c_string(str_mq.c_str());+ ctx.strings.push_back(str);+ Parser p(Parser::from_c_str(str, ctx, mq->pstate()));+ mq = &p.parse_media_queries(); // re-assign now+ List_Obj ls = SASS_MEMORY_CAST_PTR(List, mq->perform(&eval));+ Block_Obj blk = operator()(&m->block());+ Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block, m->pstate(),- static_cast<List*>(mq),- m->block()->perform(this)->block(),+ ls,+ blk, 0);+ media_block_stack.pop_back(); mm->tabs(m->tabs()); return mm; } - Statement* Expand::operator()(At_Root_Block* a)+ Statement_Ptr Expand::operator()(At_Root_Block_Ptr a) {- Block* ab = a->block();- // if (ab) ab->is_root(true);- Expression* ae = a->expression();+ Block_Obj ab = a->block();+ Expression_Obj ae = &a->expression();+ if (ae) ae = ae->perform(&eval);- else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, a->pstate());- Block* bb = ab ? ab->perform(this)->block() : 0;- At_Root_Block* aa = SASS_MEMORY_NEW(ctx.mem, At_Root_Block,+ else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());++ LOCAL_FLAG(at_root_without_rule, true);+ LOCAL_FLAG(in_keyframes, false);++ ;++ Block_Obj bb = ab ? operator()(&ab) : NULL;+ At_Root_Block_Obj aa = SASS_MEMORY_NEW(At_Root_Block, a->pstate(), bb,- static_cast<At_Root_Expression*>(ae));- // aa->block()->is_root(true);- return aa;+ SASS_MEMORY_CAST(At_Root_Query, ae));+ return aa.detach(); } - Statement* Expand::operator()(At_Rule* a)+ Statement_Ptr Expand::operator()(Directive_Ptr a) { LOCAL_FLAG(in_keyframes, a->is_keyframes());- Block* ab = a->block();- Selector* as = a->selector();- Expression* av = a->value();+ Block_Ptr ab = &a->block();+ Selector_Ptr as = &a->selector();+ Expression_Ptr av = &a->value(); selector_stack.push_back(0); if (av) av = av->perform(&eval);- if (as) as = dynamic_cast<Selector*>(as->perform(&eval));+ if (as) as = SASS_MEMORY_CAST_PTR(Selector, as->perform(&eval)); selector_stack.pop_back();- Block* bb = ab ? ab->perform(this)->block() : 0;- At_Rule* aa = SASS_MEMORY_NEW(ctx.mem, At_Rule,+ Block_Ptr bb = ab ? operator()(ab) : NULL;+ Directive_Ptr aa = SASS_MEMORY_NEW(Directive, a->pstate(), a->keyword(), as,@@ -225,29 +236,40 @@ return aa; } - Statement* Expand::operator()(Declaration* d)+ Statement_Ptr Expand::operator()(Declaration_Ptr d) {- String* old_p = d->property();- String* new_p = static_cast<String*>(old_p->perform(&eval));- Expression* value = d->value()->perform(&eval);- if (!value || (value->is_invisible() && !d->is_important())) return 0;- Declaration* decl = SASS_MEMORY_NEW(ctx.mem, Declaration,+ Block_Obj ab = d->block();+ String_Obj old_p = d->property();+ Expression_Obj prop = old_p->perform(&eval);+ String_Obj new_p = SASS_MEMORY_CAST(String, prop);+ // we might get a color back+ if (!new_p) {+ std::string str(prop->to_string(ctx.c_options));+ new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str);+ }+ Expression_Obj value = d->value()->perform(&eval);+ Block_Obj bb = ab ? operator()(&ab) : NULL;+ if (!bb) {+ if (!value || (value->is_invisible() && !d->is_important())) return 0;+ }+ Declaration_Ptr decl = SASS_MEMORY_NEW(Declaration, d->pstate(), new_p, value,- d->is_important());+ d->is_important(),+ bb); decl->tabs(d->tabs()); return decl; } - Statement* Expand::operator()(Assignment* a)+ Statement_Ptr Expand::operator()(Assignment_Ptr a) { Env* env = environment(); std::string var(a->variable()); if (a->is_global()) { if (a->is_default()) { if (env->has_global(var)) {- Expression* e = dynamic_cast<Expression*>(env->get_global(var));+ Expression_Obj e = SASS_MEMORY_CAST(Expression, env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); }@@ -265,8 +287,8 @@ auto cur = env; while (cur && cur->is_lexical()) { if (cur->has_local(var)) {- if (AST_Node* node = cur->get_local(var)) {- Expression* e = dynamic_cast<Expression*>(node);+ if (AST_Node_Obj node = cur->get_local(var)) {+ Expression_Obj e = SASS_MEMORY_CAST(Expression, node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(&eval)); }@@ -281,8 +303,8 @@ throw std::runtime_error("Env not in sync"); } else if (env->has_global(var)) {- if (AST_Node* node = env->get_global(var)) {- Expression* e = dynamic_cast<Expression*>(node);+ if (AST_Node_Obj node = env->get_global(var)) {+ Expression_Obj e = SASS_MEMORY_CAST(Expression, node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); }@@ -301,23 +323,28 @@ return 0; } - Statement* Expand::operator()(Import* imp)+ Statement_Ptr Expand::operator()(Import_Ptr imp) {- Import* result = SASS_MEMORY_NEW(ctx.mem, Import, imp->pstate());- if (imp->media_queries() && imp->media_queries()->size()) {- Expression* ex = imp->media_queries()->perform(&eval);- result->media_queries(dynamic_cast<List*>(ex));+ Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate());+ if (imp->import_queries() && imp->import_queries()->size()) {+ Expression_Obj ex = imp->import_queries()->perform(&eval);+ result->import_queries(SASS_MEMORY_CAST(List, ex)); } for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { result->urls().push_back(imp->urls()[i]->perform(&eval)); } // all resources have been dropped for Input_Stubs // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {}- return result;+ return result.detach(); } - Statement* Expand::operator()(Import_Stub* i)+ Statement_Ptr Expand::operator()(Import_Stub_Ptr i) {+ // get parent node from call stack+ AST_Node_Obj parent = call_stack.back();+ if (SASS_MEMORY_CAST(Block, parent) == NULL) {+ error("Import directives may not be used within control directives or mixins.", i->pstate());+ } // we don't seem to need that actually afterall Sass_Import_Entry import = sass_make_import( i->imp_path().c_str(),@@ -326,66 +353,75 @@ ); ctx.import_stack.push_back(import); const std::string& abs_path(i->resource().abs_path);- append_block(ctx.sheets.at(abs_path).root);+ append_block(&ctx.sheets.at(abs_path).root); sass_delete_import(ctx.import_stack.back()); ctx.import_stack.pop_back(); return 0; } - Statement* Expand::operator()(Warning* w)+ Statement_Ptr Expand::operator()(Warning_Ptr w) { // eval handles this too, because warnings may occur in functions w->perform(&eval); return 0; } - Statement* Expand::operator()(Error* e)+ Statement_Ptr Expand::operator()(Error_Ptr e) { // eval handles this too, because errors may occur in functions e->perform(&eval); return 0; } - Statement* Expand::operator()(Debug* d)+ Statement_Ptr Expand::operator()(Debug_Ptr d) { // eval handles this too, because warnings may occur in functions d->perform(&eval); return 0; } - Statement* Expand::operator()(Comment* c)+ Statement_Ptr Expand::operator()(Comment_Ptr c) {+ eval.is_in_comment = true;+ Comment_Ptr rv = SASS_MEMORY_NEW(Comment, c->pstate(), SASS_MEMORY_CAST_PTR(String, c->text()->perform(&eval)), c->is_important());+ eval.is_in_comment = false; // TODO: eval the text, once we're parsing/storing it as a String_Schema- return SASS_MEMORY_NEW(ctx.mem, Comment, c->pstate(), static_cast<String*>(c->text()->perform(&eval)), c->is_important());+ return rv; } - Statement* Expand::operator()(If* i)+ Statement_Ptr Expand::operator()(If_Ptr i) {- if (*i->predicate()->perform(&eval)) {- append_block(i->block());+ Env env(environment(), true);+ env_stack.push_back(&env);+ call_stack.push_back(i);+ Expression_Obj rv = i->predicate()->perform(&eval);+ if (*rv) {+ append_block(&i->block()); } else {- Block* alt = i->alternative();+ Block_Ptr alt = &i->alternative(); if (alt) append_block(alt); }+ call_stack.pop_back();+ env_stack.pop_back(); return 0; } // For does not create a new env scope // But iteration vars are reset afterwards- Statement* Expand::operator()(For* f)+ Statement_Ptr Expand::operator()(For_Ptr f) { std::string variable(f->variable());- Expression* low = f->lower_bound()->perform(&eval);+ Expression_Obj low = f->lower_bound()->perform(&eval); if (low->concrete_type() != Expression::NUMBER) {- error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace());+ throw Exception::TypeMismatch(*low, "integer"); }- Expression* high = f->upper_bound()->perform(&eval);+ Expression_Obj high = f->upper_bound()->perform(&eval); if (high->concrete_type() != Expression::NUMBER) {- error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace());+ throw Exception::TypeMismatch(*high, "integer"); }- Number* sass_start = static_cast<Number*>(low);- Number* sass_end = static_cast<Number*>(high);+ Number_Obj sass_start = SASS_MEMORY_CAST(Number, low);+ Number_Obj sass_end = SASS_MEMORY_CAST(Number, high); // check if units are valid for sequence if (sass_start->unit() != sass_end->unit()) { std::stringstream msg; msg << "Incompatible units: '"@@ -396,18 +432,20 @@ double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment- Env* env = environment();- Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());- AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;- env->set_local(variable, it);- Block* body = f->block();+ Env env(environment(), true);+ env_stack.push_back(&env);+ call_stack.push_back(f);+ Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), start, sass_end->unit());+ env.set_local(variable, &it);+ Block_Ptr body = &f->block(); if (start < end) { if (f->is_inclusive()) ++end; for (double i = start; i < end; ++i) {+ it = SASS_MEMORY_COPY(it); it->value(i);- env->set_local(variable, it);+ env.set_local(variable, &it); append_block(body); } } else {@@ -415,131 +453,136 @@ for (double i = start; i > end; --i) {+ it = SASS_MEMORY_COPY(it); it->value(i);- env->set_local(variable, it);+ env.set_local(variable, &it); append_block(body); } }- // restore original environment- if (!old_var) env->del_local(variable);- else env->set_local(variable, old_var);+ call_stack.pop_back();+ env_stack.pop_back(); return 0; } // Eval does not create a new env scope // But iteration vars are reset afterwards- Statement* Expand::operator()(Each* e)+ Statement_Ptr Expand::operator()(Each_Ptr e) { std::vector<std::string> variables(e->variables());- Expression* expr = e->list()->perform(&eval);- List* list = 0;- Map* map = 0;+ Expression_Obj expr = e->list()->perform(&eval);+ List_Obj list = 0;+ Map_Obj map; if (expr->concrete_type() == Expression::MAP) {- map = static_cast<Map*>(expr);+ map = SASS_MEMORY_CAST(Map, expr); }+ else if (Selector_List_Ptr ls = SASS_MEMORY_CAST(Selector_List, expr)) {+ Listize listize;+ Expression_Obj rv = ls->perform(&listize);+ list = SASS_MEMORY_CAST(List, rv);+ } else if (expr->concrete_type() != Expression::LIST) {- list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);- *list << expr;+ list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);+ list->append(expr); } else {- list = static_cast<List*>(expr);+ list = SASS_MEMORY_CAST(List, expr); } // remember variables and then reset them- Env* env = environment();- std::vector<AST_Node*> old_vars(variables.size());- for (size_t i = 0, L = variables.size(); i < L; ++i) {- old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;- env->set_local(variables[i], 0);- }- Block* body = e->block();+ Env env(environment(), true);+ env_stack.push_back(&env);+ call_stack.push_back(e);+ Block_Ptr body = &e->block(); if (map) { for (auto key : map->keys()) {- Expression* k = key->perform(&eval);- Expression* v = map->at(key)->perform(&eval);+ Expression_Obj k = key->perform(&eval);+ Expression_Obj v = map->at(key)->perform(&eval); if (variables.size() == 1) {- List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE);- *variable << k;- *variable << v;- env->set_local(variables[0], variable);+ List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);+ variable->append(k);+ variable->append(v);+ env.set_local(variables[0], &variable); } else {- env->set_local(variables[0], k);- env->set_local(variables[1], v);+ env.set_local(variables[0], &k);+ env.set_local(variables[1], &v); } append_block(body); } } else {- bool arglist = list->is_arglist();+ // bool arglist = list->is_arglist();+ if (list->length() == 1 && SASS_MEMORY_CAST(Selector_List, list)) {+ list = SASS_MEMORY_CAST(List, list);+ } for (size_t i = 0, L = list->length(); i < L; ++i) {- Expression* e = (*list)[i];+ Expression_Obj e = list->at(i); // unwrap value if the expression is an argument- if (Argument* arg = dynamic_cast<Argument*>(e)) e = arg->value();+ if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, e)) e = arg->value(); // check if we got passed a list of args (investigate)- if (List* scalars = dynamic_cast<List*>(e)) {+ if (List_Obj scalars = SASS_MEMORY_CAST(List, e)) { if (variables.size() == 1) {- Expression* var = scalars;- if (arglist) var = (*scalars)[0];- env->set_local(variables[0], var);+ List_Obj var = scalars;+ // if (arglist) var = (*scalars)[0];+ env.set_local(variables[0], &var); } else { for (size_t j = 0, K = variables.size(); j < K; ++j) {- Expression* res = j >= scalars->length()- ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())+ Expression_Obj res = j >= scalars->length()+ ? SASS_MEMORY_NEW(Null, expr->pstate()) : (*scalars)[j]->perform(&eval);- env->set_local(variables[j], res);+ env.set_local(variables[j], &res); } } } else { if (variables.size() > 0) {- env->set_local(variables[0], e);+ env.set_local(variables.at(0), &e); for (size_t j = 1, K = variables.size(); j < K; ++j) {- Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());- env->set_local(variables[j], res);+ Expression_Obj res = SASS_MEMORY_NEW(Null, expr->pstate());+ env.set_local(variables[j], &res); } } } append_block(body); } }- // restore original environment- for (size_t j = 0, K = variables.size(); j < K; ++j) {- if(!old_vars[j]) env->del_local(variables[j]);- else env->set_local(variables[j], old_vars[j]);- }+ call_stack.pop_back();+ env_stack.pop_back(); return 0; } - Statement* Expand::operator()(While* w)+ Statement_Ptr Expand::operator()(While_Ptr w) {- Expression* pred = w->predicate();- Block* body = w->block();- while (*pred->perform(&eval)) {+ Expression_Obj pred = w->predicate();+ Block_Ptr body = &w->block();+ Env env(environment(), true);+ env_stack.push_back(&env);+ call_stack.push_back(w);+ Expression_Obj cond = pred->perform(&eval);+ while (*&cond) { append_block(body);+ cond = pred->perform(&eval); }+ call_stack.pop_back();+ env_stack.pop_back(); return 0; } - Statement* Expand::operator()(Return* r)+ Statement_Ptr Expand::operator()(Return_Ptr r) { error("@return may only be used within a function", r->pstate(), backtrace()); return 0; } - Statement* Expand::operator()(Extension* e)- {- To_String to_string(&ctx);- Selector_List* extender = dynamic_cast<Selector_List*>(selector());- if (!extender) return 0;- selector_stack.push_back(0); - if (Selector_List* selector_list = dynamic_cast<Selector_List*>(e->selector())) {- for (Complex_Selector* complex_selector : selector_list->elements()) {- Complex_Selector* tail = complex_selector;+ void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {++ if (Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, s)) {+ for (Complex_Selector_Obj complex_selector : sl->elements()) {+ Complex_Selector_Obj tail = complex_selector; while (tail) {- if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) {- if (dynamic_cast<Parent_Selector*>(header) == NULL) continue; // skip all others- To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string));+ if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {+ if (SASS_MEMORY_CAST(Parent_Selector, header) == NULL) continue; // skip all others+ std::string sel_str(complex_selector->to_string(ctx.c_options)); error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace()); } tail = tail->tail();@@ -547,52 +590,82 @@ } } - Selector_List* contextualized = dynamic_cast<Selector_List*>(e->selector()->perform(&eval));- if (contextualized == NULL) return 0;++ Selector_List_Obj contextualized = SASS_MEMORY_CAST_PTR(Selector_List, s->perform(&eval));+ if (contextualized == false) return; for (auto complex_sel : contextualized->elements()) {- Complex_Selector* c = complex_sel;+ Complex_Selector_Obj c = complex_sel; if (!c->head() || c->tail()) {- To_String to_string(&ctx); std::string sel_str(contextualized->perform(&to_string));+ std::string sel_str(contextualized->to_string(ctx.c_options)); error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace()); }- Compound_Selector* placeholder = c->head();- placeholder->is_optional(e->selector()->is_optional());+ Compound_Selector_Obj placeholder = c->head();+ if (contextualized->is_optional()) placeholder->is_optional(true); for (size_t i = 0, L = extender->length(); i < L; ++i) {- Complex_Selector* sel = (*extender)[i];+ Complex_Selector_Obj sel = (*extender)[i]; if (!(sel->head() && sel->head()->length() > 0 &&- dynamic_cast<Parent_Selector*>((*sel->head())[0])))+ SASS_MEMORY_CAST(Parent_Selector, (*sel->head())[0]))) {- Compound_Selector* hh = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, (*extender)[i]->pstate());+ Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate()); hh->media_block((*extender)[i]->media_block());- Complex_Selector* ssel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, (*extender)[i]->pstate());+ Complex_Selector_Obj ssel = SASS_MEMORY_NEW(Complex_Selector, (*extender)[i]->pstate()); ssel->media_block((*extender)[i]->media_block()); if (sel->has_line_feed()) ssel->has_line_feed(true);- Parent_Selector* ps = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, (*extender)[i]->pstate());+ Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate()); ps->media_block((*extender)[i]->media_block());- *hh << ps;+ hh->append(&ps); ssel->tail(sel); ssel->head(hh); sel = ssel; } // if (c->has_line_feed()) sel->has_line_feed(true);- ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder));+ ctx.subset_map.put(placeholder, std::make_pair(sel, placeholder)); } } - selector_stack.pop_back();+ } + Statement* Expand::operator()(Extension_Ptr e)+ {+ if (Selector_List_Obj extender = SASS_MEMORY_CAST(Selector_List, selector())) {+ Selector_Obj s = e->selector();+ Selector_List_Obj sl = NULL;+ // check if we already have a valid selector list+ if ((sl = SASS_MEMORY_CAST(Selector_List, s))) {}+ // convert selector schema to a selector list+ else if (Selector_Schema_Obj schema = SASS_MEMORY_CAST(Selector_Schema, s)) {+ if (schema->has_real_parent_ref()) {+ sl = eval(&schema);+ } else {+ selector_stack.push_back(0);+ sl = eval(&schema);+ sl->remove_parent_selectors();+ selector_stack.pop_back();+ }+ }+ // abort on invalid selector+ if (sl.isNull()) return NULL;+ for (Complex_Selector_Obj cs : sl->elements()) {+ if (!cs.isNull() && !cs->head().isNull()) {+ cs->head()->media_block(media_block_stack.back());+ }+ }+ selector_stack.push_back(0);+ expand_selector_list(&sl, extender);+ selector_stack.pop_back();+ } return 0; } - Statement* Expand::operator()(Definition* d)+ Statement_Ptr Expand::operator()(Definition_Ptr d) { Env* env = environment();- Definition* dd = SASS_MEMORY_NEW(ctx.mem, Definition, *d);+ Definition_Obj dd = SASS_MEMORY_COPY(d); env->local_frame()[d->name() +- (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;+ (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = ⅆ if (d->type() == Definition::FUNCTION && (- d->name() == "calc" ||+ Prelexer::calc_fn_call(d->name().c_str()) || d->name() == "element" || d->name() == "expression" || d->name() == "url"@@ -604,86 +677,113 @@ ); } - // set the static link so we can have lexical scoping dd->environment(env); return 0; } - Statement* Expand::operator()(Mixin_Call* c)+ Statement_Ptr Expand::operator()(Mixin_Call_Ptr c) {++ if (recursions > maxRecursion) {+ throw Exception::StackError(*c);+ }++ recursions ++;+ Env* env = environment(); std::string full_name(c->name() + "[m]"); if (!env->has(full_name)) { error("no mixin named " + c->name(), c->pstate(), backtrace()); }- Definition* def = static_cast<Definition*>((*env)[full_name]);- Block* body = def->block();- Parameters* params = def->parameters();+ Definition_Obj def = SASS_MEMORY_CAST(Definition, (*env)[full_name]);+ Block_Obj body = def->block();+ Parameters_Obj params = def->parameters(); if (c->block() && c->name() != "@content" && !body->has_content()) { error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace()); }- Arguments* args = static_cast<Arguments*>(c->arguments()- ->perform(&eval));+ Expression_Obj rv = c->arguments()->perform(&eval);+ Arguments_Obj args = SASS_MEMORY_CAST(Arguments, rv); Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`"); backtrace_stack.push_back(&new_bt); Env new_env(def->environment()); env_stack.push_back(&new_env); if (c->block()) { // represent mixin content blocks as thunks/closures- Definition* thunk = SASS_MEMORY_NEW(ctx.mem, Definition,+ Definition_Obj thunk = SASS_MEMORY_NEW(Definition, c->pstate(), "@content",- SASS_MEMORY_NEW(ctx.mem, Parameters, c->pstate()),+ SASS_MEMORY_NEW(Parameters, c->pstate()), c->block(), Definition::MIXIN); thunk->environment(env);- new_env.local_frame()["@content[m]"] = thunk;+ new_env.local_frame()["@content[m]"] = &thunk; } bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);- append_block(body);- backtrace_stack.pop_back();++ Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());+ Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);+++ block_stack.push_back(&trace_block);+ for (auto bb : body->elements()) {+ Statement_Obj ith = bb->perform(this);+ if (ith) trace->block()->append(ith);+ }+ block_stack.pop_back();+ env_stack.pop_back();- return 0;+ backtrace_stack.pop_back();++ recursions --;+ return trace.detach(); } - Statement* Expand::operator()(Content* c)+ Statement_Ptr Expand::operator()(Content_Ptr c) { Env* env = environment(); // convert @content directives into mixin calls to the underlying thunk if (!env->has("@content[m]")) return 0;+ if (block_stack.back()->is_root()) { selector_stack.push_back(0); }- Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call,++ Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, c->pstate(), "@content",- SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate()));- Statement* stm = call->perform(this);+ SASS_MEMORY_NEW(Arguments, c->pstate()));++ Trace_Obj trace = SASS_MEMORY_CAST_PTR(Trace, call->perform(this));+ if (block_stack.back()->is_root()) { selector_stack.pop_back(); }- return stm;++ return trace.detach(); } // produce an error if something is not implemented- inline Statement* Expand::fallback_impl(AST_Node* n)+ inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n) { std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();- String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), err);+ String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err); error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace());- return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg);+ return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), &msg); } // process and add to last block on stack- inline void Expand::append_block(Block* b)+ inline void Expand::append_block(Block_Ptr b) {+ if (b->is_root()) call_stack.push_back(b); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* ith = (*b)[i]->perform(this);- if (ith) *block_stack.back() << ith;+ Statement_Ptr stm = &b->at(i);+ Statement_Obj ith = stm->perform(this);+ if (ith) block_stack.back()->append(ith); }+ if (b->is_root()) call_stack.pop_back(); } }
libsass/src/expand.hpp view
@@ -13,65 +13,69 @@ class Listize; class Context; class Eval;- typedef Environment<AST_Node*> Env;+ typedef Environment<AST_Node_Obj> Env; struct Backtrace; - class Expand : public Operation_CRTP<Statement*, Expand> {+ class Expand : public Operation_CRTP<Statement_Ptr, Expand> { public: Env* environment(); Context& context();- Selector_List* selector();+ Selector_List_Obj selector(); Backtrace* backtrace(); Context& ctx; Eval eval;+ size_t recursions;+ bool in_keyframes;+ bool at_root_without_rule;+ bool old_at_root_without_rule; // it's easier to work with vectors- std::vector<Env*> env_stack;- std::vector<Block*> block_stack;- std::vector<String*> property_stack;- std::vector<Selector_List*> selector_stack;- std::vector<Backtrace*>backtrace_stack;- bool in_keyframes;+ std::vector<Env*> env_stack;+ std::vector<Block_Ptr> block_stack;+ std::vector<AST_Node_Obj> call_stack;+ std::vector<Selector_List_Obj> selector_stack;+ std::vector<Media_Block_Ptr> media_block_stack;+ std::vector<Backtrace*> backtrace_stack; - Statement* fallback_impl(AST_Node* n);+ Statement_Ptr fallback_impl(AST_Node_Ptr n); - public:- Expand(Context&, Env*, Backtrace*);- virtual ~Expand() { }+ private:+ void expand_selector_list(Selector_Obj, Selector_List_Obj extender); - using Operation<Statement*>::operator();+ public:+ Expand(Context&, Env*, Backtrace*, std::vector<Selector_List_Obj>* stack = NULL);+ ~Expand() { } - Statement* operator()(Block*);- Statement* operator()(Ruleset*);- Statement* operator()(Propset*);- Statement* operator()(Media_Block*);- Statement* operator()(Supports_Block*);- Statement* operator()(At_Root_Block*);- Statement* operator()(At_Rule*);- Statement* operator()(Declaration*);- Statement* operator()(Assignment*);- Statement* operator()(Import*);- Statement* operator()(Import_Stub*);- Statement* operator()(Warning*);- Statement* operator()(Error*);- Statement* operator()(Debug*);- Statement* operator()(Comment*);- Statement* operator()(If*);- Statement* operator()(For*);- Statement* operator()(Each*);- Statement* operator()(While*);- Statement* operator()(Return*);- Statement* operator()(Extension*);- Statement* operator()(Definition*);- Statement* operator()(Mixin_Call*);- Statement* operator()(Content*);+ Block_Ptr operator()(Block_Ptr);+ Statement_Ptr operator()(Ruleset_Ptr);+ Statement_Ptr operator()(Media_Block_Ptr);+ Statement_Ptr operator()(Supports_Block_Ptr);+ Statement_Ptr operator()(At_Root_Block_Ptr);+ Statement_Ptr operator()(Directive_Ptr);+ Statement_Ptr operator()(Declaration_Ptr);+ Statement_Ptr operator()(Assignment_Ptr);+ Statement_Ptr operator()(Import_Ptr);+ Statement_Ptr operator()(Import_Stub_Ptr);+ Statement_Ptr operator()(Warning_Ptr);+ Statement_Ptr operator()(Error_Ptr);+ Statement_Ptr operator()(Debug_Ptr);+ Statement_Ptr operator()(Comment_Ptr);+ Statement_Ptr operator()(If_Ptr);+ Statement_Ptr operator()(For_Ptr);+ Statement_Ptr operator()(Each_Ptr);+ Statement_Ptr operator()(While_Ptr);+ Statement_Ptr operator()(Return_Ptr);+ Statement_Ptr operator()(Extension_Ptr);+ Statement_Ptr operator()(Definition_Ptr);+ Statement_Ptr operator()(Mixin_Call_Ptr);+ Statement_Ptr operator()(Content_Ptr); template <typename U>- Statement* fallback(U x) { return fallback_impl(x); }+ Statement_Ptr fallback(U x) { return fallback_impl(x); } - void append_block(Block*);+ void append_block(Block_Ptr); }; }
libsass/src/extend.cpp view
@@ -1,21 +1,17 @@-#ifdef _MSC_VER-#pragma warning(disable : 4503)-#endif-+#include "sass.hpp" #include "extend.hpp" #include "context.hpp"-#include "to_string.hpp" #include "backtrace.hpp" #include "paths.hpp" #include "parser.hpp" #include "node.hpp" #include "sass_util.hpp"+#include "remove_placeholders.hpp" #include "debug.hpp" #include <iostream> #include <deque> #include <set> - /* NOTES: @@ -65,7 +61,7 @@ namespace Sass { - typedef std::pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+ typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> ExtensionPair; typedef std::vector<ExtensionPair> SubsetMapEntries; #ifdef DEBUG@@ -77,7 +73,7 @@ case Complex_Selector::PARENT_OF: os << "\">\""; break; case Complex_Selector::PRECEDES: os << "\"~\""; break; case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;- case Complex_Selector::REFERENCE: os << "\"/\""; break;+ case Complex_Selector::REFERENCE: os << "\"/\""; break; } return os;@@ -85,23 +81,20 @@ std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) {- To_String to_string; for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) { if (i > 0) os << ", ";- os << compoundSelector[i]->perform(&to_string);+ os << compoundSelector[i]->to_string(); } return os; } std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) {- To_String to_string;- os << simpleSelector.perform(&to_string);+ os << simpleSelector.to_string(); return os; } // Print a string representation of a Compound_Selector static void printSimpleSelector(Simple_Selector* pSimpleSelector, const char* message=NULL, bool newline=true) {- To_String to_string; if (message) { std::cerr << message;@@ -119,13 +112,12 @@ } // Print a string representation of a Compound_Selector- typedef std::pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+ typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SelsNewSeqPair; typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection; // Print a string representation of a Compound_Selector- static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) {- To_String to_string;+ static void printCompoundSelector(Compound_Selector_Ptr pCompoundSelector, const char* message=NULL, bool newline=true) { if (message) { std::cerr << message;@@ -144,10 +136,9 @@ std::ostream& operator<<(std::ostream& os, Complex_Selector& complexSelector) {- To_String to_string; os << "[";- Complex_Selector* pIter = &complexSelector;+ Complex_Selector_Ptr pIter = &complexSelector; bool first = true; while (pIter) { if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {@@ -164,7 +155,7 @@ first = false; if (pIter->head()) {- os << pIter->head()->perform(&to_string);+ os << pIter->head()->to_string(); } else { os << "NULL_HEAD"; }@@ -178,8 +169,7 @@ // Print a string representation of a Complex_Selector- static void printComplexSelector(Complex_Selector* pComplexSelector, const char* message=NULL, bool newline=true) {- To_String to_string;+ static void printComplexSelector(Complex_Selector_Ptr pComplexSelector, const char* message=NULL, bool newline=true) { if (message) { std::cerr << message;@@ -197,7 +187,6 @@ } static void printSelsNewSeqPairCollection(SelsNewSeqPairCollection& collection, const char* message=NULL, bool newline=true) {- To_String to_string; if (message) { std::cerr << message;@@ -211,8 +200,8 @@ std::cerr << ", "; } std::cerr << "[";- Compound_Selector* pSels = pair.first;- Complex_Selector* pNewSelector = pair.second;+ Compound_Selector_Ptr pSels = pair.first;+ Complex_Selector_Ptr pNewSelector = pair.second; std::cerr << "[" << *pSels << "], "; printComplexSelector(pNewSelector, NULL, false); }@@ -225,7 +214,6 @@ // Print a string representation of a SourcesSet static void printSourcesSet(SourcesSet& sources, Context& ctx, const char* message=NULL, bool newline=true) {- To_String to_string; if (message) { std::cerr << message;@@ -236,7 +224,7 @@ typedef std::deque<std::string> SourceStrings; SourceStrings sourceStrings; for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {- Complex_Selector* pSource = *iterator;+ Complex_Selector_Ptr pSource = *iterator; std::stringstream sstream; sstream << complexSelectorToNode(pSource, ctx); sourceStrings.push_back(sstream.str());@@ -265,8 +253,8 @@ os << "SUBSET_MAP_ENTRIES["; for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {- Complex_Selector* pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge)- Compound_Selector* pExtCompoundSelector = iterator->second; // The stuff after the @extend+ Complex_Selector_Obj pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge)+ Compound_Selector_Obj pExtCompoundSelector = iterator->second; // The stuff after the @extend if (iterator != entries.begin()) { os << ", ";@@ -298,14 +286,14 @@ } #endif - static bool parentSuperselector(Complex_Selector* pOne, Complex_Selector* pTwo, Context& ctx) {+ static bool parentSuperselector(Complex_Selector_Ptr pOne, Complex_Selector_Ptr pTwo, Context& ctx) { // TODO: figure out a better way to create a Complex_Selector from scratch // TODO: There's got to be a better way. This got ugly quick... Position noPosition(-1, -1, -1);- Type_Selector fakeParent(ParserState("[FAKE]"), "temp");- Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/);- fakeHead.elements().push_back(&fakeParent);- Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);+ Element_Selector_Obj fakeParent = SASS_MEMORY_NEW(Element_Selector, ParserState("[FAKE]"), "temp");+ Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);+ fakeHead->elements().push_back(&fakeParent);+ Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/); pOne->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); pTwo->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);@@ -329,8 +317,8 @@ Node result = Node::createCollection(); for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {- Complex_Selector* pChild = *iter;- result.collection()->push_back(complexSelectorToNode(pChild, ctx));+ Complex_Selector_Obj pChild = *iter;+ result.collection()->push_back(complexSelectorToNode(&pChild, ctx)); } return result;@@ -342,7 +330,7 @@ Context& mCtx; - bool operator()(Complex_Selector* pOne, Complex_Selector* pTwo, Complex_Selector*& pOut) const {+ bool operator()(Complex_Selector_Obj pOne, Complex_Selector_Obj pTwo, Complex_Selector_Obj& pOut) const { /* This code is based on the following block from ruby sass' subweave do |s1, s2|@@ -362,12 +350,12 @@ return false; } - if (parentSuperselector(pOne, pTwo, mCtx)) {+ if (parentSuperselector(&pOne, &pTwo, mCtx)) { pOut = pTwo; return true; } - if (parentSuperselector(pTwo, pOne, mCtx)) {+ if (parentSuperselector(&pTwo, &pOne, mCtx)) { pOut = pOne; return true; }@@ -393,7 +381,7 @@ } - Complex_Selector* pCompareOut = NULL;+ Complex_Selector_Obj pCompareOut; if (comparator(x[i], y[j], pCompareOut)) { DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE") lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out);@@ -430,9 +418,9 @@ for (size_t i = 1; i < x.size(); i++) { for (size_t j = 1; j < y.size(); j++) {- Complex_Selector* pCompareOut = NULL;+ Complex_Selector_Obj pCompareOut; - if (comparator(x[i], y[j], pCompareOut)) {+ if (comparator(&x[i], &y[j], pCompareOut)) { c[i][j] = c[i - 1][j - 1] + 1; } else { c[i][j] = std::max(c[i][j - 1], c[i - 1][j]);@@ -570,7 +558,7 @@ for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) { Node& seq1 = *seqs1Iter; - Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx);+ Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1, ctx); // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code // for a good description of sources.@@ -587,7 +575,7 @@ DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIM SOURCES: ")) for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {- const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;+ const Complex_Selector_Obj& pCurrentSelector = *sourcesSetIterator; maxSpecificity = std::max(maxSpecificity, pCurrentSelector->specificity()); } @@ -617,7 +605,7 @@ for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) { Node& seq2 = *seqs2Iter; - Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx);+ Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2, ctx); DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity()) DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))@@ -665,14 +653,14 @@ // TODO: figure out a better way to create a Complex_Selector from scratch // TODO: There's got to be a better way. This got ugly quick... Position noPosition(-1, -1, -1);- Type_Selector fakeParent(ParserState("[FAKE]"), "temp");- Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/);- fakeHead.elements().push_back(&fakeParent);- Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);+ Element_Selector_Obj fakeParent = SASS_MEMORY_NEW(Element_Selector, ParserState("[FAKE]"), "temp");+ Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);+ fakeHead->elements().push_back(&fakeParent);+ Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/); - Complex_Selector* pOneWithFakeParent = nodeToComplexSelector(one, ctx);+ Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one, ctx); pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);- Complex_Selector* pTwoWithFakeParent = nodeToComplexSelector(two, ctx);+ Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two, ctx); pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);@@ -860,7 +848,7 @@ DefaultLcsComparator lcsDefaultComparator; Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx); - if (!(opsLcs == ops1 || opsLcs == ops2)) {+ if (!(nodesEqual(opsLcs, ops1, true) || nodesEqual(opsLcs, ops2, true))) { return Node::createNil(); } @@ -957,7 +945,7 @@ // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up. - if (!(opsLcs == ops1 || opsLcs == ops2)) {+ if (!(nodesEqual(opsLcs, ops1, true) || nodesEqual(opsLcs, ops2, true))) { return Node::createNil(); } @@ -998,9 +986,9 @@ DEBUG_PRINTLN(ALL, "sel1: " << sel1) DEBUG_PRINTLN(ALL, "sel2: " << sel2) - Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+ Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)- Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);+ Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(&sel2.selector()->head(), ctx); pMergedWrapper->head(pMerged); DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1023,7 +1011,7 @@ if (pMerged) { Node mergedPerm = Node::createCollection();- mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));+ mergedPerm.collection()->push_back(Node::createSelector(&pMergedWrapper, ctx)); mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); newRes.collection()->push_back(mergedPerm); }@@ -1057,9 +1045,9 @@ DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel) DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel) - Complex_Selector* pMergedWrapper = plusSel.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+ Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(plusSel.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)- Compound_Selector* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head(), ctx);+ Compound_Selector_Ptr pMerged = plusSel.selector()->head()->unify_with(&tildeSel.selector()->head(), ctx); pMergedWrapper->head(pMerged); DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1075,7 +1063,7 @@ if (pMerged) { Node mergedPerm = Node::createCollection();- mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));+ mergedPerm.collection()->push_back(Node::createSelector(&pMergedWrapper, ctx)); mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO)); newRes.collection()->push_back(mergedPerm); }@@ -1106,9 +1094,9 @@ DEBUG_PRINTLN(ALL, "sel1: " << sel1) DEBUG_PRINTLN(ALL, "sel2: " << sel2) - Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+ Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)- Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);+ Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(&sel2.selector()->head(), ctx); pMergedWrapper->head(pMerged); DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))@@ -1118,7 +1106,7 @@ } res.collection()->push_front(op1);- res.collection()->push_front(Node::createSelector(pMergedWrapper, ctx));+ res.collection()->push_front(Node::createSelector(&pMergedWrapper, ctx)); DEBUG_PRINTLN(ALL, "RESULT: " << res) @@ -1365,20 +1353,20 @@ // Check for the simple cases if (one.isNil()) {- out.collection()->push_back(two.clone(ctx));+ out.collection()->push_back(two.klone(ctx)); } else if (two.isNil()) {- out.collection()->push_back(one.clone(ctx));+ out.collection()->push_back(one.klone(ctx)); } else { // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B // See https://gist.github.com/nex3/7609394 for details. - Node firstPerm = one.clone(ctx);- Node twoCloned = two.clone(ctx);+ Node firstPerm = one.klone(ctx);+ Node twoCloned = two.klone(ctx); firstPerm.plus(twoCloned); out.collection()->push_back(firstPerm); - Node secondPerm = two.clone(ctx);- Node oneCloned = one.clone(ctx);+ Node secondPerm = two.klone(ctx);+ Node oneCloned = one.klone(ctx); secondPerm.plus(oneCloned ); out.collection()->push_back(secondPerm); }@@ -1472,9 +1460,10 @@ afters.plus(path); while (!afters.collection()->empty()) {- Node current = afters.collection()->front().clone(ctx);+ Node current = afters.collection()->front().klone(ctx); afters.collection()->pop_front(); DEBUG_PRINTLN(WEAVE, "CURRENT: " << current)+ if (current.collection()->size() == 0) continue; Node last_current = Node::createCollection(); last_current.collection()->push_back(current.collection()->back());@@ -1519,9 +1508,9 @@ // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively // call extendComplexSelector again. static Node extendComplexSelector(- Complex_Selector* pComplexSelector,+ Complex_Selector_Ptr pComplexSelector, Context& ctx,- ExtensionSubsetMap& subset_map,+ Subset_Map& subset_map, std::set<Compound_Selector> seen, bool isReplace, bool isOriginal); @@ -1542,14 +1531,14 @@ class GroupByToAFunctor { public: KeyType operator()(ExtensionPair& extPair) const {- Complex_Selector* pSelector = extPair.first;- return *pSelector;+ Complex_Selector_Obj pSelector = extPair.first;+ return &pSelector; } }; static Node extendCompoundSelector(- Compound_Selector* pSelector,+ Compound_Selector_Ptr pSelector, Context& ctx,- ExtensionSubsetMap& subset_map,+ Subset_Map& subset_map, std::set<Compound_Selector> seen, bool isReplace) { DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))@@ -1558,17 +1547,15 @@ Node extendedSelectors = Node::createCollection(); // extendedSelectors.got_line_feed = true; - To_String to_string;-- SubsetMapEntries entries = subset_map.get_v(pSelector->to_str_vec());+ SubsetMapEntries entries = subset_map.get_v(pSelector); - typedef std::vector<std::pair<Complex_Selector, std::vector<ExtensionPair> > > GroupedByToAResult;+ typedef std::vector<std::pair<Complex_Selector_Obj, std::vector<ExtensionPair> > > GroupedByToAResult; - GroupByToAFunctor<Complex_Selector> extPairKeyFunctor;+ GroupByToAFunctor<Complex_Selector_Obj> extPairKeyFunctor; GroupedByToAResult arr; group_by_to_a(entries, extPairKeyFunctor, arr); - typedef std::pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+ typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SelsNewSeqPair; typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection; @@ -1576,45 +1563,42 @@ for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) {- std::pair<Complex_Selector, std::vector<ExtensionPair> >& groupedPair = *groupedIter;+ std::pair<Complex_Selector_Obj, std::vector<ExtensionPair> >& groupedPair = *groupedIter; - Complex_Selector& seq = groupedPair.first;+ Complex_Selector_Obj seq = groupedPair.first; std::vector<ExtensionPair>& group = groupedPair.second; DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) -- Compound_Selector* pSels = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate());+// changing this makes aua+ Compound_Selector_Obj pSels = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate()); for (std::vector<ExtensionPair>::iterator groupIter = group.begin(), groupIterEnd = group.end(); groupIter != groupIterEnd; groupIter++) { ExtensionPair& pair = *groupIter;- Compound_Selector* pCompound = pair.second;+ Compound_Selector_Obj pCompound = pair.second; for (size_t index = 0; index < pCompound->length(); index++) {- Simple_Selector* pSimpleSelector = (*pCompound)[index];- (*pSels) << pSimpleSelector;+ Simple_Selector_Obj pSimpleSelector = (*pCompound)[index];+ pSels->append(&pSimpleSelector);+ pCompound->extended(true); } } - DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))+ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(&pSels, "SELS: ")) - Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge)- Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions+ Complex_Selector_Ptr pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge) // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL? // RUBY: self_without_sel = Sass::Util.array_minus(members, sels)- Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx);+ Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(&pSels, ctx); DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) - Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->last()->head();- Compound_Selector* pUnifiedSelector = NULL;+ Compound_Selector_Obj pInnermostCompoundSelector = pExtComplexSelector->last()->head(); if (!pInnermostCompoundSelector) {- pInnermostCompoundSelector = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate());+ pInnermostCompoundSelector = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate()); }-- pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx);-+ Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(&pSelectorWithoutExtendSelectors, ctx); DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))@@ -1632,22 +1616,22 @@ // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered // out and aren't operated on.- Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx); // ->first();+ Complex_Selector_Obj pNewSelector = SASS_MEMORY_CLONE(pExtComplexSelector); // ->first(); - Complex_Selector* pNewInnerMost = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL);+ Complex_Selector_Obj pNewInnerMost = SASS_MEMORY_NEW(Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL); Complex_Selector::Combinator combinator = pNewSelector->clear_innermost();- pNewSelector->set_innermost(pNewInnerMost, combinator);+ pNewSelector->set_innermost(&pNewInnerMost, combinator); #ifdef DEBUG SourcesSet debugSet; debugSet = pNewSelector->sources(); if (debugSet.size() > 0) {- throw "The new selector should start with no sources. Something needs to be cloned to fix this.";+ throw std::runtime_error("The new selector should start with no sources. Something needs to be cloned to fix this."); } debugSet = pExtComplexSelector->sources(); if (debugSet.size() > 0) {- throw "The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this.";+ throw std::runtime_error("The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this."); } #endif @@ -1671,7 +1655,7 @@ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: ")) - if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);;+ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true); holder.push_back(std::make_pair(pSels, pNewSelector)); }@@ -1680,8 +1664,8 @@ for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) { SelsNewSeqPair& pair = *holderIter; - Compound_Selector* pSels = pair.first;- Complex_Selector* pNewSelector = pair.second;+ Compound_Selector_Obj pSels = pair.first;+ Complex_Selector_Obj pNewSelector = pair.second; // RUBY??: next [] if seen.include?(sels)@@ -1695,13 +1679,13 @@ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx))- Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen, isReplace, false); // !:isOriginal+ Node recurseExtendedSelectors = extendComplexSelector(&pNewSelector, ctx, subset_map, recurseSeen, isReplace, false); // !:isOriginal DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end(); iterator != endIterator; ++iterator) {- Node& newSelector = *iterator;+ Node newSelector = *iterator; // DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << extendedSelectors) // DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/));@@ -1720,30 +1704,30 @@ static bool complexSelectorHasExtension(- Complex_Selector* pComplexSelector,+ Complex_Selector_Ptr pComplexSelector, Context& ctx,- ExtensionSubsetMap& subset_map) {+ Subset_Map& subset_map,+ std::set<Compound_Selector>& seen) { bool hasExtension = false; - Complex_Selector* pIter = pComplexSelector;+ Complex_Selector_Obj pIter = pComplexSelector; while (!hasExtension && pIter) {- Compound_Selector* pHead = pIter->head();+ Compound_Selector_Obj pHead = pIter->head(); if (pHead) {- SubsetMapEntries entries = subset_map.get_v(pHead->to_str_vec());+ SubsetMapEntries entries = subset_map.get_v(pHead); for (ExtensionPair ext : entries) { // check if both selectors have the same media block parent- if (ext.first->media_block() == pComplexSelector->media_block()) continue;- To_String to_string(&ctx);+ // if (ext.first->media_block() == pComplexSelector->media_block()) continue; if (ext.second->media_block() == 0) continue;- if (pComplexSelector->media_block() &&+ if (pHead->media_block() && ext.second->media_block()->media_queries() &&- pComplexSelector->media_block()->media_queries()+ pHead->media_block()->media_queries() ) {- std::string query_left(ext.second->media_block()->media_queries()->perform(&to_string));- std::string query_right(pComplexSelector->media_block()->media_queries()->perform(&to_string));+ std::string query_left(ext.second->media_block()->media_queries()->to_string(ctx.c_options));+ std::string query_right(pHead->media_block()->media_queries()->to_string(ctx.c_options)); if (query_left == query_right) continue; } @@ -1754,7 +1738,7 @@ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); err << "You may not @extend an outer selector from within @media.\n"; err << "You may only @extend selectors within the same directive.\n";- err << "From \"@extend " << ext.second->perform(&to_string) << "\"";+ err << "From \"@extend " << ext.second->to_string(ctx.c_options) << "\""; err << " on line " << pstate.line+1 << " of " << rel_path << "\n"; error(err.str(), pComplexSelector->pstate()); }@@ -1764,29 +1748,6 @@ pIter = pIter->tail(); } - if (!hasExtension) {- /* ToDo: don't break stuff- std::stringstream err;- To_String to_string(&ctx);- std::string cwd(Sass::File::get_cwd());- std::string sel1(pComplexSelector->perform(&to_string));- Compound_Selector* pExtendSelector = 0;- for (auto i : subset_map.values()) {- if (i.first == pComplexSelector) {- pExtendSelector = i.second;- break;- }- }- if (!pExtendSelector || !pExtendSelector->is_optional()) {- std::string sel2(pExtendSelector ? pExtendSelector->perform(&to_string) : "[unknown]");- err << "\"" << sel1 << "\" failed to @extend \"" << sel2 << "\"\n";- err << "The selector \"" << sel2 << "\" was not found.\n";- err << "Use \"@extend " << sel2 << " !optional\" if the extend should be able to fail.";- error(err.str(), pExtendSelector ? pExtendSelector->pstate() : pComplexSelector->pstate());- }- */- }- return hasExtension; } @@ -1803,9 +1764,9 @@ next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence) */ static Node extendComplexSelector(- Complex_Selector* pComplexSelector,+ Complex_Selector_Ptr pComplexSelector, Context& ctx,- ExtensionSubsetMap& subset_map,+ Subset_Map& subset_map, std::set<Compound_Selector> seen, bool isReplace, bool isOriginal) { Node complexSelector = complexSelectorToNode(pComplexSelector, ctx);@@ -1835,17 +1796,17 @@ continue; } - Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head();+ Compound_Selector_Obj pCompoundSelector = sseqOrOp.selector()->head(); // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)- Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen, isReplace);+ Node extended = extendCompoundSelector(&pCompoundSelector, ctx, subset_map, seen, isReplace); if (sseqOrOp.got_line_feed) extended.got_line_feed = true; DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended) // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with an ruby Array instead of a Sequence // due to the member mapping: choices = extended.map {|seq| seq.members}- Complex_Selector* pJustCurrentCompoundSelector = sseqOrOp.selector();+ Complex_Selector_Obj pJustCurrentCompoundSelector = sseqOrOp.selector(); // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder? if (isOriginal && !pComplexSelector->has_placeholder()) {@@ -1859,7 +1820,7 @@ for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end(); iterator != endIterator; ++iterator) { Node& childNode = *iterator;- Complex_Selector* pExtensionSelector = nodeToComplexSelector(childNode, ctx);+ Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode, ctx); if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) { isSuperselector = true; break;@@ -1868,7 +1829,7 @@ if (!isSuperselector) { if (sseqOrOp.got_line_feed) pJustCurrentCompoundSelector->has_line_feed(sseqOrOp.got_line_feed);- extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx));+ extended.collection()->push_front(complexSelectorToNode(&pJustCurrentCompoundSelector, ctx)); } DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)@@ -1926,35 +1887,40 @@ /* This is the equivalent of ruby's CommaSequence.do_extend. */- Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) {+ Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething) {+ std::set<Compound_Selector> seen;+ return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);+ } - To_String to_string(&ctx);+ /*+ This is the equivalent of ruby's CommaSequence.do_extend.+ */+ Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen) { - Selector_List* pNewSelectors = SASS_MEMORY_NEW(ctx.mem, Selector_List, pSelectorList->pstate(), pSelectorList->length());+ Selector_List_Obj pNewSelectors = SASS_MEMORY_NEW(Selector_List, pSelectorList->pstate(), pSelectorList->length()); extendedSomething = false; for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {- Complex_Selector* pSelector = (*pSelectorList)[index];+ Complex_Selector_Obj pSelector = (*pSelectorList)[index]; // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that. // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to // run through the extend code (which does a data model transformation), check if there is anything to extend before doing // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps // when debugging).- if (!complexSelectorHasExtension(pSelector, ctx, subset_map)) {- *pNewSelectors << pSelector;+ if (!complexSelectorHasExtension(&pSelector, ctx, subset_map, seen)) {+ pNewSelectors->append(&pSelector); continue; } extendedSomething = true; - std::set<Compound_Selector> seen;-- Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace, true);+ Node extendedSelectors = extendComplexSelector(&pSelector, ctx, subset_map, seen, isReplace, true); if (!pSelector->has_placeholder()) {- if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {- *pNewSelectors << pSelector;+ if (!extendedSelectors.contains(complexSelectorToNode(&pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {+ pNewSelectors->append(pSelector);+ continue; } } @@ -1963,16 +1929,84 @@ if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; Node& childNode = *iterator;- *pNewSelectors << nodeToComplexSelector(childNode, ctx);+ pNewSelectors->append(nodeToComplexSelector(childNode, ctx)); } } - return pNewSelectors;+ Remove_Placeholders remove_placeholders(ctx);+ // it seems that we have to remove the place holders early here+ // normally we do this as the very last step (compare to ruby sass)+ pNewSelectors = remove_placeholders.remove_placeholders(&pNewSelectors); + // unwrap all wrapped selectors with inner lists+ for (Complex_Selector_Obj cur : pNewSelectors->elements()) {+ // process tails+ while (cur) {+ // process header+ if (cur->head() && seen.find(*cur->head()) == seen.end()) {+ std::set<Compound_Selector> recseen(seen);+ recseen.insert(*cur->head());+ // create a copy since we add multiple items if stuff get unwrapped+ Compound_Selector_Obj cpy_head = SASS_MEMORY_NEW(Compound_Selector, cur->pstate());+ for (Simple_Selector_Obj hs : *cur->head()) {+ if (Wrapped_Selector_Obj ws = SASS_MEMORY_CAST(Wrapped_Selector, hs)) {+ ws->selector(SASS_MEMORY_CLONE(ws->selector()));+ if (Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, ws->selector())) {+ // special case for ruby ass+ if (sl->empty()) {+ // this seems inconsistent but it is how ruby sass seems to remove parentheses+ cpy_head->append(SASS_MEMORY_NEW(Element_Selector, hs->pstate(), ws->name()));+ }+ // has wrapped selectors+ else {+ // extend the inner list of wrapped selector+ Selector_List_Obj ext_sl = extendSelectorList(sl, ctx, subset_map, recseen);+ for (size_t i = 0; i < ext_sl->length(); i += 1) {+ if (Complex_Selector_Obj ext_cs = ext_sl->at(i)) {+ // create clones for wrapped selector and the inner list+ Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(&ws);+ Selector_List_Obj cpy_ws_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());+ // remove parent selectors from inner selector+ if (ext_cs->first() && ext_cs->first()->head()->length() > 0) {+ Wrapped_Selector_Ptr ext_ws = SASS_MEMORY_CAST(Wrapped_Selector, ext_cs->first()->head()->first());+ if (ext_ws/* && ext_cs->length() == 1*/) {+ Selector_List_Obj ws_cs = SASS_MEMORY_CAST(Selector_List, ext_ws->selector());+ Compound_Selector_Obj ws_ss = ws_cs->first()->head();+ if (!(+ SASS_MEMORY_CAST(Pseudo_Selector, ws_ss->first()) ||+ SASS_MEMORY_CAST(Element_Selector, ws_ss->first()) ||+ SASS_MEMORY_CAST(Placeholder_Selector, ws_ss->first())+ )) continue;+ }+ cpy_ws_sl->append(ext_cs->first());+ }+ // assign list to clone+ cpy_ws->selector(&cpy_ws_sl);+ // append the clone+ cpy_head->append(&cpy_ws);+ }+ }+ }+ } else {+ cpy_head->append(&hs);+ }+ } else {+ cpy_head->append(&hs);+ }+ }+ // replace header+ cur->head(cpy_head);+ }+ // process tail+ cur = cur->tail();+ }+ }+ return pNewSelectors.detach();+ } - bool shouldExtendBlock(Block* b) {+ bool shouldExtendBlock(Block_Obj b) { // If a block is empty, there's no reason to extend it since any rules placed on this block // won't have any output. The main benefit of this is for structures like:@@ -1988,9 +2022,9 @@ // there are no child statements. However .a .b should have extensions applied. for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];+ Statement_Obj stm = b->at(i); - if (typeid(*stm) == typeid(Ruleset)) {+ if (dynamic_cast<Ruleset_Ptr>(&stm)) { // Do nothing. This doesn't count as a statement that causes extension since we'll iterate over this rule set in a future visit and try to extend it. } else {@@ -2005,10 +2039,9 @@ // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change. template <typename ObjectType>- static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subset_map) {- To_String to_string(&ctx);+ static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, Subset_Map& subset_map) { - DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))+ DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List_Ptr>(pObject->selector())->to_string(ctx.c_options)) // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions // of doing so, so for now, let's just not extend things that won't be output later.@@ -2018,13 +2051,13 @@ } bool extendedSomething = false;- Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subset_map, false, extendedSomething);+ Selector_List_Obj pNewSelectorList = Extend::extendSelectorList(SASS_MEMORY_CAST(Selector_List, pObject->selector()), ctx, subset_map, false, extendedSomething); if (extendedSomething && pNewSelectorList) {- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string))+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List_Ptr>(pObject->selector())->to_string(ctx.c_options))+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string(ctx.c_options)) pNewSelectorList->remove_parent_selectors();- pObject->selector(pNewSelectorList);+ pObject->selector(&pNewSelectorList); } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") }@@ -2032,36 +2065,56 @@ - Extend::Extend(Context& ctx, ExtensionSubsetMap& ssm)+ Extend::Extend(Context& ctx, Subset_Map& ssm) : ctx(ctx), subset_map(ssm) { } - void Extend::operator()(Block* b)+ void Extend::operator()(Block_Ptr b) { for (size_t i = 0, L = b->length(); i < L; ++i) {- (*b)[i]->perform(this);+ Statement_Obj stm = b->at(i);+ stm->perform(this); }+ // do final check if everything was extended+ // we set `extended` flag on extended selectors+ if (b->is_root()) {+ // debug_subset_map(subset_map);+ for(auto const &it : subset_map.values()) {+ Complex_Selector_Ptr sel = it.first ? &it.first->first() : NULL;+ Compound_Selector_Ptr ext = it.second ? &it.second : NULL;+ if (ext && (ext->extended() || ext->is_optional())) continue;+ std::string str_sel(sel->to_string({ NESTED, 5 }));+ std::string str_ext(ext->to_string({ NESTED, 5 }));+ // debug_ast(sel, "sel: ");+ // debug_ast(ext, "ext: ");+ error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n"+ "The selector \"" + str_ext + "\" was not found.\n"+ "Use \"@extend " + str_ext + " !optional\" if the"+ " extend should be able to fail.", ext->pstate());+ }+ }+ } - void Extend::operator()(Ruleset* pRuleset)+ void Extend::operator()(Ruleset_Ptr pRuleset) {- extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map);+ extendObjectWithSelectorAndBlock( pRuleset, ctx, subset_map); pRuleset->block()->perform(this); } - void Extend::operator()(Supports_Block* pFeatureBlock)+ void Extend::operator()(Supports_Block_Ptr pFeatureBlock) { pFeatureBlock->block()->perform(this); } - void Extend::operator()(Media_Block* pMediaBlock)+ void Extend::operator()(Media_Block_Ptr pMediaBlock) { pMediaBlock->block()->perform(this); } - void Extend::operator()(At_Rule* a)+ void Extend::operator()(Directive_Ptr a) {- // Selector_List* ls = dynamic_cast<Selector_List*>(a->selector());+ // Selector_List_Ptr ls = dynamic_cast<Selector_List_Ptr>(a->selector()); // selector_stack.push_back(ls); if (a->block()) a->block()->perform(this); // exp.selector_stack.pop_back();
libsass/src/extend.hpp view
@@ -2,6 +2,7 @@ #define SASS_EXTEND_H #include <string>+#include <set> #include "ast.hpp" #include "operation.hpp"@@ -12,28 +13,34 @@ class Context; class Node; - typedef Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;- class Extend : public Operation_CRTP<void, Extend> { Context& ctx;- ExtensionSubsetMap& subset_map;+ Subset_Map& subset_map; - void fallback_impl(AST_Node* n) { }+ void fallback_impl(AST_Node_Ptr n) { } public: static Node subweave(Node& one, Node& two, Context& ctx);- static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething);- Extend(Context&, ExtensionSubsetMap&);- virtual ~Extend() { }-- using Operation<void>::operator();+ static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen);+ static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace, bool& extendedSomething);+ static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, bool isReplace = false) {+ bool extendedSomething = false;+ return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething);+ }+ static Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, Context& ctx, Subset_Map& subset_map, std::set<Compound_Selector>& seen) {+ bool isReplace = false;+ bool extendedSomething = false;+ return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);+ }+ Extend(Context&, Subset_Map&);+ ~Extend() { } - void operator()(Block*);- void operator()(Ruleset*);- void operator()(Supports_Block*);- void operator()(Media_Block*);- void operator()(At_Rule*);+ void operator()(Block_Ptr);+ void operator()(Ruleset_Ptr);+ void operator()(Supports_Block_Ptr);+ void operator()(Media_Block_Ptr);+ void operator()(Directive_Ptr); template <typename U> void fallback(U x) { return fallback_impl(x); }
libsass/src/file.cpp view
@@ -1,19 +1,15 @@ #ifdef _WIN32-#ifdef __MINGW32__-#ifndef off64_t-#define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */-#endif-#endif-#include <direct.h>-#define getcwd _getcwd-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)+# ifdef __MINGW32__+# ifndef off64_t+# define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */+# endif+# endif+# include <direct.h>+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #else-#include <unistd.h>-#endif-#ifdef _MSC_VER-#define NOMINMAX+# include <unistd.h> #endif-+#include "sass.hpp" #include <iostream> #include <fstream> #include <cctype>@@ -27,15 +23,24 @@ #include "sass2scss.h" #ifdef _WIN32-#include <windows.h>-#endif+# include <windows.h> -#ifndef FS_CASE_SENSITIVE-#ifdef _WIN32-#define FS_CASE_SENSITIVE 0-#else-#define FS_CASE_SENSITIVE 1-#endif+# ifdef _MSC_VER+# include <codecvt>+inline static std::string wstring_to_string(const std::wstring& wstr)+{+ std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_converter;+ return wchar_converter.to_bytes(wstr);+}+# else // mingw(/gcc) does not support C++11's codecvt yet.+inline static std::string wstring_to_string(const std::wstring &wstr)+{+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);+ std::string strTo(size_needed, 0);+ WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);+ return strTo;+}+# endif #endif namespace Sass {@@ -46,9 +51,12 @@ std::string get_cwd() { const size_t wd_len = 1024;- char wd[wd_len];- std::string cwd = getcwd(wd, wd_len);- #ifdef _WIN32+ #ifndef _WIN32+ char wd[wd_len];+ std::string cwd = getcwd(wd, wd_len);+ #else+ wchar_t wd[wd_len];+ std::string cwd = wstring_to_string(_wgetcwd(wd, wd_len)); //convert backslashes to forward slashes replace(cwd.begin(), cwd.end(), '\\', '/'); #endif@@ -184,8 +192,11 @@ if (l[l.length()-1] != '/') l += '/'; while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {- r = r.substr(3);- size_t pos = find_last_folder_separator(l, l.length() - 2);+ size_t L = l.length(), pos = find_last_folder_separator(l, L - 2);+ bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\');+ bool is_self = pos + 3 == L && (l[pos+1] == '.');+ if (!is_self && !is_slash) r = r.substr(3);+ else if (pos == std::string::npos) break; l = l.substr(0, pos == std::string::npos ? pos : pos + 1); } @@ -207,7 +218,7 @@ // create an absolute path by resolving relative paths with cwd std::string rel2abs(const std::string& path, const std::string& base, const std::string& cwd) {- return make_canonical_path(join_paths(join_paths(cwd, base), path));+ return make_canonical_path(join_paths(join_paths(cwd + "/", base + "/"), path)); } // create a path that is relative to the given base directory
libsass/src/file.hpp view
@@ -4,10 +4,9 @@ #include <string> #include <vector> -namespace Sass {+#include "ast_fwd_decl.hpp" - class Block;- class Context;+namespace Sass { namespace File { @@ -105,9 +104,9 @@ class StyleSheet : public Resource { public: // parsed root block- Block* root;+ Block_Obj root; public:- StyleSheet(const Resource& res, Block* root)+ StyleSheet(const Resource& res, Block_Obj root) : Resource(res), root(root) { } };
libsass/src/functions.cpp view
@@ -1,10 +1,10 @@+#include "sass.hpp" #include "functions.hpp" #include "ast.hpp" #include "context.hpp" #include "backtrace.hpp" #include "parser.hpp" #include "constants.hpp"-#include "to_string.hpp" #include "inspect.hpp" #include "extend.hpp" #include "eval.hpp"@@ -14,6 +14,7 @@ #include "sass/base.h" #include "utf8.h" +#include <cstdint> #include <cstdlib> #include <cmath> #include <cctype>@@ -37,13 +38,13 @@ using std::stringstream; using std::endl; - Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)+ Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx) { Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]")); sig_parser.lex<Prelexer::identifier>(); std::string name(Util::normalize_underscores(sig_parser.lexed));- Parameters* params = sig_parser.parse_parameters();- return SASS_MEMORY_NEW(ctx.mem, Definition,+ Parameters_Obj params = sig_parser.parse_parameters();+ return SASS_MEMORY_NEW(Definition, ParserState("[built-in function]"), sig, name,@@ -52,7 +53,7 @@ false); } - Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)+ Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx) { using namespace Prelexer; @@ -65,8 +66,8 @@ exactly < Constants::debug_kwd > > >(); std::string name(Util::normalize_underscores(sig_parser.lexed));- Parameters* params = sig_parser.parse_parameters();- return SASS_MEMORY_NEW(ctx.mem, Definition,+ Parameters_Obj params = sig_parser.parse_parameters();+ return SASS_MEMORY_NEW(Definition, ParserState("[c function]"), sig, name,@@ -107,7 +108,7 @@ T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) { // Minimal error handling -- the expectation is that built-ins will be written correctly!- T* val = dynamic_cast<T*>(env[argname]);+ T* val = dynamic_cast<T*>(&env[argname]); if (!val) { std::string msg("argument `"); msg += argname;@@ -120,24 +121,24 @@ return val; } - Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx)+ Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { // Minimal error handling -- the expectation is that built-ins will be written correctly!- Map* val = dynamic_cast<Map*>(env[argname]);+ Map_Ptr val = SASS_MEMORY_CAST(Map, env[argname]); if (val) return val; - List* lval = dynamic_cast<List*>(env[argname]);- if (lval && lval->length() == 0) return SASS_MEMORY_NEW(ctx.mem, Map, pstate, 0);+ List_Ptr lval = SASS_MEMORY_CAST(List, env[argname]);+ if (lval && lval->length() == 0) return SASS_MEMORY_NEW(Map, pstate, 0); // fallback on get_arg for error handling val = get_arg<Map>(argname, env, sig, pstate, backtrace); return val; } - Number* get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace)+ Number_Ptr get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace) { // Minimal error handling -- the expectation is that built-ins will be written correctly!- Number* val = get_arg<Number>(argname, env, sig, pstate, backtrace);+ Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace); double v = val->value(); if (!(lo <= v && v <= hi)) { std::stringstream msg;@@ -151,49 +152,38 @@ #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, backtrace, ctx) template <typename T>- T* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx);+ T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx); template <>- Selector_List* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {- To_String to_string(&ctx, false);- Expression* exp = ARG(argname, Expression);+ Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ Expression_Obj exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; error(msg.str(), pstate); }- std::string exp_src = exp->perform(&to_string) + "{";- return Parser::parse_selector(exp_src.c_str(), ctx);- }-- template <>- Complex_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {- To_String to_string(&ctx, false);- Expression* exp = ARG(argname, Expression);- if (exp->concrete_type() == Expression::NULL_VAL) {- std::stringstream msg;- msg << argname << ": null is not a valid selector: it must be a string,\n";- msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";- error(msg.str(), pstate);+ if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+ str->quote_mark(0); }- std::string exp_src = exp->perform(&to_string) + "{";- Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx);- return (sel_list->length() > 0) ? sel_list->first() : 0;+ std::string exp_src = exp->to_string(ctx.c_options) + "{";+ return Parser::parse_selector(exp_src.c_str(), ctx); } template <>- Compound_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {- To_String to_string(&ctx, false);- Expression* exp = ARG(argname, Expression);+ Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ Expression_Obj exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a string for `" << function_name(sig) << "'"; error(msg.str(), pstate); }- std::string exp_src = exp->perform(&to_string) + "{";- Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx);- return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0;+ if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string(ctx.c_options) + "{";+ Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx);+ return (sel_list->length() > 0) ? &sel_list->first()->tail()->head() : 0; } #ifdef __MINGW32__@@ -211,9 +201,9 @@ return seed; } #else- static std::random_device rd; uint64_t GetSeed() {+ std::random_device rd; return rd(); } #endif@@ -236,7 +226,7 @@ // RGB FUNCTIONS //////////////// - inline double color_num(Number* n) {+ inline double color_num(Number_Ptr n) { if (n->unit() == "%") { return std::min(std::max(n->value() * 255 / 100.0, 0.0), 255.0); } else {@@ -244,7 +234,7 @@ } } - inline double alpha_num(Number* n) {+ inline double alpha_num(Number_Ptr n) { if (n->unit() == "%") { return std::min(std::max(n->value(), 0.0), 100.0); } else {@@ -255,7 +245,7 @@ Signature rgb_sig = "rgb($red, $green, $blue)"; BUILT_IN(rgb) {- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color_num(ARG("$red", Number)), color_num(ARG("$green", Number)),@@ -265,7 +255,7 @@ Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; BUILT_IN(rgba_4) {- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color_num(ARG("$red", Number)), color_num(ARG("$green", Number)),@@ -276,8 +266,8 @@ Signature rgba_2_sig = "rgba($color, $alpha)"; BUILT_IN(rgba_2) {- Color* c_arg = ARG("$color", Color);- Color* new_c = SASS_MEMORY_NEW(ctx.mem, Color, *c_arg);+ Color_Ptr c_arg = ARG("$color", Color);+ Color_Ptr new_c = SASS_MEMORY_COPY(c_arg); new_c->a(alpha_num(ARG("$alpha", Number))); new_c->disp(""); return new_c;@@ -285,22 +275,22 @@ Signature red_sig = "red($color)"; BUILT_IN(red)- { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->r()); }+ { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->r()); } Signature green_sig = "green($color)"; BUILT_IN(green)- { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->g()); }+ { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->g()); } Signature blue_sig = "blue($color)"; BUILT_IN(blue)- { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->b()); }+ { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); } Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)"; BUILT_IN(mix) {- Color* color1 = ARG("$color-1", Color);- Color* color2 = ARG("$color-2", Color);- Number* weight = ARGR("$weight", Number, 0, 100);+ Color_Ptr color1 = ARG("$color-1", Color);+ Color_Ptr color2 = ARG("$color-2", Color);+ Number_Ptr weight = ARGR("$weight", Number, 0, 100); double p = weight->value()/100; double w = 2*p - 1;@@ -309,11 +299,11 @@ double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0; double w2 = 1 - w1; - return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate,- Sass::round(w1*color1->r() + w2*color2->r()),- Sass::round(w1*color1->g() + w2*color2->g()),- Sass::round(w1*color1->b() + w2*color2->b()),+ Sass::round(w1*color1->r() + w2*color2->r(), ctx.c_options.precision),+ Sass::round(w1*color1->g() + w2*color2->g(), ctx.c_options.precision),+ Sass::round(w1*color1->b() + w2*color2->b(), ctx.c_options.precision), color1->a()*p + color2->a()*(1-p)); } @@ -365,7 +355,7 @@ return m1; } - Color* hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate)+ Color_Ptr hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate) { h /= 360.0; s /= 100.0;@@ -378,6 +368,12 @@ while (h < 0) h += 1; while (h > 1) h -= 1; + // if saturation is exacly zero, we loose+ // information for hue, since it will evaluate+ // to zero if converted back from rgb. Setting+ // saturation to a very tiny number solves this.+ if (s == 0) s = 1e-10;+ // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color. double m2; if (l <= 0.5) m2 = l*(s+1.0);@@ -388,7 +384,7 @@ double g = (h_to_rgb(m1, m2, h) * 255.0); double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0); - return SASS_MEMORY_NEW(ctx.mem, Color, pstate, r, g, b, a);+ return SASS_MEMORY_NEW(Color, pstate, r, g, b, a); } Signature hsl_sig = "hsl($hue, $saturation, $lightness)";@@ -416,38 +412,38 @@ Signature hue_sig = "hue($color)"; BUILT_IN(hue) {- Color* rgb_color = ARG("$color", Color);+ Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, hsl_color.h, "deg");+ return SASS_MEMORY_NEW(Number, pstate, hsl_color.h, "deg"); } Signature saturation_sig = "saturation($color)"; BUILT_IN(saturation) {- Color* rgb_color = ARG("$color", Color);+ Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, hsl_color.s, "%");+ return SASS_MEMORY_NEW(Number, pstate, hsl_color.s, "%"); } Signature lightness_sig = "lightness($color)"; BUILT_IN(lightness) {- Color* rgb_color = ARG("$color", Color);+ Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, hsl_color.l, "%");+ return SASS_MEMORY_NEW(Number, pstate, hsl_color.l, "%"); } Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; BUILT_IN(adjust_hue) {- Color* rgb_color = ARG("$color", Color);- Number* degrees = ARG("$degrees", Number);+ Color_Ptr rgb_color = ARG("$color", Color);+ Number_Ptr degrees = ARG("$degrees", Number); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -462,8 +458,8 @@ Signature lighten_sig = "lighten($color, $amount)"; BUILT_IN(lighten) {- Color* rgb_color = ARG("$color", Color);- Number* amount = ARGR("$amount", Number, 0, 100);+ Color_Ptr rgb_color = ARG("$color", Color);+ Number_Ptr amount = ARGR("$amount", Number, 0, 100); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -484,8 +480,8 @@ Signature darken_sig = "darken($color, $amount)"; BUILT_IN(darken) {- Color* rgb_color = ARG("$color", Color);- Number* amount = ARGR("$amount", Number, 0, 100);+ Color_Ptr rgb_color = ARG("$color", Color);+ Number_Ptr amount = ARGR("$amount", Number, 0, 100); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -508,14 +504,13 @@ BUILT_IN(saturate) { // CSS3 filter function overload: pass literal through directly- Number* amount = dynamic_cast<Number*>(env["$amount"]);+ Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$amount"]); if (!amount) {- To_String to_string(&ctx);- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "saturate(" + env["$color"]->perform(&to_string) + ")");+ return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); } ARGR("$amount", Number, 0, 100);- Color* rgb_color = ARG("$color", Color);+ Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -541,8 +536,8 @@ Signature desaturate_sig = "desaturate($color, $amount)"; BUILT_IN(desaturate) {- Color* rgb_color = ARG("$color", Color);- Number* amount = ARGR("$amount", Number, 0, 100);+ Color_Ptr rgb_color = ARG("$color", Color);+ Number_Ptr amount = ARGR("$amount", Number, 0, 100); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -569,13 +564,12 @@ BUILT_IN(grayscale) { // CSS3 filter function overload: pass literal through directly- Number* amount = dynamic_cast<Number*>(env["$color"]);+ Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]); if (amount) {- To_String to_string(&ctx);- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "grayscale(" + amount->perform(&to_string) + ")");+ return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); } - Color* rgb_color = ARG("$color", Color);+ Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -590,7 +584,7 @@ Signature complement_sig = "complement($color)"; BUILT_IN(complement) {- Color* rgb_color = ARG("$color", Color);+ Color_Ptr rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b());@@ -606,14 +600,13 @@ BUILT_IN(invert) { // CSS3 filter function overload: pass literal through directly- Number* amount = dynamic_cast<Number*>(env["$color"]);+ Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]); if (amount) {- To_String to_string(&ctx);- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "invert(" + amount->perform(&to_string) + ")");+ return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); } - Color* rgb_color = ARG("$color", Color);- return SASS_MEMORY_NEW(ctx.mem, Color,+ Color_Ptr rgb_color = ARG("$color", Color);+ return SASS_MEMORY_NEW(Color, pstate, 255 - rgb_color->r(), 255 - rgb_color->g(),@@ -628,29 +621,28 @@ Signature opacity_sig = "opacity($color)"; BUILT_IN(alpha) {- String_Constant* ie_kwd = dynamic_cast<String_Constant*>(env["$color"]);+ String_Constant_Ptr ie_kwd = SASS_MEMORY_CAST(String_Constant, env["$color"]); if (ie_kwd) {- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");+ return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")"); } // CSS3 filter function overload: pass literal through directly- Number* amount = dynamic_cast<Number*>(env["$color"]);+ Number_Ptr amount = SASS_MEMORY_CAST(Number, env["$color"]); if (amount) {- To_String to_string(&ctx);- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "opacity(" + amount->perform(&to_string) + ")");+ return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); } - return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->a());+ return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a()); } Signature opacify_sig = "opacify($color, $amount)"; Signature fade_in_sig = "fade-in($color, $amount)"; BUILT_IN(opacify) {- Color* color = ARG("$color", Color);+ Color_Ptr color = ARG("$color", Color); double amount = ARGR("$amount", Number, 0, 1)->value(); double alpha = std::min(color->a() + amount, 1.0);- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r(), color->g(),@@ -662,10 +654,10 @@ Signature fade_out_sig = "fade-out($color, $amount)"; BUILT_IN(transparentize) {- Color* color = ARG("$color", Color);+ Color_Ptr color = ARG("$color", Color); double amount = ARGR("$amount", Number, 0, 1)->value(); double alpha = std::max(color->a() - amount, 0.0);- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r(), color->g(),@@ -680,27 +672,27 @@ Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; BUILT_IN(adjust_color) {- Color* color = ARG("$color", Color);- Number* r = dynamic_cast<Number*>(env["$red"]);- Number* g = dynamic_cast<Number*>(env["$green"]);- Number* b = dynamic_cast<Number*>(env["$blue"]);- Number* h = dynamic_cast<Number*>(env["$hue"]);- Number* s = dynamic_cast<Number*>(env["$saturation"]);- Number* l = dynamic_cast<Number*>(env["$lightness"]);- Number* a = dynamic_cast<Number*>(env["$alpha"]);+ Color_Ptr color = ARG("$color", Color);+ Number_Ptr r = SASS_MEMORY_CAST(Number, env["$red"]);+ Number_Ptr g = SASS_MEMORY_CAST(Number, env["$green"]);+ Number_Ptr b = SASS_MEMORY_CAST(Number, env["$blue"]);+ Number_Ptr h = SASS_MEMORY_CAST(Number, env["$hue"]);+ Number_Ptr s = SASS_MEMORY_CAST(Number, env["$saturation"]);+ Number_Ptr l = SASS_MEMORY_CAST(Number, env["$lightness"]);+ Number_Ptr a = SASS_MEMORY_CAST(Number, env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) {- error("cannot specify both RGB and HSL values for `adjust-color`", pstate);+ error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate); } if (rgb) { double rr = r ? ARGR("$red", Number, -255, 255)->value() : 0; double gg = g ? ARGR("$green", Number, -255, 255)->value() : 0; double bb = b ? ARGR("$blue", Number, -255, 255)->value() : 0; double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0;- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r() + rr, color->g() + gg,@@ -720,14 +712,14 @@ pstate); } if (a) {- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r(), color->g(), color->b(), color->a() + (a ? a->value() : 0)); }- error("not enough arguments for `adjust-color`", pstate);+ error("not enough arguments for `adjust-color'", pstate); // unreachable return color; }@@ -735,27 +727,27 @@ Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; BUILT_IN(scale_color) {- Color* color = ARG("$color", Color);- Number* r = dynamic_cast<Number*>(env["$red"]);- Number* g = dynamic_cast<Number*>(env["$green"]);- Number* b = dynamic_cast<Number*>(env["$blue"]);- Number* h = dynamic_cast<Number*>(env["$hue"]);- Number* s = dynamic_cast<Number*>(env["$saturation"]);- Number* l = dynamic_cast<Number*>(env["$lightness"]);- Number* a = dynamic_cast<Number*>(env["$alpha"]);+ Color_Ptr color = ARG("$color", Color);+ Number_Ptr r = SASS_MEMORY_CAST(Number, env["$red"]);+ Number_Ptr g = SASS_MEMORY_CAST(Number, env["$green"]);+ Number_Ptr b = SASS_MEMORY_CAST(Number, env["$blue"]);+ Number_Ptr h = SASS_MEMORY_CAST(Number, env["$hue"]);+ Number_Ptr s = SASS_MEMORY_CAST(Number, env["$saturation"]);+ Number_Ptr l = SASS_MEMORY_CAST(Number, env["$lightness"]);+ Number_Ptr a = SASS_MEMORY_CAST(Number, env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) {- error("cannot specify both RGB and HSL values for `scale-color`", pstate);+ error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate); } if (rgb) { double rscale = (r ? ARGR("$red", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double gscale = (g ? ARGR("$green", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double bscale = (b ? ARGR("$blue", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()), color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()),@@ -776,14 +768,14 @@ } if (a) { double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r(), color->g(), color->b(), color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); }- error("not enough arguments for `scale-color`", pstate);+ error("not enough arguments for `scale-color'", pstate); // unreachable return color; }@@ -791,23 +783,23 @@ Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; BUILT_IN(change_color) {- Color* color = ARG("$color", Color);- Number* r = dynamic_cast<Number*>(env["$red"]);- Number* g = dynamic_cast<Number*>(env["$green"]);- Number* b = dynamic_cast<Number*>(env["$blue"]);- Number* h = dynamic_cast<Number*>(env["$hue"]);- Number* s = dynamic_cast<Number*>(env["$saturation"]);- Number* l = dynamic_cast<Number*>(env["$lightness"]);- Number* a = dynamic_cast<Number*>(env["$alpha"]);+ Color_Ptr color = ARG("$color", Color);+ Number_Ptr r = SASS_MEMORY_CAST(Number, env["$red"]);+ Number_Ptr g = SASS_MEMORY_CAST(Number, env["$green"]);+ Number_Ptr b = SASS_MEMORY_CAST(Number, env["$blue"]);+ Number_Ptr h = SASS_MEMORY_CAST(Number, env["$hue"]);+ Number_Ptr s = SASS_MEMORY_CAST(Number, env["$saturation"]);+ Number_Ptr l = SASS_MEMORY_CAST(Number, env["$lightness"]);+ Number_Ptr a = SASS_MEMORY_CAST(Number, env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) {- error("cannot specify both RGB and HSL values for `change-color`", pstate);+ error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate); } if (rgb) {- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, r ? ARGR("$red", Number, 0, 255)->value() : color->r(), g ? ARGR("$green", Number, 0, 255)->value() : color->g(),@@ -816,7 +808,7 @@ } if (hsl) { HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());- if (h) hsl_struct.h = static_cast<double>(((static_cast<int>(h->value()) % 360) + 360) % 360) / 360.0;+ if (h) hsl_struct.h = std::fmod(h->value(), 360.0); if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value(); if (l) hsl_struct.l = ARGR("$lightness", Number, 0, 100)->value(); double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();@@ -824,14 +816,14 @@ } if (a) { double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();- return SASS_MEMORY_NEW(ctx.mem, Color,+ return SASS_MEMORY_NEW(Color, pstate, color->r(), color->g(), color->b(), alpha); }- error("not enough arguments for `change-color`", pstate);+ error("not enough arguments for `change-color'", pstate); // unreachable return color; }@@ -846,7 +838,7 @@ Signature ie_hex_str_sig = "ie-hex-str($color)"; BUILT_IN(ie_hex_str) {- Color* c = ARG("$color", Color);+ Color_Ptr c = ARG("$color", Color); double r = cap_channel<0xff>(c->r()); double g = cap_channel<0xff>(c->g()); double b = cap_channel<0xff>(c->b());@@ -854,16 +846,16 @@ std::stringstream ss; ss << '#' << std::setw(2) << std::setfill('0');- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a));- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r));- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g));- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision)); std::string result(ss.str()); for (size_t i = 0, L = result.length(); i < L; ++i) { result[i] = std::toupper(result[i]); }- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, result);+ return SASS_MEMORY_NEW(String_Quoted, pstate, result); } ///////////////////@@ -873,34 +865,41 @@ Signature unquote_sig = "unquote($string)"; BUILT_IN(sass_unquote) {- AST_Node* arg = env["$string"];- if (String_Quoted* string_quoted = dynamic_cast<String_Quoted*>(arg)) {- String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, string_quoted->value());+ AST_Node_Obj arg = env["$string"];+ if (String_Quoted_Ptr string_quoted = SASS_MEMORY_CAST(String_Quoted, arg)) {+ String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value()); // remember if the string was quoted (color tokens)- result->sass_fix_1291(string_quoted->quote_mark() != 0);+ result->is_delayed(true); // delay colors return result; }- else if (dynamic_cast<String_Constant*>(arg)) {- return (Expression*) arg;+ else if (SASS_MEMORY_CAST(String_Constant, arg)) {+ return (Expression_Ptr) &arg; } else {- To_String to_string(&ctx, false, true);- std::string val(arg->perform(&to_string));- val = dynamic_cast<Null*>(arg) ? "null" : val;+ Sass_Output_Style oldstyle = ctx.c_options.output_style;+ ctx.c_options.output_style = SASS_STYLE_NESTED;+ std::string val(arg->to_string(ctx.c_options));+ val = SASS_MEMORY_CAST(Null, arg) ? "null" : val;+ ctx.c_options.output_style = oldstyle; deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);- return (Expression*) arg;+ return (Expression_Ptr) &arg; } } Signature quote_sig = "quote($string)"; BUILT_IN(sass_quote) {- To_String to_string(&ctx);- AST_Node* arg = env["$string"];- std::string str(quote(arg->perform(&to_string), String_Constant::double_quote()));- String_Quoted* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);- result->is_delayed(true);+ AST_Node_Obj arg = env["$string"];+ // only set quote mark to true if already a string+ if (String_Quoted_Ptr qstr = SASS_MEMORY_CAST(String_Quoted, arg)) {+ qstr->quote_mark('*');+ return qstr;+ }+ // all other nodes must be converted to a string node+ std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote()));+ String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str);+ result->quote_mark('*'); return result; } @@ -910,7 +909,7 @@ { size_t len = std::string::npos; try {- String_Constant* s = ARG("$string", String_Constant);+ String_Constant_Ptr s = ARG("$string", String_Constant); len = UTF_8::code_point_count(s->value(), 0, s->value().size()); }@@ -918,7 +917,7 @@ // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); } // return something even if we had an error (-1)- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)len);+ return SASS_MEMORY_NEW(Number, pstate, (double)len); } Signature str_insert_sig = "str-insert($string, $insert, $index)";@@ -926,13 +925,13 @@ { std::string str; try {- String_Constant* s = ARG("$string", String_Constant);+ String_Constant_Ptr s = ARG("$string", String_Constant); str = s->value(); str = unquote(str);- String_Constant* i = ARG("$insert", String_Constant);+ String_Constant_Ptr i = ARG("$insert", String_Constant); std::string ins = i->value(); ins = unquote(ins);- Number* ind = ARG("$index", Number);+ Number_Ptr ind = ARG("$index", Number); double index = ind->value(); size_t len = UTF_8::code_point_count(str, 0, str.size()); @@ -957,14 +956,14 @@ str = ins + str; } - if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) { if (ss->quote_mark()) str = quote(str); } } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); }- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+ return SASS_MEMORY_NEW(String_Quoted, pstate, str); } Signature str_index_sig = "str-index($string, $substring)";@@ -972,8 +971,8 @@ { size_t index = std::string::npos; try {- String_Constant* s = ARG("$string", String_Constant);- String_Constant* t = ARG("$substring", String_Constant);+ String_Constant_Ptr s = ARG("$string", String_Constant);+ String_Constant_Ptr t = ARG("$substring", String_Constant); std::string str = s->value(); str = unquote(str); std::string substr = t->value();@@ -981,7 +980,7 @@ size_t c_index = str.find(substr); if(c_index == std::string::npos) {- return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ return SASS_MEMORY_NEW(Null, pstate); } index = UTF_8::code_point_count(str, 0, c_index) + 1; }@@ -989,7 +988,7 @@ // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); } // return something even if we had an error (-1)- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)index);+ return SASS_MEMORY_NEW(Number, pstate, (double)index); } Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";@@ -997,17 +996,33 @@ { std::string newstr; try {- String_Constant* s = ARG("$string", String_Constant);+ String_Constant_Ptr s = ARG("$string", String_Constant); double start_at = ARG("$start-at", Number)->value(); double end_at = ARG("$end-at", Number)->value();+ String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s); std::string str = unquote(s->value()); size_t size = utf8::distance(str.begin(), str.end());- if (end_at <= size * -1.0) { end_at += size; }- if (end_at < 0) { end_at += size + 1; }++ if (!SASS_MEMORY_CAST(Number, env["$end-at"])) {+ end_at = -1;+ }++ if (end_at == 0 || (end_at + size) < 0) {+ if (ss && ss->quote_mark()) newstr = quote("");+ return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);+ }++ if (end_at < 0) {+ end_at += size + 1;+ if (end_at == 0) end_at = 1;+ } if (end_at > size) { end_at = (double)size; }- if (start_at < 0) { start_at += size + 1; }+ if (start_at < 0) {+ start_at += size + 1;+ if (start_at < 0) start_at = 0;+ } else if (start_at == 0) { ++ start_at; } if (start_at <= end_at)@@ -1018,20 +1033,20 @@ utf8::advance(end, end_at - start_at + 1, str.end()); newstr = std::string(start, end); }- if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ if (ss) { if(ss->quote_mark()) newstr = quote(newstr); } } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); }- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, newstr);+ return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); } Signature to_upper_case_sig = "to-upper-case($string)"; BUILT_IN(to_upper_case) {- String_Constant* s = ARG("$string", String_Constant);+ String_Constant_Ptr s = ARG("$string", String_Constant); std::string str = s->value(); for (size_t i = 0, L = str.length(); i < L; ++i) {@@ -1040,19 +1055,19 @@ } } - if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {- String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss);+ if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+ String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); cpy->value(str); return cpy; } else {- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+ return SASS_MEMORY_NEW(String_Quoted, pstate, str); } } Signature to_lower_case_sig = "to-lower-case($string)"; BUILT_IN(to_lower_case) {- String_Constant* s = ARG("$string", String_Constant);+ String_Constant_Ptr s = ARG("$string", String_Constant); std::string str = s->value(); for (size_t i = 0, L = str.length(); i < L; ++i) {@@ -1061,12 +1076,12 @@ } } - if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {- String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss);+ if (String_Quoted_Ptr ss = SASS_MEMORY_CAST_PTR(String_Quoted, s)) {+ String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); cpy->value(str); return cpy; } else {- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+ return SASS_MEMORY_NEW(String_Quoted, pstate, str); } } @@ -1077,26 +1092,26 @@ Signature percentage_sig = "percentage($number)"; BUILT_IN(percentage) {- Number* n = ARG("$number", Number);+ Number_Ptr n = ARG("$number", Number); if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate);- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, n->value() * 100, "%");+ return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%"); } Signature round_sig = "round($number)"; BUILT_IN(round) {- Number* n = ARG("$number", Number);- Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+ Number_Ptr n = ARG("$number", Number);+ Number_Ptr r = SASS_MEMORY_COPY(n); r->pstate(pstate);- r->value(Sass::round(r->value()));+ r->value(Sass::round(r->value(), ctx.c_options.precision)); return r; } Signature ceil_sig = "ceil($number)"; BUILT_IN(ceil) {- Number* n = ARG("$number", Number);- Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+ Number_Ptr n = ARG("$number", Number);+ Number_Ptr r = SASS_MEMORY_COPY(n); r->pstate(pstate); r->value(std::ceil(r->value())); return r;@@ -1105,8 +1120,8 @@ Signature floor_sig = "floor($number)"; BUILT_IN(floor) {- Number* n = ARG("$number", Number);- Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+ Number_Ptr n = ARG("$number", Number);+ Number_Ptr r = SASS_MEMORY_COPY(n); r->pstate(pstate); r->value(std::floor(r->value())); return r;@@ -1115,8 +1130,8 @@ Signature abs_sig = "abs($number)"; BUILT_IN(abs) {- Number* n = ARG("$number", Number);- Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+ Number_Ptr n = ARG("$number", Number);+ Number_Ptr r = SASS_MEMORY_COPY(n); r->pstate(pstate); r->value(std::abs(r->value())); return r;@@ -1125,61 +1140,67 @@ Signature min_sig = "min($numbers...)"; BUILT_IN(min) {- List* arglist = ARG("$numbers", List);- Number* least = 0;+ List_Ptr arglist = ARG("$numbers", List);+ Number_Obj least = NULL; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));- if (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate);+ Expression_Obj val = arglist->value_at_index(i);+ Number_Obj xi = SASS_MEMORY_CAST(Number, val);+ if (!xi) {+ error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate);+ } if (least) {- if (Eval::lt(xi, least)) least = xi;+ if (*xi < *least) least = xi; } else least = xi; }- return least;+ return least.detach(); } Signature max_sig = "max($numbers...)"; BUILT_IN(max) {- List* arglist = ARG("$numbers", List);- Number* greatest = 0;+ List_Ptr arglist = ARG("$numbers", List);+ Number_Obj greatest = NULL; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));- if (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate);+ Expression_Obj val = arglist->value_at_index(i);+ Number_Obj xi = SASS_MEMORY_CAST(Number, val);+ if (!xi) {+ error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate);+ } if (greatest) {- if (Eval::lt(greatest, xi)) greatest = xi;+ if (*greatest < *xi) greatest = xi; } else greatest = xi; }- return greatest;+ return greatest.detach(); } Signature random_sig = "random($limit:false)"; BUILT_IN(random) {- AST_Node* arg = env["$limit"];- Value* v = dynamic_cast<Value*>(arg);- Number* l = dynamic_cast<Number*>(arg);- Boolean* b = dynamic_cast<Boolean*>(arg);+ AST_Node_Obj arg = env["$limit"];+ Value_Ptr v = SASS_MEMORY_CAST(Value, arg);+ Number_Ptr l = SASS_MEMORY_CAST(Number, arg);+ Boolean_Ptr b = SASS_MEMORY_CAST(Boolean, arg); if (l) { double v = l->value(); if (v < 1) { stringstream err;- err << "$limit " << v << " must be greater than or equal to 1 for `random`";+ err << "$limit " << v << " must be greater than or equal to 1 for `random'"; error(err.str(), pstate); } bool eq_int = std::fabs(trunc(v) - v) < NUMBER_EPSILON; if (!eq_int) { stringstream err;- err << "Expected $limit to be an integer but got `" << v << "` for `random`";+ err << "Expected $limit to be an integer but got " << v << " for `random'"; error(err.str(), pstate); } std::uniform_real_distribution<> distributor(1, v + 1); uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)distributed);+ return SASS_MEMORY_NEW(Number, pstate, (double)distributed); } else if (b) { std::uniform_real_distribution<> distributor(0, 1); double distributed = static_cast<double>(distributor(rand));- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, distributed);+ return SASS_MEMORY_NEW(Number, pstate, distributed); } else if (v) { throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number", v); } else {@@ -1195,26 +1216,26 @@ Signature length_sig = "length($list)"; BUILT_IN(length) {- if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)sl->length());+ if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) {+ return SASS_MEMORY_NEW(Number, pstate, (double)sl->length()); }- Expression* v = ARG("$list", Expression);+ Expression_Ptr v = ARG("$list", Expression); if (v->concrete_type() == Expression::MAP) {- Map* map = dynamic_cast<Map*>(env["$list"]);- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(map ? map->length() : 1));+ Map_Ptr map = SASS_MEMORY_CAST(Map, env["$list"]);+ return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1)); } if (v->concrete_type() == Expression::SELECTOR) {- if (Compound_Selector* h = dynamic_cast<Compound_Selector*>(v)) {- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)h->length());- } else if (Selector_List* ls = dynamic_cast<Selector_List*>(v)) {- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)ls->length());+ if (Compound_Selector_Ptr h = dynamic_cast<Compound_Selector_Ptr>(v)) {+ return SASS_MEMORY_NEW(Number, pstate, (double)h->length());+ } else if (Selector_List_Ptr ls = SASS_MEMORY_CAST_PTR(Selector_List, v)) {+ return SASS_MEMORY_NEW(Number, pstate, (double)ls->length()); } else {- return SASS_MEMORY_NEW(ctx.mem, Number, pstate, 1);+ return SASS_MEMORY_NEW(Number, pstate, 1); } } - List* list = dynamic_cast<List*>(env["$list"]);- return SASS_MEMORY_NEW(ctx.mem, Number,+ List_Ptr list = SASS_MEMORY_CAST(List, env["$list"]);+ return SASS_MEMORY_NEW(Number, pstate, (double)(list ? list->size() : 1)); }@@ -1222,24 +1243,24 @@ Signature nth_sig = "nth($list, $n)"; BUILT_IN(nth) {- Number* n = ARG("$n", Number);- Map* m = dynamic_cast<Map*>(env["$list"]);- if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {+ Number_Ptr n = ARG("$n", Number);+ Map_Ptr m = SASS_MEMORY_CAST(Map, env["$list"]);+ if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) { size_t len = m ? m->length() : sl->length(); bool empty = m ? m->empty() : sl->empty(); if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1); if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); // return (*sl)[static_cast<int>(index)];- Listize listize(ctx);+ Listize listize; return (*sl)[static_cast<int>(index)]->perform(&listize); }- List* l = dynamic_cast<List*>(env["$list"]);+ List_Obj l = SASS_MEMORY_CAST(List, env["$list"]); if (n->value() == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate); // if the argument isn't a list, then wrap it in a singleton list if (!m && !l) {- l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l << ARG("$list", Expression);+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression)); } size_t len = m ? m->length() : l->length(); bool empty = m ? m->empty() : l->empty();@@ -1248,32 +1269,38 @@ if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); if (m) {- l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l << m->keys()[static_cast<unsigned int>(index)];- *l << m->at(m->keys()[static_cast<unsigned int>(index)]);- return l;+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(m->keys()[static_cast<unsigned int>(index)]);+ l->append(m->at(m->keys()[static_cast<unsigned int>(index)]));+ return l.detach(); } else {- return l->value_at_index(static_cast<int>(index));+ Expression_Obj rv = l->value_at_index(static_cast<int>(index));+ rv->set_delayed(false);+ return rv.detach(); } } Signature set_nth_sig = "set-nth($list, $n, $value)"; BUILT_IN(set_nth) {- List* l = dynamic_cast<List*>(env["$list"]);- Number* n = ARG("$n", Number);- Expression* v = ARG("$value", Expression);+ Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);+ List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ Number_Obj n = ARG("$n", Number);+ Expression_Obj v = ARG("$value", Expression); if (!l) {- l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l << ARG("$list", Expression);+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression)); }+ if (m) {+ l = m->to_list(ctx, pstate);+ } if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);- List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length(), l->separator());+ List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator()); for (size_t i = 0, L = l->length(); i < L; ++i) {- *result << ((i == index) ? v : (*l)[i]);+ result->append(((i == index) ? v : (*l)[i])); } return result; }@@ -1281,77 +1308,93 @@ Signature index_sig = "index($list, $value)"; BUILT_IN(index) {- List* l = dynamic_cast<List*>(env["$list"]);- Expression* v = ARG("$value", Expression);+ Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);+ List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ Expression_Obj v = ARG("$value", Expression); if (!l) {- l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l << ARG("$list", Expression);+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression)); }+ if (m) {+ l = m->to_list(ctx, pstate);+ } for (size_t i = 0, L = l->length(); i < L; ++i) {- if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(i+1));+ if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1)); }- return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ return SASS_MEMORY_NEW(Null, pstate); } Signature join_sig = "join($list1, $list2, $separator: auto)"; BUILT_IN(join) {- List* l1 = dynamic_cast<List*>(env["$list1"]);- List* l2 = dynamic_cast<List*>(env["$list2"]);- String_Constant* sep = ARG("$separator", String_Constant);+ Map_Obj m1 = SASS_MEMORY_CAST(Map, env["$list1"]);+ Map_Obj m2 = SASS_MEMORY_CAST(Map, env["$list2"]);+ List_Obj l1 = SASS_MEMORY_CAST(List, env["$list1"]);+ List_Obj l2 = SASS_MEMORY_CAST(List, env["$list2"]);+ String_Constant_Obj sep = ARG("$separator", String_Constant); enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); if (!l1) {- l1 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l1 << ARG("$list1", Expression);+ l1 = SASS_MEMORY_NEW(List, pstate, 1);+ l1->append(ARG("$list1", Expression)); sep_val = (l2 ? l2->separator() : SASS_SPACE); } if (!l2) {- l2 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l2 << ARG("$list2", Expression);+ l2 = SASS_MEMORY_NEW(List, pstate, 1);+ l2->append(ARG("$list2", Expression)); }+ if (m1) {+ l1 = m1->to_list(ctx, pstate);+ sep_val = SASS_COMMA;+ }+ if (m2) {+ l2 = m2->to_list(ctx, pstate);+ } size_t len = l1->length() + l2->length(); std::string sep_str = unquote(sep->value()); if (sep_str == "space") sep_val = SASS_SPACE; else if (sep_str == "comma") sep_val = SASS_COMMA; else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);- List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, len, sep_val);- *result += l1;- *result += l2;- return result;+ List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val);+ result->concat(&l1);+ result->concat(&l2);+ return result.detach(); } Signature append_sig = "append($list, $val, $separator: auto)"; BUILT_IN(append) {- List* l = dynamic_cast<List*>(env["$list"]);- Expression* v = ARG("$val", Expression);- if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {- Listize listize(ctx);- l = dynamic_cast<List*>(sl->perform(&listize));+ Map_Obj m = SASS_MEMORY_CAST(Map, env["$list"]);+ List_Obj l = SASS_MEMORY_CAST(List, env["$list"]);+ Expression_Obj v = ARG("$val", Expression);+ if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, env["$list"])) {+ Listize listize;+ l = SASS_MEMORY_CAST_PTR(List, sl->perform(&listize)); }- String_Constant* sep = ARG("$separator", String_Constant);+ String_Constant_Obj sep = ARG("$separator", String_Constant); if (!l) {- l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l << ARG("$list", Expression);+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression)); }- List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length() + 1, l->separator());+ if (m) {+ l = m->to_list(ctx, pstate);+ }+ List_Ptr result = SASS_MEMORY_COPY(l); std::string sep_str(unquote(sep->value()));- if (sep_str == "space") result->separator(SASS_SPACE);- else if (sep_str == "comma") result->separator(SASS_COMMA);- else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);- *result += l;- bool is_arglist = l->is_arglist();- result->is_arglist(is_arglist);- if (is_arglist) {- *result << SASS_MEMORY_NEW(ctx.mem, Argument,- v->pstate(),- v,- "",- false,- false);+ if (sep_str != "auto") { // check default first+ if (sep_str == "space") result->separator(SASS_SPACE);+ else if (sep_str == "comma") result->separator(SASS_COMMA);+ else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);+ }+ if (l->is_arglist()) {+ result->append(SASS_MEMORY_NEW(Argument,+ v->pstate(),+ v,+ "",+ false,+ false)); } else {- *result << v;+ result->append(v); } return result; }@@ -1359,29 +1402,35 @@ Signature zip_sig = "zip($lists...)"; BUILT_IN(zip) {- List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$lists", List));+ List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List)); size_t shortest = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- List* ith = dynamic_cast<List*>(arglist->value_at_index(i));+ List_Obj ith = SASS_MEMORY_CAST(List, arglist->value_at_index(i));+ Map_Obj mith = SASS_MEMORY_CAST(Map, arglist->value_at_index(i)); if (!ith) {- ith = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *ith << arglist->value_at_index(i);+ if (mith) {+ ith = mith->to_list(ctx, pstate);+ } else {+ ith = SASS_MEMORY_NEW(List, pstate, 1);+ ith->append(arglist->value_at_index(i));+ } if (arglist->is_arglist()) {- ((Argument*)(*arglist)[i])->value(ith);+ Argument_Obj arg = (Argument_Ptr)(&arglist->at(i));+ arg->value(&ith); } else {- (*arglist)[i] = ith;+ (*arglist)[i] = &ith; } } shortest = (i ? std::min(shortest, ith->length()) : ith->length()); }- List* zippers = SASS_MEMORY_NEW(ctx.mem, List, pstate, shortest, SASS_COMMA);+ List_Ptr zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA); size_t L = arglist->length(); for (size_t i = 0; i < shortest; ++i) {- List* zipper = SASS_MEMORY_NEW(ctx.mem, List, pstate, L);+ List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L); for (size_t j = 0; j < L; ++j) {- *zipper << (*static_cast<List*>(arglist->value_at_index(j)))[i];+ zipper->append(SASS_MEMORY_CAST(List, arglist->value_at_index(j))->at(i)); }- *zippers << zipper;+ zippers->append(zipper); } return zippers; }@@ -1389,12 +1438,12 @@ Signature list_separator_sig = "list_separator($list)"; BUILT_IN(list_separator) {- List* l = dynamic_cast<List*>(env["$list"]);+ List_Obj l = SASS_MEMORY_CAST(List, env["$list"]); if (!l) {- l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);- *l << ARG("$list", Expression);+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression)); }- return SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ return SASS_MEMORY_NEW(String_Quoted, pstate, l->separator() == SASS_COMMA ? "comma" : "space"); }@@ -1406,12 +1455,15 @@ Signature map_get_sig = "map-get($map, $key)"; BUILT_IN(map_get) {- Map* m = ARGM("$map", Map, ctx);- Expression* v = ARG("$key", Expression);+ // leaks for "map-get((), foo)" if not Obj+ // investigate why this is (unexpected)+ Map_Obj m = ARGM("$map", Map, ctx);+ Expression_Obj v = ARG("$key", Expression); try {- return m->at(v);+ Expression_Obj val = m->at(v); // XXX+ return val ? val.detach() : SASS_MEMORY_NEW(Null, pstate); } catch (const std::out_of_range&) {- return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ return SASS_MEMORY_NEW(Null, pstate); } catch (...) { throw; } }@@ -1419,18 +1471,18 @@ Signature map_has_key_sig = "map-has-key($map, $key)"; BUILT_IN(map_has_key) {- Map* m = ARGM("$map", Map, ctx);- Expression* v = ARG("$key", Expression);- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, m->has(v));+ Map_Obj m = ARGM("$map", Map, ctx);+ Expression_Obj v = ARG("$key", Expression);+ return SASS_MEMORY_NEW(Boolean, pstate, m->has(v)); } Signature map_keys_sig = "map-keys($map)"; BUILT_IN(map_keys) {- Map* m = ARGM("$map", Map, ctx);- List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_COMMA);+ Map_Obj m = ARGM("$map", Map, ctx);+ List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); for ( auto key : m->keys()) {- *result << key;+ result->append(key); } return result; }@@ -1438,10 +1490,10 @@ Signature map_values_sig = "map-values($map)"; BUILT_IN(map_values) {- Map* m = ARGM("$map", Map, ctx);- List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_COMMA);+ Map_Obj m = ARGM("$map", Map, ctx);+ List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); for ( auto key : m->keys()) {- *result << m->at(key);+ result->append(m->at(key)); } return result; }@@ -1449,13 +1501,14 @@ Signature map_merge_sig = "map-merge($map1, $map2)"; BUILT_IN(map_merge) {- Map* m1 = ARGM("$map1", Map, ctx);- Map* m2 = ARGM("$map2", Map, ctx);+ Map_Obj m1 = ARGM("$map1", Map, ctx);+ Map_Obj m2 = ARGM("$map2", Map, ctx); size_t len = m1->length() + m2->length();- Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, len);- *result += m1;- *result += m2;+ Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len);+ // concat not implemented for maps+ *result += &m1;+ *result += &m2; return result; } @@ -1463,9 +1516,9 @@ BUILT_IN(map_remove) { bool remove;- Map* m = ARGM("$map", Map, ctx);- List* arglist = ARG("$keys", List);- Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);+ Map_Obj m = ARGM("$map", Map, ctx);+ List_Obj arglist = ARG("$keys", List);+ Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, 1); for (auto key : m->keys()) { remove = false; for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {@@ -1479,16 +1532,18 @@ Signature keywords_sig = "keywords($args)"; BUILT_IN(keywords) {- List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List));- Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);+ List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy+ Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1); for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {- std::string name = std::string(((Argument*)(*arglist)[i])->name());+ Expression_Obj obj = arglist->at(i);+ Argument_Obj arg = (Argument_Ptr)&obj;+ std::string name = std::string(arg->name()); name = name.erase(0, 1); // sanitize name (remove dollar sign)- *result << std::make_pair(SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted, pstate, name),- ((Argument*)(*arglist)[i])->value());+ arg->value()); }- return result;+ return result.detach(); } //////////////////////////@@ -1498,29 +1553,29 @@ Signature type_of_sig = "type-of($value)"; BUILT_IN(type_of) {- Expression* v = ARG("$value", Expression);- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, v->type());+ Expression_Ptr v = ARG("$value", Expression);+ return SASS_MEMORY_NEW(String_Quoted, pstate, v->type()); } Signature unit_sig = "unit($number)"; BUILT_IN(unit)- { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); }+ { return SASS_MEMORY_NEW(String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); } Signature unitless_sig = "unitless($number)"; BUILT_IN(unitless)- { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, ARG("$number", Number)->is_unitless()); }+ { return SASS_MEMORY_NEW(Boolean, pstate, ARG("$number", Number)->is_unitless()); } Signature comparable_sig = "comparable($number-1, $number-2)"; BUILT_IN(comparable) {- Number* n1 = ARG("$number-1", Number);- Number* n2 = ARG("$number-2", Number);+ Number_Ptr n1 = ARG("$number-1", Number);+ Number_Ptr n2 = ARG("$number-2", Number); if (n1->is_unitless() || n2->is_unitless()) {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ return SASS_MEMORY_NEW(Boolean, pstate, true); }- Number tmp_n2(*n2);+ Number tmp_n2(n2); // copy tmp_n2.normalize(n1->find_convertible_unit());- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, n1->unit() == tmp_n2.unit());+ return SASS_MEMORY_NEW(Boolean, pstate, n1->unit() == tmp_n2.unit()); } Signature variable_exists_sig = "variable-exists($name)";@@ -1529,10 +1584,10 @@ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has("$"+s)) {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ return SASS_MEMORY_NEW(Boolean, pstate, true); } else {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ return SASS_MEMORY_NEW(Boolean, pstate, false); } } @@ -1542,10 +1597,10 @@ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has_global("$"+s)) {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ return SASS_MEMORY_NEW(Boolean, pstate, true); } else {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ return SASS_MEMORY_NEW(Boolean, pstate, false); } } @@ -1555,10 +1610,10 @@ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has_global(s+"[f]")) {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ return SASS_MEMORY_NEW(Boolean, pstate, true); } else {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ return SASS_MEMORY_NEW(Boolean, pstate, false); } } @@ -1568,10 +1623,10 @@ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has_global(s+"[m]")) {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ return SASS_MEMORY_NEW(Boolean, pstate, true); } else {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ return SASS_MEMORY_NEW(Boolean, pstate, false); } } @@ -1581,10 +1636,10 @@ std::string s = unquote(ARG("$name", String_Constant)->value()); if(features.find(s) == features.end()) {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ return SASS_MEMORY_NEW(Boolean, pstate, false); } else {- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ return SASS_MEMORY_NEW(Boolean, pstate, true); } } @@ -1592,34 +1647,36 @@ BUILT_IN(call) { std::string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));- List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List));+ List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); - Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);+ Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); // std::string full_name(name + "[f]");- // Definition* def = d_env.has(full_name) ? static_cast<Definition*>((d_env)[full_name]) : 0;- // Parameters* params = def ? def->parameters() : 0;+ // Definition_Ptr def = d_env.has(full_name) ? static_cast<Definition_Ptr>((d_env)[full_name]) : 0;+ // Parameters_Ptr params = def ? def->parameters() : 0; // size_t param_size = params ? params->length() : 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression* expr = arglist->value_at_index(i);+ Expression_Obj expr = arglist->value_at_index(i); // if (params && params->has_rest_parameter()) {- // Parameter* p = param_size > i ? (*params)[i] : 0;- // List* list = dynamic_cast<List*>(expr);+ // Parameter_Obj p = param_size > i ? (*params)[i] : 0;+ // List_Ptr list = SASS_MEMORY_CAST(List, expr); // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; // } if (arglist->is_arglist()) {- Argument* arg = dynamic_cast<Argument*>((*arglist)[i]);- *args << SASS_MEMORY_NEW(ctx.mem, Argument,- pstate,- expr,- arg ? arg->name() : "",- arg ? arg->is_rest_argument() : false,- arg ? arg->is_keyword_argument() : false);+ Expression_Obj obj = arglist->at(i);+ Argument_Obj arg = (Argument_Ptr)&obj;+ args->append(SASS_MEMORY_NEW(Argument,+ pstate,+ expr,+ arg ? arg->name() : "",+ arg ? arg->is_rest_argument() : false,+ arg ? arg->is_keyword_argument() : false)); } else {- *args << SASS_MEMORY_NEW(ctx.mem, Argument, pstate, expr);+ args->append(SASS_MEMORY_NEW(Argument, pstate, expr)); } }- Function_Call* func = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, name, args);- Expand expand(ctx, &d_env, backtrace);+ Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, &args);+ Expand expand(ctx, &d_env, backtrace, &selector_stack);+ func->via_call(true); // calc invoke is allowed return func->perform(&expand.eval); }@@ -1630,21 +1687,22 @@ Signature not_sig = "not($value)"; BUILT_IN(sass_not)- { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, ARG("$value", Expression)->is_false()); }+ {+ return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());+ } Signature if_sig = "if($condition, $if-true, $if-false)"; // BUILT_IN(sass_if) // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } BUILT_IN(sass_if) {- Expand expand(ctx, &d_env, backtrace);- bool is_true = !ARG("$condition", Expression)->perform(&expand.eval)->is_false();- if (is_true) {- return ARG("$if-true", Expression)->perform(&expand.eval);- }- else {- return ARG("$if-false", Expression)->perform(&expand.eval);- }+ Expand expand(ctx, &d_env, backtrace, &selector_stack);+ Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);+ bool is_true = !cond->is_false();+ Expression_Ptr res = ARG(is_true ? "$if-true" : "$if-false", Expression);+ res = res->perform(&expand.eval);+ res->set_delayed(false); // clone?+ return res; } ////////////////@@ -1662,117 +1720,126 @@ // MISCELLANEOUS FUNCTIONS ////////////////////////// + // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String)+ // unquoted_string(value.to_sass)+ Signature inspect_sig = "inspect($value)"; BUILT_IN(inspect) {- Expression* v = ARG("$value", Expression);+ Expression_Ptr v = ARG("$value", Expression); if (v->concrete_type() == Expression::NULL_VAL) {- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "null");- } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) {- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "false");+ return SASS_MEMORY_NEW(String_Quoted, pstate, "null");+ } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {+ return SASS_MEMORY_NEW(String_Quoted, pstate, "false"); } else if (v->concrete_type() == Expression::STRING) { return v; } else {- bool parentheses = v->concrete_type() == Expression::MAP ||- v->concrete_type() == Expression::LIST;+ // ToDo: fix to_sass for nested parentheses Sass_Output_Style old_style;- old_style = ctx.c_options->output_style;- ctx.c_options->output_style = SASS_STYLE_NESTED;- To_String to_string(&ctx, false);- std::string inspect = v->perform(&to_string);- if (inspect.empty() && parentheses) inspect = "()";- ctx.c_options->output_style = old_style;- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, inspect);+ old_style = ctx.c_options.output_style;+ ctx.c_options.output_style = TO_SASS;+ Emitter emitter(ctx.c_options);+ Inspect i(emitter);+ i.in_declaration = false;+ v->perform(&i);+ ctx.c_options.output_style = old_style;+ return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer()); } // return v; } Signature selector_nest_sig = "selector-nest($selectors...)"; BUILT_IN(selector_nest) {- To_String to_string(&ctx, false);- List* arglist = ARG("$selectors", List);+ List_Ptr arglist = ARG("$selectors", List); // Not enough parameters if( arglist->length() == 0 )- error("$selectors: At least one selector must be passed", pstate);+ error("$selectors: At least one selector must be passed for `selector-nest'", pstate); // Parse args into vector of selectors- std::vector<Selector_List*> parsedSelectors;+ std::vector<Selector_List_Obj> parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression* exp = dynamic_cast<Expression*>(arglist->value_at_index(i));+ Expression_Obj exp = SASS_MEMORY_CAST(Expression, arglist->value_at_index(i)); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << "$selectors: null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for 'selector-nest'"; error(msg.str(), pstate); }- std::string exp_src = exp->perform(&to_string) + "{";- Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx);- parsedSelectors.push_back(sel);+ if (String_Constant_Obj str = SASS_MEMORY_CAST(String_Constant, exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string(ctx.c_options) + "{";+ Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx);+ parsedSelectors.push_back(&sel); } // Nothing to do if( parsedSelectors.empty() ) {- return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ return SASS_MEMORY_NEW(Null, pstate); } // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.- std::vector<Selector_List*>::iterator itr = parsedSelectors.begin();- Selector_List* result = *itr;+ std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();+ Selector_List_Obj result = *itr; ++itr; for(;itr != parsedSelectors.end(); ++itr) {- Selector_List* child = *itr;- std::vector<Complex_Selector*> exploded;- Selector_List* rv = child->parentize(result, ctx);+ Selector_List_Obj child = *itr;+ std::vector<Complex_Selector_Obj> exploded;+ selector_stack.push_back(&result);+ Selector_List_Obj rv = child->resolve_parent_refs(ctx, selector_stack);+ selector_stack.pop_back(); for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) { exploded.push_back((*rv)[m]); } result->elements(exploded); } - Listize listize(ctx);+ Listize listize; return result->perform(&listize); } Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) {- To_String to_string;- List* arglist = ARG("$selectors", List);+ List_Ptr arglist = ARG("$selectors", List); // Not enough parameters if( arglist->length() == 0 )- error("$selectors: At least one selector must be passed", pstate);+ error("$selectors: At least one selector must be passed for `selector-append'", pstate); // Parse args into vector of selectors- std::vector<Selector_List*> parsedSelectors;+ std::vector<Selector_List_Obj> parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression* exp = dynamic_cast<Expression*>(arglist->value_at_index(i));+ Expression_Obj exp = SASS_MEMORY_CAST(Expression, arglist->value_at_index(i)); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << "$selectors: null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for 'selector-append'"; error(msg.str(), pstate); }- std::string exp_src = exp->perform(&to_string) + "{";- Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx);- parsedSelectors.push_back(sel);+ if (String_Constant_Ptr str = SASS_MEMORY_CAST(String_Constant, exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string() + "{";+ Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx);+ parsedSelectors.push_back(&sel); } // Nothing to do if( parsedSelectors.empty() ) {- return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ return SASS_MEMORY_NEW(Null, pstate); } // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.- std::vector<Selector_List*>::iterator itr = parsedSelectors.begin();- Selector_List* result = *itr;+ std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();+ Selector_List_Obj result = *itr; ++itr; for(;itr != parsedSelectors.end(); ++itr) {- Selector_List* child = *itr;- std::vector<Complex_Selector*> newElements;+ Selector_List_Obj child = *itr;+ std::vector<Complex_Selector_Obj> newElements; // For every COMPLEX_SELECTOR in `result` // For every COMPLEX_SELECTOR in `child`@@ -1783,74 +1850,73 @@ // Replace result->elements with newElements for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { for (size_t j = 0, childLen = child->length(); j < childLen; ++j) {- Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx);- Complex_Selector* childSeq = (*child)[j];- Complex_Selector* base = childSeq->tail();+ Complex_Selector_Obj parentSeqClone = SASS_MEMORY_CLONE((*result)[i]);+ Complex_Selector_Obj childSeq = (*child)[j];+ Complex_Selector_Obj base = childSeq->tail(); // Must be a simple sequence if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) {- std::string msg("Can't append `");- msg += childSeq->perform(&to_string);- msg += "` to `";- msg += parentSeqClone->perform(&to_string);;- msg += "`";+ std::string msg("Can't append \"");+ msg += childSeq->to_string();+ msg += "\" to \"";+ msg += parentSeqClone->to_string();+ msg += "\" for `selector-append'"; error(msg, pstate, backtrace); } // Cannot be a Universal selector- Type_Selector* pType = dynamic_cast<Type_Selector*>(base->head()->first());+ Element_Selector_Obj pType = SASS_MEMORY_CAST(Element_Selector, childSeq->head()->first()); if(pType && pType->name() == "*") {- std::string msg("Can't append `");- msg += childSeq->perform(&to_string);- msg += "` to `";- msg += parentSeqClone->perform(&to_string);;- msg += "`";+ std::string msg("Can't append \"");+ msg += childSeq->to_string();+ msg += "\" to \"";+ msg += parentSeqClone->to_string();+ msg += "\" for `selector-append'"; error(msg, pstate, backtrace); } // TODO: Add check for namespace stuff // append any selectors in childSeq's head- *(parentSeqClone->innermost()->head()) += (base->head());+ parentSeqClone->innermost()->head()->concat(&base->head()); // Set parentSeqClone new tail parentSeqClone->innermost()->tail( base->tail() ); - newElements.push_back(parentSeqClone);+ newElements.push_back(&parentSeqClone); } } result->elements(newElements); } - Listize listize(ctx);+ Listize listize; return result->perform(&listize); } Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; BUILT_IN(selector_unify) {- Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize);- Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize);+ Selector_List_Obj selector1 = ARGSEL("$selector1", Selector_List_Obj, p_contextualize);+ Selector_List_Obj selector2 = ARGSEL("$selector2", Selector_List_Obj, p_contextualize); - Selector_List* result = selector1->unify_with(selector2, ctx);- Listize listize(ctx);+ Selector_List_Obj result = selector1->unify_with(&selector2, ctx);+ Listize listize; return result->perform(&listize); } Signature simple_selectors_sig = "simple-selectors($selector)"; BUILT_IN(simple_selectors) {- Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize);+ Compound_Selector_Obj sel = ARGSEL("$selector", Compound_Selector_Obj, p_contextualize); - To_String to_string;- List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA);+ List_Ptr l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) {- Simple_Selector* ss = (*sel)[i];- std::string ss_string = ss->perform(&to_string) ;+ Simple_Selector_Obj ss = (*sel)[i];+ std::string ss_string = ss->to_string() ; - *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, ss->pstate(), ss_string);+ l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string)); } return l;@@ -1859,54 +1925,50 @@ Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; BUILT_IN(selector_extend) {- Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize);- Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize);- Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize);+ Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize);+ Selector_List_Obj extendee = ARGSEL("$extendee", Selector_List_Obj, p_contextualize);+ Selector_List_Obj extender = ARGSEL("$extender", Selector_List_Obj, p_contextualize); - ExtensionSubsetMap subset_map;+ Subset_Map subset_map; extender->populate_extends(extendee, ctx, subset_map); - bool extendedSomething;- Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething);+ Selector_List_Obj result = Extend::extendSelectorList(selector, ctx, subset_map, false); - Listize listize(ctx);+ Listize listize; return result->perform(&listize); } Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; BUILT_IN(selector_replace) {- Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize);- Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize);- Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize);-- ExtensionSubsetMap subset_map;+ Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize);+ Selector_List_Obj original = ARGSEL("$original", Selector_List_Obj, p_contextualize);+ Selector_List_Obj replacement = ARGSEL("$replacement", Selector_List_Obj, p_contextualize);+ Subset_Map subset_map; replacement->populate_extends(original, ctx, subset_map); - bool extendedSomething;- Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething);+ Selector_List_Obj result = Extend::extendSelectorList(selector, ctx, subset_map, true); - Listize listize(ctx);+ Listize listize; return result->perform(&listize); } Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) {- Selector_List* sel = ARGSEL("$selector", Selector_List, p_contextualize);+ Selector_List_Obj sel = ARGSEL("$selector", Selector_List_Obj, p_contextualize); - Listize listize(ctx);+ Listize listize; return sel->perform(&listize); } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) {- To_String to_string(&ctx, false);- Selector_List* sel_sup = ARGSEL("$super", Selector_List, p_contextualize);- Selector_List* sel_sub = ARGSEL("$sub", Selector_List, p_contextualize);+ Selector_List_Obj sel_sup = ARGSEL("$super", Selector_List_Obj, p_contextualize);+ Selector_List_Obj sel_sub = ARGSEL("$sub", Selector_List_Obj, p_contextualize); bool result = sel_sup->is_superselector_of(sel_sub);- return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, result);+ return SASS_MEMORY_NEW(Boolean, pstate, result); } Signature unique_id_sig = "unique-id()";@@ -1916,7 +1978,7 @@ std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8 uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand)); ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, ss.str());+ return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str()); } }
libsass/src/functions.hpp view
@@ -1,25 +1,23 @@ #ifndef SASS_FUNCTIONS_H #define SASS_FUNCTIONS_H +#include "listize.hpp" #include "position.hpp" #include "environment.hpp"+#include "ast_fwd_decl.hpp" #include "sass/functions.h" -#define BUILT_IN(name) Expression*\-name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace)+#define BUILT_IN(name) Expression_Ptr \+name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace, std::vector<Selector_List_Obj> selector_stack) namespace Sass {- class Context; struct Backtrace;- class AST_Node;- class Expression;- class Definition;- typedef Environment<AST_Node*> Env;+ typedef Environment<AST_Node_Obj> Env; typedef const char* Signature;- typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);+ typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*, std::vector<Selector_List_Obj>); - Definition* make_native_function(Signature, Native_Function, Context& ctx);- Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);+ Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx);+ Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx); std::string function_name(Signature);
libsass/src/inspect.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cmath> #include <string> #include <iostream>@@ -20,17 +21,17 @@ Inspect::~Inspect() { } // statements- void Inspect::operator()(Block* block)+ void Inspect::operator()(Block_Ptr block) { if (!block->is_root()) { add_open_mapping(block); append_scope_opener(); }- if (output_style() == SASS_STYLE_NESTED) indentation += block->tabs();+ if (output_style() == NESTED) indentation += block->tabs(); for (size_t i = 0, L = block->length(); i < L; ++i) { (*block)[i]->perform(this); }- if (output_style() == SASS_STYLE_NESTED) indentation -= block->tabs();+ if (output_style() == NESTED) indentation -= block->tabs(); if (!block->is_root()) { append_scope_closer(); add_close_mapping(block);@@ -38,26 +39,23 @@ } - void Inspect::operator()(Ruleset* ruleset)+ void Inspect::operator()(Ruleset_Ptr ruleset) {- ruleset->selector()->perform(this);- ruleset->block()->perform(this);+ if (ruleset->selector()) {+ ruleset->selector()->perform(this);+ }+ if (ruleset->block()) {+ ruleset->block()->perform(this);+ } } - void Inspect::operator()(Keyframe_Rule* rule)+ void Inspect::operator()(Keyframe_Rule_Ptr rule) {- if (rule->selector()) rule->selector()->perform(this);+ if (rule->name()) rule->name()->perform(this); if (rule->block()) rule->block()->perform(this); } - void Inspect::operator()(Propset* propset)- {- propset->property_fragment()->perform(this);- append_colon_separator();- propset->block()->perform(this);- }-- void Inspect::operator()(Bubble* bubble)+ void Inspect::operator()(Bubble_Ptr bubble) { append_indentation(); append_token("::BUBBLE", bubble);@@ -66,7 +64,7 @@ append_scope_closer(); } - void Inspect::operator()(Media_Block* media_block)+ void Inspect::operator()(Media_Block_Ptr media_block) { append_indentation(); append_token("@media", media_block);@@ -77,7 +75,7 @@ media_block->block()->perform(this); } - void Inspect::operator()(Supports_Block* feature_block)+ void Inspect::operator()(Supports_Block_Ptr feature_block) { append_indentation(); append_token("@supports", feature_block);@@ -86,7 +84,7 @@ feature_block->block()->perform(this); } - void Inspect::operator()(At_Root_Block* at_root_block)+ void Inspect::operator()(At_Root_Block_Ptr at_root_block) { append_indentation(); append_token("@at-root ", at_root_block);@@ -95,7 +93,7 @@ at_root_block->block()->perform(this); } - void Inspect::operator()(At_Rule* at_rule)+ void Inspect::operator()(Directive_Ptr at_rule) { append_indentation(); append_token(at_rule->keyword(), at_rule);@@ -118,20 +116,22 @@ } } - void Inspect::operator()(Declaration* dec)+ void Inspect::operator()(Declaration_Ptr dec) { if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true;- if (output_style() == SASS_STYLE_NESTED)+ if (output_style() == NESTED) indentation += dec->tabs(); append_indentation();- dec->property()->perform(this);+ if (dec->property())+ dec->property()->perform(this); append_colon_separator(); if (dec->value()->concrete_type() == Expression::SELECTOR) {- Listize listize(*ctx);- dec->value()->perform(&listize)->perform(this);+ Listize listize;+ Expression_Obj ls = dec->value()->perform(&listize);+ ls->perform(this); } else { dec->value()->perform(this); }@@ -141,12 +141,12 @@ append_string("!important"); } append_delimiter();- if (output_style() == SASS_STYLE_NESTED)+ if (output_style() == NESTED) indentation -= dec->tabs(); in_declaration = was_decl; } - void Inspect::operator()(Assignment* assn)+ void Inspect::operator()(Assignment_Ptr assn) { append_token(assn->variable(), assn); append_colon_separator();@@ -158,20 +158,18 @@ append_delimiter(); } - void Inspect::operator()(Import* import)+ void Inspect::operator()(Import_Ptr import) { if (!import->urls().empty()) { append_token("@import", import); append_mandatory_space(); - if (String_Quoted* strq = dynamic_cast<String_Quoted*>(import->urls().front())) {- strq->is_delayed(false);- }- import->urls().front()->perform(this);- if (import->media_queries()) {- append_mandatory_space();- import->media_queries()->perform(this);+ if (import->urls().size() == 1) {+ if (&import->import_queries()) {+ append_mandatory_space();+ import->import_queries()->perform(this);+ } } append_delimiter(); for (size_t i = 1, S = import->urls().size(); i < S; ++i) {@@ -179,21 +177,19 @@ append_token("@import", import); append_mandatory_space(); - if (String_Quoted* strq = dynamic_cast<String_Quoted*>(import->urls()[i])) {- strq->is_delayed(false);- }- import->urls()[i]->perform(this);- if (import->media_queries()) {- append_mandatory_space();- import->media_queries()->perform(this);+ if (import->urls().size() - 1 == i) {+ if (&import->import_queries()) {+ append_mandatory_space();+ import->import_queries()->perform(this);+ } } append_delimiter(); } } } - void Inspect::operator()(Import_Stub* import)+ void Inspect::operator()(Import_Stub_Ptr import) { append_indentation(); append_token("@import", import);@@ -202,7 +198,7 @@ append_delimiter(); } - void Inspect::operator()(Warning* warning)+ void Inspect::operator()(Warning_Ptr warning) { append_indentation(); append_token("@warn", warning);@@ -211,7 +207,7 @@ append_delimiter(); } - void Inspect::operator()(Error* error)+ void Inspect::operator()(Error_Ptr error) { append_indentation(); append_token("@error", error);@@ -220,7 +216,7 @@ append_delimiter(); } - void Inspect::operator()(Debug* debug)+ void Inspect::operator()(Debug_Ptr debug) { append_indentation(); append_token("@debug", debug);@@ -229,14 +225,14 @@ append_delimiter(); } - void Inspect::operator()(Comment* comment)+ void Inspect::operator()(Comment_Ptr comment) { in_comment = true; comment->text()->perform(this); in_comment = false; } - void Inspect::operator()(If* cond)+ void Inspect::operator()(If_Ptr cond) { append_indentation(); append_token("@if", cond);@@ -251,7 +247,7 @@ } } - void Inspect::operator()(For* loop)+ void Inspect::operator()(For_Ptr loop) { append_indentation(); append_token("@for", loop);@@ -264,7 +260,7 @@ loop->block()->perform(this); } - void Inspect::operator()(Each* loop)+ void Inspect::operator()(Each_Ptr loop) { append_indentation(); append_token("@each", loop);@@ -279,7 +275,7 @@ loop->block()->perform(this); } - void Inspect::operator()(While* loop)+ void Inspect::operator()(While_Ptr loop) { append_indentation(); append_token("@while", loop);@@ -288,7 +284,7 @@ loop->block()->perform(this); } - void Inspect::operator()(Return* ret)+ void Inspect::operator()(Return_Ptr ret) { append_indentation(); append_token("@return", ret);@@ -297,7 +293,7 @@ append_delimiter(); } - void Inspect::operator()(Extension* extend)+ void Inspect::operator()(Extension_Ptr extend) { append_indentation(); append_token("@extend", extend);@@ -306,7 +302,7 @@ append_delimiter(); } - void Inspect::operator()(Definition* def)+ void Inspect::operator()(Definition_Ptr def) { append_indentation(); if (def->type() == Definition::MIXIN) {@@ -321,7 +317,7 @@ def->block()->perform(this); } - void Inspect::operator()(Mixin_Call* call)+ void Inspect::operator()(Mixin_Call_Ptr call) { append_indentation(); append_token("@include", call);@@ -337,15 +333,19 @@ if (!call->block()) append_delimiter(); } - void Inspect::operator()(Content* content)+ void Inspect::operator()(Content_Ptr content) { append_indentation(); append_token("@content", content); append_delimiter(); } - void Inspect::operator()(Map* map)+ void Inspect::operator()(Map_Ptr map) {+ if (output_style() == TO_SASS && map->empty()) {+ append_string("()");+ return;+ } if (map->empty()) return; if (map->is_invisible()) return; bool items_output = false;@@ -360,17 +360,29 @@ append_string(")"); } - void Inspect::operator()(List* list)+ void Inspect::operator()(List_Ptr list) {+ if (output_style() == TO_SASS && list->empty()) {+ append_string("()");+ return;+ } std::string sep(list->separator() == SASS_SPACE ? " " : ",");- if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && sep == ",") sep += " ";+ if ((output_style() != COMPRESSED) && sep == ",") sep += " "; else if (in_media_block && sep != " ") sep += " "; // verified if (list->empty()) return; bool items_output = false; bool was_space_array = in_space_array; bool was_comma_array = in_comma_array;- if (!in_declaration && (+ // probably ruby sass eqivalent of element_needs_parens+ if (output_style() == TO_SASS &&+ list->length() == 1 &&+ !list->from_selector() &&+ !SASS_MEMORY_CAST(List, list->at(0)) &&+ !SASS_MEMORY_CAST(Selector_List, list->at(0))) {+ append_string("(");+ }+ else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) {@@ -381,9 +393,16 @@ else if (list->separator() == SASS_COMMA) in_comma_array = true; for (size_t i = 0, L = list->size(); i < L; ++i) {- Expression* list_item = (*list)[i];- if (list_item->is_invisible()) {- continue;+ if (list->separator() == SASS_HASH)+ { sep[0] = i % 2 ? ':' : ','; }+ Expression_Obj list_item = list->at(i);+ if (output_style() != TO_SASS) {+ if (list_item->is_invisible()) {+ // this fixes an issue with "" in a list+ if (!SASS_MEMORY_CAST(String_Constant, list_item)) {+ continue;+ }+ } } if (items_output) { append_string(sep);@@ -396,7 +415,15 @@ in_comma_array = was_comma_array; in_space_array = was_space_array;- if (!in_declaration && (+ // probably ruby sass eqivalent of element_needs_parens+ if (output_style() == TO_SASS &&+ list->length() == 1 &&+ !list->from_selector() &&+ !SASS_MEMORY_CAST(List, list->at(0)) &&+ !SASS_MEMORY_CAST(Selector_List, list->at(0))) {+ append_string(",)");+ }+ else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) {@@ -405,85 +432,258 @@ } - void Inspect::operator()(Binary_Expression* expr)+ void Inspect::operator()(Binary_Expression_Ptr expr) { expr->left()->perform(this);+ if ( in_media_block ||+ (output_style() == INSPECT) || (+ expr->op().ws_before+ && (!expr->is_interpolant())+ && (expr->is_left_interpolant() ||+ expr->is_right_interpolant())++ )) append_string(" "); switch (expr->type()) {- case Sass_OP::AND: append_string(" and "); break;- case Sass_OP::OR: append_string(" or "); break;- case Sass_OP::EQ: append_string(" == "); break;- case Sass_OP::NEQ: append_string(" != "); break;- case Sass_OP::GT: append_string(" > "); break;- case Sass_OP::GTE: append_string(" >= "); break;- case Sass_OP::LT: append_string(" < "); break;- case Sass_OP::LTE: append_string(" <= "); break;- case Sass_OP::ADD: append_string(" + "); break;- case Sass_OP::SUB: append_string(" - "); break;- case Sass_OP::MUL: append_string(" * "); break;- case Sass_OP::DIV: append_string(in_media_block ? " / " : "/"); break;- case Sass_OP::MOD: append_string(" % "); break;+ case Sass_OP::AND: append_string("&&"); break;+ case Sass_OP::OR: append_string("||"); break;+ case Sass_OP::EQ: append_string("=="); break;+ case Sass_OP::NEQ: append_string("!="); break;+ case Sass_OP::GT: append_string(">"); break;+ case Sass_OP::GTE: append_string(">="); break;+ case Sass_OP::LT: append_string("<"); break;+ case Sass_OP::LTE: append_string("<="); break;+ case Sass_OP::ADD: append_string("+"); break;+ case Sass_OP::SUB: append_string("-"); break;+ case Sass_OP::MUL: append_string("*"); break;+ case Sass_OP::DIV: append_string("/"); break;+ case Sass_OP::MOD: append_string("%"); break; default: break; // shouldn't get here }+ if ( in_media_block ||+ (output_style() == INSPECT) || (+ expr->op().ws_after+ && (!expr->is_interpolant())+ && (expr->is_left_interpolant() ||+ expr->is_right_interpolant())+ )) append_string(" "); expr->right()->perform(this); } - void Inspect::operator()(Unary_Expression* expr)+ void Inspect::operator()(Unary_Expression_Ptr expr) { if (expr->type() == Unary_Expression::PLUS) append_string("+"); else append_string("-"); expr->operand()->perform(this); } - void Inspect::operator()(Function_Call* call)+ void Inspect::operator()(Function_Call_Ptr call) { append_token(call->name(), call); call->arguments()->perform(this); } - void Inspect::operator()(Function_Call_Schema* call)+ void Inspect::operator()(Function_Call_Schema_Ptr call) { call->name()->perform(this); call->arguments()->perform(this); } - void Inspect::operator()(Variable* var)+ void Inspect::operator()(Variable_Ptr var) { append_token(var->name(), var); } - void Inspect::operator()(Textual* txt)+ void Inspect::operator()(Textual_Ptr txt) { append_token(txt->value(), txt); } - void Inspect::operator()(Number* n)+ void Inspect::operator()(Number_Ptr n) {- // use values to_string facility- bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;- std::string res(n->to_string(compressed, (int)ctx->c_options->precision));++ std::string res;++ // check if the fractional part of the value equals to zero+ // neat trick from http://stackoverflow.com/a/1521682/1550314+ // double int_part; bool is_int = modf(value, &int_part) == 0.0;++ // this all cannot be done with one run only, since fixed+ // output differs from normal output and regular output+ // can contain scientific notation which we do not want!++ // first sample+ std::stringstream ss;+ ss.precision(12);+ ss << n->value();++ // check if we got scientific notation in result+ if (ss.str().find_first_of("e") != std::string::npos) {+ ss.clear(); ss.str(std::string());+ ss.precision(std::max(12, opt.precision));+ ss << std::fixed << n->value();+ }++ std::string tmp = ss.str();+ size_t pos_point = tmp.find_first_of(".,");+ size_t pos_fract = tmp.find_last_not_of("0");+ bool is_int = pos_point == pos_fract ||+ pos_point == std::string::npos;++ // reset stream for another run+ ss.clear(); ss.str(std::string());++ // take a shortcut for integers+ if (is_int)+ {+ ss.precision(0);+ ss << std::fixed << n->value();+ res = std::string(ss.str());+ }+ // process floats+ else+ {+ // do we have have too much precision?+ if (pos_fract < opt.precision + pos_point)+ { ss.precision((int)(pos_fract - pos_point)); }+ else { ss.precision(opt.precision); }+ // round value again+ ss << std::fixed << n->value();+ res = std::string(ss.str());+ // maybe we truncated up to decimal point+ size_t pos = res.find_last_not_of("0");+ // handle case where we have a "0"+ if (pos == std::string::npos) {+ res = "0.0";+ } else {+ bool at_dec_point = res[pos] == '.' ||+ res[pos] == ',';+ // don't leave a blank point+ if (at_dec_point) ++ pos;+ res.resize (pos + 1);+ }+ }++ // some final cosmetics+ if (res == "0.0") res = "0";+ else if (res == "") res = "0";+ else if (res == "-0") res = "0";+ else if (res == "-0.0") res = "0";+ else if (opt.output_style == COMPRESSED)+ {+ // check if handling negative nr+ size_t off = res[0] == '-' ? 1 : 0;+ // remove leading zero from floating point in compressed mode+ if (n->zero() && res[off] == '0' && res[off+1] == '.') res.erase(off, 1);+ }++ // add unit now+ res += n->unit();+ // output the final token append_token(res, n); } - void Inspect::operator()(Color* c)+ // helper function for serializing colors+ template <size_t range>+ static double cap_channel(double c) {+ if (c > range) return range;+ else if (c < 0) return 0;+ else return c;+ }++ void Inspect::operator()(Color_Ptr c) {- // use values to_string facility- bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;- std::string res(c->to_string(compressed, (int)ctx->c_options->precision)); // output the final token- append_token(res, c);+ std::stringstream ss;++ // original color name+ // maybe an unknown token+ std::string name = c->disp();++ // resolved color+ std::string res_name = name;++ double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision);+ double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision);+ double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision);+ double a = cap_channel<1> (c->a());++ // get color from given name (if one was given at all)+ if (name != "" && name_to_color(name)) {+ Color_Ptr_Const n = name_to_color(name);+ r = Sass::round(cap_channel<0xff>(n->r()), opt.precision);+ g = Sass::round(cap_channel<0xff>(n->g()), opt.precision);+ b = Sass::round(cap_channel<0xff>(n->b()), opt.precision);+ a = cap_channel<1> (n->a());+ }+ // otherwise get the possible resolved color name+ else {+ double numval = r * 0x10000 + g * 0x100 + b;+ if (color_to_name(numval))+ res_name = color_to_name(numval);+ }++ std::stringstream hexlet;+ bool compressed = opt.output_style == COMPRESSED;+ hexlet << '#' << std::setw(1) << std::setfill('0');+ // create a short color hexlet if there is any need for it+ if (compressed && is_color_doublet(r, g, b) && a == 1) {+ hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);+ hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);+ hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);+ } else {+ hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);+ hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);+ hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);+ }++ if (compressed && !c->is_delayed()) name = "";+ if (opt.output_style == INSPECT && a >= 1) {+ append_token(hexlet.str(), c);+ return;+ }++ // retain the originally specified color definition if unchanged+ if (name != "") {+ ss << name;+ }+ else if (r == 0 && g == 0 && b == 0 && a == 0) {+ ss << "transparent";+ }+ else if (a >= 1) {+ if (res_name != "") {+ if (compressed && hexlet.str().size() < res_name.size()) {+ ss << hexlet.str();+ } else {+ ss << res_name;+ }+ }+ else {+ ss << hexlet.str();+ }+ }+ else {+ ss << "rgba(";+ ss << static_cast<unsigned long>(r) << ",";+ if (!compressed) ss << " ";+ ss << static_cast<unsigned long>(g) << ",";+ if (!compressed) ss << " ";+ ss << static_cast<unsigned long>(b) << ",";+ if (!compressed) ss << " ";+ ss << a << ')';+ }++ append_token(ss.str(), c);+ } - void Inspect::operator()(Boolean* b)+ void Inspect::operator()(Boolean_Ptr b) {- // use values to_string facility- bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;- std::string res(b->to_string(compressed, (int)ctx->c_options->precision)); // output the final token- append_token(res, b);+ append_token(b->value() ? "true" : "false", b); } - void Inspect::operator()(String_Schema* ss)+ void Inspect::operator()(String_Schema_Ptr ss) { // Evaluation should turn these into String_Constants, // so this method is only for inspection purposes.@@ -494,30 +694,33 @@ } } - void Inspect::operator()(String_Constant* s)+ void Inspect::operator()(String_Constant_Ptr s) {- // get options from optional? context- int precision = ctx ? (int)ctx->c_options->precision : 5;- bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false;- // use values to_string facility- std::string res(s->to_string(compressed, precision));- // output the final token- append_token(res, s);+ append_token(s->value(), s); } - void Inspect::operator()(String_Quoted* s)+ void Inspect::operator()(String_Quoted_Ptr s) {- // get options from optional? context- int precision = ctx ? (int)ctx->c_options->precision : 5;- bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false;- // use values to_string facility- std::string res(s->to_string(compressed, precision));- // output the final token- append_token(res, s);+ if (const char q = s->quote_mark()) {+ append_token(quote(s->value(), q), s);+ } else {+ append_token(s->value(), s);+ } }- void Inspect::operator()(Supports_Operator* so)++ void Inspect::operator()(Custom_Error_Ptr e) {+ append_token(e->message(), e);+ } + void Inspect::operator()(Custom_Warning_Ptr w)+ {+ append_token(w->message(), w);+ }++ void Inspect::operator()(Supports_Operator_Ptr so)+ {+ if (so->needs_parens(so->left())) append_string("("); so->left()->perform(this); if (so->needs_parens(so->left())) append_string(")");@@ -537,16 +740,16 @@ if (so->needs_parens(so->right())) append_string(")"); } - void Inspect::operator()(Supports_Negation* sn)+ void Inspect::operator()(Supports_Negation_Ptr sn) { append_token("not", sn); append_mandatory_space();- if (sn->needs_parens(sn->condition())) append_string("(");+ if (sn->needs_parens(&sn->condition())) append_string("("); sn->condition()->perform(this);- if (sn->needs_parens(sn->condition())) append_string(")");+ if (sn->needs_parens(&sn->condition())) append_string(")"); } - void Inspect::operator()(Supports_Declaration* sd)+ void Inspect::operator()(Supports_Declaration_Ptr sd) { append_string("("); sd->feature()->perform(this);@@ -555,15 +758,15 @@ append_string(")"); } - void Inspect::operator()(Supports_Interpolation* sd)+ void Inspect::operator()(Supports_Interpolation_Ptr sd) { sd->value()->perform(this); } - void Inspect::operator()(Media_Query* mq)+ void Inspect::operator()(Media_Query_Ptr mq) { size_t i = 0;- if (mq->media_type()) {+ if (&mq->media_type()) { if (mq->is_negated()) append_string("not "); else if (mq->is_restricted()) append_string("only "); mq->media_type()->perform(this);@@ -577,7 +780,7 @@ } } - void Inspect::operator()(Media_Query_Expression* mqe)+ void Inspect::operator()(Media_Query_Expression_Ptr mqe) { if (mqe->is_interpolated()) { mqe->feature()->perform(this);@@ -593,33 +796,25 @@ } } - void Inspect::operator()(At_Root_Expression* ae)+ void Inspect::operator()(At_Root_Query_Ptr ae) {- if (ae->is_interpolated()) {- ae->feature()->perform(this);- }- else {- append_string("(");- ae->feature()->perform(this);- if (ae->value()) {- append_colon_separator();- ae->value()->perform(this);- }- append_string(")");+ append_string("(");+ ae->feature()->perform(this);+ if (ae->value()) {+ append_colon_separator();+ ae->value()->perform(this); }+ append_string(")"); } - void Inspect::operator()(Null* n)+ void Inspect::operator()(Null_Ptr n) {- // use values to_string facility- bool compressed = output_style() == SASS_STYLE_COMPRESSED;- std::string res(n->to_string(compressed, (int)ctx->c_options->precision)); // output the final token- append_token(res, n);+ append_token("null", n); } // parameters and arguments- void Inspect::operator()(Parameter* p)+ void Inspect::operator()(Parameter_Ptr p) { append_token(p->name(), p); if (p->default_value()) {@@ -631,7 +826,7 @@ } } - void Inspect::operator()(Parameters* p)+ void Inspect::operator()(Parameters_Ptr p) { append_string("("); if (!p->empty()) {@@ -644,26 +839,29 @@ append_string(")"); } - void Inspect::operator()(Argument* a)+ void Inspect::operator()(Argument_Ptr a) { if (!a->name().empty()) { append_token(a->name(), a); append_colon_separator(); }+ if (!a->value()) return; // Special case: argument nulls can be ignored if (a->value()->concrete_type() == Expression::NULL_VAL) { return; } if (a->value()->concrete_type() == Expression::STRING) {- String_Constant* s = static_cast<String_Constant*>(a->value());- s->perform(this);- } else a->value()->perform(this);+ String_Constant_Ptr s = SASS_MEMORY_CAST(String_Constant, a->value());+ if (s) s->perform(this);+ } else {+ a->value()->perform(this);+ } if (a->is_rest_argument()) { append_string("..."); } } - void Inspect::operator()(Arguments* a)+ void Inspect::operator()(Arguments_Ptr a) { append_string("("); if (!a->empty()) {@@ -677,17 +875,17 @@ append_string(")"); } - void Inspect::operator()(Selector_Schema* s)+ void Inspect::operator()(Selector_Schema_Ptr s) { s->contents()->perform(this); } - void Inspect::operator()(Parent_Selector* p)+ void Inspect::operator()(Parent_Selector_Ptr p) {- append_string("&");+ if (p->is_real_parent_ref()) append_string("&"); } - void Inspect::operator()(Selector_Placeholder* s)+ void Inspect::operator()(Placeholder_Selector_Ptr s) { append_token(s->name(), s); if (s->has_line_break()) append_optional_linefeed();@@ -695,26 +893,33 @@ } - void Inspect::operator()(Type_Selector* s)+ void Inspect::operator()(Element_Selector_Ptr s) { append_token(s->ns_name(), s); } - void Inspect::operator()(Selector_Qualifier* s)+ void Inspect::operator()(Class_Selector_Ptr s) { append_token(s->ns_name(), s); if (s->has_line_break()) append_optional_linefeed(); if (s->has_line_break()) append_indentation(); } - void Inspect::operator()(Attribute_Selector* s)+ void Inspect::operator()(Id_Selector_Ptr s) {+ append_token(s->ns_name(), s);+ if (s->has_line_break()) append_optional_linefeed();+ if (s->has_line_break()) append_indentation();+ }++ void Inspect::operator()(Attribute_Selector_Ptr s)+ { append_string("["); add_open_mapping(s); append_token(s->ns_name(), s); if (!s->matcher().empty()) { append_string(s->matcher());- if (s->value()) {+ if (&s->value() && *s->value()) { s->value()->perform(this); } }@@ -722,7 +927,7 @@ append_string("]"); } - void Inspect::operator()(Pseudo_Selector* s)+ void Inspect::operator()(Pseudo_Selector_Ptr s) { append_token(s->ns_name(), s); if (s->expression()) {@@ -732,7 +937,7 @@ } } - void Inspect::operator()(Wrapped_Selector* s)+ void Inspect::operator()(Wrapped_Selector_Ptr s) { bool was = in_wrapped; in_wrapped = true;@@ -746,24 +951,29 @@ in_wrapped = was; } - void Inspect::operator()(Compound_Selector* s)+ void Inspect::operator()(Compound_Selector_Ptr s) { for (size_t i = 0, L = s->length(); i < L; ++i) { (*s)[i]->perform(this); } if (s->has_line_break()) {- if (output_style() != SASS_STYLE_COMPACT) {+ if (output_style() != COMPACT) { append_optional_linefeed(); } } } - void Inspect::operator()(Complex_Selector* c)+ void Inspect::operator()(Complex_Selector_Ptr c) {- Compound_Selector* head = c->head();- Complex_Selector* tail = c->tail();+ Compound_Selector_Obj head = c->head();+ Complex_Selector_Obj tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); + if (comb == Complex_Selector::ANCESTOR_OF && (!head || head->empty())) {+ if (tail) tail->perform(this);+ return;+ }+ if (c->has_line_feed()) { if (!(c->has_parent_ref())) { append_optional_linefeed();@@ -774,7 +984,7 @@ if (head && head->length() != 0) head->perform(this); bool is_empty = !head || head->length() == 0 || head->is_empty_reference(); bool is_tail = head && !head->is_empty_reference() && tail;- if (output_style() == SASS_STYLE_COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;+ if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0; switch (comb) { case Complex_Selector::ANCESTOR_OF:@@ -810,27 +1020,43 @@ } if (tail) tail->perform(this); if (!tail && c->has_line_break()) {- if (output_style() == SASS_STYLE_COMPACT) {+ if (output_style() == COMPACT) { append_mandatory_space(); } } } - void Inspect::operator()(Selector_List* g)+ void Inspect::operator()(Selector_List_Ptr g) {- if (g->empty()) return; + if (g->empty()) {+ if (output_style() == TO_SASS) {+ append_token("()", g);+ }+ return;+ }++ bool was_comma_array = in_comma_array;- if (!in_declaration && in_comma_array) {+ // probably ruby sass eqivalent of element_needs_parens+ if (output_style() == TO_SASS && g->length() == 1 &&+ (!SASS_MEMORY_CAST(List, (*g)[0]) &&+ !SASS_MEMORY_CAST(Selector_List, (*g)[0]))) { append_string("("); }+ else if (!in_declaration && in_comma_array) {+ append_string("(");+ } if (in_declaration) in_comma_array = true; for (size_t i = 0, L = g->length(); i < L; ++i) { if (!in_wrapped && i == 0) append_indentation(); if ((*g)[i] == 0) continue;+ schedule_mapping(&g->at(i)->last());+ // add_open_mapping((*g)[i]->last()); (*g)[i]->perform(this);+ // add_close_mapping((*g)[i]->last()); if (i < L - 1) { scheduled_space = 0; append_comma_separator();@@ -838,13 +1064,19 @@ } in_comma_array = was_comma_array;- if (!in_declaration && in_comma_array) {+ // probably ruby sass eqivalent of element_needs_parens+ if (output_style() == TO_SASS && g->length() == 1 &&+ (!SASS_MEMORY_CAST(List, (*g)[0]) &&+ !SASS_MEMORY_CAST(Selector_List, (*g)[0]))) {+ append_string(",)");+ }+ else if (!in_declaration && in_comma_array) { append_string(")"); } } - void Inspect::fallback_impl(AST_Node* n)+ void Inspect::fallback_impl(AST_Node_Ptr n) { }
libsass/src/inspect.hpp view
@@ -13,7 +13,7 @@ // import all the class-specific methods and override as desired using Operation_CRTP<void, Inspect>::operator(); - void fallback_impl(AST_Node* n);+ void fallback_impl(AST_Node_Ptr n); public: @@ -21,77 +21,79 @@ virtual ~Inspect(); // statements- virtual void operator()(Block*);- virtual void operator()(Ruleset*);- virtual void operator()(Propset*);- virtual void operator()(Bubble*);- virtual void operator()(Supports_Block*);- virtual void operator()(Media_Block*);- virtual void operator()(At_Root_Block*);- virtual void operator()(At_Rule*);- virtual void operator()(Keyframe_Rule*);- virtual void operator()(Declaration*);- virtual void operator()(Assignment*);- virtual void operator()(Import*);- virtual void operator()(Import_Stub*);- virtual void operator()(Warning*);- virtual void operator()(Error*);- virtual void operator()(Debug*);- virtual void operator()(Comment*);- virtual void operator()(If*);- virtual void operator()(For*);- virtual void operator()(Each*);- virtual void operator()(While*);- virtual void operator()(Return*);- virtual void operator()(Extension*);- virtual void operator()(Definition*);- virtual void operator()(Mixin_Call*);- virtual void operator()(Content*);+ virtual void operator()(Block_Ptr);+ virtual void operator()(Ruleset_Ptr);+ virtual void operator()(Bubble_Ptr);+ virtual void operator()(Supports_Block_Ptr);+ virtual void operator()(Media_Block_Ptr);+ virtual void operator()(At_Root_Block_Ptr);+ virtual void operator()(Directive_Ptr);+ virtual void operator()(Keyframe_Rule_Ptr);+ virtual void operator()(Declaration_Ptr);+ virtual void operator()(Assignment_Ptr);+ virtual void operator()(Import_Ptr);+ virtual void operator()(Import_Stub_Ptr);+ virtual void operator()(Warning_Ptr);+ virtual void operator()(Error_Ptr);+ virtual void operator()(Debug_Ptr);+ virtual void operator()(Comment_Ptr);+ virtual void operator()(If_Ptr);+ virtual void operator()(For_Ptr);+ virtual void operator()(Each_Ptr);+ virtual void operator()(While_Ptr);+ virtual void operator()(Return_Ptr);+ virtual void operator()(Extension_Ptr);+ virtual void operator()(Definition_Ptr);+ virtual void operator()(Mixin_Call_Ptr);+ virtual void operator()(Content_Ptr); // expressions- virtual void operator()(Map*);- virtual void operator()(List*);- virtual void operator()(Binary_Expression*);- virtual void operator()(Unary_Expression*);- virtual void operator()(Function_Call*);- virtual void operator()(Function_Call_Schema*);- // virtual void operator()(Custom_Warning*);- // virtual void operator()(Custom_Error*);- virtual void operator()(Variable*);- virtual void operator()(Textual*);- virtual void operator()(Number*);- virtual void operator()(Color*);- virtual void operator()(Boolean*);- virtual void operator()(String_Schema*);- virtual void operator()(String_Constant*);- virtual void operator()(String_Quoted*);- virtual void operator()(Supports_Operator*);- virtual void operator()(Supports_Negation*);- virtual void operator()(Supports_Declaration*);- virtual void operator()(Supports_Interpolation*);- virtual void operator()(Media_Query*);- virtual void operator()(Media_Query_Expression*);- virtual void operator()(At_Root_Expression*);- virtual void operator()(Null*);- virtual void operator()(Parent_Selector* p);+ virtual void operator()(Map_Ptr);+ virtual void operator()(List_Ptr);+ virtual void operator()(Binary_Expression_Ptr);+ virtual void operator()(Unary_Expression_Ptr);+ virtual void operator()(Function_Call_Ptr);+ virtual void operator()(Function_Call_Schema_Ptr);+ // virtual void operator()(Custom_Warning_Ptr);+ // virtual void operator()(Custom_Error_Ptr);+ virtual void operator()(Variable_Ptr);+ virtual void operator()(Textual_Ptr);+ virtual void operator()(Number_Ptr);+ virtual void operator()(Color_Ptr);+ virtual void operator()(Boolean_Ptr);+ virtual void operator()(String_Schema_Ptr);+ virtual void operator()(String_Constant_Ptr);+ virtual void operator()(String_Quoted_Ptr);+ virtual void operator()(Custom_Error_Ptr);+ virtual void operator()(Custom_Warning_Ptr);+ virtual void operator()(Supports_Operator_Ptr);+ virtual void operator()(Supports_Negation_Ptr);+ virtual void operator()(Supports_Declaration_Ptr);+ virtual void operator()(Supports_Interpolation_Ptr);+ virtual void operator()(Media_Query_Ptr);+ virtual void operator()(Media_Query_Expression_Ptr);+ virtual void operator()(At_Root_Query_Ptr);+ virtual void operator()(Null_Ptr);+ virtual void operator()(Parent_Selector_Ptr p); // parameters and arguments- virtual void operator()(Parameter*);- virtual void operator()(Parameters*);- virtual void operator()(Argument*);- virtual void operator()(Arguments*);+ virtual void operator()(Parameter_Ptr);+ virtual void operator()(Parameters_Ptr);+ virtual void operator()(Argument_Ptr);+ virtual void operator()(Arguments_Ptr); // selectors- virtual void operator()(Selector_Schema*);- virtual void operator()(Selector_Placeholder*);- virtual void operator()(Type_Selector*);- virtual void operator()(Selector_Qualifier*);- virtual void operator()(Attribute_Selector*);- virtual void operator()(Pseudo_Selector*);- virtual void operator()(Wrapped_Selector*);- virtual void operator()(Compound_Selector*);- virtual void operator()(Complex_Selector*);- virtual void operator()(Selector_List*);+ virtual void operator()(Selector_Schema_Ptr);+ virtual void operator()(Placeholder_Selector_Ptr);+ virtual void operator()(Element_Selector_Ptr);+ virtual void operator()(Class_Selector_Ptr);+ virtual void operator()(Id_Selector_Ptr);+ virtual void operator()(Attribute_Selector_Ptr);+ virtual void operator()(Pseudo_Selector_Ptr);+ virtual void operator()(Wrapped_Selector_Ptr);+ virtual void operator()(Compound_Selector_Ptr);+ virtual void operator()(Complex_Selector_Ptr);+ virtual void operator()(Selector_List_Ptr); // template <typename U>- // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node*>(x)); }+ // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node_Ptr>(x)); } }; }
libsass/src/json.cpp view
@@ -28,6 +28,10 @@ #include "json.hpp" +// include utf8 library used by libsass+// ToDo: replace internal json utf8 code+#include "utf8.h"+ #include <assert.h> #include <stdint.h> #include <stdio.h>@@ -395,7 +399,13 @@ SB sb; sb_init(&sb); - emit_string(&sb, str);+ try {+ emit_string(&sb, str);+ }+ catch (std::exception) {+ sb_free(&sb);+ throw;+ } return sb_finish(&sb); }@@ -405,10 +415,16 @@ SB sb; sb_init(&sb); - if (space != NULL)- emit_value_indented(&sb, node, space, 0);- else- emit_value(&sb, node);+ try {+ if (space != NULL)+ emit_value_indented(&sb, node, space, 0);+ else+ emit_value(&sb, node);+ }+ catch (std::exception) {+ sb_free(&sb);+ throw;+ } return sb_finish(&sb); }@@ -544,42 +560,50 @@ static void append_node(JsonNode *parent, JsonNode *child) {- child->parent = parent;- child->prev = parent->children.tail;- child->next = NULL;+ if (child != NULL && parent != NULL) {+ child->parent = parent;+ child->prev = parent->children.tail;+ child->next = NULL; - if (parent->children.tail != NULL)- parent->children.tail->next = child;- else- parent->children.head = child;- parent->children.tail = child;+ if (parent->children.tail != NULL)+ parent->children.tail->next = child;+ else+ parent->children.head = child;+ parent->children.tail = child;+ } } static void prepend_node(JsonNode *parent, JsonNode *child) {- child->parent = parent;- child->prev = NULL;- child->next = parent->children.head;+ if (child != NULL && parent != NULL) {+ child->parent = parent;+ child->prev = NULL;+ child->next = parent->children.head; - if (parent->children.head != NULL)- parent->children.head->prev = child;- else- parent->children.tail = child;- parent->children.head = child;+ if (parent->children.head != NULL)+ parent->children.head->prev = child;+ else+ parent->children.tail = child;+ parent->children.head = child;+ } } static void append_member(JsonNode *object, char *key, JsonNode *value) {- value->key = key;- append_node(object, value);+ if (value != NULL && object != NULL) {+ value->key = key;+ append_node(object, value);+ } } void json_append_element(JsonNode *array, JsonNode *element) {- assert(array->tag == JSON_ARRAY);- assert(element->parent == NULL);+ if (array != NULL && element !=NULL) {+ assert(array->tag == JSON_ARRAY);+ assert(element->parent == NULL); - append_node(array, element);+ append_node(array, element);+ } } void json_prepend_element(JsonNode *array, JsonNode *element)@@ -592,40 +616,47 @@ void json_append_member(JsonNode *object, const char *key, JsonNode *value) {- assert(object->tag == JSON_OBJECT);- assert(value->parent == NULL);+ if (object != NULL && key != NULL && value != NULL) {+ assert(object->tag == JSON_OBJECT);+ assert(value->parent == NULL); - append_member(object, json_strdup(key), value);+ append_member(object, json_strdup(key), value);+ } } void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) {- assert(object->tag == JSON_OBJECT);- assert(value->parent == NULL);+ if (object != NULL && key != NULL && value != NULL) {+ assert(object->tag == JSON_OBJECT);+ assert(value->parent == NULL); - value->key = json_strdup(key);- prepend_node(object, value);+ value->key = json_strdup(key);+ prepend_node(object, value);+ } } void json_remove_from_parent(JsonNode *node) {- JsonNode *parent = node->parent;+ if (node != NULL) {+ JsonNode *parent = node->parent; - if (parent != NULL) {- if (node->prev != NULL)- node->prev->next = node->next;- else- parent->children.head = node->next;- if (node->next != NULL)- node->next->prev = node->prev;- else- parent->children.tail = node->prev;+ if (parent != NULL) {+ if (node->prev != NULL)+ node->prev->next = node->next;+ else+ parent->children.head = node->next; - free(node->key);+ if (node->next != NULL)+ node->next->prev = node->prev;+ else+ parent->children.tail = node->prev; - node->parent = NULL;- node->prev = node->next = NULL;- node->key = NULL;+ free(node->key);++ node->parent = NULL;+ node->prev = node->next = NULL;+ node->key = NULL;+ } } } @@ -1125,6 +1156,13 @@ bool escape_unicode = false; const char *s = str; char *b;++// make assertion catchable+#ifndef NDEBUG+ if (!utf8_validate(str)) {+ throw utf8::invalid_utf8(0);+ }+#endif assert(utf8_validate(str));
libsass/src/json.hpp view
@@ -43,21 +43,21 @@ /* only if parent is an object or array (NULL otherwise) */ JsonNode *parent; JsonNode *prev, *next;- + /* only if parent is an object (NULL otherwise) */ char *key; /* Must be valid UTF-8. */- + JsonTag tag; union { /* JSON_BOOL */ bool bool_;- + /* JSON_STRING */ char *string_; /* Must be valid UTF-8. */- + /* JSON_NUMBER */ double number_;- + /* JSON_ARRAY */ /* JSON_OBJECT */ struct {
libsass/src/lexer.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cctype> #include <cstddef> #include <iostream>@@ -80,9 +81,9 @@ bool is_nonascii(const char& chr) { return (- (unsigned(chr) > 127 && unsigned(chr) < 55296) ||- (unsigned(chr) > 57343 && unsigned(chr) < 65534) ||- (unsigned(chr) > 65535 && unsigned(chr) < 1114111)+ (unsigned(chr) >= 128 && unsigned(chr) <= 15572911) ||+ (unsigned(chr) >= 15630464 && unsigned(chr) <= 15712189) ||+ (unsigned(chr) >= 4036001920) ); } @@ -90,7 +91,8 @@ // valid in a uri (copied from Ruby Sass) bool is_uri_character(const char& chr) {- return unsigned(chr) > 41 && unsigned(chr) < 127;+ return (unsigned(chr) > 41 && unsigned(chr) < 127) ||+ unsigned(chr) == ':' || unsigned(chr) == '/'; } // check if char is within a reduced ascii range@@ -120,6 +122,7 @@ const char* xdigit(const char* src) { return is_xdigit(*src) ? src + 1 : 0; } const char* alnum(const char* src) { return is_alnum(*src) ? src + 1 : 0; } const char* punct(const char* src) { return is_punct(*src) ? src + 1 : 0; }+ const char* hyphen(const char* src) { return *src && *src == '-' ? src + 1 : 0; } const char* character(const char* src) { return is_character(*src) ? src + 1 : 0; } const char* uri_character(const char* src) { return is_uri_character(*src) ? src + 1 : 0; } const char* escapable_character(const char* src) { return is_escapable_character(*src) ? src + 1 : 0; }@@ -127,6 +130,7 @@ // Match multiple ctype characters. const char* spaces(const char* src) { return one_plus<space>(src); } const char* digits(const char* src) { return one_plus<digit>(src); }+ const char* hyphens(const char* src) { return one_plus<hyphen>(src); } // Whitespace handling. const char* no_spaces(const char* src) { return negate< space >(src); }@@ -136,7 +140,7 @@ const char* any_char(const char* src) { return *src ? src + 1 : src; } // Match word boundary (zero-width lookahead).- const char* word_boundary(const char* src) { return is_character(*src) ? 0 : src; }+ const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; } // Match linefeed /(?:\n|\r\n?)/ const char* re_linebreak(const char* src)
libsass/src/lexer.hpp view
@@ -44,6 +44,7 @@ const char* xdigit(const char* src); const char* alnum(const char* src); const char* punct(const char* src);+ const char* hyphen(const char* src); const char* unicode(const char* src); const char* nonascii(const char* src); const char* character(const char* src);@@ -53,6 +54,7 @@ // Match multiple ctype characters. const char* spaces(const char* src); const char* digits(const char* src);+ const char* hyphens(const char* src); // Whitespace handling. const char* no_spaces(const char* src);@@ -139,6 +141,25 @@ const char* class_chars(const char* src) { const char* p = src; while (class_char<char_class>(p)) ++p;+ return p == src ? 0 : p;+ }++ // Match for members of char class.+ // Regex equivalent: /[^axy]/+ template <const char* neg_char_class>+ const char* neg_class_char(const char* src) {+ if (*src == 0) return 0;+ const char* cc = neg_char_class;+ while (*cc && *src != *cc) ++cc;+ return *cc ? 0 : src + 1;+ }++ // Match for members of char class.+ // Regex equivalent: /[^axy]+/+ template <const char* neg_char_class>+ const char* neg_class_chars(const char* src) {+ const char* p = src;+ while (neg_class_char<neg_char_class>(p)) ++p; return p == src ? 0 : p; }
libsass/src/listize.cpp view
@@ -1,87 +1,85 @@+#include "sass.hpp" #include <iostream> #include <typeinfo> #include <string> #include "listize.hpp"-#include "to_string.hpp" #include "context.hpp" #include "backtrace.hpp" #include "error_handling.hpp" namespace Sass { - Listize::Listize(Context& ctx)- : ctx(ctx)+ Listize::Listize() { } - Expression* Listize::operator()(Selector_List* sel)+ Expression_Ptr Listize::operator()(Selector_List_Ptr sel) {- List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA);+ List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);+ l->from_selector(true); for (size_t i = 0, L = sel->length(); i < L; ++i) {- if (!(*sel)[i]) continue;- *l << (*sel)[i]->perform(this);+ if (!sel->at(i)) continue;+ l->append(sel->at(i)->perform(this)); }- return l;+ if (l->length()) return l.detach();+ return SASS_MEMORY_NEW(Null, l->pstate()); } - Expression* Listize::operator()(Compound_Selector* sel)+ Expression_Ptr Listize::operator()(Compound_Selector_Ptr sel) {- To_String to_string; std::string str; for (size_t i = 0, L = sel->length(); i < L; ++i) {- Expression* e = (*sel)[i]->perform(this);- if (e) str += e->perform(&to_string);+ Expression_Ptr e = (*sel)[i]->perform(this);+ if (e) str += e->to_string(); }- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), str);+ return SASS_MEMORY_NEW(String_Quoted, sel->pstate(), str); } - Expression* Listize::operator()(Complex_Selector* sel)+ Expression_Ptr Listize::operator()(Complex_Selector_Ptr sel) {- List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), 2);-- Compound_Selector* head = sel->head();+ List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), 2);+ l->from_selector(true);+ Compound_Selector_Obj head = sel->head(); if (head && !head->is_empty_reference()) {- Expression* hh = head->perform(this);- if (hh) *l << hh;+ Expression_Ptr hh = head->perform(this);+ if (hh) l->append(hh); } - To_String to_string; std::string reference = ! sel->reference() ? ""- : sel->reference()->perform(&to_string);+ : sel->reference()->to_string(); switch(sel->combinator()) { case Complex_Selector::PARENT_OF:- *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), ">");+ l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), ">")); break; case Complex_Selector::ADJACENT_TO:- *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "+");+ l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "+")); break; case Complex_Selector::REFERENCE:- *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "/" + reference + "/");+ l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "/" + reference + "/")); break; case Complex_Selector::PRECEDES:- *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "~");+ l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "~")); break; case Complex_Selector::ANCESTOR_OF: break; } - Complex_Selector* tail = sel->tail();+ Complex_Selector_Obj tail = sel->tail(); if (tail) {- Expression* tt = tail->perform(this);- if (tt && tt->concrete_type() == Expression::LIST)- { *l += static_cast<List*>(tt); }- else if (tt) *l << static_cast<List*>(tt);+ Expression_Obj tt = tail->perform(this);+ if (List_Ptr ls = SASS_MEMORY_CAST(List, tt))+ { l->concat(ls); } } if (l->length() == 0) return 0;- return l;+ return l.detach(); } - Expression* Listize::fallback_impl(AST_Node* n)+ Expression_Ptr Listize::fallback_impl(AST_Node_Ptr n) {- return dynamic_cast<Expression*>(n);+ return dynamic_cast<Expression_Ptr>(n); } }
libsass/src/listize.hpp view
@@ -11,27 +11,23 @@ namespace Sass { - typedef Environment<AST_Node*> Env;+ typedef Environment<AST_Node_Obj> Env; struct Backtrace; - class Listize : public Operation_CRTP<Expression*, Listize> {-- Context& ctx;+ class Listize : public Operation_CRTP<Expression_Ptr, Listize> { - Expression* fallback_impl(AST_Node* n);+ Expression_Ptr fallback_impl(AST_Node_Ptr n); public:- Listize(Context&);- virtual ~Listize() { }-- using Operation<Expression*>::operator();+ Listize();+ ~Listize() { } - Expression* operator()(Selector_List*);- Expression* operator()(Complex_Selector*);- Expression* operator()(Compound_Selector*);+ Expression_Ptr operator()(Selector_List_Ptr);+ Expression_Ptr operator()(Complex_Selector_Ptr);+ Expression_Ptr operator()(Compound_Selector_Ptr); template <typename U>- Expression* fallback(U x) { return fallback_impl(x); }+ Expression_Ptr fallback(U x) { return fallback_impl(x); } }; }
@@ -0,0 +1,116 @@+#include "../sass.hpp"+#include <iostream>+#include <typeinfo>++#include "SharedPtr.hpp"+#include "../ast_fwd_decl.hpp"++#ifdef DEBUG_SHARED_PTR+#include "../debugger.hpp"+#endif++namespace Sass {++ #ifdef DEBUG_SHARED_PTR+ void SharedObj::dumpMemLeaks() {+ if (!all.empty()) {+ std::cerr << "###################################\n";+ std::cerr << "# REPORTING MISSING DEALLOCATIONS #\n";+ std::cerr << "###################################\n";+ for (auto var : all) {+ if (AST_Node_Ptr ast = SASS_MEMORY_CAST_PTR(AST_Node, var)) {+ debug_ast(ast);+ } else {+ std::cerr << "LEAKED " << var << "\n";+ }+ }+ }+ }+ std::vector<SharedObj*> SharedObj::all;+ #endif++ bool SharedObj::taint = false;++ SharedObj::SharedObj()+ : detached(false)+ #ifdef DEBUG_SHARED_PTR+ , dbg(false)+ #endif+ {+ refcounter = 0;+ #ifdef DEBUG_SHARED_PTR+ if (taint) all.push_back(this);+ #endif+ };++ SharedObj::~SharedObj() {+ #ifdef DEBUG_SHARED_PTR+ if (dbg) std::cerr << "Destruct " << this << "\n";+ if(!all.empty()) { // check needed for MSVC (no clue why?)+ all.erase(std::remove(all.begin(), all.end(), this), all.end());+ }+ #endif+ };++++ void SharedPtr::decRefCount() {+ if (node) {+ -- node->refcounter;+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) std::cerr << "- " << node << " X " << node->refcounter << " (" << this << ") " << "\n";+ #endif+ if (node->refcounter == 0) {+ #ifdef DEBUG_SHARED_PTR+ AST_Node_Ptr ptr = SASS_MEMORY_CAST_PTR(AST_Node, node);+ if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";+ #endif+ if (!node->detached) {+ delete(node);+ }+ }+ }+ }++ void SharedPtr::incRefCount() {+ if (node) {+ ++ node->refcounter;+ node->detached = false;+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) {+ std::cerr << "+ " << node << " X " << node->refcounter << " (" << this << ") " << "\n";+ }+ #endif+ }+ }++ SharedPtr::~SharedPtr() {+ decRefCount();+ }+++ // the create constructor+ SharedPtr::SharedPtr(SharedObj* ptr)+ : node(ptr) {+ incRefCount();+ }+ // copy assignment operator+ SharedPtr& SharedPtr::operator=(const SharedPtr& rhs) {+ void* cur_ptr = (void*) node;+ void* rhs_ptr = (void*) rhs.node;+ if (cur_ptr == rhs_ptr) {+ return *this;+ }+ decRefCount();+ node = rhs.node;+ incRefCount();+ return *this;+ }++ // the copy constructor+ SharedPtr::SharedPtr(const SharedPtr& obj)+ : node(obj.node) {+ incRefCount();+ }++}
@@ -0,0 +1,202 @@+#ifndef SASS_MEMORY_SHARED_PTR_H+#define SASS_MEMORY_SHARED_PTR_H++#include "sass/base.h"++#include <vector>++namespace Sass {++ class SharedPtr;++ ///////////////////////////////////////////////////////////////////////////////+ // Use macros for the allocation task, since overloading operator `new`+ // has been proven to be flaky under certain compilers (see comment below).+ ///////////////////////////////////////////////////////////////////////////////++ #ifdef DEBUG_SHARED_PTR++ #define SASS_MEMORY_NEW(Class, ...) \+ static_cast<Class##_Ptr>((new Class(__VA_ARGS__))->trace(__FILE__, __LINE__)) \++ #define SASS_MEMORY_COPY(obj) \+ ((obj)->copy(__FILE__, __LINE__)) \++ #define SASS_MEMORY_CLONE(obj) \+ ((obj)->clone(__FILE__, __LINE__)) \++ #else++ #define SASS_MEMORY_NEW(Class, ...) \+ new Class(__VA_ARGS__) \++ #define SASS_MEMORY_COPY(obj) \+ ((obj)->copy()) \++ #define SASS_MEMORY_CLONE(obj) \+ ((obj)->clone()) \++ #endif++ #define SASS_MEMORY_CAST(Class, obj) \+ (dynamic_cast<Class##_Ptr>(&obj)) \++ #define SASS_MEMORY_CAST_PTR(Class, ptr) \+ (dynamic_cast<Class##_Ptr>(ptr)) \++ class SharedObj {+ protected:+ friend class SharedPtr;+ friend class Memory_Manager;+ #ifdef DEBUG_SHARED_PTR+ static std::vector<SharedObj*> all;+ std::string file;+ size_t line;+ #endif+ static bool taint;+ long refcounter;+ // long refcount;+ bool detached;+ #ifdef DEBUG_SHARED_PTR+ bool dbg;+ #endif+ public:+ #ifdef DEBUG_SHARED_PTR+ static void dumpMemLeaks();+ SharedObj* trace(std::string file, size_t line) {+ this->file = file;+ this->line = line;+ return this;+ }+ #endif+ SharedObj();+ #ifdef DEBUG_SHARED_PTR+ std::string getDbgFile() {+ return file;+ }+ size_t getDbgLine() {+ return line;+ }+ void setDbg(bool dbg) {+ this->dbg = dbg;+ }+ #endif+ static void setTaint(bool val) {+ taint = val;+ }+ virtual ~SharedObj();+ long getRefCount() {+ return refcounter;+ }+ };+++ class SharedPtr {+ private:+ SharedObj* node;+ private:+ void decRefCount();+ void incRefCount();+ public:+ // the empty constructor+ SharedPtr()+ : node(NULL) {};+ // the create constructor+ SharedPtr(SharedObj* ptr);+ // copy assignment operator+ SharedPtr& operator=(const SharedPtr& rhs);+ // move assignment operator+ /* SharedPtr& operator=(SharedPtr&& rhs); */+ // the copy constructor+ SharedPtr(const SharedPtr& obj);+ // the move constructor+ /* SharedPtr(SharedPtr&& obj); */+ // destructor+ ~SharedPtr();+ public:+ SharedObj* obj () {+ return node;+ };+ SharedObj* obj () const {+ return node;+ };+ SharedObj* operator-> () {+ return node;+ };+ bool isNull () {+ return node == NULL;+ };+ bool isNull () const {+ return node == NULL;+ };+ SharedObj* detach() {+ node->detached = true;+ return node;+ };+ SharedObj* detach() const {+ if (node) {+ node->detached = true;+ }+ return node;+ };+ operator bool() {+ return node != NULL;+ };+ operator bool() const {+ return node != NULL;+ };++ };++ template < typename T >+ class SharedImpl : private SharedPtr {+ public:+ SharedImpl()+ : SharedPtr(NULL) {};+ SharedImpl(T* node)+ : SharedPtr(node) {};+ SharedImpl(T&& node)+ : SharedPtr(node) {};+ SharedImpl(const T& node)+ : SharedPtr(node) {};+ ~SharedImpl() {};+ public:+ T* operator& () {+ return static_cast<T*>(this->obj());+ };+ T* operator& () const {+ return static_cast<T*>(this->obj());+ };+ T& operator* () {+ return *static_cast<T*>(this->obj());+ };+ T& operator* () const {+ return *static_cast<T*>(this->obj());+ };+ T* operator-> () {+ return static_cast<T*>(this->obj());+ };+ T* operator-> () const {+ return static_cast<T*>(this->obj());+ };+ T* ptr () {+ return static_cast<T*>(this->obj());+ };+ T* detach() {+ if (this->obj() == NULL) return NULL;+ return static_cast<T*>(SharedPtr::detach());+ }+ bool isNull() {+ return this->obj() == NULL;+ }+ operator bool() {+ return this->obj() != NULL;+ };+ operator bool() const {+ return this->obj() != NULL;+ };+ };++}++#endif
− libsass/src/memory_manager.cpp
@@ -1,76 +0,0 @@-#include "ast.hpp"-#include "memory_manager.hpp"--namespace Sass {-- Memory_Manager::Memory_Manager(size_t size)- : nodes(std::vector<Memory_Object*>())- {- size_t init = size;- if (init < 8) init = 8;- // reserve some space- nodes.reserve(init);- }-- Memory_Manager::~Memory_Manager()- {- // release memory for all controlled nodes- // avoid calling erase for every single node- for (size_t i = 0, S = nodes.size(); i < S; ++i) {- deallocate(nodes[i]);- }- // just in case- nodes.clear();- }-- Memory_Object* Memory_Manager::add(Memory_Object* np)- {- // object has been initialized- // it can be "deleted" from now on- np->refcount = 1;- return np;- }-- bool Memory_Manager::has(Memory_Object* np)- {- // check if the pointer is controlled under our pool- return find(nodes.begin(), nodes.end(), np) != nodes.end();- }-- Memory_Object* Memory_Manager::allocate(size_t size)- {- // allocate requested memory- void* heap = malloc(size);- // init internal refcount status to zero- (static_cast<Memory_Object*>(heap))->refcount = 0;- // add the memory under our management- nodes.push_back(static_cast<Memory_Object*>(heap));- // cast object to its initial type- return static_cast<Memory_Object*>(heap);- }-- void Memory_Manager::deallocate(Memory_Object* np)- {- // only call destructor if initialized- if (np->refcount) np->~Memory_Object();- // always free the memory- free(np);- }-- void Memory_Manager::remove(Memory_Object* np)- {- // remove node from pool (no longer active)- nodes.erase(find(nodes.begin(), nodes.end(), np));- // you are now in control of the memory- }-- void Memory_Manager::destroy(Memory_Object* np)- {- // remove from pool- remove(np);- // release memory- deallocate(np);- }--}-
− libsass/src/memory_manager.hpp
@@ -1,48 +0,0 @@-#ifndef SASS_MEMORY_MANAGER_H-#define SASS_MEMORY_MANAGER_H--#include <vector>--namespace Sass {-- class Memory_Object {- friend class Memory_Manager;- long refcount;- public:- Memory_Object() { refcount = 0; };- virtual ~Memory_Object() {};- };-- /////////////////////////////////////////////////////////////////////////////- // A class for tracking allocations of AST_Node objects. The intended usage- // is something like: Some_Node* n = new (mem_mgr) Some_Node(...);- // Then, at the end of the program, the memory manager will delete all of the- // allocated nodes that have been passed to it.- // In the future, this class may implement a custom allocator.- /////////////////////////////////////////////////////////////////////////////- class Memory_Manager {- std::vector<Memory_Object*> nodes;-- public:- Memory_Manager(size_t size = 0);- ~Memory_Manager();-- bool has(Memory_Object* np);- Memory_Object* allocate(size_t size);- void deallocate(Memory_Object* np);- void remove(Memory_Object* np);- void destroy(Memory_Object* np);- Memory_Object* add(Memory_Object* np);-- };-}--///////////////////////////////////////////////////////////////////////////////-// Use macros for the allocation task, since overloading operator `new`-// has been proven to be flaky under certain compilers (see comment below).-///////////////////////////////////////////////////////////////////////////////--#define SASS_MEMORY_NEW(mgr, Class, ...) \- (static_cast<Class*>(mgr.add(new (mgr.allocate(sizeof(Class))) Class(__VA_ARGS__)))) \--#endif
libsass/src/node.cpp view
@@ -1,7 +1,7 @@+#include "sass.hpp" #include <vector> #include "node.hpp"-#include "to_string.hpp" #include "context.hpp" #include "parser.hpp" @@ -14,10 +14,10 @@ } - Node Node::createSelector(Complex_Selector* pSelector, Context& ctx) {+ Node Node::createSelector(Complex_Selector_Ptr pSelector, Context& ctx) { NodeDequePtr null; - Complex_Selector* pStripped = pSelector->clone(ctx);+ Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(pSelector); pStripped->tail(NULL); pStripped->combinator(Complex_Selector::ANCESTOR_OF); @@ -45,21 +45,21 @@ } - Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection)+ Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector_Ptr pSelector, NodeDequePtr& pCollection) : got_line_feed(false), mType(type), mCombinator(combinator), mpSelector(pSelector), mpCollection(pCollection) { if (pSelector) got_line_feed = pSelector->has_line_feed(); } - Node Node::clone(Context& ctx) const {+ Node Node::klone(Context& ctx) const { NodeDequePtr pNewCollection = std::make_shared<NodeDeque>(); if (mpCollection) { for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toClone = *iter;- pNewCollection->push_back(toClone.clone(ctx));+ pNewCollection->push_back(toClone.klone(ctx)); } } - Node n(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection);+ Node n(mType, mCombinator, mpSelector ? SASS_MEMORY_COPY(mpSelector) : NULL, pNewCollection); n.got_line_feed = got_line_feed; return n; }@@ -101,7 +101,7 @@ } else if (lhs.isSelector()){ - return selectors_equal(*lhs.selector(), *rhs.selector(), simpleSelectorOrderDependent);+ return selectors_equal(*&lhs.selector(), *&rhs.selector(), simpleSelectorOrderDependent); } else if (lhs.isCollection()) { @@ -153,8 +153,7 @@ } else if (node.isSelector()){ - To_String to_string;- os << node.selector()->head()->perform(&to_string);+ os << node.selector()->head()->to_string(); } else if (node.isCollection()) { @@ -178,7 +177,7 @@ #endif - Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx) {+ Node complexSelectorToNode(Complex_Selector_Ptr pToConvert, Context& ctx) { if (pToConvert == NULL) { return Node::createNil(); }@@ -188,20 +187,20 @@ // unwrap the selector from parent ref if (pToConvert->head() && pToConvert->head()->has_parent_ref()) {- Complex_Selector* tail = pToConvert->tail();+ Complex_Selector_Obj tail = pToConvert->tail(); if (tail) tail->has_line_feed(pToConvert->has_line_feed());- pToConvert = tail;+ pToConvert = &tail; } while (pToConvert) { bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference(); - if (pToConvert->head() == NULL || empty_parent_ref) {+ if (pToConvert->head() || empty_parent_ref) { } // the first Complex_Selector may contain a dummy head pointer, skip it.- if (pToConvert->head() != NULL && !empty_parent_ref) {+ if (pToConvert->head() && !empty_parent_ref) { node.collection()->push_back(Node::createSelector(pToConvert, ctx)); if (has_lf) node.collection()->back().got_line_feed = has_lf; has_lf = false;@@ -217,21 +216,21 @@ // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed()); } - pToConvert = pToConvert->tail();+ pToConvert = &pToConvert->tail(); } return node; } - Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx) {+ Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert, Context& ctx) { if (toConvert.isNil()) { return NULL; } if (!toConvert.isCollection()) {- throw "The node to convert to a Complex_Selector* must be a collection type or nil.";+ throw "The node to convert to a Complex_Selector_Ptr must be a collection type or nil."; } @@ -239,9 +238,9 @@ std::string noPath(""); Position noPosition(-1, -1, -1);- Complex_Selector* pFirst = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL);+ Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL); - Complex_Selector* pCurrent = pFirst;+ Complex_Selector_Obj pCurrent = pFirst; if (toConvert.isSelector()) pFirst->has_line_feed(toConvert.got_line_feed); if (toConvert.isCombinator()) pFirst->has_line_feed(toConvert.got_line_feed);@@ -251,9 +250,11 @@ Node& child = *childIter; if (child.isSelector()) {- pCurrent->tail(child.selector()->clone(ctx)); // JMA - need to clone the selector, because they can end up getting shared across Node collections, and can result in an infinite loop during the call to parentSuperselector()+ // JMA - need to clone the selector, because they can end up getting shared across Node+ // collections, and can result in an infinite loop during the call to parentSuperselector()+ pCurrent->tail(SASS_MEMORY_COPY(child.selector())); // if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);- pCurrent = pCurrent->tail();+ pCurrent = &pCurrent->tail(); } else if (child.isCombinator()) { pCurrent->combinator(child.combinator()); if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);@@ -262,9 +263,9 @@ if (childIter+1 != childIterEnd) { Node& nextNode = *(childIter+1); if (nextNode.isCombinator()) {- pCurrent->tail(SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL));+ pCurrent->tail(SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL)); if (nextNode.got_line_feed) pCurrent->tail()->has_line_feed(nextNode.got_line_feed);- pCurrent = pCurrent->tail();+ pCurrent = &pCurrent->tail(); } } } else {@@ -273,13 +274,13 @@ } // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass- Compound_Selector* fakeHead = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, ParserState("[NODE]"), 1);- Parent_Selector* selectorRef = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, ParserState("[NODE]"));+ Compound_Selector_Ptr fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[NODE]"), 1);+ Parent_Selector_Ptr selectorRef = SASS_MEMORY_NEW(Parent_Selector, ParserState("[NODE]")); fakeHead->elements().push_back(selectorRef); if (toConvert.got_line_feed) pFirst->has_line_feed(toConvert.got_line_feed); // pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed); pFirst->head(fakeHead);- return pFirst;+ return SASS_MEMORY_COPY(pFirst); } // A very naive trim function, which removes duplicates in a node@@ -287,7 +288,7 @@ Node Node::naiveTrim(Node& seqses, Context& ctx) { std::vector<Node*> res;- std::vector<Complex_Selector*> known;+ std::vector<Complex_Selector_Obj> known; NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(), seqsesIterEnd = seqses.collection()->rend();@@ -296,8 +297,8 @@ { Node& seqs1 = *seqsesIter; if( seqs1.isSelector() ) {- Complex_Selector* sel = seqs1.selector();- std::vector<Complex_Selector*>::iterator it;+ Complex_Selector_Obj sel = seqs1.selector();+ std::vector<Complex_Selector_Obj>::iterator it; bool found = false; for (it = known.begin(); it != known.end(); ++it) { if (**it == *sel) { found = true; break; }
libsass/src/node.hpp view
@@ -52,16 +52,16 @@ Complex_Selector::Combinator combinator() const { return mCombinator; } - Complex_Selector* selector() { return mpSelector; }- const Complex_Selector* selector() const { return mpSelector; }+ Complex_Selector_Obj selector() { return mpSelector; }+ Complex_Selector_Obj selector() const { return mpSelector; } NodeDequePtr collection() { return mpCollection; } const NodeDequePtr collection() const { return mpCollection; } static Node createCombinator(const Complex_Selector::Combinator& combinator); - // This method will clone the selector, stripping off the tail and combinator- static Node createSelector(Complex_Selector* pSelector, Context& ctx);+ // This method will klone the selector, stripping off the tail and combinator+ static Node createSelector(Complex_Selector_Ptr pSelector, Context& ctx); static Node createCollection(); static Node createCollection(const NodeDeque& values);@@ -69,7 +69,7 @@ static Node createNil(); static Node naiveTrim(Node& seqses, Context& ctx); - Node clone(Context& ctx) const;+ Node klone(Context& ctx) const; bool operator==(const Node& rhs) const; inline bool operator!=(const Node& rhs) const { return !(*this == rhs); }@@ -97,21 +97,21 @@ // Private constructor; Use the static methods (like createCombinator and createSelector) // to instantiate this object. This is more expressive, and it allows us to break apart each // case into separate functions.- Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection);+ Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector_Ptr pSelector, NodeDequePtr& pCollection); TYPE mType; // TODO: can we union these to save on memory? Complex_Selector::Combinator mCombinator;- Complex_Selector* mpSelector; // this is an AST_Node, so it will be handled by the Memory_Manager+ Complex_Selector_Obj mpSelector; NodeDequePtr mpCollection; }; #ifdef DEBUG std::ostream& operator<<(std::ostream& os, const Node& node); #endif- Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx);- Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx);+ Node complexSelectorToNode(Complex_Selector_Ptr pToConvert, Context& ctx);+ Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert, Context& ctx); bool nodesEqual(const Node& one, const Node& two, bool simpleSelectorOrderDependent);
libsass/src/operation.hpp view
@@ -8,78 +8,79 @@ template<typename T> class Operation { public:- virtual T operator()(AST_Node* x) = 0;+ virtual T operator()(AST_Node_Ptr x) = 0; virtual ~Operation() { } // statements- virtual T operator()(Block* x) = 0;- virtual T operator()(Ruleset* x) = 0;- virtual T operator()(Propset* x) = 0;- virtual T operator()(Bubble* x) = 0;- virtual T operator()(Supports_Block* x) = 0;- virtual T operator()(Media_Block* x) = 0;- virtual T operator()(At_Root_Block* x) = 0;- virtual T operator()(At_Rule* x) = 0;- virtual T operator()(Keyframe_Rule* x) = 0;- virtual T operator()(Declaration* x) = 0;- virtual T operator()(Assignment* x) = 0;- virtual T operator()(Import* x) = 0;- virtual T operator()(Import_Stub* x) = 0;- virtual T operator()(Warning* x) = 0;- virtual T operator()(Error* x) = 0;- virtual T operator()(Debug* x) = 0;- virtual T operator()(Comment* x) = 0;- virtual T operator()(If* x) = 0;- virtual T operator()(For* x) = 0;- virtual T operator()(Each* x) = 0;- virtual T operator()(While* x) = 0;- virtual T operator()(Return* x) = 0;- virtual T operator()(Content* x) = 0;- virtual T operator()(Extension* x) = 0;- virtual T operator()(Definition* x) = 0;- virtual T operator()(Mixin_Call* x) = 0;+ virtual T operator()(Block_Ptr x) = 0;+ virtual T operator()(Ruleset_Ptr x) = 0;+ virtual T operator()(Bubble_Ptr x) = 0;+ virtual T operator()(Trace_Ptr x) = 0;+ virtual T operator()(Supports_Block_Ptr x) = 0;+ virtual T operator()(Media_Block_Ptr x) = 0;+ virtual T operator()(At_Root_Block_Ptr x) = 0;+ virtual T operator()(Directive_Ptr x) = 0;+ virtual T operator()(Keyframe_Rule_Ptr x) = 0;+ virtual T operator()(Declaration_Ptr x) = 0;+ virtual T operator()(Assignment_Ptr x) = 0;+ virtual T operator()(Import_Ptr x) = 0;+ virtual T operator()(Import_Stub_Ptr x) = 0;+ virtual T operator()(Warning_Ptr x) = 0;+ virtual T operator()(Error_Ptr x) = 0;+ virtual T operator()(Debug_Ptr x) = 0;+ virtual T operator()(Comment_Ptr x) = 0;+ virtual T operator()(If_Ptr x) = 0;+ virtual T operator()(For_Ptr x) = 0;+ virtual T operator()(Each_Ptr x) = 0;+ virtual T operator()(While_Ptr x) = 0;+ virtual T operator()(Return_Ptr x) = 0;+ virtual T operator()(Content_Ptr x) = 0;+ virtual T operator()(Extension_Ptr x) = 0;+ virtual T operator()(Definition_Ptr x) = 0;+ virtual T operator()(Mixin_Call_Ptr x) = 0; // expressions- virtual T operator()(List* x) = 0;- virtual T operator()(Map* x) = 0;- virtual T operator()(Binary_Expression* x) = 0;- virtual T operator()(Unary_Expression* x) = 0;- virtual T operator()(Function_Call* x) = 0;- virtual T operator()(Function_Call_Schema* x) = 0;- virtual T operator()(Custom_Warning* x) = 0;- virtual T operator()(Custom_Error* x) = 0;- virtual T operator()(Variable* x) = 0;- virtual T operator()(Textual* x) = 0;- virtual T operator()(Number* x) = 0;- virtual T operator()(Color* x) = 0;- virtual T operator()(Boolean* x) = 0;- virtual T operator()(String_Schema* x) = 0;- virtual T operator()(String_Quoted* x) = 0;- virtual T operator()(String_Constant* x) = 0;- virtual T operator()(Supports_Condition* x) = 0;- virtual T operator()(Supports_Operator* x) = 0;- virtual T operator()(Supports_Negation* x) = 0;- virtual T operator()(Supports_Declaration* x) = 0;- virtual T operator()(Supports_Interpolation* x) = 0;- virtual T operator()(Media_Query* x) = 0;- virtual T operator()(Media_Query_Expression* x) = 0;- virtual T operator()(At_Root_Expression* x) = 0;- virtual T operator()(Null* x) = 0;- virtual T operator()(Parent_Selector* x) = 0;+ virtual T operator()(List_Ptr x) = 0;+ virtual T operator()(Map_Ptr x) = 0;+ virtual T operator()(Binary_Expression_Ptr x) = 0;+ virtual T operator()(Unary_Expression_Ptr x) = 0;+ virtual T operator()(Function_Call_Ptr x) = 0;+ virtual T operator()(Function_Call_Schema_Ptr x) = 0;+ virtual T operator()(Custom_Warning_Ptr x) = 0;+ virtual T operator()(Custom_Error_Ptr x) = 0;+ virtual T operator()(Variable_Ptr x) = 0;+ virtual T operator()(Textual_Ptr x) = 0;+ virtual T operator()(Number_Ptr x) = 0;+ virtual T operator()(Color_Ptr x) = 0;+ virtual T operator()(Boolean_Ptr x) = 0;+ virtual T operator()(String_Schema_Ptr x) = 0;+ virtual T operator()(String_Quoted_Ptr x) = 0;+ virtual T operator()(String_Constant_Ptr x) = 0;+ virtual T operator()(Supports_Condition_Ptr x) = 0;+ virtual T operator()(Supports_Operator_Ptr x) = 0;+ virtual T operator()(Supports_Negation_Ptr x) = 0;+ virtual T operator()(Supports_Declaration_Ptr x) = 0;+ virtual T operator()(Supports_Interpolation_Ptr x) = 0;+ virtual T operator()(Media_Query_Ptr x) = 0;+ virtual T operator()(Media_Query_Expression_Ptr x) = 0;+ virtual T operator()(At_Root_Query_Ptr x) = 0;+ virtual T operator()(Null_Ptr x) = 0;+ virtual T operator()(Parent_Selector_Ptr x) = 0; // parameters and arguments- virtual T operator()(Parameter* x) = 0;- virtual T operator()(Parameters* x) = 0;- virtual T operator()(Argument* x) = 0;- virtual T operator()(Arguments* x) = 0;+ virtual T operator()(Parameter_Ptr x) = 0;+ virtual T operator()(Parameters_Ptr x) = 0;+ virtual T operator()(Argument_Ptr x) = 0;+ virtual T operator()(Arguments_Ptr x) = 0; // selectors- virtual T operator()(Selector_Schema* x) = 0;- virtual T operator()(Selector_Placeholder* x) = 0;- virtual T operator()(Type_Selector* x) = 0;- virtual T operator()(Selector_Qualifier* x) = 0;- virtual T operator()(Attribute_Selector* x) = 0;- virtual T operator()(Pseudo_Selector* x) = 0;- virtual T operator()(Wrapped_Selector* x) = 0;- virtual T operator()(Compound_Selector* x) = 0;- virtual T operator()(Complex_Selector* x) = 0;- virtual T operator()(Selector_List* x) = 0;+ virtual T operator()(Selector_Schema_Ptr x) = 0;+ virtual T operator()(Placeholder_Selector_Ptr x) = 0;+ virtual T operator()(Element_Selector_Ptr x) = 0;+ virtual T operator()(Class_Selector_Ptr x) = 0;+ virtual T operator()(Id_Selector_Ptr x) = 0;+ virtual T operator()(Attribute_Selector_Ptr x) = 0;+ virtual T operator()(Pseudo_Selector_Ptr x) = 0;+ virtual T operator()(Wrapped_Selector_Ptr x) = 0;+ virtual T operator()(Compound_Selector_Ptr x)= 0;+ virtual T operator()(Complex_Selector_Ptr x) = 0;+ virtual T operator()(Selector_List_Ptr x) = 0; template <typename U> T fallback(U x) { return T(); }@@ -88,84 +89,84 @@ template <typename T, typename D> class Operation_CRTP : public Operation<T> { public:- virtual T operator()(AST_Node* x) { return static_cast<D*>(this)->fallback(x); }- virtual ~Operation_CRTP() = 0;+ D& impl() { return static_cast<D&>(*this); }+ public:+ T operator()(AST_Node_Ptr x) { return static_cast<D*>(this)->fallback(x); } // statements- virtual T operator()(Block* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Ruleset* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Propset* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Bubble* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Supports_Block* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Media_Block* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(At_Root_Block* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(At_Rule* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Keyframe_Rule* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Declaration* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Assignment* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Import* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Import_Stub* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Warning* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Error* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Debug* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Comment* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(If* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(For* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Each* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(While* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Return* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Content* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Extension* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Definition* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Mixin_Call* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Ruleset_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Bubble_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Trace_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Media_Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(At_Root_Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Directive_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Keyframe_Rule_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Declaration_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Assignment_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Import_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Import_Stub_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Warning_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Error_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Debug_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Comment_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(If_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(For_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Each_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(While_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Return_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Content_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Extension_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Definition_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Mixin_Call_Ptr x) { return static_cast<D*>(this)->fallback(x); } // expressions- virtual T operator()(List* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Map* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Binary_Expression* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Unary_Expression* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Function_Call* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Function_Call_Schema* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Custom_Warning* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Custom_Error* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Variable* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Textual* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Number* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Color* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Boolean* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(String_Schema* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(String_Constant* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(String_Quoted* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Supports_Condition* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Supports_Operator* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Supports_Negation* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Supports_Declaration* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Supports_Interpolation* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Media_Query* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Media_Query_Expression* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(At_Root_Expression* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Null* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Parent_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(List_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Map_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Binary_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Unary_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Function_Call_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Function_Call_Schema_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Custom_Warning_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Custom_Error_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Variable_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Textual_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Number_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Color_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Boolean_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(String_Schema_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(String_Constant_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(String_Quoted_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Condition_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Operator_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Negation_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Declaration_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Interpolation_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Media_Query_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Media_Query_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(At_Root_Query_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Null_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Parent_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); } // parameters and arguments- virtual T operator()(Parameter* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Parameters* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Argument* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Arguments* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Parameter_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Parameters_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Argument_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Arguments_Ptr x) { return static_cast<D*>(this)->fallback(x); } // selectors- virtual T operator()(Selector_Schema* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Selector_Placeholder* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Type_Selector* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Selector_Qualifier* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Attribute_Selector* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Pseudo_Selector* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Wrapped_Selector* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Compound_Selector* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Complex_Selector* x) { return static_cast<D*>(this)->fallback(x); }- virtual T operator()(Selector_List* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Selector_Schema_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Placeholder_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Element_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Class_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Id_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Attribute_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Pseudo_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Wrapped_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Compound_Selector_Ptr x){ return static_cast<D*>(this)->fallback(x); }+ T operator()(Complex_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Selector_List_Ptr x) { return static_cast<D*>(this)->fallback(x); } template <typename U>- T fallback(U x) { return T(); }+ T fallback(U x) { return T(); } };- template<typename T, typename D>- inline Operation_CRTP<T, D>::~Operation_CRTP() { } }
libsass/src/output.cpp view
@@ -1,56 +1,50 @@+#include "sass.hpp" #include "ast.hpp" #include "output.hpp"-#include "to_string.hpp" namespace Sass { - Output::Output(Context* ctx)- : Inspect(Emitter(ctx)),+ Output::Output(Sass_Output_Options& opt)+ : Inspect(Emitter(opt)), charset(""), top_nodes(0) {} Output::~Output() { } - void Output::fallback_impl(AST_Node* n)+ void Output::fallback_impl(AST_Node_Ptr n) { return n->perform(this); } - void Output::operator()(Number* n)+ void Output::operator()(Number_Ptr n) { // use values to_string facility- To_String to_string(ctx);- std::string res = n->perform(&to_string);+ std::string res = n->to_string(opt); // check for a valid unit here // includes result for reporting- if (n->numerator_units().size() > 1 ||- n->denominator_units().size() > 0 ||- (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != std::string::npos) ||- (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != std::string::npos)- ) {- error(res + " isn't a valid CSS value.", n->pstate());+ if (!n->is_valid_css_unit()) {+ throw Exception::InvalidValue(*n); } // output the final token append_token(res, n); } - void Output::operator()(Import* imp)+ void Output::operator()(Import_Ptr imp) { top_nodes.push_back(imp); } - void Output::operator()(Map* m)+ void Output::operator()(Map_Ptr m) {- To_String to_string(ctx);- std::string dbg(m->perform(&to_string));+ std::string dbg(m->to_string(opt)); error(dbg + " isn't a valid CSS value.", m->pstate()); } OutputBuffer Output::get_buffer(void) { - Emitter emitter(ctx);+ Emitter emitter(opt); Inspect inspect(emitter); size_t size_nodes = top_nodes.size();@@ -60,23 +54,25 @@ } // flush scheduled outputs- inspect.finalize();+ // maybe omit semicolon if possible+ inspect.finalize(wbuf.buffer.size() == 0); // prepend buffer on top prepend_output(inspect.output()); // make sure we end with a linefeed- if (!ends_with(wbuf.buffer, ctx->linefeed)) {+ if (!ends_with(wbuf.buffer, opt.linefeed)) { // if the output is not completely empty- if (!wbuf.buffer.empty()) append_string(ctx->linefeed);+ if (!wbuf.buffer.empty()) append_string(opt.linefeed); } // search for unicode char for(const char& chr : wbuf.buffer) { // skip all ascii chars- if (chr >= 0) continue;+ // static cast to unsigned to handle `char` being signed / unsigned+ if (static_cast<unsigned>(chr) < 128) continue; // declare the charset- if (output_style() != SASS_STYLE_COMPRESSED)+ if (output_style() != COMPRESSED) charset = "@charset \"UTF-8\";"- + ctx->linefeed;+ + std::string(opt.linefeed); else charset = "\xEF\xBB\xBF"; // abort search break;@@ -89,13 +85,12 @@ } - void Output::operator()(Comment* c)+ void Output::operator()(Comment_Ptr c) {- To_String to_string(ctx);- std::string txt = c->text()->perform(&to_string);+ std::string txt = c->text()->to_string(opt); // if (indentation && txt == "/**/") return; bool important = c->is_important();- if (output_style() != SASS_STYLE_COMPRESSED || important) {+ if (output_style() != COMPRESSED || important) { if (buffer().size() == 0) { top_nodes.push_back(c); } else {@@ -112,87 +107,72 @@ } } - void Output::operator()(Ruleset* r)+ void Output::operator()(Ruleset_Ptr r) {- Selector* s = r->selector();- Block* b = r->block();- bool decls = false;+ Selector_Obj s = r->selector();+ Block_Obj b = r->block(); // Filter out rulesets that aren't printable (process its children though) if (!Util::isPrintable(r, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (dynamic_cast<Has_Block*>(stm)) {- stm->perform(this);+ const Statement_Obj& stm = b->at(i);+ if (dynamic_cast<Has_Block_Ptr>(&stm)) {+ if (!dynamic_cast<Declaration_Ptr>(&stm)) {+ stm->perform(this);+ } } } return; } - if (b->has_non_hoistable()) {- decls = true;- if (output_style() == SASS_STYLE_NESTED) indentation += r->tabs();- if (ctx && ctx->c_options->source_comments) {- std::stringstream ss;- append_indentation();- ss << "/* line " << r->pstate().line + 1 << ", " << r->pstate().path << " */";- append_string(ss.str());- append_optional_linefeed();- }- s->perform(this);- append_scope_opener(b);- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- bool bPrintExpression = true;- // Check print conditions- if (typeid(*stm) == typeid(Declaration)) {- Declaration* dec = static_cast<Declaration*>(stm);- if (dec->value()->concrete_type() == Expression::STRING) {- String_Constant* valConst = static_cast<String_Constant*>(dec->value());- std::string val(valConst->value());- if (auto qstr = dynamic_cast<String_Quoted*>(valConst)) {- if (!qstr->quote_mark() && val.empty()) {- bPrintExpression = false;- }+ if (output_style() == NESTED) indentation += r->tabs();+ if (opt.source_comments) {+ std::stringstream ss;+ append_indentation();+ std::string path(File::abs2rel(r->pstate().path));+ ss << "/* line " << r->pstate().line + 1 << ", " << path << " */";+ append_string(ss.str());+ append_optional_linefeed();+ }+ if (s) s->perform(this);+ append_scope_opener(&b);+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement_Obj stm = b->at(i);+ bool bPrintExpression = true;+ // Check print conditions+ if (Declaration_Ptr dec = SASS_MEMORY_CAST(Declaration, stm)) {+ if (String_Constant_Ptr valConst = SASS_MEMORY_CAST(String_Constant, dec->value())) {+ std::string val(valConst->value());+ if (String_Quoted_Ptr qstr = SASS_MEMORY_CAST_PTR(String_Quoted, valConst)) {+ if (!qstr->quote_mark() && val.empty()) {+ bPrintExpression = false; } }- else if (dec->value()->concrete_type() == Expression::LIST) {- List* list = static_cast<List*>(dec->value());- bool all_invisible = true;- for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {- Expression* item = (*list)[list_i];- if (!item->is_invisible()) all_invisible = false;- }- if (all_invisible) bPrintExpression = false;- } }- // Print if OK- if (!stm->is_hoistable() && bPrintExpression) {- stm->perform(this);+ else if (List_Ptr list = SASS_MEMORY_CAST(List, dec->value())) {+ bool all_invisible = true;+ for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {+ Expression_Ptr item = &list->at(list_i);+ if (!item->is_invisible()) all_invisible = false;+ }+ if (all_invisible) bPrintExpression = false; } }- if (output_style() == SASS_STYLE_NESTED) indentation -= r->tabs();- append_scope_closer(b);- }-- if (b->has_hoistable()) {- if (decls) ++indentation;- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (stm->is_hoistable()) {- stm->perform(this);- }+ // Print if OK+ if (bPrintExpression) {+ stm->perform(this); }- if (decls) --indentation; }- }+ if (output_style() == NESTED) indentation -= r->tabs();+ append_scope_closer(&b); - void Output::operator()(Keyframe_Rule* r)+ }+ void Output::operator()(Keyframe_Rule_Ptr r) {- Block* b = r->block();- Selector* v = r->selector();+ Block_Obj b = r->block();+ Selector_Obj v = r->name(); - if (v) {+ if (&v) { v->perform(this); } @@ -203,124 +183,93 @@ append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (!stm->is_hoistable()) {- stm->perform(this);- if (i < L - 1) append_special_linefeed();- }- }-- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (stm->is_hoistable()) {- stm->perform(this);- }+ Statement_Obj stm = b->at(i);+ stm->perform(this);+ if (i < L - 1) append_special_linefeed(); }- append_scope_closer(); } - void Output::operator()(Supports_Block* f)+ void Output::operator()(Supports_Block_Ptr f) { if (f->is_invisible()) return; - Supports_Condition* c = f->condition();- Block* b = f->block();+ Supports_Condition_Obj c = f->condition();+ Block_Obj b = f->block(); // Filter out feature blocks that aren't printable (process its children though) if (!Util::isPrintable(f, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (dynamic_cast<Has_Block*>(stm)) {+ Statement_Obj stm = b->at(i);+ if (dynamic_cast<Has_Block_Ptr>(&stm)) { stm->perform(this); } } return; } - if (output_style() == SASS_STYLE_NESTED) indentation += f->tabs();+ if (output_style() == NESTED) indentation += f->tabs(); append_indentation(); append_token("@supports", f); append_mandatory_space(); c->perform(this); append_scope_opener(); - if (b->has_non_hoistable()) {- // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable- append_scope_opener();-- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (!stm->is_hoistable()) {- stm->perform(this);- }- }-- append_scope_closer();-- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (stm->is_hoistable()) {- stm->perform(this);- }- }- }- else {- // JMA - not hoisted, just output in order- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- stm->perform(this);- if (i < L - 1) append_special_linefeed();- }+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement_Obj stm = b->at(i);+ stm->perform(this);+ if (i < L - 1) append_special_linefeed(); } - if (output_style() == SASS_STYLE_NESTED) indentation -= f->tabs();+ if (output_style() == NESTED) indentation -= f->tabs(); append_scope_closer(); } - void Output::operator()(Media_Block* m)+ void Output::operator()(Media_Block_Ptr m) { if (m->is_invisible()) return; - List* q = m->media_queries();- Block* b = m->block();+ Block_Obj b = m->block(); // Filter out media blocks that aren't printable (process its children though) if (!Util::isPrintable(m, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (dynamic_cast<Has_Block*>(stm)) {+ Statement_Obj stm = b->at(i);+ if (dynamic_cast<Has_Block_Ptr>(&stm)) { stm->perform(this); } } return; }- if (output_style() == SASS_STYLE_NESTED) indentation += m->tabs();+ if (output_style() == NESTED) indentation += m->tabs(); append_indentation(); append_token("@media", m); append_mandatory_space(); in_media_block = true;- q->perform(this);+ m->media_queries()->perform(this); in_media_block = false; append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) {- if ((*b)[i]) (*b)[i]->perform(this);+ if (b->at(i)) {+ Statement_Obj stm = b->at(i);+ stm->perform(this);+ } if (i < L - 1) append_special_linefeed(); } - if (output_style() == SASS_STYLE_NESTED) indentation -= m->tabs();+ if (output_style() == NESTED) indentation -= m->tabs(); append_scope_closer(); } - void Output::operator()(At_Rule* a)+ void Output::operator()(Directive_Ptr a) { std::string kwd = a->keyword();- Selector* s = a->selector();- Expression* v = a->value();- Block* b = a->block();+ Selector_Obj s = a->selector();+ Expression_Obj v = a->value();+ Block_Obj b = a->block(); append_indentation(); append_token(kwd, a);@@ -332,7 +281,8 @@ } if (v) { append_mandatory_space();- v->perform(this);+ // ruby sass bug? should use options?+ append_token(v->to_string(/* opt */), &v); } if (!b) { append_delimiter();@@ -340,7 +290,8 @@ } if (b->is_invisible() || b->length() == 0) {- return append_string(" {}");+ append_optional_space();+ return append_string("{}"); } append_scope_opener();@@ -348,25 +299,15 @@ bool format = kwd != "@font-face";; for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (!stm->is_hoistable()) {- stm->perform(this);- if (i < L - 1 && format) append_special_linefeed();- }- }-- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (stm->is_hoistable()) {- stm->perform(this);- if (i < L - 1 && format) append_special_linefeed();- }+ Statement_Obj stm = b->at(i);+ stm->perform(this);+ if (i < L - 1 && format) append_special_linefeed(); } append_scope_closer(); } - void Output::operator()(String_Quoted* s)+ void Output::operator()(String_Quoted_Ptr s) { if (s->quote_mark()) { append_token(quote(s->value(), s->quote_mark()), s);@@ -377,10 +318,10 @@ } } - void Output::operator()(String_Constant* s)+ void Output::operator()(String_Constant_Ptr s) { std::string value(s->value());- if (s->can_compress_whitespace() && output_style() == SASS_STYLE_COMPRESSED) {+ if (s->can_compress_whitespace() && output_style() == COMPRESSED) { value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); } if (!in_comment) {
libsass/src/output.hpp view
@@ -23,31 +23,29 @@ using Inspect::operator(); public:- // change to Emitter- Output(Context* ctx);+ Output(Sass_Output_Options& opt); virtual ~Output(); protected: std::string charset;- std::vector<AST_Node*> top_nodes;+ std::vector<AST_Node_Ptr> top_nodes; public: OutputBuffer get_buffer(void); - virtual void operator()(Map*);- virtual void operator()(Ruleset*);- // virtual void operator()(Propset*);- virtual void operator()(Supports_Block*);- virtual void operator()(Media_Block*);- virtual void operator()(At_Rule*);- virtual void operator()(Keyframe_Rule*);- virtual void operator()(Import*);- virtual void operator()(Comment*);- virtual void operator()(Number*);- virtual void operator()(String_Quoted*);- virtual void operator()(String_Constant*);+ virtual void operator()(Map_Ptr);+ virtual void operator()(Ruleset_Ptr);+ virtual void operator()(Supports_Block_Ptr);+ virtual void operator()(Media_Block_Ptr);+ virtual void operator()(Directive_Ptr);+ virtual void operator()(Keyframe_Rule_Ptr);+ virtual void operator()(Import_Ptr);+ virtual void operator()(Comment_Ptr);+ virtual void operator()(Number_Ptr);+ virtual void operator()(String_Quoted_Ptr);+ virtual void operator()(String_Constant_Ptr); - void fallback_impl(AST_Node* n);+ void fallback_impl(AST_Node_Ptr n); };
libsass/src/parser.cpp view
@@ -1,2434 +1,2774 @@-#include <cstdlib>-#include <iostream>-#include <vector>-#include "parser.hpp"-#include "file.hpp"-#include "inspect.hpp"-#include "to_string.hpp"-#include "constants.hpp"-#include "util.hpp"-#include "prelexer.hpp"-#include "color_maps.hpp"-#include "sass/functions.h"-#include "error_handling.hpp"--#include <typeinfo>-#include <tuple>--namespace Sass {- using namespace Constants;- using namespace Prelexer;-- Parser Parser::from_c_str(const char* str, Context& ctx, ParserState pstate)- {- Parser p(ctx, pstate);- p.source = str;- p.position = p.source;- p.end = str + strlen(str);- Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);- p.block_stack.push_back(root);- root->is_root(true);- return p;- }-- Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate)- {- Parser p(ctx, pstate);- p.source = beg;- p.position = p.source;- p.end = end;- Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);- p.block_stack.push_back(root);- root->is_root(true);- return p;- }-- Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)- {- Parser p = Parser::from_c_str(src, ctx, pstate);- // ToDo: ruby sass errors on parent references- // ToDo: remap the source-map entries somehow- return p.parse_selector_list(false);- }-- bool Parser::peek_newline(const char* start)- {- return peek_linefeed(start ? start : position)- && ! peek_css<exactly<'{'>>(start);- }-- Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)- {- Parser p(ctx, pstate);- p.source = t.begin;- p.position = p.source;- p.end = t.end;- Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);- p.block_stack.push_back(root);- root->is_root(true);- return p;- }-- /* main entry point to parse root block */- Block* Parser::parse()- {- bool is_root = false;- Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true);- read_bom();-- // custom headers- if (ctx.resources.size() == 1) {- is_root = true;- ctx.apply_custom_headers(root, path, pstate);- }-- block_stack.push_back(root);- /* bool rv = */ parse_block_nodes(is_root);- block_stack.pop_back();-- // update for end position- root->update_pstate(pstate);-- if (position != end) {- css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");- }-- return root;- }--- // convenience function for block parsing- // will create a new block ad-hoc for you- // this is the base block parsing function- Block* Parser::parse_css_block(bool is_root)- {-- // parse comments before block- // lex < optional_css_comments >();-- // lex mandatory opener or error out- if (!lex_css < exactly<'{'> >()) {- css_error("Invalid CSS", " after ", ": expected \"{\", was ");- }- // create new block and push to the selector stack- Block* block = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, is_root);- block_stack.push_back(block);-- if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");;-- if (!lex_css < exactly<'}'> >()) {- css_error("Invalid CSS", " after ", ": expected \"}\", was ");- }-- // update for end position- block->update_pstate(pstate);-- // parse comments after block- // lex < optional_css_comments >();-- block_stack.pop_back();-- return block;- }-- // convenience function for block parsing- // will create a new block ad-hoc for you- // also updates the `in_at_root` flag- Block* Parser::parse_block(bool is_root)- {- LOCAL_FLAG(in_at_root, is_root);- return parse_css_block(is_root);- }-- // the main block parsing function- // parses stuff between `{` and `}`- bool Parser::parse_block_nodes(bool is_root)- {-- // loop until end of string- while (position < end) {-- // we should be able to refactor this- parse_block_comments();- lex < css_whitespace >();-- if (lex < exactly<';'> >()) continue;- if (peek < end_of_file >()) return true;- if (peek < exactly<'}'> >()) return true;-- if (parse_block_node(is_root)) continue;-- parse_block_comments();-- if (lex_css < exactly<';'> >()) continue;- if (peek_css < end_of_file >()) return true;- if (peek_css < exactly<'}'> >()) return true;-- // illegal sass- return false;- }- // return success- return true;- }-- // parser for a single node in a block- // semicolons must be lexed beforehand- bool Parser::parse_block_node(bool is_root) {-- Block* block = block_stack.back();-- while (lex< block_comment >()) {- bool is_important = lexed.begin[2] == '!';- String* contents = parse_interpolated_chunk(lexed);- (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);- }-- // throw away white-space- // includes line comments- lex < css_whitespace >();-- Lookahead lookahead_result;-- // also parse block comments-- // first parse everything that is allowed in functions- if (lex < variable >(true)) { (*block) << parse_assignment(); }- else if (lex < kwd_err >(true)) { (*block) << parse_error(); }- else if (lex < kwd_dbg >(true)) { (*block) << parse_debug(); }- else if (lex < kwd_warn >(true)) { (*block) << parse_warning(); }- else if (lex < kwd_if_directive >(true)) { (*block) << parse_if_directive(); }- else if (lex < kwd_for_directive >(true)) { (*block) << parse_for_directive(); }- else if (lex < kwd_each_directive >(true)) { (*block) << parse_each_directive(); }- else if (lex < kwd_while_directive >(true)) { (*block) << parse_while_directive(); }- else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); }-- // abort if we are in function context and have nothing parsed yet- else if (stack.back() == function_def) {- error("Functions can only contain variable declarations and control directives", pstate);- }-- // parse imports to process later- else if (lex < kwd_import >(true)) {- if (stack.back() == mixin_def || stack.back() == function_def) {- error("Import directives may not be used within control directives or mixins.", pstate);- }- Import* imp = parse_import();- // if it is a url, we only add the statement- if (!imp->urls().empty()) (*block) << imp;- // process all resources now (add Import_Stub nodes)- for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {- (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->incs()[i]);- }- }-- else if (lex < kwd_extend >(true)) {- if (block->is_root()) {- error("Extend directives may only be used within rules.", pstate);- }-- Lookahead lookahead = lookahead_for_include(position);- if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");- Selector* target;- if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);- else target = parse_selector_list(true);- (*block) << SASS_MEMORY_NEW(ctx.mem, Extension, pstate, target);- }-- // selector may contain interpolations which need delayed evaluation- else if (!(lookahead_result = lookahead_for_selector(position)).error)- { (*block) << parse_ruleset(lookahead_result, is_root); }-- // parse multiple specific keyword directives- else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); }- else if (lex < kwd_at_root >(true)) { (*block) << parse_at_root_block(); }- else if (lex < kwd_include_directive >(true)) { (*block) << parse_include_directive(); }- else if (lex < kwd_content_directive >(true)) { (*block) << parse_content_directive(); }- else if (lex < kwd_supports_directive >(true)) { (*block) << parse_supports_directive(); }- else if (lex < kwd_mixin >(true)) { (*block) << parse_definition(Definition::MIXIN); }- else if (lex < kwd_function >(true)) { (*block) << parse_definition(Definition::FUNCTION); }-- // ignore the @charset directive for now- else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); }-- // generic at keyword (keep last)- else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); }-- else if (block->is_root()) {- lex< css_whitespace >();- if (position >= end) return true;- css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was ");- }- // parse a declaration- else- {- // ToDo: how does it handle parse errors?- // maybe we are expected to parse something?- Declaration* decl = parse_declaration();- decl->tabs(indentation);- (*block) << decl;- // maybe we have a "sub-block"- if (peek< exactly<'{'> >()) {- if (decl->is_indented()) ++ indentation;- // parse a propset that rides on the declaration's property- (*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block());- if (decl->is_indented()) -- indentation;- }- }- // something matched- return true;- }- // EO parse_block_nodes-- // parse imports inside the- Import* Parser::parse_import()- {- Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate);- std::vector<std::pair<std::string,Function_Call*>> to_import;- bool first = true;- do {- while (lex< block_comment >());- if (lex< quoted_string >()) {- if (!ctx.call_importers(unquote(std::string(lexed)), path, pstate, imp))- {- // push single file import- // import_single_file(imp, lexed);- to_import.push_back(std::pair<std::string,Function_Call*>(std::string(lexed), 0));- }- }- else if (lex< uri_prefix >()) {- Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);- Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args);- if (lex< quoted_string >()) {- Expression* the_url = parse_string();- *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);- }- else if (lex < uri_value >(false)) { // don't skip comments- String* the_url = parse_interpolated_chunk(lexed);- *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);- }- else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {- Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed);- *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);- }- else {- error("malformed URL", pstate);- }- if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);- to_import.push_back(std::pair<std::string, Function_Call*>("", result));- }- else {- if (first) error("@import directive requires a url or quoted path", pstate);- else error("expecting another url or quoted path in @import list", pstate);- }- first = false;- } while (lex_css< exactly<','> >());-- if (!peek_css<alternatives<exactly<';'>,end_of_file>>()) {- List* media_queries = parse_media_queries();- imp->media_queries(media_queries);- }-- for(auto location : to_import) {- if (location.second) {- imp->urls().push_back(location.second);- } else {- ctx.import_url(imp, location.first, path);- }- }-- return imp;- }-- Definition* Parser::parse_definition(Definition::Type which_type)- {- std::string which_str(lexed);- if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);- std::string name(Util::normalize_underscores(lexed));- if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))- { error("Invalid function name \"" + name + "\".", pstate); }- ParserState source_position_of_def = pstate;- Parameters* params = parse_parameters();- if (which_type == Definition::MIXIN) stack.push_back(mixin_def);- else stack.push_back(function_def);- Block* body = parse_block();- stack.pop_back();- Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type);- return def;- }-- Parameters* Parser::parse_parameters()- {- std::string name(lexed);- Position position = after_token;- Parameters* params = SASS_MEMORY_NEW(ctx.mem, Parameters, pstate);- if (lex_css< exactly<'('> >()) {- // if there's anything there at all- if (!peek_css< exactly<')'> >()) {- do (*params) << parse_parameter();- while (lex_css< exactly<','> >());- }- if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);- }- return params;- }-- Parameter* Parser::parse_parameter()- {- while (lex< alternatives < spaces, block_comment > >());- lex < variable >();- std::string name(Util::normalize_underscores(lexed));- ParserState pos = pstate;- Expression* val = 0;- bool is_rest = false;- while (lex< alternatives < spaces, block_comment > >());- if (lex< exactly<':'> >()) { // there's a default value- while (lex< block_comment >());- val = parse_space_list();- val->is_delayed(false);- }- else if (lex< exactly< ellipsis > >()) {- is_rest = true;- }- Parameter* p = SASS_MEMORY_NEW(ctx.mem, Parameter, pos, name, val, is_rest);- return p;- }-- Arguments* Parser::parse_arguments()- {- std::string name(lexed);- Position position = after_token;- Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);- if (lex_css< exactly<'('> >()) {- // if there's anything there at all- if (!peek_css< exactly<')'> >()) {- do (*args) << parse_argument();- while (lex_css< exactly<','> >());- }- if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);- }- return args;- }-- Argument* Parser::parse_argument()- {- if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) {- position += 2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }-- Argument* arg;- if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {- lex_css< variable >();- std::string name(Util::normalize_underscores(lexed));- ParserState p = pstate;- lex_css< exactly<':'> >();- Expression* val = parse_space_list();- val->is_delayed(false);- arg = SASS_MEMORY_NEW(ctx.mem, Argument, p, val, name);- }- else {- bool is_arglist = false;- bool is_keyword = false;- Expression* val = parse_space_list();- val->is_delayed(false);- if (lex_css< exactly< ellipsis > >()) {- if (val->concrete_type() == Expression::MAP) is_keyword = true;- else is_arglist = true;- }- arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword);- }- return arg;- }-- Assignment* Parser::parse_assignment()- {- std::string name(Util::normalize_underscores(lexed));- ParserState var_source_position = pstate;- if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);- Expression* val;- Lookahead lookahead = lookahead_for_value(position);- if (lookahead.has_interpolants && lookahead.found) {- val = parse_value_schema(lookahead.found);- } else {- val = parse_list();- }- val->is_delayed(false);- bool is_default = false;- bool is_global = false;- while (peek< alternatives < default_flag, global_flag > >()) {- if (lex< default_flag >()) is_default = true;- else if (lex< global_flag >()) is_global = true;- }- Assignment* var = SASS_MEMORY_NEW(ctx.mem, Assignment, var_source_position, name, val, is_default, is_global);- return var;- }-- // a ruleset connects a selector and a block- Ruleset* Parser::parse_ruleset(Lookahead lookahead, bool is_root)- {- // make sure to move up the the last position- lex < optional_css_whitespace >(false, true);- // create the connector object (add parts later)- Ruleset* ruleset = SASS_MEMORY_NEW(ctx.mem, Ruleset, pstate);- // parse selector static or as schema to be evaluated later- if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root));- else ruleset->selector(parse_selector_schema(lookahead.found));- // then parse the inner block- ruleset->block(parse_block());- // update for end position- ruleset->update_pstate(pstate);- // inherit is_root from parent block- // need this info for sanity checks- ruleset->is_root(is_root);- // return AST Node- return ruleset;- }-- // parse a selector schema that will be evaluated in the eval stage- // uses a string schema internally to do the actual schema handling- // in the eval stage we will be re-parse it into an actual selector- Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)- {- // move up to the start- lex< optional_spaces >();- const char* i = position;- // selector schema re-uses string schema implementation- String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);- // the selector schema is pretty much just a wrapper for the string schema- Selector_Schema* selector_schema = SASS_MEMORY_NEW(ctx.mem, Selector_Schema, pstate, schema);- selector_schema->media_block(last_media_block);-- // process until end- while (i < end_of_selector) {- // try to parse mutliple interpolants- if (const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector)) {- // accumulate the preceding segment if the position has advanced- if (i < p) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));- // check if the interpolation only contains white-space (error out)- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- // skip over all nested inner interpolations up to our own delimiter- const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);- // pass inner expression to the parser to resolve nested interpolations- Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();- // set status on the list expression- interpolant->is_interpolant(true);- // add to the string schema- (*schema) << interpolant;- // advance position- i = j;- }- // no more interpolants have been found- // add the last segment if there is one- else {- // make sure to add the last bits of the string up to the end (if any)- if (i < end_of_selector) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, end_of_selector));- // exit loop- i = end_of_selector;- }- }- // EO until eos-- // update position- position = i;-- // update for end position- selector_schema->update_pstate(pstate);-- // return parsed result- return selector_schema;- }- // EO parse_selector_schema-- void Parser::parse_charset_directive()- {- lex <- sequence <- quoted_string,- optional_spaces,- exactly <';'>- >- >();- }-- // called after parsing `kwd_include_directive`- Mixin_Call* Parser::parse_include_directive()- {- // lex identifier into `lexed` var- lex_identifier(); // may error out- // normalize underscores to hyphens- std::string name(Util::normalize_underscores(lexed));- // create the initial mixin call object- Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call, pstate, name, 0, 0);- // parse mandatory arguments- call->arguments(parse_arguments());- // parse optional block- if (peek < exactly <'{'> >()) {- call->block(parse_block());- }- // return ast node- return call;- }- // EO parse_include_directive-- // parse a list of complex selectors- // this is the main entry point for most- Selector_List* Parser::parse_selector_list(bool in_root)- {- bool reloop = true;- bool had_linefeed = false;- Complex_Selector* sel = 0;- To_String to_string(&ctx);- Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate);- group->media_block(last_media_block);-- do {- reloop = false;-- had_linefeed = had_linefeed || peek_newline();-- if (peek_css< class_char < selector_list_delims > >())- break; // in case there are superfluous commas at the end--- // now parse the complex selector- sel = parse_complex_selector(in_root);-- if (!sel) return group;-- sel->has_line_feed(had_linefeed);-- had_linefeed = false;-- while (peek_css< exactly<','> >())- {- lex< css_comments >(false);- // consume everything up and including the comma speparator- reloop = lex< exactly<','> >() != 0;- // remember line break (also between some commas)- had_linefeed = had_linefeed || peek_newline();- // remember line break (also between some commas)- }- (*group) << sel;- }- while (reloop);- while (lex_css< kwd_optional >()) {- group->is_optional(true);- }- // update for end position- group->update_pstate(pstate);- if (sel) sel->last()->has_line_break(false);- return group;- }- // EO parse_selector_list-- // a complex selector combines a compound selector with another- // complex selector, with one of four combinator operations.- // the compound selector (head) is optional, since the combinator- // can come first in the whole selector sequence (like `> DIV').- Complex_Selector* Parser::parse_complex_selector(bool in_root)- {-- String* reference = 0;- lex < block_comment >();- // parse the left hand side- Compound_Selector* lhs = 0;- // special case if it starts with combinator ([+~>])- if (!peek_css< class_char < selector_combinator_ops > >()) {- // parse the left hand side- lhs = parse_compound_selector();- }-- // check for end of file condition- if (peek < end_of_file >()) return 0;-- // parse combinator between lhs and rhs- Complex_Selector::Combinator combinator;- if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO;- else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES;- else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF;- else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) {- // comments are allowed, but not spaces?- combinator = Complex_Selector::REFERENCE;- if (!lex < re_reference_combinator >()) return 0;- reference = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?- }- else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF;-- if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;-- // lex < block_comment >();- // source position of a complex selector points to the combinator- // ToDo: make sure we update pstate for ancestor of (lex < zero >());- Complex_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, combinator, lhs);- sel->media_block(last_media_block);-- if (combinator == Complex_Selector::REFERENCE) sel->reference(reference);- // has linfeed after combinator?- sel->has_line_break(peek_newline());- // sel->has_line_feed(has_line_feed);-- // check if we got the abort condition (ToDo: optimize)- if (!peek_css< class_char < complex_selector_delims > >()) {- // parse next selector in sequence- sel->tail(parse_complex_selector(true));- if (sel->tail()) {- // ToDo: move this logic below into tail setter- if (sel->tail()->has_reference()) sel->has_reference(true);- if (sel->tail()->has_placeholder()) sel->has_placeholder(true);- }- }-- // add a parent selector if we are not in a root- // also skip adding parent ref if we only have refs- if (!sel->has_reference() && !in_at_root && !in_root) {- // create the objects to wrap parent selector reference- Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);- parent->media_block(last_media_block);- Compound_Selector* head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);- head->media_block(last_media_block);- // add simple selector- (*head) << parent;- // selector may not have any head yet- if (!sel->head()) { sel->head(head); }- // otherwise we need to create a new complex selector and set the old one as its tail- else {- sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel);- sel->media_block(last_media_block);- }- // peek for linefeed and remember result on head- // if (peek_newline()) head->has_line_break(true);- }-- // complex selector- return sel;- }- // EO parse_complex_selector-- // parse one compound selector, which is basically- // a list of simple selectors (directly adjancent)- // lex them exactly (without skipping white-space)- Compound_Selector* Parser::parse_compound_selector()- {- // init an empty compound selector wrapper- Compound_Selector* seq = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);- seq->media_block(last_media_block);-- // skip initial white-space- lex< css_whitespace >();-- // parse list- while (true)- {- // remove all block comments (don't skip white-space)- lex< delimited_by< slash_star, star_slash, false > >(false);- // parse functional- if (peek < re_pseudo_selector >())- {- (*seq) << parse_simple_selector();- }- // parse parent selector- else if (lex< exactly<'&'> >(false))- {- // this produces a linefeed!?- seq->has_parent_reference(true);- (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);- }- // parse type selector- else if (lex< re_type_selector >(false))- {- (*seq) << SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);- }- // peek for abort conditions- else if (peek< spaces >()) break;- else if (peek< end_of_file >()) { break; }- else if (peek_css < class_char < selector_combinator_ops > >()) break;- else if (peek_css < class_char < complex_selector_delims > >()) break;- // otherwise parse another simple selector- else {- Simple_Selector* sel = parse_simple_selector();- if (!sel) return 0;- (*seq) << sel;- }- }-- if (seq && !peek_css<exactly<'{'>>()) {- seq->has_line_break(peek_newline());- }-- // EO while true- return seq;-- }- // EO parse_compound_selector-- Simple_Selector* Parser::parse_simple_selector()- {- lex < css_comments >(false);- if (lex< alternatives < id_name, class_name > >()) {- return SASS_MEMORY_NEW(ctx.mem, Selector_Qualifier, pstate, lexed);- }- else if (lex< quoted_string >()) {- return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, unquote(lexed));- }- else if (lex< alternatives < variable, number, static_reference_combinator > >()) {- return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);- }- else if (peek< pseudo_not >()) {- return parse_negated_selector();- }- else if (peek< re_pseudo_selector >()) {- return parse_pseudo_selector();- }- else if (peek< exactly<':'> >()) {- return parse_pseudo_selector();- }- else if (lex < exactly<'['> >()) {- return parse_attribute_selector();- }- else if (lex< placeholder >()) {- Selector_Placeholder* sel = SASS_MEMORY_NEW(ctx.mem, Selector_Placeholder, pstate, lexed);- sel->media_block(last_media_block);- return sel;- }- // failed- return 0;- }-- Wrapped_Selector* Parser::parse_negated_selector()- {- lex< pseudo_not >();- std::string name(lexed);- ParserState nsource_position = pstate;- Selector* negated = parse_selector_list(true);- if (!lex< exactly<')'> >()) {- error("negated selector is missing ')'", pstate);- }- name.erase(name.size() - 1);- return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, nsource_position, name, negated);- }-- // a pseudo selector often starts with one or two colons- // it can contain more selectors inside parantheses- Simple_Selector* Parser::parse_pseudo_selector() {- if (lex< sequence<- optional < pseudo_prefix >,- // we keep the space within the name, strange enough- // ToDo: refactor output to schedule the space for it- // or do we really want to keep the real white-space?- sequence< identifier, optional < block_comment >, exactly<'('> >- > >())- {-- std::string name(lexed);- name.erase(name.size() - 1);- ParserState p = pstate;-- // specially parse static stuff- // ToDo: really everything static?- if (peek_css <- sequence <- alternatives <- static_value,- binomial- >,- optional_css_whitespace,- exactly<')'>- >- >()- ) {- lex_css< alternatives < static_value, binomial > >();- String_Constant* expr = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- if (expr && lex_css< exactly<')'> >()) {- expr->can_compress_whitespace(true);- return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, p, name, expr);- }- }- else if (Selector* wrapped = parse_selector_list(true)) {- if (wrapped && lex_css< exactly<')'> >()) {- return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, p, name, wrapped);- }- }-- }- // EO if pseudo selector-- else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {- return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, pstate, lexed);- }- else if(lex < pseudo_prefix >()) {- css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");- }-- css_error("Invalid CSS", " after ", ": expected \")\", was ");-- // unreachable statement- return 0;- }-- Attribute_Selector* Parser::parse_attribute_selector()- {- ParserState p = pstate;- if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);- std::string name(lexed);- if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, "", 0);- if (!lex_css< alternatives< exact_match, class_match, dash_match,- prefix_match, suffix_match, substring_match > >()) {- error("invalid operator in attribute selector for " + name, pstate);- }- std::string matcher(lexed);-- String* value = 0;- if (lex_css< identifier >()) {- value = SASS_MEMORY_NEW(ctx.mem, String_Constant, p, lexed);- }- else if (lex_css< quoted_string >()) {- value = parse_interpolated_chunk(lexed, true); // needed!- }- else {- error("expected a string constant or identifier in attribute selector for " + name, pstate);- }-- if (!lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate);- return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, matcher, value);- }-- /* parse block comment and add to block */- void Parser::parse_block_comments()- {- Block* block = block_stack.back();- while (lex< block_comment >()) {- bool is_important = lexed.begin[2] == '!';- String* contents = parse_interpolated_chunk(lexed);- (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);- }- }-- Declaration* Parser::parse_declaration() {- String* prop = 0;- if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {- prop = parse_identifier_schema();- }- else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {- prop = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- prop->is_delayed(true);- }- else {- css_error("Invalid CSS", " after ", ": expected \"}\", was ");- }- bool is_indented = true;- const std::string property(lexed);- if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);- lex < css_comments >(false);- if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);- if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty- if (peek_css< static_value >()) {- return SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/);- }- else {- Expression* value;- Lookahead lookahead = lookahead_for_value(position);- if (lookahead.found) {- if (lookahead.has_interpolants) {- value = parse_value_schema(lookahead.found);- } else {- value = parse_list();- }- }- else {- value = parse_list();- if (List* list = dynamic_cast<List*>(value)) {- if (list->length() == 0 && !peek< exactly <'{'> >()) {- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- }- }- lex < css_comments >(false);- auto decl = SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/);- decl->is_indented(is_indented);- return decl;- }- }-- // parse +/- and return false if negative- bool Parser::parse_number_prefix()- {- bool positive = true;- while(true) {- if (lex < block_comment >()) continue;- if (lex < number_prefix >()) continue;- if (lex < exactly < '-' > >()) {- positive = !positive;- continue;- }- break;- }- return positive;- }-- Expression* Parser::parse_map()- {- Expression* key = parse_list();- Map* map = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);- if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {- if (!str->quote_mark() && !str->is_delayed()) {- if (const Color* col = name_to_color(str->value())) {- Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);- c->pstate(str->pstate());- c->disp(str->value());- key = c;- }- }- }-- // it's not a map so return the lexed value as a list value- if (!peek< exactly<':'> >())- { return key; }-- lex< exactly<':'> >();-- Expression* value = parse_space_list();-- (*map) << std::make_pair(key, value);-- while (lex_css< exactly<','> >())- {- // allow trailing commas - #495- if (peek_css< exactly<')'> >(position))- { break; }-- Expression* key = parse_list();- if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {- if (!str->quote_mark() && !str->is_delayed()) {- if (const Color* col = name_to_color(str->value())) {- Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);- c->pstate(str->pstate());- c->disp(str->value());- key = c;- }- }- }-- if (!(lex< exactly<':'> >()))- { error("invalid syntax", pstate); }-- Expression* value = parse_space_list();-- (*map) << std::make_pair(key, value);- }-- ParserState ps = map->pstate();- ps.offset = pstate - ps + pstate.offset;- map->pstate(ps);-- return map;- }-- // parse list returns either a space separated list,- // a comma separated list or any bare expression found.- // so to speak: we unwrap items from lists if possible here!- Expression* Parser::parse_list()- {- // parse list is relly just an alias- return parse_comma_list();- }-- // will return singletons unwrapped- Expression* Parser::parse_comma_list()- {- // check if we have an empty list- // return the empty list as such- if (peek_css< alternatives <- // exactly<'!'>,- exactly<';'>,- exactly<'}'>,- exactly<'{'>,- exactly<')'>,- exactly<':'>,- exactly<ellipsis>,- default_flag,- global_flag- > >(position))- { return SASS_MEMORY_NEW(ctx.mem, List, pstate, 0); }-- // now try to parse a space list- Expression* list = parse_space_list();- // if it's a singleton, return it (don't wrap it)- if (!peek_css< exactly<','> >(position)) return list;-- // if we got so far, we actually do have a comma list- List* comma_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_COMMA);- // wrap the first expression- (*comma_list) << list;-- while (lex_css< exactly<','> >())- {- // check for abort condition- if (peek_css< alternatives <- exactly<';'>,- exactly<'}'>,- exactly<'{'>,- exactly<')'>,- exactly<':'>,- exactly<ellipsis>,- default_flag,- global_flag- > >(position)- ) { break; }- // otherwise add another expression- (*comma_list) << parse_space_list();- }- // return the list- return comma_list;- }- // EO parse_comma_list-- // will return singletons unwrapped- Expression* Parser::parse_space_list()- {- Expression* disj1 = parse_disjunction();- // if it's a singleton, return it (don't wrap it)- if (peek_css< alternatives <- // exactly<'!'>,- exactly<';'>,- exactly<'}'>,- exactly<'{'>,- exactly<')'>,- exactly<','>,- exactly<':'>,- exactly<ellipsis>,- default_flag,- global_flag- > >(position)- ) { return disj1; }-- List* space_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_SPACE);- (*space_list) << disj1;-- while (!(peek_css< alternatives <- // exactly<'!'>,- exactly<';'>,- exactly<'}'>,- exactly<'{'>,- exactly<')'>,- exactly<','>,- exactly<':'>,- exactly<ellipsis>,- default_flag,- global_flag- > >(position)) && peek_css< optional_css_whitespace >() != end- ) {- // the space is parsed implicitly?- (*space_list) << parse_disjunction();- }- // return the list- return space_list;- }- // EO parse_space_list-- // parse logical OR operation- Expression* Parser::parse_disjunction()- {- // parse the left hand side conjunction- Expression* conj = parse_conjunction();- // parse multiple right hand sides- std::vector<Expression*> operands;- while (lex_css< kwd_or >())- operands.push_back(parse_conjunction());- // if it's a singleton, return it directly- if (operands.size() == 0) return conj;- // fold all operands into one binary expression- return fold_operands(conj, operands, Sass_OP::OR);- }- // EO parse_disjunction-- // parse logical AND operation- Expression* Parser::parse_conjunction()- {- // parse the left hand side relation- Expression* rel = parse_relation();- // parse multiple right hand sides- std::vector<Expression*> operands;- while (lex_css< kwd_and >())- operands.push_back(parse_relation());- // if it's a singleton, return it directly- if (operands.size() == 0) return rel;- // fold all operands into one binary expression- return fold_operands(rel, operands, Sass_OP::AND);- }- // EO parse_conjunction-- // parse comparison operations- Expression* Parser::parse_relation()- {- // parse the left hand side expression- Expression* lhs = parse_expression();- // if it's a singleton, return it (don't wrap it)- if (!(peek< alternatives <- kwd_eq,- kwd_neq,- kwd_gte,- kwd_gt,- kwd_lte,- kwd_lt- > >(position)))- { return lhs; }- // parse the operator- enum Sass_OP op- = lex<kwd_eq>() ? Sass_OP::EQ- : lex<kwd_neq>() ? Sass_OP::NEQ- : lex<kwd_gte>() ? Sass_OP::GTE- : lex<kwd_lte>() ? Sass_OP::LTE- : lex<kwd_gt>() ? Sass_OP::GT- : lex<kwd_lt>() ? Sass_OP::LT- // we checked the possibilites on top of fn- : Sass_OP::EQ;- // parse the right hand side expression- Expression* rhs = parse_expression();- // return binary expression with a left and a right hand side- return SASS_MEMORY_NEW(ctx.mem, Binary_Expression, lhs->pstate(), op, lhs, rhs);- }- // parse_relation-- // parse expression valid for operations- // called from parse_relation- // called from parse_for_directive- // called from parse_media_expression- // parse addition and subtraction operations- Expression* Parser::parse_expression()- {- Expression* lhs = parse_operators();- // if it's a singleton, return it (don't wrap it)- if (!(peek< exactly<'+'> >(position) ||- // condition is a bit misterious, but some combinations should not be counted as operations- (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||- (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||- peek< identifier >(position))- { return lhs; }-- std::vector<Expression*> operands;- std::vector<Sass_OP> operators;- while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {- operators.push_back(lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB);- operands.push_back(parse_operators());- }-- if (operands.size() == 0) return lhs;- return fold_operands(lhs, operands, operators);- }-- // parse addition and subtraction operations- Expression* Parser::parse_operators()- {- Expression* factor = parse_factor();- // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant- if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) {- String_Schema* ss = dynamic_cast<String_Schema*>(factor);- if (ss && ss->has_interpolants()) return factor;- }- // if it's a singleton, return it (don't wrap it)- if (!peek_css< class_char< static_ops > >()) return factor;- // parse more factors and operators- std::vector<Expression*> operands; // factors- std::vector<enum Sass_OP> operators; // ops- // lex operations to apply to lhs- while (lex_css< class_char< static_ops > >()) {- switch(*lexed.begin) {- case '*': operators.push_back(Sass_OP::MUL); break;- case '/': operators.push_back(Sass_OP::DIV); break;- case '%': operators.push_back(Sass_OP::MOD); break;- default: throw std::runtime_error("unknown static op parsed"); break;- }- operands.push_back(parse_factor());- }- // operands and operators to binary expression- return fold_operands(factor, operands, operators);- }- // EO parse_operators--- // called from parse_operators- // called from parse_value_schema- Expression* Parser::parse_factor()- {- lex < css_comments >(false);- if (lex_css< exactly<'('> >()) {- // parse_map may return a list- Expression* value = parse_map();- // lex the expected closing parenthesis- if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);- // expression can be evaluated- value->is_delayed(false);- // make sure wrapped lists and division expressions are non-delayed within parentheses- if (value->concrete_type() == Expression::LIST) {- List* l = static_cast<List*>(value);- if (!l->empty()) (*l)[0]->is_delayed(false);- } else if (typeid(*value) == typeid(Binary_Expression)) {- Binary_Expression* b = static_cast<Binary_Expression*>(value);- Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());- if (lhs && lhs->type() == Sass_OP::DIV) lhs->is_delayed(false);- }- return value;- }- else if (peek< ie_property >()) {- return parse_ie_property();- }- else if (peek< ie_keyword_arg >()) {- return parse_ie_keyword_arg();- }- else if (peek< exactly< calc_kwd > >() ||- peek< exactly< moz_calc_kwd > >() ||- peek< exactly< ms_calc_kwd > >() ||- peek< exactly< webkit_calc_kwd > >()) {- return parse_calc_function();- }- else if (lex < functional_schema >()) {- return parse_function_call_schema();- }- else if (lex< identifier_schema >()) {- String* string = parse_identifier_schema();- if (String_Schema* schema = dynamic_cast<String_Schema*>(string)) {- if (lex < exactly < '(' > >()) {- *schema << parse_list();- lex < exactly < ')' > >();- }- }- return string;- }- else if (peek< sequence< uri_prefix, W, real_uri_value > >()) {- return parse_url_function_string();- }- else if (peek< re_functional >()) {- return parse_function_call();- }- else if (lex< exactly<'+'> >()) {- return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());- }- else if (lex< exactly<'-'> >()) {- return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());- }- else if (lex< sequence< kwd_not > >()) {- return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());- }- else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {- if (parse_number_prefix()) return parse_value(); // prefix is positive- return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());- }- else {- return parse_value();- }- }-- // parse one value for a list- Expression* Parser::parse_value()- {- lex< css_comments >(false);- if (lex< ampersand >())- {- return SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); }-- if (lex< kwd_important >())- { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); }-- if (const char* stop = peek< value_schema >())- { return parse_value_schema(stop); }-- // string may be interpolated- if (lex< quoted_string >())- { return parse_string(); }-- if (lex< kwd_true >())- { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); }-- if (lex< kwd_false >())- { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false); }-- if (lex< kwd_null >())- { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); }-- if (lex< identifier >()) {- return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- }-- if (lex< percentage >())- { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); }-- // match hex number first because 0x000 looks like a number followed by an indentifier- if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >())- { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); }-- if (lex< sequence < exactly <'#'>, identifier > >())- { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); }-- // also handle the 10em- foo special case- if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())- { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); }-- if (lex< sequence< static_component, one_plus< strict_identifier > > >())- { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }-- if (lex< number >())- { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); }-- if (lex< variable >())- { return SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed)); }-- // Special case handling for `%` proceeding an interpolant.- if (lex< sequence< exactly<'%'>, optional< percentage > > >())- { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }-- error("error reading values after " + lexed.to_string(), pstate);-- // unreachable statement- return 0;- }-- // this parses interpolation inside other strings- // means the result should later be quoted again- String* Parser::parse_interpolated_chunk(Token chunk, bool constant)- {- const char* i = chunk.begin;- // see if there any interpolants- const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);- if (!p) {- String_Quoted* str_quoted = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(i, chunk.end));- if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');- str_quoted->is_delayed(true);- return str_quoted;- }-- String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);- while (i < chunk.end) {- p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);- if (p) {- if (i < p) {- // accumulate the preceding segment if it's nonempty- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));- }- // we need to skip anything inside strings- // create a new target in parser/prelexer- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace- if (j) { --j;- // parse the interpolant and accumulate it- Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();- interp_node->is_interpolant(true);- (*schema) << interp_node;- i = j;- }- else {- // throw an error if the interpolant is unterminated- error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);- }- }- else { // no interpolants left; add the last segment if nonempty- // check if we need quotes here (was not sure after merge)- if (i < chunk.end) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, chunk.end));- break;- }- ++ i;- }-- return schema;- }-- String_Constant* Parser::parse_static_expression()- {- if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {- return parse_static_value();- }- return 0;- }-- String_Constant* Parser::parse_static_value()- {- lex< static_value >();- Token str(lexed);- --str.end;- --position;-- String_Constant* str_node = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, str.time_wspace());- str_node->is_delayed(true);- return str_node;- }-- String* Parser::parse_string()- {- return parse_interpolated_chunk(Token(lexed));- }-- String* Parser::parse_ie_property()- {- lex< ie_property >();- Token str(lexed);- const char* i = str.begin;- // see if there any interpolants- const char* p = find_first_in_interval< exactly<hash_lbrace> >(str.begin, str.end);- if (!p) {- return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end));- }-- String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);- while (i < str.end) {- p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);- if (p) {- if (i < p) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty- }- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace- if (j) {- // parse the interpolant and accumulate it- Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();- interp_node->is_interpolant(true);- (*schema) << interp_node;- i = j;- }- else {- // throw an error if the interpolant is unterminated- error("unterminated interpolant inside IE function " + str.to_string(), pstate);- }- }- else { // no interpolants left; add the last segment if nonempty- if (i < str.end) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, str.end));- }- break;- }- }- return schema;- }-- String* Parser::parse_ie_keyword_arg()- {- String_Schema* kwd_arg = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate, 3);- if (lex< variable >()) {- *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed));- } else {- lex< alternatives< identifier_schema, identifier > >();- *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- }- lex< exactly<'='> >();- *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- if (peek< variable >()) *kwd_arg << parse_list();- else if (lex< number >()) *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, Util::normalize_decimals(lexed));- else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }- return kwd_arg;- }-- String_Schema* Parser::parse_value_schema(const char* stop)- {- // initialize the string schema object to add tokens- String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);-- if (peek<exactly<'}'>>()) {- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }-- const char* e = 0;- size_t num_items = 0;- while (position < stop) {- // parse space between tokens- if (lex< spaces >() && num_items) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");- }- if ((e = peek< re_functional >()) && e < stop) {- (*schema) << parse_function_call();- }- // lex an interpolant /#{...}/- else if (lex< exactly < hash_lbrace > >()) {- // Try to lex static expression first- if (peek< exactly< rbrace > >()) {- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- if (lex< re_static_expression >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- } else {- (*schema) << parse_list();- }- // ToDo: no error check here?- lex < exactly < rbrace > >();- }- // lex some string constants- else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);- if (*position == '"' || *position == '\'') {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");- }- }- // lex a quoted string- else if (lex< quoted_string >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed, '"');- if (*position == '"' || *position == '\'' || alpha(position)) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");- }- }- // lex (normalized) variable- else if (lex< variable >()) {- std::string name(Util::normalize_underscores(lexed));- (*schema) << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, name);- }- // lex percentage value- else if (lex< percentage >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed);- }- // lex dimension value- else if (lex< dimension >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed);- }- // lex number value- else if (lex< number >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed);- }- // lex hex color value- else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed);- }- else if (lex< sequence < exactly <'#'>, identifier > >()) {- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed);- }- // lex a value in parentheses- else if (peek< parenthese_scope >()) {- (*schema) << parse_factor();- }- else {- return schema;- }- ++num_items;- }- return schema;- }-- // this parses interpolation outside other strings- // means the result must not be quoted again later- String* Parser::parse_identifier_schema()- {- Token id(lexed);- const char* i = id.begin;- // see if there any interpolants- const char* p = find_first_in_interval< exactly<hash_lbrace> >(id.begin, id.end);- if (!p) {- return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end));- }-- String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);- while (i < id.end) {- p = find_first_in_interval< exactly<hash_lbrace> >(i, id.end);- if (p) {- if (i < p) {- // accumulate the preceding segment if it's nonempty- const char* o = position; position = i;- *schema << parse_value_schema(p);- position = o;- }- // we need to skip anything inside strings- // create a new target in parser/prelexer- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace- if (j) {- // parse the interpolant and accumulate it- Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();- interp_node->is_interpolant(true);- (*schema) << interp_node;- schema->has_interpolants(true);- i = j;- }- else {- // throw an error if the interpolant is unterminated- error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);- }- }- else { // no interpolants left; add the last segment if nonempty- if (i < end) {- const char* o = position; position = i;- *schema << parse_value_schema(id.end);- position = o;- }- break;- }- }- return schema;- }-- // calc functions should preserve arguments- Function_Call* Parser::parse_calc_function()- {- lex< identifier >();- std::string name(lexed);- ParserState call_pos = pstate;- lex< exactly<'('> >();- ParserState arg_pos = pstate;- const char* arg_beg = position;- parse_list();- const char* arg_end = position;- lex< skip_over_scopes <- exactly < '(' >,- exactly < ')' >- > >();-- Argument* arg = SASS_MEMORY_NEW(ctx.mem, Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));- Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, arg_pos);- *args << arg;- return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);- }-- String* Parser::parse_url_function_string()- {- const char* p = position;-- lex< uri_prefix >();- std::string prefix = lexed;-- lex< real_uri_value >(false);- std::string uri = lexed;-- if (peek< exactly< hash_lbrace > >()) {- const char* pp = position;- // TODO: error checking for unclosed interpolants- while (peek< exactly< hash_lbrace > >(pp)) {- pp = sequence< interpolant, real_uri_value >(pp);- }- position = peek< real_uri_suffix >(pp);- return parse_interpolated_chunk(Token(p, position));- } else {- lex< real_uri_suffix >();- std::string res = prefix + Util::rtrim(uri) + lexed.to_string();- return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res);- }-- }-- Function_Call* Parser::parse_function_call()- {- lex< identifier >();- std::string name(lexed);-- ParserState call_pos = pstate;- Arguments* args = parse_arguments();- return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);- }-- Function_Call_Schema* Parser::parse_function_call_schema()- {- String* name = parse_identifier_schema();- ParserState source_position_of_call = pstate;-- Function_Call_Schema* the_call = SASS_MEMORY_NEW(ctx.mem, Function_Call_Schema, source_position_of_call, name, parse_arguments());- return the_call;- }-- Content* Parser::parse_content_directive()- {- if (stack.back() != mixin_def) {- error("@content may only be used within a mixin", pstate);- }- return SASS_MEMORY_NEW(ctx.mem, Content, pstate);- }-- If* Parser::parse_if_directive(bool else_if)- {- ParserState if_source_position = pstate;- Expression* predicate = parse_list();- predicate->is_delayed(false);- Block* block = parse_block();- Block* alternative = 0;-- // only throw away comment if we parse a case- // we want all other comments to be parsed- if (lex_css< elseif_directive >()) {- alternative = SASS_MEMORY_NEW(ctx.mem, Block, pstate);- (*alternative) << parse_if_directive(true);- }- else if (lex_css< kwd_else_directive >()) {- alternative = parse_block();- }- return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative);- }-- For* Parser::parse_for_directive()- {- ParserState for_source_position = pstate;- lex_variable();- std::string var(Util::normalize_underscores(lexed));- if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);- Expression* lower_bound = parse_expression();- lower_bound->is_delayed(false);- bool inclusive = false;- if (lex< kwd_through >()) inclusive = true;- else if (lex< kwd_to >()) inclusive = false;- else error("expected 'through' or 'to' keyword in @for directive", pstate);- Expression* upper_bound = parse_expression();- upper_bound->is_delayed(false);- Block* body = parse_block();- return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive);- }-- // helper to parse a var token- Token Parser::lex_variable()- {- // peek for dollar sign first- if (!peek< exactly <'$'> >()) {- css_error("Invalid CSS", " after ", ": expected \"$\", was ");- }- // we expect a simple identfier as the call name- if (!lex< sequence < exactly <'$'>, identifier > >()) {- lex< exactly <'$'> >(); // move pstate and position up- css_error("Invalid CSS", " after ", ": expected identifier, was ");- }- // return object- return token;- }- // helper to parse identifier- Token Parser::lex_identifier()- {- // we expect a simple identfier as the call name- if (!lex< identifier >()) { // ToDo: pstate wrong?- css_error("Invalid CSS", " after ", ": expected identifier, was ");- }- // return object- return token;- }-- Each* Parser::parse_each_directive()- {- ParserState each_source_position = pstate;- std::vector<std::string> vars;- lex_variable();- vars.push_back(Util::normalize_underscores(lexed));- while (lex< exactly<','> >()) {- if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);- vars.push_back(Util::normalize_underscores(lexed));- }- if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);- Expression* list = parse_list();- list->is_delayed(false);- if (list->concrete_type() == Expression::LIST) {- List* l = static_cast<List*>(list);- for (size_t i = 0, L = l->length(); i < L; ++i) {- (*l)[i]->is_delayed(false);- }- }- Block* body = parse_block();- return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body);- }-- // called after parsing `kwd_while_directive`- While* Parser::parse_while_directive()- {- // create the initial while call object- While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0);- // parse mandatory predicate- Expression* predicate = parse_list();- predicate->is_delayed(false);- call->predicate(predicate);- // parse mandatory block- call->block(parse_block());- // return ast node- return call;- }-- // EO parse_while_directive- Media_Block* Parser::parse_media_block()- {- Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0);- media_block->media_queries(parse_media_queries());-- Media_Block* prev_media_block = last_media_block;- last_media_block = media_block;- media_block->block(parse_css_block());- last_media_block = prev_media_block;-- return media_block;- }-- List* Parser::parse_media_queries()- {- List* media_queries = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_COMMA);- if (!peek_css < exactly <'{'> >()) (*media_queries) << parse_media_query();- while (lex_css < exactly <','> >()) (*media_queries) << parse_media_query();- return media_queries;- }-- // Expression* Parser::parse_media_query()- Media_Query* Parser::parse_media_query()- {- Media_Query* media_query = SASS_MEMORY_NEW(ctx.mem, Media_Query, pstate);-- lex < css_comments >(false);- if (lex < kwd_not >()) media_query->is_negated(true);- else if (lex < kwd_only >()) media_query->is_restricted(true);-- lex < css_comments >(false);- if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema());- else if (lex < identifier >()) media_query->media_type(parse_interpolated_chunk(lexed));- else (*media_query) << parse_media_expression();-- while (lex_css < kwd_and >()) (*media_query) << parse_media_expression();- if (lex < identifier_schema >()) {- String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);- *schema << media_query->media_type();- *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");- *schema << parse_identifier_schema();- media_query->media_type(schema);- }- while (lex_css < kwd_and >()) (*media_query) << parse_media_expression();- return media_query;- }-- Media_Query_Expression* Parser::parse_media_expression()- {- if (lex < identifier_schema >()) {- String* ss = parse_identifier_schema();- return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, pstate, ss, 0, true);- }- if (!lex_css< exactly<'('> >()) {- error("media query expression must begin with '('", pstate);- }- Expression* feature = 0;- if (peek_css< exactly<')'> >()) {- error("media feature required in media query expression", pstate);- }- feature = parse_expression();- Expression* expression = 0;- if (lex< exactly<':'> >()) {- expression = parse_list();- }- if (!lex< exactly<')'> >()) {- error("unclosed parenthesis in media query expression", pstate);- }- return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression);- }-- // lexed after `kwd_supports_directive`- // these are very similar to media blocks- Supports_Block* Parser::parse_supports_directive()- {- Supports_Condition* cond = parse_supports_condition();- // create the ast node object for the support queries- Supports_Block* query = SASS_MEMORY_NEW(ctx.mem, Supports_Block, pstate, cond);- // additional block is mandatory- // parse inner block- query->block(parse_block());- // return ast node- return query;- }-- // parse one query operation- // may encounter nested queries- Supports_Condition* Parser::parse_supports_condition()- {- lex < css_whitespace >();- Supports_Condition* cond = parse_supports_negation();- if (!cond) cond = parse_supports_operator();- if (!cond) cond = parse_supports_interpolation();- return cond;- }-- Supports_Condition* Parser::parse_supports_negation()- {- if (!lex < kwd_not >()) return 0;-- Supports_Condition* cond = parse_supports_condition_in_parens();- return SASS_MEMORY_NEW(ctx.mem, Supports_Negation, pstate, cond);- }-- Supports_Condition* Parser::parse_supports_operator()- {- Supports_Condition* cond = parse_supports_condition_in_parens();- if (!cond) return 0;-- while (true) {- Supports_Operator::Operand op = Supports_Operator::OR;- if (lex < kwd_and >()) { op = Supports_Operator::AND; }- else if(!lex < kwd_or >()) { break; }-- lex < css_whitespace >();- Supports_Condition* right = parse_supports_condition_in_parens();-- // Supports_Condition* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Condition, *static_cast<Supports_Condition*>(cond));- cond = SASS_MEMORY_NEW(ctx.mem, Supports_Operator, pstate, cond, right, op);- }- return cond;- }-- Supports_Condition* Parser::parse_supports_interpolation()- {- if (!lex < interpolant >()) return 0;-- String* interp = parse_interpolated_chunk(lexed);- if (!interp) return 0;-- return SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation, pstate, interp);- }-- // TODO: This needs some major work. Although feature conditions- // look like declarations their semantics differ siginificantly- Supports_Condition* Parser::parse_supports_declaration()- {- Supports_Condition* cond = 0;- // parse something declaration like- Declaration* declaration = parse_declaration();- if (!declaration) error("@supports condition expected declaration", pstate);- cond = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,- declaration->pstate(),- declaration->property(),- declaration->value());- // ToDo: maybe we need an additional error condition?- return cond;- }-- Supports_Condition* Parser::parse_supports_condition_in_parens()- {- Supports_Condition* interp = parse_supports_interpolation();- if (interp != 0) return interp;-- if (!lex < exactly <'('> >()) return 0;- lex < css_whitespace >();-- Supports_Condition* cond = parse_supports_condition();- if (cond != 0) {- if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);- } else {- cond = parse_supports_declaration();- if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);- }- lex < css_whitespace >();- return cond;- }-- At_Root_Block* Parser::parse_at_root_block()- {- ParserState at_source_position = pstate;- Block* body = 0;- At_Root_Expression* expr = 0;- Lookahead lookahead_result;- LOCAL_FLAG(in_at_root, true);- if (lex< exactly<'('> >()) {- expr = parse_at_root_expression();- }- if (peek < exactly<'{'> >()) {- body = parse_block(true);- }- else if ((lookahead_result = lookahead_for_selector(position)).found) {- Ruleset* r = parse_ruleset(lookahead_result, false);- body = SASS_MEMORY_NEW(ctx.mem, Block, r->pstate(), 1, true);- *body << r;- }- At_Root_Block* at_root = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, at_source_position, body);- if (expr) at_root->expression(expr);- return at_root;- }-- At_Root_Expression* Parser::parse_at_root_expression()- {- if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);-- if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {- css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");- }-- Declaration* declaration = parse_declaration();- List* value = SASS_MEMORY_NEW(ctx.mem, List, declaration->value()->pstate(), 1);-- if (declaration->value()->concrete_type() == Expression::LIST) {- value = static_cast<List*>(declaration->value());- }- else *value << declaration->value();-- At_Root_Expression* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression,- declaration->pstate(),- declaration->property(),- value);- if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);- return cond;- }-- At_Rule* Parser::parse_at_rule()- {- std::string kwd(lexed);-- if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);-- At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd);- Lookahead lookahead = lookahead_for_include(position);- if (lookahead.found && !lookahead.has_interpolants) {- at_rule->selector(parse_selector_list(true));- }-- lex < css_comments >(false);-- if (lex < static_property >()) {- at_rule->value(parse_interpolated_chunk(Token(lexed)));- } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {- at_rule->value(parse_list());- }-- lex < css_comments >(false);-- if (peek< exactly<'{'> >()) {- at_rule->block(parse_block());- }-- return at_rule;- }-- Warning* Parser::parse_warning()- {- return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list());- }-- Error* Parser::parse_error()- {- return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list());- }-- Debug* Parser::parse_debug()- {- return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list());- }-- Return* Parser::parse_return_directive()- {- // check that we do not have an empty list (ToDo: check if we got all cases)- if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >())- { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }- return SASS_MEMORY_NEW(ctx.mem, Return, pstate, parse_list());- }-- Lookahead Parser::lookahead_for_selector(const char* start)- {- // init result struct- Lookahead rv = Lookahead();- // get start position- const char* p = start ? start : position;- // match in one big "regex"- rv.error = p;- if (const char* q =- peek <- one_plus <- alternatives <- // consume whitespace and comments- spaces, block_comment, line_comment,- // match `/deep/` selector (pass-trough)- // there is no functionality for it yet- schema_reference_combinator,- // match selector ops /[*&%,()\[\]]/- class_char < selector_lookahead_ops >,- // match selector combinators /[>+~]/- class_char < selector_combinator_ops >,- // match attribute compare operators- alternatives <- exact_match, class_match, dash_match,- prefix_match, suffix_match, substring_match- >,- // main selector match- sequence <- // allow namespace prefix- optional < namespace_schema >,- // modifiers prefixes- alternatives <- sequence <- exactly <'#'>,- // not for interpolation- negate < exactly <'{'> >- >,- // class match- exactly <'.'>,- // single or double colon- optional < pseudo_prefix >- >,- // accept hypens in token- one_plus < sequence <- // can start with hyphens- zero_plus < exactly<'-'> >,- // now the main token- alternatives <- kwd_optional,- exactly <'*'>,- quoted_string,- interpolant,- identifier,- percentage,- dimension,- variable,- alnum- >- > >,- // can also end with hyphens- zero_plus < exactly<'-'> >- >- >- >- >(p)- ) {- while (p < q) {- // did we have interpolations?- if (*p == '#' && *(p+1) == '{') {- rv.has_interpolants = true;- p = q; break;- }- ++ p;- }- // store anyway }--- // ToDo: remove- rv.error = q;- rv.position = q;- // check expected opening bracket- // only after successfull matching- if (peek < exactly<'{'> >(q)) rv.found = q;- // else if (peek < exactly<';'> >(q)) rv.found = q;- // else if (peek < exactly<'}'> >(q)) rv.found = q;- if (rv.found || *p == 0) rv.error = 0;- }-- rv.parsable = ! rv.has_interpolants;-- // return result- return rv;-- }- // EO lookahead_for_selector-- // used in parse_block_nodes and parse_at_rule- // ToDo: actual usage is still not really clear to me?- Lookahead Parser::lookahead_for_include(const char* start)- {- // we actually just lookahead for a selector- Lookahead rv = lookahead_for_selector(start);- // but the "found" rules are different- if (const char* p = rv.position) {- // check for additional abort condition- if (peek < exactly<';'> >(p)) rv.found = p;- else if (peek < exactly<'}'> >(p)) rv.found = p;- }- // return result- return rv;- }- // EO lookahead_for_include-- // look ahead for a token with interpolation in it- // we mostly use the result if there is an interpolation- // everything that passes here gets parsed as one schema- // meaning it will not be parsed as a space separated list- Lookahead Parser::lookahead_for_value(const char* start)- {- // init result struct- Lookahead rv = Lookahead();- // get start position- const char* p = start ? start : position;- // match in one big "regex"- if (const char* q =- peek <- non_greedy <- alternatives <- // consume whitespace- block_comment, spaces,- // main tokens- interpolant,- identifier,- variable,- // issue #442- sequence <- parenthese_scope,- interpolant- >- >,- sequence <- optional_spaces,- alternatives <- exactly<'{'>,- exactly<'}'>,- exactly<';'>- >- >- >- >(p)- ) {- if (p == q) return rv;- while (p < q) {- // did we have interpolations?- if (*p == '#' && *(p+1) == '{') {- rv.has_interpolants = true;- p = q; break;- }- ++ p;- }- // store anyway- // ToDo: remove- rv.position = q;- // check expected opening bracket- // only after successfull matching- if (peek < exactly<'{'> >(q)) rv.found = q;- else if (peek < exactly<';'> >(q)) rv.found = q;- else if (peek < exactly<'}'> >(q)) rv.found = q;- }-- // return result- return rv;- }- // EO lookahead_for_value-- void Parser::read_bom()- {- size_t skip = 0;- std::string encoding;- bool utf_8 = false;- switch ((unsigned char) source[0]) {- case 0xEF:- skip = check_bom_chars(source, end, utf_8_bom, 3);- encoding = "UTF-8";- utf_8 = true;- break;- case 0xFE:- skip = check_bom_chars(source, end, utf_16_bom_be, 2);- encoding = "UTF-16 (big endian)";- break;- case 0xFF:- skip = check_bom_chars(source, end, utf_16_bom_le, 2);- skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);- encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");- break;- case 0x00:- skip = check_bom_chars(source, end, utf_32_bom_be, 4);- encoding = "UTF-32 (big endian)";- break;- case 0x2B:- skip = check_bom_chars(source, end, utf_7_bom_1, 4)- | check_bom_chars(source, end, utf_7_bom_2, 4)- | check_bom_chars(source, end, utf_7_bom_3, 4)- | check_bom_chars(source, end, utf_7_bom_4, 4)- | check_bom_chars(source, end, utf_7_bom_5, 5);- encoding = "UTF-7";- break;- case 0xF7:- skip = check_bom_chars(source, end, utf_1_bom, 3);- encoding = "UTF-1";- break;- case 0xDD:- skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);- encoding = "UTF-EBCDIC";- break;- case 0x0E:- skip = check_bom_chars(source, end, scsu_bom, 3);- encoding = "SCSU";- break;- case 0xFB:- skip = check_bom_chars(source, end, bocu_1_bom, 3);- encoding = "BOCU-1";- break;- case 0x84:- skip = check_bom_chars(source, end, gb_18030_bom, 4);- encoding = "GB-18030";- break;- }- if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);- position += skip;- }-- size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)- {- size_t skip = 0;- if (src + len > end) return 0;- for (size_t i = 0; i < len; ++i, ++skip) {- if ((unsigned char) src[i] != bom[i]) return 0;- }- return skip;- }--- Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, enum Sass_OP op)- {- for (size_t i = 0, S = operands.size(); i < S; ++i) {- base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]);- Binary_Expression* b = static_cast<Binary_Expression*>(base);- if (op == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {- base->is_delayed(true);- }- else {- b->left()->is_delayed(false);- b->right()->is_delayed(false);- }- }- return base;- }-- Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<enum Sass_OP>& ops)- {- for (size_t i = 0, S = operands.size(); i < S; ++i) {- base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);- Binary_Expression* b = static_cast<Binary_Expression*>(base);- if (ops[i] == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {- base->is_delayed(true);- }- else {- b->left()->is_delayed(false);- b->right()->is_delayed(false);- }- }- return base;- }-- void Parser::error(std::string msg, Position pos)- {- throw Exception::InvalidSass(ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);- }-- // print a css parsing error with actual context information from parsed source- void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle)- {- int max_len = 18;- const char* pos = peek < optional_spaces >();-- const char* last_pos(pos - 1);- // backup position to last significant char- while ((!*last_pos || Prelexer::is_space(*last_pos)) && last_pos > source) -- last_pos;-- bool ellipsis_left = false;- const char* pos_left(last_pos + 1);- const char* end_left(last_pos + 1);- while (pos_left > source) {- if (end_left - pos_left >= max_len) {- ellipsis_left = true;- break;- }-- const char* prev = pos_left - 1;- if (*prev == '\r') break;- if (*prev == '\n') break;- pos_left = prev;- }- if (pos_left < source) {- pos_left = source;- }-- bool ellipsis_right = false;- const char* end_right(pos);- const char* pos_right(pos);- while (end_right <= end) {- if (end_right - pos_right > max_len) {- ellipsis_right = true;- break;- }- if (*end_right == '\r') break;- if (*end_right == '\n') break;- ++ end_right;- }- if (end_right > end) end_right = end;-- std::string left(pos_left, end_left);- std::string right(pos_right, end_right);- if (ellipsis_left) left = ellipsis + left.substr(left.size() - 15);- if (ellipsis_right) right = right.substr(right.size() - 15) + ellipsis;+#include "sass.hpp"+#include <cstdlib>+#include <iostream>+#include <vector>+#include "parser.hpp"+#include "file.hpp"+#include "inspect.hpp"+#include "constants.hpp"+#include "util.hpp"+#include "prelexer.hpp"+#include "color_maps.hpp"+#include "sass/functions.h"+#include "error_handling.hpp"++// Notes about delayed: some ast nodes can have delayed evaluation so+// they can preserve their original semantics if needed. This is most+// prominently exhibited by the division operation, since it is not+// only a valid operation, but also a valid css statement (i.e. for+// fonts, as in `16px/24px`). When parsing lists and expression we+// unwrap single items from lists and other operations. A nested list+// must not be delayed, only the items of the first level sometimes+// are delayed (as with argument lists). To achieve this we need to+// pass status to the list parser, so this can be set correctly.+// Another case with delayed values are colors. In compressed mode+// only processed values get compressed (other are left as written).++#include <typeinfo>+#include <tuple>++namespace Sass {+ using namespace Constants;+ using namespace Prelexer;++ Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source)+ {+ pstate.offset.column = 0;+ pstate.offset.line = 0;+ Parser p(ctx, pstate);+ p.source = source ? source : beg;+ p.position = beg ? beg : p.source;+ p.end = p.position + strlen(p.position);+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source)+ {+ pstate.offset.column = 0;+ pstate.offset.line = 0;+ Parser p(ctx, pstate);+ p.source = source ? source : beg;+ p.position = beg ? beg : p.source;+ p.end = end ? end : p.position + strlen(p.position);+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ void Parser::advanceToNextToken() {+ lex < css_comments >(false);+ // advance to position+ pstate += pstate.offset;+ pstate.offset.column = 0;+ pstate.offset.line = 0;+ }++ Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source)+ {+ Parser p = Parser::from_c_str(beg, ctx, pstate, source);+ // ToDo: ruby sass errors on parent references+ // ToDo: remap the source-map entries somehow+ return p.parse_selector_list(false);+ }++ bool Parser::peek_newline(const char* start)+ {+ return peek_linefeed(start ? start : position)+ && ! peek_css<exactly<'{'>>(start);+ }++ Parser Parser::from_token(Token t, Context& ctx, ParserState pstate, const char* source)+ {+ Parser p(ctx, pstate);+ p.source = source ? source : t.begin;+ p.position = t.begin ? t.begin : p.source;+ p.end = t.end ? t.end : p.position + strlen(p.position);+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ /* main entry point to parse root block */+ Block_Obj Parser::parse()+ {+ bool is_root = false;+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true);+ read_bom();++ // custom headers+ if (ctx.resources.size() == 1) {+ is_root = true;+ ctx.apply_custom_headers(&root, path, pstate);+ }++ block_stack.push_back(root);+ /* bool rv = */ parse_block_nodes(is_root);+ block_stack.pop_back();++ // update for end position+ root->update_pstate(pstate);++ if (position != end) {+ css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");+ }++ return root;+ }+++ // convenience function for block parsing+ // will create a new block ad-hoc for you+ // this is the base block parsing function+ Block_Obj Parser::parse_css_block(bool is_root)+ {++ // parse comments before block+ // lex < optional_css_comments >();++ // lex mandatory opener or error out+ if (!lex_css < exactly<'{'> >()) {+ css_error("Invalid CSS", " after ", ": expected \"{\", was ");+ }+ // create new block and push to the selector stack+ Block_Obj block = SASS_MEMORY_NEW(Block, pstate, 0, is_root);+ block_stack.push_back(block);++ if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");++ if (!lex_css < exactly<'}'> >()) {+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");+ }++ // update for end position+ // this seems to be done somewhere else+ // but that fixed selector schema issue+ // block->update_pstate(pstate);++ // parse comments after block+ // lex < optional_css_comments >();++ block_stack.pop_back();++ return █+ }++ // 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); }
libsass/src/parser.hpp view
@@ -23,12 +23,12 @@ class Parser : public ParserState { public: - enum Syntactic_Context { nothing, mixin_def, function_def };+ enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules }; Context& ctx;- std::vector<Block*> block_stack;- std::vector<Syntactic_Context> stack;- Media_Block* last_media_block;+ std::vector<Block_Obj> block_stack;+ std::vector<Scope> stack;+ Media_Block_Ptr last_media_block; const char* source; const char* position; const char* end;@@ -42,16 +42,16 @@ bool in_at_root; Parser(Context& ctx, const ParserState& pstate)- : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), last_media_block(0),+ : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(), source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0)- { in_at_root = false; stack.push_back(nothing); }+ { in_at_root = false; stack.push_back(Scope::Root); } // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));- static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));- static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));- static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"));+ static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);+ static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);+ static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0); // special static parsers to convert strings into certain selectors- static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"));+ static Selector_List_Obj parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0); #ifdef __clang__ @@ -66,6 +66,10 @@ #endif + // skip current token and next whitespace+ // moves ParserState right before next token+ void advanceToNextToken();+ bool peek_newline(const char* start = 0); // skip over spaces, tabs and line comments@@ -99,6 +103,15 @@ // peek will only skip over space, tabs and line comment // return the position where the lexer match will occur template <Prelexer::prelexer mx>+ const char* match(const char* start = 0)+ {+ // match the given prelexer+ return mx(position);+ }++ // peek will only skip over space, tabs and line comment+ // return the position where the lexer match will occur+ template <Prelexer::prelexer mx> const char* peek(const char* start = 0) { @@ -107,8 +120,11 @@ const char* it_before_token = sneak < mx >(start); // match the given prelexer- return mx(it_before_token);+ const char* match = mx(it_before_token); + // check if match is in valid range+ return match <= end ? match : 0;+ } // white-space handling is built into the lexer@@ -121,6 +137,8 @@ const char* lex(bool lazy = true, bool force = false) { + if (*position == 0) return 0;+ // position considered before lexed token // we can skip whitespace or comments for // lazy developers (but we need control)@@ -133,6 +151,9 @@ // now call matcher to get position after token const char* it_after_token = mx(it_before_token); + // check if match is in valid range+ if (it_after_token > end) return 0;+ // maybe we want to update the parser state anyway? if (force == false) { // assertion that we got a valid match@@ -210,84 +231,89 @@ const std::string& middle = ", was: "); void read_bom(); - Block* parse();- Import* parse_import();- Definition* parse_definition(Definition::Type which_type);- Parameters* parse_parameters();- Parameter* parse_parameter();- Mixin_Call* parse_include_directive();- Arguments* parse_arguments();- Argument* parse_argument();- Assignment* parse_assignment();- // Propset* parse_propset();- Ruleset* parse_ruleset(Lookahead lookahead, bool is_root = false);- Selector_Schema* parse_selector_schema(const char* end_of_selector);- Selector_List* parse_selector_list(bool at_root = false);- Complex_Selector* parse_complex_selector(bool in_root = true);- Compound_Selector* parse_compound_selector();- Simple_Selector* parse_simple_selector();- Wrapped_Selector* parse_negated_selector();- Simple_Selector* parse_pseudo_selector();- Attribute_Selector* parse_attribute_selector();- Block* parse_block(bool is_root = false);- Block* parse_css_block(bool is_root = false);+ Block_Obj parse();+ Import_Obj parse_import();+ Definition_Obj parse_definition(Definition::Type which_type);+ Parameters_Obj parse_parameters();+ Parameter_Obj parse_parameter();+ Mixin_Call_Obj parse_include_directive();+ Arguments_Obj parse_arguments();+ Argument_Obj parse_argument();+ Assignment_Obj parse_assignment();+ Ruleset_Obj parse_ruleset(Lookahead lookahead, bool is_root = false);+ Selector_Schema_Obj parse_selector_schema(const char* end_of_selector);+ Selector_List_Obj parse_selector_list(bool at_root = false);+ Complex_Selector_Obj parse_complex_selector(bool in_root = true);+ Compound_Selector_Obj parse_compound_selector();+ Simple_Selector_Obj parse_simple_selector();+ Wrapped_Selector_Obj parse_negated_selector();+ Simple_Selector_Obj parse_pseudo_selector();+ Attribute_Selector_Obj parse_attribute_selector();+ Block_Obj parse_block(bool is_root = false);+ Block_Obj parse_css_block(bool is_root = false); bool parse_block_nodes(bool is_root = false); bool parse_block_node(bool is_root = false); bool parse_number_prefix();- Declaration* parse_declaration();- Expression* parse_map_value();- Expression* parse_map();- Expression* parse_list();- Expression* parse_comma_list();- Expression* parse_space_list();- Expression* parse_disjunction();- Expression* parse_conjunction();- Expression* parse_relation();- Expression* parse_expression();- Expression* parse_operators();- Expression* parse_factor();- Expression* parse_value2();- Expression* parse_value();- Function_Call* parse_calc_function();- Function_Call* parse_function_call();- Function_Call_Schema* parse_function_call_schema();- String* parse_url_function_string();- String* parse_interpolated_chunk(Token, bool constant = false);- String* parse_string();- String_Constant* parse_static_expression();- // String_Constant* parse_static_property();- String_Constant* parse_static_value();- String* parse_ie_property();- String* parse_ie_keyword_arg();- String_Schema* parse_value_schema(const char* stop);- String* parse_identifier_schema();- // String_Schema* parse_url_schema();- If* parse_if_directive(bool else_if = false);- For* parse_for_directive();- Each* parse_each_directive();- While* parse_while_directive();- Return* parse_return_directive();- Content* parse_content_directive();+ Declaration_Obj parse_declaration();+ Expression_Obj parse_map();+ Expression_Obj parse_list(bool delayed = false);+ Expression_Obj parse_comma_list(bool delayed = false);+ Expression_Obj parse_space_list();+ Expression_Obj parse_disjunction();+ Expression_Obj parse_conjunction();+ Expression_Obj parse_relation();+ Expression_Obj parse_expression();+ Expression_Obj parse_operators();+ Expression_Obj parse_factor();+ Expression_Obj parse_value();+ Function_Call_Obj parse_calc_function();+ Function_Call_Obj parse_function_call();+ Function_Call_Schema_Obj parse_function_call_schema();+ String_Obj parse_url_function_string();+ String_Obj parse_url_function_argument();+ String_Obj parse_interpolated_chunk(Token, bool constant = false);+ String_Obj parse_string();+ String_Constant_Obj parse_static_value();+ String_Obj parse_ie_property();+ String_Obj parse_ie_keyword_arg();+ String_Schema_Obj parse_value_schema(const char* stop);+ String_Obj parse_identifier_schema();+ If_Obj parse_if_directive(bool else_if = false);+ For_Obj parse_for_directive();+ Each_Obj parse_each_directive();+ While_Obj parse_while_directive();+ Return_Obj parse_return_directive();+ Content_Obj parse_content_directive(); void parse_charset_directive();- Media_Block* parse_media_block();- List* parse_media_queries();- Media_Query* parse_media_query();- Media_Query_Expression* parse_media_expression();- Supports_Block* parse_supports_directive();- Supports_Condition* parse_supports_condition();- Supports_Condition* parse_supports_negation();- Supports_Condition* parse_supports_operator();- Supports_Condition* parse_supports_interpolation();- Supports_Condition* parse_supports_declaration();- Supports_Condition* parse_supports_condition_in_parens();- At_Root_Block* parse_at_root_block();- At_Root_Expression* parse_at_root_expression();- At_Rule* parse_at_rule();- Warning* parse_warning();- Error* parse_error();- Debug* parse_debug();+ Media_Block_Obj parse_media_block();+ List_Obj parse_media_queries();+ Media_Query_Obj parse_media_query();+ Media_Query_Expression_Obj parse_media_expression();+ Supports_Block_Obj parse_supports_directive();+ Supports_Condition_Obj parse_supports_condition();+ Supports_Condition_Obj parse_supports_negation();+ Supports_Condition_Obj parse_supports_operator();+ Supports_Condition_Obj parse_supports_interpolation();+ Supports_Condition_Obj parse_supports_declaration();+ Supports_Condition_Obj parse_supports_condition_in_parens();+ At_Root_Block_Obj parse_at_root_block();+ At_Root_Query_Obj parse_at_root_query();+ String_Schema_Obj parse_almost_any_value();+ Directive_Obj parse_special_directive();+ Directive_Obj parse_prefixed_directive();+ Directive_Obj parse_directive();+ Warning_Obj parse_warning();+ Error_Obj parse_error();+ Debug_Obj parse_debug(); + // be more like ruby sass+ Expression_Obj lex_almost_any_value_token();+ Expression_Obj lex_almost_any_value_chars();+ Expression_Obj lex_interp_string();+ Expression_Obj lex_interp_uri();+ Expression_Obj lex_interpolation();+ // these will throw errors Token lex_variable(); Token lex_identifier();@@ -298,11 +324,39 @@ Lookahead lookahead_for_selector(const char* start = 0); Lookahead lookahead_for_include(const char* start = 0); - Expression* fold_operands(Expression* base, std::vector<Expression*>& operands, Sass_OP op);- Expression* fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<Sass_OP>& ops);+ Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, Operand op);+ Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, std::vector<Operand>& ops, size_t i = 0); void throw_syntax_error(std::string message, size_t ln = 0); void throw_read_error(std::string message, size_t ln = 0);+++ template <Prelexer::prelexer open, Prelexer::prelexer close>+ Expression_Obj lex_interp()+ {+ if (lex < open >(false)) {+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);+ // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+ if (position[0] == '#' && position[1] == '{') {+ Expression_Obj itpl = lex_interpolation();+ if (&itpl) schema->append(&itpl);+ while (lex < close >(false)) {+ // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));+ if (position[0] == '#' && position[1] == '{') {+ Expression_Obj itpl = lex_interpolation();+ if (&itpl) schema->append(&itpl);+ } else {+ return &schema;+ }+ }+ } else {+ return SASS_MEMORY_NEW(String_Constant, pstate, lexed);+ }+ }+ return 0;+ } }; size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
libsass/src/plugins.cpp view
@@ -7,6 +7,7 @@ #include <dlfcn.h> #endif +#include "sass.hpp" #include <iostream> #include "output.hpp" #include "plugins.hpp"
libsass/src/position.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "position.hpp" namespace Sass {
libsass/src/prelexer.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cctype> #include <cstddef> #include <iostream>@@ -15,6 +16,251 @@ namespace Prelexer { + /*++ def string_re(open, close)+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m+ end+ end++ # A hash of regular expressions that are used for tokenizing strings.+ #+ # The key is a `[Symbol, Boolean]` pair.+ # The symbol represents which style of quotation to use,+ # while the boolean represents whether or not the string+ # is following an interpolated segment.+ STRING_REGULAR_EXPRESSIONS = {+ :double => {+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m+ false => string_re('"', '"'),+ true => string_re('', '"')+ },+ :single => {+ false => string_re("'", "'"),+ true => string_re('', "'")+ },+ :uri => {+ false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+ },+ # Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a+ # non-standard version of http://www.w3.org/TR/css3-conditional/+ :url_prefix => {+ false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+ },+ :domain => {+ false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+ }+ }+ */++ /*+ /#{open}+ (+ \\.+ |+ \# (?!\{)+ |+ [^#{close}\\#]+ )*+ (#{close}|#\{)+ /m+ false => string_re('"', '"'),+ true => string_re('', '"')+ */+ extern const char string_double_negates[] = "\"\\#";+ const char* re_string_double_close(const char* src)+ {+ return sequence <+ // valid chars+ zero_plus <+ alternatives <+ // escaped char+ sequence <+ exactly <'\\'>,+ any_char+ >,+ // non interpolate hash+ sequence <+ exactly <'#'>,+ negate <+ exactly <'{'>+ >+ >,+ // other valid chars+ neg_class_char <+ string_double_negates+ >+ >+ >,+ // quoted string closer+ // or interpolate opening+ alternatives <+ exactly <'"'>,+ lookahead < exactly< hash_lbrace > >+ >+ >(src);+ }++ const char* re_string_double_open(const char* src)+ {+ return sequence <+ // quoted string opener+ exactly <'"'>,+ // valid chars+ zero_plus <+ alternatives <+ // escaped char+ sequence <+ exactly <'\\'>,+ any_char+ >,+ // non interpolate hash+ sequence <+ exactly <'#'>,+ negate <+ exactly <'{'>+ >+ >,+ // other valid chars+ neg_class_char <+ string_double_negates+ >+ >+ >,+ // quoted string closer+ // or interpolate opening+ alternatives <+ exactly <'"'>,+ lookahead < exactly< hash_lbrace > >+ >+ >(src);+ }++ extern const char string_single_negates[] = "'\\#";+ const char* re_string_single_close(const char* src)+ {+ return sequence <+ // valid chars+ zero_plus <+ alternatives <+ // escaped char+ sequence <+ exactly <'\\'>,+ any_char+ >,+ // non interpolate hash+ sequence <+ exactly <'#'>,+ negate <+ exactly <'{'>+ >+ >,+ // other valid chars+ neg_class_char <+ string_single_negates+ >+ >+ >,+ // quoted string closer+ // or interpolate opening+ alternatives <+ exactly <'\''>,+ lookahead < exactly< hash_lbrace > >+ >+ >(src);+ }++ const char* re_string_single_open(const char* src)+ {+ return sequence <+ // quoted string opener+ exactly <'\''>,+ // valid chars+ zero_plus <+ alternatives <+ // escaped char+ sequence <+ exactly <'\\'>,+ any_char+ >,+ // non interpolate hash+ sequence <+ exactly <'#'>,+ negate <+ exactly <'{'>+ >+ >,+ // other valid chars+ neg_class_char <+ string_single_negates+ >+ >+ >,+ // quoted string closer+ // or interpolate opening+ alternatives <+ exactly <'\''>,+ lookahead < exactly< hash_lbrace > >+ >+ >(src);+ }++ /*+ :uri => {+ false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+ },+ */+ const char* re_string_uri_close(const char* src)+ {+ return sequence <+ non_greedy<+ alternatives<+ class_char< real_uri_chars >,+ uri_character,+ NONASCII,+ ESCAPE+ >,+ alternatives<+ sequence < optional < W >, exactly <')'> >,+ lookahead < exactly< hash_lbrace > >+ >+ >,+ optional <+ sequence < optional < W >, exactly <')'> >+ >+ >(src);+ }++ const char* re_string_uri_open(const char* src)+ {+ return sequence <+ exactly <'u'>,+ exactly <'r'>,+ exactly <'l'>,+ exactly <'('>,+ W,+ alternatives<+ quoted_string,+ non_greedy<+ alternatives<+ class_char< real_uri_chars >,+ uri_character,+ NONASCII,+ ESCAPE+ >,+ alternatives<+ sequence < W, exactly <')'> >,+ exactly< hash_lbrace >+ >+ >+ >+ >(src);+ }+ // Match a line comment (/.*?(?=\n|\r\n?|\Z)/. const char* line_comment(const char* src) {@@ -153,8 +399,8 @@ >(src); } - // Match CSS unit identifier.- const char* unit_identifier(const char* src)+ // Match a single CSS unit+ const char* one_unit(const char* src) { return sequence < optional < exactly <'-'> >,@@ -169,6 +415,34 @@ >(src); } + // Match numerator/denominator CSS units+ const char* multiple_units(const char* src)+ {+ return+ sequence <+ one_unit,+ zero_plus <+ sequence <+ exactly <'*'>,+ one_unit+ >+ >+ >(src);+ }++ // Match complex CSS unit identifiers+ const char* unit_identifier(const char* src)+ {+ return sequence <+ multiple_units,+ optional <+ sequence <+ exactly <'/'>,+ multiple_units+ > >+ >(src);+ }+ const char* identifier_alnums(const char* src) { return one_plus< identifier_alnum >(src);@@ -194,7 +468,12 @@ sequence < zero_plus < alternatives <- identifier,+ sequence <+ optional <+ exactly <'$'>+ >,+ identifier+ >, exactly <'-'> > >,@@ -202,9 +481,13 @@ zero_plus < alternatives < digits,- identifier,+ sequence <+ optional <+ exactly <'$'>+ >,+ identifier+ >, quoted_string,- exactly<'+'>, exactly<'-'> > >@@ -278,24 +561,187 @@ >(src); } - const char* value_schema(const char* src) {- // follows this pattern: ([xyz]*i[xyz]*)+- return one_plus< sequence< zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string > >,- interpolant,- zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string, exactly<'%'> > > > >(src);+ const char* sass_value(const char* src) {+ return alternatives <+ quoted_string,+ identifier,+ percentage,+ hex,+ dimension,+ number+ >(src); } - /* not used anymore - remove?- const char* filename(const char* src) {- return one_plus< alternatives< identifier, number, exactly<'.'> > >(src);+ // this is basically `one_plus < sass_value >`+ // takes care to not parse invalid combinations+ const char* value_combinations(const char* src) {+ // `2px-2px` is invalid combo+ bool was_number = false;+ const char* pos = src;+ while (src) {+ if ((pos = alternatives < quoted_string, identifier, percentage, hex >(src))) {+ was_number = false;+ src = pos;+ } else if (!was_number && !exactly<'+'>(src) && (pos = alternatives < dimension, number >(src))) {+ was_number = true;+ src = pos;+ } else {+ break;+ }+ }+ return src; }- */ + // must be at least one interpolant+ // can be surrounded by sass values+ // make sure to never parse (dim)(dim)+ // since this wrongly consumes `2px-1px`+ // `2px1px` is valid number (unit `px1px`)+ const char* value_schema(const char* src)+ {+ return sequence <+ one_plus <+ sequence <+ optional < value_combinations >,+ interpolant,+ optional < value_combinations >+ >+ >+ >(src);+ }+ // Match CSS '@' keywords. const char* at_keyword(const char* src) { return sequence<exactly<'@'>, identifier>(src); } + /*+ tok(%r{+ (+ \\.+ |+ (?!url\()+ [^"'/\#!;\{\}] # "+ |+ /(?![\*\/])+ |+ \#(?!\{)+ |+ !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed.+ )++ }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri ||+ interpolation(:warn_for_color)+ */+ const char* re_almost_any_value_token(const char* src) {++ return alternatives <+ one_plus <+ alternatives <+ sequence <+ exactly <'\\'>,+ any_char+ >,+ sequence <+ negate <+ sequence <+ exactly < url_kwd >,+ exactly <'('>+ >+ >,+ neg_class_char <+ almost_any_value_class+ >+ >,+ sequence <+ exactly <'/'>,+ negate <+ alternatives <+ exactly <'/'>,+ exactly <'*'>+ >+ >+ >,+ sequence <+ exactly <'\\'>,+ exactly <'#'>,+ negate <+ exactly <'{'>+ >+ >,+ sequence <+ exactly <'!'>,+ negate <+ alpha+ >+ >+ >+ >,+ block_comment,+ line_comment,+ interpolant,+ space,+ sequence <+ exactly<'u'>,+ exactly<'r'>,+ exactly<'l'>,+ exactly<'('>,+ zero_plus <+ alternatives <+ class_char< real_uri_chars >,+ uri_character,+ NONASCII,+ ESCAPE+ >+ >,+ // false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,+ // true => /(#{URLCHAR}*?)(#{W}\)|#\{)/+ exactly<')'>+ >+ >(src);+ }++ /*+ DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,+ :each, :while, :if, :else, :extend, :import, :media, :charset, :content,+ :_moz_document, :at_root, :error]+ */+ const char* re_special_directive(const char* src) {+ return alternatives <+ word < mixin_kwd >,+ word < include_kwd >,+ word < function_kwd >,+ word < return_kwd >,+ word < debug_kwd >,+ word < warn_kwd >,+ word < for_kwd >,+ word < each_kwd >,+ word < while_kwd >,+ word < if_kwd >,+ word < else_kwd >,+ word < extend_kwd >,+ word < import_kwd >,+ word < media_kwd >,+ word < charset_kwd >,+ word < content_kwd >,+ // exactly < moz_document_kwd >,+ word < at_root_kwd >,+ word < error_kwd >+ >(src);+ }++ const char* re_prefixed_directive(const char* src) {+ return sequence <+ optional <+ sequence <+ exactly <'-'>,+ one_plus < alnum >,+ exactly <'-'>+ >+ >,+ exactly < supports_kwd >+ >(src);+ }+ const char* re_reference_combinator(const char* src) { return sequence < optional <@@ -434,7 +880,7 @@ return one_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>,- exactly<'\\'> > >(src);+ escape_seq > >(src); } const char* kwd_warn(const char* src) {@@ -512,7 +958,7 @@ } const char* hyphens_and_identifier(const char* src) {- return sequence< zero_plus< exactly< '-' > >, identifier >(src);+ return sequence< zero_plus< exactly< '-' > >, identifier_alnums >(src); } const char* hyphens_and_name(const char* src) { return sequence< zero_plus< exactly< '-' > >, name >(src);@@ -522,11 +968,11 @@ } // Match CSS id names. const char* id_name(const char* src) {- return sequence<exactly<'#'>, name>(src);+ return sequence<exactly<'#'>, identifier_alnums >(src); } // Match CSS class names. const char* class_name(const char* src) {- return sequence<exactly<'.'>, identifier>(src);+ return sequence<exactly<'.'>, identifier >(src); } // Attribute name in an attribute selector. const char* attribute_name(const char* src) {@@ -535,10 +981,13 @@ } // match placeholder selectors const char* placeholder(const char* src) {- return sequence<exactly<'%'>, identifier>(src);+ return sequence<exactly<'%'>, identifier_alnums >(src); } // Match CSS numeric constants. + const char* op(const char* src) {+ return class_char<op_chars>(src);+ } const char* sign(const char* src) { return class_char<sign_chars>(src); }@@ -603,23 +1052,19 @@ // Match CSS uri specifiers. const char* uri_prefix(const char* src) {- return exactly<url_kwd>(src);- }- const char* uri_value(const char* src)- {- return- sequence <- negate <- exactly < '$' >+ return sequence <+ exactly <+ url_kwd >, zero_plus <- alternatives <- alnum,- interpolant,- exactly <'/'>,- class_char < uri_chars >+ sequence <+ exactly <'-'>,+ one_plus <+ alpha+ > >- >+ >,+ exactly <'('> >(src); } @@ -752,6 +1197,22 @@ return sequence<exactly<'$'>, identifier>(src); } + // parse `calc`, `-a-calc` and `--b-c-calc`+ // but do not parse `foocalc` or `foo-calc`+ const char* calc_fn_call(const char* src) {+ return sequence <+ optional < sequence <+ hyphens,+ one_plus < sequence <+ strict_identifier,+ hyphens+ > >+ > >,+ exactly < calc_fn_kwd >,+ word_boundary+ >(src);+ }+ // Match Sass boolean keywords. const char* kwd_true(const char* src) { return word<true_kwd>(src);@@ -1094,6 +1555,83 @@ >(src); } + const char* re_selector_list(const char* src) {+ return alternatives <+ // partial bem selector+ sequence <+ ampersand,+ one_plus <+ exactly < '-' >+ >,+ word_boundary,+ optional_spaces+ >,+ // main selector matching+ one_plus <+ alternatives <+ // consume whitespace and comments+ spaces, block_comment, line_comment,+ // match `/deep/` selector (pass-trough)+ // there is no functionality for it yet+ schema_reference_combinator,+ // match selector ops /[*&%,\[\]]/+ class_char < selector_lookahead_ops >,+ // match selector combinators /[>+~]/+ class_char < selector_combinator_ops >,+ // match attribute compare operators+ sequence <+ exactly <'('>,+ optional_spaces,+ optional <re_selector_list>,+ optional_spaces,+ exactly <')'>+ >,+ alternatives <+ exact_match, class_match, dash_match,+ prefix_match, suffix_match, substring_match+ >,+ // main selector match+ sequence <+ // allow namespace prefix+ optional < namespace_schema >,+ // modifiers prefixes+ alternatives <+ sequence <+ exactly <'#'>,+ // not for interpolation+ negate < exactly <'{'> >+ >,+ // class match+ exactly <'.'>,+ // single or double colon+ optional < pseudo_prefix >+ >,+ // accept hypens in token+ one_plus < sequence <+ // can start with hyphens+ zero_plus < exactly<'-'> >,+ // now the main token+ alternatives <+ kwd_optional,+ exactly <'*'>,+ quoted_string,+ interpolant,+ identifier,+ variable,+ percentage,+ binomial,+ dimension,+ alnum+ >+ > >,+ // can also end with hyphens+ zero_plus < exactly<'-'> >+ >+ >+ >+ >(src);+ }+ const char* type_selector(const char* src) { return sequence< optional<namespace_schema>, identifier>(src); }@@ -1107,34 +1645,44 @@ return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src); } - template <size_t size, prelexer mx, prelexer pad>- const char* padded_token(const char* src)- {- size_t got = 0;- const char* pos = src;- while (got < size) {- if (!mx(pos)) break;- ++ pos; ++ got;- }- while (got < size) {- if (!pad(pos)) break;- ++ pos; ++ got;- }- return got ? pos : 0;- }+ // lexer special_fn: these functions cannot be overloaded+ // (/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)+ const char* re_special_fun(const char* src) { - template <size_t min, size_t max, prelexer mx>- const char* minmax_range(const char* src)- {- size_t got = 0;- const char* pos = src;- while (got < max) {- if (!mx(pos)) break;- ++ pos; ++ got;+ // match this first as we test prefix hyphens+ if (const char* calc = calc_fn_call(src)) {+ return calc; }- if (got < min) return 0;- if (got > max) return 0;- return pos;++ return sequence <+ optional <+ sequence <+ exactly <'-'>,+ one_plus <+ alternatives <+ alpha,+ exactly <'+'>,+ exactly <'-'>+ >+ >+ >+ >,+ alternatives <+ word < expression_kwd >,+ sequence <+ sequence <+ exactly < progid_kwd >,+ exactly <':'>+ >,+ zero_plus <+ alternatives <+ char_range <'a', 'z'>,+ exactly <'.'>+ >+ >+ >+ >+ >(src); } }
libsass/src/prelexer.hpp view
@@ -162,13 +162,19 @@ return src; } + // equivalent of STRING_REGULAR_EXPRESSIONS+ const char* re_string_double_open(const char* src);+ const char* re_string_double_close(const char* src);+ const char* re_string_single_open(const char* src);+ const char* re_string_single_close(const char* src);+ const char* re_string_uri_open(const char* src);+ const char* re_string_uri_close(const char* src);+ // Match a line comment. const char* line_comment(const char* src);- const char* line_comment_prefix(const char* src); // Match a block comment. const char* block_comment(const char* src);- const char* block_comment_prefix(const char* src); // Match either. const char* comment(const char* src); // Match double- and single-quoted strings.@@ -200,6 +206,8 @@ const char* strict_identifier_alpha(const char* src); const char* strict_identifier_alnum(const char* src); // Match a CSS unit identifier.+ const char* one_unit(const char* src);+ const char* multiple_units(const char* src); const char* unit_identifier(const char* src); // const char* strict_identifier_alnums(const char* src); // Match reference selector.@@ -210,11 +218,17 @@ // Match interpolant schemas const char* identifier_schema(const char* src); const char* value_schema(const char* src);+ const char* sass_value(const char* src); // const char* filename(const char* src); // const char* filename_schema(const char* src); // const char* url_schema(const char* src); // const char* url_value(const char* src); const char* vendor_prefix(const char* src);++ const char* re_special_directive(const char* src);+ const char* re_prefixed_directive(const char* src);+ const char* re_almost_any_value_token(const char* src);+ // Match CSS '@' keywords. const char* at_keyword(const char* src); const char* kwd_import(const char* src);@@ -252,6 +266,7 @@ const char* re_nothing(const char* src); const char* re_type_selector2(const char* src); + const char* re_special_fun(const char* src); const char* kwd_warn(const char* src); const char* kwd_err(const char* src);@@ -259,6 +274,7 @@ const char* kwd_null(const char* src); + const char* re_selector_list(const char* src); const char* re_type_selector(const char* src); const char* re_static_expression(const char* src); @@ -282,6 +298,7 @@ // Match placeholder selectors. const char* placeholder(const char* src); // Match CSS numeric constants.+ const char* op(const char* src); const char* sign(const char* src); const char* unsigned_number(const char* src); const char* number(const char* src);@@ -296,7 +313,6 @@ // const char* rgb_prefix(const char* src); // Match CSS uri specifiers. const char* uri_prefix(const char* src);- const char* uri_value(const char* src); // Match CSS "!important" keyword. const char* kwd_important(const char* src); // Match CSS "!optional" keyword.@@ -329,6 +345,7 @@ // Match SCSS variable names. const char* variable(const char* src);+ const char* calc_fn_call(const char* src); // IE stuff const char* ie_progid(const char* src);@@ -381,6 +398,18 @@ } return 0; }+ template<prelexer mx, prelexer skip>+ const char* find_first_in_interval(const char* beg, const char* end) {+ bool esc = false;+ while ((beg < end) && *beg) {+ if (esc) esc = false;+ else if (*beg == '\\') esc = true;+ else if (const char* pos = skip(beg)) beg = pos;+ else if (mx(beg)) return beg;+ ++beg;+ }+ return 0;+ } template <prelexer mx> unsigned int count_interval(const char* beg, const char* end) { unsigned int counter = 0;@@ -405,10 +434,42 @@ } template <size_t size, prelexer mx, prelexer pad>- const char* padded_token(const char* src);+ const char* padded_token(const char* src)+ {+ size_t got = 0;+ const char* pos = src;+ while (got < size) {+ if (!mx(pos)) break;+ ++ pos; ++ got;+ }+ while (got < size) {+ if (!pad(pos)) break;+ ++ pos; ++ got;+ }+ return got ? pos : 0;+ } template <size_t min, size_t max, prelexer mx>- const char* minmax_range(const char* src);+ const char* minmax_range(const char* src)+ {+ size_t got = 0;+ const char* pos = src;+ while (got < max) {+ if (!mx(pos)) break;+ ++ pos; ++ got;+ }+ if (got < min) return 0;+ if (got > max) return 0;+ return pos;+ }++ template <char min, char max>+ const char* char_range(const char* src)+ {+ if (*src < min) return 0;+ if (*src > max) return 0;+ return src + 1;+ } } }
libsass/src/remove_placeholders.cpp view
@@ -1,7 +1,7 @@+#include "sass.hpp" #include "remove_placeholders.hpp" #include "context.hpp" #include "inspect.hpp"-#include "to_string.hpp" #include <iostream> namespace Sass {@@ -10,45 +10,75 @@ : ctx(ctx) { } - void Remove_Placeholders::operator()(Block* b) {+ void Remove_Placeholders::operator()(Block_Ptr b) { for (size_t i = 0, L = b->length(); i < L; ++i) {- (*b)[i]->perform(this);+ Statement_Ptr st = &b->at(i);+ st->perform(this); } } - void Remove_Placeholders::operator()(Ruleset* r) {+ Selector_List_Ptr Remove_Placeholders::remove_placeholders(Selector_List_Ptr sl)+ {+ Selector_List_Ptr new_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());++ for (size_t i = 0, L = sl->length(); i < L; ++i) {+ if (!sl->at(i)->contains_placeholder()) {+ new_sl->append(sl->at(i));+ }+ }++ return new_sl;++ }+++ void Remove_Placeholders::operator()(Ruleset_Ptr r) { // Create a new selector group without placeholders- Selector_List* sl = static_cast<Selector_List*>(r->selector());+ Selector_List_Obj sl = SASS_MEMORY_CAST(Selector_List, r->selector()); if (sl) {- Selector_List* new_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate());-- for (size_t i = 0, L = sl->length(); i < L; ++i) {- if (!(*sl)[i]->contains_placeholder()) {- *new_sl << (*sl)[i];+ // Set the new placeholder selector list+ r->selector(remove_placeholders(&sl));+ // Remove placeholders in wrapped selectors+ for (Complex_Selector_Obj cs : sl->elements()) {+ while (cs) {+ if (cs->head()) {+ for (Simple_Selector_Obj& ss : cs->head()->elements()) {+ if (Wrapped_Selector_Ptr ws = SASS_MEMORY_CAST(Wrapped_Selector, ss)) {+ if (Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, ws->selector())) {+ Selector_List_Ptr clean = remove_placeholders(sl);+ // also clean superflous parent selectors+ // probably not really the correct place+ clean->remove_parent_selectors();+ ws->selector(clean);+ }+ } }+ }+ cs = cs->tail(); }-- // Set the new placeholder selector list- r->selector(new_sl);+ } } // Iterate into child blocks- Block* b = r->block();+ Block_Obj b = r->block(); for (size_t i = 0, L = b->length(); i < L; ++i) {- if ((*b)[i]) (*b)[i]->perform(this);+ if (b->at(i)) {+ Statement_Obj st = b->at(i);+ st->perform(this);+ } } } - void Remove_Placeholders::operator()(Media_Block* m) {- Block* b = m->block();- for (size_t i = 0, L = b->length(); i < L; ++i) {- if ((*b)[i]) (*b)[i]->perform(this);- }+ void Remove_Placeholders::operator()(Media_Block_Ptr m) {+ operator()(&m->block()); }+ void Remove_Placeholders::operator()(Supports_Block_Ptr m) {+ operator()(&m->block());+ } - void Remove_Placeholders::operator()(At_Rule* a) {+ void Remove_Placeholders::operator()(Directive_Ptr a) { if (a->block()) a->block()->perform(this); }
libsass/src/remove_placeholders.hpp view
@@ -15,18 +15,20 @@ Context& ctx; - void fallback_impl(AST_Node* n) {}+ void fallback_impl(AST_Node_Ptr n) {} public:- Remove_Placeholders(Context&);- virtual ~Remove_Placeholders() { }+ Selector_List_Ptr remove_placeholders(Selector_List_Ptr); - using Operation<void>::operator();+ public:+ Remove_Placeholders(Context&);+ ~Remove_Placeholders() { } - void operator()(Block*);- void operator()(Ruleset*);- void operator()(Media_Block*);- void operator()(At_Rule*);+ void operator()(Block_Ptr);+ void operator()(Ruleset_Ptr);+ void operator()(Media_Block_Ptr);+ void operator()(Supports_Block_Ptr);+ void operator()(Directive_Ptr); template <typename U> void fallback(U x) { return fallback_impl(x); }
libsass/src/sass.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstdlib> #include <cstring> #include <vector>@@ -10,18 +11,42 @@ extern "C" { using namespace Sass; + // Allocate libsass heap memory+ // Don't forget string termination!+ void* ADDCALL sass_alloc_memory(size_t size)+ {+ void* ptr = malloc(size);+ if (ptr == NULL)+ out_of_memory();+ return ptr;+ }++ char* ADDCALL sass_copy_c_string(const char* str)+ {+ size_t len = strlen(str) + 1;+ char* cpy = (char*) sass_alloc_memory(len);+ std::memcpy(cpy, str, len);+ return cpy;+ }++ // Deallocate libsass heap memory+ void ADDCALL sass_free_memory(void* ptr)+ {+ if (ptr) free (ptr);+ }+ // caller must free the returned memory char* ADDCALL sass_string_quote (const char *str, const char quote_mark) { std::string quoted = quote(str, quote_mark);- return sass_strdup(quoted.c_str());+ return sass_copy_c_string(quoted.c_str()); } // caller must free the returned memory char* ADDCALL sass_string_unquote (const char *str) { std::string unquoted = unquote(str);- return sass_strdup(unquoted.c_str());+ return sass_copy_c_string(unquoted.c_str()); } // Make sure to free the returned value!@@ -29,13 +54,40 @@ char* ADDCALL sass_resolve_file (const char* file, const char* paths[]) { std::string resolved(File::find_file(file, paths));- return sass_strdup(resolved.c_str());+ return sass_copy_c_string(resolved.c_str()); } // Get compiled libsass version const char* ADDCALL libsass_version(void) { return LIBSASS_VERSION;+ }++ // Get compiled libsass version+ const char* ADDCALL libsass_language_version(void)+ {+ return LIBSASS_LANGUAGE_VERSION;+ }++}++namespace Sass {++ // helper to aid dreaded MSVC debug mode+ char* sass_copy_string(std::string str)+ {+ // In MSVC the following can lead to segfault:+ // sass_copy_c_string(stream.str().c_str());+ // Reason is that the string returned by str() is disposed before+ // sass_copy_c_string is invoked. The string is actually a stack+ // object, so indeed nobody is holding on to it. So it seems+ // perfectly fair to release it right away. So the const char*+ // by c_str will point to invalid memory. I'm not sure if this is+ // the behavior for all compiler, but I'm pretty sure we would+ // have gotten more issues reported if that would be the case.+ // Wrapping it in a functions seems the cleanest approach as the+ // function must hold on to the stack variable until it's done.+ return sass_copy_c_string(str.c_str()); } }
+ libsass/src/sass.hpp view
@@ -0,0 +1,136 @@+// must be the first include in all compile units+#ifndef SASS_SASS_H+#define SASS_SASS_H++// undefine extensions macro to tell sys includes+// that we do not want any macros to be exported+// mainly fixes an issue on SmartOS (SEC macro)+#undef __EXTENSIONS__++#ifdef _MSC_VER+#pragma warning(disable : 4005)+#endif++// aplies to MSVC and MinGW+#ifdef _WIN32+// we do not want the ERROR macro+# define NOGDI+// we do not want the min/max macro+# define NOMINMAX+// we do not want the IN/OUT macro+# define _NO_W32_PSEUDO_MODIFIERS+#endif+++// should we be case insensitive+// when dealing with files or paths+#ifndef FS_CASE_SENSITIVE+# ifdef _WIN32+# define FS_CASE_SENSITIVE 0+# else+# define FS_CASE_SENSITIVE 1+# endif+#endif++// path separation char+#ifndef PATH_SEP+# ifdef _WIN32+# define PATH_SEP ';'+# else+# define PATH_SEP ':'+# endif+#endif+++// include C-API header+#include "sass/base.h"++// For C++ helper+#include <string>++// output behaviours+namespace Sass {++ // create some C++ aliases for the most used options+ const static Sass_Output_Style NESTED = SASS_STYLE_NESTED;+ const static Sass_Output_Style COMPACT = SASS_STYLE_COMPACT;+ const static Sass_Output_Style EXPANDED = SASS_STYLE_EXPANDED;+ const static Sass_Output_Style COMPRESSED = SASS_STYLE_COMPRESSED;+ // only used internal to trigger ruby inspect behavior+ const static Sass_Output_Style INSPECT = SASS_STYLE_INSPECT;+ const static Sass_Output_Style TO_SASS = SASS_STYLE_TO_SASS;++ // helper to aid dreaded MSVC debug mode+ // see implementation for more details+ char* sass_copy_string(std::string str);++}++// input behaviours+enum Sass_Input_Style {+ SASS_CONTEXT_NULL,+ SASS_CONTEXT_FILE,+ SASS_CONTEXT_DATA,+ SASS_CONTEXT_FOLDER+};++// simple linked list+struct string_list {+ string_list* next;+ char* string;+};++// sass config options structure+struct Sass_Inspect_Options {++ // Output style for the generated css code+ // A value from above SASS_STYLE_* constants+ enum Sass_Output_Style output_style;++ // Precision for fractional numbers+ int precision;++ // initialization list (constructor with defaults)+ Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED,+ int precision = 5)+ : output_style(style), precision(precision)+ { }++};++// sass config options structure+struct Sass_Output_Options : Sass_Inspect_Options {++ // String to be used for indentation+ const char* indent;+ // String to be used to for line feeds+ const char* linefeed;++ // Emit comments in the generated CSS indicating+ // the corresponding source line.+ bool source_comments;++ // initialization list (constructor with defaults)+ Sass_Output_Options(struct Sass_Inspect_Options opt,+ const char* indent = " ",+ const char* linefeed = "\n",+ bool source_comments = false)+ : Sass_Inspect_Options(opt),+ indent(indent), linefeed(linefeed),+ source_comments(source_comments)+ { }++ // initialization list (constructor with defaults)+ Sass_Output_Options(Sass_Output_Style style = Sass::NESTED,+ int precision = 5,+ const char* indent = " ",+ const char* linefeed = "\n",+ bool source_comments = false)+ : Sass_Inspect_Options(style, precision),+ indent(indent), linefeed(linefeed),+ source_comments(source_comments)+ { }++};++#endif
libsass/src/sass2scss.cpp view
@@ -528,6 +528,13 @@ // reset converter state converter.selector = false; + // looks like some undocumented behavior ...+ // https://github.com/mgreter/sass2scss/issues/29+ if (sass.substr(pos_left, 1) == "\\") {+ converter.selector = true;+ sass[pos_left] = ' ';+ }+ // check if we have sass property syntax if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::") {@@ -567,6 +574,12 @@ } } + // check if we have a BEM property (one colon and no selector)+ if (sass.substr(pos_left, 1) == ":" && converter.selector == true) {+ size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left);+ sass = indent + sass.substr(pos_left + 1, pos_wspace) + ":";+ }+ } // terminate some statements immediately@@ -574,13 +587,19 @@ sass.substr(pos_left, 5) == "@warn" || sass.substr(pos_left, 6) == "@debug" || sass.substr(pos_left, 6) == "@error" ||- sass.substr(pos_left, 8) == "@charset"+ sass.substr(pos_left, 8) == "@charset" ||+ sass.substr(pos_left, 10) == "@namespace" ) { sass = indent + sass.substr(pos_left); } // replace some specific sass shorthand directives (if not fallowed by a white space character)- else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)+ else if (sass.substr(pos_left, 1) == "=") { sass = indent + "@mixin " + sass.substr(pos_left + 1); }- else if (sass.substr(pos_left, 1) == "+" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)- { sass = indent + "@include " + sass.substr(pos_left + 1); }+ else if (sass.substr(pos_left, 1) == "+")+ {+ // must be followed by a mixin call (no whitespace afterwards or at ending directly)+ if (sass[pos_left+1] != 0 && sass[pos_left+1] != ' ' && sass[pos_left+1] != '\t') {+ sass = indent + "@include " + sass.substr(pos_left + 1);+ }+ } // add quotes for import if needed else if (sass.substr(pos_left, 7) == "@import")
libsass/src/sass_context.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstring> #include <stdexcept> #include <sstream>@@ -5,82 +6,88 @@ #include <vector> #include "sass.h"+#include "ast.hpp" #include "file.hpp" #include "json.hpp" #include "util.hpp" #include "context.hpp" #include "sass_context.hpp"+#include "sass_functions.hpp" #include "ast_fwd_decl.hpp" #include "error_handling.hpp" #define LFEED "\n" -extern "C" {- using namespace Sass;-- static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { *to = *from; }-- #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \- type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \- void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; }- #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \- type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \- void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \- { free(options->option); options->option = option || def ? sass_strdup(option ? option : def) : 0; }-- #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \- type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; }- #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \- type sass_context_take_##option (struct Sass_Context* ctx) \- { type foo = ctx->option; ctx->option = 0; return foo; }+// C++ helper+namespace Sass {+ // see sass_copy_c_string(std::string str)+ static inline JsonNode* json_mkstream(const std::stringstream& stream)+ {+ // hold on to string on stack!+ std::string str(stream.str());+ return json_mkstring(str.c_str());+ } - static int handle_errors(Sass_Context* c_ctx) {+ static int handle_error(Sass_Context* c_ctx) { try {- throw;+ throw; } catch (Exception::Base& e) { std::stringstream msg_stream; std::string cwd(Sass::File::get_cwd());- std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); - std::string msg_prefix("Error: ");+ std::string msg_prefix(e.errtype()); bool got_newline = false;- msg_stream << msg_prefix;+ msg_stream << msg_prefix << ": "; const char* msg = e.what();- while(msg && *msg) {+ while (msg && *msg) { if (*msg == '\r') { got_newline = true;- } else if (*msg == '\n') {+ }+ else if (*msg == '\n') { got_newline = true;- } else if (got_newline) {- msg_stream << std::string(msg_prefix.size(), ' ');+ }+ else if (got_newline) {+ msg_stream << std::string(msg_prefix.size() + 2, ' '); got_newline = false; } msg_stream << *msg;- ++ msg;+ ++msg; } if (!got_newline) msg_stream << "\n";- msg_stream << std::string(msg_prefix.size(), ' ');- msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";+ if (e.import_stack) {+ for (size_t i = 1; i < e.import_stack->size() - 1; ++i) {+ std::string path((*e.import_stack)[i]->imp_path);+ std::string rel_path(Sass::File::abs2rel(path, cwd, cwd));+ msg_stream << std::string(msg_prefix.size() + 2, ' ');+ msg_stream << (i == 1 ? " on line " : " from line ");+ msg_stream << e.pstate.line + 1 << " of " << rel_path << "\n";+ }+ }+ else {+ std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));+ msg_stream << std::string(msg_prefix.size() + 2, ' ');+ msg_stream << " on line " << e.pstate.line + 1 << " of " << rel_path << "\n";+ } // now create the code trace (ToDo: maybe have util functions?) if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) { size_t line = e.pstate.line; const char* line_beg = e.pstate.src; while (line_beg && *line_beg && line) {- if (*line_beg == '\n') -- line;- ++ line_beg;+ if (*line_beg == '\n') --line;+ ++line_beg; } const char* line_end = line_beg; while (line_end && *line_end && *line_end != '\n') { if (*line_end == '\n') break; if (*line_end == '\r') break;- line_end ++;+ line_end++; } size_t max_left = 42; size_t max_right = 78; size_t move_in = e.pstate.column > max_left ? e.pstate.column - max_left : 0; size_t shorten = (line_end - line_beg) - move_in > max_right ?- (line_end - line_beg) - move_in - max_right : 0;+ (line_end - line_beg) - move_in - max_right : 0; msg_stream << ">> " << std::string(line_beg + move_in, line_end - shorten) << "\n"; msg_stream << " " << std::string(e.pstate.column - move_in, '-') << "^\n"; }@@ -88,18 +95,18 @@ JsonNode* json_err = json_mkobject(); json_append_member(json_err, "status", json_mknumber(1)); json_append_member(json_err, "file", json_mkstring(e.pstate.path));- json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line+1)));- json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column+1)));+ json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line + 1)));+ json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column + 1))); json_append_member(json_err, "message", json_mkstring(e.what()));- json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str()));-- c_ctx->error_json = json_stringify(json_err, " ");;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_text = sass_strdup(e.what());+ json_append_member(json_err, "formatted", json_mkstream(msg_stream));+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string(e.what()); c_ctx->error_status = 1;- c_ctx->error_file = sass_strdup(e.pstate.path);- c_ctx->error_line = e.pstate.line+1;- c_ctx->error_column = e.pstate.column+1;+ c_ctx->error_file = sass_copy_c_string(e.pstate.path);+ c_ctx->error_line = e.pstate.line + 1;+ c_ctx->error_column = e.pstate.column + 1; c_ctx->error_src = e.pstate.src; c_ctx->output_string = 0; c_ctx->source_map_string = 0;@@ -111,9 +118,11 @@ msg_stream << "Unable to allocate memory: " << ba.what() << std::endl; json_append_member(json_err, "status", json_mknumber(2)); json_append_member(json_err, "message", json_mkstring(ba.what()));- c_ctx->error_json = json_stringify(json_err, " ");;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_text = sass_strdup(ba.what());+ json_append_member(json_err, "formatted", json_mkstream(msg_stream));+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string(ba.what()); c_ctx->error_status = 2; c_ctx->output_string = 0; c_ctx->source_map_string = 0;@@ -122,12 +131,14 @@ catch (std::exception& e) { std::stringstream msg_stream; JsonNode* json_err = json_mkobject();- msg_stream << "Error: " << e.what() << std::endl;+ msg_stream << "Internal Error: " << e.what() << std::endl; json_append_member(json_err, "status", json_mknumber(3)); json_append_member(json_err, "message", json_mkstring(e.what()));- c_ctx->error_json = json_stringify(json_err, " ");;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_text = sass_strdup(e.what());+ json_append_member(json_err, "formatted", json_mkstream(msg_stream));+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string(e.what()); c_ctx->error_status = 3; c_ctx->output_string = 0; c_ctx->source_map_string = 0;@@ -136,12 +147,14 @@ catch (std::string& e) { std::stringstream msg_stream; JsonNode* json_err = json_mkobject();- msg_stream << "Error: " << e << std::endl;+ msg_stream << "Internal Error: " << e << std::endl; json_append_member(json_err, "status", json_mknumber(4)); json_append_member(json_err, "message", json_mkstring(e.c_str()));- c_ctx->error_json = json_stringify(json_err, " ");;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_text = sass_strdup(e.c_str());+ json_append_member(json_err, "formatted", json_mkstream(msg_stream));+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string(e.c_str()); c_ctx->error_status = 4; c_ctx->output_string = 0; c_ctx->source_map_string = 0;@@ -150,12 +163,14 @@ catch (const char* e) { std::stringstream msg_stream; JsonNode* json_err = json_mkobject();- msg_stream << "Error: " << e << std::endl;+ msg_stream << "Internal Error: " << e << std::endl; json_append_member(json_err, "status", json_mknumber(4)); json_append_member(json_err, "message", json_mkstring(e));- c_ctx->error_json = json_stringify(json_err, " ");;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_text = sass_strdup(e);+ json_append_member(json_err, "formatted", json_mkstream(msg_stream));+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string(e); c_ctx->error_status = 4; c_ctx->output_string = 0; c_ctx->source_map_string = 0;@@ -167,9 +182,10 @@ msg_stream << "Unknown error occurred" << std::endl; json_append_member(json_err, "status", json_mknumber(5)); json_append_member(json_err, "message", json_mkstring("unknown"));- c_ctx->error_json = json_stringify(json_err, " ");;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_text = sass_strdup("unknown");+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string("unknown"); c_ctx->error_status = 5; c_ctx->output_string = 0; c_ctx->source_map_string = 0;@@ -178,45 +194,100 @@ return c_ctx->error_status; } - // generic compilation function (not exported, use file/data compile instead)- static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw()+ // allow one error handler to throw another error+ // this can happen with invalid utf8 and json lib+ static int handle_errors(Sass_Context* c_ctx) {+ try { return handle_error(c_ctx); }+ catch (...) { return handle_error(c_ctx); }+ return c_ctx->error_status;+ }++ static Block_Obj sass_parse_block(Sass_Compiler* compiler) throw() {++ // assert valid pointer+ if (compiler == 0) return 0;+ // The cpp context must be set by now+ Context* cpp_ctx = compiler->cpp_ctx;+ Sass_Context* c_ctx = compiler->c_ctx;+ // We will take care to wire up the rest+ compiler->cpp_ctx->c_compiler = compiler;+ compiler->state = SASS_COMPILER_PARSED;+ try { - // convert include path linked list to static array- struct string_list* inc = c_ctx->include_paths;- // very poor loop to get the length of the linked list- size_t inc_size = 0; while (inc) { inc_size ++; inc = inc->next; }- // create char* array to hold all paths plus null terminator- const char** include_paths = (const char**) calloc(inc_size + 1, sizeof(char*));- if (include_paths == 0) throw(std::bad_alloc());- // reset iterator- inc = c_ctx->include_paths;- // copy over the paths- for (size_t i = 0; inc; i++) {- include_paths[i] = inc->string;- inc = inc->next;- }+ // get input/output path from options+ std::string input_path = safe_str(c_ctx->input_path);+ std::string output_path = safe_str(c_ctx->output_path); - // convert plugin path linked list to static array- struct string_list* imp = c_ctx->plugin_paths;- // very poor loop to get the length of the linked list- size_t imp_size = 0; while (imp) { imp_size ++; imp = imp->next; }- // create char* array to hold all paths plus null terminator- const char** plugin_paths = (const char**) calloc(imp_size + 1, sizeof(char*));- if (plugin_paths == 0) throw(std::bad_alloc());- // reset iterator- imp = c_ctx->plugin_paths;- // copy over the paths- for (size_t i = 0; imp; i++) {- plugin_paths[i] = imp->string;- imp = imp->next;- }+ // maybe skip some entries of included files+ // we do not include stdin for data contexts+ bool skip = c_ctx->type == SASS_CONTEXT_DATA; - // free intermediate data- free(include_paths);- free(plugin_paths);+ // dispatch parse call+ Block_Obj root(cpp_ctx->parse());+ // abort on errors+ if (!root) return 0; + // skip all prefixed files? (ToDo: check srcmap)+ // IMO source-maps should point to headers already+ // therefore don't skip it for now. re-enable or+ // remove completely once this is tested+ size_t headers = cpp_ctx->head_imports;++ // copy the included files on to the context (dont forget to free later)+ if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL)+ throw(std::bad_alloc());++ // return parsed block+ return root;++ }+ // pass errors to generic error handler+ catch (...) { handle_errors(c_ctx); }++ // error+ return 0;++ }++}++extern "C" {+ using namespace Sass;++ static void sass_clear_options (struct Sass_Options* options);+ static void sass_reset_options (struct Sass_Options* options);+ static void copy_options(struct Sass_Options* to, struct Sass_Options* from) {+ // do not overwrite ourself+ if (to == from) return;+ // free assigned memory+ sass_clear_options(to);+ // move memory+ *to = *from;+ // Reset pointers on source+ sass_reset_options(from);+ }++ #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \+ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \+ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; }+ #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \+ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \+ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \+ { free(options->option); options->option = option || def ? sass_copy_c_string(option ? option : def) : 0; }++ #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \+ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; }+ #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \+ type sass_context_take_##option (struct Sass_Context* ctx) \+ { type foo = ctx->option; ctx->option = 0; return foo; }+++ // generic compilation function (not exported, use file/data compile instead)+ static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw()+ {+ try { // register our custom functions if (c_ctx->c_functions) { auto this_func_data = c_ctx->c_functions;@@ -276,58 +347,6 @@ } - static Block* sass_parse_block (Sass_Compiler* compiler) throw()- {-- // assert valid pointer- if (compiler == 0) return 0;- // The cpp context must be set by now- Context* cpp_ctx = compiler->cpp_ctx;- Sass_Context* c_ctx = compiler->c_ctx;- // We will take care to wire up the rest- compiler->cpp_ctx->c_compiler = compiler;- compiler->state = SASS_COMPILER_PARSED;-- try {-- // get input/output path from options- std::string input_path = safe_str(c_ctx->input_path);- std::string output_path = safe_str(c_ctx->output_path);-- // parsed root block- Block* root = 0;-- // maybe skip some entries of included files- // we do not include stdin for data contexts- bool skip = c_ctx->type == SASS_CONTEXT_DATA;-- // dispatch parse call- root = cpp_ctx->parse();- // abort on errors- if (!root) return 0;-- // skip all prefixed files? (ToDo: check srcmap)- // IMO source-maps should point to headers already- // therefore don't skip it for now. re-enable or- // remove completely once this is tested- size_t headers = cpp_ctx->head_imports;-- // copy the included files on to the context (dont forget to free later)- if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL)- throw(std::bad_alloc());-- // return parsed block- return root;-- }- // pass errors to generic error handler- catch (...) { handle_errors(c_ctx); }-- // error- return 0;-- }- // generic compilation function (not exported, use file/data compile instead) static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx) {@@ -365,6 +384,7 @@ Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path) {+ SharedObj::setTaint(true); // needed for static colors struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(1, sizeof(struct Sass_File_Context)); if (ctx == 0) { std::cerr << "Error allocating memory for file context" << std::endl; return 0; } ctx->type = SASS_CONTEXT_FILE;@@ -398,14 +418,14 @@ struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 0;- Context* cpp_ctx = new Data_Context(data_ctx);+ Context* cpp_ctx = new Data_Context(*data_ctx); return sass_prepare_context(data_ctx, cpp_ctx); } struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) { if (file_ctx == 0) return 0;- Context* cpp_ctx = new File_Context(file_ctx);+ Context* cpp_ctx = new File_Context(*file_ctx); return sass_prepare_context(file_ctx, cpp_ctx); } @@ -416,10 +436,11 @@ return data_ctx->error_status; try { if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); }- if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); }+ // empty source string is a valid case, even if not really usefull (different than with file context)+ // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } } catch (...) { return handle_errors(data_ctx) | 1; }- Context* cpp_ctx = new Data_Context(data_ctx);+ Context* cpp_ctx = new Data_Context(*data_ctx); return sass_compile_context(data_ctx, cpp_ctx); } @@ -433,7 +454,7 @@ if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); } } catch (...) { return handle_errors(file_ctx) | 1; }- Context* cpp_ctx = new File_Context(file_ctx);+ Context* cpp_ctx = new File_Context(*file_ctx); return sass_compile_context(file_ctx, cpp_ctx); } @@ -447,7 +468,7 @@ if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; // parse the context we have set up (file or data)- compiler->root = sass_parse_block(compiler);+ compiler->root = &sass_parse_block(compiler); // success return 0; }@@ -459,12 +480,12 @@ if (compiler->state != SASS_COMPILER_PARSED) return -1; if (compiler->c_ctx == NULL) return 1; if (compiler->cpp_ctx == NULL) return 1;- if (compiler->root == NULL) return 1;+ if (compiler->root.isNull()) return 1; if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; compiler->state = SASS_COMPILER_EXECUTED; Context* cpp_ctx = compiler->cpp_ctx;- Block* root = compiler->root;+ Block_Obj root = compiler->root; // compile the parsed root block try { compiler->c_ctx->output_string = cpp_ctx->render(root); } // pass catched errors to generic error handler@@ -476,6 +497,24 @@ } // helper function, not exported, only accessible locally+ static void sass_reset_options (struct Sass_Options* options)+ {+ // free pointer before+ // or copy/move them+ options->input_path = 0;+ options->output_path = 0;+ options->plugin_path = 0;+ options->include_path = 0;+ options->source_map_file = 0;+ options->source_map_root = 0;+ options->c_functions = 0;+ options->c_importers = 0;+ options->c_headers = 0;+ options->plugin_paths = 0;+ options->include_paths = 0;+ }++ // helper function, not exported, only accessible locally static void sass_clear_options (struct Sass_Options* options) { if (options == 0) return;@@ -527,12 +566,25 @@ cur = next; } }+ // Free options strings+ free(options->input_path);+ free(options->output_path);+ free(options->plugin_path);+ free(options->include_path);+ free(options->source_map_file);+ free(options->source_map_root); // Free custom functions free(options->c_functions); // Free custom importers free(options->c_importers); free(options->c_headers); // Reset our pointers+ options->input_path = 0;+ options->output_path = 0;+ options->plugin_path = 0;+ options->include_path = 0;+ options->source_map_file = 0;+ options->source_map_root = 0; options->c_functions = 0; options->c_importers = 0; options->c_headers = 0;@@ -545,19 +597,13 @@ static void sass_clear_context (struct Sass_Context* ctx) { if (ctx == 0) return;- // release the allocated memory (mostly via sass_strdup)+ // release the allocated memory (mostly via sass_copy_c_string) if (ctx->output_string) free(ctx->output_string); if (ctx->source_map_string) free(ctx->source_map_string); if (ctx->error_message) free(ctx->error_message); if (ctx->error_text) free(ctx->error_text); if (ctx->error_json) free(ctx->error_json); if (ctx->error_file) free(ctx->error_file);- if (ctx->input_path) free(ctx->input_path);- if (ctx->output_path) free(ctx->output_path);- if (ctx->plugin_path) free(ctx->plugin_path);- if (ctx->include_path) free(ctx->include_path);- if (ctx->source_map_file) free(ctx->source_map_file);- if (ctx->source_map_root) free(ctx->source_map_root); free_string_array(ctx->included_files); // play safe and reset properties ctx->output_string = 0;@@ -566,12 +612,11 @@ ctx->error_text = 0; ctx->error_json = 0; ctx->error_file = 0;- ctx->input_path = 0;- ctx->output_path = 0;- ctx->include_path = 0;- ctx->source_map_file = 0;- ctx->source_map_root = 0; ctx->included_files = 0;+ // debug leaked memory+ #ifdef DEBUG_SHARED_PTR+ SharedObj::dumpMemLeaks();+ #endif // now clear the options sass_clear_options(ctx); }@@ -583,10 +628,17 @@ } Context* cpp_ctx = compiler->cpp_ctx; if (cpp_ctx) delete(cpp_ctx);- compiler->cpp_ctx = 0;+ compiler->cpp_ctx = NULL;+ compiler->c_ctx = NULL;+ compiler->root = NULL; free(compiler); } + void ADDCALL sass_delete_options (struct Sass_Options* options)+ {+ sass_clear_options(options); free(options);+ }+ // Deallocate all associated memory with file context void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx) {@@ -633,6 +685,7 @@ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_comments); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents);+ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_file_urls); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions);@@ -675,7 +728,7 @@ struct string_list* include_path = (struct string_list*) calloc(1, sizeof(struct string_list)); if (include_path == 0) return;- include_path->string = path ? sass_strdup(path) : 0;+ include_path->string = path ? sass_copy_c_string(path) : 0; struct string_list* last = options->include_paths; if (!options->include_paths) { options->include_paths = include_path;@@ -693,7 +746,7 @@ struct string_list* plugin_path = (struct string_list*) calloc(1, sizeof(struct string_list)); if (plugin_path == 0) return;- plugin_path->string = path ? sass_strdup(path) : 0;+ plugin_path->string = path ? sass_copy_c_string(path) : 0; struct string_list* last = options->plugin_paths; if (!options->plugin_paths) { options->plugin_paths = plugin_path;
libsass/src/sass_context.hpp view
@@ -2,36 +2,12 @@ #define SASS_SASS_CONTEXT_H #include "sass.h"+#include "sass.hpp" #include "context.hpp" #include "ast_fwd_decl.hpp" -// Input behaviours-enum Sass_Input_Style {- SASS_CONTEXT_NULL,- SASS_CONTEXT_FILE,- SASS_CONTEXT_DATA,- SASS_CONTEXT_FOLDER-};--// simple linked list-struct string_list {- string_list* next;- char* string;-};- // sass config options structure-struct Sass_Options {-- // Precision for fractional numbers- int precision;-- // Output style for the generated css code- // A value from above SASS_STYLE_* constants- enum Sass_Output_Style output_style;-- // Emit comments in the generated CSS indicating- // the corresponding source line.- bool source_comments;+struct Sass_Options : Sass_Output_Options { // embed sourceMappingUrl as data uri bool source_map_embed;@@ -39,6 +15,9 @@ // embed include contents in maps bool source_map_contents; + // create file urls for sources+ bool source_map_file_urls;+ // Disable sourceMappingUrl in css output bool omit_source_map_url; @@ -59,11 +38,6 @@ // information in source-maps etc. char* output_path; - // String to be used for indentation- const char* indent;- // String to be used to for line feeds- const char* linefeed;- // Colon-separated list of paths // Semicolon-separated on Windows // Maybe use array interface instead?@@ -150,7 +124,7 @@ // Sass::Context Sass::Context* cpp_ctx; // Sass::Block- Sass::Block* root;+ Sass::Block_Obj root; }; #endif
libsass/src/sass_functions.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstring> #include "util.hpp" #include "context.hpp"@@ -71,8 +72,8 @@ { Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import)); if (v == 0) return 0;- v->imp_path = imp_path ? sass_strdup(imp_path) : 0;- v->abs_path = abs_path ? sass_strdup(abs_path) : 0;+ v->imp_path = imp_path ? sass_copy_c_string(imp_path) : 0;+ v->abs_path = abs_path ? sass_copy_c_string(abs_path) : 0; v->source = source; v->srcmap = srcmap; v->error = 0;@@ -92,7 +93,7 @@ { if (import == 0) return 0; if (import->error) free(import->error);- import->error = error ? sass_strdup(error) : 0;+ import->error = error ? sass_copy_c_string(error) : 0; import->line = line ? line : -1; import->column = col ? col : -1; return import;
− libsass/src/sass_interface.cpp
@@ -1,216 +0,0 @@-#include <string>-#include <cstdlib>-#include <cstring>-#include <sstream>-#include <iostream>--#include "util.hpp"-#include "context.hpp"-#include "inspect.hpp"-#include "error_handling.hpp"-#include "sass/base.h"-#include "sass/interface.h"--#define LFEED "\n"--extern "C" {-- sass_context* sass_new_context()- { return (sass_context*) calloc(1, sizeof(sass_context)); }-- void sass_free_context(sass_context* ctx)- {- if (ctx->output_string) free(ctx->output_string);- if (ctx->source_map_string) free(ctx->source_map_string);- if (ctx->error_message) free(ctx->error_message);- if (ctx->c_functions) free(ctx->c_functions);-- Sass::free_string_array(ctx->included_files);-- free(ctx);- }-- sass_file_context* sass_new_file_context()- { return (sass_file_context*) calloc(1, sizeof(sass_file_context)); }-- void sass_free_file_context(sass_file_context* ctx)- {- if (ctx->output_string) free(ctx->output_string);- if (ctx->source_map_string) free(ctx->source_map_string);- if (ctx->error_message) free(ctx->error_message);- if (ctx->c_functions) free(ctx->c_functions);-- Sass::free_string_array(ctx->included_files);-- free(ctx);- }-- sass_folder_context* sass_new_folder_context()- { return (sass_folder_context*) calloc(1, sizeof(sass_folder_context)); }-- void sass_free_folder_context(sass_folder_context* ctx)- {- Sass::free_string_array(ctx->included_files);- free(ctx);- }-- int sass_compile(sass_context* c_ctx)- {- using namespace Sass;- try {- std::string input_path = safe_str(c_ctx->input_path);- int lastindex = static_cast<int>(input_path.find_last_of("."));- std::string output_path;- if (!c_ctx->output_path) {- if (input_path != "") {- output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";- }- }- else {- output_path = c_ctx->output_path;- }- Data_Context cpp_ctx(- (Sass_Data_Context*) 0- );- if (c_ctx->c_functions) {- Sass_Function_List this_func_data = c_ctx->c_functions;- while ((this_func_data) && (*this_func_data)) {- cpp_ctx.c_functions.push_back(*this_func_data);- ++this_func_data;- }- }- Block* root = cpp_ctx.parse();- c_ctx->output_string = cpp_ctx.render(root);- c_ctx->source_map_string = cpp_ctx.render_srcmap();- c_ctx->error_message = 0;- c_ctx->error_status = 0;-- if (copy_strings(cpp_ctx.get_included_files(true), &c_ctx->included_files, 1) == NULL)- throw(std::bad_alloc());- }- catch (Exception::InvalidSass& e) {- std::stringstream msg_stream;- msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch(std::bad_alloc& ba) {- std::stringstream msg_stream;- msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch (std::exception& e) {- std::stringstream msg_stream;- msg_stream << "Error: " << e.what() << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch (std::string& e) {- std::stringstream msg_stream;- msg_stream << "Error: " << e << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch (...) {- // couldn't find the specified file in the include paths; report an error- std::stringstream msg_stream;- msg_stream << "Unknown error occurred" << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- return 0;- }-- int sass_compile_file(sass_file_context* c_ctx)- {- using namespace Sass;- try {- std::string input_path = safe_str(c_ctx->input_path);- int lastindex = static_cast<int>(input_path.find_last_of("."));- std::string output_path;- if (!c_ctx->output_path) {- output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";- }- else {- output_path = c_ctx->output_path;- }- File_Context cpp_ctx(- (Sass_File_Context*) 0- );- if (c_ctx->c_functions) {- Sass_Function_List this_func_data = c_ctx->c_functions;- while ((this_func_data) && (*this_func_data)) {- cpp_ctx.c_functions.push_back(*this_func_data);- ++this_func_data;- }- }- Block* root = cpp_ctx.parse();- c_ctx->output_string = cpp_ctx.render(root);- c_ctx->source_map_string = cpp_ctx.render_srcmap();- c_ctx->error_message = 0;- c_ctx->error_status = 0;-- if (copy_strings(cpp_ctx.get_included_files(false), &c_ctx->included_files) == NULL)- throw(std::bad_alloc());- }- catch (Exception::InvalidSass& e) {- std::stringstream msg_stream;- msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch(std::bad_alloc& ba) {- std::stringstream msg_stream;- msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch (std::exception& e) {- std::stringstream msg_stream;- msg_stream << "Error: " << e.what() << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch (std::string& e) {- std::stringstream msg_stream;- msg_stream << "Error: " << e << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- catch (...) {- // couldn't find the specified file in the include paths; report an error- std::stringstream msg_stream;- msg_stream << "Unknown error occurred" << std::endl;- c_ctx->error_message = sass_strdup(msg_stream.str().c_str());- c_ctx->error_status = 1;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- }- return 0;- }-- int sass_compile_folder(sass_folder_context* c_ctx)- {- return 1;- }--}
libsass/src/sass_util.cpp view
@@ -1,5 +1,5 @@+#include "sass.hpp" #include "node.hpp"-#include "to_string.hpp" namespace Sass { @@ -38,7 +38,6 @@ end */ Node paths(const Node& arrs, Context& ctx) {- To_String to_string(&ctx); Node loopStart = Node::createCollection(); loopStart.collection()->push_back(Node::createCollection());
libsass/src/sass_util.hpp view
@@ -40,7 +40,7 @@ bool operator()(const Node& one, const Node& two, Node& out) const { // TODO: Is this the correct C++ interpretation? // block ||= proc {|a, b| a == b && a}- if (one == two) {+ if (nodesEqual(one, two, true)) { out = one; return true; }@@ -221,28 +221,28 @@ std::map<unsigned int, KeyType> order; - std::map<KeyType, std::vector<EnumType> > grouped;+ std::map<size_t, std::vector<EnumType> > grouped; for (typename std::vector<EnumType>::iterator enumIter = enumeration.begin(), enumIterEnd = enumeration.end(); enumIter != enumIterEnd; enumIter++) { EnumType& e = *enumIter; KeyType key = keyFunc(e); - if (grouped.find(key) == grouped.end()) {+ if (grouped.find(key->hash()) == grouped.end()) { order.insert(std::make_pair((unsigned int)order.size(), key)); std::vector<EnumType> newCollection; newCollection.push_back(e);- grouped.insert(std::make_pair(key, newCollection));+ grouped.insert(std::make_pair(key->hash(), newCollection)); } else {- std::vector<EnumType>& collection = grouped.at(key);+ std::vector<EnumType>& collection = grouped.at(key->hash()); collection.push_back(e); } } for (unsigned int index = 0; index < order.size(); index++) { KeyType& key = order.at(index);- std::vector<EnumType>& values = grouped.at(key);+ std::vector<EnumType>& values = grouped.at(key->hash()); std::pair<KeyType, std::vector<EnumType> > grouping = std::make_pair(key, values);
libsass/src/sass_values.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <cstdlib> #include <cstring> #include "util.hpp"@@ -90,7 +91,7 @@ if (v == 0) return 0; v->number.tag = SASS_NUMBER; v->number.value = val;- v->number.unit = unit ? sass_strdup(unit) : 0;+ v->number.unit = unit ? sass_copy_c_string(unit) : 0; if (v->number.unit == 0) { free(v); return 0; } return v; }@@ -113,7 +114,7 @@ if (v == 0) return 0; v->string.quoted = false; v->string.tag = SASS_STRING;- v->string.value = val ? sass_strdup(val) : 0;+ v->string.value = val ? sass_copy_c_string(val) : 0; if (v->string.value == 0) { free(v); return 0; } return v; }@@ -124,7 +125,7 @@ if (v == 0) return 0; v->string.quoted = true; v->string.tag = SASS_STRING;- v->string.value = val ? sass_strdup(val) : 0;+ v->string.value = val ? sass_copy_c_string(val) : 0; if (v->string.value == 0) { free(v); return 0; } return v; }@@ -165,7 +166,7 @@ union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->error.tag = SASS_ERROR;- v->error.message = msg ? sass_strdup(msg) : 0;+ v->error.message = msg ? sass_copy_c_string(msg) : 0; if (v->error.message == 0) { free(v); return 0; } return v; }@@ -175,7 +176,7 @@ union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->warning.tag = SASS_WARNING;- v->warning.message = msg ? sass_strdup(msg) : 0;+ v->warning.message = msg ? sass_copy_c_string(msg) : 0; if (v->warning.message == 0) { free(v); return 0; } return v; }@@ -274,58 +275,60 @@ union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* v, bool compressed, int precision) {- Memory_Manager mem;- Value* val = sass_value_to_ast_node(mem, v);- std::string str(val->to_string(compressed, precision));+ Value_Obj val = sass_value_to_ast_node(v);+ Sass_Inspect_Options options(compressed ? COMPRESSED : NESTED, precision);+ std::string str(val->to_string(options)); return sass_make_qstring(str.c_str()); } union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b) { - Sass::Value* rv = 0;- Memory_Manager mem;+ Sass::Value_Ptr rv = 0; try { - Value* lhs = sass_value_to_ast_node(mem, a);- Value* rhs = sass_value_to_ast_node(mem, b);+ Value_Obj lhs = sass_value_to_ast_node(a);+ Value_Obj rhs = sass_value_to_ast_node(b);+ struct Sass_Inspect_Options options(NESTED, 5); // see if it's a relational expression switch(op) {- case Sass_OP::EQ: return sass_make_boolean(Eval::eq(lhs, rhs));- case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs));- case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs) && !Eval::eq(lhs, rhs));- case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs));- case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs));- case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs) || Eval::eq(lhs, rhs));+ case Sass_OP::EQ: return sass_make_boolean(Eval::eq(&lhs, &rhs));+ case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(&lhs, &rhs));+ case Sass_OP::GT: return sass_make_boolean(!Eval::lt(&lhs, &rhs, "gt") && !Eval::eq(&lhs, &rhs));+ case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(&lhs, &rhs, "gte"));+ case Sass_OP::LT: return sass_make_boolean(Eval::lt(&lhs, &rhs, "lt"));+ case Sass_OP::LTE: return sass_make_boolean(Eval::lt(&lhs, &rhs, "lte") || Eval::eq(&lhs, &rhs));+ case Sass_OP::AND: return ast_node_to_sass_value(lhs->is_false() ? &lhs : &rhs);+ case Sass_OP::OR: return ast_node_to_sass_value(lhs->is_false() ? &rhs : &lhs); default: break; } if (sass_value_is_number(a) && sass_value_is_number(b)) {- const Number* l_n = dynamic_cast<const Number*>(lhs);- const Number* r_n = dynamic_cast<const Number*>(rhs);- rv = Eval::op_numbers(mem, op, *l_n, *r_n);+ Number_Ptr_Const l_n = SASS_MEMORY_CAST(Number, lhs);+ Number_Ptr_Const r_n = SASS_MEMORY_CAST(Number, rhs);+ rv = Eval::op_numbers(op, *l_n, *r_n, options); } else if (sass_value_is_number(a) && sass_value_is_color(a)) {- const Number* l_n = dynamic_cast<const Number*>(lhs);- const Color* r_c = dynamic_cast<const Color*>(rhs);- rv = Eval::op_number_color(mem, op, *l_n, *r_c);+ Number_Ptr_Const l_n = SASS_MEMORY_CAST(Number, lhs);+ Color_Ptr_Const r_c = SASS_MEMORY_CAST(Color, rhs);+ rv = Eval::op_number_color(op, *l_n, *r_c, options); } else if (sass_value_is_color(a) && sass_value_is_number(b)) {- const Color* l_c = dynamic_cast<const Color*>(lhs);- const Number* r_n = dynamic_cast<const Number*>(rhs);- rv = Eval::op_color_number(mem, op, *l_c, *r_n);+ Color_Ptr_Const l_c = SASS_MEMORY_CAST(Color, lhs);+ Number_Ptr_Const r_n = SASS_MEMORY_CAST(Number, rhs);+ rv = Eval::op_color_number(op, *l_c, *r_n, options); } else if (sass_value_is_color(a) && sass_value_is_color(b)) {- const Color* l_c = dynamic_cast<const Color*>(lhs);- const Color* r_c = dynamic_cast<const Color*>(rhs);- rv = Eval::op_colors(mem, op, *l_c, *r_c);+ Color_Ptr_Const l_c = SASS_MEMORY_CAST(Color, lhs);+ Color_Ptr_Const r_c = SASS_MEMORY_CAST(Color, rhs);+ rv = Eval::op_colors(op, *l_c, *r_c, options); } else /* convert other stuff to string and apply operation */ {- Value* l_v = dynamic_cast<Value*>(lhs);- Value* r_v = dynamic_cast<Value*>(rhs);- rv = Eval::op_strings(mem, op, *l_v, *r_v);+ Value_Ptr l_v = SASS_MEMORY_CAST(Value, lhs);+ Value_Ptr r_v = SASS_MEMORY_CAST(Value, rhs);+ rv = Eval::op_strings(op, *l_v, *r_v, options); } // ToDo: maybe we should should return null value?
libsass/src/source_map.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <string> #include <sstream> #include <iostream>@@ -16,7 +17,7 @@ std::string SourceMap::render_srcmap(Context &ctx) { - const bool include_sources = ctx.c_options->source_map_contents;+ const bool include_sources = ctx.c_options.source_map_contents; const std::vector<std::string> links = ctx.srcmap_links; const std::vector<Resource>& sources(ctx.resources); @@ -24,20 +25,32 @@ json_append_member(json_srcmap, "version", json_mknumber(3)); + const char *include = file.c_str();+ JsonNode *json_include = json_mkstring(include);+ json_append_member(json_srcmap, "file", json_include);+ // pass-through sourceRoot option if (!ctx.source_map_root.empty()) { JsonNode* root = json_mkstring(ctx.source_map_root.c_str()); json_append_member(json_srcmap, "sourceRoot", root); } - const char *include = file.c_str();- JsonNode *json_include = json_mkstring(include);- json_append_member(json_srcmap, "file", json_include);- JsonNode *json_includes = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) {- const char *include = links[source_index[i]].c_str();- JsonNode *json_include = json_mkstring(include);+ std::string include(links[source_index[i]]);+ if (ctx.c_options.source_map_file_urls) {+ include = File::rel2abs(include);+ // check for windows abs path+ if (include[0] == '/') {+ // ends up with three slashes+ include = "file://" + include;+ } else {+ // needs an additional slash+ include = "file:///" + include;+ }+ }+ const char* inc = include.c_str();+ JsonNode *json_include = json_mkstring(inc); json_append_element(json_includes, json_include); } json_append_member(json_srcmap, "sources", json_includes);@@ -53,15 +66,15 @@ json_append_member(json_srcmap, "sourcesContent", json_contents); } - std::string mappings = serialize_mappings();- JsonNode *json_mappings = json_mkstring(mappings.c_str());- json_append_member(json_srcmap, "mappings", json_mappings);- JsonNode *json_names = json_mkarray(); // so far we have no implementation for names // no problem as we do not alter any identifiers json_append_member(json_srcmap, "names", json_names); + std::string mappings = serialize_mappings();+ JsonNode *json_mappings = json_mkstring(mappings.c_str());+ json_append_member(json_srcmap, "mappings", json_mappings);+ char *str = json_stringify(json_srcmap, "\t"); std::string result = std::string(str); free(str);@@ -159,12 +172,12 @@ current_position += offset; } - void SourceMap::add_open_mapping(AST_Node* node)+ void SourceMap::add_open_mapping(const AST_Node_Ptr node) { mappings.push_back(Mapping(node->pstate(), current_position)); } - void SourceMap::add_close_mapping(AST_Node* node)+ void SourceMap::add_close_mapping(const AST_Node_Ptr node) { mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position)); }
libsass/src/source_map.hpp view
@@ -28,8 +28,8 @@ void prepend(const Offset& offset); void append(const OutputBuffer& out); void prepend(const OutputBuffer& out);- void add_open_mapping(AST_Node* node);- void add_close_mapping(AST_Node* node);+ void add_open_mapping(const AST_Node_Ptr node);+ void add_close_mapping(const AST_Node_Ptr node); std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate);
+ libsass/src/subset_map.cpp view
@@ -0,0 +1,57 @@+#include "sass.hpp"+#include "ast.hpp"+#include "subset_map.hpp"++namespace Sass {++ void Subset_Map::put(const Compound_Selector_Obj& sel, const Subset_Map_Val& value)+ {+ if (sel->empty()) throw std::runtime_error("internal error: subset map keys may not be empty");+ size_t index = values_.size();+ values_.push_back(value);+ for (size_t i = 0, S = sel->length(); i < S; ++i)+ {+ hash_[(*sel)[i]].push_back(std::make_pair(&sel, index));+ }+ }++ std::vector<Subset_Map_Val> Subset_Map::get_kv(const Compound_Selector_Obj& sel)+ {+ // std::vector<Subset_Map_Key> s = sel->to_str_vec();+ // std::set<std::string> dict(s.begin(), s.end());+ std::unordered_set<Simple_Selector_Obj, HashSimpleSelector, CompareSimpleSelector> dict(sel->begin(), sel->end());+ std::vector<size_t> indices;+ for (size_t i = 0, S = sel->length(); i < S; ++i) {+ if (!hash_.count((*sel)[i])) {+ continue;+ }+ const std::vector<std::pair<Compound_Selector_Obj, size_t> >& subsets = hash_[(*sel)[i]];+ for (const std::pair<Compound_Selector_Obj, size_t>& item : subsets) {+ bool include = true;+ for (const Simple_Selector_Obj& it : item.first->elements()) {+ auto found = dict.find(it);+ if (found == dict.end()) {+ include = false;+ break;+ }+ }+ if (include) indices.push_back(item.second);+ }+ }+ sort(indices.begin(), indices.end());+ std::vector<size_t>::iterator indices_end = unique(indices.begin(), indices.end());+ indices.resize(distance(indices.begin(), indices_end));++ std::vector<Subset_Map_Val> results;+ for (size_t i = 0, S = indices.size(); i < S; ++i) {+ results.push_back(values_[indices[i]]);+ }+ return results;+ }++ std::vector<Subset_Map_Val> Subset_Map::get_v(const Compound_Selector_Obj& sel)+ {+ return get_kv(sel);+ }++}
libsass/src/subset_map.hpp view
@@ -7,7 +7,9 @@ #include <algorithm> #include <iterator> +#include "ast_fwd_decl.hpp" + // #include <iostream> // #include <sstream> // template<typename T>@@ -56,89 +58,18 @@ namespace Sass { - template<typename F, typename S, typename T>- struct triple {- F first;- S second;- T third;-- triple(const F& f, const S& s, const T& t) : first(f), second(s), third(t) { }- };-- template<typename F, typename S, typename T>- triple<F, S, T> make_triple(const F& f, const S& s, const T& t)- { return triple<F, S, T>(f, s, t); }-- template<typename K, typename V> class Subset_Map { private:- std::vector<V> values_;- std::map<K, std::vector<triple<std::vector<K>, std::set<K>, size_t> > > hash_;+ std::vector<Subset_Map_Val> values_;+ std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> > > hash_; public:- void put(const std::vector<K>& s, const V& value);- std::vector<std::pair<V, std::vector<K> > > get_kv(const std::vector<K>& s);- std::vector<V> get_v(const std::vector<K>& s);+ void put(const Compound_Selector_Obj& sel, const Subset_Map_Val& value);+ std::vector<Subset_Map_Val> get_kv(const Compound_Selector_Obj& s);+ std::vector<Subset_Map_Val> get_v(const Compound_Selector_Obj& s); bool empty() { return values_.empty(); } void clear() { values_.clear(); hash_.clear(); }- const std::vector<V> values(void) { return values_; }+ const std::vector<Subset_Map_Val> values(void) { return values_; } };-- template<typename K, typename V>- void Subset_Map<K, V>::put(const std::vector<K>& s, const V& value)- {- if (s.empty()) throw "internal error: subset map keys may not be empty";- size_t index = values_.size();- values_.push_back(value);- std::set<K> ss;- for (size_t i = 0, S = s.size(); i < S; ++i)- { ss.insert(s[i]); }- for (size_t i = 0, S = s.size(); i < S; ++i)- {- hash_[s[i]];- hash_[s[i]].push_back(make_triple(s, ss, index));- }- }-- template<typename K, typename V>- std::vector<std::pair<V, std::vector<K> > > Subset_Map<K, V>::get_kv(const std::vector<K>& s)- {- std::vector<K> sorted = s;- sort(sorted.begin(), sorted.end());- std::vector<std::pair<size_t, std::vector<K> > > indices;- for (size_t i = 0, S = s.size(); i < S; ++i) {- if (!hash_.count(s[i])) {- continue;- }- std::vector<triple<std::vector<K>, std::set<K>, size_t> > subsets = hash_[s[i]];- // std::cerr << "length of subsets: " << subsets.size() << std::endl;- for (size_t j = 0, T = subsets.size(); j < T; ++j) {- if (!includes(sorted.begin(), sorted.end(), subsets[j].second.begin(), subsets[j].second.end())) {- // std::cout << vector_to_string(s) << " doesn't include " << set_to_string(subsets[j].second) << std::endl;- continue;- }- indices.push_back(std::make_pair(subsets[j].third, subsets[j].first));- // std::cerr << "pushed " << subsets[j].third << " and " << vector_to_string(subsets[j].first) << " onto indices" << std::endl;- }- }- sort(indices.begin(), indices.end());- typename std::vector<std::pair<size_t, std::vector<K> > >::iterator indices_end = unique(indices.begin(), indices.end());- indices.resize(distance(indices.begin(), indices_end));-- std::vector<std::pair<V, std::vector<K> > > results;- for (size_t i = 0, S = indices.size(); i < S; ++i) {- results.push_back(std::make_pair(values_[indices[i].first], indices[i].second));- }- return results;- }-- template<typename K, typename V>- std::vector<V> Subset_Map<K, V>::get_v(const std::vector<K>& s)- {- std::vector<std::pair<V, std::vector<K> > > kvs = get_kv(s);- std::vector<V> results;- for (size_t i = 0, S = kvs.size(); i < S; ++i) results.push_back(kvs[i].first);- return results;- } }
libsass/src/to_c.cpp view
@@ -1,27 +1,28 @@+#include "sass.hpp" #include "to_c.hpp" #include "ast.hpp" namespace Sass { - union Sass_Value* To_C::fallback_impl(AST_Node* n)+ union Sass_Value* To_C::fallback_impl(AST_Node_Ptr n) { return sass_make_error("unknown type for C-API"); } - union Sass_Value* To_C::operator()(Boolean* b)+ union Sass_Value* To_C::operator()(Boolean_Ptr b) { return sass_make_boolean(b->value()); } - union Sass_Value* To_C::operator()(Number* n)+ union Sass_Value* To_C::operator()(Number_Ptr n) { return sass_make_number(n->value(), n->unit().c_str()); } - union Sass_Value* To_C::operator()(Custom_Warning* w)+ union Sass_Value* To_C::operator()(Custom_Warning_Ptr w) { return sass_make_warning(w->message().c_str()); } - union Sass_Value* To_C::operator()(Custom_Error* e)+ union Sass_Value* To_C::operator()(Custom_Error_Ptr e) { return sass_make_error(e->message().c_str()); } - union Sass_Value* To_C::operator()(Color* c)+ union Sass_Value* To_C::operator()(Color_Ptr c) { return sass_make_color(c->r(), c->g(), c->b(), c->a()); } - union Sass_Value* To_C::operator()(String_Constant* s)+ union Sass_Value* To_C::operator()(String_Constant_Ptr s) { if (s->quote_mark()) { return sass_make_qstring(s->value().c_str());@@ -30,10 +31,10 @@ } } - union Sass_Value* To_C::operator()(String_Quoted* s)+ union Sass_Value* To_C::operator()(String_Quoted_Ptr s) { return sass_make_qstring(s->value().c_str()); } - union Sass_Value* To_C::operator()(List* l)+ union Sass_Value* To_C::operator()(List_Ptr l) { union Sass_Value* v = sass_make_list(l->length(), l->separator()); for (size_t i = 0, L = l->length(); i < L; ++i) {@@ -42,7 +43,7 @@ return v; } - union Sass_Value* To_C::operator()(Map* m)+ union Sass_Value* To_C::operator()(Map_Ptr m) { union Sass_Value* v = sass_make_map(m->length()); int i = 0;@@ -54,7 +55,7 @@ return v; } - union Sass_Value* To_C::operator()(Arguments* a)+ union Sass_Value* To_C::operator()(Arguments_Ptr a) { union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA); for (size_t i = 0, L = a->length(); i < L; ++i) {@@ -63,11 +64,11 @@ return v; } - union Sass_Value* To_C::operator()(Argument* a)+ union Sass_Value* To_C::operator()(Argument_Ptr a) { return a->value()->perform(this); } // not strictly necessary because of the fallback- union Sass_Value* To_C::operator()(Null* n)+ union Sass_Value* To_C::operator()(Null_Ptr n) { return sass_make_null(); } };
libsass/src/to_c.hpp view
@@ -8,31 +8,29 @@ namespace Sass { class To_C : public Operation_CRTP<union Sass_Value*, To_C> {- // import all the class-specific methods and override as desired- using Operation<union Sass_Value*>::operator(); // override this to define a catch-all- union Sass_Value* fallback_impl(AST_Node* n);+ union Sass_Value* fallback_impl(AST_Node_Ptr n); public: To_C() { }- virtual ~To_C() { }+ ~To_C() { } - union Sass_Value* operator()(Boolean*);- union Sass_Value* operator()(Number*);- union Sass_Value* operator()(Color*);- union Sass_Value* operator()(String_Constant*);- union Sass_Value* operator()(String_Quoted*);- union Sass_Value* operator()(Custom_Warning*);- union Sass_Value* operator()(Custom_Error*);- union Sass_Value* operator()(List*);- union Sass_Value* operator()(Map*);- union Sass_Value* operator()(Null*);- union Sass_Value* operator()(Arguments*);- union Sass_Value* operator()(Argument*);+ union Sass_Value* operator()(Boolean_Ptr);+ union Sass_Value* operator()(Number_Ptr);+ union Sass_Value* operator()(Color_Ptr);+ union Sass_Value* operator()(String_Constant_Ptr);+ union Sass_Value* operator()(String_Quoted_Ptr);+ union Sass_Value* operator()(Custom_Warning_Ptr);+ union Sass_Value* operator()(Custom_Error_Ptr);+ union Sass_Value* operator()(List_Ptr);+ union Sass_Value* operator()(Map_Ptr);+ union Sass_Value* operator()(Null_Ptr);+ union Sass_Value* operator()(Arguments_Ptr);+ union Sass_Value* operator()(Argument_Ptr); // dispatch to fallback implementation- union Sass_Value* fallback(AST_Node* x)+ union Sass_Value* fallback(AST_Node_Ptr x) { return fallback_impl(x); } };
− libsass/src/to_string.cpp
@@ -1,48 +0,0 @@-#include <cmath>-#include <sstream>-#include <iomanip>-#include <iostream>--#include "ast.hpp"-#include "inspect.hpp"-#include "context.hpp"-#include "to_string.hpp"--namespace Sass {-- To_String::To_String(Context* ctx, bool in_declaration, bool in_debug)- : ctx(ctx), in_declaration(in_declaration), in_debug(in_debug) { }- To_String::~To_String() { }-- inline std::string To_String::fallback_impl(AST_Node* n)- {- Emitter emitter(ctx);- Inspect i(emitter);- i.in_declaration = in_declaration;- i.in_debug = in_debug;- if (n) n->perform(&i);- return i.get_buffer();- }-- inline std::string To_String::operator()(String_Schema* s)- {- std::string acc("");- for (size_t i = 0, L = s->length(); i < L; ++i) {- acc += s->elements()[i]->perform(this);- }- return acc;- }-- inline std::string To_String::operator()(String_Quoted* s)- {- return s->value();- }-- inline std::string To_String::operator()(String_Constant* s)- {- return s->value();- }-- inline std::string To_String::operator()(Null* n)- { return in_debug ? "null" : ""; }-}
− libsass/src/to_string.hpp
@@ -1,38 +0,0 @@-#ifndef SASS_TO_STRING_H-#define SASS_TO_STRING_H--#include <string>--#include "operation.hpp"--namespace Sass {-- class Context;- class Null;-- class To_String : public Operation_CRTP<std::string, To_String> {- // import all the class-specific methods and override as desired- using Operation<std::string>::operator();- // override this to define a catch-all- std::string fallback_impl(AST_Node* n);-- Context* ctx;- bool in_declaration;- bool in_debug;-- public:-- To_String(Context* ctx = 0, bool in_declaration = true, bool in_debug = false);- virtual ~To_String();-- std::string operator()(Null* n);- std::string operator()(String_Schema*);- std::string operator()(String_Quoted*);- std::string operator()(String_Constant*);-- template <typename U>- std::string fallback(U n) { return fallback_impl(n); }- };-}--#endif
libsass/src/to_value.cpp view
@@ -1,10 +1,10 @@+#include "sass.hpp" #include "ast.hpp" #include "to_value.hpp"-#include "to_string.hpp" namespace Sass { - Value* To_Value::fallback_impl(AST_Node* n)+ Value_Ptr To_Value::fallback_impl(AST_Node_Ptr n) { // throw a runtime error if this happens // we want a well defined set of possible nodes@@ -14,96 +14,94 @@ } // Custom_Error is a valid value- Value* To_Value::operator()(Custom_Error* e)+ Value_Ptr To_Value::operator()(Custom_Error_Ptr e) { return e; } // Custom_Warning is a valid value- Value* To_Value::operator()(Custom_Warning* w)+ Value_Ptr To_Value::operator()(Custom_Warning_Ptr w) { return w; } // Boolean is a valid value- Value* To_Value::operator()(Boolean* b)+ Value_Ptr To_Value::operator()(Boolean_Ptr b) { return b; } // Number is a valid value- Value* To_Value::operator()(Number* n)+ Value_Ptr To_Value::operator()(Number_Ptr n) { return n; } // Color is a valid value- Value* To_Value::operator()(Color* c)+ Value_Ptr To_Value::operator()(Color_Ptr c) { return c; } // String_Constant is a valid value- Value* To_Value::operator()(String_Constant* s)+ Value_Ptr To_Value::operator()(String_Constant_Ptr s) { return s; } // String_Quoted is a valid value- Value* To_Value::operator()(String_Quoted* s)+ Value_Ptr To_Value::operator()(String_Quoted_Ptr s) { return s; } // List is a valid value- Value* To_Value::operator()(List* l)+ Value_Ptr To_Value::operator()(List_Ptr l) {- List* ll = SASS_MEMORY_NEW(mem, List,+ List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), l->length(), l->separator(), l->is_arglist()); for (size_t i = 0, L = l->length(); i < L; ++i) {- *ll << (*l)[i]->perform(this);+ ll->append((*l)[i]->perform(this)); }- return ll;+ return ll.detach(); } // Map is a valid value- Value* To_Value::operator()(Map* m)+ Value_Ptr To_Value::operator()(Map_Ptr m) { return m; } // Null is a valid value- Value* To_Value::operator()(Null* n)+ Value_Ptr To_Value::operator()(Null_Ptr n) { return n; } // Argument returns its value- Value* To_Value::operator()(Argument* arg)+ Value_Ptr To_Value::operator()(Argument_Ptr arg) { if (!arg->name().empty()) return 0; return arg->value()->perform(this); } // Selector_List is converted to a string- Value* To_Value::operator()(Selector_List* s)+ Value_Ptr To_Value::operator()(Selector_List_Ptr s) {- To_String to_string(&ctx);- return SASS_MEMORY_NEW(mem, String_Quoted,+ return SASS_MEMORY_NEW(String_Quoted, s->pstate(),- s->perform(&to_string));+ s->to_string(ctx.c_options)); } // Binary_Expression is converted to a string- Value* To_Value::operator()(Binary_Expression* s)+ Value_Ptr To_Value::operator()(Binary_Expression_Ptr s) {- To_String to_string(&ctx);- return SASS_MEMORY_NEW(mem, String_Quoted,+ return SASS_MEMORY_NEW(String_Quoted, s->pstate(),- s->perform(&to_string));+ s->to_string(ctx.c_options)); } };
libsass/src/to_value.hpp view
@@ -7,42 +7,41 @@ namespace Sass { - class To_Value : public Operation_CRTP<Value*, To_Value> {+ class To_Value : public Operation_CRTP<Value_Ptr, To_Value> { - Value* fallback_impl(AST_Node* n);+ Value_Ptr fallback_impl(AST_Node_Ptr n); private: Context& ctx;- Memory_Manager& mem; public: - To_Value(Context& ctx, Memory_Manager& mem)- : ctx(ctx), mem(mem)+ To_Value(Context& ctx)+ : ctx(ctx) { }- virtual ~To_Value() { }- using Operation<Value*>::operator();+ ~To_Value() { }+ using Operation<Value_Ptr>::operator(); - Value* operator()(Argument*);- Value* operator()(Boolean*);- Value* operator()(Number*);- Value* operator()(Color*);- Value* operator()(String_Constant*);- Value* operator()(String_Quoted*);- Value* operator()(Custom_Warning*);- Value* operator()(Custom_Error*);- Value* operator()(List*);- Value* operator()(Map*);- Value* operator()(Null*);+ Value_Ptr operator()(Argument_Ptr);+ Value_Ptr operator()(Boolean_Ptr);+ Value_Ptr operator()(Number_Ptr);+ Value_Ptr operator()(Color_Ptr);+ Value_Ptr operator()(String_Constant_Ptr);+ Value_Ptr operator()(String_Quoted_Ptr);+ Value_Ptr operator()(Custom_Warning_Ptr);+ Value_Ptr operator()(Custom_Error_Ptr);+ Value_Ptr operator()(List_Ptr);+ Value_Ptr operator()(Map_Ptr);+ Value_Ptr operator()(Null_Ptr); // convert to string via `To_String`- Value* operator()(Selector_List*);- Value* operator()(Binary_Expression*);+ Value_Ptr operator()(Selector_List_Ptr);+ Value_Ptr operator()(Binary_Expression_Ptr); // fallback throws error template <typename U>- Value* fallback(U x) { return fallback_impl(x); }+ Value_Ptr fallback(U x) { return fallback_impl(x); } }; }
libsass/src/units.cpp view
@@ -1,10 +1,7 @@+#include "sass.hpp" #include <stdexcept> #include "units.hpp" -#ifdef IN-#undef IN-#endif- namespace Sass { /* the conversion matrix can be readed the following way */@@ -47,93 +44,133 @@ const double resolution_conversion_factors[3][3] = { /* dpi dpcm dppx */- /* dpi */ { 1, 2.54, 96 },- /* dpcm */ { 1/2.54, 1, 96/2.54 },- /* dppx */ { 1/96.0, 2.54/96, 1 }+ /* dpi */ { 1, 1/2.54, 1/96.0 },+ /* dpcm */ { 2.54, 1, 2.54/96 },+ /* dppx */ { 96, 96/2.54, 1 } }; - SassUnitType get_unit_type(SassUnit unit)+ UnitClass get_unit_type(UnitType unit) { switch (unit & 0xFF00) {- case SassUnitType::LENGTH: return SassUnitType::LENGTH; break;- case SassUnitType::ANGLE: return SassUnitType::ANGLE; break;- case SassUnitType::TIME: return SassUnitType::TIME; break;- case SassUnitType::FREQUENCY: return SassUnitType::FREQUENCY; break;- case SassUnitType::RESOLUTION: return SassUnitType::RESOLUTION; break;- default: return SassUnitType::INCOMMENSURABLE; break;+ case UnitClass::LENGTH: return UnitClass::LENGTH; break;+ case UnitClass::ANGLE: return UnitClass::ANGLE; break;+ case UnitClass::TIME: return UnitClass::TIME; break;+ case UnitClass::FREQUENCY: return UnitClass::FREQUENCY; break;+ case UnitClass::RESOLUTION: return UnitClass::RESOLUTION; break;+ default: return UnitClass::INCOMMENSURABLE; break; } }; - SassUnit string_to_unit(const std::string& s)+ std::string get_unit_class(UnitType unit) {+ switch (unit & 0xFF00)+ {+ case UnitClass::LENGTH: return "LENGTH"; break;+ case UnitClass::ANGLE: return "ANGLE"; break;+ case UnitClass::TIME: return "TIME"; break;+ case UnitClass::FREQUENCY: return "FREQUENCY"; break;+ case UnitClass::RESOLUTION: return "RESOLUTION"; break;+ default: return "INCOMMENSURABLE"; break;+ }+ };++ UnitType string_to_unit(const std::string& s)+ { // size units- if (s == "px") return SassUnit::PX;- else if (s == "pt") return SassUnit::PT;- else if (s == "pc") return SassUnit::PC;- else if (s == "mm") return SassUnit::MM;- else if (s == "cm") return SassUnit::CM;- else if (s == "in") return SassUnit::IN;+ if (s == "px") return UnitType::PX;+ else if (s == "pt") return UnitType::PT;+ else if (s == "pc") return UnitType::PC;+ else if (s == "mm") return UnitType::MM;+ else if (s == "cm") return UnitType::CM;+ else if (s == "in") return UnitType::IN; // angle units- else if (s == "deg") return SassUnit::DEG;- else if (s == "grad") return SassUnit::GRAD;- else if (s == "rad") return SassUnit::RAD;- else if (s == "turn") return SassUnit::TURN;+ else if (s == "deg") return UnitType::DEG;+ else if (s == "grad") return UnitType::GRAD;+ else if (s == "rad") return UnitType::RAD;+ else if (s == "turn") return UnitType::TURN; // time units- else if (s == "s") return SassUnit::SEC;- else if (s == "ms") return SassUnit::MSEC;+ else if (s == "s") return UnitType::SEC;+ else if (s == "ms") return UnitType::MSEC; // frequency units- else if (s == "Hz") return SassUnit::HERTZ;- else if (s == "kHz") return SassUnit::KHERTZ;+ else if (s == "Hz") return UnitType::HERTZ;+ else if (s == "kHz") return UnitType::KHERTZ; // resolutions units- else if (s == "dpi") return SassUnit::DPI;- else if (s == "dpcm") return SassUnit::DPCM;- else if (s == "dppx") return SassUnit::DPPX;+ else if (s == "dpi") return UnitType::DPI;+ else if (s == "dpcm") return UnitType::DPCM;+ else if (s == "dppx") return UnitType::DPPX; // for unknown units- else return SassUnit::UNKNOWN;+ else return UnitType::UNKNOWN; } - const char* unit_to_string(SassUnit unit)+ const char* unit_to_string(UnitType unit) { switch (unit) { // size units- case SassUnit::PX: return "px"; break;- case SassUnit::PT: return "pt"; break;- case SassUnit::PC: return "pc"; break;- case SassUnit::MM: return "mm"; break;- case SassUnit::CM: return "cm"; break;- case SassUnit::IN: return "in"; break;+ case UnitType::PX: return "px"; break;+ case UnitType::PT: return "pt"; break;+ case UnitType::PC: return "pc"; break;+ case UnitType::MM: return "mm"; break;+ case UnitType::CM: return "cm"; break;+ case UnitType::IN: return "in"; break; // angle units- case SassUnit::DEG: return "deg"; break;- case SassUnit::GRAD: return "grad"; break;- case SassUnit::RAD: return "rad"; break;- case SassUnit::TURN: return "turn"; break;+ case UnitType::DEG: return "deg"; break;+ case UnitType::GRAD: return "grad"; break;+ case UnitType::RAD: return "rad"; break;+ case UnitType::TURN: return "turn"; break; // time units- case SassUnit::SEC: return "s"; break;- case SassUnit::MSEC: return "ms"; break;+ case UnitType::SEC: return "s"; break;+ case UnitType::MSEC: return "ms"; break; // frequency units- case SassUnit::HERTZ: return "Hz"; break;- case SassUnit::KHERTZ: return "kHz"; break;+ case UnitType::HERTZ: return "Hz"; break;+ case UnitType::KHERTZ: return "kHz"; break; // resolutions units- case SassUnit::DPI: return "dpi"; break;- case SassUnit::DPCM: return "dpcm"; break;- case SassUnit::DPPX: return "dppx"; break;+ case UnitType::DPI: return "dpi"; break;+ case UnitType::DPCM: return "dpcm"; break;+ case UnitType::DPPX: return "dppx"; break; // for unknown units default: return ""; break; } } + std::string unit_to_class(const std::string& s)+ {+ if (s == "px") return "LENGTH";+ else if (s == "pt") return "LENGTH";+ else if (s == "pc") return "LENGTH";+ else if (s == "mm") return "LENGTH";+ else if (s == "cm") return "LENGTH";+ else if (s == "in") return "LENGTH";+ // angle units+ else if (s == "deg") return "ANGLE";+ else if (s == "grad") return "ANGLE";+ else if (s == "rad") return "ANGLE";+ else if (s == "turn") return "ANGLE";+ // time units+ else if (s == "s") return "TIME";+ else if (s == "ms") return "TIME";+ // frequency units+ else if (s == "Hz") return "FREQUENCY";+ else if (s == "kHz") return "FREQUENCY";+ // resolutions units+ else if (s == "dpi") return "RESOLUTION";+ else if (s == "dpcm") return "RESOLUTION";+ else if (s == "dppx") return "RESOLUTION";+ // for unknown units+ return "CUSTOM:" + s;+ }+ // throws incompatibleUnits exceptions double conversion_factor(const std::string& s1, const std::string& s2, bool strict) { // assert for same units if (s1 == s2) return 1; // get unit enum from string- SassUnit u1 = string_to_unit(s1);- SassUnit u2 = string_to_unit(s2);+ UnitType u1 = string_to_unit(s1);+ UnitType u2 = string_to_unit(s2); // query unit group types- SassUnitType t1 = get_unit_type(u1);- SassUnitType t2 = get_unit_type(u2);+ UnitClass t1 = get_unit_type(u1);+ UnitClass t2 = get_unit_type(u2); // get absolute offset // used for array acces size_t i1 = u1 - t1;@@ -144,17 +181,17 @@ // only process known units if (u1 != UNKNOWN && u2 != UNKNOWN) { switch (t1) {- case SassUnitType::LENGTH: return size_conversion_factors[i1][i2]; break;- case SassUnitType::ANGLE: return angle_conversion_factors[i1][i2]; break;- case SassUnitType::TIME: return time_conversion_factors[i1][i2]; break;- case SassUnitType::FREQUENCY: return frequency_conversion_factors[i1][i2]; break;- case SassUnitType::RESOLUTION: return resolution_conversion_factors[i1][i2]; break;+ case UnitClass::LENGTH: return size_conversion_factors[i1][i2]; break;+ case UnitClass::ANGLE: return angle_conversion_factors[i1][i2]; break;+ case UnitClass::TIME: return time_conversion_factors[i1][i2]; break;+ case UnitClass::FREQUENCY: return frequency_conversion_factors[i1][i2]; break;+ case UnitClass::RESOLUTION: return resolution_conversion_factors[i1][i2]; break; // ToDo: should we throw error here?- case SassUnitType::INCOMMENSURABLE: return 0; break;+ case UnitClass::INCOMMENSURABLE: return 0; break; } } // fallback- return 1;+ return 0; } }
libsass/src/units.hpp view
@@ -7,9 +7,9 @@ namespace Sass { - const double PI = acos(-1);+ const double PI = std::acos(-1); - enum SassUnitType {+ enum UnitClass { LENGTH = 0x000, ANGLE = 0x100, TIME = 0x200,@@ -18,10 +18,10 @@ INCOMMENSURABLE = 0x500 }; - enum SassUnit {+ enum UnitType { // size units- IN = SassUnitType::LENGTH,+ IN = UnitClass::LENGTH, CM, PC, MM,@@ -58,9 +58,11 @@ extern const double frequency_conversion_factors[2][2]; extern const double resolution_conversion_factors[3][3]; - enum SassUnit string_to_unit(const std::string&);- const char* unit_to_string(SassUnit unit);- enum SassUnitType get_unit_type(SassUnit unit);+ enum Sass::UnitType string_to_unit(const std::string&);+ const char* unit_to_string(Sass::UnitType unit);+ enum Sass::UnitClass get_unit_type(Sass::UnitType unit);+ std::string get_unit_class(Sass::UnitType unit);+ std::string unit_to_class(const std::string&); // throws incompatibleUnits exceptions double conversion_factor(const std::string&, const std::string&, bool = true); @@ -68,14 +70,16 @@ { public: const char* msg;- incompatibleUnits(SassUnit a, SassUnit b)+ incompatibleUnits(Sass::UnitType a, Sass::UnitType b) : exception() { std::stringstream ss; ss << "Incompatible units: "; ss << "'" << unit_to_string(a) << "' and "; ss << "'" << unit_to_string(b) << "'";- msg = ss.str().c_str();+ // hold on to string on stack!+ std::string str(ss.str());+ msg = str.c_str(); } virtual const char* what() const throw() {
libsass/src/utf8/unchecked.h view
@@ -32,13 +32,13 @@ namespace utf8 {- namespace unchecked + namespace unchecked { template <typename octet_iterator> octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) // one octet- *(result++) = static_cast<uint8_t>(cp); + *(result++) = static_cast<uint8_t>(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0); *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);@@ -70,28 +70,28 @@ cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3:- ++it; + ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it;- cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp += (utf8::internal::mask8(*it) << 6) & 0xfff; ++it;- cp += (*it) & 0x3f; + cp += (*it) & 0x3f; break; } ++it;- return cp; + return cp; } template <typename octet_iterator> uint32_t peek_next(octet_iterator it) {- return utf8::unchecked::next(it); + return utf8::unchecked::next(it); } template <typename octet_iterator>@@ -121,14 +121,14 @@ distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits<octet_iterator>::difference_type dist;- for (dist = 0; first < last; ++dist) + for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } template <typename u16bit_iterator, typename octet_iterator> octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)- { + { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first@@ -138,7 +138,7 @@ } result = utf8::unchecked::append(cp, result); }- return result; + return result; } template <typename u16bit_iterator, typename octet_iterator>@@ -176,7 +176,7 @@ // The iterator class template <typename octet_iterator>- class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { octet_iterator it; public: iterator () {}@@ -188,15 +188,15 @@ octet_iterator temp = it; return utf8::unchecked::next(temp); }- bool operator == (const iterator& rhs) const - { + bool operator == (const iterator& rhs) const+ { return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); }- iterator& operator ++ () + iterator& operator ++ () { ::std::advance(it, utf8::internal::sequence_length(it)); return *this;@@ -206,7 +206,7 @@ iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp;- } + } iterator& operator -- () { utf8::unchecked::prior(it);@@ -221,7 +221,7 @@ }; // class iterator } // namespace utf8::unchecked-} // namespace utf8 +} // namespace utf8 #endif // header guard
libsass/src/utf8_string.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include <string> #include <vector> #include <cstdlib>@@ -68,6 +69,8 @@ } } + #ifdef _WIN32+ // utf16 functions using std::wstring; @@ -92,6 +95,8 @@ back_inserter(utf16)); return utf16; }++ #endif } }
libsass/src/util.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "sass.h" #include "ast.hpp" #include "util.hpp"@@ -11,38 +12,17 @@ namespace Sass { - #define out_of_memory() do { \- std::cerr << "Out of memory.\n"; \- exit(EXIT_FAILURE); \- } while (0)-- double round(double val)+ double round(double val, size_t precision) {+ // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93+ if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);+ else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val); // work around some compiler issue // cygwin has it not defined in std using namespace std;-- // This was later repatched in 3.4.20- // which is as yet unreleased.- // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93- if (fmod(val, 1) - 0.5 > -0.00001) return std::ceil(val); return ::round(val);-- // Use this version once sass-spec is at 3.4.20- // if (fmod(val, 1) - 0.5 > -0.00001) return ::round(val);- // return value > 0 ? std::ceil(val) : std::floor(val); } - /* Sadly, sass_strdup is not portable. */- char *sass_strdup(const char *str)- {- char *ret = (char*) malloc(strlen(str) + 1);- if (ret == NULL)- out_of_memory();- strcpy(ret, str);- return ret;- }- /* Locale unspecific atof function. */ double sass_atof(const char *str) {@@ -55,7 +35,7 @@ if(found != NULL){ // substitution is required. perform the substitution on a copy // of the string. This is slower but it is thread safe.- char *copy = sass_strdup(str);+ char *copy = sass_copy_c_string(str); *(copy + (found - str)) = separator; double res = atof(copy); free(copy);@@ -104,94 +84,6 @@ return *array = arr; } - std::string string_eval_escapes(const std::string& s)- {-- std::string out("");- bool esc = false;- for (size_t i = 0, L = s.length(); i < L; ++i) {- if(s[i] == '\\' && esc == false) {- esc = true;-- // escape length- size_t len = 1;-- // parse as many sequence chars as possible- // ToDo: Check if ruby aborts after possible max- while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;-- // hex string?- if (len > 1) {-- // convert the extracted hex string to code point value- // ToDo: Maybe we could do this without creating a substring- uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);-- if (cp == 0) cp = 0xFFFD;-- // assert invalid code points- if (cp >= 1) {-- // use a very simple approach to convert via utf8 lib- // maybe there is a more elegant way; maybe we shoud- // convert the whole output from string to a stream!?- // allocate memory for utf8 char and convert to utf8- unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);- for(size_t m = 0; u[m] && m < 5; m++) out.push_back(u[m]);-- // skip some more chars?- i += len - 1; esc = false;- if (cp == 10) out += ' ';-- }-- }-- }- else {- out += s[i];- esc = false;- }- }- return out;-- }-- // double escape every escape sequences- // escape unescaped quotes and backslashes- std::string string_escape(const std::string& str)- {- std::string out("");- for (auto i : str) {- // escape some characters- if (i == '"') out += '\\';- if (i == '\'') out += '\\';- if (i == '\\') out += '\\';- out += i;- }- return out;- }-- // unescape every escape sequence- // only removes unescaped backslashes- std::string string_unescape(const std::string& str)- {- std::string out("");- bool esc = false;- for (auto i : str) {- if (esc || i != '\\') {- esc = false;- out += i;- } else {- esc = true;- }- }- // open escape sequence at end- // maybe it should thow an error- if (esc) { out += '\\'; }- return out;- }- // read css string (handle multiline DELIM) std::string read_css_string(const std::string& str) {@@ -211,29 +103,10 @@ } out.push_back(i); }- if (esc) out += '\\';- return out;- }-- // evacuate unescaped quoted- // leave everything else untouched- std::string evacuate_quotes(const std::string& str)- {- std::string out("");- bool esc = false;- for (auto i : str) {- if (!esc) {- // ignore next character- if (i == '\\') esc = true;- // evacuate unescaped quotes- else if (i == '"') out += '\\';- else if (i == '\'') out += '\\';- }- // get escaped char now- else { esc = false; }- // remove nothing- out += i;- }+ // happens when parsing does not correctly skip+ // over escaped sequences for ie. interpolations+ // one example: foo\#{interpolate}+ // if (esc) out += '\\'; return out; } @@ -272,13 +145,20 @@ return out; } - // bell character is replaces with space+ // bell characters are replaced with spaces+ void newline_to_space(std::string& str)+ {+ std::replace(str.begin(), str.end(), '\n', ' ');+ }++ // bell characters are replaced with spaces+ // also eats spaces after line-feeds (ltrim) std::string string_to_output(const std::string& str) { std::string out(""); bool lf = false; for (auto i : str) {- if (i == 10) {+ if (i == '\n') { out += ' '; lf = true; } else if (!(lf && isspace(i))) {@@ -321,38 +201,6 @@ else return text; } - std::string normalize_wspace(const std::string& str)- {- bool ws = false;- bool esc = false;- std::string text = "";- for(const char& i : str) {- if (!esc && i == '\\') {- esc = true;- ws = false;- text += i;- } else if (esc) {- esc = false;- ws = false;- text += i;- } else if (- i == ' ' ||- i == '\r' ||- i == '\n' ||- i == ' '- ) {- // only add one space- if (!ws) text += ' ';- ws = true;- } else {- ws = false;- text += i;- }- }- if (esc) text += '\\';- return text;- }- // find best quote_mark by detecting if the string contains any single // or double quotes. When a single quote is found, we not we want a double // quote as quote_mark. Otherwise we check if the string cotains any double@@ -373,7 +221,7 @@ return quote_mark; } - std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences)+ std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences, bool strict) { // not enough room for quotes@@ -426,7 +274,7 @@ // assert invalid code points if (cp == 0) cp = 0xFFFD; // replace bell character- // if (cp == 10) cp = 32;+ // if (cp == '\n') cp = 32; // use a very simple approach to convert via utf8 lib // maybe there is a more elegant way; maybe we shoud@@ -451,6 +299,9 @@ // error("Unescaped delimiter in string to unquote found. [" + s + "]", ParserState("[UNQUOTE]")); // } else {+ if (strict && !skipped) {+ if (s[i] == q) return s;+ } skipped = false; unq.push_back(s[i]); }@@ -462,7 +313,7 @@ } - std::string quote(const std::string& s, char q, bool keep_linefeed_whitespace)+ std::string quote(const std::string& s, char q) { // autodetect with fallback to given quote@@ -488,13 +339,27 @@ int cp = utf8::next(it, end); + // in case of \r, check if the next in sequence+ // is \n and then advance the iterator and skip \r+ if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') {+ cp = utf8::next(it, end);+ }+ if (cp == '\n') { quoted.push_back('\\'); quoted.push_back('a'); // we hope we can remove this flag once we figure out // why ruby sass has these different output behaviors- if (keep_linefeed_whitespace)+ // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")+ using namespace Prelexer;+ if (alternatives <+ Prelexer::char_range<'a', 'f'>,+ Prelexer::char_range<'A', 'F'>,+ Prelexer::char_range<'0', '9'>,+ space+ >(it) != NULL) { quoted.push_back(' ');+ } } else if (cp < 127) { quoted.push_back((char) cp); } else {@@ -573,30 +438,15 @@ return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized; } - // compress a color sixtuplet if possible- // input: "#CC9900" -> output: "#C90"- std::string normalize_sixtuplet(const std::string& col) {- if(- col.substr(1, 1) == col.substr(2, 1) &&- col.substr(3, 1) == col.substr(4, 1) &&- col.substr(5, 1) == col.substr(6, 1)- ) {- return std::string("#" + col.substr(1, 1)- + col.substr(3, 1)- + col.substr(5, 1));- } else {- return std::string(col);- }- }-- bool isPrintable(Ruleset* r, Sass_Output_Style style) {+ bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style) { if (r == NULL) { return false; } - Block* b = r->block();+ Block_Obj b = r->block(); - bool hasSelectors = static_cast<Selector_List*>(r->selector())->length() > 0;+ Selector_List_Ptr sl = SASS_MEMORY_CAST(Selector_List, r->selector());+ bool hasSelectors = sl ? sl->length() > 0 : false; if (!hasSelectors) { return false;@@ -605,25 +455,25 @@ bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (dynamic_cast<At_Rule*>(stm)) {+ Statement_Obj stm = b->at(i);+ if (dynamic_cast<Directive_Ptr>(&stm)) { return true;- } else if (dynamic_cast<Has_Block*>(stm)) {- Block* pChildBlock = ((Has_Block*)stm)->block();- if (isPrintable(pChildBlock, style)) {+ } else if (Declaration_Ptr d = dynamic_cast<Declaration_Ptr>(&stm)) {+ return isPrintable(d, style);+ } else if (dynamic_cast<Has_Block_Ptr>(&stm)) {+ Block_Obj pChildBlock = ((Has_Block_Ptr)&stm)->block();+ if (isPrintable(&pChildBlock, style)) { hasPrintableChildBlocks = true; }- } else if (Comment* c = dynamic_cast<Comment*>(stm)) {+ } else if (Comment_Ptr c = dynamic_cast<Comment_Ptr>(&stm)) { // keep for uncompressed- if (style != SASS_STYLE_COMPRESSED) {+ if (style != COMPRESSED) { hasDeclarations = true; } // output style compressed if (c->is_important()) { hasDeclarations = c->is_important(); }- } else if (Declaration* d = dynamic_cast<Declaration*>(stm)) {- return isPrintable(d, style); } else { hasDeclarations = true; }@@ -636,49 +486,43 @@ return false; } - bool isPrintable(String_Constant* s, Sass_Output_Style style)+ bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style) { return ! s->value().empty(); } - bool isPrintable(String_Quoted* s, Sass_Output_Style style)+ bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style) { return true; } - bool isPrintable(Declaration* d, Sass_Output_Style style)+ bool isPrintable(Declaration_Ptr d, Sass_Output_Style style) {- Expression* val = d->value();- if (String_Quoted* sq = dynamic_cast<String_Quoted*>(val)) return isPrintable(sq, style);- if (String_Constant* sc = dynamic_cast<String_Constant*>(val)) return isPrintable(sc, style);+ Expression_Obj val = d->value();+ if (String_Quoted_Obj sq = SASS_MEMORY_CAST(String_Quoted, val)) return isPrintable(&sq, style);+ if (String_Constant_Obj sc = SASS_MEMORY_CAST(String_Constant, val)) return isPrintable(&sc, style); return true; } - bool isPrintable(Supports_Block* f, Sass_Output_Style style) {+ bool isPrintable(Supports_Block_Ptr f, Sass_Output_Style style) { if (f == NULL) { return false; } - Block* b = f->block();+ Block_Obj b = f->block(); -// bool hasSelectors = f->selector() && static_cast<Selector_List*>(f->selector())->length() > 0;+// bool hasSelectors = f->selector() && static_cast<Selector_List_Ptr>(f->selector())->length() > 0; bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (!stm->is_hoistable()) {- // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0),- // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector- // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with- // a declaration in it).- }- else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+ Statement_Obj stm = b->at(i);+ if (dynamic_cast<Declaration_Ptr>(&stm) || dynamic_cast<Directive_Ptr>(&stm)) { hasDeclarations = true; }- else if (dynamic_cast<Has_Block*>(stm)) {- Block* pChildBlock = ((Has_Block*)stm)->block();- if (isPrintable(pChildBlock, style)) {+ else if (dynamic_cast<Has_Block_Ptr>(&stm)) {+ Block_Obj pChildBlock = ((Has_Block_Ptr)&stm)->block();+ if (isPrintable(&pChildBlock, style)) { hasPrintableChildBlocks = true; } }@@ -691,62 +535,95 @@ return false; } - bool isPrintable(Media_Block* m, Sass_Output_Style style)+ bool isPrintable(Media_Block_Ptr m, Sass_Output_Style style) { if (m == 0) return false;- Block* b = m->block();+ Block_Obj b = m->block(); if (b == 0) return false; for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (typeid(*stm) == typeid(At_Rule)) return true;- if (typeid(*stm) == typeid(Declaration)) return true;- if (Has_Block* child = dynamic_cast<Has_Block*>(stm)) {- if (isPrintable(child->block(), style)) return true;+ Statement_Obj stm = b->at(i);+ if (dynamic_cast<Directive_Ptr>(&stm)) return true;+ else if (dynamic_cast<Declaration_Ptr>(&stm)) return true;+ else if (dynamic_cast<Comment_Ptr>(&stm)) {+ Comment_Ptr c = (Comment_Ptr) &stm;+ if (isPrintable(c, style)) {+ return true;+ } }+ else if (dynamic_cast<Ruleset_Ptr>(&stm)) {+ Ruleset_Ptr r = (Ruleset_Ptr) &stm;+ if (isPrintable(r, style)) {+ return true;+ }+ }+ else if (dynamic_cast<Supports_Block_Ptr>(&stm)) {+ Supports_Block_Ptr f = (Supports_Block_Ptr) &stm;+ if (isPrintable(f, style)) {+ return true;+ }+ }+ else if (dynamic_cast<Media_Block_Ptr>(&stm)) {+ Media_Block_Ptr m = (Media_Block_Ptr) &stm;+ if (isPrintable(m, style)) {+ return true;+ }+ }+ else if (dynamic_cast<Has_Block_Ptr>(&stm) && isPrintable(((Has_Block_Ptr)&stm)->block(), style)) {+ return true;+ } } return false; } - bool isPrintable(Block* b, Sass_Output_Style style) {- if (b == NULL) {+ bool isPrintable(Comment_Ptr c, Sass_Output_Style style)+ {+ // keep for uncompressed+ if (style != COMPRESSED) {+ return true;+ }+ // output style compressed+ if (c->is_important()) {+ return true;+ }+ // not printable+ return false;+ };++ bool isPrintable(Block_Obj b, Sass_Output_Style style) {+ if (!b) { return false; } for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement* stm = (*b)[i];- if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+ Statement_Obj stm = b->at(i);+ if (dynamic_cast<Declaration_Ptr>(&stm) || dynamic_cast<Directive_Ptr>(&stm)) { return true; }- else if (typeid(*stm) == typeid(Comment)) {- Comment* c = (Comment*) stm;- // keep for uncompressed- if (style != SASS_STYLE_COMPRESSED) {- return true;- }- // output style compressed- if (c->is_important()) {+ else if (dynamic_cast<Comment_Ptr>(&stm)) {+ Comment_Ptr c = (Comment_Ptr) &stm;+ if (isPrintable(c, style)) { return true; } }- else if (typeid(*stm) == typeid(Ruleset)) {- Ruleset* r = (Ruleset*) stm;+ else if (dynamic_cast<Ruleset_Ptr>(&stm)) {+ Ruleset_Ptr r = (Ruleset_Ptr) &stm; if (isPrintable(r, style)) { return true; } }- else if (typeid(*stm) == typeid(Supports_Block)) {- Supports_Block* f = (Supports_Block*) stm;+ else if (dynamic_cast<Supports_Block_Ptr>(&stm)) {+ Supports_Block_Ptr f = (Supports_Block_Ptr) &stm; if (isPrintable(f, style)) { return true; } }- else if (typeid(*stm) == typeid(Media_Block)) {- Media_Block* m = (Media_Block*) stm;+ else if (dynamic_cast<Media_Block_Ptr>(&stm)) {+ Media_Block_Ptr m = (Media_Block_Ptr) &stm; if (isPrintable(m, style)) { return true; } }- else if (dynamic_cast<Has_Block*>(stm) && isPrintable(((Has_Block*)stm)->block(), style)) {+ else if (dynamic_cast<Has_Block_Ptr>(&stm) && isPrintable(((Has_Block_Ptr)&stm)->block(), style)) { return true; } }@@ -754,27 +631,9 @@ return false; } - std::string vecJoin(const std::vector<std::string>& vec, const std::string& sep)- {- switch (vec.size())- {- case 0:- return std::string("");- case 1:- return vec[0];- default:- std::ostringstream os;- os << vec[0];- for (size_t i = 1; i < vec.size(); i++) {- os << sep << vec[i];- }- return os.str();- }+ bool isAscii(const char chr) {+ return unsigned(chr) < 128; }-- bool isAscii(const char chr) {- return unsigned(chr) < 128;- } } }
libsass/src/util.hpp view
@@ -4,6 +4,7 @@ #include <vector> #include <string> #include <assert.h>+#include "sass.hpp" #include "sass/base.h" #include "ast_fwd_decl.hpp" @@ -11,24 +12,24 @@ namespace Sass { - double round(double val);- char* sass_strdup(const char* str);+ #define out_of_memory() do { \+ std::cerr << "Out of memory.\n"; \+ exit(EXIT_FAILURE); \+ } while (0)++ double round(double val, size_t precision = 0); double sass_atof(const char* str); const char* safe_str(const char *, const char* = ""); void free_string_array(char **); char **copy_strings(const std::vector<std::string>&, char ***, int = 0);- std::string string_escape(const std::string& str);- std::string string_unescape(const std::string& str);- std::string string_eval_escapes(const std::string& str); std::string read_css_string(const std::string& str);- std::string evacuate_quotes(const std::string& str); std::string evacuate_escapes(const std::string& str); std::string string_to_output(const std::string& str); std::string comment_to_string(const std::string& text);- std::string normalize_wspace(const std::string& str);+ void newline_to_space(std::string& str); - std::string quote(const std::string&, char q = 0, bool keep_linefeed_whitespace = false);- std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false);+ std::string quote(const std::string&, char q = 0);+ std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false, bool strict = true); char detect_best_quotemark(const char* s, char qm = '"'); bool is_hex_doublet(double n);@@ -42,18 +43,15 @@ std::string normalize_underscores(const std::string& str); std::string normalize_decimals(const std::string& str);- std::string normalize_sixtuplet(const std::string& col); - std::string vecJoin(const std::vector<std::string>& vec, const std::string& sep);- bool containsAnyPrintableStatements(Block* b);-- bool isPrintable(Ruleset* r, Sass_Output_Style style = SASS_STYLE_NESTED);- bool isPrintable(Supports_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED);- bool isPrintable(Media_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED);- bool isPrintable(Block* b, Sass_Output_Style style = SASS_STYLE_NESTED);- bool isPrintable(String_Constant* s, Sass_Output_Style style = SASS_STYLE_NESTED);- bool isPrintable(String_Quoted* s, Sass_Output_Style style = SASS_STYLE_NESTED);- bool isPrintable(Declaration* d, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style = NESTED);+ bool isPrintable(Supports_Block_Ptr r, Sass_Output_Style style = NESTED);+ bool isPrintable(Media_Block_Ptr r, Sass_Output_Style style = NESTED);+ bool isPrintable(Comment_Ptr b, Sass_Output_Style style = NESTED);+ bool isPrintable(Block_Obj b, Sass_Output_Style style = NESTED);+ bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style = NESTED);+ bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style = NESTED);+ bool isPrintable(Declaration_Ptr d, Sass_Output_Style style = NESTED); bool isAscii(const char chr); }
libsass/src/values.cpp view
@@ -1,3 +1,4 @@+#include "sass.hpp" #include "sass.h" #include "values.hpp" @@ -6,35 +7,36 @@ namespace Sass { // convert value from C++ side to C-API- union Sass_Value* ast_node_to_sass_value (const Expression* val)+ union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val) { if (val->concrete_type() == Expression::NUMBER) {- const Number* res = dynamic_cast<const Number*>(val);+ Number_Ptr_Const res = dynamic_cast<Number_Ptr_Const>(val); return sass_make_number(res->value(), res->unit().c_str()); } else if (val->concrete_type() == Expression::COLOR) {- const Color* col = dynamic_cast<const Color*>(val);+ Color_Ptr_Const col = dynamic_cast<Color_Ptr_Const>(val); return sass_make_color(col->r(), col->g(), col->b(), col->a()); } else if (val->concrete_type() == Expression::LIST) {- const List* l = dynamic_cast<const List*>(val);+ List_Ptr_Const l = dynamic_cast<List_Ptr_Const>(val); union Sass_Value* list = sass_make_list(l->size(), l->separator()); for (size_t i = 0, L = l->length(); i < L; ++i) {- auto val = ast_node_to_sass_value((*l)[i]);+ Expression_Obj obj = l->at(i);+ auto val = ast_node_to_sass_value(&obj); sass_list_set_value(list, i, val); } return list; } else if (val->concrete_type() == Expression::MAP) {- const Map* m = dynamic_cast<const Map*>(val);+ Map_Ptr_Const m = dynamic_cast<Map_Ptr_Const>(val); union Sass_Value* map = sass_make_map(m->length());- size_t i = 0; for (auto key : m->keys()) {- sass_map_set_key(map, i, ast_node_to_sass_value(key));- sass_map_set_value(map, i, ast_node_to_sass_value(m->at(key)));+ size_t i = 0; for (Expression_Obj key : m->keys()) {+ sass_map_set_key(map, i, ast_node_to_sass_value(&key));+ sass_map_set_value(map, i, ast_node_to_sass_value(&m->at(key))); ++ i; } return map;@@ -45,16 +47,16 @@ } else if (val->concrete_type() == Expression::BOOLEAN) {- const Boolean* res = dynamic_cast<const Boolean*>(val);+ Boolean_Ptr_Const res = dynamic_cast<Boolean_Ptr_Const>(val); return sass_make_boolean(res->value()); } else if (val->concrete_type() == Expression::STRING) {- if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(val))+ if (String_Quoted_Ptr_Const qstr = dynamic_cast<String_Quoted_Ptr_Const>(val)) { return sass_make_qstring(qstr->value().c_str()); }- else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(val))+ else if (String_Constant_Ptr_Const cstr = dynamic_cast<String_Constant_Ptr_Const>(val)) { return sass_make_string(cstr->value().c_str()); }@@ -63,22 +65,22 @@ } // convert value from C-API to C++ side- Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val)+ Value_Ptr sass_value_to_ast_node (const union Sass_Value* val) { switch (sass_value_get_tag(val)) { case SASS_NUMBER:- return SASS_MEMORY_NEW(mem, Number,+ return SASS_MEMORY_NEW(Number, ParserState("[C-VALUE]"), sass_number_get_value(val), sass_number_get_unit(val)); break; case SASS_BOOLEAN:- return SASS_MEMORY_NEW(mem, Boolean,+ return SASS_MEMORY_NEW(Boolean, ParserState("[C-VALUE]"), sass_boolean_get_value(val)); break; case SASS_COLOR:- return SASS_MEMORY_NEW(mem, Color,+ return SASS_MEMORY_NEW(Color, ParserState("[C-VALUE]"), sass_color_get_r(val), sass_color_get_g(val),@@ -87,46 +89,46 @@ break; case SASS_STRING: if (sass_string_is_quoted(val)) {- return SASS_MEMORY_NEW(mem, String_Quoted,+ return SASS_MEMORY_NEW(String_Quoted, ParserState("[C-VALUE]"), sass_string_get_value(val)); } else {- return SASS_MEMORY_NEW(mem, String_Constant,+ return SASS_MEMORY_NEW(String_Constant, ParserState("[C-VALUE]"), sass_string_get_value(val)); } break; case SASS_LIST: {- List* l = SASS_MEMORY_NEW(mem, List,+ List_Ptr l = SASS_MEMORY_NEW(List, ParserState("[C-VALUE]"), sass_list_get_length(val), sass_list_get_separator(val)); for (size_t i = 0, L = sass_list_get_length(val); i < L; ++i) {- *l << sass_value_to_ast_node(mem, sass_list_get_value(val, i));+ l->append(sass_value_to_ast_node(sass_list_get_value(val, i))); } return l; } break; case SASS_MAP: {- Map* m = SASS_MEMORY_NEW(mem, Map, ParserState("[C-VALUE]"));+ Map_Ptr m = SASS_MEMORY_NEW(Map, ParserState("[C-VALUE]")); for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) { *m << std::make_pair(- sass_value_to_ast_node(mem, sass_map_get_key(val, i)),- sass_value_to_ast_node(mem, sass_map_get_value(val, i)));+ sass_value_to_ast_node(sass_map_get_key(val, i)),+ sass_value_to_ast_node(sass_map_get_value(val, i))); } return m; } break; case SASS_NULL:- return SASS_MEMORY_NEW(mem, Null, ParserState("[C-VALUE]"));+ return SASS_MEMORY_NEW(Null, ParserState("[C-VALUE]")); break; case SASS_ERROR:- return SASS_MEMORY_NEW(mem, Custom_Error,+ return SASS_MEMORY_NEW(Custom_Error, ParserState("[C-VALUE]"), sass_error_get_message(val)); break; case SASS_WARNING:- return SASS_MEMORY_NEW(mem, Custom_Warning,+ return SASS_MEMORY_NEW(Custom_Warning, ParserState("[C-VALUE]"), sass_warning_get_message(val)); break;
libsass/src/values.hpp view
@@ -5,8 +5,8 @@ namespace Sass { - union Sass_Value* ast_node_to_sass_value (const Expression* val);- Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val);+ union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val);+ Value_Ptr sass_value_to_ast_node (const union Sass_Value* val); } #endif
tests/Main.hs view
@@ -35,7 +35,7 @@ #ifndef EXTERNAL_LIBSASS it "should report correct version" $ do str <- peekCString libsass_version- str `shouldBe` "3.3.2"+ str `shouldBe` "3.4.3" #endif it "should support quoted strings" $ withCString "sample" $ \cstr -> do