hlibsass 0.1.8.1 → 0.1.9.0
raw patch · 144 files changed
+17034/−15310 lines, 144 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Bindings.Libsass.Context: sass_context_take_error_src :: Ptr SassContext -> IO CString
+ Bindings.Libsass.Types: SassStyleToCss :: SassOutputStyle
Files
- Bindings/Libsass/Context.hs +4/−0
- Bindings/Libsass/Types.hs +3/−0
- CHANGELOG.md +5/−0
- hlibsass.cabal +1/−1
- libsass/Makefile +86/−96
- libsass/Makefile.conf +23/−6
- libsass/include/sass/base.h +9/−1
- libsass/include/sass/context.h +2/−1
- libsass/src/GNUmakefile.am +1/−5
- libsass/src/ast.cpp +952/−2225
- libsass/src/ast.hpp +1059/−3042
- libsass/src/ast2c.cpp +80/−0
- libsass/src/ast2c.hpp +39/−0
- libsass/src/ast_def_macros.hpp +70/−10
- libsass/src/ast_fwd_decl.cpp +3/−1
- libsass/src/ast_fwd_decl.hpp +54/−252
- libsass/src/ast_helpers.hpp +292/−0
- libsass/src/ast_sel_cmp.cpp +396/−0
- libsass/src/ast_sel_super.cpp +539/−0
- libsass/src/ast_sel_unify.cpp +275/−0
- libsass/src/ast_sel_weave.cpp +616/−0
- libsass/src/ast_selectors.cpp +1033/−0
- libsass/src/ast_selectors.hpp +512/−0
- libsass/src/ast_supports.cpp +114/−0
- libsass/src/ast_supports.hpp +121/−0
- libsass/src/ast_values.cpp +1154/−0
- libsass/src/ast_values.hpp +498/−0
- libsass/src/backtrace.cpp +4/−0
- libsass/src/base64vlq.cpp +3/−0
- libsass/src/bind.cpp +19/−18
- libsass/src/bind.hpp +3/−1
- libsass/src/c2ast.cpp +64/−0
- libsass/src/c2ast.hpp +14/−0
- libsass/src/cencode.c +4/−6
- libsass/src/check_nesting.cpp +56/−61
- libsass/src/check_nesting.hpp +39/−34
- libsass/src/color_maps.cpp +166/−162
- libsass/src/color_maps.hpp +152/−160
- libsass/src/constants.cpp +20/−0
- libsass/src/constants.hpp +19/−0
- libsass/src/context.cpp +46/−66
- libsass/src/context.hpp +14/−26
- libsass/src/cssize.cpp +88/−174
- libsass/src/cssize.hpp +42/−48
- libsass/src/dart_helpers.hpp +199/−0
- libsass/src/debugger.hpp +463/−300
- libsass/src/emitter.cpp +16/−16
- libsass/src/emitter.hpp +11/−9
- libsass/src/environment.cpp +15/−1
- libsass/src/environment.hpp +11/−0
- libsass/src/error_handling.cpp +57/−59
- libsass/src/error_handling.hpp +54/−19
- libsass/src/eval.cpp +277/−403
- libsass/src/eval.hpp +62/−55
- libsass/src/eval_selectors.cpp +75/−0
- libsass/src/expand.cpp +273/−221
- libsass/src/expand.hpp +53/−37
- libsass/src/extend.cpp +0/−2130
- libsass/src/extend.hpp +0/−86
- libsass/src/extender.cpp +1186/−0
- libsass/src/extender.hpp +399/−0
- libsass/src/extension.cpp +43/−0
- libsass/src/extension.hpp +89/−0
- libsass/src/file.cpp +68/−30
- libsass/src/file.hpp +5/−12
- libsass/src/fn_colors.cpp +596/−0
- libsass/src/fn_colors.hpp +85/−0
- libsass/src/fn_lists.cpp +285/−0
- libsass/src/fn_lists.hpp +34/−0
- libsass/src/fn_maps.cpp +94/−0
- libsass/src/fn_maps.hpp +30/−0
- libsass/src/fn_miscs.cpp +244/−0
- libsass/src/fn_miscs.hpp +40/−0
- libsass/src/fn_numbers.cpp +227/−0
- libsass/src/fn_numbers.hpp +45/−0
- libsass/src/fn_selectors.cpp +205/−0
- libsass/src/fn_selectors.hpp +35/−0
- libsass/src/fn_strings.cpp +256/−0
- libsass/src/fn_strings.hpp +34/−0
- libsass/src/fn_utils.cpp +155/−0
- libsass/src/fn_utils.hpp +62/−0
- libsass/src/functions.cpp +0/−2234
- libsass/src/functions.hpp +0/−198
- libsass/src/inspect.cpp +240/−254
- libsass/src/inspect.hpp +72/−74
- libsass/src/json.cpp +2/−2
- libsass/src/lexer.cpp +23/−84
- libsass/src/lexer.hpp +5/−16
- libsass/src/listize.cpp +26/−42
- libsass/src/listize.hpp +14/−11
- libsass/src/memory/SharedPtr.cpp +1/−82
- libsass/src/memory/SharedPtr.hpp +255/−141
- libsass/src/node.cpp +0/−319
- libsass/src/node.hpp +0/−118
- libsass/src/operation.hpp +193/−143
- libsass/src/operators.cpp +22/−22
- libsass/src/operators.hpp +5/−5
- libsass/src/ordered_map.hpp +112/−0
- libsass/src/output.cpp +47/−63
- libsass/src/output.hpp +13/−20
- libsass/src/parser.cpp +375/−529
- libsass/src/parser.hpp +41/−37
- libsass/src/parser_selectors.cpp +189/−0
- libsass/src/paths.hpp +0/−71
- libsass/src/permutate.hpp +140/−0
- libsass/src/plugins.cpp +5/−1
- libsass/src/position.cpp +3/−0
- libsass/src/position.hpp +1/−1
- libsass/src/prelexer.cpp +13/−7
- libsass/src/prelexer.hpp +9/−9
- libsass/src/remove_placeholders.cpp +59/−57
- libsass/src/remove_placeholders.hpp +20/−18
- libsass/src/sass.cpp +4/−0
- libsass/src/sass.hpp +13/−9
- libsass/src/sass2scss.cpp +49/−18
- libsass/src/sass_context.cpp +59/−105
- libsass/src/sass_context.hpp +1/−1
- libsass/src/sass_functions.cpp +7/−4
- libsass/src/sass_functions.hpp +1/−1
- libsass/src/sass_util.cpp +0/−149
- libsass/src/sass_util.hpp +0/−256
- libsass/src/sass_values.cpp +21/−14
- libsass/src/source_map.cpp +5/−2
- libsass/src/source_map.hpp +2/−2
- libsass/src/stylesheet.cpp +22/−0
- libsass/src/stylesheet.hpp +57/−0
- libsass/src/subset_map.cpp +0/−55
- libsass/src/subset_map.hpp +0/−76
- libsass/src/to_c.cpp +0/−74
- libsass/src/to_c.hpp +0/−39
- libsass/src/to_value.cpp +24/−22
- libsass/src/to_value.hpp +17/−21
- libsass/src/units.cpp +9/−3
- libsass/src/units.hpp +1/−0
- libsass/src/utf8/checked.h +12/−10
- libsass/src/utf8/core.h +3/−0
- libsass/src/utf8_string.cpp +3/−0
- libsass/src/util.cpp +75/−85
- libsass/src/util.hpp +65/−16
- libsass/src/util_string.cpp +125/−0
- libsass/src/util_string.hpp +72/−0
- libsass/src/values.cpp +22/−13
- libsass/src/values.hpp +2/−2
- tests/Main.hs +1/−1
Bindings/Libsass/Context.hs view
@@ -327,6 +327,10 @@ :: Ptr SassContext -> IO CString +foreign import ccall unsafe "sass/context.h" sass_context_take_error_src+ :: Ptr SassContext+ -> IO CString+ foreign import ccall unsafe "sass/context.h" sass_context_take_output_string :: Ptr SassContext -> IO CString
Bindings/Libsass/Types.hs view
@@ -13,6 +13,7 @@ | SassStyleCompressed | SassStyleInspect -- ^ Marked as internal | SassStyleToSass -- ^ Marked as internal+ | SassStyleToCss -- ^ Marked as internal deriving (Eq, Show) instance Enum SassOutputStyle where@@ -22,6 +23,7 @@ fromEnum SassStyleCompressed = 3 fromEnum SassStyleInspect = 4 fromEnum SassStyleToSass = 5+ fromEnum SassStyleToCss = 6 toEnum 0 = SassStyleNested toEnum 1 = SassStyleExpanded@@ -29,6 +31,7 @@ toEnum 3 = SassStyleCompressed toEnum 4 = SassStyleInspect toEnum 5 = SassStyleToSass+ toEnum 6 = SassStyleToCss toEnum u = error ("SassOutputStyle.toEnum: Cannot match " ++ show u) -- From sass/context.h
CHANGELOG.md view
@@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.1.9.0] - 2020-03-26+### Changed+- Update to LibSass 3.6.3+ ## [0.1.8.1] - 2020-02-21 ### Changed - Support Cabal 3@@ -87,6 +91,7 @@ - Bindings to Libsass C API - Basic tests +[0.1.9.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.8.1...v0.1.9.0 [0.1.8.1]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.8.0...v0.1.8.1 [0.1.8.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.7.0...v0.1.8.0 [0.1.7.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.6.1...v0.1.7.0
hlibsass.cabal view
@@ -1,5 +1,5 @@ name: hlibsass-version: 0.1.8.1+version: 0.1.9.0 license: MIT license-file: LICENSE author: Jakub Fijałkowski <kuba@codinginfinity.me>
libsass/Makefile view
@@ -1,6 +1,6 @@ OS ?= $(shell uname -s)-CC ?= gcc-CXX ?= g+++CC ?= cc+CXX ?= c++ RM ?= rm -f CP ?= cp -a MKDIR ?= mkdir@@ -15,7 +15,7 @@ CFLAGS ?= -Wall CXXFLAGS ?= -Wall LDFLAGS ?= -Wall-ifeq "x$(COVERAGE)" "x"+ifndef COVERAGE CFLAGS += -O2 CXXFLAGS += -O2 LDFLAGS += -O2@@ -24,66 +24,58 @@ CXXFLAGS += -O1 -fno-omit-frame-pointer LDFLAGS += -O1 -fno-omit-frame-pointer endif-LDFLAGS += -Wl,-undefined,error-CAT ?= $(if $(filter $(OS),Windows_NT),type,cat)+CAT ?= $(if $(filter $(OS),Windows_NT),type,cat) ifneq (,$(findstring /cygdrive/,$(PATH))) UNAME := Cygwin else- ifneq (,$(findstring Windows_NT,$(OS)))- UNAME := Windows- else- ifneq (,$(findstring mingw32,$(MAKE)))- UNAME := Windows- else- ifneq (,$(findstring MINGW32,$(shell uname -s)))- UNAME = Windows- else- UNAME := $(shell uname -s)- endif- endif- endif+ifneq (,$(findstring Windows_NT,$(OS)))+ UNAME := Windows+else+ifneq (,$(findstring mingw32,$(MAKE)))+ UNAME := Windows+else+ifneq (,$(findstring MINGW32,$(shell uname -s)))+ UNAME := Windows+else+ UNAME := $(shell uname -s) endif--ifeq ($(SASS_LIBSASS_PATH),)- SASS_LIBSASS_PATH = $(abspath $(CURDIR)) endif+endif+endif -ifeq ($(LIBSASS_VERSION),)+ifndef LIBSASS_VERSION ifneq ($(wildcard ./.git/ ),) LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags) endif-endif--ifeq ($(LIBSASS_VERSION),) ifneq ($(wildcard VERSION),) LIBSASS_VERSION ?= $(shell $(CAT) VERSION) endif endif--ifneq ($(LIBSASS_VERSION),)+ifdef LIBSASS_VERSION CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" endif -# enable mandatory flag+CXXFLAGS += -std=c++11+LDFLAGS += -std=c++11+ ifeq (Windows,$(UNAME)) ifneq ($(BUILD),shared) STATIC_ALL ?= 1 endif STATIC_LIBGCC ?= 1 STATIC_LIBSTDCPP ?= 1- CXXFLAGS += -std=gnu++0x- LDFLAGS += -std=gnu++0x else STATIC_ALL ?= 0 STATIC_LIBGCC ?= 0 STATIC_LIBSTDCPP ?= 0- CXXFLAGS += -std=c++0x- LDFLAGS += -std=c++0x endif -ifneq ($(SASS_LIBSASS_PATH),)+ifndef SASS_LIBSASS_PATH+ SASS_LIBSASS_PATH = $(CURDIR)+endif+ifdef SASS_LIBSASS_PATH CFLAGS += -I $(SASS_LIBSASS_PATH)/include CXXFLAGS += -I $(SASS_LIBSASS_PATH)/include else@@ -92,20 +84,15 @@ CXXFLAGS += -I include endif -ifneq ($(EXTRA_CFLAGS),)- CFLAGS += $(EXTRA_CFLAGS)-endif-ifneq ($(EXTRA_CXXFLAGS),)- CXXFLAGS += $(EXTRA_CXXFLAGS)-endif-ifneq ($(EXTRA_LDFLAGS),)- LDFLAGS += $(EXTRA_LDFLAGS)-endif+CFLAGS += $(EXTRA_CFLAGS)+CXXFLAGS += $(EXTRA_CXXFLAGS)+LDFLAGS += $(EXTRA_LDFLAGS) LDLIBS = -lm- ifneq ($(BUILD),shared)- LDLIBS += -lstdc+++ ifneq ($(STATIC_LIBSTDCPP),1)+ LDLIBS += -lstdc+++ endif endif # link statically into lib@@ -143,7 +130,7 @@ BUILD := debug-$(BUILD) endif -ifeq (,$(TRAVIS_BUILD_DIR))+ifndef TRAVIS_BUILD_DIR ifeq ($(OS),SunOS) PREFIX ?= /opt/local else@@ -153,54 +140,39 @@ PREFIX ?= $(TRAVIS_BUILD_DIR) endif - SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc RUBY_BIN = ruby +RESOURCES =+STATICLIB = lib/libsass.a+SHAREDLIB = lib/libsass.so LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so--ifeq (Windows,$(UNAME))- ifeq (shared,$(BUILD))- CFLAGS += -D ADD_EXPORTS- CXXFLAGS += -D ADD_EXPORTS- LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dll- endif-else- ifneq (Cygwin,$(UNAME))- CFLAGS += -fPIC- CXXFLAGS += -fPIC- LDFLAGS += -fPIC- endif+ifeq ($(UNAME),Darwin)+ SHAREDLIB = lib/libsass.dylib+ LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dylib endif- ifeq (Windows,$(UNAME)) SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe-endif--include Makefile.conf--RESOURCES =-STATICLIB = lib/libsass.a-SHAREDLIB = lib/libsass.so-ifeq (Windows,$(UNAME)) RESOURCES += res/resource.rc SHAREDLIB = lib/libsass.dll ifeq (shared,$(BUILD)) CFLAGS += -D ADD_EXPORTS CXXFLAGS += -D ADD_EXPORTS+ LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dll endif else- ifneq (Cygwin,$(UNAME))- CFLAGS += -fPIC- CXXFLAGS += -fPIC- LDFLAGS += -fPIC- endif+ifneq (Cygwin,$(UNAME))+ CFLAGS += -fPIC+ CXXFLAGS += -fPIC+ LDFLAGS += -fPIC endif+endif +include Makefile.conf OBJECTS = $(addprefix src/,$(SOURCES:.cpp=.o)) COBJECTS = $(addprefix src/,$(CSOURCES:.c=.o)) RCOBJECTS = $(RESOURCES:.rc=.o)@@ -230,15 +202,20 @@ lib: $(MKDIR) lib -lib/libsass.a: lib $(COBJECTS) $(OBJECTS)+lib/libsass.a: $(COBJECTS) $(OBJECTS) | lib $(AR) rcvs $@ $(COBJECTS) $(OBJECTS) -lib/libsass.so: lib $(COBJECTS) $(OBJECTS)+lib/libsass.so: $(COBJECTS) $(OBJECTS) | lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS) -lib/libsass.dll: lib $(COBJECTS) $(OBJECTS) $(RCOBJECTS)- $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -Wl,--subsystem,windows,--out-implib,lib/libsass.a+lib/libsass.dylib: $(COBJECTS) $(OBJECTS) | lib+ $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS) \+ -install_name @rpath/libsass.dylib +lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) | lib+ $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) \+ -s -Wl,--subsystem,windows,--out-implib,lib/libsass.a+ %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< @@ -259,18 +236,17 @@ $(DESTDIR)$(PREFIX): $(MKDIR) $(DESTDIR)$(PREFIX) -$(DESTDIR)$(PREFIX)/lib: $(DESTDIR)$(PREFIX)+$(DESTDIR)$(PREFIX)/lib: | $(DESTDIR)$(PREFIX) $(MKDIR) $(DESTDIR)$(PREFIX)/lib -$(DESTDIR)$(PREFIX)/include: $(DESTDIR)$(PREFIX)+$(DESTDIR)$(PREFIX)/include: | $(DESTDIR)$(PREFIX) $(MKDIR) $(DESTDIR)$(PREFIX)/include -$(DESTDIR)$(PREFIX)/include/sass: $(DESTDIR)$(PREFIX)/include+$(DESTDIR)$(PREFIX)/include/sass: | $(DESTDIR)$(PREFIX)/include $(MKDIR) $(DESTDIR)$(PREFIX)/include/sass $(DESTDIR)$(PREFIX)/include/%.h: include/%.h \- $(DESTDIR)$(PREFIX)/include \- $(DESTDIR)$(PREFIX)/include/sass+ | $(DESTDIR)$(PREFIX)/include/sass $(INSTALL) -v -m0644 "$<" "$@" install-headers: $(DESTDIR)$(PREFIX)/include/sass.h \@@ -282,46 +258,59 @@ $(DESTDIR)$(PREFIX)/include/sass/functions.h $(DESTDIR)$(PREFIX)/lib/%.a: lib/%.a \- $(DESTDIR)$(PREFIX)/lib+ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.so: lib/%.so \- $(DESTDIR)$(PREFIX)/lib+ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.dll: lib/%.dll \- $(DESTDIR)$(PREFIX)/lib+ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" +$(DESTDIR)$(PREFIX)/lib/%.dylib: lib/%.dylib \+ | $(DESTDIR)$(PREFIX)/lib+ @$(INSTALL) -v -m0755 "$<" "$@"+ install-static: $(DESTDIR)$(PREFIX)/lib/libsass.a -install-shared: $(DESTDIR)$(PREFIX)/lib/libsass.so \+install-shared: $(DESTDIR)$(PREFIX)/$(SHAREDLIB) \ install-headers $(SASSC_BIN): $(BUILD)- $(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD)-dev+ $(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD) sassc: $(SASSC_BIN) $(SASSC_BIN) -v version: $(SASSC_BIN)- $(SASSC_BIN) -h $(SASSC_BIN) -v -test: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+test: test_build test_build: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+ $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_full: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+ --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_probe: $(SASSC_BIN)- $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -V 3.5 -c $(SASSC_BIN) --impl libsass --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+ --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) -clean-objects: lib- -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la+test_interactive: $(SASSC_BIN)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \+ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \+ --interactive $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)++clean-objects: | lib+ -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.dylib lib/*.la -$(RMDIR) lib clean: clean-objects $(RM) $(CLEANUPS)@@ -347,5 +336,6 @@ debug debug-static debug-shared \ install install-static install-shared \ lib-opts lib-opts-shared lib-opts-static \- lib-file lib-file-shared lib-file-static+ lib-file lib-file-shared lib-file-static \+ test test_build test_full test_probe .DELETE_ON_ERROR:
libsass/Makefile.conf view
@@ -7,16 +7,30 @@ SOURCES = \ ast.cpp \- node.cpp \+ ast_values.cpp \+ ast_supports.cpp \+ ast_sel_cmp.cpp \+ ast_sel_unify.cpp \+ ast_sel_super.cpp \+ ast_sel_weave.cpp \+ ast_selectors.cpp \ context.cpp \ constants.cpp \- functions.cpp \+ fn_utils.cpp \+ fn_miscs.cpp \+ fn_maps.cpp \+ fn_lists.cpp \+ fn_colors.cpp \+ fn_numbers.cpp \+ fn_strings.cpp \+ fn_selectors.cpp \ color_maps.cpp \ environment.cpp \ ast_fwd_decl.cpp \ bind.cpp \ file.cpp \ util.cpp \+ util_string.cpp \ json.cpp \ units.cpp \ values.cpp \@@ -24,29 +38,32 @@ position.cpp \ lexer.cpp \ parser.cpp \+ parser_selectors.cpp \ prelexer.cpp \ eval.cpp \+ eval_selectors.cpp \ expand.cpp \ listize.cpp \ cssize.cpp \- extend.cpp \+ extender.cpp \+ extension.cpp \+ stylesheet.cpp \ output.cpp \ inspect.cpp \ emitter.cpp \ check_nesting.cpp \ remove_placeholders.cpp \ sass.cpp \- sass_util.cpp \ sass_values.cpp \ sass_context.cpp \ sass_functions.cpp \ sass2scss.cpp \ backtrace.cpp \ operators.cpp \- to_c.cpp \+ ast2c.cpp \+ c2ast.cpp \ to_value.cpp \ source_map.cpp \- subset_map.cpp \ error_handling.cpp \ memory/SharedPtr.cpp \ utf8_string.cpp \
libsass/include/sass/base.h view
@@ -1,6 +1,7 @@ #ifndef SASS_BASE_H #define SASS_BASE_H +// #define DEBUG // #define DEBUG_SHARED_PTR #ifdef _MSC_VER@@ -16,6 +17,12 @@ #endif #endif +// Work around lack of `noexcept` keyword support in VS2013+#if defined(_MSC_VER) && (_MSC_VER <= 1800) && !defined(_ALLOW_KEYWORD_MACROS)+#define _ALLOW_KEYWORD_MACROS 1+#define noexcept throw( )+#endif+ #include <stddef.h> #include <stdbool.h> @@ -61,7 +68,8 @@ SASS_STYLE_COMPRESSED, // only used internaly SASS_STYLE_INSPECT,- SASS_STYLE_TO_SASS+ SASS_STYLE_TO_SASS,+ SASS_STYLE_TO_CSS }; // to allocate buffer to be filled
libsass/include/sass/context.h view
@@ -43,7 +43,7 @@ ADDAPI struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx); // Execute the different compilation steps individually-// Usefull if you only want to query the included files+// Useful if you only want to query the included files ADDAPI int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler); ADDAPI int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler); @@ -134,6 +134,7 @@ ADDAPI char* ADDCALL sass_context_take_error_text (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_message (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_file (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_error_src (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_output_string (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_source_map_string (struct Sass_Context* ctx); ADDAPI char** ADDCALL sass_context_take_included_files (struct Sass_Context* ctx);
libsass/src/GNUmakefile.am view
@@ -13,11 +13,7 @@ AM_CXXFLAGS = $(AM_COPT) AM_LDFLAGS = $(AM_COPT) $(AM_COVLDFLAGS) -if COMPILER_IS_MINGW32- AM_CXXFLAGS += -std=gnu++0x-else- AM_CXXFLAGS += -std=c++0x-endif+AM_CXXFLAGS += -std=c++11 EXTRA_DIST = \ COPYING \
libsass/src/ast.cpp view
@@ -1,2226 +1,953 @@-#include "sass.hpp"-#include "ast.hpp"-#include "context.hpp"-#include "node.hpp"-#include "eval.hpp"-#include "extend.hpp"-#include "emitter.hpp"-#include "color_maps.hpp"-#include "ast_fwd_decl.hpp"-#include <set>-#include <iomanip>-#include <iostream>-#include <algorithm>-#include <functional>-#include <cctype>-#include <locale>--namespace Sass {-- static Null sass_null(ParserState("null"));-- bool Wrapped_Selector::find ( bool (*f)(AST_Node_Obj) )- {- // check children first- if (selector_) {- if (selector_->find(f)) return true;- }- // execute last- return f(this);- }-- bool Selector_List::find ( bool (*f)(AST_Node_Obj) )- {- // check children first- for (Complex_Selector_Obj sel : elements()) {- if (sel->find(f)) return true;- }- // execute last- return f(this);- }-- bool Compound_Selector::find ( bool (*f)(AST_Node_Obj) )- {- // check children first- for (Simple_Selector_Obj sel : elements()) {- if (sel->find(f)) return true;- }- // execute last- return f(this);- }-- bool Complex_Selector::find ( bool (*f)(AST_Node_Obj) )- {- // check children first- if (head_ && head_->find(f)) return true;- if (tail_ && tail_->find(f)) return true;- // execute last- return f(this);- }-- bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const {- if (Supports_Operator_Obj op = Cast<Supports_Operator>(cond)) {- return op->operand() != operand();- }- return Cast<Supports_Negation>(cond) != NULL;- }-- bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const {- return Cast<Supports_Negation>(cond) ||- Cast<Supports_Operator>(cond);- }-- void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v")- {- str.erase( str.find_last_not_of( delimiters ) + 1 );- }-- void String_Constant::rtrim()- {- str_rtrim(value_);- }-- void String_Schema::rtrim()- {- if (!empty()) {- if (String_Ptr str = Cast<String>(last())) str->rtrim();- }- }-- void Argument::set_delayed(bool delayed)- {- if (value_) value_->set_delayed(delayed);- is_delayed(delayed);- }-- void Arguments::set_delayed(bool delayed)- {- for (Argument_Obj arg : elements()) {- if (arg) arg->set_delayed(delayed);- }- is_delayed(delayed);- }--- bool At_Root_Query::exclude(std::string str)- {- bool with = feature() && unquote(feature()->to_string()).compare("with") == 0;- List_Ptr l = static_cast<List_Ptr>(value().ptr());- std::string v;-- if (with)- {- if (!l || l->length() == 0) return str.compare("rule") != 0;- for (size_t i = 0, L = l->length(); i < L; ++i)- {- v = unquote((*l)[i]->to_string());- if (v.compare("all") == 0 || v == str) return false;- }- return true;- }- else- {- if (!l || !l->length()) return str.compare("rule") == 0;- for (size_t i = 0, L = l->length(); i < L; ++i)- {- v = unquote((*l)[i]->to_string());- if (v.compare("all") == 0 || v == str) return true;- }- return false;- }- }-- void AST_Node::update_pstate(const ParserState& pstate)- {- pstate_.offset += pstate - pstate_ + pstate.offset;- }-- bool Simple_Selector::is_ns_eq(const Simple_Selector& r) const- {- // https://github.com/sass/sass/issues/2229- if ((has_ns_ == r.has_ns_) ||- (has_ns_ && ns_.empty()) ||- (r.has_ns_ && r.ns_.empty())- ) {- if (ns_.empty() && r.ns() == "*") return false;- else if (r.ns().empty() && ns() == "*") return false;- else return ns() == r.ns();- }- return false;- }-- bool Compound_Selector::operator< (const Compound_Selector& rhs) const- {- size_t L = std::min(length(), rhs.length());- for (size_t i = 0; i < L; ++i)- {- Simple_Selector_Obj l = (*this)[i];- Simple_Selector_Obj r = rhs[i];- if (!l && !r) return false;- else if (!r) return false;- else if (!l) return true;- else if (*l != *r)- { return *l < *r; }- }- // just compare the length now- return length() < rhs.length();- }-- bool Compound_Selector::has_parent_ref() const- {- for (Simple_Selector_Obj s : *this) {- if (s && s->has_parent_ref()) return true;- }- return false;- }-- bool Compound_Selector::has_real_parent_ref() const- {- for (Simple_Selector_Obj s : *this) {- if (s && s->has_real_parent_ref()) return true;- }- return false;- }-- bool Complex_Selector::has_parent_ref() const- {- return (head() && head()->has_parent_ref()) ||- (tail() && tail()->has_parent_ref());- }-- bool Complex_Selector::has_real_parent_ref() const- {- return (head() && head()->has_real_parent_ref()) ||- (tail() && tail()->has_real_parent_ref());- }-- bool Complex_Selector::operator< (const Complex_Selector& rhs) const- {- // const iterators for tails- Complex_Selector_Ptr_Const l = this;- Complex_Selector_Ptr_Const r = &rhs;- Compound_Selector_Ptr l_h = NULL;- Compound_Selector_Ptr r_h = NULL;- if (l) l_h = l->head();- if (r) r_h = r->head();- // process all tails- while (true)- {- #ifdef DEBUG- // skip empty ancestor first- if (l && l->is_empty_ancestor())- {- l_h = NULL;- l = l->tail();- if(l) l_h = l->head();- continue;- }- // skip empty ancestor first- if (r && r->is_empty_ancestor())- {- r_h = NULL;- r = r->tail();- if (r) r_h = r->head();- continue;- }- #endif- // check for valid selectors- if (!l) return !!r;- if (!r) return false;- // both are null- else if (!l_h && !r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() < r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next headers- l_h = NULL; r_h = NULL;- if (l) l_h = l->head();- if (r) r_h = r->head();- }- // one side is null- else if (!r_h) return true;- else if (!l_h) return false;- // heads ok and equal- else if (*l_h == *r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() < r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next headers- l_h = NULL; r_h = NULL;- if (l) l_h = l->head();- if (r) r_h = r->head();- }- // heads are not equal- else return *l_h < *r_h;- }- }-- bool Complex_Selector::operator== (const Complex_Selector& rhs) const- {- // const iterators for tails- Complex_Selector_Ptr_Const l = this;- Complex_Selector_Ptr_Const r = &rhs;- Compound_Selector_Ptr l_h = NULL;- Compound_Selector_Ptr r_h = NULL;- if (l) l_h = l->head();- if (r) r_h = r->head();- // process all tails- while (true)- {- #ifdef DEBUG- // skip empty ancestor first- if (l && l->is_empty_ancestor())- {- l_h = NULL;- l = l->tail();- if (l) l_h = l->head();- continue;- }- // skip empty ancestor first- if (r && r->is_empty_ancestor())- {- r_h = NULL;- r = r->tail();- if (r) r_h = r->head();- continue;- }- #endif- // check the pointers- if (!r) return !l;- if (!l) return !r;- // both are null- if (!l_h && !r_h)- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() < r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next heads- l_h = NULL; r_h = NULL;- if (l) l_h = l->head();- if (r) r_h = r->head();- }- // equals if other head is empty- else if ((!l_h && !r_h) ||- (!l_h && r_h->empty()) ||- (!r_h && l_h->empty()) ||- (l_h && r_h && *l_h == *r_h))- {- // check combinator after heads- if (l->combinator() != r->combinator())- { return l->combinator() == r->combinator(); }- // advance to next tails- l = l->tail();- r = r->tail();- // fetch the next heads- l_h = NULL; r_h = NULL;- if (l) l_h = l->head();- if (r) r_h = r->head();- }- // abort- else break;- }- // unreachable- return false;- }-- Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs)- {- if (empty()) return rhs;- Compound_Selector_Obj unified = SASS_MEMORY_COPY(rhs);- for (size_t i = 0, L = length(); i < L; ++i)- {- if (unified.isNull()) break;- unified = at(i)->unify_with(unified);- }- return unified.detach();- }-- bool Complex_Selector::operator== (const Selector& rhs) const- {- if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;- if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;- if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;- if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;- throw std::runtime_error("invalid selector base classes to compare");- }--- bool Complex_Selector::operator< (const Selector& rhs) const- {- if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;- if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;- if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;- if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;- throw std::runtime_error("invalid selector base classes to compare");- }-- bool Compound_Selector::operator== (const Selector& rhs) const- {- if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;- if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;- if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;- if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;- throw std::runtime_error("invalid selector base classes to compare");- }-- bool Compound_Selector::operator< (const Selector& rhs) const- {- if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;- if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;- if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;- if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;- throw std::runtime_error("invalid selector base classes to compare");- }-- bool Selector_Schema::operator== (const Selector& rhs) const- {- if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this == *sl;- if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;- if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this == *cs;- if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this == *ch;- throw std::runtime_error("invalid selector base classes to compare");- }-- bool Selector_Schema::operator< (const Selector& rhs) const- {- if (const Selector_List* sl = Cast<Selector_List>(&rhs)) return *this < *sl;- if (const Simple_Selector* sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;- if (const Complex_Selector* cs = Cast<Complex_Selector>(&rhs)) return *this < *cs;- if (const Compound_Selector* ch = Cast<Compound_Selector>(&rhs)) return *this < *ch;- throw std::runtime_error("invalid selector base classes to compare");- }-- bool Simple_Selector::operator== (const Selector& rhs) const- {- if (Simple_Selector_Ptr_Const sp = Cast<Simple_Selector>(&rhs)) return *this == *sp;- return false;- }-- bool Simple_Selector::operator< (const Selector& rhs) const- {- if (Simple_Selector_Ptr_Const sp = Cast<Simple_Selector>(&rhs)) return *this < *sp;- return false;- }-- bool Simple_Selector::operator== (const Simple_Selector& rhs) const- {- // solve the double dispatch problem by using RTTI information via dynamic cast- if (const Pseudo_Selector* lhs = Cast<Pseudo_Selector>(this)) {return *lhs == rhs; }- else if (const Wrapped_Selector* lhs = Cast<Wrapped_Selector>(this)) {return *lhs == rhs; }- else if (const Element_Selector* lhs = Cast<Element_Selector>(this)) {return *lhs == rhs; }- else if (const Attribute_Selector* lhs = Cast<Attribute_Selector>(this)) {return *lhs == rhs; }- else if (name_ == rhs.name_)- { return is_ns_eq(rhs); }- else return false;- }-- bool Simple_Selector::operator< (const Simple_Selector& rhs) const- {- // solve the double dispatch problem by using RTTI information via dynamic cast- if (const Pseudo_Selector* lhs = Cast<Pseudo_Selector>(this)) {return *lhs < rhs; }- else if (const Wrapped_Selector* lhs = Cast<Wrapped_Selector>(this)) {return *lhs < rhs; }- else if (const Element_Selector* lhs = Cast<Element_Selector>(this)) {return *lhs < rhs; }- else if (const Attribute_Selector* lhs = Cast<Attribute_Selector>(this)) {return *lhs < rhs; }- if (is_ns_eq(rhs))- { return name_ < rhs.name_; }- return ns_ < rhs.ns_;- }-- bool Selector_List::operator== (const Selector& rhs) const- {- // solve the double dispatch problem by using RTTI information via dynamic cast- if (Selector_List_Ptr_Const sl = Cast<Selector_List>(&rhs)) { return *this == *sl; }- else if (Complex_Selector_Ptr_Const cpx = Cast<Complex_Selector>(&rhs)) { return *this == *cpx; }- else if (Compound_Selector_Ptr_Const cpd = Cast<Compound_Selector>(&rhs)) { return *this == *cpd; }- // no compare method- return this == &rhs;- }-- // Selector lists can be compared to comma lists- bool Selector_List::operator== (const Expression& rhs) const- {- // solve the double dispatch problem by using RTTI information via dynamic cast- if (List_Ptr_Const ls = Cast<List>(&rhs)) { return *ls == *this; }- if (Selector_Ptr_Const ls = Cast<Selector>(&rhs)) { return *this == *ls; }- // compare invalid (maybe we should error?)- return false;- }-- bool Selector_List::operator== (const Selector_List& rhs) const- {- // for array access- size_t i = 0, n = 0;- size_t iL = length();- size_t nL = rhs.length();- // create temporary vectors and sort them- std::vector<Complex_Selector_Obj> l_lst = this->elements();- std::vector<Complex_Selector_Obj> r_lst = rhs.elements();- std::sort(l_lst.begin(), l_lst.end(), OrderNodes());- std::sort(r_lst.begin(), r_lst.end(), OrderNodes());- // process loop- while (true)- {- // first check for valid index- if (i == iL) return iL == nL;- else if (n == nL) return iL == nL;- // the access the vector items- Complex_Selector_Obj l = l_lst[i];- Complex_Selector_Obj r = r_lst[n];- // skip nulls- if (!l) ++i;- else if (!r) ++n;- // do the check- else if (*l != *r)- { return false; }- // advance- ++i; ++n;- }- // there is no break?!- }-- bool Selector_List::operator< (const Selector& rhs) const- {- if (Selector_List_Ptr_Const sp = Cast<Selector_List>(&rhs)) return *this < *sp;- return false;- }-- bool Selector_List::operator< (const Selector_List& rhs) const- {- size_t l = rhs.length();- if (length() < l) l = length();- for (size_t i = 0; i < l; i ++) {- if (*at(i) < *rhs.at(i)) return true;- }- return false;- }-- Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs)- {- for (size_t i = 0, L = rhs->length(); i < L; ++i)- { if (to_string() == rhs->at(i)->to_string()) return rhs; }-- // check for pseudo elements because they are always last- size_t i, L;- bool found = false;- if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector) || typeid(*this) == typeid(Attribute_Selector))- {- for (i = 0, L = rhs->length(); i < L; ++i)- {- if ((Cast<Pseudo_Selector>((*rhs)[i]) || Cast<Wrapped_Selector>((*rhs)[i]) || Cast<Attribute_Selector>((*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element())- { found = true; break; }- }- }- else- {- for (i = 0, L = rhs->length(); i < L; ++i)- {- if (Cast<Pseudo_Selector>((*rhs)[i]) || Cast<Wrapped_Selector>((*rhs)[i]) || Cast<Attribute_Selector>((*rhs)[i]))- { found = true; break; }- }- }- if (!found)- {- rhs->append(this);- } else {- rhs->elements().insert(rhs->elements().begin() + i, this);- }- return rhs;- }-- Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs)- {- // check if ns can be extended- // true for no ns or universal- if (has_universal_ns())- {- // but dont extend with universal- // true for valid ns and universal- if (!rhs->is_universal_ns())- {- // overwrite the name if star is given as name- if (this->name() == "*") { this->name(rhs->name()); }- // now overwrite the namespace name and flag- this->ns(rhs->ns()); this->has_ns(rhs->has_ns());- // return copy- return this;- }- }- // namespace may changed, check the name now- // overwrite star (but not with another star)- if (name() == "*" && rhs->name() != "*")- {- // simply set the new name- this->name(rhs->name());- // return copy- return this;- }- // return original- return this;- }-- Compound_Selector_Ptr Element_Selector::unify_with(Compound_Selector_Ptr rhs)- {- // TODO: handle namespaces-- // if the rhs is empty, just return a copy of this- if (rhs->length() == 0) {- rhs->append(this);- return rhs;- }-- Simple_Selector_Ptr rhs_0 = rhs->at(0);- // otherwise, this is a tag name- if (name() == "*")- {- if (typeid(*rhs_0) == typeid(Element_Selector))- {- // if rhs is universal, just return this tagname + rhs's qualifiers- Element_Selector_Ptr ts = Cast<Element_Selector>(rhs_0);- rhs->at(0) = this->unify_with(ts);- return rhs;- }- else if (Cast<Class_Selector>(rhs_0) || Cast<Id_Selector>(rhs_0)) {- // qualifier is `.class`, so we can prefix with `ns|*.class`- if (has_ns() && !rhs_0->has_ns()) {- if (ns() != "*") rhs->elements().insert(rhs->begin(), this);- }- return rhs;- }--- return rhs;- }-- if (typeid(*rhs_0) == typeid(Element_Selector))- {- // if rhs is universal, just return this tagname + rhs's qualifiers- if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0;- // otherwise create new compound and unify first simple selector- rhs->at(0) = this->unify_with(rhs_0);- return rhs;-- }- // else it's a tag name and a bunch of qualifiers -- just append them- if (name() != "*") rhs->elements().insert(rhs->begin(), this);- return rhs;- }-- Compound_Selector_Ptr Class_Selector::unify_with(Compound_Selector_Ptr rhs)- {- rhs->has_line_break(has_line_break());- return Simple_Selector::unify_with(rhs);- }-- Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs)- {- for (size_t i = 0, L = rhs->length(); i < L; ++i)- {- if (Id_Selector_Ptr sel = Cast<Id_Selector>(rhs->at(i))) {- if (sel->name() != name()) return 0;- }- }- rhs->has_line_break(has_line_break());- return Simple_Selector::unify_with(rhs);- }-- Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs)- {- if (is_pseudo_element())- {- for (size_t i = 0, L = rhs->length(); i < L; ++i)- {- if (Pseudo_Selector_Ptr sel = Cast<Pseudo_Selector>(rhs->at(i))) {- if (sel->is_pseudo_element() && sel->name() != name()) return 0;- }- }- }- return Simple_Selector::unify_with(rhs);- }-- bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const- {- if (is_ns_eq(rhs)) {- if (name() == rhs.name()) {- if (matcher() == rhs.matcher()) {- bool no_lhs_val = value().isNull();- bool no_rhs_val = rhs.value().isNull();- if (no_lhs_val && no_rhs_val) return false; // equal- else if (no_lhs_val) return true; // lhs is null- else if (no_rhs_val) return false; // rhs is null- return *value() < *rhs.value(); // both are given- } else { return matcher() < rhs.matcher(); }- } else { return name() < rhs.name(); }- } else { return ns() < rhs.ns(); }- }-- bool Attribute_Selector::operator< (const Simple_Selector& rhs) const- {- if (Attribute_Selector_Ptr_Const w = Cast<Attribute_Selector>(&rhs))- {- return *this < *w;- }- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const- {- // get optional value state- bool no_lhs_val = value().isNull();- bool no_rhs_val = rhs.value().isNull();- // both are null, therefore equal- if (no_lhs_val && no_rhs_val) {- return (name() == rhs.name())- && (matcher() == rhs.matcher())- && (is_ns_eq(rhs));- }- // both are defined, evaluate- if (no_lhs_val == no_rhs_val) {- return (name() == rhs.name())- && (matcher() == rhs.matcher())- && (is_ns_eq(rhs))- && (*value() == *rhs.value());- }- // not equal- return false;-- }-- bool Attribute_Selector::operator== (const Simple_Selector& rhs) const- {- if (Attribute_Selector_Ptr_Const w = Cast<Attribute_Selector>(&rhs))- {- return is_ns_eq(rhs) &&- name() == rhs.name() &&- *this == *w;- }- return false;- }-- bool Element_Selector::operator< (const Element_Selector& rhs) const- {- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Element_Selector::operator< (const Simple_Selector& rhs) const- {- if (Element_Selector_Ptr_Const w = Cast<Element_Selector>(&rhs))- {- return *this < *w;- }- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Element_Selector::operator== (const Element_Selector& rhs) const- {- return is_ns_eq(rhs) &&- name() == rhs.name();- }-- bool Element_Selector::operator== (const Simple_Selector& rhs) const- {- if (Element_Selector_Ptr_Const w = Cast<Element_Selector>(&rhs))- {- return is_ns_eq(rhs) &&- name() == rhs.name() &&- *this == *w;- }- return false;- }-- bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const- {- if (is_ns_eq(rhs) && name() == rhs.name())- {- String_Obj lhs_ex = expression();- String_Obj rhs_ex = rhs.expression();- if (rhs_ex && lhs_ex) return *lhs_ex == *rhs_ex;- else return lhs_ex.ptr() == rhs_ex.ptr();- }- else return false;- }-- bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const- {- if (Pseudo_Selector_Ptr_Const w = Cast<Pseudo_Selector>(&rhs))- {- return *this == *w;- }- return is_ns_eq(rhs) &&- name() == rhs.name();- }-- bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const- {- if (is_ns_eq(rhs) && name() == rhs.name())- {- String_Obj lhs_ex = expression();- String_Obj rhs_ex = rhs.expression();- if (rhs_ex && lhs_ex) return *lhs_ex < *rhs_ex;- else return lhs_ex.ptr() < rhs_ex.ptr();- }- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const- {- if (Pseudo_Selector_Ptr_Const w = Cast<Pseudo_Selector>(&rhs))- {- return *this < *w;- }- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const- {- if (is_ns_eq(rhs) && name() == rhs.name())- { return *(selector()) == *(rhs.selector()); }- else return false;- }-- bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const- {- if (Wrapped_Selector_Ptr_Const w = Cast<Wrapped_Selector>(&rhs))- {- return *this == *w;- }- return is_ns_eq(rhs) &&- name() == rhs.name();- }-- bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const- {- if (is_ns_eq(rhs) && name() == rhs.name())- { return *(selector()) < *(rhs.selector()); }- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const- {- if (Wrapped_Selector_Ptr_Const w = Cast<Wrapped_Selector>(&rhs))- {- return *this < *w;- }- if (is_ns_eq(rhs))- { return name() < rhs.name(); }- return ns() < rhs.ns();- }-- bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Obj sub)- {- if (this->name() != sub->name()) return false;- if (this->name() == ":current") return false;- if (Selector_List_Obj rhs_list = Cast<Selector_List>(sub->selector())) {- if (Selector_List_Obj lhs_list = Cast<Selector_List>(selector())) {- return lhs_list->is_superselector_of(rhs_list);- }- }- coreError("is_superselector expected a Selector_List", sub->pstate());- return false;- }-- bool Compound_Selector::is_superselector_of(Selector_List_Obj rhs, std::string wrapped)- {- for (Complex_Selector_Obj item : rhs->elements()) {- if (is_superselector_of(item, wrapped)) return true;- }- return false;- }-- bool Compound_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapped)- {- if (rhs->head()) return is_superselector_of(rhs->head(), wrapped);- return false;- }-- bool Compound_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)- {- Compound_Selector_Ptr lhs = this;- Simple_Selector_Ptr lbase = lhs->base();- Simple_Selector_Ptr rbase = rhs->base();-- // Check if pseudo-elements are the same between the selectors-- std::set<std::string> lpsuedoset, rpsuedoset;- for (size_t i = 0, L = length(); i < L; ++i)- {- if ((*this)[i]->is_pseudo_element()) {- std::string pseudo((*this)[i]->to_string());- pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving- lpsuedoset.insert(pseudo);- }- }- for (size_t i = 0, L = rhs->length(); i < L; ++i)- {- if ((*rhs)[i]->is_pseudo_element()) {- std::string pseudo((*rhs)[i]->to_string());- pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving- rpsuedoset.insert(pseudo);- }- }- if (lpsuedoset != rpsuedoset) {- return false;- }-- // would like to replace this without stringification- // https://github.com/sass/sass/issues/2229- // SimpleSelectorSet lset, rset;- std::set<std::string> lset, rset;-- if (lbase && rbase)- {- if (lbase->to_string() == rbase->to_string()) {- for (size_t i = 1, L = length(); i < L; ++i)- { lset.insert((*this)[i]->to_string()); }- for (size_t i = 1, L = rhs->length(); i < L; ++i)- { rset.insert((*rhs)[i]->to_string()); }- return includes(rset.begin(), rset.end(), lset.begin(), lset.end());- }- return false;- }-- for (size_t i = 0, iL = length(); i < iL; ++i)- {- Selector_Obj wlhs = (*this)[i];- // very special case for wrapped matches selector- if (Wrapped_Selector_Obj wrapped = Cast<Wrapped_Selector>(wlhs)) {- if (wrapped->name() == ":not") {- if (Selector_List_Obj not_list = Cast<Selector_List>(wrapped->selector())) {- if (not_list->is_superselector_of(rhs, wrapped->name())) return false;- } else {- throw std::runtime_error("wrapped not selector is not a list");- }- }- if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {- wlhs = wrapped->selector();- if (Selector_List_Obj list = Cast<Selector_List>(wrapped->selector())) {- if (Compound_Selector_Obj comp = Cast<Compound_Selector>(rhs)) {- if (!wrapping.empty() && wrapping != wrapped->name()) return false;- if (wrapping.empty() || wrapping != wrapped->name()) {;- if (list->is_superselector_of(comp, wrapped->name())) return true;- }- }- }- }- Simple_Selector_Ptr rhs_sel = NULL;- if (rhs->elements().size() > i) rhs_sel = (*rhs)[i];- if (Wrapped_Selector_Ptr wrapped_r = Cast<Wrapped_Selector>(rhs_sel)) {- if (wrapped->name() == wrapped_r->name()) {- if (wrapped->is_superselector_of(wrapped_r)) {- continue;- }}- }- }- // match from here on as strings- lset.insert(wlhs->to_string());- }-- for (size_t n = 0, nL = rhs->length(); n < nL; ++n)- {- Selector_Obj r = (*rhs)[n];- if (Wrapped_Selector_Obj wrapped = Cast<Wrapped_Selector>(r)) {- if (wrapped->name() == ":not") {- if (Selector_List_Obj ls = Cast<Selector_List>(wrapped->selector())) {- ls->remove_parent_selectors();- if (is_superselector_of(ls, wrapped->name())) return false;- }- }- if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {- if (!wrapping.empty()) {- if (wrapping != wrapped->name()) return false;- }- if (Selector_List_Obj ls = Cast<Selector_List>(wrapped->selector())) {- ls->remove_parent_selectors();- return (is_superselector_of(ls, wrapped->name()));- }- }- }- rset.insert(r->to_string());- }-- //for (auto l : lset) { cerr << "l: " << l << endl; }- //for (auto r : rset) { cerr << "r: " << r << endl; }-- if (lset.empty()) return true;- // return true if rset contains all the elements of lset- return includes(rset.begin(), rset.end(), lset.begin(), lset.end());-- }-- // create complex selector (ancestor of) from compound selector- Complex_Selector_Obj Compound_Selector::to_complex()- {- // create an intermediate complex selector- return SASS_MEMORY_NEW(Complex_Selector,- pstate(),- Complex_Selector::ANCESTOR_OF,- this,- 0);- }-- Selector_List_Ptr Complex_Selector::unify_with(Complex_Selector_Ptr other)- {-- // get last tails (on the right side)- Complex_Selector_Obj l_last = this->last();- Complex_Selector_Obj r_last = other->last();-- // check valid pointers (assertion)- SASS_ASSERT(l_last, "lhs is null");- SASS_ASSERT(r_last, "rhs is null");-- // Not sure about this check, but closest way I could check- // was to see if this is a ruby 'SimpleSequence' equivalent.- // It seems to do the job correctly as some specs react to this- if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0;- if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0;-- // get the headers for the last tails- Compound_Selector_Obj l_last_head = l_last->head();- Compound_Selector_Obj r_last_head = r_last->head();-- // check valid head pointers (assertion)- SASS_ASSERT(l_last_head, "lhs head is null");- SASS_ASSERT(r_last_head, "rhs head is null");-- // get the unification of the last compound selectors- Compound_Selector_Obj unified = r_last_head->unify_with(l_last_head);-- // abort if we could not unify heads- if (unified == 0) return 0;-- // check for universal (star: `*`) selector- bool is_universal = l_last_head->is_universal() ||- r_last_head->is_universal();-- if (is_universal)- {- // move the head- l_last->head(0);- r_last->head(unified);- }-- // create nodes from both selectors- Node lhsNode = complexSelectorToNode(this);- Node rhsNode = complexSelectorToNode(other);-- // overwrite universal base- if (!is_universal)- {- // create some temporaries to convert to node- Complex_Selector_Obj fake = unified->to_complex();- Node unified_node = complexSelectorToNode(fake);- // add to permutate the list?- rhsNode.plus(unified_node);- }-- // do some magic we inherit from node and extend- Node node = subweave(lhsNode, rhsNode);- Selector_List_Obj result = SASS_MEMORY_NEW(Selector_List, pstate());- NodeDequePtr col = node.collection(); // move from collection to list- for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++)- { result->append(nodeToComplexSelector(Node::naiveTrim(*it))); }-- // only return if list has some entries- return result->length() ? result.detach() : 0;-- }-- bool Compound_Selector::operator== (const Compound_Selector& rhs) const- {- // for array access- size_t i = 0, n = 0;- size_t iL = length();- size_t nL = rhs.length();- // create temporary vectors and sort them- std::vector<Simple_Selector_Obj> l_lst = this->elements();- std::vector<Simple_Selector_Obj> r_lst = rhs.elements();- std::sort(l_lst.begin(), l_lst.end(), OrderNodes());- std::sort(r_lst.begin(), r_lst.end(), OrderNodes());- // process loop- while (true)- {- // first check for valid index- if (i == iL) return iL == nL;- else if (n == nL) return iL == nL;- // the access the vector items- Simple_Selector_Obj l = l_lst[i];- Simple_Selector_Obj r = r_lst[n];- // skip nulls- if (!l) ++i;- if (!r) ++n;- // do the check now- else if (*l != *r)- { return false; }- // advance now- ++i; ++n;- }- // there is no break?!- }-- bool Complex_Selector::is_superselector_of(Compound_Selector_Obj rhs, std::string wrapping)- {- return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);- }-- bool Complex_Selector::is_superselector_of(Complex_Selector_Obj rhs, std::string wrapping)- {- Complex_Selector_Ptr lhs = this;- // check for selectors with leading or trailing combinators- if (!lhs->head() || !rhs->head())- { return false; }- Complex_Selector_Obj l_innermost = lhs->innermost();- if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)- { return false; }- Complex_Selector_Obj r_innermost = rhs->innermost();- if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF)- { return false; }- // more complex (i.e., longer) selectors are always more specific- size_t l_len = lhs->length(), r_len = rhs->length();- if (l_len > r_len)- { return false; }-- if (l_len == 1)- { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); }-- // we have to look one tail deeper, since we cary the- // combinator around for it (which is important here)- if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {- Complex_Selector_Obj lhs_tail = lhs->tail();- Complex_Selector_Obj rhs_tail = rhs->tail();- if (lhs_tail->combinator() != rhs_tail->combinator()) return false;- if (lhs_tail->head() && !rhs_tail->head()) return false;- if (!lhs_tail->head() && rhs_tail->head()) return false;- if (lhs_tail->head() && rhs_tail->head()) {- if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;- }- }-- bool found = false;- Complex_Selector_Obj marker = rhs;- for (size_t i = 0, L = rhs->length(); i < L; ++i) {- if (i == L-1)- { return false; }- if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping))- { found = true; break; }- marker = marker->tail();- }- if (!found)- { return false; }-- /*- Hmm, I hope I have the logic right:-- if lhs has a combinator:- if !(marker has a combinator) return false- if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false- return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)- else if marker has a combinator:- if !(marker.combinator == ">") return false- return lhs.tail.is_superselector_of(marker.tail)- else- return lhs.tail.is_superselector_of(marker.tail)- */- if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)- {- if (marker->combinator() == Complex_Selector::ANCESTOR_OF)- { return false; }- if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))- { return false; }- return lhs->tail()->is_superselector_of(marker->tail());- }- else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)- {- if (marker->combinator() != Complex_Selector::PARENT_OF)- { return false; }- return lhs->tail()->is_superselector_of(marker->tail());- }- return lhs->tail()->is_superselector_of(marker->tail());- }-- size_t Complex_Selector::length() const- {- // TODO: make this iterative- if (!tail()) return 1;- return 1 + tail()->length();- }-- // append another complex selector at the end- // check if we need to append some headers- // then we need to check for the combinator- // only then we can safely set the new tail- void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces)- {-- Complex_Selector_Obj t = ss->tail();- Combinator c = ss->combinator();- String_Obj r = ss->reference();- Compound_Selector_Obj h = ss->head();-- if (ss->has_line_feed()) has_line_feed(true);- if (ss->has_line_break()) has_line_break(true);-- // append old headers- if (h && h->length()) {- if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {- traces.push_back(Backtrace(pstate()));- throw Exception::InvalidParent(this, traces, ss);- } else if (last()->head_ && last()->head_->length()) {- Compound_Selector_Obj rh = last()->head();- size_t i;- size_t L = h->length();- if (Cast<Element_Selector>(h->first())) {- if (Class_Selector_Ptr cs = Cast<Class_Selector>(rh->last())) {- Class_Selector_Ptr sqs = SASS_MEMORY_COPY(cs);- sqs->name(sqs->name() + (*h)[0]->name());- sqs->pstate((*h)[0]->pstate());- (*rh)[rh->length()-1] = sqs;- rh->pstate(h->pstate());- for (i = 1; i < L; ++i) rh->append((*h)[i]);- } else if (Id_Selector_Ptr is = Cast<Id_Selector>(rh->last())) {- Id_Selector_Ptr sqs = SASS_MEMORY_COPY(is);- sqs->name(sqs->name() + (*h)[0]->name());- sqs->pstate((*h)[0]->pstate());- (*rh)[rh->length()-1] = sqs;- rh->pstate(h->pstate());- for (i = 1; i < L; ++i) rh->append((*h)[i]);- } else if (Element_Selector_Ptr ts = Cast<Element_Selector>(rh->last())) {- Element_Selector_Ptr tss = SASS_MEMORY_COPY(ts);- tss->name(tss->name() + (*h)[0]->name());- tss->pstate((*h)[0]->pstate());- (*rh)[rh->length()-1] = tss;- rh->pstate(h->pstate());- for (i = 1; i < L; ++i) rh->append((*h)[i]);- } else if (Placeholder_Selector_Ptr ps = Cast<Placeholder_Selector>(rh->last())) {- Placeholder_Selector_Ptr pss = SASS_MEMORY_COPY(ps);- pss->name(pss->name() + (*h)[0]->name());- pss->pstate((*h)[0]->pstate());- (*rh)[rh->length()-1] = pss;- rh->pstate(h->pstate());- for (i = 1; i < L; ++i) rh->append((*h)[i]);- } else {- last()->head_->concat(h);- }- } else {- last()->head_->concat(h);- }- } else if (last()->head_) {- last()->head_->concat(h);- }- } else {- // std::cerr << "has no or empty head\n";- }-- if (last()) {- if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {- Complex_Selector_Ptr inter = SASS_MEMORY_NEW(Complex_Selector, pstate());- inter->reference(r);- inter->combinator(c);- inter->tail(t);- last()->tail(inter);- } else {- if (last()->combinator() == ANCESTOR_OF) {- last()->combinator(c);- last()->reference(r);- }- last()->tail(t);- }- }-- }-- Selector_List_Obj Selector_List::eval(Eval& eval)- {- Selector_List_Obj list = schema() ?- eval(schema()) : eval(this);- list->schema(schema());- return list;- }-- Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)- {- if (!this->has_parent_ref()) return this;- Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());- Selector_List_Ptr ps = pstack.back();- for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {- for (size_t si = 0, sL = this->length(); si < sL; ++si) {- Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent);- ss->concat(rv);- }- }- return ss;- }-- Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)- {- Complex_Selector_Obj tail = this->tail();- Compound_Selector_Obj head = this->head();- Selector_List_Ptr parents = pstack.back();-- if (!this->has_real_parent_ref() && !implicit_parent) {- Selector_List_Ptr retval = SASS_MEMORY_NEW(Selector_List, pstate());- retval->append(this);- return retval;- }-- // first resolve_parent_refs the tail (which may return an expanded list)- Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0;-- if (head && head->length() > 0) {-- Selector_List_Obj retval;- // we have a parent selector in a simple compound list- // mix parent complex selector into the compound list- if (Cast<Parent_Selector>((*head)[0])) {- retval = SASS_MEMORY_NEW(Selector_List, pstate());-- // it turns out that real parent references reach- // across @at-root rules, which comes unexpected- if (parents == NULL && head->has_real_parent_ref()) {- int i = pstack.size() - 1;- while (!parents && i > -1) {- parents = pstack.at(i--);- }- }-- if (parents && parents->length()) {- if (tails && tails->length() > 0) {- for (size_t n = 0, nL = tails->length(); n < nL; ++n) {- for (size_t i = 0, iL = parents->length(); i < iL; ++i) {- Complex_Selector_Obj t = (*tails)[n];- Complex_Selector_Obj parent = (*parents)[i];- Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);- Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);- ss->tail(t ? SASS_MEMORY_CLONE(t) : NULL);- Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);- // remove parent selector from sequence- if (h->length()) {- h->erase(h->begin());- ss->head(h);- } else {- ss->head(NULL);- }- // adjust for parent selector (1 char)- // if (h->length()) {- // ParserState state(h->at(0)->pstate());- // state.offset.column += 1;- // state.column -= 1;- // (*h)[0]->pstate(state);- // }- // keep old parser state- s->pstate(pstate());- // append new tail- s->append(ss, traces);- retval->append(s);- }- }- }- // have no tails but parents- // loop above is inside out- else {- for (size_t i = 0, iL = parents->length(); i < iL; ++i) {- Complex_Selector_Obj parent = (*parents)[i];- Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent);- Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this);- // this is only if valid if the parent has no trailing op- // otherwise we cannot append more simple selectors to head- if (parent->last()->combinator() != ANCESTOR_OF) {- traces.push_back(Backtrace(pstate()));- throw Exception::InvalidParent(parent, traces, ss);- }- ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL);- Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);- // remove parent selector from sequence- if (h->length()) {- h->erase(h->begin());- ss->head(h);- } else {- ss->head(NULL);- }- // \/ IMO ruby sass bug \/- ss->has_line_feed(false);- // adjust for parent selector (1 char)- // if (h->length()) {- // ParserState state(h->at(0)->pstate());- // state.offset.column += 1;- // state.column -= 1;- // (*h)[0]->pstate(state);- // }- // keep old parser state- s->pstate(pstate());- // append new tail- s->append(ss, traces);- retval->append(s);- }- }- }- // have no parent but some tails- else {- if (tails && tails->length() > 0) {- for (size_t n = 0, nL = tails->length(); n < nL; ++n) {- Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);- cpy->tail(SASS_MEMORY_CLONE(tails->at(n)));- cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));- for (size_t i = 1, L = this->head()->length(); i < L; ++i)- cpy->head()->append((*this->head())[i]);- if (!cpy->head()->length()) cpy->head(0);- retval->append(cpy->skip_empty_reference());- }- }- // have no parent nor tails- else {- Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this);- cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate()));- for (size_t i = 1, L = this->head()->length(); i < L; ++i)- cpy->head()->append((*this->head())[i]);- if (!cpy->head()->length()) cpy->head(0);- retval->append(cpy->skip_empty_reference());- }- }- }- // no parent selector in head- else {- retval = this->tails(tails);- }-- for (Simple_Selector_Obj ss : head->elements()) {- if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {- if (Selector_List_Ptr sl = Cast<Selector_List>(ws->selector())) {- if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent));- }- }- }-- return retval.detach();-- }- // has no head- return this->tails(tails);- }-- Selector_List_Ptr Complex_Selector::tails(Selector_List_Ptr tails)- {- Selector_List_Ptr rv = SASS_MEMORY_NEW(Selector_List, pstate_);- if (tails && tails->length()) {- for (size_t i = 0, iL = tails->length(); i < iL; ++i) {- Complex_Selector_Obj pr = SASS_MEMORY_CLONE(this);- pr->tail(tails->at(i));- rv->append(pr);- }- }- else {- rv->append(this);- }- return rv;- }-- // return the last tail that is defined- Complex_Selector_Obj Complex_Selector::first()- {- // declare variables used in loop- Complex_Selector_Obj cur = this;- Compound_Selector_Obj head;- // processing loop- while (cur)- {- // get the head- head = cur->head_;- // abort (and return) if it is not a parent selector- if (!head || head->length() != 1 || !Cast<Parent_Selector>((*head)[0])) {- break;- }- // advance to next- cur = cur->tail_;- }- // result- return cur;- }-- // return the last tail that is defined- Complex_Selector_Obj Complex_Selector::last()- {- Complex_Selector_Ptr cur = this;- Complex_Selector_Ptr nxt = cur;- // loop until last- while (nxt) {- cur = nxt;- nxt = cur->tail();- }- return cur;- }-- Complex_Selector::Combinator Complex_Selector::clear_innermost()- {- Combinator c;- if (!tail() || tail()->tail() == 0)- { c = combinator(); combinator(ANCESTOR_OF); tail(0); }- else- { c = tail()->clear_innermost(); }- return c;- }-- void Complex_Selector::set_innermost(Complex_Selector_Obj val, Combinator c)- {- if (!tail())- { tail(val); combinator(c); }- else- { tail()->set_innermost(val, c); }- }-- void Complex_Selector::cloneChildren()- {- if (head()) head(SASS_MEMORY_CLONE(head()));- if (tail()) tail(SASS_MEMORY_CLONE(tail()));- }-- void Compound_Selector::cloneChildren()- {- for (size_t i = 0, l = length(); i < l; i++) {- at(i) = SASS_MEMORY_CLONE(at(i));- }- }-- void Selector_List::cloneChildren()- {- for (size_t i = 0, l = length(); i < l; i++) {- at(i) = SASS_MEMORY_CLONE(at(i));- }- }-- void Wrapped_Selector::cloneChildren()- {- selector(SASS_MEMORY_CLONE(selector()));- }-- // remove parent selector references- // basically unwraps parsed selectors- void Selector_List::remove_parent_selectors()- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = length(); i < L; ++i) {- if (!(*this)[i]->head()) continue;- if ((*this)[i]->head()->is_empty_reference()) {- // simply move to the next tail if we have "no" combinator- if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) {- if ((*this)[i]->tail()) {- if ((*this)[i]->has_line_feed()) {- (*this)[i]->tail()->has_line_feed(true);- }- (*this)[i] = (*this)[i]->tail();- }- }- // otherwise remove the first item from head- else {- (*this)[i]->head()->erase((*this)[i]->head()->begin());- }- }- }- }-- size_t Wrapped_Selector::hash()- {- if (hash_ == 0) {- hash_combine(hash_, Simple_Selector::hash());- if (selector_) hash_combine(hash_, selector_->hash());- }- return hash_;- }- bool Wrapped_Selector::has_parent_ref() const {- // if (has_reference()) return true;- if (!selector()) return false;- return selector()->has_parent_ref();- }- bool Wrapped_Selector::has_real_parent_ref() const {- // if (has_reference()) return true;- if (!selector()) return false;- return selector()->has_real_parent_ref();- }- unsigned long Wrapped_Selector::specificity() const- {- return selector_ ? selector_->specificity() : 0;- }--- bool Selector_List::has_parent_ref() const- {- for (Complex_Selector_Obj s : elements()) {- if (s && s->has_parent_ref()) return true;- }- return false;- }-- bool Selector_List::has_real_parent_ref() const- {- for (Complex_Selector_Obj s : elements()) {- if (s && s->has_real_parent_ref()) return true;- }- return false;- }-- bool Selector_Schema::has_parent_ref() const- {- if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {- return schema->length() > 0 && Cast<Parent_Selector>(schema->at(0)) != NULL;- }- return false;- }-- bool Selector_Schema::has_real_parent_ref() const- {- if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {- Parent_Selector_Obj p = Cast<Parent_Selector>(schema->at(0));- return schema->length() > 0 && p && p->is_real_parent_ref();- }- return false;- }-- void Selector_List::adjust_after_pushing(Complex_Selector_Obj c)- {- // if (c->has_reference()) has_reference(true);- }-- // it's a superselector if every selector of the right side- // list is a superselector of the given left side selector- bool Complex_Selector::is_superselector_of(Selector_List_Obj sub, std::string wrapping)- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = sub->length(); i < L; ++i) {- if (!is_superselector_of((*sub)[i], wrapping)) return false;- }- return true;- }-- // it's a superselector if every selector of the right side- // list is a superselector of the given left side selector- bool Selector_List::is_superselector_of(Selector_List_Obj sub, std::string wrapping)- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = sub->length(); i < L; ++i) {- if (!is_superselector_of((*sub)[i], wrapping)) return false;- }- return true;- }-- // it's a superselector if every selector on the right side- // is a superselector of any one of the left side selectors- bool Selector_List::is_superselector_of(Compound_Selector_Obj sub, std::string wrapping)- {- // Check every lhs selector against right hand- for(size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_superselector_of(sub, wrapping)) return true;- }- return false;- }-- // it's a superselector if every selector on the right side- // is a superselector of any one of the left side selectors- bool Selector_List::is_superselector_of(Complex_Selector_Obj sub, std::string wrapping)- {- // Check every lhs selector against right hand- for(size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_superselector_of(sub)) return true;- }- return false;- }-- Selector_List_Ptr Selector_List::unify_with(Selector_List_Ptr rhs) {- std::vector<Complex_Selector_Obj> unified_complex_selectors;- // Unify all of children with RHS's children, storing the results in `unified_complex_selectors`- for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) {- Complex_Selector_Obj seq1 = (*this)[lhs_i];- for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {- Complex_Selector_Ptr seq2 = rhs->at(rhs_i);-- Selector_List_Obj result = seq1->unify_with(seq2);- if( result ) {- for(size_t i = 0, L = result->length(); i < L; ++i) {- unified_complex_selectors.push_back( (*result)[i] );- }- }- }- }-- // Creates the final Selector_List by combining all the complex selectors- Selector_List_Ptr final_result = SASS_MEMORY_NEW(Selector_List, pstate());- for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {- final_result->append(*itr);- }- return final_result;- }-- void Selector_List::populate_extends(Selector_List_Obj extendee, Subset_Map& extends)- {-- Selector_List_Ptr extender = this;- for (auto complex_sel : extendee->elements()) {- Complex_Selector_Obj c = complex_sel;--- // Ignore any parent selectors, until we find the first non Selectorerence head- Compound_Selector_Obj compound_sel = c->head();- Complex_Selector_Obj pIter = complex_sel;- while (pIter) {- Compound_Selector_Obj pHead = pIter->head();- if (pHead && Cast<Parent_Selector>(pHead->elements()[0]) == NULL) {- compound_sel = pHead;- break;- }-- pIter = pIter->tail();- }-- if (!pIter->head() || pIter->tail()) {- coreError("nested selectors may not be extended", c->pstate());- }-- compound_sel->is_optional(extendee->is_optional());-- for (size_t i = 0, L = extender->length(); i < L; ++i) {- extends.put(compound_sel, std::make_pair((*extender)[i], compound_sel));- }- }- };-- void Compound_Selector::append(Simple_Selector_Ptr element)- {- Vectorized<Simple_Selector_Obj>::append(element);- pstate_.offset += element->pstate().offset;- }-- Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs)- {- Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate());- // result->has_parent_reference(has_parent_reference());-- // not very efficient because it needs to preserve order- for (size_t i = 0, L = length(); i < L; ++i)- {- bool found = false;- std::string thisSelector((*this)[i]->to_string());- for (size_t j = 0, M = rhs->length(); j < M; ++j)- {- if (thisSelector == (*rhs)[j]->to_string())- {- found = true;- break;- }- }- if (!found) result->append((*this)[i]);- }-- return result;- }-- void Compound_Selector::mergeSources(ComplexSelectorSet& sources)- {- for (ComplexSelectorSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) {- this->sources_.insert(SASS_MEMORY_CLONE(*iterator));- }- }-- Argument_Obj Arguments::get_rest_argument()- {- if (this->has_rest_argument()) {- for (Argument_Obj arg : this->elements()) {- if (arg->is_rest_argument()) {- return arg;- }- }- }- return NULL;- }-- Argument_Obj Arguments::get_keyword_argument()- {- if (this->has_keyword_argument()) {- for (Argument_Obj arg : this->elements()) {- if (arg->is_keyword_argument()) {- return arg;- }- }- }- return NULL;- }-- void Arguments::adjust_after_pushing(Argument_Obj a)- {- if (!a->name().empty()) {- if (has_keyword_argument()) {- coreError("named arguments must precede variable-length argument", a->pstate());- }- has_named_arguments(true);- }- else if (a->is_rest_argument()) {- if (has_rest_argument()) {- coreError("functions and mixins may only be called with one variable-length argument", a->pstate());- }- if (has_keyword_argument_) {- coreError("only keyword arguments may follow variable arguments", a->pstate());- }- has_rest_argument(true);- }- else if (a->is_keyword_argument()) {- if (has_keyword_argument()) {- coreError("functions and mixins may only be called with one keyword argument", a->pstate());- }- has_keyword_argument(true);- }- else {- if (has_rest_argument()) {- coreError("ordinal arguments must precede variable-length arguments", a->pstate());- }- if (has_named_arguments()) {- coreError("ordinal arguments must precede named arguments", a->pstate());- }- }- }-- bool Ruleset::is_invisible() const {- if (Selector_List_Ptr sl = Cast<Selector_List>(selector())) {- for (size_t i = 0, L = sl->length(); i < L; ++i)- if (!(*sl)[i]->has_placeholder()) return false;- }- return true;- }-- bool Media_Block::is_invisible() const {- for (size_t i = 0, L = block()->length(); i < L; ++i) {- Statement_Obj stm = block()->at(i);- if (!stm->is_invisible()) return false;- }- return true;- }-- Number::Number(ParserState pstate, double val, std::string u, bool zero)- : Value(pstate),- Units(),- value_(val),- zero_(zero),- hash_(0)- {- size_t l = 0;- size_t r;- if (!u.empty()) {- bool nominator = true;- while (true) {- r = u.find_first_of("*/", l);- std::string unit(u.substr(l, r == std::string::npos ? r : r - l));- if (!unit.empty()) {- if (nominator) numerators.push_back(unit);- else denominators.push_back(unit);- }- if (r == std::string::npos) break;- // ToDo: should error for multiple slashes- // if (!nominator && u[r] == '/') error(...)- if (u[r] == '/')- nominator = false;- // strange math parsing?- // else if (u[r] == '*')- // nominator = true;- l = r + 1;- }- }- concrete_type(NUMBER);- }-- // cancel out unnecessary units- void Number::reduce()- {- // apply conversion factor- value_ *= this->Units::reduce();- }-- void Number::normalize()- {- // apply conversion factor- value_ *= this->Units::normalize();- }-- bool Custom_Warning::operator== (const Expression& rhs) const- {- if (Custom_Warning_Ptr_Const r = Cast<Custom_Warning>(&rhs)) {- return message() == r->message();- }- return false;- }-- bool Custom_Error::operator== (const Expression& rhs) const- {- if (Custom_Error_Ptr_Const r = Cast<Custom_Error>(&rhs)) {- return message() == r->message();- }- return false;- }-- bool Number::operator== (const Expression& rhs) const- {- if (auto rhsnr = Cast<Number>(&rhs)) {- return *this == *rhsnr;- }- return false;- }-- bool Number::operator== (const Number& rhs) const- {- Number l(*this), r(rhs); l.reduce(); r.reduce();- size_t lhs_units = l.numerators.size() + l.denominators.size();- size_t rhs_units = r.numerators.size() + r.denominators.size();- // unitless and only having one unit seems equivalent (will change in future)- if (!lhs_units || !rhs_units) {- return NEAR_EQUAL(l.value(), r.value());- }- l.normalize(); r.normalize();- Units &lhs_unit = l, &rhs_unit = r;- return lhs_unit == rhs_unit &&- NEAR_EQUAL(l.value(), r.value());- }-- bool Number::operator< (const Number& rhs) const- {- Number l(*this), r(rhs); l.reduce(); r.reduce();- size_t lhs_units = l.numerators.size() + l.denominators.size();- size_t rhs_units = r.numerators.size() + r.denominators.size();- // unitless and only having one unit seems equivalent (will change in future)- if (!lhs_units || !rhs_units) {- return l.value() < r.value();- }- l.normalize(); r.normalize();- Units &lhs_unit = l, &rhs_unit = r;- if (!(lhs_unit == rhs_unit)) {- /* ToDo: do we always get usefull backtraces? */- throw Exception::IncompatibleUnits(rhs, *this);- }- return lhs_unit < rhs_unit ||- l.value() < r.value();- }-- bool String_Quoted::operator== (const Expression& rhs) const- {- if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(&rhs)) {- return (value() == qstr->value());- } else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(&rhs)) {- return (value() == cstr->value());- }- return false;- }-- bool String_Constant::is_invisible() const {- return value_.empty() && quote_mark_ == 0;- }-- bool String_Constant::operator== (const Expression& rhs) const- {- if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(&rhs)) {- return (value() == qstr->value());- } else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(&rhs)) {- return (value() == cstr->value());- }- return false;- }-- bool String_Schema::is_left_interpolant(void) const- {- return length() && first()->is_left_interpolant();- }- bool String_Schema::is_right_interpolant(void) const- {- return length() && last()->is_right_interpolant();- }-- bool String_Schema::operator== (const Expression& rhs) const- {- if (String_Schema_Ptr_Const r = Cast<String_Schema>(&rhs)) {- if (length() != r->length()) return false;- for (size_t i = 0, L = length(); i < L; ++i) {- Expression_Obj rv = (*r)[i];- Expression_Obj lv = (*this)[i];- if (!lv || !rv) return false;- if (!(*lv == *rv)) return false;- }- return true;- }- return false;- }-- bool Boolean::operator== (const Expression& rhs) const- {- if (Boolean_Ptr_Const r = Cast<Boolean>(&rhs)) {- return (value() == r->value());- }- return false;- }-- bool Color::operator== (const Expression& rhs) const- {- if (Color_Ptr_Const r = Cast<Color>(&rhs)) {- return r_ == r->r() &&- g_ == r->g() &&- b_ == r->b() &&- a_ == r->a();- }- return false;- }-- bool List::operator== (const Expression& rhs) const- {- if (List_Ptr_Const r = Cast<List>(&rhs)) {- if (length() != r->length()) return false;- if (separator() != r->separator()) return false;- if (is_bracketed() != r->is_bracketed()) return false;- for (size_t i = 0, L = length(); i < L; ++i) {- Expression_Obj rv = r->at(i);- Expression_Obj lv = this->at(i);- if (!lv || !rv) return false;- if (!(*lv == *rv)) return false;- }- return true;- }- return false;- }-- bool Map::operator== (const Expression& rhs) const- {- if (Map_Ptr_Const r = Cast<Map>(&rhs)) {- if (length() != r->length()) return false;- for (auto key : keys()) {- Expression_Obj lv = at(key);- Expression_Obj rv = r->at(key);- if (!rv || !lv) return false;- if (!(*lv == *rv)) return false;- }- return true;- }- return false;- }-- bool Null::operator== (const Expression& rhs) const- {- return rhs.concrete_type() == NULL_VAL;- }-- bool Function::operator== (const Expression& rhs) const- {- if (Function_Ptr_Const r = Cast<Function>(&rhs)) {- Definition_Ptr_Const d1 = Cast<Definition>(definition());- Definition_Ptr_Const d2 = Cast<Definition>(r->definition());- return d1 && d2 && d1 == d2 && is_css() == r->is_css();- }- return false;- }-- size_t List::size() const {- if (!is_arglist_) return length();- // arglist expects a list of arguments- // so we need to break before keywords- for (size_t i = 0, L = length(); i < L; ++i) {- Expression_Obj obj = this->at(i);- if (Argument_Ptr arg = Cast<Argument>(obj)) {- if (!arg->name().empty()) return i;- }- }- return length();- }-- Expression_Obj Hashed::at(Expression_Obj k) const- {- if (elements_.count(k))- { return elements_.at(k); }- else { return NULL; }- }-- bool Binary_Expression::is_left_interpolant(void) const- {- return is_interpolant() || (left() && left()->is_left_interpolant());- }- bool Binary_Expression::is_right_interpolant(void) const- {- return is_interpolant() || (right() && right()->is_right_interpolant());- }-- const std::string AST_Node::to_string(Sass_Inspect_Options opt) const- {- Sass_Output_Options out(opt);- Emitter emitter(out);- Inspect i(emitter);- i.in_declaration = true;- // ToDo: inspect should be const- const_cast<AST_Node_Ptr>(this)->perform(&i);- return i.get_buffer();- }-- const std::string AST_Node::to_string() const- {- return to_string({ NESTED, 5 });- }-- std::string String_Quoted::inspect() const- {- return quote(value_, '*');- }-- std::string String_Constant::inspect() const- {- return quote(value_, '*');- }-- bool Declaration::is_invisible() const- {- if (is_custom_property()) return false;-- return !(value_ && value_->concrete_type() != Expression::NULL_VAL);- }-- //////////////////////////////////////////////////////////////////////////////////////////- // Additional method on Lists to retrieve values directly or from an encompassed Argument.- //////////////////////////////////////////////////////////////////////////////////////////- Expression_Obj List::value_at_index(size_t i) {- Expression_Obj obj = this->at(i);- if (is_arglist_) {- if (Argument_Ptr arg = Cast<Argument>(obj)) {- return arg->value();- } else {- return obj;- }- } else {- return obj;- }- }-- //////////////////////////////////////////////////////////////////////////////////////////- // Convert map to (key, value) list.- //////////////////////////////////////////////////////////////////////////////////////////- List_Obj Map::to_list(ParserState& pstate) {- List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA);-- for (auto key : keys()) {- List_Obj l = SASS_MEMORY_NEW(List, pstate, 2);- l->append(key);- l->append(at(key));- ret->append(l);- }-- return ret;- }-- //////////////////////////////////////////////////////////////////////////////////////////- // Copy implementations- //////////////////////////////////////////////////////////////////////////////////////////-- #ifdef DEBUG_SHARED_PTR-- #define IMPLEMENT_AST_OPERATORS(klass) \- klass##_Ptr klass::copy(std::string file, size_t line) const { \- klass##_Ptr cpy = new klass(this); \- cpy->trace(file, line); \- return cpy; \- } \- klass##_Ptr klass::clone(std::string file, size_t line) const { \- klass##_Ptr cpy = copy(file, line); \- cpy->cloneChildren(); \- return cpy; \- } \-- #else-- #define IMPLEMENT_AST_OPERATORS(klass) \- klass##_Ptr klass::copy() const { \- return new klass(this); \- } \- klass##_Ptr klass::clone() const { \- klass##_Ptr cpy = copy(); \- cpy->cloneChildren(); \- return cpy; \- } \-- #endif-- IMPLEMENT_AST_OPERATORS(Supports_Operator);- IMPLEMENT_AST_OPERATORS(Supports_Negation);- IMPLEMENT_AST_OPERATORS(Compound_Selector);- IMPLEMENT_AST_OPERATORS(Complex_Selector);- IMPLEMENT_AST_OPERATORS(Element_Selector);- IMPLEMENT_AST_OPERATORS(Class_Selector);- IMPLEMENT_AST_OPERATORS(Id_Selector);- IMPLEMENT_AST_OPERATORS(Pseudo_Selector);- IMPLEMENT_AST_OPERATORS(Wrapped_Selector);- IMPLEMENT_AST_OPERATORS(Selector_List);- IMPLEMENT_AST_OPERATORS(Ruleset);- IMPLEMENT_AST_OPERATORS(Media_Block);- IMPLEMENT_AST_OPERATORS(Custom_Warning);- IMPLEMENT_AST_OPERATORS(Custom_Error);- IMPLEMENT_AST_OPERATORS(List);- IMPLEMENT_AST_OPERATORS(Map);- IMPLEMENT_AST_OPERATORS(Function);- IMPLEMENT_AST_OPERATORS(Number);- IMPLEMENT_AST_OPERATORS(Binary_Expression);- IMPLEMENT_AST_OPERATORS(String_Schema);- IMPLEMENT_AST_OPERATORS(String_Constant);- IMPLEMENT_AST_OPERATORS(String_Quoted);- IMPLEMENT_AST_OPERATORS(Boolean);- IMPLEMENT_AST_OPERATORS(Color);- IMPLEMENT_AST_OPERATORS(Null);- IMPLEMENT_AST_OPERATORS(Parent_Selector);- IMPLEMENT_AST_OPERATORS(Import);- IMPLEMENT_AST_OPERATORS(Import_Stub);- IMPLEMENT_AST_OPERATORS(Function_Call);- IMPLEMENT_AST_OPERATORS(Directive);- IMPLEMENT_AST_OPERATORS(At_Root_Block);- IMPLEMENT_AST_OPERATORS(Supports_Block);- IMPLEMENT_AST_OPERATORS(While);- IMPLEMENT_AST_OPERATORS(Each);- IMPLEMENT_AST_OPERATORS(For);- IMPLEMENT_AST_OPERATORS(If);- IMPLEMENT_AST_OPERATORS(Mixin_Call);- IMPLEMENT_AST_OPERATORS(Extension);- IMPLEMENT_AST_OPERATORS(Media_Query);- IMPLEMENT_AST_OPERATORS(Media_Query_Expression);- IMPLEMENT_AST_OPERATORS(Debug);- IMPLEMENT_AST_OPERATORS(Error);- IMPLEMENT_AST_OPERATORS(Warning);- IMPLEMENT_AST_OPERATORS(Assignment);- IMPLEMENT_AST_OPERATORS(Return);- IMPLEMENT_AST_OPERATORS(At_Root_Query);- IMPLEMENT_AST_OPERATORS(Variable);- IMPLEMENT_AST_OPERATORS(Comment);- IMPLEMENT_AST_OPERATORS(Attribute_Selector);- IMPLEMENT_AST_OPERATORS(Supports_Interpolation);- IMPLEMENT_AST_OPERATORS(Supports_Declaration);- IMPLEMENT_AST_OPERATORS(Supports_Condition);- IMPLEMENT_AST_OPERATORS(Parameters);- IMPLEMENT_AST_OPERATORS(Parameter);- IMPLEMENT_AST_OPERATORS(Arguments);- IMPLEMENT_AST_OPERATORS(Argument);- IMPLEMENT_AST_OPERATORS(Unary_Expression);- IMPLEMENT_AST_OPERATORS(Function_Call_Schema);- IMPLEMENT_AST_OPERATORS(Block);- IMPLEMENT_AST_OPERATORS(Content);- IMPLEMENT_AST_OPERATORS(Trace);- IMPLEMENT_AST_OPERATORS(Keyframe_Rule);- IMPLEMENT_AST_OPERATORS(Bubble);- IMPLEMENT_AST_OPERATORS(Selector_Schema);- IMPLEMENT_AST_OPERATORS(Placeholder_Selector);- IMPLEMENT_AST_OPERATORS(Definition);- IMPLEMENT_AST_OPERATORS(Declaration);+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"++namespace Sass {++ static Null sass_null(ParserState("null"));++ const char* sass_op_to_name(enum Sass_OP op) {+ switch (op) {+ case AND: return "and";+ case OR: return "or";+ case EQ: return "eq";+ case NEQ: return "neq";+ case GT: return "gt";+ case GTE: return "gte";+ case LT: return "lt";+ case LTE: return "lte";+ case ADD: return "plus";+ case SUB: return "minus";+ case MUL: return "times";+ case DIV: return "div";+ case MOD: return "mod";+ // this is only used internally!+ case NUM_OPS: return "[OPS]";+ default: return "invalid";+ }+ }++ const char* sass_op_separator(enum Sass_OP op) {+ switch (op) {+ case AND: return "&&";+ case OR: return "||";+ case EQ: return "==";+ case NEQ: return "!=";+ case GT: return ">";+ case GTE: return ">=";+ case LT: return "<";+ case LTE: return "<=";+ case ADD: return "+";+ case SUB: return "-";+ case MUL: return "*";+ case DIV: return "/";+ case MOD: return "%";+ // this is only used internally!+ case NUM_OPS: return "[OPS]";+ default: return "invalid";+ }+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ void AST_Node::update_pstate(const ParserState& pstate)+ {+ pstate_.offset += pstate - pstate_ + pstate.offset;+ }++ std::string AST_Node::to_string(Sass_Inspect_Options opt) const+ {+ Sass_Output_Options out(opt);+ Emitter emitter(out);+ Inspect i(emitter);+ i.in_declaration = true;+ // ToDo: inspect should be const+ const_cast<AST_Node*>(this)->perform(&i);+ return i.get_buffer();+ }++ std::string AST_Node::to_css(Sass_Inspect_Options opt) const+ {+ opt.output_style = TO_CSS;+ Sass_Output_Options out(opt);+ Emitter emitter(out);+ Inspect i(emitter);+ i.in_declaration = true;+ // ToDo: inspect should be const+ const_cast<AST_Node*>(this)->perform(&i);+ return i.get_buffer();+ }++ std::string AST_Node::to_string() const+ {+ return to_string({ NESTED, 5 });+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Statement::Statement(ParserState pstate, Type st, size_t t)+ : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)+ { }+ Statement::Statement(const Statement* ptr)+ : AST_Node(ptr),+ statement_type_(ptr->statement_type_),+ tabs_(ptr->tabs_),+ group_end_(ptr->group_end_)+ { }++ bool Statement::bubbles()+ {+ return false;+ }++ bool Statement::has_content()+ {+ return statement_type_ == CONTENT;+ }++ bool Statement::is_invisible() const+ {+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Block::Block(ParserState pstate, size_t s, bool r)+ : Statement(pstate),+ Vectorized<Statement_Obj>(s),+ is_root_(r)+ { }+ Block::Block(const Block* ptr)+ : Statement(ptr),+ Vectorized<Statement_Obj>(*ptr),+ is_root_(ptr->is_root_)+ { }++ bool Block::isInvisible() const+ {+ for (auto& item : elements()) {+ if (!item->is_invisible()) return false;+ }+ return true;+ }++ bool Block::has_content()+ {+ for (size_t i = 0, L = elements().size(); i < L; ++i) {+ if (elements()[i]->has_content()) return true;+ }+ return Statement::has_content();+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Has_Block::Has_Block(ParserState pstate, Block_Obj b)+ : Statement(pstate), block_(b)+ { }+ Has_Block::Has_Block(const Has_Block* ptr)+ : Statement(ptr), block_(ptr->block_)+ { }++ bool Has_Block::has_content()+ {+ return (block_ && block_->has_content()) || Statement::has_content();+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Ruleset::Ruleset(ParserState pstate, SelectorListObj s, Block_Obj b)+ : Has_Block(pstate, b), selector_(s), schema_(), is_root_(false)+ { statement_type(RULESET); }+ Ruleset::Ruleset(const Ruleset* ptr)+ : Has_Block(ptr),+ selector_(ptr->selector_),+ schema_(ptr->schema_),+ is_root_(ptr->is_root_)+ { statement_type(RULESET); }++ bool Ruleset::is_invisible() const {+ if (const SelectorList * sl = Cast<SelectorList>(selector())) {+ for (size_t i = 0, L = sl->length(); i < L; i += 1)+ if (!(*sl)[i]->isInvisible()) return false;+ }+ return true;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Bubble::Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g, size_t t)+ : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == nullptr)+ { }+ Bubble::Bubble(const Bubble* ptr)+ : Statement(ptr),+ node_(ptr->node_),+ group_end_(ptr->group_end_)+ { }++ bool Bubble::bubbles()+ {+ return true;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Trace::Trace(ParserState pstate, std::string n, Block_Obj b, char type)+ : Has_Block(pstate, b), type_(type), name_(n)+ { }+ Trace::Trace(const Trace* ptr)+ : Has_Block(ptr),+ type_(ptr->type_),+ name_(ptr->name_)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Directive::Directive(ParserState pstate, std::string kwd, SelectorListObj sel, Block_Obj b, Expression_Obj val)+ : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed+ { statement_type(DIRECTIVE); }+ Directive::Directive(const Directive* ptr)+ : Has_Block(ptr),+ keyword_(ptr->keyword_),+ selector_(ptr->selector_),+ value_(ptr->value_) // set value manually if needed+ { statement_type(DIRECTIVE); }++ bool Directive::bubbles() { return is_keyframes() || is_media(); }++ bool Directive::is_media() {+ return keyword_.compare("@-webkit-media") == 0 ||+ keyword_.compare("@-moz-media") == 0 ||+ keyword_.compare("@-o-media") == 0 ||+ keyword_.compare("@media") == 0;+ }+ bool Directive::is_keyframes() {+ return keyword_.compare("@-webkit-keyframes") == 0 ||+ keyword_.compare("@-moz-keyframes") == 0 ||+ keyword_.compare("@-o-keyframes") == 0 ||+ keyword_.compare("@keyframes") == 0;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Keyframe_Rule::Keyframe_Rule(ParserState pstate, Block_Obj b)+ : Has_Block(pstate, b), name_()+ { statement_type(KEYFRAMERULE); }+ Keyframe_Rule::Keyframe_Rule(const Keyframe_Rule* ptr)+ : Has_Block(ptr), name_(ptr->name_)+ { statement_type(KEYFRAMERULE); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Declaration::Declaration(ParserState pstate, String_Obj prop, Expression_Obj val, bool i, bool c, Block_Obj b)+ : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false)+ { statement_type(DECLARATION); }+ Declaration::Declaration(const Declaration* ptr)+ : Has_Block(ptr),+ property_(ptr->property_),+ value_(ptr->value_),+ is_important_(ptr->is_important_),+ is_custom_property_(ptr->is_custom_property_),+ is_indented_(ptr->is_indented_)+ { statement_type(DECLARATION); }++ bool Declaration::is_invisible() const+ {+ if (is_custom_property()) return false;+ return !(value_ && !Cast<Null>(value_));+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Assignment::Assignment(ParserState pstate, std::string var, Expression_Obj val, bool is_default, bool is_global)+ : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)+ { statement_type(ASSIGNMENT); }+ Assignment::Assignment(const Assignment* ptr)+ : Statement(ptr),+ variable_(ptr->variable_),+ value_(ptr->value_),+ is_default_(ptr->is_default_),+ is_global_(ptr->is_global_)+ { statement_type(ASSIGNMENT); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Import::Import(ParserState pstate)+ : Statement(pstate),+ urls_(std::vector<Expression_Obj>()),+ incs_(std::vector<Include>()),+ import_queries_()+ { statement_type(IMPORT); }+ Import::Import(const Import* ptr)+ : Statement(ptr),+ urls_(ptr->urls_),+ incs_(ptr->incs_),+ import_queries_(ptr->import_queries_)+ { statement_type(IMPORT); }++ std::vector<Include>& Import::incs() { return incs_; }+ std::vector<Expression_Obj>& Import::urls() { return urls_; }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Import_Stub::Import_Stub(ParserState pstate, Include res)+ : Statement(pstate), resource_(res)+ { statement_type(IMPORT_STUB); }+ Import_Stub::Import_Stub(const Import_Stub* ptr)+ : Statement(ptr), resource_(ptr->resource_)+ { statement_type(IMPORT_STUB); }+ Include Import_Stub::resource() { return resource_; };+ std::string Import_Stub::imp_path() { return resource_.imp_path; };+ std::string Import_Stub::abs_path() { return resource_.abs_path; };++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Warning::Warning(ParserState pstate, Expression_Obj msg)+ : Statement(pstate), message_(msg)+ { statement_type(WARNING); }+ Warning::Warning(const Warning* ptr)+ : Statement(ptr), message_(ptr->message_)+ { statement_type(WARNING); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Error::Error(ParserState pstate, Expression_Obj msg)+ : Statement(pstate), message_(msg)+ { statement_type(ERROR); }+ Error::Error(const Error* ptr)+ : Statement(ptr), message_(ptr->message_)+ { statement_type(ERROR); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Debug::Debug(ParserState pstate, Expression_Obj val)+ : Statement(pstate), value_(val)+ { statement_type(DEBUGSTMT); }+ Debug::Debug(const Debug* ptr)+ : Statement(ptr), value_(ptr->value_)+ { statement_type(DEBUGSTMT); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Comment::Comment(ParserState pstate, String_Obj txt, bool is_important)+ : Statement(pstate), text_(txt), is_important_(is_important)+ { statement_type(COMMENT); }+ Comment::Comment(const Comment* ptr)+ : Statement(ptr),+ text_(ptr->text_),+ is_important_(ptr->is_important_)+ { statement_type(COMMENT); }++ bool Comment::is_invisible() const+ {+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ If::If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt)+ : Has_Block(pstate, con), predicate_(pred), alternative_(alt)+ { statement_type(IF); }+ If::If(const If* ptr)+ : Has_Block(ptr),+ predicate_(ptr->predicate_),+ alternative_(ptr->alternative_)+ { statement_type(IF); }++ bool If::has_content()+ {+ return Has_Block::has_content() || (alternative_ && alternative_->has_content());+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ For::For(ParserState pstate,+ std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc)+ : Has_Block(pstate, b),+ variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)+ { statement_type(FOR); }+ For::For(const For* ptr)+ : Has_Block(ptr),+ variable_(ptr->variable_),+ lower_bound_(ptr->lower_bound_),+ upper_bound_(ptr->upper_bound_),+ is_inclusive_(ptr->is_inclusive_)+ { statement_type(FOR); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Each::Each(ParserState pstate, std::vector<std::string> vars, Expression_Obj lst, Block_Obj b)+ : Has_Block(pstate, b), variables_(vars), list_(lst)+ { statement_type(EACH); }+ Each::Each(const Each* ptr)+ : Has_Block(ptr), variables_(ptr->variables_), list_(ptr->list_)+ { statement_type(EACH); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ While::While(ParserState pstate, Expression_Obj pred, Block_Obj b)+ : Has_Block(pstate, b), predicate_(pred)+ { statement_type(WHILE); }+ While::While(const While* ptr)+ : Has_Block(ptr), predicate_(ptr->predicate_)+ { statement_type(WHILE); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Return::Return(ParserState pstate, Expression_Obj val)+ : Statement(pstate), value_(val)+ { statement_type(RETURN); }+ Return::Return(const Return* ptr)+ : Statement(ptr), value_(ptr->value_)+ { statement_type(RETURN); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ ExtendRule::ExtendRule(ParserState pstate, SelectorListObj s)+ : Statement(pstate), isOptional_(false), selector_(s), schema_()+ { statement_type(EXTEND); }+ ExtendRule::ExtendRule(ParserState pstate, Selector_Schema_Obj s)+ : Statement(pstate), isOptional_(false), selector_(), schema_(s)+ {+ statement_type(EXTEND);+ }+ ExtendRule::ExtendRule(const ExtendRule* ptr)+ : Statement(ptr),+ isOptional_(ptr->isOptional_),+ selector_(ptr->selector_),+ schema_(ptr->schema_)+ { statement_type(EXTEND); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Definition::Definition(const Definition* ptr)+ : Has_Block(ptr),+ name_(ptr->name_),+ parameters_(ptr->parameters_),+ environment_(ptr->environment_),+ type_(ptr->type_),+ native_function_(ptr->native_function_),+ c_function_(ptr->c_function_),+ cookie_(ptr->cookie_),+ is_overload_stub_(ptr->is_overload_stub_),+ signature_(ptr->signature_)+ { }++ Definition::Definition(ParserState pstate,+ std::string n,+ Parameters_Obj params,+ Block_Obj b,+ Type t)+ : Has_Block(pstate, b),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(t),+ native_function_(0),+ c_function_(0),+ cookie_(0),+ is_overload_stub_(false),+ signature_(0)+ { }++ Definition::Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters_Obj params,+ Native_Function func_ptr,+ bool overload_stub)+ : Has_Block(pstate, {}),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(func_ptr),+ c_function_(0),+ cookie_(0),+ is_overload_stub_(overload_stub),+ signature_(sig)+ { }++ Definition::Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters_Obj params,+ Sass_Function_Entry c_func)+ : Has_Block(pstate, {}),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(0),+ c_function_(c_func),+ cookie_(sass_function_get_cookie(c_func)),+ is_overload_stub_(false),+ signature_(sig)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Mixin_Call::Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Parameters_Obj b_params, Block_Obj b)+ : Has_Block(pstate, b), name_(n), arguments_(args), block_parameters_(b_params)+ { }+ Mixin_Call::Mixin_Call(const Mixin_Call* ptr)+ : Has_Block(ptr),+ name_(ptr->name_),+ arguments_(ptr->arguments_),+ block_parameters_(ptr->block_parameters_)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Content::Content(ParserState pstate, Arguments_Obj args)+ : Statement(pstate),+ arguments_(args)+ { statement_type(CONTENT); }+ Content::Content(const Content* ptr)+ : Statement(ptr),+ arguments_(ptr->arguments_)+ { statement_type(CONTENT); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Expression::Expression(ParserState pstate, bool d, bool e, bool i, Type ct)+ : AST_Node(pstate),+ is_delayed_(d),+ is_expanded_(e),+ is_interpolant_(i),+ concrete_type_(ct)+ { }++ Expression::Expression(const Expression* ptr)+ : AST_Node(ptr),+ is_delayed_(ptr->is_delayed_),+ is_expanded_(ptr->is_expanded_),+ is_interpolant_(ptr->is_interpolant_),+ concrete_type_(ptr->concrete_type_)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Unary_Expression::Unary_Expression(ParserState pstate, Type t, Expression_Obj o)+ : Expression(pstate), optype_(t), operand_(o), hash_(0)+ { }+ Unary_Expression::Unary_Expression(const Unary_Expression* ptr)+ : Expression(ptr),+ optype_(ptr->optype_),+ operand_(ptr->operand_),+ hash_(ptr->hash_)+ { }+ const std::string Unary_Expression::type_name() {+ switch (optype_) {+ case PLUS: return "plus";+ case MINUS: return "minus";+ case SLASH: return "slash";+ case NOT: return "not";+ default: return "invalid";+ }+ }+ bool Unary_Expression::operator==(const Expression& rhs) const+ {+ try+ {+ const Unary_Expression* m = Cast<Unary_Expression>(&rhs);+ if (m == 0) return false;+ return type() == m->type() &&+ *operand() == *m->operand();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }+ size_t Unary_Expression::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<size_t>()(optype_);+ hash_combine(hash_, operand()->hash());+ };+ return hash_;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Argument::Argument(ParserState pstate, Expression_Obj val, std::string n, bool rest, bool keyword)+ : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)+ {+ if (!name_.empty() && is_rest_argument_) {+ coreError("variable-length argument may not be passed by name", pstate_);+ }+ }+ Argument::Argument(const Argument* ptr)+ : Expression(ptr),+ value_(ptr->value_),+ name_(ptr->name_),+ is_rest_argument_(ptr->is_rest_argument_),+ is_keyword_argument_(ptr->is_keyword_argument_),+ hash_(ptr->hash_)+ {+ if (!name_.empty() && is_rest_argument_) {+ coreError("variable-length argument may not be passed by name", pstate_);+ }+ }++ void Argument::set_delayed(bool delayed)+ {+ if (value_) value_->set_delayed(delayed);+ is_delayed(delayed);+ }++ bool Argument::operator==(const Expression& rhs) const+ {+ try+ {+ const Argument* m = Cast<Argument>(&rhs);+ if (!(m && name() == m->name())) return false;+ return *value() == *m->value();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ size_t Argument::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(name());+ hash_combine(hash_, value()->hash());+ }+ return hash_;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Arguments::Arguments(ParserState pstate)+ : Expression(pstate),+ Vectorized<Argument_Obj>(),+ has_named_arguments_(false),+ has_rest_argument_(false),+ has_keyword_argument_(false)+ { }+ Arguments::Arguments(const Arguments* ptr)+ : Expression(ptr),+ Vectorized<Argument_Obj>(*ptr),+ has_named_arguments_(ptr->has_named_arguments_),+ has_rest_argument_(ptr->has_rest_argument_),+ has_keyword_argument_(ptr->has_keyword_argument_)+ { }++ void Arguments::set_delayed(bool delayed)+ {+ for (Argument_Obj arg : elements()) {+ if (arg) arg->set_delayed(delayed);+ }+ is_delayed(delayed);+ }++ Argument_Obj Arguments::get_rest_argument()+ {+ if (this->has_rest_argument()) {+ for (Argument_Obj arg : this->elements()) {+ if (arg->is_rest_argument()) {+ return arg;+ }+ }+ }+ return {};+ }++ Argument_Obj Arguments::get_keyword_argument()+ {+ if (this->has_keyword_argument()) {+ for (Argument_Obj arg : this->elements()) {+ if (arg->is_keyword_argument()) {+ return arg;+ }+ }+ }+ return {};+ }++ void Arguments::adjust_after_pushing(Argument_Obj a)+ {+ if (!a->name().empty()) {+ if (has_keyword_argument()) {+ coreError("named arguments must precede variable-length argument", a->pstate());+ }+ has_named_arguments(true);+ }+ else if (a->is_rest_argument()) {+ if (has_rest_argument()) {+ coreError("functions and mixins may only be called with one variable-length argument", a->pstate());+ }+ if (has_keyword_argument_) {+ coreError("only keyword arguments may follow variable arguments", a->pstate());+ }+ has_rest_argument(true);+ }+ else if (a->is_keyword_argument()) {+ if (has_keyword_argument()) {+ coreError("functions and mixins may only be called with one keyword argument", a->pstate());+ }+ has_keyword_argument(true);+ }+ else {+ if (has_rest_argument()) {+ coreError("ordinal arguments must precede variable-length arguments", a->pstate());+ }+ if (has_named_arguments()) {+ coreError("ordinal arguments must precede named arguments", a->pstate());+ }+ }+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Media_Query::Media_Query(ParserState pstate, String_Obj t, size_t s, bool n, bool r)+ : Expression(pstate), Vectorized<Media_Query_Expression_Obj>(s),+ media_type_(t), is_negated_(n), is_restricted_(r)+ { }+ Media_Query::Media_Query(const Media_Query* ptr)+ : Expression(ptr),+ Vectorized<Media_Query_Expression_Obj>(*ptr),+ media_type_(ptr->media_type_),+ is_negated_(ptr->is_negated_),+ is_restricted_(ptr->is_restricted_)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Media_Query_Expression::Media_Query_Expression(ParserState pstate,+ Expression_Obj f, Expression_Obj v, bool i)+ : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)+ { }+ Media_Query_Expression::Media_Query_Expression(const Media_Query_Expression* ptr)+ : Expression(ptr),+ feature_(ptr->feature_),+ value_(ptr->value_),+ is_interpolated_(ptr->is_interpolated_)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ At_Root_Query::At_Root_Query(ParserState pstate, Expression_Obj f, Expression_Obj v, bool i)+ : Expression(pstate), feature_(f), value_(v)+ { }+ At_Root_Query::At_Root_Query(const At_Root_Query* ptr)+ : Expression(ptr),+ feature_(ptr->feature_),+ value_(ptr->value_)+ { }++ bool At_Root_Query::exclude(std::string str)+ {+ bool with = feature() && unquote(feature()->to_string()).compare("with") == 0;+ List* l = static_cast<List*>(value().ptr());+ std::string v;++ if (with)+ {+ if (!l || l->length() == 0) return str.compare("rule") != 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->to_string());+ if (v.compare("all") == 0 || v == str) return false;+ }+ return true;+ }+ else+ {+ if (!l || !l->length()) return str.compare("rule") == 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->to_string());+ if (v.compare("all") == 0 || v == str) return true;+ }+ return false;+ }+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ At_Root_Block::At_Root_Block(ParserState pstate, Block_Obj b, At_Root_Query_Obj e)+ : Has_Block(pstate, b), expression_(e)+ { statement_type(ATROOT); }+ At_Root_Block::At_Root_Block(const At_Root_Block* ptr)+ : Has_Block(ptr), expression_(ptr->expression_)+ { statement_type(ATROOT); }++ bool At_Root_Block::bubbles() {+ return true;+ }++ bool At_Root_Block::exclude_node(Statement_Obj s) {+ if (expression() == nullptr)+ {+ return s->statement_type() == Statement::RULESET;+ }++ if (s->statement_type() == Statement::DIRECTIVE)+ {+ if (Directive_Obj dir = Cast<Directive>(s))+ {+ std::string keyword(dir->keyword());+ if (keyword.length() > 0) keyword.erase(0, 1);+ return expression()->exclude(keyword);+ }+ }+ if (s->statement_type() == Statement::MEDIA)+ {+ return expression()->exclude("media");+ }+ if (s->statement_type() == Statement::RULESET)+ {+ return expression()->exclude("rule");+ }+ if (s->statement_type() == Statement::SUPPORTS)+ {+ return expression()->exclude("supports");+ }+ if (Directive_Obj dir = Cast<Directive>(s))+ {+ if (dir->is_keyframes()) return expression()->exclude("keyframes");+ }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Parameter::Parameter(ParserState pstate, std::string n, Expression_Obj def, bool rest)+ : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)+ { }+ Parameter::Parameter(const Parameter* ptr)+ : AST_Node(ptr),+ name_(ptr->name_),+ default_value_(ptr->default_value_),+ is_rest_parameter_(ptr->is_rest_parameter_)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Parameters::Parameters(ParserState pstate)+ : AST_Node(pstate),+ Vectorized<Parameter_Obj>(),+ has_optional_parameters_(false),+ has_rest_parameter_(false)+ { }+ Parameters::Parameters(const Parameters* ptr)+ : AST_Node(ptr),+ Vectorized<Parameter_Obj>(*ptr),+ has_optional_parameters_(ptr->has_optional_parameters_),+ has_rest_parameter_(ptr->has_rest_parameter_)+ { }++ void Parameters::adjust_after_pushing(Parameter_Obj p)+ {+ if (p->default_value()) {+ if (has_rest_parameter()) {+ coreError("optional parameters may not be combined with variable-length parameters", p->pstate());+ }+ has_optional_parameters(true);+ }+ else if (p->is_rest_parameter()) {+ if (has_rest_parameter()) {+ coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate());+ }+ has_rest_parameter(true);+ }+ else {+ if (has_rest_parameter()) {+ coreError("required parameters must precede variable-length parameters", p->pstate());+ }+ if (has_optional_parameters()) {+ coreError("required parameters must precede optional parameters", p->pstate());+ }+ }+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ // If you forget to add a class here you will get+ // undefined reference to `vtable for Sass::Class'++ IMPLEMENT_AST_OPERATORS(Ruleset);+ IMPLEMENT_AST_OPERATORS(MediaRule);+ IMPLEMENT_AST_OPERATORS(CssMediaRule);+ IMPLEMENT_AST_OPERATORS(CssMediaQuery);+ IMPLEMENT_AST_OPERATORS(Import);+ IMPLEMENT_AST_OPERATORS(Import_Stub);+ IMPLEMENT_AST_OPERATORS(Directive);+ IMPLEMENT_AST_OPERATORS(At_Root_Block);+ IMPLEMENT_AST_OPERATORS(While);+ IMPLEMENT_AST_OPERATORS(Each);+ IMPLEMENT_AST_OPERATORS(For);+ IMPLEMENT_AST_OPERATORS(If);+ IMPLEMENT_AST_OPERATORS(Mixin_Call);+ IMPLEMENT_AST_OPERATORS(ExtendRule);+ IMPLEMENT_AST_OPERATORS(Media_Query);+ IMPLEMENT_AST_OPERATORS(Media_Query_Expression);+ IMPLEMENT_AST_OPERATORS(Debug);+ IMPLEMENT_AST_OPERATORS(Error);+ IMPLEMENT_AST_OPERATORS(Warning);+ IMPLEMENT_AST_OPERATORS(Assignment);+ IMPLEMENT_AST_OPERATORS(Return);+ IMPLEMENT_AST_OPERATORS(At_Root_Query);+ IMPLEMENT_AST_OPERATORS(Comment);+ IMPLEMENT_AST_OPERATORS(Parameters);+ IMPLEMENT_AST_OPERATORS(Parameter);+ IMPLEMENT_AST_OPERATORS(Arguments);+ IMPLEMENT_AST_OPERATORS(Argument);+ IMPLEMENT_AST_OPERATORS(Unary_Expression);+ IMPLEMENT_AST_OPERATORS(Block);+ IMPLEMENT_AST_OPERATORS(Content);+ IMPLEMENT_AST_OPERATORS(Trace);+ IMPLEMENT_AST_OPERATORS(Keyframe_Rule);+ IMPLEMENT_AST_OPERATORS(Bubble);+ IMPLEMENT_AST_OPERATORS(Definition);+ IMPLEMENT_AST_OPERATORS(Declaration);++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////+ }
libsass/src/ast.hpp view
@@ -1,3048 +1,1065 @@ #ifndef SASS_AST_H #define SASS_AST_H -#include "sass.hpp"-#include <set>-#include <deque>-#include <vector>-#include <string>-#include <sstream>-#include <iostream>-#include <typeinfo>-#include <algorithm>-#include "sass/base.h"-#include "ast_fwd_decl.hpp"--#ifdef DEBUG_SHARED_PTR--#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \- virtual klass##_Ptr copy(std::string, size_t) const = 0; \- virtual klass##_Ptr clone(std::string, size_t) const = 0; \--#define ATTACH_AST_OPERATIONS(klass) \- virtual klass##_Ptr copy(std::string, size_t) const; \- virtual klass##_Ptr clone(std::string, size_t) const; \--#else--#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \- virtual klass##_Ptr copy() const = 0; \- virtual klass##_Ptr clone() const = 0; \--#define ATTACH_AST_OPERATIONS(klass) \- virtual klass##_Ptr copy() const; \- virtual klass##_Ptr clone() const; \--#endif--#ifdef __clang__--/*- * There are some overloads used here that trigger the clang overload- * hiding warning. Specifically:- *- * Type type() which hides string type() from Expression- *- */-#pragma clang diagnostic push-#pragma clang diagnostic ignored "-Woverloaded-virtual"--#endif--#include "util.hpp"-#include "units.hpp"-#include "context.hpp"-#include "position.hpp"-#include "constants.hpp"-#include "operation.hpp"-#include "position.hpp"-#include "inspect.hpp"-#include "source_map.hpp"-#include "environment.hpp"-#include "error_handling.hpp"-#include "ast_def_macros.hpp"-#include "ast_fwd_decl.hpp"-#include "source_map.hpp"--#include "sass.h"--namespace Sass {-- // easier to search with name- const bool DELAYED = true;-- // ToDo: should this really be hardcoded- // Note: most methods follow precision option- const double NUMBER_EPSILON = 0.00000000000001;-- // macro to test if numbers are equal within a small error margin- #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON-- // ToDo: where does this fit best?- // We don't share this with C-API?- class Operand {- public:- Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false)- : operand(operand), ws_before(ws_before), ws_after(ws_after)- { }- public:- enum Sass_OP operand;- bool ws_before;- bool ws_after;- };-- //////////////////////////////////////////////////////////- // `hash_combine` comes from boost (functional/hash):- // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html- // Boost Software License - Version 1.0- // http://www.boost.org/users/license.html- template <typename T>- void hash_combine (std::size_t& seed, const T& val)- {- seed ^= std::hash<T>()(val) + 0x9e3779b9- + (seed<<6) + (seed>>2);- }- //////////////////////////////////////////////////////////-- //////////////////////////////////////////////////////////- // Abstract base class for all abstract syntax tree nodes.- //////////////////////////////////////////////////////////- class AST_Node : public SharedObj {- ADD_PROPERTY(ParserState, pstate)- public:- AST_Node(ParserState pstate)- : pstate_(pstate)- { }- AST_Node(const AST_Node* ptr)- : pstate_(ptr->pstate_)- { }-- // AST_Node(AST_Node& ptr) = delete;-- virtual ~AST_Node() = 0;- virtual size_t hash() { return 0; }- ATTACH_VIRTUAL_AST_OPERATIONS(AST_Node);- virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }- virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }- virtual const std::string to_string(Sass_Inspect_Options opt) const;- virtual const std::string to_string() const;- virtual void cloneChildren() {};- // generic find function (not fully implemented yet)- // ToDo: add specific implementions to all children- virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); };- public:- void update_pstate(const ParserState& pstate);- public:- Offset off() { return pstate(); }- Position pos() { return pstate(); }- ATTACH_OPERATIONS()- };- inline AST_Node::~AST_Node() { }-- //////////////////////////////////////////////////////////////////////- // define cast template now (need complete type)- //////////////////////////////////////////////////////////////////////-- template<class T>- T* Cast(AST_Node* ptr) {- return ptr && typeid(T) == typeid(*ptr) ?- static_cast<T*>(ptr) : NULL;- };-- template<class T>- const T* Cast(const AST_Node* ptr) {- return ptr && typeid(T) == typeid(*ptr) ?- static_cast<const T*>(ptr) : NULL;- };-- //////////////////////////////////////////////////////////////////////- // Abstract base class for expressions. This side of the AST hierarchy- // represents elements in value contexts, which exist primarily to be- // evaluated and returned.- //////////////////////////////////////////////////////////////////////- class Expression : public AST_Node {- public:- enum Concrete_Type {- NONE,- BOOLEAN,- NUMBER,- COLOR,- STRING,- LIST,- MAP,- SELECTOR,- NULL_VAL,- FUNCTION_VAL,- C_WARNING,- C_ERROR,- FUNCTION,- VARIABLE,- NUM_TYPES- };- enum Simple_Type {- SIMPLE,- ATTR_SEL,- PSEUDO_SEL,- WRAPPED_SEL,- };- private:- // expressions in some contexts shouldn't be evaluated- ADD_PROPERTY(bool, is_delayed)- ADD_PROPERTY(bool, is_expanded)- ADD_PROPERTY(bool, is_interpolant)- ADD_PROPERTY(Concrete_Type, concrete_type)- public:- Expression(ParserState pstate,- bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)- : AST_Node(pstate),- is_delayed_(d),- is_expanded_(e),- is_interpolant_(i),- concrete_type_(ct)- { }- Expression(const Expression* ptr)- : AST_Node(ptr),- is_delayed_(ptr->is_delayed_),- is_expanded_(ptr->is_expanded_),- is_interpolant_(ptr->is_interpolant_),- concrete_type_(ptr->concrete_type_)- { }- virtual operator bool() { return true; }- virtual ~Expression() { }- virtual std::string type() const { return ""; /* TODO: raise an error? */ }- virtual bool is_invisible() const { return false; }- static std::string type_name() { return ""; }- virtual bool is_false() { return false; }- // virtual bool is_true() { return !is_false(); }- virtual bool operator== (const Expression& rhs) const { return false; }- virtual bool eq(const Expression& rhs) const { return *this == rhs; };- virtual void set_delayed(bool delayed) { is_delayed(delayed); }- virtual bool has_interpolant() const { return is_interpolant(); }- virtual bool is_left_interpolant() const { return is_interpolant(); }- virtual bool is_right_interpolant() const { return is_interpolant(); }- virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }- virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }- ATTACH_VIRTUAL_AST_OPERATIONS(Expression);- virtual size_t hash() { return 0; }- };-- //////////////////////////////////////////////////////////////////////- // Still just an expression, but with a to_string method- //////////////////////////////////////////////////////////////////////- class PreValue : public Expression {- public:- PreValue(ParserState pstate,- bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)- : Expression(pstate, d, e, i, ct)- { }- PreValue(const PreValue* ptr)- : Expression(ptr)- { }- ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);- virtual ~PreValue() { }- };-- //////////////////////////////////////////////////////////////////////- // base class for values that support operations- //////////////////////////////////////////////////////////////////////- class Value : public Expression {- public:- Value(ParserState pstate,- bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)- : Expression(pstate, d, e, i, ct)- { }- Value(const Value* ptr)- : Expression(ptr)- { }- ATTACH_VIRTUAL_AST_OPERATIONS(Value);- virtual bool operator== (const Expression& rhs) const = 0;- };-}--/////////////////////////////////////////////////////////////////////////////////////-// Hash method specializations for std::unordered_map to work with Sass::Expression-/////////////////////////////////////////////////////////////////////////////////////--namespace std {- template<>- struct hash<Sass::Expression_Obj>- {- size_t operator()(Sass::Expression_Obj s) const- {- return s->hash();- }- };- template<>- struct equal_to<Sass::Expression_Obj>- {- bool operator()( Sass::Expression_Obj lhs, Sass::Expression_Obj rhs) const- {- return lhs->hash() == rhs->hash();- }- };-}--namespace Sass {-- /////////////////////////////////////////////////////////////////////////////- // Mixin class for AST nodes that should behave like vectors. Uses the- // "Template Method" design pattern to allow subclasses to adjust their flags- // when certain objects are pushed.- /////////////////////////////////////////////////////////////////////////////- template <typename T>- class Vectorized {- std::vector<T> elements_;- protected:- size_t hash_;- void reset_hash() { hash_ = 0; }- virtual void adjust_after_pushing(T element) { }- public:- Vectorized(size_t s = 0) : elements_(std::vector<T>()), hash_(0)- { elements_.reserve(s); }- virtual ~Vectorized() = 0;- size_t length() const { return elements_.size(); }- bool empty() const { return elements_.empty(); }- void clear() { return elements_.clear(); }- T last() const { return elements_.back(); }- T first() const { return elements_.front(); }- T& operator[](size_t i) { return elements_[i]; }- virtual const T& at(size_t i) const { return elements_.at(i); }- virtual T& at(size_t i) { return elements_.at(i); }- const T& operator[](size_t i) const { return elements_[i]; }- virtual void append(T element)- {- if (element) {- reset_hash();- elements_.push_back(element);- adjust_after_pushing(element);- }- }- virtual void concat(Vectorized* v)- {- for (size_t i = 0, L = v->length(); i < L; ++i) this->append((*v)[i]);- }- Vectorized& unshift(T element)- {- elements_.insert(elements_.begin(), element);- return *this;- }- std::vector<T>& elements() { return elements_; }- const std::vector<T>& elements() const { return elements_; }- std::vector<T>& elements(std::vector<T>& e) { elements_ = e; return elements_; }-- virtual size_t hash()- {- if (hash_ == 0) {- for (T& el : elements_) {- hash_combine(hash_, el->hash());- }- }- return hash_;- }-- typename std::vector<T>::iterator end() { return elements_.end(); }- typename std::vector<T>::iterator begin() { return elements_.begin(); }- typename std::vector<T>::const_iterator end() const { return elements_.end(); }- typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }- typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { return elements_.erase(el); }- typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { return elements_.erase(el); }-- };- template <typename T>- inline Vectorized<T>::~Vectorized() { }-- /////////////////////////////////////////////////////////////////////////////- // Mixin class for AST nodes that should behave like a hash table. Uses an- // extra <std::vector> internally to maintain insertion order for interation.- /////////////////////////////////////////////////////////////////////////////- class Hashed {- private:- ExpressionMap elements_;- std::vector<Expression_Obj> list_;- protected:- size_t hash_;- Expression_Obj duplicate_key_;- void reset_hash() { hash_ = 0; }- void reset_duplicate_key() { duplicate_key_ = 0; }- virtual void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) { }- public:- Hashed(size_t s = 0)- : elements_(ExpressionMap(s)),- list_(std::vector<Expression_Obj>()),- hash_(0), duplicate_key_(NULL)- { elements_.reserve(s); list_.reserve(s); }- virtual ~Hashed();- size_t length() const { return list_.size(); }- bool empty() const { return list_.empty(); }- bool has(Expression_Obj k) const { return elements_.count(k) == 1; }- Expression_Obj at(Expression_Obj k) const;- bool has_duplicate_key() const { return duplicate_key_ != 0; }- Expression_Obj get_duplicate_key() const { return duplicate_key_; }- const ExpressionMap elements() { return elements_; }- Hashed& operator<<(std::pair<Expression_Obj, Expression_Obj> p)- {- reset_hash();-- if (!has(p.first)) list_.push_back(p.first);- else if (!duplicate_key_) duplicate_key_ = p.first;-- elements_[p.first] = p.second;-- adjust_after_pushing(p);- return *this;- }- Hashed& operator+=(Hashed* h)- {- if (length() == 0) {- this->elements_ = h->elements_;- this->list_ = h->list_;- return *this;- }-- for (auto key : h->keys()) {- *this << std::make_pair(key, h->at(key));- }-- reset_duplicate_key();- return *this;- }- const ExpressionMap& pairs() const { return elements_; }- const std::vector<Expression_Obj>& keys() const { return list_; }--// std::unordered_map<Expression_Obj, Expression_Obj>::iterator end() { return elements_.end(); }-// std::unordered_map<Expression_Obj, Expression_Obj>::iterator begin() { return elements_.begin(); }-// std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator end() const { return elements_.end(); }-// std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator begin() const { return elements_.begin(); }-- };- inline Hashed::~Hashed() { }--- /////////////////////////////////////////////////////////////////////////- // Abstract base class for statements. This side of the AST hierarchy- // represents elements in expansion contexts, which exist primarily to be- // rewritten and macro-expanded.- /////////////////////////////////////////////////////////////////////////- class Statement : public AST_Node {- public:- enum Statement_Type {- NONE,- RULESET,- MEDIA,- DIRECTIVE,- SUPPORTS,- ATROOT,- BUBBLE,- CONTENT,- KEYFRAMERULE,- DECLARATION,- ASSIGNMENT,- IMPORT_STUB,- IMPORT,- COMMENT,- WARNING,- RETURN,- EXTEND,- ERROR,- DEBUGSTMT,- WHILE,- EACH,- FOR,- IF- };- private:- ADD_PROPERTY(Statement_Type, statement_type)- ADD_PROPERTY(size_t, tabs)- ADD_PROPERTY(bool, group_end)- public:- Statement(ParserState pstate, Statement_Type st = NONE, size_t t = 0)- : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)- { }- Statement(const Statement* ptr)- : AST_Node(ptr),- statement_type_(ptr->statement_type_),- tabs_(ptr->tabs_),- group_end_(ptr->group_end_)- { }- virtual ~Statement() = 0;- // needed for rearranging nested rulesets during CSS emission- virtual bool is_invisible() const { return false; }- virtual bool bubbles() { return false; }- virtual bool has_content()- {- return statement_type_ == CONTENT;- }- };- inline Statement::~Statement() { }-- ////////////////////////- // Blocks of statements.- ////////////////////////- class Block : public Statement, public Vectorized<Statement_Obj> {- ADD_PROPERTY(bool, is_root)- // needed for properly formatted CSS emission- protected:- void adjust_after_pushing(Statement_Obj s)- {- }- public:- Block(ParserState pstate, size_t s = 0, bool r = false)- : Statement(pstate),- Vectorized<Statement_Obj>(s),- is_root_(r)- { }- Block(const Block* ptr)- : Statement(ptr),- Vectorized<Statement_Obj>(*ptr),- is_root_(ptr->is_root_)- { }- virtual bool has_content()- {- for (size_t i = 0, L = elements().size(); i < L; ++i) {- if (elements()[i]->has_content()) return true;- }- return Statement::has_content();- }- ATTACH_AST_OPERATIONS(Block)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Abstract base class for statements that contain blocks of statements.- ////////////////////////////////////////////////////////////////////////- class Has_Block : public Statement {- ADD_PROPERTY(Block_Obj, block)- public:- Has_Block(ParserState pstate, Block_Obj b)- : Statement(pstate), block_(b)- { }- Has_Block(const Has_Block* ptr)- : Statement(ptr), block_(ptr->block_)- { }- virtual bool has_content()- {- return (block_ && block_->has_content()) || Statement::has_content();- }- virtual ~Has_Block() = 0;- };- inline Has_Block::~Has_Block() { }-- /////////////////////////////////////////////////////////////////////////////- // Rulesets (i.e., sets of styles headed by a selector and containing a block- // of style declarations.- /////////////////////////////////////////////////////////////////////////////- class Ruleset : public Has_Block {- ADD_PROPERTY(Selector_List_Obj, selector)- ADD_PROPERTY(bool, is_root);- public:- Ruleset(ParserState pstate, Selector_List_Obj s = 0, Block_Obj b = 0)- : Has_Block(pstate, b), selector_(s), is_root_(false)- { statement_type(RULESET); }- Ruleset(const Ruleset* ptr)- : Has_Block(ptr),- selector_(ptr->selector_),- is_root_(ptr->is_root_)- { statement_type(RULESET); }- bool is_invisible() const;- ATTACH_AST_OPERATIONS(Ruleset)- ATTACH_OPERATIONS()- };-- /////////////////- // Bubble.- /////////////////- class Bubble : public Statement {- ADD_PROPERTY(Statement_Obj, node)- ADD_PROPERTY(bool, group_end)- public:- Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = 0, size_t t = 0)- : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0)- { }- Bubble(const Bubble* ptr)- : Statement(ptr),- node_(ptr->node_),- group_end_(ptr->group_end_)- { }- bool bubbles() { return true; }- ATTACH_AST_OPERATIONS(Bubble)- ATTACH_OPERATIONS()- };-- /////////////////- // Trace.- /////////////////- class Trace : public Has_Block {- ADD_CONSTREF(char, type)- ADD_CONSTREF(std::string, name)- public:- Trace(ParserState pstate, std::string n, Block_Obj b = 0, char type = 'm')- : Has_Block(pstate, b), type_(type), name_(n)- { }- Trace(const Trace* ptr)- : Has_Block(ptr),- type_(ptr->type_),- name_(ptr->name_)- { }- ATTACH_AST_OPERATIONS(Trace)- ATTACH_OPERATIONS()- };-- /////////////////- // Media queries.- /////////////////- class Media_Block : public Has_Block {- ADD_PROPERTY(List_Obj, media_queries)- public:- Media_Block(ParserState pstate, List_Obj mqs, Block_Obj b)- : Has_Block(pstate, b), media_queries_(mqs)- { statement_type(MEDIA); }- Media_Block(const Media_Block* ptr)- : Has_Block(ptr), media_queries_(ptr->media_queries_)- { statement_type(MEDIA); }- bool bubbles() { return true; }- bool is_invisible() const;- ATTACH_AST_OPERATIONS(Media_Block)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // At-rules -- arbitrary directives beginning with "@" that may have an- // optional statement block.- ///////////////////////////////////////////////////////////////////////- class Directive : public Has_Block {- ADD_CONSTREF(std::string, keyword)- ADD_PROPERTY(Selector_List_Obj, selector)- ADD_PROPERTY(Expression_Obj, value)- public:- Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel = 0, Block_Obj b = 0, Expression_Obj val = 0)- : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed- { statement_type(DIRECTIVE); }- Directive(const Directive* ptr)- : Has_Block(ptr),- keyword_(ptr->keyword_),- selector_(ptr->selector_),- value_(ptr->value_) // set value manually if needed- { statement_type(DIRECTIVE); }- bool bubbles() { return is_keyframes() || is_media(); }- bool is_media() {- return keyword_.compare("@-webkit-media") == 0 ||- keyword_.compare("@-moz-media") == 0 ||- keyword_.compare("@-o-media") == 0 ||- keyword_.compare("@media") == 0;- }- bool is_keyframes() {- return keyword_.compare("@-webkit-keyframes") == 0 ||- keyword_.compare("@-moz-keyframes") == 0 ||- keyword_.compare("@-o-keyframes") == 0 ||- keyword_.compare("@keyframes") == 0;- }- ATTACH_AST_OPERATIONS(Directive)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // Keyframe-rules -- the child blocks of "@keyframes" nodes.- ///////////////////////////////////////////////////////////////////////- class Keyframe_Rule : public Has_Block {- // according to css spec, this should be <keyframes-name>- // <keyframes-name> = <custom-ident> | <string>- ADD_PROPERTY(Selector_List_Obj, name)- public:- Keyframe_Rule(ParserState pstate, Block_Obj b)- : Has_Block(pstate, b), name_()- { statement_type(KEYFRAMERULE); }- Keyframe_Rule(const Keyframe_Rule* ptr)- : Has_Block(ptr), name_(ptr->name_)- { statement_type(KEYFRAMERULE); }- ATTACH_AST_OPERATIONS(Keyframe_Rule)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Declarations -- style rules consisting of a property name and values.- ////////////////////////////////////////////////////////////////////////- class Declaration : public Has_Block {- ADD_PROPERTY(String_Obj, property)- ADD_PROPERTY(Expression_Obj, value)- ADD_PROPERTY(bool, is_important)- ADD_PROPERTY(bool, is_custom_property)- ADD_PROPERTY(bool, is_indented)- public:- Declaration(ParserState pstate,- String_Obj prop, Expression_Obj val, bool i = false, bool c = false, Block_Obj b = 0)- : Has_Block(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false)- { statement_type(DECLARATION); }- Declaration(const Declaration* ptr)- : Has_Block(ptr),- property_(ptr->property_),- value_(ptr->value_),- is_important_(ptr->is_important_),- is_custom_property_(ptr->is_custom_property_),- is_indented_(ptr->is_indented_)- { statement_type(DECLARATION); }- virtual bool is_invisible() const;- ATTACH_AST_OPERATIONS(Declaration)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////- // Assignments -- variable and value.- /////////////////////////////////////- class Assignment : public Statement {- ADD_CONSTREF(std::string, variable)- ADD_PROPERTY(Expression_Obj, value)- ADD_PROPERTY(bool, is_default)- ADD_PROPERTY(bool, is_global)- public:- Assignment(ParserState pstate,- std::string var, Expression_Obj val,- bool is_default = false,- bool is_global = false)- : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)- { statement_type(ASSIGNMENT); }- Assignment(const Assignment* ptr)- : Statement(ptr),- variable_(ptr->variable_),- value_(ptr->value_),- is_default_(ptr->is_default_),- is_global_(ptr->is_global_)- { statement_type(ASSIGNMENT); }- ATTACH_AST_OPERATIONS(Assignment)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // Import directives. CSS and Sass import lists can be intermingled, so it's- // necessary to store a list of each in an Import node.- ////////////////////////////////////////////////////////////////////////////- class Import : public Statement {- std::vector<Expression_Obj> urls_;- std::vector<Include> incs_;- ADD_PROPERTY(List_Obj, import_queries);- public:- Import(ParserState pstate)- : Statement(pstate),- urls_(std::vector<Expression_Obj>()),- incs_(std::vector<Include>()),- import_queries_()- { statement_type(IMPORT); }- Import(const Import* ptr)- : Statement(ptr),- urls_(ptr->urls_),- incs_(ptr->incs_),- import_queries_(ptr->import_queries_)- { statement_type(IMPORT); }- std::vector<Expression_Obj>& urls() { return urls_; }- std::vector<Include>& incs() { return incs_; }- ATTACH_AST_OPERATIONS(Import)- ATTACH_OPERATIONS()- };-- // not yet resolved single import- // so far we only know requested name- class Import_Stub : public Statement {- Include resource_;- public:- std::string abs_path() { return resource_.abs_path; };- std::string imp_path() { return resource_.imp_path; };- Include resource() { return resource_; };-- Import_Stub(ParserState pstate, Include res)- : Statement(pstate), resource_(res)- { statement_type(IMPORT_STUB); }- Import_Stub(const Import_Stub* ptr)- : Statement(ptr), resource_(ptr->resource_)- { statement_type(IMPORT_STUB); }- ATTACH_AST_OPERATIONS(Import_Stub)- ATTACH_OPERATIONS()- };-- //////////////////////////////- // The Sass `@warn` directive.- //////////////////////////////- class Warning : public Statement {- ADD_PROPERTY(Expression_Obj, message)- public:- Warning(ParserState pstate, Expression_Obj msg)- : Statement(pstate), message_(msg)- { statement_type(WARNING); }- Warning(const Warning* ptr)- : Statement(ptr), message_(ptr->message_)- { statement_type(WARNING); }- ATTACH_AST_OPERATIONS(Warning)- ATTACH_OPERATIONS()- };-- ///////////////////////////////- // The Sass `@error` directive.- ///////////////////////////////- class Error : public Statement {- ADD_PROPERTY(Expression_Obj, message)- public:- Error(ParserState pstate, Expression_Obj msg)- : Statement(pstate), message_(msg)- { statement_type(ERROR); }- Error(const Error* ptr)- : Statement(ptr), message_(ptr->message_)- { statement_type(ERROR); }- ATTACH_AST_OPERATIONS(Error)- ATTACH_OPERATIONS()- };-- ///////////////////////////////- // The Sass `@debug` directive.- ///////////////////////////////- class Debug : public Statement {- ADD_PROPERTY(Expression_Obj, value)- public:- Debug(ParserState pstate, Expression_Obj val)- : Statement(pstate), value_(val)- { statement_type(DEBUGSTMT); }- Debug(const Debug* ptr)- : Statement(ptr), value_(ptr->value_)- { statement_type(DEBUGSTMT); }- ATTACH_AST_OPERATIONS(Debug)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////- // CSS comments. These may be interpolated.- ///////////////////////////////////////////- class Comment : public Statement {- ADD_PROPERTY(String_Obj, text)- ADD_PROPERTY(bool, is_important)- public:- Comment(ParserState pstate, String_Obj txt, bool is_important)- : Statement(pstate), text_(txt), is_important_(is_important)- { statement_type(COMMENT); }- Comment(const Comment* ptr)- : Statement(ptr),- text_(ptr->text_),- is_important_(ptr->is_important_)- { statement_type(COMMENT); }- virtual bool is_invisible() const- { return /* is_important() == */ false; }- ATTACH_AST_OPERATIONS(Comment)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////- // The Sass `@if` control directive.- ////////////////////////////////////- class If : public Has_Block {- ADD_PROPERTY(Expression_Obj, predicate)- ADD_PROPERTY(Block_Obj, alternative)- public:- If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt = 0)- : Has_Block(pstate, con), predicate_(pred), alternative_(alt)- { statement_type(IF); }- If(const If* ptr)- : Has_Block(ptr),- predicate_(ptr->predicate_),- alternative_(ptr->alternative_)- { statement_type(IF); }- virtual bool has_content()- {- return Has_Block::has_content() || (alternative_ && alternative_->has_content());- }- ATTACH_AST_OPERATIONS(If)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////- // The Sass `@for` control directive.- /////////////////////////////////////- class For : public Has_Block {- ADD_CONSTREF(std::string, variable)- ADD_PROPERTY(Expression_Obj, lower_bound)- ADD_PROPERTY(Expression_Obj, upper_bound)- ADD_PROPERTY(bool, is_inclusive)- public:- For(ParserState pstate,- std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc)- : Has_Block(pstate, b),- variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)- { statement_type(FOR); }- For(const For* ptr)- : Has_Block(ptr),- variable_(ptr->variable_),- lower_bound_(ptr->lower_bound_),- upper_bound_(ptr->upper_bound_),- is_inclusive_(ptr->is_inclusive_)- { statement_type(FOR); }- ATTACH_AST_OPERATIONS(For)- ATTACH_OPERATIONS()- };-- //////////////////////////////////////- // The Sass `@each` control directive.- //////////////////////////////////////- class Each : public Has_Block {- ADD_PROPERTY(std::vector<std::string>, variables)- ADD_PROPERTY(Expression_Obj, list)- public:- Each(ParserState pstate, std::vector<std::string> vars, Expression_Obj lst, Block_Obj b)- : Has_Block(pstate, b), variables_(vars), list_(lst)- { statement_type(EACH); }- Each(const Each* ptr)- : Has_Block(ptr), variables_(ptr->variables_), list_(ptr->list_)- { statement_type(EACH); }- ATTACH_AST_OPERATIONS(Each)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////- // The Sass `@while` control directive.- ///////////////////////////////////////- class While : public Has_Block {- ADD_PROPERTY(Expression_Obj, predicate)- public:- While(ParserState pstate, Expression_Obj pred, Block_Obj b)- : Has_Block(pstate, b), predicate_(pred)- { statement_type(WHILE); }- While(const While* ptr)- : Has_Block(ptr), predicate_(ptr->predicate_)- { statement_type(WHILE); }- ATTACH_AST_OPERATIONS(While)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////- // The @return directive for use inside SassScript functions.- /////////////////////////////////////////////////////////////- class Return : public Statement {- ADD_PROPERTY(Expression_Obj, value)- public:- Return(ParserState pstate, Expression_Obj val)- : Statement(pstate), value_(val)- { statement_type(RETURN); }- Return(const Return* ptr)- : Statement(ptr), value_(ptr->value_)- { statement_type(RETURN); }- ATTACH_AST_OPERATIONS(Return)- ATTACH_OPERATIONS()- };-- ////////////////////////////////- // The Sass `@extend` directive.- ////////////////////////////////- class Extension : public Statement {- ADD_PROPERTY(Selector_List_Obj, selector)- public:- Extension(ParserState pstate, Selector_List_Obj s)- : Statement(pstate), selector_(s)- { statement_type(EXTEND); }- Extension(const Extension* ptr)- : Statement(ptr), selector_(ptr->selector_)- { statement_type(EXTEND); }- ATTACH_AST_OPERATIONS(Extension)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////////////- // Definitions for both mixins and functions. The two cases are distinguished- // by a type tag.- /////////////////////////////////////////////////////////////////////////////- struct Backtrace;- typedef const char* Signature;- typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>);- class Definition : public Has_Block {- public:- enum Type { MIXIN, FUNCTION };- ADD_CONSTREF(std::string, name)- ADD_PROPERTY(Parameters_Obj, parameters)- ADD_PROPERTY(Env*, environment)- ADD_PROPERTY(Type, type)- ADD_PROPERTY(Native_Function, native_function)- ADD_PROPERTY(Sass_Function_Entry, c_function)- ADD_PROPERTY(void*, cookie)- ADD_PROPERTY(bool, is_overload_stub)- ADD_PROPERTY(Signature, signature)- public:- Definition(const Definition* ptr)- : Has_Block(ptr),- name_(ptr->name_),- parameters_(ptr->parameters_),- environment_(ptr->environment_),- type_(ptr->type_),- native_function_(ptr->native_function_),- c_function_(ptr->c_function_),- cookie_(ptr->cookie_),- is_overload_stub_(ptr->is_overload_stub_),- signature_(ptr->signature_)- { }-- Definition(ParserState pstate,- std::string n,- Parameters_Obj params,- Block_Obj b,- Type t)- : Has_Block(pstate, b),- name_(n),- parameters_(params),- environment_(0),- type_(t),- native_function_(0),- c_function_(0),- cookie_(0),- is_overload_stub_(false),- signature_(0)- { }- Definition(ParserState pstate,- Signature sig,- std::string n,- Parameters_Obj params,- Native_Function func_ptr,- bool overload_stub = false)- : Has_Block(pstate, 0),- name_(n),- parameters_(params),- environment_(0),- type_(FUNCTION),- native_function_(func_ptr),- c_function_(0),- cookie_(0),- is_overload_stub_(overload_stub),- signature_(sig)- { }- Definition(ParserState pstate,- Signature sig,- std::string n,- Parameters_Obj params,- Sass_Function_Entry c_func,- bool whatever,- bool whatever2)- : Has_Block(pstate, 0),- name_(n),- parameters_(params),- environment_(0),- type_(FUNCTION),- native_function_(0),- c_function_(c_func),- cookie_(sass_function_get_cookie(c_func)),- is_overload_stub_(false),- signature_(sig)- { }- ATTACH_AST_OPERATIONS(Definition)- ATTACH_OPERATIONS()- };-- //////////////////////////////////////- // Mixin calls (i.e., `@include ...`).- //////////////////////////////////////- class Mixin_Call : public Has_Block {- ADD_CONSTREF(std::string, name)- ADD_PROPERTY(Arguments_Obj, arguments)- public:- Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Block_Obj b = 0)- : Has_Block(pstate, b), name_(n), arguments_(args)- { }- Mixin_Call(const Mixin_Call* ptr)- : Has_Block(ptr),- name_(ptr->name_),- arguments_(ptr->arguments_)- { }- ATTACH_AST_OPERATIONS(Mixin_Call)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////- // The @content directive for mixin content blocks.- ///////////////////////////////////////////////////- class Content : public Statement {- ADD_PROPERTY(Media_Block_Ptr, media_block)- public:- Content(ParserState pstate)- : Statement(pstate),- media_block_(NULL)- { statement_type(CONTENT); }- Content(const Content* ptr)- : Statement(ptr),- media_block_(ptr->media_block_)- { statement_type(CONTENT); }- ATTACH_AST_OPERATIONS(Content)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // Lists of values, both comma- and space-separated (distinguished by a- // type-tag.) Also used to represent variable-length argument lists.- ///////////////////////////////////////////////////////////////////////- class List : public Value, public Vectorized<Expression_Obj> {- void adjust_after_pushing(Expression_Obj e) { is_expanded(false); }- private:- ADD_PROPERTY(enum Sass_Separator, separator)- ADD_PROPERTY(bool, is_arglist)- ADD_PROPERTY(bool, is_bracketed)- ADD_PROPERTY(bool, from_selector)- public:- List(ParserState pstate,- size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false)- : Value(pstate),- Vectorized<Expression_Obj>(size),- separator_(sep),- is_arglist_(argl),- is_bracketed_(bracket),- from_selector_(false)- { concrete_type(LIST); }- List(const List* ptr)- : Value(ptr),- Vectorized<Expression_Obj>(*ptr),- separator_(ptr->separator_),- is_arglist_(ptr->is_arglist_),- is_bracketed_(ptr->is_bracketed_),- from_selector_(ptr->from_selector_)- { concrete_type(LIST); }- std::string type() const { return is_arglist_ ? "arglist" : "list"; }- static std::string type_name() { return "list"; }- const char* sep_string(bool compressed = false) const {- return separator() == SASS_SPACE ?- " " : (compressed ? "," : ", ");- }- bool is_invisible() const { return empty() && !is_bracketed(); }- Expression_Obj value_at_index(size_t i);-- virtual size_t size() const;-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(sep_string());- hash_combine(hash_, std::hash<bool>()(is_bracketed()));- for (size_t i = 0, L = length(); i < L; ++i)- hash_combine(hash_, (elements()[i])->hash());- }- return hash_;- }-- virtual void set_delayed(bool delayed)- {- is_delayed(delayed);- // don't set children- }-- virtual bool operator== (const Expression& rhs) const;-- ATTACH_AST_OPERATIONS(List)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////////////////////////- // Key value paris.- ///////////////////////////////////////////////////////////////////////- class Map : public Value, public Hashed {- void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) { is_expanded(false); }- public:- Map(ParserState pstate,- size_t size = 0)- : Value(pstate),- Hashed(size)- { concrete_type(MAP); }- Map(const Map* ptr)- : Value(ptr),- Hashed(*ptr)- { concrete_type(MAP); }- std::string type() const { return "map"; }- static std::string type_name() { return "map"; }- bool is_invisible() const { return empty(); }- List_Obj to_list(ParserState& pstate);-- virtual size_t hash()- {- if (hash_ == 0) {- for (auto key : keys()) {- hash_combine(hash_, key->hash());- hash_combine(hash_, at(key)->hash());- }- }-- return hash_;- }-- virtual bool operator== (const Expression& rhs) const;-- ATTACH_AST_OPERATIONS(Map)- ATTACH_OPERATIONS()- };-- inline static const std::string sass_op_to_name(enum Sass_OP op) {- switch (op) {- case AND: return "and";- case OR: return "or";- case EQ: return "eq";- case NEQ: return "neq";- case GT: return "gt";- case GTE: return "gte";- case LT: return "lt";- case LTE: return "lte";- case ADD: return "plus";- case SUB: return "sub";- case MUL: return "times";- case DIV: return "div";- case MOD: return "mod";- // this is only used internally!- case NUM_OPS: return "[OPS]";- default: return "invalid";- }- }-- inline static const std::string sass_op_separator(enum Sass_OP op) {- switch (op) {- case AND: return "&&";- case OR: return "||";- case EQ: return "==";- case NEQ: return "!=";- case GT: return ">";- case GTE: return ">=";- case LT: return "<";- case LTE: return "<=";- case ADD: return "+";- case SUB: return "-";- case MUL: return "*";- case DIV: return "/";- case MOD: return "%";- // this is only used internally!- case NUM_OPS: return "[OPS]";- default: return "invalid";- }- }-- //////////////////////////////////////////////////////////////////////////- // Binary expressions. Represents logical, relational, and arithmetic- // operations. Templatized to avoid large switch statements and repetitive- // subclassing.- //////////////////////////////////////////////////////////////////////////- class Binary_Expression : public PreValue {- private:- HASH_PROPERTY(Operand, op)- HASH_PROPERTY(Expression_Obj, left)- HASH_PROPERTY(Expression_Obj, right)- size_t hash_;- public:- Binary_Expression(ParserState pstate,- Operand op, Expression_Obj lhs, Expression_Obj rhs)- : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0)- { }- Binary_Expression(const Binary_Expression* ptr)- : PreValue(ptr),- op_(ptr->op_),- left_(ptr->left_),- right_(ptr->right_),- hash_(ptr->hash_)- { }- const std::string type_name() {- return sass_op_to_name(optype());- }- const std::string separator() {- return sass_op_separator(optype());- }- bool is_left_interpolant(void) const;- bool is_right_interpolant(void) const;- bool has_interpolant() const- {- return is_left_interpolant() ||- is_right_interpolant();- }- virtual void set_delayed(bool delayed)- {- right()->set_delayed(delayed);- left()->set_delayed(delayed);- is_delayed(delayed);- }- virtual bool operator==(const Expression& rhs) const- {- try- {- Binary_Expression_Ptr_Const m = Cast<Binary_Expression>(&rhs);- if (m == 0) return false;- return type() == m->type() &&- *left() == *m->left() &&- *right() == *m->right();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<size_t>()(optype());- hash_combine(hash_, left()->hash());- hash_combine(hash_, right()->hash());- }- return hash_;- }- enum Sass_OP optype() const { return op_.operand; }- ATTACH_AST_OPERATIONS(Binary_Expression)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // Arithmetic negation (logical negation is just an ordinary function call).- ////////////////////////////////////////////////////////////////////////////- class Unary_Expression : public Expression {- public:- enum Type { PLUS, MINUS, NOT, SLASH };- private:- HASH_PROPERTY(Type, optype)- HASH_PROPERTY(Expression_Obj, operand)- size_t hash_;- public:- Unary_Expression(ParserState pstate, Type t, Expression_Obj o)- : Expression(pstate), optype_(t), operand_(o), hash_(0)- { }- Unary_Expression(const Unary_Expression* ptr)- : Expression(ptr),- optype_(ptr->optype_),- operand_(ptr->operand_),- hash_(ptr->hash_)- { }- const std::string type_name() {- switch (optype_) {- case PLUS: return "plus";- case MINUS: return "minus";- case SLASH: return "slash";- case NOT: return "not";- default: return "invalid";- }- }- virtual bool operator==(const Expression& rhs) const- {- try- {- Unary_Expression_Ptr_Const m = Cast<Unary_Expression>(&rhs);- if (m == 0) return false;- return type() == m->type() &&- *operand() == *m->operand();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<size_t>()(optype_);- hash_combine(hash_, operand()->hash());- };- return hash_;- }- ATTACH_AST_OPERATIONS(Unary_Expression)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////- // Individual argument objects for mixin and function calls.- ////////////////////////////////////////////////////////////- class Argument : public Expression {- HASH_PROPERTY(Expression_Obj, value)- HASH_CONSTREF(std::string, name)- ADD_PROPERTY(bool, is_rest_argument)- ADD_PROPERTY(bool, is_keyword_argument)- size_t hash_;- public:- Argument(ParserState pstate, Expression_Obj val, std::string n = "", bool rest = false, bool keyword = false)- : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)- {- if (!name_.empty() && is_rest_argument_) {- coreError("variable-length argument may not be passed by name", pstate_);- }- }- Argument(const Argument* ptr)- : Expression(ptr),- value_(ptr->value_),- name_(ptr->name_),- is_rest_argument_(ptr->is_rest_argument_),- is_keyword_argument_(ptr->is_keyword_argument_),- hash_(ptr->hash_)- {- if (!name_.empty() && is_rest_argument_) {- coreError("variable-length argument may not be passed by name", pstate_);- }- }-- virtual void set_delayed(bool delayed);- virtual bool operator==(const Expression& rhs) const- {- try- {- Argument_Ptr_Const m = Cast<Argument>(&rhs);- if (!(m && name() == m->name())) return false;- return *value() == *m->value();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(name());- hash_combine(hash_, value()->hash());- }- return hash_;- }-- ATTACH_AST_OPERATIONS(Argument)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Argument lists -- in their own class to facilitate context-sensitive- // error checking (e.g., ensuring that all ordinal arguments precede all- // named arguments).- ////////////////////////////////////////////////////////////////////////- class Arguments : public Expression, public Vectorized<Argument_Obj> {- ADD_PROPERTY(bool, has_named_arguments)- ADD_PROPERTY(bool, has_rest_argument)- ADD_PROPERTY(bool, has_keyword_argument)- protected:- void adjust_after_pushing(Argument_Obj a);- public:- Arguments(ParserState pstate)- : Expression(pstate),- Vectorized<Argument_Obj>(),- has_named_arguments_(false),- has_rest_argument_(false),- has_keyword_argument_(false)- { }- Arguments(const Arguments* ptr)- : Expression(ptr),- Vectorized<Argument_Obj>(*ptr),- has_named_arguments_(ptr->has_named_arguments_),- has_rest_argument_(ptr->has_rest_argument_),- has_keyword_argument_(ptr->has_keyword_argument_)- { }-- virtual void set_delayed(bool delayed);-- Argument_Obj get_rest_argument();- Argument_Obj get_keyword_argument();-- ATTACH_AST_OPERATIONS(Arguments)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////- // Function reference.- ////////////////////////////////////////////////////- class Function : public Value {- public:- ADD_PROPERTY(Definition_Obj, definition)- ADD_PROPERTY(bool, is_css)- public:- Function(ParserState pstate, Definition_Obj def, bool css)- : Value(pstate), definition_(def), is_css_(css)- { concrete_type(FUNCTION_VAL); }- Function(const Function* ptr)- : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_)- { concrete_type(FUNCTION_VAL); }-- std::string type() const { return "function"; }- static std::string type_name() { return "function"; }- bool is_invisible() const { return true; }-- std::string name() {- if (definition_) {- return definition_->name();- }- return "";- }-- virtual bool operator== (const Expression& rhs) const;-- ATTACH_AST_OPERATIONS(Function)- ATTACH_OPERATIONS()- };-- //////////////////- // Function calls.- //////////////////- class Function_Call : public PreValue {- HASH_CONSTREF(std::string, name)- HASH_PROPERTY(Arguments_Obj, arguments)- HASH_PROPERTY(Function_Obj, func)- ADD_PROPERTY(bool, via_call)- ADD_PROPERTY(void*, cookie)- size_t hash_;- public:- Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie)- : PreValue(pstate), name_(n), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0)- { concrete_type(FUNCTION); }- Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func)- : PreValue(pstate), name_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0)- { concrete_type(FUNCTION); }- Function_Call(ParserState pstate, std::string n, Arguments_Obj args)- : PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0)- { concrete_type(FUNCTION); }- Function_Call(const Function_Call* ptr)- : PreValue(ptr),- name_(ptr->name_),- arguments_(ptr->arguments_),- func_(ptr->func_),- via_call_(ptr->via_call_),- cookie_(ptr->cookie_),- hash_(ptr->hash_)- { concrete_type(FUNCTION); }-- bool is_css() {- if (func_) return func_->is_css();- return false;- }-- virtual bool operator==(const Expression& rhs) const- {- try- {- Function_Call_Ptr_Const m = Cast<Function_Call>(&rhs);- if (!(m && name() == m->name())) return false;- if (!(m && arguments()->length() == m->arguments()->length())) return false;- for (size_t i =0, L = arguments()->length(); i < L; ++i)- if (!(*(*arguments())[i] == *(*m->arguments())[i])) return false;- return true;- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(name());- for (auto argument : arguments()->elements())- hash_combine(hash_, argument->hash());- }- return hash_;- }- ATTACH_AST_OPERATIONS(Function_Call)- ATTACH_OPERATIONS()- };-- /////////////////////////- // Function call schemas.- /////////////////////////- class Function_Call_Schema : public Expression {- ADD_PROPERTY(String_Obj, name)- ADD_PROPERTY(Arguments_Obj, arguments)- public:- Function_Call_Schema(ParserState pstate, String_Obj n, Arguments_Obj args)- : Expression(pstate), name_(n), arguments_(args)- { concrete_type(STRING); }- Function_Call_Schema(const Function_Call_Schema* ptr)- : Expression(ptr),- name_(ptr->name_),- arguments_(ptr->arguments_)- { concrete_type(STRING); }- ATTACH_AST_OPERATIONS(Function_Call_Schema)- ATTACH_OPERATIONS()- };-- ///////////////////////- // Variable references.- ///////////////////////- class Variable : public PreValue {- ADD_CONSTREF(std::string, name)- public:- Variable(ParserState pstate, std::string n)- : PreValue(pstate), name_(n)- { concrete_type(VARIABLE); }- Variable(const Variable* ptr)- : PreValue(ptr), name_(ptr->name_)- { concrete_type(VARIABLE); }-- virtual bool operator==(const Expression& rhs) const- {- try- {- Variable_Ptr_Const e = Cast<Variable>(&rhs);- return e && name() == e->name();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }-- virtual size_t hash()- {- return std::hash<std::string>()(name());- }-- ATTACH_AST_OPERATIONS(Variable)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////- // Numbers, percentages, dimensions, and colors.- ////////////////////////////////////////////////- class Number : public Value, public Units {- HASH_PROPERTY(double, value)- ADD_PROPERTY(bool, zero)- size_t hash_;- public:- Number(ParserState pstate, double val, std::string u = "", bool zero = true);-- Number(const Number* ptr)- : Value(ptr),- Units(ptr),- value_(ptr->value_), zero_(ptr->zero_),- hash_(ptr->hash_)- { concrete_type(NUMBER); }-- bool zero() { return zero_; }- std::string type() const { return "number"; }- static std::string type_name() { return "number"; }-- void reduce();- void normalize();-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<double>()(value_);- for (const auto numerator : numerators)- hash_combine(hash_, std::hash<std::string>()(numerator));- for (const auto denominator : denominators)- hash_combine(hash_, std::hash<std::string>()(denominator));- }- return hash_;- }-- virtual bool operator< (const Number& rhs) const;- virtual bool operator== (const Number& rhs) const;- virtual bool operator== (const Expression& rhs) const;- ATTACH_AST_OPERATIONS(Number)- ATTACH_OPERATIONS()- };-- //////////- // Colors.- //////////- class Color : public Value {- HASH_PROPERTY(double, r)- HASH_PROPERTY(double, g)- HASH_PROPERTY(double, b)- HASH_PROPERTY(double, a)- ADD_CONSTREF(std::string, disp)- size_t hash_;- public:- Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "")- : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp),- hash_(0)- { concrete_type(COLOR); }- Color(const Color* ptr)- : Value(ptr),- r_(ptr->r_),- g_(ptr->g_),- b_(ptr->b_),- a_(ptr->a_),- disp_(ptr->disp_),- hash_(ptr->hash_)- { concrete_type(COLOR); }- std::string type() const { return "color"; }- static std::string type_name() { return "color"; }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<double>()(a_);- hash_combine(hash_, std::hash<double>()(r_));- hash_combine(hash_, std::hash<double>()(g_));- hash_combine(hash_, std::hash<double>()(b_));- }- return hash_;- }-- virtual bool operator== (const Expression& rhs) const;-- ATTACH_AST_OPERATIONS(Color)- ATTACH_OPERATIONS()- };-- //////////////////////////////- // Errors from Sass_Values.- //////////////////////////////- class Custom_Error : public Value {- ADD_CONSTREF(std::string, message)- public:- Custom_Error(ParserState pstate, std::string msg)- : Value(pstate), message_(msg)- { concrete_type(C_ERROR); }- Custom_Error(const Custom_Error* ptr)- : Value(ptr), message_(ptr->message_)- { concrete_type(C_ERROR); }- virtual bool operator== (const Expression& rhs) const;- ATTACH_AST_OPERATIONS(Custom_Error)- ATTACH_OPERATIONS()- };-- //////////////////////////////- // Warnings from Sass_Values.- //////////////////////////////- class Custom_Warning : public Value {- ADD_CONSTREF(std::string, message)- public:- Custom_Warning(ParserState pstate, std::string msg)- : Value(pstate), message_(msg)- { concrete_type(C_WARNING); }- Custom_Warning(const Custom_Warning* ptr)- : Value(ptr), message_(ptr->message_)- { concrete_type(C_WARNING); }- virtual bool operator== (const Expression& rhs) const;- ATTACH_AST_OPERATIONS(Custom_Warning)- ATTACH_OPERATIONS()- };-- ////////////- // Booleans.- ////////////- class Boolean : public Value {- HASH_PROPERTY(bool, value)- size_t hash_;- public:- Boolean(ParserState pstate, bool val)- : Value(pstate), value_(val),- hash_(0)- { concrete_type(BOOLEAN); }- Boolean(const Boolean* ptr)- : Value(ptr),- value_(ptr->value_),- hash_(ptr->hash_)- { concrete_type(BOOLEAN); }- virtual operator bool() { return value_; }- std::string type() const { return "bool"; }- static std::string type_name() { return "bool"; }- virtual bool is_false() { return !value_; }-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<bool>()(value_);- }- return hash_;- }-- virtual bool operator== (const Expression& rhs) const;-- ATTACH_AST_OPERATIONS(Boolean)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////- // Abstract base class for Sass string values. Includes interpolated and- // "flat" strings.- ////////////////////////////////////////////////////////////////////////- class String : public Value {- public:- String(ParserState pstate, bool delayed = false)- : Value(pstate, delayed)- { concrete_type(STRING); }- String(const String* ptr)- : Value(ptr)- { concrete_type(STRING); }- static std::string type_name() { return "string"; }- virtual ~String() = 0;- virtual void rtrim() = 0;- virtual bool operator==(const Expression& rhs) const = 0;- virtual bool operator<(const Expression& rhs) const {- return this->to_string() < rhs.to_string();- };- ATTACH_VIRTUAL_AST_OPERATIONS(String);- ATTACH_OPERATIONS()- };- inline String::~String() { };-- ///////////////////////////////////////////////////////////////////////- // Interpolated strings. Meant to be reduced to flat strings during the- // evaluation phase.- ///////////////////////////////////////////////////////////////////////- class String_Schema : public String, public Vectorized<Expression_Obj> {- ADD_PROPERTY(bool, css)- size_t hash_;- public:- String_Schema(ParserState pstate, size_t size = 0, bool css = true)- : String(pstate), Vectorized<Expression_Obj>(size), css_(css), hash_(0)- { concrete_type(STRING); }- String_Schema(const String_Schema* ptr)- : String(ptr),- Vectorized<Expression_Obj>(*ptr),- css_(ptr->css_),- hash_(ptr->hash_)- { concrete_type(STRING); }-- std::string type() const { return "string"; }- static std::string type_name() { return "string"; }-- bool is_left_interpolant(void) const;- bool is_right_interpolant(void) const;- // void has_interpolants(bool tc) { }- bool has_interpolants() {- for (auto el : elements()) {- if (el->is_interpolant()) return true;- }- return false;- }- virtual void rtrim();-- virtual size_t hash()- {- if (hash_ == 0) {- for (auto string : elements())- hash_combine(hash_, string->hash());- }- return hash_;- }-- virtual void set_delayed(bool delayed) {- is_delayed(delayed);- }-- virtual bool operator==(const Expression& rhs) const;- ATTACH_AST_OPERATIONS(String_Schema)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////- // Flat strings -- the lowest level of raw textual data.- ////////////////////////////////////////////////////////- class String_Constant : public String {- ADD_PROPERTY(char, quote_mark)- ADD_PROPERTY(bool, can_compress_whitespace)- HASH_CONSTREF(std::string, value)- protected:- size_t hash_;- public:- String_Constant(const String_Constant* ptr)- : String(ptr),- quote_mark_(ptr->quote_mark_),- can_compress_whitespace_(ptr->can_compress_whitespace_),- value_(ptr->value_),- hash_(ptr->hash_)- { }- String_Constant(ParserState pstate, std::string val, bool css = true)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0)- { }- String_Constant(ParserState pstate, const char* beg, bool css = true)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0)- { }- String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0)- { }- String_Constant(ParserState pstate, const Token& tok, bool css = true)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0)- { }- std::string type() const { return "string"; }- static std::string type_name() { return "string"; }- virtual bool is_invisible() const;- virtual void rtrim();-- virtual size_t hash()- {- if (hash_ == 0) {- hash_ = std::hash<std::string>()(value_);- }- return hash_;- }-- virtual bool operator==(const Expression& rhs) const;- virtual std::string inspect() const; // quotes are forced on inspection-- // static char auto_quote() { return '*'; }- static char double_quote() { return '"'; }- static char single_quote() { return '\''; }-- ATTACH_AST_OPERATIONS(String_Constant)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////- // Possibly quoted string (unquote on instantiation)- ////////////////////////////////////////////////////////- class String_Quoted : public String_Constant {- public:- String_Quoted(ParserState pstate, std::string val, char q = 0,- bool keep_utf8_escapes = false, bool skip_unquoting = false,- bool strict_unquoting = true, bool css = true)- : String_Constant(pstate, val, css)- {- if (skip_unquoting == false) {- value_ = unquote(value_, "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 = Cast<Directive>(s))- {- std::string keyword(dir->keyword());- if (keyword.length() > 0) keyword.erase(0, 1);- return expression()->exclude(keyword);- }- }- if (s->statement_type() == Statement::MEDIA)- {- return expression()->exclude("media");- }- if (s->statement_type() == Statement::RULESET)- {- return expression()->exclude("rule");- }- if (s->statement_type() == Statement::SUPPORTS)- {- return expression()->exclude("supports");- }- if (Directive_Obj dir = Cast<Directive>(s))- {- if (dir->is_keyframes()) return expression()->exclude("keyframes");- }- return false;- }- ATTACH_AST_OPERATIONS(At_Root_Block)- ATTACH_OPERATIONS()- };-- //////////////////- // The null value.- //////////////////- class Null : public Value {- public:- Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); }- Null(const Null* ptr) : Value(ptr) { concrete_type(NULL_VAL); }- std::string type() const { return "null"; }- static std::string type_name() { return "null"; }- bool is_invisible() const { return true; }- operator bool() { return false; }- bool is_false() { return true; }-- virtual size_t hash()- {- return -1;- }-- virtual bool operator== (const Expression& rhs) const;-- ATTACH_AST_OPERATIONS(Null)- ATTACH_OPERATIONS()- };-- /////////////////////////////////- // Thunks for delayed evaluation.- /////////////////////////////////- class Thunk : public Expression {- ADD_PROPERTY(Expression_Obj, expression)- ADD_PROPERTY(Env*, environment)- public:- Thunk(ParserState pstate, Expression_Obj exp, Env* env = 0)- : Expression(pstate), expression_(exp), environment_(env)- { }- };-- /////////////////////////////////////////////////////////- // Individual parameter objects for mixins and functions.- /////////////////////////////////////////////////////////- class Parameter : public AST_Node {- ADD_CONSTREF(std::string, name)- ADD_PROPERTY(Expression_Obj, default_value)- ADD_PROPERTY(bool, is_rest_parameter)- public:- Parameter(ParserState pstate,- std::string n, Expression_Obj def = 0, bool rest = false)- : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)- {- // tried to come up with a spec test for this, but it does no longer- // get past the parser (it error out earlier). A spec test was added!- // if (default_value_ && is_rest_parameter_) {- // error("variable-length parameter may not have a default value", pstate_);- // }- }- Parameter(const Parameter* ptr)- : AST_Node(ptr),- name_(ptr->name_),- default_value_(ptr->default_value_),- is_rest_parameter_(ptr->is_rest_parameter_)- {- // tried to come up with a spec test for this, but it does no longer- // get past the parser (it error out earlier). A spec test was added!- // if (default_value_ && is_rest_parameter_) {- // error("variable-length parameter may not have a default value", pstate_);- // }- }- ATTACH_AST_OPERATIONS(Parameter)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////////- // Parameter lists -- in their own class to facilitate context-sensitive- // error checking (e.g., ensuring that all optional parameters follow all- // required parameters).- /////////////////////////////////////////////////////////////////////////- class Parameters : public AST_Node, public Vectorized<Parameter_Obj> {- ADD_PROPERTY(bool, has_optional_parameters)- ADD_PROPERTY(bool, has_rest_parameter)- protected:- void adjust_after_pushing(Parameter_Obj p)- {- if (p->default_value()) {- if (has_rest_parameter()) {- coreError("optional parameters may not be combined with variable-length parameters", p->pstate());- }- has_optional_parameters(true);- }- else if (p->is_rest_parameter()) {- if (has_rest_parameter()) {- coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate());- }- has_rest_parameter(true);- }- else {- if (has_rest_parameter()) {- coreError("required parameters must precede variable-length parameters", p->pstate());- }- if (has_optional_parameters()) {- coreError("required parameters must precede optional parameters", p->pstate());- }- }- }- public:- Parameters(ParserState pstate)- : AST_Node(pstate),- Vectorized<Parameter_Obj>(),- has_optional_parameters_(false),- has_rest_parameter_(false)- { }- Parameters(const Parameters* ptr)- : AST_Node(ptr),- Vectorized<Parameter_Obj>(*ptr),- has_optional_parameters_(ptr->has_optional_parameters_),- has_rest_parameter_(ptr->has_rest_parameter_)- { }- ATTACH_AST_OPERATIONS(Parameters)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////- // Abstract base class for CSS selectors.- /////////////////////////////////////////- class Selector : public Expression {- // ADD_PROPERTY(bool, has_reference)- // line break before list separator- ADD_PROPERTY(bool, has_line_feed)- // line break after list separator- ADD_PROPERTY(bool, has_line_break)- // maybe we have optional flag- ADD_PROPERTY(bool, is_optional)- // parent block pointers-- // must not be a reference counted object- // otherwise we create circular references- ADD_PROPERTY(Media_Block_Ptr, media_block)- protected:- size_t hash_;- public:- Selector(ParserState pstate)- : Expression(pstate),- has_line_feed_(false),- has_line_break_(false),- is_optional_(false),- media_block_(0),- hash_(0)- { concrete_type(SELECTOR); }- Selector(const Selector* ptr)- : Expression(ptr),- // has_reference_(ptr->has_reference_),- has_line_feed_(ptr->has_line_feed_),- has_line_break_(ptr->has_line_break_),- is_optional_(ptr->is_optional_),- media_block_(ptr->media_block_),- hash_(ptr->hash_)- { concrete_type(SELECTOR); }- virtual ~Selector() = 0;- virtual size_t hash() = 0;- virtual unsigned long specificity() const = 0;- virtual void set_media_block(Media_Block_Ptr mb) {- media_block(mb);- }- virtual bool has_parent_ref() const {- return false;- }- virtual bool has_real_parent_ref() const {- return false;- }- // dispatch to correct handlers- virtual bool operator<(const Selector& rhs) const = 0;- virtual bool operator==(const Selector& rhs) const = 0;- ATTACH_VIRTUAL_AST_OPERATIONS(Selector);- };- inline Selector::~Selector() { }-- /////////////////////////////////////////////////////////////////////////- // Interpolated selectors -- the interpolated String will be expanded and- // re-parsed into a normal selector class.- /////////////////////////////////////////////////////////////////////////- class Selector_Schema : public AST_Node {- ADD_PROPERTY(String_Obj, contents)- ADD_PROPERTY(bool, connect_parent);- // must not be a reference counted object- // otherwise we create circular references- ADD_PROPERTY(Media_Block_Ptr, media_block)- // store computed hash- size_t hash_;- public:- Selector_Schema(ParserState pstate, String_Obj c)- : AST_Node(pstate),- contents_(c),- connect_parent_(true),- media_block_(NULL),- hash_(0)- { }- Selector_Schema(const Selector_Schema* ptr)- : AST_Node(ptr),- contents_(ptr->contents_),- connect_parent_(ptr->connect_parent_),- media_block_(ptr->media_block_),- hash_(ptr->hash_)- { }- virtual bool has_parent_ref() const;- virtual bool has_real_parent_ref() const;- virtual bool operator<(const Selector& rhs) const;- virtual bool operator==(const Selector& rhs) const;- // selector schema is not yet a final selector, so we do not- // have a specificity for it yet. We need to- virtual unsigned long specificity() const { return 0; }- virtual size_t hash() {- if (hash_ == 0) {- hash_combine(hash_, contents_->hash());- }- return hash_;- }- ATTACH_AST_OPERATIONS(Selector_Schema)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////- // Abstract base class for simple selectors.- ////////////////////////////////////////////- class Simple_Selector : public Selector {- ADD_CONSTREF(std::string, ns)- ADD_CONSTREF(std::string, name)- ADD_PROPERTY(Simple_Type, simple_type)- ADD_PROPERTY(bool, has_ns)- public:- Simple_Selector(ParserState pstate, std::string n = "")- : Selector(pstate), ns_(""), name_(n), has_ns_(false)- {- simple_type(SIMPLE);- size_t pos = n.find('|');- // found some namespace- if (pos != std::string::npos) {- has_ns_ = true;- ns_ = n.substr(0, pos);- name_ = n.substr(pos + 1);- }- }- Simple_Selector(const Simple_Selector* ptr)- : Selector(ptr),- ns_(ptr->ns_),- name_(ptr->name_),- has_ns_(ptr->has_ns_)- { simple_type(SIMPLE); }- virtual std::string ns_name() const- {- std::string name("");- if (has_ns_)- name += ns_ + "|";- return name + name_;- }- virtual size_t hash()- {- if (hash_ == 0) {- hash_combine(hash_, std::hash<int>()(SELECTOR));- hash_combine(hash_, std::hash<std::string>()(ns()));- hash_combine(hash_, std::hash<std::string>()(name()));- }- return hash_;- }- // namespace compare functions- bool is_ns_eq(const Simple_Selector& r) const;- // namespace query functions- bool is_universal_ns() const- {- return has_ns_ && ns_ == "*";- }- bool has_universal_ns() const- {- return !has_ns_ || ns_ == "*";- }- bool is_empty_ns() const- {- return !has_ns_ || ns_ == "";- }- bool has_empty_ns() const- {- return has_ns_ && ns_ == "";- }- bool has_qualified_ns() const- {- return has_ns_ && ns_ != "" && ns_ != "*";- }- // name query functions- bool is_universal() const- {- return name_ == "*";- }-- virtual bool has_placeholder() {- return false;- }-- virtual ~Simple_Selector() = 0;- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);- virtual bool has_parent_ref() const { return false; };- virtual bool has_real_parent_ref() const { return false; };- virtual bool is_pseudo_element() const { return false; }-- virtual bool is_superselector_of(Compound_Selector_Obj sub) { return false; }-- virtual bool operator==(const Selector& rhs) const;- virtual bool operator==(const Simple_Selector& rhs) const;- inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); }-- bool operator<(const Selector& rhs) const;- bool operator<(const Simple_Selector& rhs) const;- // default implementation should work for most of the simple selectors (otherwise overload)- ATTACH_VIRTUAL_AST_OPERATIONS(Simple_Selector);- ATTACH_OPERATIONS();- };- inline Simple_Selector::~Simple_Selector() { }--- //////////////////////////////////- // The Parent Selector Expression.- //////////////////////////////////- // parent selectors can occur in selectors but also- // inside strings in declarations (Compound_Selector).- // only one simple parent selector means the first case.- class Parent_Selector : public Simple_Selector {- ADD_PROPERTY(bool, real)- public:- Parent_Selector(ParserState pstate, bool r = true)- : Simple_Selector(pstate, "&"), real_(r)- { /* has_reference(true); */ }- Parent_Selector(const Parent_Selector* ptr)- : Simple_Selector(ptr), real_(ptr->real_)- { /* has_reference(true); */ }- bool is_real_parent_ref() const { return real(); };- virtual bool has_parent_ref() const { return true; };- virtual bool has_real_parent_ref() const { return is_real_parent_ref(); };- virtual unsigned long specificity() const- {- return 0;- }- std::string type() const { return "selector"; }- static std::string type_name() { return "selector"; }- ATTACH_AST_OPERATIONS(Parent_Selector)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////////- // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.- /////////////////////////////////////////////////////////////////////////- class Placeholder_Selector : public Simple_Selector {- public:- Placeholder_Selector(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { }- Placeholder_Selector(const Placeholder_Selector* ptr)- : Simple_Selector(ptr)- { }- virtual unsigned long specificity() const- {- return Constants::Specificity_Base;- }- virtual bool has_placeholder() {- return true;- }- virtual ~Placeholder_Selector() {};- ATTACH_AST_OPERATIONS(Placeholder_Selector)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////////////////////////- // Element selectors (and the universal selector) -- e.g., div, span, *.- /////////////////////////////////////////////////////////////////////- class Element_Selector : public Simple_Selector {- public:- Element_Selector(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { }- Element_Selector(const Element_Selector* ptr)- : Simple_Selector(ptr)- { }- virtual unsigned long specificity() const- {- if (name() == "*") return 0;- else return Constants::Specificity_Element;- }- virtual Simple_Selector_Ptr unify_with(Simple_Selector_Ptr);- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);- virtual bool operator==(const Simple_Selector& rhs) const;- virtual bool operator==(const Element_Selector& rhs) const;- virtual bool operator<(const Simple_Selector& rhs) const;- virtual bool operator<(const Element_Selector& rhs) const;- ATTACH_AST_OPERATIONS(Element_Selector)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////- // Class selectors -- i.e., .foo.- ////////////////////////////////////////////////- class Class_Selector : public Simple_Selector {- public:- Class_Selector(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { }- Class_Selector(const Class_Selector* ptr)- : Simple_Selector(ptr)- { }- virtual unsigned long specificity() const- {- return Constants::Specificity_Class;- }- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);- ATTACH_AST_OPERATIONS(Class_Selector)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////- // ID selectors -- i.e., #foo.- ////////////////////////////////////////////////- class Id_Selector : public Simple_Selector {- public:- Id_Selector(ParserState pstate, std::string n)- : Simple_Selector(pstate, n)- { }- Id_Selector(const Id_Selector* ptr)- : Simple_Selector(ptr)- { }- virtual unsigned long specificity() const- {- return Constants::Specificity_ID;- }- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);- ATTACH_AST_OPERATIONS(Id_Selector)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////////////////////- // Attribute selectors -- e.g., [src*=".jpg"], etc.- ///////////////////////////////////////////////////- class Attribute_Selector : public Simple_Selector {- ADD_CONSTREF(std::string, matcher)- // this cannot be changed to obj atm!!!!!!????!!!!!!!- ADD_PROPERTY(String_Obj, value) // might be interpolated- ADD_PROPERTY(char, modifier);- public:- Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o = 0)- : Simple_Selector(pstate, n), matcher_(m), value_(v), modifier_(o)- { simple_type(ATTR_SEL); }- Attribute_Selector(const Attribute_Selector* ptr)- : Simple_Selector(ptr),- matcher_(ptr->matcher_),- value_(ptr->value_),- modifier_(ptr->modifier_)- { simple_type(ATTR_SEL); }- virtual size_t hash()- {- if (hash_ == 0) {- hash_combine(hash_, Simple_Selector::hash());- hash_combine(hash_, std::hash<std::string>()(matcher()));- if (value_) hash_combine(hash_, value_->hash());- }- return hash_;- }- virtual unsigned long specificity() const- {- return Constants::Specificity_Attr;- }- virtual bool operator==(const Simple_Selector& rhs) const;- virtual bool operator==(const Attribute_Selector& rhs) const;- virtual bool operator<(const Simple_Selector& rhs) const;- virtual bool operator<(const Attribute_Selector& rhs) const;- ATTACH_AST_OPERATIONS(Attribute_Selector)- ATTACH_OPERATIONS()- };-- //////////////////////////////////////////////////////////////////- // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.- //////////////////////////////////////////////////////////////////- /* '::' starts a pseudo-element, ':' a pseudo-class */- /* Except :first-line, :first-letter, :before and :after */- /* Note that pseudo-elements are restricted to one per selector */- /* and occur only in the last simple_selector_sequence. */- inline bool is_pseudo_class_element(const std::string& name)- {- return name == ":before" ||- name == ":after" ||- name == ":first-line" ||- name == ":first-letter";- }-- // Pseudo Selector cannot have any namespace?- class Pseudo_Selector : public Simple_Selector {- ADD_PROPERTY(String_Obj, expression)- public:- Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = 0)- : Simple_Selector(pstate, n), expression_(expr)- { simple_type(PSEUDO_SEL); }- Pseudo_Selector(const Pseudo_Selector* ptr)- : Simple_Selector(ptr), expression_(ptr->expression_)- { simple_type(PSEUDO_SEL); }-- // A pseudo-element is made of two colons (::) followed by the name.- // The `::` notation is introduced by the current document in order to- // establish a discrimination between pseudo-classes and pseudo-elements.- // For compatibility with existing style sheets, user agents must also- // accept the previous one-colon notation for pseudo-elements introduced- // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and- // :after). This compatibility is not allowed for the new pseudo-elements- // introduced in this specification.- virtual bool is_pseudo_element() const- {- return (name_[0] == ':' && name_[1] == ':')- || is_pseudo_class_element(name_);- }- virtual size_t hash()- {- if (hash_ == 0) {- hash_combine(hash_, Simple_Selector::hash());- if (expression_) hash_combine(hash_, expression_->hash());- }- return hash_;- }- virtual unsigned long specificity() const- {- if (is_pseudo_element())- return Constants::Specificity_Element;- return Constants::Specificity_Pseudo;- }- virtual bool operator==(const Simple_Selector& rhs) const;- virtual bool operator==(const Pseudo_Selector& rhs) const;- virtual bool operator<(const Simple_Selector& rhs) const;- virtual bool operator<(const Pseudo_Selector& rhs) const;- virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr);- ATTACH_AST_OPERATIONS(Pseudo_Selector)- ATTACH_OPERATIONS()- };-- /////////////////////////////////////////////////- // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir)- /////////////////////////////////////////////////- class Wrapped_Selector : public Simple_Selector {- ADD_PROPERTY(Selector_List_Obj, selector)- public:- Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel)- : Simple_Selector(pstate, n), selector_(sel)- { simple_type(WRAPPED_SEL); }- Wrapped_Selector(const Wrapped_Selector* ptr)- : Simple_Selector(ptr), selector_(ptr->selector_)- { simple_type(WRAPPED_SEL); }- virtual bool is_superselector_of(Wrapped_Selector_Obj sub);- // Selectors inside the negation pseudo-class are counted like any- // other, but the negation itself does not count as a pseudo-class.- virtual size_t hash();- virtual bool has_parent_ref() const;- virtual bool has_real_parent_ref() const;- virtual unsigned long specificity() const;- virtual bool find ( bool (*f)(AST_Node_Obj) );- virtual bool operator==(const Simple_Selector& rhs) const;- virtual bool operator==(const Wrapped_Selector& rhs) const;- virtual bool operator<(const Simple_Selector& rhs) const;- virtual bool operator<(const Wrapped_Selector& rhs) const;- virtual void cloneChildren();- ATTACH_AST_OPERATIONS(Wrapped_Selector)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // Simple selector sequences. Maintains flags indicating whether it contains- // any parent references or placeholders, to simplify expansion.- ////////////////////////////////////////////////////////////////////////////- class Compound_Selector : public Selector, public Vectorized<Simple_Selector_Obj> {- private:- ComplexSelectorSet sources_;- ADD_PROPERTY(bool, extended);- ADD_PROPERTY(bool, has_parent_reference);- protected:- void adjust_after_pushing(Simple_Selector_Obj s)- {- // if (s->has_reference()) has_reference(true);- // if (s->has_placeholder()) has_placeholder(true);- }- public:- Compound_Selector(ParserState pstate, size_t s = 0)- : Selector(pstate),- Vectorized<Simple_Selector_Obj>(s),- extended_(false),- has_parent_reference_(false)- { }- Compound_Selector(const Compound_Selector* ptr)- : Selector(ptr),- Vectorized<Simple_Selector_Obj>(*ptr),- extended_(ptr->extended_),- has_parent_reference_(ptr->has_parent_reference_)- { }- bool contains_placeholder() {- for (size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->has_placeholder()) return true;- }- return false;- };-- void append(Simple_Selector_Ptr element);-- bool is_universal() const- {- return length() == 1 && (*this)[0]->is_universal();- }-- Complex_Selector_Obj to_complex();- Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs);- // virtual Placeholder_Selector_Ptr find_placeholder();- virtual bool has_parent_ref() const;- virtual bool has_real_parent_ref() const;- Simple_Selector_Ptr base() const {- if (length() == 0) return 0;- // ToDo: why is this needed?- if (Cast<Element_Selector>((*this)[0]))- return (*this)[0];- return 0;- }- virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapped = "");- virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapped = "");- virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapped = "");- virtual size_t hash()- {- if (Selector::hash_ == 0) {- hash_combine(Selector::hash_, std::hash<int>()(SELECTOR));- if (length()) hash_combine(Selector::hash_, Vectorized::hash());- }- return Selector::hash_;- }- virtual unsigned long specificity() const- {- int sum = 0;- for (size_t i = 0, L = length(); i < L; ++i)- { sum += (*this)[i]->specificity(); }- return sum;- }-- virtual bool has_placeholder()- {- if (length() == 0) return false;- if (Simple_Selector_Obj ss = elements().front()) {- if (ss->has_placeholder()) return true;- }- return false;- }-- bool is_empty_reference()- {- return length() == 1 &&- Cast<Parent_Selector>((*this)[0]);- }-- virtual bool find ( bool (*f)(AST_Node_Obj) );- virtual bool operator<(const Selector& rhs) const;- virtual bool operator==(const Selector& rhs) const;- virtual bool operator<(const Compound_Selector& rhs) const;- virtual bool operator==(const Compound_Selector& rhs) const;- inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); }-- ComplexSelectorSet& sources() { return sources_; }- void clearSources() { sources_.clear(); }- void mergeSources(ComplexSelectorSet& sources);-- Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs);- virtual void cloneChildren();- ATTACH_AST_OPERATIONS(Compound_Selector)- ATTACH_OPERATIONS()- };-- ////////////////////////////////////////////////////////////////////////////- // General selectors -- i.e., simple sequences combined with one of the four- // CSS selector combinators (">", "+", "~", and whitespace). Essentially a- // linked list.- ////////////////////////////////////////////////////////////////////////////- class Complex_Selector : public Selector {- public:- enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE };- private:- HASH_CONSTREF(Combinator, combinator)- HASH_PROPERTY(Compound_Selector_Obj, head)- HASH_PROPERTY(Complex_Selector_Obj, tail)- HASH_PROPERTY(String_Obj, reference);- public:- bool contains_placeholder() {- if (head() && head()->contains_placeholder()) return true;- if (tail() && tail()->contains_placeholder()) return true;- return false;- };- Complex_Selector(ParserState pstate,- Combinator c = ANCESTOR_OF,- Compound_Selector_Obj h = 0,- Complex_Selector_Obj t = 0,- String_Obj r = 0)- : Selector(pstate),- combinator_(c),- head_(h), tail_(t),- reference_(r)- {}- Complex_Selector(const Complex_Selector* ptr)- : Selector(ptr),- combinator_(ptr->combinator_),- head_(ptr->head_), tail_(ptr->tail_),- reference_(ptr->reference_)- {};- virtual bool has_parent_ref() const;- virtual bool has_real_parent_ref() const;-- Complex_Selector_Obj skip_empty_reference()- {- if ((!head_ || !head_->length() || head_->is_empty_reference()) &&- combinator() == Combinator::ANCESTOR_OF)- {- if (!tail_) return 0;- tail_->has_line_feed_ = this->has_line_feed_;- // tail_->has_line_break_ = this->has_line_break_;- return tail_->skip_empty_reference();- }- return this;- }-- // can still have a tail- bool is_empty_ancestor() const- {- return (!head() || head()->length() == 0) &&- combinator() == Combinator::ANCESTOR_OF;- }-- Selector_List_Ptr tails(Selector_List_Ptr tails);-- // front returns the first real tail- // skips over parent and empty ones- Complex_Selector_Obj first();- // last returns the last real tail- Complex_Selector_Obj last();-- // some shortcuts that should be removed- Complex_Selector_Obj innermost() { return last(); };-- size_t length() const;- Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);- virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");- virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");- virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");- Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs);- Combinator clear_innermost();- void append(Complex_Selector_Obj, Backtraces& traces);- void set_innermost(Complex_Selector_Obj, Combinator);- virtual size_t hash()- {- if (hash_ == 0) {- hash_combine(hash_, std::hash<int>()(SELECTOR));- hash_combine(hash_, std::hash<int>()(combinator_));- if (head_) hash_combine(hash_, head_->hash());- if (tail_) hash_combine(hash_, tail_->hash());- }- return hash_;- }- virtual unsigned long specificity() const- {- int sum = 0;- if (head()) sum += head()->specificity();- if (tail()) sum += tail()->specificity();- return sum;- }- virtual void set_media_block(Media_Block_Ptr mb) {- media_block(mb);- if (tail_) tail_->set_media_block(mb);- if (head_) head_->set_media_block(mb);- }- virtual bool has_placeholder() {- if (head_ && head_->has_placeholder()) return true;- if (tail_ && tail_->has_placeholder()) return true;- return false;- }- virtual bool find ( bool (*f)(AST_Node_Obj) );- virtual bool operator<(const Selector& rhs) const;- virtual bool operator==(const Selector& rhs) const;- virtual bool operator<(const Complex_Selector& rhs) const;- virtual bool operator==(const Complex_Selector& rhs) const;- inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }- const ComplexSelectorSet sources()- {- //s = Set.new- //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}- //s-- ComplexSelectorSet srcs;-- Compound_Selector_Obj pHead = head();- Complex_Selector_Obj pTail = tail();-- if (pHead) {- const ComplexSelectorSet& headSources = pHead->sources();- srcs.insert(headSources.begin(), headSources.end());- }-- if (pTail) {- const ComplexSelectorSet& tailSources = pTail->sources();- srcs.insert(tailSources.begin(), tailSources.end());- }-- return srcs;- }- void addSources(ComplexSelectorSet& sources) {- // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}- Complex_Selector_Ptr pIter = this;- while (pIter) {- Compound_Selector_Ptr pHead = pIter->head();-- if (pHead) {- pHead->mergeSources(sources);- }-- pIter = pIter->tail();- }- }- void clearSources() {- Complex_Selector_Ptr pIter = this;- while (pIter) {- Compound_Selector_Ptr pHead = pIter->head();-- if (pHead) {- pHead->clearSources();- }-- pIter = pIter->tail();- }- }-- virtual void cloneChildren();- ATTACH_AST_OPERATIONS(Complex_Selector)- ATTACH_OPERATIONS()- };-- ///////////////////////////////////- // Comma-separated selector groups.- ///////////////////////////////////- class Selector_List : public Selector, public Vectorized<Complex_Selector_Obj> {- ADD_PROPERTY(Selector_Schema_Obj, schema)- ADD_CONSTREF(std::vector<std::string>, wspace)- protected:- void adjust_after_pushing(Complex_Selector_Obj c);- public:- Selector_List(ParserState pstate, size_t s = 0)- : Selector(pstate),- Vectorized<Complex_Selector_Obj>(s),- schema_(NULL),- wspace_(0)- { }- Selector_List(const Selector_List* ptr)- : Selector(ptr),- Vectorized<Complex_Selector_Obj>(*ptr),- schema_(ptr->schema_),- wspace_(ptr->wspace_)- { }- std::string type() const { return "list"; }- // remove parent selector references- // basically unwraps parsed selectors- virtual bool has_parent_ref() const;- virtual bool has_real_parent_ref() const;- void remove_parent_selectors();- Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);- virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");- virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");- virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");- Selector_List_Ptr unify_with(Selector_List_Ptr);- void populate_extends(Selector_List_Obj, Subset_Map&);- Selector_List_Obj eval(Eval& eval);- virtual size_t hash()- {- if (Selector::hash_ == 0) {- hash_combine(Selector::hash_, std::hash<int>()(SELECTOR));- hash_combine(Selector::hash_, Vectorized::hash());- }- return Selector::hash_;- }- virtual unsigned long specificity() const- {- unsigned long sum = 0;- unsigned long specificity;- for (size_t i = 0, L = length(); i < L; ++i)- {- specificity = (*this)[i]->specificity();- if (sum < specificity) sum = specificity;- }- return sum;- }- virtual void set_media_block(Media_Block_Ptr mb) {- media_block(mb);- for (Complex_Selector_Obj cs : elements()) {- cs->set_media_block(mb);- }- }- virtual bool has_placeholder() {- for (Complex_Selector_Obj cs : elements()) {- if (cs->has_placeholder()) return true;- }- return false;- }- virtual bool find ( bool (*f)(AST_Node_Obj) );- virtual bool operator<(const Selector& rhs) const;- virtual bool operator==(const Selector& rhs) const;- virtual bool operator<(const Selector_List& rhs) const;- virtual bool operator==(const Selector_List& rhs) const;- // Selector Lists can be compared to comma lists- virtual bool operator==(const Expression& rhs) const;- virtual void cloneChildren();- ATTACH_AST_OPERATIONS(Selector_List)- ATTACH_OPERATIONS()- };-- // compare function for sorting and probably other other uses- struct cmp_complex_selector { inline bool operator() (const Complex_Selector_Obj l, const Complex_Selector_Obj r) { return (*l < *r); } };- struct cmp_compound_selector { inline bool operator() (const Compound_Selector_Obj l, const Compound_Selector_Obj r) { return (*l < *r); } };- struct cmp_simple_selector { inline bool operator() (const Simple_Selector_Obj l, const Simple_Selector_Obj r) { return (*l < *r); } };--}--#ifdef __clang__--#pragma clang diagnostic pop+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <typeinfo>+#include <unordered_map>++#include "sass/base.h"+#include "ast_helpers.hpp"+#include "ast_fwd_decl.hpp"+#include "ast_def_macros.hpp"++#include "file.hpp"+#include "position.hpp"+#include "operation.hpp"+#include "environment.hpp"+#include "fn_utils.hpp"++namespace Sass {++ // ToDo: where does this fit best?+ // We don't share this with C-API?+ class Operand {+ public:+ Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false)+ : operand(operand), ws_before(ws_before), ws_after(ws_after)+ { }+ public:+ enum Sass_OP operand;+ bool ws_before;+ bool ws_after;+ };++ //////////////////////////////////////////////////////////+ // `hash_combine` comes from boost (functional/hash):+ // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html+ // Boost Software License - Version 1.0+ // http://www.boost.org/users/license.html+ template <typename T>+ void hash_combine (std::size_t& seed, const T& val)+ {+ seed ^= std::hash<T>()(val) + 0x9e3779b9+ + (seed<<6) + (seed>>2);+ }+ //////////////////////////////////////////////////////////++ const char* sass_op_to_name(enum Sass_OP op);++ const char* sass_op_separator(enum Sass_OP op);++ //////////////////////////////////////////////////////////+ // Abstract base class for all abstract syntax tree nodes.+ //////////////////////////////////////////////////////////+ class AST_Node : public SharedObj {+ ADD_PROPERTY(ParserState, pstate)+ public:+ AST_Node(ParserState pstate)+ : pstate_(pstate)+ { }+ AST_Node(const AST_Node* ptr)+ : pstate_(ptr->pstate_)+ { }++ // allow implicit conversion to string+ // needed for by SharedPtr implementation+ operator std::string() {+ return to_string();+ }++ // AST_Node(AST_Node& ptr) = delete;++ virtual ~AST_Node() = 0;+ virtual size_t hash() const { return 0; }+ virtual std::string inspect() const { return to_string({ INSPECT, 5 }); }+ virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); }+ virtual std::string to_string(Sass_Inspect_Options opt) const;+ virtual std::string to_css(Sass_Inspect_Options opt) const;+ virtual std::string to_string() const;+ virtual void cloneChildren() {};+ // generic find function (not fully implemented yet)+ // ToDo: add specific implementions to all children+ virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); };+ void update_pstate(const ParserState& pstate);+ Offset off() { return pstate(); }+ Position pos() { return pstate(); }++ // Some obects are not meant to be compared+ // ToDo: maybe fallback to pointer comparison?+ virtual bool operator== (const AST_Node& rhs) const {+ throw std::runtime_error("operator== not implemented");+ }++ // We can give some reasonable implementations by using+ // inverst operators on the specialized implementations+ virtual bool operator!= (const AST_Node& rhs) const {+ // Unequal if not equal+ return !(*this == rhs);+ }++ ATTACH_ABSTRACT_AST_OPERATIONS(AST_Node);+ ATTACH_ABSTRACT_CRTP_PERFORM_METHODS()+ };+ inline AST_Node::~AST_Node() { }++ //////////////////////////////////////////////////////////////////////+ // define cast template now (need complete type)+ //////////////////////////////////////////////////////////////////////++ template<class T>+ T* Cast(AST_Node* ptr) {+ return ptr && typeid(T) == typeid(*ptr) ?+ static_cast<T*>(ptr) : NULL;+ };++ template<class T>+ const T* Cast(const AST_Node* ptr) {+ return ptr && typeid(T) == typeid(*ptr) ?+ static_cast<const T*>(ptr) : NULL;+ };++ //////////////////////////////////////////////////////////////////////+ // Abstract base class for expressions. This side of the AST hierarchy+ // represents elements in value contexts, which exist primarily to be+ // evaluated and returned.+ //////////////////////////////////////////////////////////////////////+ class Expression : public AST_Node {+ public:+ enum Type {+ NONE,+ BOOLEAN,+ NUMBER,+ COLOR,+ STRING,+ LIST,+ MAP,+ SELECTOR,+ NULL_VAL,+ FUNCTION_VAL,+ C_WARNING,+ C_ERROR,+ FUNCTION,+ VARIABLE,+ PARENT,+ NUM_TYPES+ };+ private:+ // expressions in some contexts shouldn't be evaluated+ ADD_PROPERTY(bool, is_delayed)+ ADD_PROPERTY(bool, is_expanded)+ ADD_PROPERTY(bool, is_interpolant)+ ADD_PROPERTY(Type, concrete_type)+ public:+ Expression(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);+ virtual operator bool() { return true; }+ virtual ~Expression() { }+ virtual bool is_invisible() const { return false; }++ virtual std::string type() const { return ""; }+ static std::string type_name() { return ""; }++ virtual bool is_false() { return false; }+ // virtual bool is_true() { return !is_false(); }+ virtual bool operator< (const Expression& rhs) const { return false; }+ virtual bool operator== (const Expression& rhs) const { return false; }+ inline bool operator>(const Expression& rhs) const { return rhs < *this; }+ inline bool operator!=(const Expression& rhs) const { return !(rhs == *this); }+ virtual bool eq(const Expression& rhs) const { return *this == rhs; };+ virtual void set_delayed(bool delayed) { is_delayed(delayed); }+ virtual bool has_interpolant() const { return is_interpolant(); }+ virtual bool is_left_interpolant() const { return is_interpolant(); }+ virtual bool is_right_interpolant() const { return is_interpolant(); }+ ATTACH_VIRTUAL_AST_OPERATIONS(Expression);+ size_t hash() const override { return 0; }+ };++}++/////////////////////////////////////////////////////////////////////////////////////+// Hash method specializations for std::unordered_map to work with Sass::Expression+/////////////////////////////////////////////////////////////////////////////////////++namespace std {+ template<>+ struct hash<Sass::Expression_Obj>+ {+ size_t operator()(Sass::Expression_Obj s) const+ {+ return s->hash();+ }+ };+ template<>+ struct equal_to<Sass::Expression_Obj>+ {+ bool operator()( Sass::Expression_Obj lhs, Sass::Expression_Obj rhs) const+ {+ return lhs->hash() == rhs->hash();+ }+ };+}++namespace Sass {++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like vectors. Uses the+ // "Template Method" design pattern to allow subclasses to adjust their flags+ // when certain objects are pushed.+ /////////////////////////////////////////////////////////////////////////////+ template <typename T>+ class Vectorized {+ std::vector<T> elements_;+ protected:+ mutable size_t hash_;+ void reset_hash() { hash_ = 0; }+ virtual void adjust_after_pushing(T element) { }+ public:+ Vectorized(size_t s = 0) : hash_(0)+ { elements_.reserve(s); }+ Vectorized(std::vector<T> vec) :+ elements_(std::move(vec)),+ hash_(0)+ {}+ virtual ~Vectorized() = 0;+ size_t length() const { return elements_.size(); }+ bool empty() const { return elements_.empty(); }+ void clear() { return elements_.clear(); }+ T& last() { return elements_.back(); }+ T& first() { return elements_.front(); }+ const T& last() const { return elements_.back(); }+ const T& first() const { return elements_.front(); }++ bool operator== (const Vectorized<T>& rhs) const {+ // Abort early if sizes do not match+ if (length() != rhs.length()) return false;+ // Otherwise test each node for object equalicy in order+ return std::equal(begin(), end(), rhs.begin(), ObjEqualityFn<T>);+ }++ bool operator!= (const Vectorized<T>& rhs) const {+ return !(*this == rhs);+ }++ T& operator[](size_t i) { return elements_[i]; }+ virtual const T& at(size_t i) const { return elements_.at(i); }+ virtual T& at(size_t i) { return elements_.at(i); }+ const T& get(size_t i) const { return elements_[i]; }+ const T& operator[](size_t i) const { return elements_[i]; }++ // Implicitly get the std::vector from our object+ // Makes the Vector directly assignable to std::vector+ // You are responsible to make a copy if needed+ // Note: since this returns the real object, we can't+ // Note: guarantee that the hash will not get out of sync+ operator std::vector<T>&() { return elements_; }+ operator const std::vector<T>&() const { return elements_; }++ // Explicitly request all elements as a real std::vector+ // You are responsible to make a copy if needed+ // Note: since this returns the real object, we can't+ // Note: guarantee that the hash will not get out of sync+ std::vector<T>& elements() { return elements_; }+ const std::vector<T>& elements() const { return elements_; }++ // Insert all items from compatible vector+ void concat(const std::vector<T>& v)+ {+ if (!v.empty()) reset_hash();+ elements().insert(end(), v.begin(), v.end());+ }++ // Syntatic sugar for pointers+ void concat(const Vectorized<T>* v)+ {+ if (v != nullptr) {+ return concat(*v);+ }+ }++ // Insert one item on the front+ void unshift(T element)+ {+ reset_hash();+ elements_.insert(begin(), element);+ }++ // Remove and return item on the front+ // ToDo: handle empty vectors+ T shift() {+ reset_hash();+ T first = get(0);+ elements_.erase(begin());+ return first;+ }++ // Insert one item on the back+ // ToDo: rename this to push+ void append(T element)+ {+ reset_hash();+ elements_.insert(end(), element);+ // ToDo: Mostly used by parameters and arguments+ // ToDo: Find a more elegant way to support this+ adjust_after_pushing(element);+ }++ // Check if an item already exists+ // Uses underlying object `operator==`+ // E.g. compares the actual objects+ bool contains(const T& el) const {+ for (const T& rhs : elements_) {+ // Test the underlying objects for equality+ // A std::find checks for pointer equality+ if (ObjEqualityFn(el, rhs)) {+ return true;+ }+ }+ return false;+ }++ // This might be better implemented as `operator=`?+ void elements(std::vector<T> e) {+ reset_hash();+ elements_ = std::move(e);+ }++ virtual size_t hash() const+ {+ if (hash_ == 0) {+ for (const T& el : elements_) {+ hash_combine(hash_, el->hash());+ }+ }+ return hash_;+ }++ template <typename P, typename V>+ typename std::vector<T>::iterator insert(P position, const V& val) {+ reset_hash();+ return elements_.insert(position, val);+ }++ typename std::vector<T>::iterator end() { return elements_.end(); }+ typename std::vector<T>::iterator begin() { return elements_.begin(); }+ typename std::vector<T>::const_iterator end() const { return elements_.end(); }+ typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }+ typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { reset_hash(); return elements_.erase(el); }+ typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { reset_hash(); return elements_.erase(el); }++ };+ template <typename T>+ inline Vectorized<T>::~Vectorized() { }++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like a hash table. Uses an+ // extra <std::vector> internally to maintain insertion order for interation.+ /////////////////////////////////////////////////////////////////////////////+ template <typename K, typename T, typename U>+ class Hashed {+ private:+ std::unordered_map<+ K, T, ObjHash, ObjEquality+ > elements_;++ std::vector<K> _keys;+ std::vector<T> _values;+ protected:+ mutable size_t hash_;+ K duplicate_key_;+ void reset_hash() { hash_ = 0; }+ void reset_duplicate_key() { duplicate_key_ = {}; }+ virtual void adjust_after_pushing(std::pair<K, T> p) { }+ public:+ Hashed(size_t s = 0)+ : elements_(),+ _keys(),+ _values(),+ hash_(0), duplicate_key_({})+ {+ _keys.reserve(s);+ _values.reserve(s);+ elements_.reserve(s);+ }+ virtual ~Hashed();+ size_t length() const { return _keys.size(); }+ bool empty() const { return _keys.empty(); }+ bool has(K k) const {+ return elements_.find(k) != elements_.end();+ }+ T at(K k) const {+ if (elements_.count(k))+ {+ return elements_.at(k);+ }+ else { return {}; }+ }+ bool has_duplicate_key() const { return duplicate_key_ != nullptr; }+ K get_duplicate_key() const { return duplicate_key_; }+ const std::unordered_map<+ K, T, ObjHash, ObjEquality+ >& elements() { return elements_; }+ Hashed& operator<<(std::pair<K, T> p)+ {+ reset_hash();++ if (!has(p.first)) {+ _keys.push_back(p.first);+ _values.push_back(p.second);+ }+ else if (!duplicate_key_) {+ duplicate_key_ = p.first;+ }++ elements_[p.first] = p.second;++ adjust_after_pushing(p);+ return *this;+ }+ Hashed& operator+=(Hashed* h)+ {+ if (length() == 0) {+ this->elements_ = h->elements_;+ this->_values = h->_values;+ this->_keys = h->_keys;+ return *this;+ }++ for (auto key : h->keys()) {+ *this << std::make_pair(key, h->at(key));+ }++ reset_duplicate_key();+ return *this;+ }+ const std::unordered_map<+ K, T, ObjHash, ObjEquality+ >& pairs() const { return elements_; }++ const std::vector<K>& keys() const { return _keys; }+ const std::vector<T>& values() const { return _values; }++// std::unordered_map<Expression_Obj, Expression_Obj>::iterator end() { return elements_.end(); }+// std::unordered_map<Expression_Obj, Expression_Obj>::iterator begin() { return elements_.begin(); }+// std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator end() const { return elements_.end(); }+// std::unordered_map<Expression_Obj, Expression_Obj>::const_iterator begin() const { return elements_.begin(); }++ };+ template <typename K, typename T, typename U>+ inline Hashed<K, T, U>::~Hashed() { }++ /////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements. This side of the AST hierarchy+ // represents elements in expansion contexts, which exist primarily to be+ // rewritten and macro-expanded.+ /////////////////////////////////////////////////////////////////////////+ class Statement : public AST_Node {+ public:+ enum Type {+ NONE,+ RULESET,+ MEDIA,+ DIRECTIVE,+ SUPPORTS,+ ATROOT,+ BUBBLE,+ CONTENT,+ KEYFRAMERULE,+ DECLARATION,+ ASSIGNMENT,+ IMPORT_STUB,+ IMPORT,+ COMMENT,+ WARNING,+ RETURN,+ EXTEND,+ ERROR,+ DEBUGSTMT,+ WHILE,+ EACH,+ FOR,+ IF+ };+ private:+ ADD_PROPERTY(Type, statement_type)+ ADD_PROPERTY(size_t, tabs)+ ADD_PROPERTY(bool, group_end)+ public:+ Statement(ParserState pstate, Type st = NONE, size_t t = 0);+ virtual ~Statement() = 0; // virtual destructor+ // needed for rearranging nested rulesets during CSS emission+ virtual bool bubbles();+ virtual bool has_content();+ virtual bool is_invisible() const;+ ATTACH_VIRTUAL_AST_OPERATIONS(Statement)+ };+ inline Statement::~Statement() { }++ ////////////////////////+ // Blocks of statements.+ ////////////////////////+ class Block final : public Statement, public Vectorized<Statement_Obj> {+ ADD_PROPERTY(bool, is_root)+ // needed for properly formatted CSS emission+ protected:+ void adjust_after_pushing(Statement_Obj s) override {}+ public:+ Block(ParserState pstate, size_t s = 0, bool r = false);+ bool isInvisible() const;+ bool has_content() override;+ ATTACH_AST_OPERATIONS(Block)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements that contain blocks of statements.+ ////////////////////////////////////////////////////////////////////////+ class Has_Block : public Statement {+ ADD_PROPERTY(Block_Obj, block)+ public:+ Has_Block(ParserState pstate, Block_Obj b);+ Has_Block(const Has_Block* ptr); // copy constructor+ virtual ~Has_Block() = 0; // virtual destructor+ virtual bool has_content() override;+ };+ inline Has_Block::~Has_Block() { }++ /////////////////////////////////////////////////////////////////////////////+ // Rulesets (i.e., sets of styles headed by a selector and containing a block+ // of style declarations.+ /////////////////////////////////////////////////////////////////////////////+ class Ruleset final : public Has_Block {+ ADD_PROPERTY(SelectorListObj, selector)+ ADD_PROPERTY(Selector_Schema_Obj, schema)+ ADD_PROPERTY(bool, is_root);+ public:+ Ruleset(ParserState pstate, SelectorListObj s = {}, Block_Obj b = {});+ bool is_invisible() const override;+ ATTACH_AST_OPERATIONS(Ruleset)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////+ // Bubble.+ /////////////////+ class Bubble final : public Statement {+ ADD_PROPERTY(Statement_Obj, node)+ ADD_PROPERTY(bool, group_end)+ public:+ Bubble(ParserState pstate, Statement_Obj n, Statement_Obj g = {}, size_t t = 0);+ bool bubbles() override;+ ATTACH_AST_OPERATIONS(Bubble)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////+ // Trace.+ /////////////////+ class Trace final : public Has_Block {+ ADD_CONSTREF(char, type)+ ADD_CONSTREF(std::string, name)+ public:+ Trace(ParserState pstate, std::string n, Block_Obj b = {}, char type = 'm');+ ATTACH_AST_OPERATIONS(Trace)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////////////////////////////////+ // At-rules -- arbitrary directives beginning with "@" that may have an+ // optional statement block.+ ///////////////////////////////////////////////////////////////////////+ class Directive final : public Has_Block {+ ADD_CONSTREF(std::string, keyword)+ ADD_PROPERTY(SelectorListObj, selector)+ ADD_PROPERTY(Expression_Obj, value)+ public:+ Directive(ParserState pstate, std::string kwd, SelectorListObj sel = {}, Block_Obj b = {}, Expression_Obj val = {});+ bool bubbles() override;+ bool is_media();+ bool is_keyframes();+ ATTACH_AST_OPERATIONS(Directive)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Keyframe-rules -- the child blocks of "@keyframes" nodes.+ ///////////////////////////////////////////////////////////////////////+ class Keyframe_Rule final : public Has_Block {+ // according to css spec, this should be <keyframes-name>+ // <keyframes-name> = <custom-ident> | <string>+ ADD_PROPERTY(SelectorListObj, name)+ public:+ Keyframe_Rule(ParserState pstate, Block_Obj b);+ ATTACH_AST_OPERATIONS(Keyframe_Rule)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Declarations -- style rules consisting of a property name and values.+ ////////////////////////////////////////////////////////////////////////+ class Declaration final : public Has_Block {+ ADD_PROPERTY(String_Obj, property)+ ADD_PROPERTY(Expression_Obj, value)+ ADD_PROPERTY(bool, is_important)+ ADD_PROPERTY(bool, is_custom_property)+ ADD_PROPERTY(bool, is_indented)+ public:+ Declaration(ParserState pstate, String_Obj prop, Expression_Obj val, bool i = false, bool c = false, Block_Obj b = {});+ bool is_invisible() const override;+ ATTACH_AST_OPERATIONS(Declaration)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////+ // Assignments -- variable and value.+ /////////////////////////////////////+ class Assignment final : public Statement {+ ADD_CONSTREF(std::string, variable)+ ADD_PROPERTY(Expression_Obj, value)+ ADD_PROPERTY(bool, is_default)+ ADD_PROPERTY(bool, is_global)+ public:+ Assignment(ParserState pstate, std::string var, Expression_Obj val, bool is_default = false, bool is_global = false);+ ATTACH_AST_OPERATIONS(Assignment)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Import directives. CSS and Sass import lists can be intermingled, so it's+ // necessary to store a list of each in an Import node.+ ////////////////////////////////////////////////////////////////////////////+ class Import final : public Statement {+ std::vector<Expression_Obj> urls_;+ std::vector<Include> incs_;+ ADD_PROPERTY(List_Obj, import_queries);+ public:+ Import(ParserState pstate);+ std::vector<Include>& incs();+ std::vector<Expression_Obj>& urls();+ ATTACH_AST_OPERATIONS(Import)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ // not yet resolved single import+ // so far we only know requested name+ class Import_Stub final : public Statement {+ Include resource_;+ public:+ Import_Stub(ParserState pstate, Include res);+ Include resource();+ std::string imp_path();+ std::string abs_path();+ ATTACH_AST_OPERATIONS(Import_Stub)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////+ // The Sass `@warn` directive.+ //////////////////////////////+ class Warning final : public Statement {+ ADD_PROPERTY(Expression_Obj, message)+ public:+ Warning(ParserState pstate, Expression_Obj msg);+ ATTACH_AST_OPERATIONS(Warning)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////+ // The Sass `@error` directive.+ ///////////////////////////////+ class Error final : public Statement {+ ADD_PROPERTY(Expression_Obj, message)+ public:+ Error(ParserState pstate, Expression_Obj msg);+ ATTACH_AST_OPERATIONS(Error)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////+ // The Sass `@debug` directive.+ ///////////////////////////////+ class Debug final : public Statement {+ ADD_PROPERTY(Expression_Obj, value)+ public:+ Debug(ParserState pstate, Expression_Obj val);+ ATTACH_AST_OPERATIONS(Debug)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////+ // CSS comments. These may be interpolated.+ ///////////////////////////////////////////+ class Comment final : public Statement {+ ADD_PROPERTY(String_Obj, text)+ ADD_PROPERTY(bool, is_important)+ public:+ Comment(ParserState pstate, String_Obj txt, bool is_important);+ virtual bool is_invisible() const override;+ ATTACH_AST_OPERATIONS(Comment)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////+ // The Sass `@if` control directive.+ ////////////////////////////////////+ class If final : public Has_Block {+ ADD_PROPERTY(Expression_Obj, predicate)+ ADD_PROPERTY(Block_Obj, alternative)+ public:+ If(ParserState pstate, Expression_Obj pred, Block_Obj con, Block_Obj alt = {});+ virtual bool has_content() override;+ ATTACH_AST_OPERATIONS(If)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////+ // The Sass `@for` control directive.+ /////////////////////////////////////+ class For final : public Has_Block {+ ADD_CONSTREF(std::string, variable)+ ADD_PROPERTY(Expression_Obj, lower_bound)+ ADD_PROPERTY(Expression_Obj, upper_bound)+ ADD_PROPERTY(bool, is_inclusive)+ public:+ For(ParserState pstate, std::string var, Expression_Obj lo, Expression_Obj hi, Block_Obj b, bool inc);+ ATTACH_AST_OPERATIONS(For)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////////////+ // The Sass `@each` control directive.+ //////////////////////////////////////+ class Each final : public Has_Block {+ ADD_PROPERTY(std::vector<std::string>, variables)+ ADD_PROPERTY(Expression_Obj, list)+ public:+ Each(ParserState pstate, std::vector<std::string> vars, Expression_Obj lst, Block_Obj b);+ ATTACH_AST_OPERATIONS(Each)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////+ // The Sass `@while` control directive.+ ///////////////////////////////////////+ class While final : public Has_Block {+ ADD_PROPERTY(Expression_Obj, predicate)+ public:+ While(ParserState pstate, Expression_Obj pred, Block_Obj b);+ ATTACH_AST_OPERATIONS(While)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////////////////+ // The @return directive for use inside SassScript functions.+ /////////////////////////////////////////////////////////////+ class Return final : public Statement {+ ADD_PROPERTY(Expression_Obj, value)+ public:+ Return(ParserState pstate, Expression_Obj val);+ ATTACH_AST_OPERATIONS(Return)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////////////////////////////////+ // Definitions for both mixins and functions. The two cases are distinguished+ // by a type tag.+ /////////////////////////////////////////////////////////////////////////////+ class Definition final : public Has_Block {+ public:+ enum Type { MIXIN, FUNCTION };+ ADD_CONSTREF(std::string, name)+ ADD_PROPERTY(Parameters_Obj, parameters)+ ADD_PROPERTY(Env*, environment)+ ADD_PROPERTY(Type, type)+ ADD_PROPERTY(Native_Function, native_function)+ ADD_PROPERTY(Sass_Function_Entry, c_function)+ ADD_PROPERTY(void*, cookie)+ ADD_PROPERTY(bool, is_overload_stub)+ ADD_PROPERTY(Signature, signature)+ public:+ Definition(ParserState pstate,+ std::string n,+ Parameters_Obj params,+ Block_Obj b,+ Type t);+ Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters_Obj params,+ Native_Function func_ptr,+ bool overload_stub = false);+ Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters_Obj params,+ Sass_Function_Entry c_func);+ ATTACH_AST_OPERATIONS(Definition)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////////////+ // Mixin calls (i.e., `@include ...`).+ //////////////////////////////////////+ class Mixin_Call final : public Has_Block {+ ADD_CONSTREF(std::string, name)+ ADD_PROPERTY(Arguments_Obj, arguments)+ ADD_PROPERTY(Parameters_Obj, block_parameters)+ public:+ Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Parameters_Obj b_params = {}, Block_Obj b = {});+ ATTACH_AST_OPERATIONS(Mixin_Call)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////////////+ // The @content directive for mixin content blocks.+ ///////////////////////////////////////////////////+ class Content final : public Statement {+ ADD_PROPERTY(Arguments_Obj, arguments)+ public:+ Content(ParserState pstate, Arguments_Obj args);+ ATTACH_AST_OPERATIONS(Content)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Arithmetic negation (logical negation is just an ordinary function call).+ ////////////////////////////////////////////////////////////////////////////+ class Unary_Expression final : public Expression {+ public:+ enum Type { PLUS, MINUS, NOT, SLASH };+ private:+ HASH_PROPERTY(Type, optype)+ HASH_PROPERTY(Expression_Obj, operand)+ mutable size_t hash_;+ public:+ Unary_Expression(ParserState pstate, Type t, Expression_Obj o);+ const std::string type_name();+ virtual bool operator==(const Expression& rhs) const override;+ size_t hash() const override;+ ATTACH_AST_OPERATIONS(Unary_Expression)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////+ // Individual argument objects for mixin and function calls.+ ////////////////////////////////////////////////////////////+ class Argument final : public Expression {+ HASH_PROPERTY(Expression_Obj, value)+ HASH_CONSTREF(std::string, name)+ ADD_PROPERTY(bool, is_rest_argument)+ ADD_PROPERTY(bool, is_keyword_argument)+ mutable size_t hash_;+ public:+ Argument(ParserState pstate, Expression_Obj val, std::string n = "", bool rest = false, bool keyword = false);+ void set_delayed(bool delayed) override;+ bool operator==(const Expression& rhs) const override;+ size_t hash() const override;+ ATTACH_AST_OPERATIONS(Argument)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Argument lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all ordinal arguments precede all+ // named arguments).+ ////////////////////////////////////////////////////////////////////////+ class Arguments final : public Expression, public Vectorized<Argument_Obj> {+ ADD_PROPERTY(bool, has_named_arguments)+ ADD_PROPERTY(bool, has_rest_argument)+ ADD_PROPERTY(bool, has_keyword_argument)+ protected:+ void adjust_after_pushing(Argument_Obj a) override;+ public:+ Arguments(ParserState pstate);+ void set_delayed(bool delayed) override;+ Argument_Obj get_rest_argument();+ Argument_Obj get_keyword_argument();+ ATTACH_AST_OPERATIONS(Arguments)+ ATTACH_CRTP_PERFORM_METHODS()+ };+++ // A Media Ruleset before it has been evaluated+ // Could be already final or an interpolation+ class MediaRule final : public Has_Block {+ ADD_PROPERTY(List_Obj, schema)+ public:+ MediaRule(ParserState pstate, Block_Obj block = {});++ bool bubbles() override { return true; };+ bool is_invisible() const override { return false; };+ ATTACH_AST_OPERATIONS(MediaRule)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ // A Media Ruleset after it has been evaluated+ // Representing the static or resulting css+ class CssMediaRule final : public Has_Block,+ public Vectorized<CssMediaQuery_Obj> {+ public:+ CssMediaRule(ParserState pstate, Block_Obj b);+ bool bubbles() override { return true; };+ bool isInvisible() const { return empty(); }+ bool is_invisible() const override { return false; };++ public:+ // Hash and equality implemtation from vector+ size_t hash() const override { return Vectorized::hash(); }+ // Check if two instances are considered equal+ bool operator== (const CssMediaRule& rhs) const {+ return Vectorized::operator== (rhs);+ }+ bool operator!=(const CssMediaRule& rhs) const {+ // Invert from equality+ return !(*this == rhs);+ }++ ATTACH_AST_OPERATIONS(CssMediaRule)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ // Media Queries after they have been evaluated+ // Representing the static or resulting css+ class CssMediaQuery final : public AST_Node {++ // The modifier, probably either "not" or "only".+ // This may be `null` if no modifier is in use.+ ADD_PROPERTY(std::string, modifier);++ // The media type, for example "screen" or "print".+ // This may be `null`. If so, [features] will not be empty.+ ADD_PROPERTY(std::string, type);++ // Feature queries, including parentheses.+ ADD_PROPERTY(std::vector<std::string>, features);++ public:+ CssMediaQuery(ParserState pstate);++ // Check if two instances are considered equal+ bool operator== (const CssMediaQuery& rhs) const;+ bool operator!=(const CssMediaQuery& rhs) const {+ // Invert from equality+ return !(*this == rhs);+ }++ // Returns true if this query is empty+ // Meaning it has no type and features+ bool empty() const {+ return type_.empty()+ && modifier_.empty()+ && features_.empty();+ }++ // Whether this media query matches all media types.+ bool matchesAllTypes() const {+ return type_.empty() || Util::equalsLiteral("all", type_);+ }++ // Merges this with [other] and adds a query that matches the intersection+ // of both inputs to [result]. Returns false if the result is unrepresentable+ CssMediaQuery_Obj merge(CssMediaQuery_Obj& other);++ ATTACH_AST_OPERATIONS(CssMediaQuery)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////+ // Media queries (replaced by MediaRule at al).+ // ToDo: only used for interpolation case+ ////////////////////////////////////////////////////+ class Media_Query final : public Expression,+ public Vectorized<Media_Query_Expression_Obj> {+ ADD_PROPERTY(String_Obj, media_type)+ ADD_PROPERTY(bool, is_negated)+ ADD_PROPERTY(bool, is_restricted)+ public:+ Media_Query(ParserState pstate, String_Obj t = {}, size_t s = 0, bool n = false, bool r = false);+ ATTACH_AST_OPERATIONS(Media_Query)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////+ // Media expressions (for use inside media queries).+ // ToDo: only used for interpolation case+ ////////////////////////////////////////////////////+ class Media_Query_Expression final : public Expression {+ ADD_PROPERTY(Expression_Obj, feature)+ ADD_PROPERTY(Expression_Obj, value)+ ADD_PROPERTY(bool, is_interpolated)+ public:+ Media_Query_Expression(ParserState pstate, Expression_Obj f, Expression_Obj v, bool i = false);+ ATTACH_AST_OPERATIONS(Media_Query_Expression)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////+ // At root expressions (for use inside @at-root).+ /////////////////////////////////////////////////+ class At_Root_Query final : public Expression {+ private:+ ADD_PROPERTY(Expression_Obj, feature)+ ADD_PROPERTY(Expression_Obj, value)+ public:+ At_Root_Query(ParserState pstate, Expression_Obj f = {}, Expression_Obj v = {}, bool i = false);+ bool exclude(std::string str);+ ATTACH_AST_OPERATIONS(At_Root_Query)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////+ // At-root.+ ///////////+ class At_Root_Block final : public Has_Block {+ ADD_PROPERTY(At_Root_Query_Obj, expression)+ public:+ At_Root_Block(ParserState pstate, Block_Obj b = {}, At_Root_Query_Obj e = {});+ bool bubbles() override;+ bool exclude_node(Statement_Obj s);+ ATTACH_AST_OPERATIONS(At_Root_Block)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////////////+ // Individual parameter objects for mixins and functions.+ /////////////////////////////////////////////////////////+ class Parameter final : public AST_Node {+ ADD_CONSTREF(std::string, name)+ ADD_PROPERTY(Expression_Obj, default_value)+ ADD_PROPERTY(bool, is_rest_parameter)+ public:+ Parameter(ParserState pstate, std::string n, Expression_Obj def = {}, bool rest = false);+ ATTACH_AST_OPERATIONS(Parameter)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////////////////////////////+ // Parameter lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all optional parameters follow all+ // required parameters).+ /////////////////////////////////////////////////////////////////////////+ class Parameters final : public AST_Node, public Vectorized<Parameter_Obj> {+ ADD_PROPERTY(bool, has_optional_parameters)+ ADD_PROPERTY(bool, has_rest_parameter)+ protected:+ void adjust_after_pushing(Parameter_Obj p) override;+ public:+ Parameters(ParserState pstate);+ ATTACH_AST_OPERATIONS(Parameters)+ ATTACH_CRTP_PERFORM_METHODS()+ };++}++#include "ast_values.hpp"+#include "ast_supports.hpp"+#include "ast_selectors.hpp"++#ifdef __clang__++// #pragma clang diagnostic pop+// #pragma clang diagnostic push #endif
+ libsass/src/ast2c.cpp view
@@ -0,0 +1,80 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast2c.hpp"+#include "ast.hpp"++namespace Sass {++ union Sass_Value* AST2C::operator()(Boolean* b)+ { return sass_make_boolean(b->value()); }++ union Sass_Value* AST2C::operator()(Number* n)+ { return sass_make_number(n->value(), n->unit().c_str()); }++ union Sass_Value* AST2C::operator()(Custom_Warning* w)+ { return sass_make_warning(w->message().c_str()); }++ union Sass_Value* AST2C::operator()(Custom_Error* e)+ { return sass_make_error(e->message().c_str()); }++ union Sass_Value* AST2C::operator()(Color_RGBA* c)+ { return sass_make_color(c->r(), c->g(), c->b(), c->a()); }++ union Sass_Value* AST2C::operator()(Color_HSLA* c)+ {+ Color_RGBA_Obj rgba = c->copyAsRGBA();+ return operator()(rgba.ptr());+ }++ union Sass_Value* AST2C::operator()(String_Constant* s)+ {+ if (s->quote_mark()) {+ return sass_make_qstring(s->value().c_str());+ } else {+ return sass_make_string(s->value().c_str());+ }+ }++ union Sass_Value* AST2C::operator()(String_Quoted* s)+ { return sass_make_qstring(s->value().c_str()); }++ union Sass_Value* AST2C::operator()(List* l)+ {+ union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ sass_list_set_value(v, i, (*l)[i]->perform(this));+ }+ return v;+ }++ union Sass_Value* AST2C::operator()(Map* m)+ {+ union Sass_Value* v = sass_make_map(m->length());+ int i = 0;+ for (auto key : m->keys()) {+ sass_map_set_key(v, i, key->perform(this));+ sass_map_set_value(v, i, m->at(key)->perform(this));+ i++;+ }+ return v;+ }++ union Sass_Value* AST2C::operator()(Arguments* a)+ {+ union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false);+ for (size_t i = 0, L = a->length(); i < L; ++i) {+ sass_list_set_value(v, i, (*a)[i]->perform(this));+ }+ return v;+ }++ union Sass_Value* AST2C::operator()(Argument* a)+ { return a->value()->perform(this); }++ // not strictly necessary because of the fallback+ union Sass_Value* AST2C::operator()(Null* n)+ { return sass_make_null(); }++};
+ libsass/src/ast2c.hpp view
@@ -0,0 +1,39 @@+#ifndef SASS_AST2C_H+#define SASS_AST2C_H++#include "ast_fwd_decl.hpp"+#include "operation.hpp"+#include "sass/values.h"++namespace Sass {++ class AST2C : public Operation_CRTP<union Sass_Value*, AST2C> {++ public:++ AST2C() { }+ ~AST2C() { }++ union Sass_Value* operator()(Boolean*);+ union Sass_Value* operator()(Number*);+ union Sass_Value* operator()(Color_RGBA*);+ union Sass_Value* operator()(Color_HSLA*);+ union Sass_Value* operator()(String_Constant*);+ union Sass_Value* operator()(String_Quoted*);+ union Sass_Value* operator()(Custom_Warning*);+ union Sass_Value* operator()(Custom_Error*);+ union Sass_Value* operator()(List*);+ union Sass_Value* operator()(Map*);+ union Sass_Value* operator()(Null*);+ union Sass_Value* operator()(Arguments*);+ union Sass_Value* operator()(Argument*);++ // return sass error if type is not supported+ union Sass_Value* fallback(AST_Node* x)+ { return sass_make_error("unknown type for C-API"); }++ };++}++#endif
libsass/src/ast_def_macros.hpp view
@@ -35,16 +35,6 @@ LocalOption<size_t> cnt_##name(name, name + 1); \ if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \ -#define ATTACH_OPERATIONS()\-virtual void perform(Operation<void>* op) { (*op)(this); }\-virtual AST_Node_Ptr perform(Operation<AST_Node_Ptr>* op) { return (*op)(this); }\-virtual Statement_Ptr perform(Operation<Statement_Ptr>* op) { return (*op)(this); }\-virtual Expression_Ptr perform(Operation<Expression_Ptr>* op) { return (*op)(this); }\-virtual Selector_Ptr perform(Operation<Selector_Ptr>* op) { return (*op)(this); }\-virtual std::string perform(Operation<std::string>* op) { return (*op)(this); }\-virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) { return (*op)(this); }\-virtual Value_Ptr perform(Operation<Value_Ptr>* op) { return (*op)(this); }- #define ADD_PROPERTY(type, name)\ protected:\ type name##_;\@@ -76,5 +66,75 @@ const type& name() const { return name##_; } \ void name(type name##__) { hash_ = 0; name##_ = name##__; } \ private:++#ifdef DEBUG_SHARED_PTR++#define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \+ virtual klass* copy(std::string, size_t) const = 0; \+ virtual klass* clone(std::string, size_t) const = 0; \++#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \+ klass(const klass* ptr); \+ virtual klass* copy(std::string, size_t) const override = 0; \+ virtual klass* clone(std::string, size_t) const override = 0; \++#define ATTACH_AST_OPERATIONS(klass) \+ klass(const klass* ptr); \+ virtual klass* copy(std::string, size_t) const override; \+ virtual klass* clone(std::string, size_t) const override; \++#else++#define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \+ virtual klass* copy() const = 0; \+ virtual klass* clone() const = 0; \++#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \+ klass(const klass* ptr); \+ virtual klass* copy() const override = 0; \+ virtual klass* clone() const override = 0; \++#define ATTACH_AST_OPERATIONS(klass) \+ klass(const klass* ptr); \+ virtual klass* copy() const override; \+ virtual klass* clone() const override; \++#endif++#define ATTACH_VIRTUAL_CMP_OPERATIONS(klass) \+ virtual bool operator==(const klass& rhs) const = 0; \+ virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \++#define ATTACH_CMP_OPERATIONS(klass) \+ virtual bool operator==(const klass& rhs) const; \+ virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \++#ifdef DEBUG_SHARED_PTR++ #define IMPLEMENT_AST_OPERATORS(klass) \+ klass* klass::copy(std::string file, size_t line) const { \+ klass* cpy = new klass(this); \+ cpy->trace(file, line); \+ return cpy; \+ } \+ klass* klass::clone(std::string file, size_t line) const { \+ klass* cpy = copy(file, line); \+ cpy->cloneChildren(); \+ return cpy; \+ } \++#else++ #define IMPLEMENT_AST_OPERATORS(klass) \+ klass* klass::copy() const { \+ return new klass(this); \+ } \+ klass* klass::clone() const { \+ klass* cpy = copy(); \+ cpy->cloneChildren(); \+ return cpy; \+ } \++#endif #endif
libsass/src/ast_fwd_decl.cpp view
@@ -19,11 +19,13 @@ IMPLEMENT_BASE_CAST(Has_Block) IMPLEMENT_BASE_CAST(PreValue) IMPLEMENT_BASE_CAST(Value)+ IMPLEMENT_BASE_CAST(Color) IMPLEMENT_BASE_CAST(List) IMPLEMENT_BASE_CAST(String) IMPLEMENT_BASE_CAST(String_Constant) IMPLEMENT_BASE_CAST(Supports_Condition) IMPLEMENT_BASE_CAST(Selector)- IMPLEMENT_BASE_CAST(Simple_Selector)+ IMPLEMENT_BASE_CAST(SelectorComponent)+ IMPLEMENT_BASE_CAST(SimpleSelector) }
libsass/src/ast_fwd_decl.hpp view
@@ -1,17 +1,12 @@ #ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H -#include <map>-#include <set>-#include <deque>-#include <vector>-#include <typeinfo>-#include <iostream>-#include <algorithm>-#include <unordered_map>-#include <unordered_set>-#include "memory/SharedPtr.hpp"+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include "sass/functions.h"+#include "memory/SharedPtr.hpp" ///////////////////////////////////////////// // Forward declarations for the AST visitors.@@ -19,266 +14,119 @@ namespace Sass { class AST_Node;- typedef AST_Node* AST_Node_Ptr;- typedef AST_Node const* AST_Node_Ptr_Const; class Has_Block;- typedef Has_Block* Has_Block_Ptr;- typedef Has_Block const* Has_Block_Ptr_Const; - class Simple_Selector;- typedef Simple_Selector* Simple_Selector_Ptr;- typedef Simple_Selector const* Simple_Selector_Ptr_Const;+ class SimpleSelector; + class Parent_Reference;+ class PreValue;- typedef PreValue* PreValue_Ptr;- typedef PreValue const* PreValue_Ptr_Const;- class Thunk;- typedef Thunk* Thunk_Ptr;- typedef Thunk const* Thunk_Ptr_Const; class Block;- typedef Block* Block_Ptr;- typedef Block const* Block_Ptr_Const; class Expression;- typedef Expression* Expression_Ptr;- typedef Expression const* Expression_Ptr_Const; class Statement;- typedef Statement* Statement_Ptr;- typedef Statement const* Statement_Ptr_Const; class Value;- typedef Value* Value_Ptr;- typedef Value const* Value_Ptr_Const; class Declaration;- typedef Declaration* Declaration_Ptr;- typedef Declaration const* Declaration_Ptr_Const; class Ruleset;- typedef Ruleset* Ruleset_Ptr;- typedef Ruleset const* Ruleset_Ptr_Const; class Bubble;- typedef Bubble* Bubble_Ptr;- typedef Bubble const* Bubble_Ptr_Const; class Trace;- typedef Trace* Trace_Ptr;- typedef Trace const* Trace_Ptr_Const; - class Media_Block;- typedef Media_Block* Media_Block_Ptr;- typedef Media_Block const* Media_Block_Ptr_Const;+ class MediaRule;+ class CssMediaRule;+ class CssMediaQuery;+ class Supports_Block;- typedef Supports_Block* Supports_Block_Ptr;- typedef Supports_Block const* Supports_Block_Ptr_Const; class Directive;- typedef Directive* Directive_Ptr;- typedef Directive const* Directive_Ptr_Const; - class Keyframe_Rule;- typedef Keyframe_Rule* Keyframe_Rule_Ptr;- typedef Keyframe_Rule const* Keyframe_Rule_Ptr_Const; class At_Root_Block;- typedef At_Root_Block* At_Root_Block_Ptr;- typedef At_Root_Block const* At_Root_Block_Ptr_Const; class Assignment;- typedef Assignment* Assignment_Ptr;- typedef Assignment const* Assignment_Ptr_Const; class Import;- typedef Import* Import_Ptr;- typedef Import const* Import_Ptr_Const; class Import_Stub;- typedef Import_Stub* Import_Stub_Ptr;- typedef Import_Stub const* Import_Stub_Ptr_Const; class Warning;- typedef Warning* Warning_Ptr;- typedef Warning const* Warning_Ptr_Const; class Error;- typedef Error* Error_Ptr;- typedef Error const* Error_Ptr_Const; class Debug;- typedef Debug* Debug_Ptr;- typedef Debug const* Debug_Ptr_Const; class Comment;- typedef Comment* Comment_Ptr;- typedef Comment const* Comment_Ptr_Const; class If;- typedef If* If_Ptr;- typedef If const* If_Ptr_Const; class For;- typedef For* For_Ptr;- typedef For const* For_Ptr_Const; class Each;- typedef Each* Each_Ptr;- typedef Each const* Each_Ptr_Const; class While;- typedef While* While_Ptr;- typedef While const* While_Ptr_Const; class Return;- typedef Return* Return_Ptr;- typedef Return const* Return_Ptr_Const; class Content;- typedef Content* Content_Ptr;- typedef Content const* Content_Ptr_Const;- class Extension;- typedef Extension* Extension_Ptr;- typedef Extension const* Extension_Ptr_Const;+ class ExtendRule; class Definition;- typedef Definition* Definition_Ptr;- typedef Definition const* Definition_Ptr_Const; class List;- typedef List* List_Ptr;- typedef List const* List_Ptr_Const; class Map;- typedef Map* Map_Ptr;- typedef Map const* Map_Ptr_Const; class Function;- typedef Function* Function_Ptr;- typedef Function const* Function_Ptr_Const; class Mixin_Call;- typedef Mixin_Call* Mixin_Call_Ptr;- typedef Mixin_Call const* Mixin_Call_Ptr_Const; class Binary_Expression;- typedef Binary_Expression* Binary_Expression_Ptr;- typedef Binary_Expression const* Binary_Expression_Ptr_Const; class Unary_Expression;- typedef Unary_Expression* Unary_Expression_Ptr;- typedef Unary_Expression const* Unary_Expression_Ptr_Const; class Function_Call;- typedef Function_Call* Function_Call_Ptr;- typedef Function_Call const* Function_Call_Ptr_Const;- class Function_Call_Schema;- typedef Function_Call_Schema* Function_Call_Schema_Ptr;- typedef Function_Call_Schema const* Function_Call_Schema_Ptr_Const; class Custom_Warning;- typedef Custom_Warning* Custom_Warning_Ptr;- typedef Custom_Warning const* Custom_Warning_Ptr_Const; class Custom_Error;- typedef Custom_Error* Custom_Error_Ptr;- typedef Custom_Error const* Custom_Error_Ptr_Const; class Variable;- typedef Variable* Variable_Ptr;- typedef Variable const* Variable_Ptr_Const; class Number;- typedef Number* Number_Ptr;- typedef Number const* Number_Ptr_Const; class Color;- typedef Color* Color_Ptr;- typedef Color const* Color_Ptr_Const;+ class Color_RGBA;+ class Color_HSLA; class Boolean;- typedef Boolean* Boolean_Ptr;- typedef Boolean const* Boolean_Ptr_Const; class String;- typedef String* String_Ptr;- typedef String const* String_Ptr_Const;+ class Null; class String_Schema;- typedef String_Schema* String_Schema_Ptr;- typedef String_Schema const* String_Schema_Ptr_Const; class String_Constant;- typedef String_Constant* String_Constant_Ptr;- typedef String_Constant const* String_Constant_Ptr_Const; class String_Quoted;- typedef String_Quoted* String_Quoted_Ptr;- typedef String_Quoted const* String_Quoted_Ptr_Const; class Media_Query;- typedef Media_Query* Media_Query_Ptr;- typedef Media_Query const* Media_Query_Ptr_Const; class Media_Query_Expression;- typedef Media_Query_Expression* Media_Query_Expression_Ptr;- typedef Media_Query_Expression const* Media_Query_Expression_Ptr_Const; class Supports_Condition;- typedef Supports_Condition* Supports_Condition_Ptr;- typedef Supports_Condition const* Supports_Condition_Ptr_Const; class Supports_Operator;- typedef Supports_Operator* Supports_Operator_Ptr;- typedef Supports_Operator const* Supports_Operator_Ptr_Const; class Supports_Negation;- typedef Supports_Negation* Supports_Negation_Ptr;- typedef Supports_Negation const* Supports_Negation_Ptr_Const; class Supports_Declaration;- typedef Supports_Declaration* Supports_Declaration_Ptr;- typedef Supports_Declaration const* Supports_Declaration_Ptr_Const; class Supports_Interpolation;- typedef Supports_Interpolation* Supports_Interpolation_Ptr;- typedef Supports_Interpolation const* Supports_Interpolation_Ptr_Const;--- class Null;- typedef Null* Null_Ptr;- typedef Null const* Null_Ptr_Const;-+ class At_Root_Query;- typedef At_Root_Query* At_Root_Query_Ptr;- typedef At_Root_Query const* At_Root_Query_Ptr_Const;- class Parent_Selector;- typedef Parent_Selector* Parent_Selector_Ptr;- typedef Parent_Selector const* Parent_Selector_Ptr_Const; class Parameter;- typedef Parameter* Parameter_Ptr;- typedef Parameter const* Parameter_Ptr_Const; class Parameters;- typedef Parameters* Parameters_Ptr;- typedef Parameters const* Parameters_Ptr_Const; class Argument;- typedef Argument* Argument_Ptr;- typedef Argument const* Argument_Ptr_Const; class Arguments;- typedef Arguments* Arguments_Ptr;- typedef Arguments const* Arguments_Ptr_Const; class Selector;- typedef Selector* Selector_Ptr;- typedef Selector const* Selector_Ptr_Const; class Selector_Schema;- typedef Selector_Schema* Selector_Schema_Ptr;- typedef Selector_Schema const* Selector_Schema_Ptr_Const; class Placeholder_Selector;- typedef Placeholder_Selector* Placeholder_Selector_Ptr;- typedef Placeholder_Selector const* Placeholder_Selector_Ptr_Const;- class Element_Selector;- typedef Element_Selector* Element_Selector_Ptr;- typedef Element_Selector const* Element_Selector_Ptr_Const;+ class Type_Selector; class Class_Selector;- typedef Class_Selector* Class_Selector_Ptr;- typedef Class_Selector const* Class_Selector_Ptr_Const; class Id_Selector;- typedef Id_Selector* Id_Selector_Ptr;- typedef Id_Selector const* Id_Selector_Ptr_Const; class Attribute_Selector;- typedef Attribute_Selector* Attribute_Selector_Ptr;- typedef Attribute_Selector const* Attribute_Selector_Ptr_Const; class Pseudo_Selector;- typedef Pseudo_Selector* Pseudo_Selector_Ptr;- typedef Pseudo_Selector const * Pseudo_Selector_Ptr_Const;- class Wrapped_Selector;- typedef Wrapped_Selector* Wrapped_Selector_Ptr;- typedef Wrapped_Selector const * Wrapped_Selector_Ptr_Const;- class Compound_Selector;- typedef Compound_Selector* Compound_Selector_Ptr;- typedef Compound_Selector const * Compound_Selector_Ptr_Const;- class Complex_Selector;- typedef Complex_Selector* Complex_Selector_Ptr;- typedef Complex_Selector const * Complex_Selector_Ptr_Const;- class Selector_List;- typedef Selector_List* Selector_List_Ptr;- typedef Selector_List const * Selector_List_Ptr_Const;-+ + class SelectorComponent;+ class SelectorCombinator;+ class CompoundSelector;+ class ComplexSelector;+ class SelectorList; // common classes class Context; class Expand; class Eval; + class Extension;+ // declare classes that are instances of memory nodes- // #define IMPL_MEM_OBJ(type) using type##_Obj = SharedImpl<type>- #define IMPL_MEM_OBJ(type) typedef SharedImpl<type> type##_Obj+ // Note: also add a mapping without underscore+ // ToDo: move to camelCase vars in the future+ #define IMPL_MEM_OBJ(type) \+ typedef SharedImpl<type> type##Obj; \+ typedef SharedImpl<type> type##_Obj; \ IMPL_MEM_OBJ(AST_Node); IMPL_MEM_OBJ(Statement);@@ -286,7 +134,9 @@ IMPL_MEM_OBJ(Ruleset); IMPL_MEM_OBJ(Bubble); IMPL_MEM_OBJ(Trace);- IMPL_MEM_OBJ(Media_Block);+ IMPL_MEM_OBJ(MediaRule);+ IMPL_MEM_OBJ(CssMediaRule);+ IMPL_MEM_OBJ(CssMediaQuery); IMPL_MEM_OBJ(Supports_Block); IMPL_MEM_OBJ(Directive); IMPL_MEM_OBJ(Keyframe_Rule);@@ -301,14 +151,13 @@ IMPL_MEM_OBJ(Comment); IMPL_MEM_OBJ(PreValue); IMPL_MEM_OBJ(Has_Block);- IMPL_MEM_OBJ(Thunk); IMPL_MEM_OBJ(If); IMPL_MEM_OBJ(For); IMPL_MEM_OBJ(Each); IMPL_MEM_OBJ(While); IMPL_MEM_OBJ(Return); IMPL_MEM_OBJ(Content);- IMPL_MEM_OBJ(Extension);+ IMPL_MEM_OBJ(ExtendRule); IMPL_MEM_OBJ(Definition); IMPL_MEM_OBJ(Mixin_Call); IMPL_MEM_OBJ(Value);@@ -319,12 +168,13 @@ IMPL_MEM_OBJ(Binary_Expression); IMPL_MEM_OBJ(Unary_Expression); IMPL_MEM_OBJ(Function_Call);- IMPL_MEM_OBJ(Function_Call_Schema); IMPL_MEM_OBJ(Custom_Warning); IMPL_MEM_OBJ(Custom_Error); IMPL_MEM_OBJ(Variable); IMPL_MEM_OBJ(Number); IMPL_MEM_OBJ(Color);+ IMPL_MEM_OBJ(Color_RGBA);+ IMPL_MEM_OBJ(Color_HSLA); IMPL_MEM_OBJ(Boolean); IMPL_MEM_OBJ(String_Schema); IMPL_MEM_OBJ(String);@@ -339,87 +189,37 @@ IMPL_MEM_OBJ(Supports_Interpolation); IMPL_MEM_OBJ(At_Root_Query); IMPL_MEM_OBJ(Null);- IMPL_MEM_OBJ(Parent_Selector);+ IMPL_MEM_OBJ(Parent_Reference); IMPL_MEM_OBJ(Parameter); IMPL_MEM_OBJ(Parameters); IMPL_MEM_OBJ(Argument); IMPL_MEM_OBJ(Arguments); IMPL_MEM_OBJ(Selector); IMPL_MEM_OBJ(Selector_Schema);- IMPL_MEM_OBJ(Simple_Selector);+ IMPL_MEM_OBJ(SimpleSelector); IMPL_MEM_OBJ(Placeholder_Selector);- IMPL_MEM_OBJ(Element_Selector);+ IMPL_MEM_OBJ(Type_Selector); IMPL_MEM_OBJ(Class_Selector); IMPL_MEM_OBJ(Id_Selector); IMPL_MEM_OBJ(Attribute_Selector); IMPL_MEM_OBJ(Pseudo_Selector);- IMPL_MEM_OBJ(Wrapped_Selector);- IMPL_MEM_OBJ(Compound_Selector);- IMPL_MEM_OBJ(Complex_Selector);- IMPL_MEM_OBJ(Selector_List); - // ###########################################################################- // Implement compare, order and hashing operations for AST Nodes- // ###########################################################################-- struct HashNodes {- template <class T>- size_t operator() (const T& ex) const {- return ex.isNull() ? 0 : ex->hash();- }- };- struct OrderNodes {- template <class T>- bool operator() (const T& lhs, const T& rhs) const {- return !lhs.isNull() && !rhs.isNull() && *lhs < *rhs;- }- };- struct CompareNodes {- template <class T>- bool operator() (const T& lhs, const T& rhs) const {- // code around sass logic issue. 1px == 1 is true- // but both items are still different keys in maps- if (dynamic_cast<Number*>(lhs.ptr()))- if (dynamic_cast<Number*>(rhs.ptr()))- return lhs->hash() == rhs->hash();- return !lhs.isNull() && !rhs.isNull() && *lhs == *rhs;- }- };+ IMPL_MEM_OBJ(SelectorComponent);+ IMPL_MEM_OBJ(SelectorCombinator);+ IMPL_MEM_OBJ(CompoundSelector);+ IMPL_MEM_OBJ(ComplexSelector);+ IMPL_MEM_OBJ(SelectorList); // ########################################################################### // some often used typedefs // ########################################################################### - typedef std::unordered_map<- Expression_Obj, // key- Expression_Obj, // value- HashNodes, // hasher- CompareNodes // compare- > ExpressionMap;- typedef std::unordered_set<- Expression_Obj, // value- HashNodes, // hasher- CompareNodes // compare- > ExpressionSet;-- typedef std::string SubSetMapKey;- typedef std::vector<std::string> SubSetMapKeys;-- typedef std::pair<Complex_Selector_Obj, Compound_Selector_Obj> SubSetMapPair;- typedef std::pair<Compound_Selector_Obj, Complex_Selector_Obj> SubSetMapLookup;- typedef std::vector<SubSetMapPair> SubSetMapPairs;- typedef std::vector<SubSetMapLookup> SubSetMapLookups;-- typedef std::pair<Complex_Selector_Obj, SubSetMapPairs> SubSetMapResult;- typedef std::vector<SubSetMapResult> SubSetMapResults;-- typedef std::deque<Complex_Selector_Obj> ComplexSelectorDeque;- typedef std::set<Simple_Selector_Obj, OrderNodes> SimpleSelectorSet;- typedef std::set<Complex_Selector_Obj, OrderNodes> ComplexSelectorSet;- typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet;- typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict;-- typedef std::vector<Sass_Import_Entry>* ImporterStack;+ typedef std::vector<Block*> BlockStack;+ typedef std::vector<Sass_Callee> CalleeStack;+ typedef std::vector<AST_Node_Obj> CallStack;+ typedef std::vector<CssMediaRuleObj> MediaStack;+ typedef std::vector<SelectorListObj> SelectorStack;+ typedef std::vector<Sass_Import_Entry> ImporterStack; // only to switch implementations for testing #define environment_map std::map@@ -452,11 +252,13 @@ DECLARE_BASE_CAST(PreValue) DECLARE_BASE_CAST(Value) DECLARE_BASE_CAST(List)+ DECLARE_BASE_CAST(Color) DECLARE_BASE_CAST(String) DECLARE_BASE_CAST(String_Constant) DECLARE_BASE_CAST(Supports_Condition) DECLARE_BASE_CAST(Selector)- DECLARE_BASE_CAST(Simple_Selector)+ DECLARE_BASE_CAST(SimpleSelector)+ DECLARE_BASE_CAST(SelectorComponent) }
+ libsass/src/ast_helpers.hpp view
@@ -0,0 +1,292 @@+#ifndef SASS_AST_HELPERS_H+#define SASS_AST_HELPERS_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include <algorithm>+#include <functional>+#include "util_string.hpp"++namespace Sass {++ // ###########################################################################+ // ###########################################################################++ // easier to search with name+ const bool DELAYED = true;++ // ToDo: should this really be hardcoded+ // Note: most methods follow precision option+ const double NUMBER_EPSILON = 1e-12;++ // macro to test if numbers are equal within a small error margin+ #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON++ // ###########################################################################+ // We define various functions and functors here.+ // Functions satisfy the BinaryPredicate requirement+ // Functors are structs used for e.g. unordered_map+ // ###########################################################################++ // ###########################################################################+ // Implement compare and hashing operations for raw pointers+ // ###########################################################################++ template <class T>+ size_t PtrHashFn(const T* ptr) {+ return std::hash<std::size_t>()((size_t)ptr);+ }++ struct PtrHash {+ template <class T>+ size_t operator() (const T* ptr) const {+ return PtrHashFn(ptr);+ }+ };++ template <class T>+ bool PtrEqualityFn(const T* lhs, const T* rhs) {+ return lhs == rhs; // compare raw pointers+ }++ struct PtrEquality {+ template <class T>+ bool operator() (const T* lhs, const T* rhs) const {+ return PtrEqualityFn<T>(lhs, rhs);+ }+ };++ // ###########################################################################+ // Implement compare and hashing operations for AST Nodes+ // ###########################################################################++ // TODO: get rid of funtions and use ObjEquality<T>++ template <class T>+ // Hash the raw pointer instead of object+ size_t ObjPtrHashFn(const T& obj) {+ return PtrHashFn(obj.ptr());+ }++ struct ObjPtrHash {+ template <class T>+ // Hash the raw pointer instead of object+ size_t operator() (const T& obj) const {+ return ObjPtrHashFn(obj);+ }+ };++ template <class T>+ // Hash the object and its content+ size_t ObjHashFn(const T& obj) {+ return obj ? obj->hash() : 0;+ }++ struct ObjHash {+ template <class T>+ // Hash the object and its content+ size_t operator() (const T& obj) const {+ return ObjHashFn(obj);+ }+ };++ template <class T>+ // Hash the object behind pointer+ size_t PtrObjHashFn(const T* obj) {+ return obj ? obj->hash() : 0;+ }++ struct PtrObjHash {+ template <class T>+ // Hash the object behind pointer+ size_t operator() (const T* obj) const {+ return PtrObjHashFn(obj);+ }+ };++ template <class T>+ // Compare raw pointers to the object+ bool ObjPtrEqualityFn(const T& lhs, const T& rhs) {+ return PtrEqualityFn(lhs.ptr(), rhs.ptr());+ }++ struct ObjPtrEquality {+ template <class T>+ // Compare raw pointers to the object+ bool operator() (const T& lhs, const T& rhs) const {+ return ObjPtrEqualityFn<T>(lhs, rhs);+ }+ };++ template <class T>+ // Compare the objects behind the pointers+ bool PtrObjEqualityFn(const T* lhs, const T* rhs) {+ if (lhs == nullptr) return rhs == nullptr;+ else if (rhs == nullptr) return false;+ else return *lhs == *rhs;+ }++ struct PtrObjEquality {+ template <class T>+ // Compare the objects behind the pointers+ bool operator() (const T* lhs, const T* rhs) const {+ return PtrObjEqualityFn<T>(lhs, rhs);+ }+ };++ template <class T>+ // Compare the objects and its contents+ bool ObjEqualityFn(const T& lhs, const T& rhs) {+ return PtrObjEqualityFn(lhs.ptr(), rhs.ptr());+ }++ struct ObjEquality {+ template <class T>+ // Compare the objects and its contents+ bool operator() (const T& lhs, const T& rhs) const {+ return ObjEqualityFn<T>(lhs, rhs);+ }+ };++ // ###########################################################################+ // Implement ordering operations for AST Nodes+ // ###########################################################################++ template <class T>+ // Compare the objects behind pointers+ bool PtrObjLessThanFn(const T* lhs, const T* rhs) {+ if (lhs == nullptr) return rhs != nullptr;+ else if (rhs == nullptr) return false;+ else return *lhs < *rhs;+ }++ struct PtrObjLessThan {+ template <class T>+ // Compare the objects behind pointers+ bool operator() (const T* lhs, const T* rhs) const {+ return PtrObjLessThanFn<T>(lhs, rhs);+ }+ };++ template <class T>+ // Compare the objects and its content+ bool ObjLessThanFn(const T& lhs, const T& rhs) {+ return PtrObjLessThanFn(lhs.ptr(), rhs.ptr());+ };++ struct ObjLessThan {+ template <class T>+ // Compare the objects and its content+ bool operator() (const T& lhs, const T& rhs) const {+ return ObjLessThanFn<T>(lhs, rhs);+ }+ };++ // ###########################################################################+ // Some STL helper functions+ // ###########################################################################++ // Check if all elements are equal+ template <class X, class Y,+ typename XT = typename X::value_type,+ typename YT = typename Y::value_type>+ bool ListEquality(const X& lhs, const Y& rhs,+ bool(*cmp)(const XT*, const YT*))+ {+ return lhs.size() == rhs.size() &&+ std::equal(lhs.begin(), lhs.end(),+ rhs.begin(), cmp);+ }++ // Return if Vector is empty+ template <class T>+ bool listIsEmpty(T* cnt) {+ return cnt && cnt->empty();+ }++ // Erase items from vector that match predicate+ template<class T, class UnaryPredicate>+ void listEraseItemIf(T& vec, UnaryPredicate* predicate)+ {+ vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end());+ }++ // Check that every item in `lhs` is also in `rhs`+ // Note: this works by comparing the raw pointers+ template <typename T>+ bool listIsSubsetOrEqual(const T& lhs, const T& rhs) {+ for (const auto& item : lhs) {+ if (std::find(rhs.begin(), rhs.end(), item) == rhs.end())+ return false;+ }+ return true;+ }++ // ##########################################################################+ // Returns whether [name] is the name of a pseudo-element+ // that can be written with pseudo-class syntax (CSS2 vs CSS3):+ // `:before`, `:after`, `:first-line`, or `:first-letter`+ // ##########################################################################+ inline bool isFakePseudoElement(const std::string& name)+ {+ return Util::equalsLiteral("after", name)+ || Util::equalsLiteral("before", name)+ || Util::equalsLiteral("first-line", name)+ || Util::equalsLiteral("first-letter", name);+ }++ // ##########################################################################+ // Names of pseudo selectors that take selectors as arguments,+ // and that are subselectors of their arguments.+ // For example, `.foo` is a superselector of `:matches(.foo)`.+ // ##########################################################################+ inline bool isSubselectorPseudo(const std::string& norm)+ {+ return Util::equalsLiteral("any", norm)+ || Util::equalsLiteral("matches", norm)+ || Util::equalsLiteral("nth-child", norm)+ || Util::equalsLiteral("nth-last-child", norm);+ }+ // EO isSubselectorPseudo++ // ###########################################################################+ // Pseudo-class selectors that take unadorned selectors as arguments.+ // ###########################################################################+ inline bool isSelectorPseudoClass(const std::string& test)+ {+ return Util::equalsLiteral("not", test)+ || Util::equalsLiteral("matches", test)+ || Util::equalsLiteral("current", test)+ || Util::equalsLiteral("any", test)+ || Util::equalsLiteral("has", test)+ || Util::equalsLiteral("host", test)+ || Util::equalsLiteral("host-context", test);+ }+ // EO isSelectorPseudoClass++ // ###########################################################################+ // Pseudo-element selectors that take unadorned selectors as arguments.+ // ###########################################################################+ inline bool isSelectorPseudoElement(const std::string& test)+ {+ return Util::equalsLiteral("slotted", test);+ }+ // EO isSelectorPseudoElement++ // ###########################################################################+ // Pseudo-element selectors that has binominals+ // ###########################################################################+ inline bool isSelectorPseudoBinominal(const std::string& test)+ {+ return Util::equalsLiteral("nth-child", test)+ || Util::equalsLiteral("nth-last-child", test);+ }+ // isSelectorPseudoBinominal++ // ###########################################################################+ // ###########################################################################++}++#endif
+ libsass/src/ast_sel_cmp.cpp view
@@ -0,0 +1,396 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast_selectors.hpp"++namespace Sass {++ /*#########################################################################*/+ // Compare against base class on right hand side+ // try to find the most specialized implementation+ /*#########################################################################*/++ // Selector lists can be compared to comma lists+ bool SelectorList::operator== (const Expression& rhs) const+ {+ if (auto l = Cast<List>(&rhs)) { return *this == *l; }+ if (auto s = Cast<Selector>(&rhs)) { return *this == *s; }+ if (Cast<String>(&rhs) || Cast<Null>(&rhs)) { return false; }+ throw std::runtime_error("invalid selector base classes to compare");+ }++ // Selector lists can be compared to comma lists+ bool SelectorList::operator== (const Selector& rhs) const+ {+ if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }+ if (auto list = Cast<List>(&rhs)) { return *this == *list; }+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool ComplexSelector::operator== (const Selector& rhs) const+ {+ if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<ComplexSelector>(&rhs)) { return *sel == *this; }+ if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool SelectorCombinator::operator== (const Selector& rhs) const+ {+ if (auto cpx = Cast<SelectorCombinator>(&rhs)) { return *this == *cpx; }+ return false;+ }++ bool CompoundSelector::operator== (const Selector& rhs) const+ {+ if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+ throw std::runtime_error("invalid selector base classes to compare");+ }++ bool SimpleSelector::operator== (const Selector& rhs) const+ {+ if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }+ if (auto sel = Cast<SimpleSelector>(&rhs)) return *this == *sel;+ throw std::runtime_error("invalid selector base classes to compare");+ }++ /*#########################################################################*/+ /*#########################################################################*/++ bool SelectorList::operator== (const SelectorList& rhs) const+ {+ if (&rhs == this) return true;+ if (rhs.length() != length()) return false;+ std::unordered_set<const ComplexSelector*, PtrObjHash, PtrObjEquality> lhs_set;+ lhs_set.reserve(length());+ for (const ComplexSelectorObj& element : elements()) {+ lhs_set.insert(element.ptr());+ }+ for (const ComplexSelectorObj& element : rhs.elements()) {+ if (lhs_set.find(element.ptr()) == lhs_set.end()) return false;+ }+ return true;+ }++++ /*#########################################################################*/+ // Compare SelectorList against all other selector types+ /*#########################################################################*/++ bool SelectorList::operator== (const ComplexSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (length() != 1) return false;+ // Compare simple selectors+ return *get(0) == rhs;+ }++ bool SelectorList::operator== (const CompoundSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (length() != 1) return false;+ // Compare simple selectors+ return *get(0) == rhs;+ }++ bool SelectorList::operator== (const SimpleSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (length() != 1) return false;+ // Compare simple selectors+ return *get(0) == rhs;+ }++ /*#########################################################################*/+ // Compare ComplexSelector against itself+ /*#########################################################################*/++ bool ComplexSelector::operator== (const ComplexSelector& rhs) const+ {+ size_t len = length();+ size_t rlen = rhs.length();+ if (len != rlen) return false;+ for (size_t i = 0; i < len; i += 1) {+ if (*get(i) != *rhs.get(i)) return false;+ }+ return true;+ }++ /*#########################################################################*/+ // Compare ComplexSelector against all other selector types+ /*#########################################################################*/++ bool ComplexSelector::operator== (const SelectorList& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (rhs.length() != 1) return false;+ // Compare complex selector+ return *this == *rhs.get(0);+ }++ bool ComplexSelector::operator== (const CompoundSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (length() != 1) return false;+ // Compare compound selector+ return *get(0) == rhs;+ }++ bool ComplexSelector::operator== (const SimpleSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (length() != 1) return false;+ // Compare simple selectors+ return *get(0) == rhs;+ }++ /*#########################################################################*/+ // Compare SelectorCombinator against itself+ /*#########################################################################*/++ bool SelectorCombinator::operator==(const SelectorCombinator& rhs) const+ {+ return combinator() == rhs.combinator();+ }++ /*#########################################################################*/+ // Compare SelectorCombinator against SelectorComponent+ /*#########################################################################*/++ bool SelectorCombinator::operator==(const SelectorComponent& rhs) const+ {+ if (const SelectorCombinator * sel = rhs.getCombinator()) {+ return *this == *sel;+ }+ return false;+ }++ bool CompoundSelector::operator==(const SelectorComponent& rhs) const+ {+ if (const CompoundSelector * sel = rhs.getCompound()) {+ return *this == *sel;+ }+ return false;+ }++ /*#########################################################################*/+ // Compare CompoundSelector against itself+ /*#########################################################################*/+ // ToDo: Verifiy implementation+ /*#########################################################################*/++ bool CompoundSelector::operator== (const CompoundSelector& rhs) const+ {+ // std::cerr << "comp vs comp\n";+ if (&rhs == this) return true;+ if (rhs.length() != length()) return false;+ std::unordered_set<const SimpleSelector*, PtrObjHash, PtrObjEquality> lhs_set;+ lhs_set.reserve(length());+ for (const SimpleSelectorObj& element : elements()) {+ lhs_set.insert(element.ptr());+ }+ // there is no break?!+ for (const SimpleSelectorObj& element : rhs.elements()) {+ if (lhs_set.find(element.ptr()) == lhs_set.end()) return false;+ }+ return true;+ }+++ /*#########################################################################*/+ // Compare CompoundSelector against all other selector types+ /*#########################################################################*/++ bool CompoundSelector::operator== (const SelectorList& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (rhs.length() != 1) return false;+ // Compare complex selector+ return *this == *rhs.get(0);+ }++ bool CompoundSelector::operator== (const ComplexSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (rhs.length() != 1) return false;+ // Compare compound selector+ return *this == *rhs.get(0);+ }++ bool CompoundSelector::operator== (const SimpleSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return false;+ // Must have exactly one item+ size_t rlen = length();+ if (rlen > 1) return false;+ if (rlen == 0) return true;+ // Compare simple selectors+ return *get(0) < rhs;+ }++ /*#########################################################################*/+ // Compare SimpleSelector against itself (upcast from abstract base)+ /*#########################################################################*/++ // DOES NOT EXIST FOR ABSTRACT BASE CLASS++ /*#########################################################################*/+ // Compare SimpleSelector against all other selector types+ /*#########################################################################*/++ bool SimpleSelector::operator== (const SelectorList& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (rhs.length() != 1) return false;+ // Compare complex selector+ return *this == *rhs.get(0);+ }++ bool SimpleSelector::operator== (const ComplexSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return true;+ // Must have exactly one item+ if (rhs.length() != 1) return false;+ // Compare compound selector+ return *this == *rhs.get(0);+ }++ bool SimpleSelector::operator== (const CompoundSelector& rhs) const+ {+ // If both are empty they are equal+ if (empty() && rhs.empty()) return false;+ // Must have exactly one item+ if (rhs.length() != 1) return false;+ // Compare simple selector+ return *this == *rhs.get(0);+ }++ /*#########################################################################*/+ /*#########################################################################*/++ bool Id_Selector::operator== (const SimpleSelector& rhs) const+ {+ auto sel = Cast<Id_Selector>(&rhs);+ return sel ? *this == *sel : false;+ }++ bool Type_Selector::operator== (const SimpleSelector& rhs) const+ {+ auto sel = Cast<Type_Selector>(&rhs);+ return sel ? *this == *sel : false;+ }++ bool Class_Selector::operator== (const SimpleSelector& rhs) const+ {+ auto sel = Cast<Class_Selector>(&rhs);+ return sel ? *this == *sel : false;+ }++ bool Pseudo_Selector::operator== (const SimpleSelector& rhs) const+ {+ auto sel = Cast<Pseudo_Selector>(&rhs);+ return sel ? *this == *sel : false;+ }++ bool Attribute_Selector::operator== (const SimpleSelector& rhs) const+ {+ auto sel = Cast<Attribute_Selector>(&rhs);+ return sel ? *this == *sel : false;+ }++ bool Placeholder_Selector::operator== (const SimpleSelector& rhs) const+ {+ auto sel = Cast<Placeholder_Selector>(&rhs);+ return sel ? *this == *sel : false;+ }++ /*#########################################################################*/+ /*#########################################################################*/++ bool Id_Selector::operator== (const Id_Selector& rhs) const+ {+ // ID has no namespacing+ return name() == rhs.name();+ }++ bool Type_Selector::operator== (const Type_Selector& rhs) const+ {+ return is_ns_eq(rhs) && name() == rhs.name();+ }++ bool Class_Selector::operator== (const Class_Selector& rhs) const+ {+ // Class has no namespacing+ return name() == rhs.name();+ }++ bool Placeholder_Selector::operator== (const Placeholder_Selector& rhs) const+ {+ // Placeholder has no namespacing+ return name() == rhs.name();+ }++ bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const+ {+ // smaller return, equal go on, bigger abort+ if (is_ns_eq(rhs)) {+ if (name() != rhs.name()) return false;+ if (matcher() != rhs.matcher()) return false;+ if (modifier() != rhs.modifier()) return false;+ const String* lhs_val = value();+ const String* rhs_val = rhs.value();+ return PtrObjEquality()(lhs_val, rhs_val);+ }+ else { return false; }+ }++ bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const+ {+ if (is_ns_eq(rhs)) {+ if (name() != rhs.name()) return false;+ if (isElement() != rhs.isElement()) return false;+ const String* lhs_arg = argument();+ const String* rhs_arg = rhs.argument();+ if (!PtrObjEquality()(lhs_arg, rhs_arg)) return false;+ const SelectorList* lhs_sel = selector();+ const SelectorList* rhs_sel = rhs.selector();+ return PtrObjEquality()(lhs_sel, rhs_sel);+ }+ else { return false; }+ }++ /*#########################################################################*/+ /*#########################################################################*/++}
+ libsass/src/ast_sel_super.cpp view
@@ -0,0 +1,539 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++#include "util_string.hpp"++namespace Sass {++ // ##########################################################################+ // To compare/debug against libsass you can use debugger.hpp:+ // c++: std::cerr << "result " << debug_vec(compound) << "\n";+ // dart: stderr.writeln("result " + compound.toString());+ // ##########################################################################++ // ##########################################################################+ // Returns whether [list1] is a superselector of [list2].+ // That is, whether [list1] matches every element that+ // [list2] matches, as well as possibly additional elements.+ // ##########################################################################+ bool listIsSuperslector(+ const std::vector<ComplexSelectorObj>& list1,+ const std::vector<ComplexSelectorObj>& list2);++ // ##########################################################################+ // Returns whether [complex1] is a superselector of [complex2].+ // That is, whether [complex1] matches every element that+ // [complex2] matches, as well as possibly additional elements.+ // ##########################################################################+ bool complexIsSuperselector(+ const std::vector<SelectorComponentObj>& complex1,+ const std::vector<SelectorComponentObj>& complex2);++ // ##########################################################################+ // Returns all pseudo selectors in [compound] that have+ // a selector argument, and that have the given [name].+ // ##########################################################################+ std::vector<Pseudo_Selector_Obj> selectorPseudoNamed(+ CompoundSelectorObj compound, std::string name)+ {+ std::vector<Pseudo_Selector_Obj> rv;+ for (SimpleSelectorObj sel : compound->elements()) {+ if (Pseudo_Selector_Obj pseudo = Cast<Pseudo_Selector>(sel)) {+ if (pseudo->isClass() && pseudo->selector()) {+ if (sel->name() == name) {+ rv.push_back(sel);+ }+ }+ }+ }+ return rv;+ }+ // EO selectorPseudoNamed++ // ##########################################################################+ // Returns whether [simple1] is a superselector of [simple2].+ // That is, whether [simple1] matches every element that+ // [simple2] matches, as well as possibly additional elements.+ // ##########################################################################+ bool simpleIsSuperselector(+ const SimpleSelectorObj& simple1,+ const SimpleSelectorObj& simple2)+ {+ // If they are equal they are superselectors+ if (ObjEqualityFn(simple1, simple2)) {+ return true;+ }+ // Some selector pseudoclasses can match normal selectors.+ if (const Pseudo_Selector* pseudo = Cast<Pseudo_Selector>(simple2)) {+ if (pseudo->selector() && isSubselectorPseudo(pseudo->normalized())) {+ for (auto complex : pseudo->selector()->elements()) {+ // Make sure we have exacly one items+ if (complex->length() != 1) {+ return false;+ }+ // That items must be a compound selector+ if (auto compound = Cast<CompoundSelector>(complex->at(0))) {+ // It must contain the lhs simple selector+ if (!compound->contains(simple1)) { + return false;+ }+ }+ }+ return true;+ }+ }+ return false;+ }+ // EO simpleIsSuperselector++ // ##########################################################################+ // Returns whether [simple] is a superselector of [compound].+ // That is, whether [simple] matches every element that+ // [compound] matches, as well as possibly additional elements.+ // ##########################################################################+ bool simpleIsSuperselectorOfCompound(+ const SimpleSelectorObj& simple,+ const CompoundSelectorObj& compound)+ {+ for (SimpleSelectorObj simple2 : compound->elements()) {+ if (simpleIsSuperselector(simple, simple2)) {+ return true;+ }+ }+ return false;+ }+ // EO simpleIsSuperselectorOfCompound++ // ##########################################################################+ // ##########################################################################+ bool typeIsSuperselectorOfCompound(+ const Type_Selector_Obj& type,+ const CompoundSelectorObj& compound)+ {+ for (const SimpleSelectorObj& simple : compound->elements()) {+ if (const Type_Selector_Obj& rhs = Cast<Type_Selector>(simple)) {+ if (*type != *rhs) return true;+ }+ }+ return false;+ }+ // EO typeIsSuperselectorOfCompound++ // ##########################################################################+ // ##########################################################################+ bool idIsSuperselectorOfCompound(+ const Id_Selector_Obj& id,+ const CompoundSelectorObj& compound)+ {+ for (const SimpleSelectorObj& simple : compound->elements()) {+ if (const Id_Selector_Obj& rhs = Cast<Id_Selector>(simple)) {+ if (*id != *rhs) return true;+ }+ }+ return false;+ }+ // EO idIsSuperselectorOfCompound++ // ##########################################################################+ // ##########################################################################+ bool pseudoIsSuperselectorOfPseudo(+ const Pseudo_Selector_Obj& pseudo1,+ const Pseudo_Selector_Obj& pseudo2,+ const ComplexSelectorObj& parent+ )+ {+ if (!pseudo2->selector()) return false;+ if (pseudo1->name() == pseudo2->name()) {+ SelectorListObj list = pseudo2->selector();+ return listIsSuperslector(list->elements(), { parent });+ }+ return false;+ }+ // EO pseudoIsSuperselectorOfPseudo++ // ##########################################################################+ // ##########################################################################+ bool pseudoNotIsSuperselectorOfCompound(+ const Pseudo_Selector_Obj& pseudo1,+ const CompoundSelectorObj& compound2,+ const ComplexSelectorObj& parent)+ {+ for (const SimpleSelectorObj& simple2 : compound2->elements()) {+ if (const Type_Selector_Obj& type2 = Cast<Type_Selector>(simple2)) {+ if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) {+ if (typeIsSuperselectorOfCompound(type2, compound1)) return true;+ }+ }+ else if (const Id_Selector_Obj& id2 = Cast<Id_Selector>(simple2)) {+ if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) {+ if (idIsSuperselectorOfCompound(id2, compound1)) return true;+ }+ }+ else if (const Pseudo_Selector_Obj& pseudo2 = Cast<Pseudo_Selector>(simple2)) {+ if (pseudoIsSuperselectorOfPseudo(pseudo1, pseudo2, parent)) return true;+ }+ }+ return false;+ }+ // pseudoNotIsSuperselectorOfCompound++ // ##########################################################################+ // Returns whether [pseudo1] is a superselector of [compound2].+ // That is, whether [pseudo1] matches every element that [compound2]+ // matches, as well as possibly additional elements. This assumes that+ // [pseudo1]'s `selector` argument is not `null`. If [parents] is passed,+ // it represents the parents of [compound2]. This is relevant for pseudo+ // selectors with selector arguments, where we may need to know if the+ // parent selectors in the selector argument match [parents].+ // ##########################################################################+ bool selectorPseudoIsSuperselector(+ const Pseudo_Selector_Obj& pseudo1,+ const CompoundSelectorObj& compound2,+ // ToDo: is this really the most convenient way to do this?+ std::vector<SelectorComponentObj>::const_iterator parents_from,+ std::vector<SelectorComponentObj>::const_iterator parents_to)+ {++ // ToDo: move normalization function+ std::string name(Util::unvendor(pseudo1->name()));++ if (name == "matches" || name == "any") {+ std::vector<Pseudo_Selector_Obj> pseudos =+ selectorPseudoNamed(compound2, pseudo1->name());+ SelectorListObj selector1 = pseudo1->selector();+ for (Pseudo_Selector_Obj pseudo2 : pseudos) {+ SelectorListObj selector = pseudo2->selector();+ if (selector1->isSuperselectorOf(selector)) {+ return true;+ }+ }++ for (ComplexSelectorObj complex1 : selector1->elements()) {+ std::vector<SelectorComponentObj> parents;+ for (auto cur = parents_from; cur != parents_to; cur++) {+ parents.push_back(*cur);+ }+ parents.push_back(compound2);+ if (complexIsSuperselector(complex1->elements(), parents)) {+ return true;+ }+ }++ }+ else if (name == "has" || name == "host" || name == "host-context" || name == "slotted") {+ std::vector<Pseudo_Selector_Obj> pseudos =+ selectorPseudoNamed(compound2, pseudo1->name());+ SelectorListObj selector1 = pseudo1->selector();+ for (Pseudo_Selector_Obj pseudo2 : pseudos) {+ SelectorListObj selector = pseudo2->selector();+ if (selector1->isSuperselectorOf(selector)) {+ return true;+ }+ }++ }+ else if (name == "not") {+ for (ComplexSelectorObj complex : pseudo1->selector()->elements()) {+ if (!pseudoNotIsSuperselectorOfCompound(pseudo1, compound2, complex)) return false;+ }+ return true;+ }+ else if (name == "current") {+ std::vector<Pseudo_Selector_Obj> pseudos =+ selectorPseudoNamed(compound2, "current");+ for (Pseudo_Selector_Obj pseudo2 : pseudos) {+ if (ObjEqualityFn(pseudo1, pseudo2)) return true;+ }++ }+ else if (name == "nth-child" || name == "nth-last-child") {+ for (auto simple2 : compound2->elements()) {+ if (Pseudo_Selector_Obj pseudo2 = simple2->getPseudoSelector()) {+ if (pseudo1->name() != pseudo2->name()) continue;+ if (!ObjEqualityFn(pseudo1->argument(), pseudo2->argument())) continue;+ if (pseudo1->selector()->isSuperselectorOf(pseudo2->selector())) return true;+ }+ }+ return false;+ }++ return false;++ }+ // EO selectorPseudoIsSuperselector++ // ##########################################################################+ // Returns whether [compound1] is a superselector of [compound2].+ // That is, whether [compound1] matches every element that [compound2]+ // matches, as well as possibly additional elements. If [parents] is+ // passed, it represents the parents of [compound2]. This is relevant+ // for pseudo selectors with selector arguments, where we may need to+ // know if the parent selectors in the selector argument match [parents].+ // ##########################################################################+ bool compoundIsSuperselector(+ const CompoundSelectorObj& compound1,+ const CompoundSelectorObj& compound2,+ // ToDo: is this really the most convenient way to do this?+ const std::vector<SelectorComponentObj>::const_iterator parents_from,+ const std::vector<SelectorComponentObj>::const_iterator parents_to)+ {+ // Every selector in [compound1.components] must have+ // a matching selector in [compound2.components].+ for (SimpleSelectorObj simple1 : compound1->elements()) {+ Pseudo_Selector_Obj pseudo1 = Cast<Pseudo_Selector>(simple1);+ if (pseudo1 && pseudo1->selector()) {+ if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) {+ return false;+ }+ }+ else if (!simpleIsSuperselectorOfCompound(simple1, compound2)) {+ return false;+ }+ }+ // [compound1] can't be a superselector of a selector+ // with pseudo-elements that [compound2] doesn't share.+ for (SimpleSelectorObj simple2 : compound2->elements()) {+ Pseudo_Selector_Obj pseudo2 = Cast<Pseudo_Selector>(simple2);+ if (pseudo2 && pseudo2->isElement()) {+ if (!simpleIsSuperselectorOfCompound(pseudo2, compound1)) {+ return false;+ }+ }+ }+ return true;+ }+ // EO compoundIsSuperselector++ // ##########################################################################+ // Returns whether [compound1] is a superselector of [compound2].+ // That is, whether [compound1] matches every element that [compound2]+ // matches, as well as possibly additional elements. If [parents] is+ // passed, it represents the parents of [compound2]. This is relevant+ // for pseudo selectors with selector arguments, where we may need to+ // know if the parent selectors in the selector argument match [parents].+ // ##########################################################################+ bool compoundIsSuperselector(+ const CompoundSelectorObj& compound1,+ const CompoundSelectorObj& compound2,+ const std::vector<SelectorComponentObj>& parents)+ {+ return compoundIsSuperselector(+ compound1, compound2,+ parents.begin(), parents.end()+ );+ }+ // EO compoundIsSuperselector++ // ##########################################################################+ // Returns whether [complex1] is a superselector of [complex2].+ // That is, whether [complex1] matches every element that+ // [complex2] matches, as well as possibly additional elements.+ // ##########################################################################+ bool complexIsSuperselector(+ const std::vector<SelectorComponentObj>& complex1,+ const std::vector<SelectorComponentObj>& complex2)+ {++ // Selectors with trailing operators are neither superselectors nor subselectors.+ if (!complex1.empty() && Cast<SelectorCombinator>(complex1.back())) return false;+ if (!complex2.empty() && Cast<SelectorCombinator>(complex2.back())) return false;++ size_t i1 = 0, i2 = 0;+ while (true) {++ size_t remaining1 = complex1.size() - i1;+ size_t remaining2 = complex2.size() - i2;++ if (remaining1 == 0 || remaining2 == 0) {+ return false;+ }+ // More complex selectors are never+ // superselectors of less complex ones.+ if (remaining1 > remaining2) {+ return false;+ }++ // Selectors with leading operators are+ // neither superselectors nor subselectors.+ if (Cast<SelectorCombinator>(complex1[i1])) {+ return false;+ }+ if (Cast<SelectorCombinator>(complex2[i2])) {+ return false;+ }++ CompoundSelectorObj compound1 = Cast<CompoundSelector>(complex1[i1]);+ CompoundSelectorObj compound2 = Cast<CompoundSelector>(complex2.back());++ if (remaining1 == 1) {+ std::vector<SelectorComponentObj>::const_iterator parents_to = complex2.end();+ std::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin();+ std::advance(parents_from, i2 + 1); // equivalent to dart `.skip(i2 + 1)`+ bool rv = compoundIsSuperselector(compound1, compound2, parents_from, parents_to);+ std::vector<SelectorComponentObj> pp;++ std::vector<SelectorComponentObj>::const_iterator end = parents_to;+ std::vector<SelectorComponentObj>::const_iterator beg = parents_from;+ while (beg != end) {+ pp.push_back(*beg);+ beg++;+ }++ return rv;+ }++ // Find the first index where `complex2.sublist(i2, afterSuperselector)`+ // is a subselector of [compound1]. We stop before the superselector+ // would encompass all of [complex2] because we know [complex1] has + // more than one element, and consuming all of [complex2] wouldn't + // leave anything for the rest of [complex1] to match.+ size_t afterSuperselector = i2 + 1;+ for (; afterSuperselector < complex2.size(); afterSuperselector++) {+ SelectorComponentObj component2 = complex2[afterSuperselector - 1];+ if (CompoundSelectorObj compound2 = Cast<CompoundSelector>(component2)) {+ std::vector<SelectorComponentObj>::const_iterator parents_to = complex2.begin();+ std::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin();+ // complex2.take(afterSuperselector - 1).skip(i2 + 1)+ std::advance(parents_from, i2 + 1); // equivalent to dart `.skip`+ std::advance(parents_to, afterSuperselector); // equivalent to dart `.take`+ if (compoundIsSuperselector(compound1, compound2, parents_from, parents_to)) {+ break;+ }+ }+ }+ if (afterSuperselector == complex2.size()) {+ return false;+ }++ SelectorComponentObj component1 = complex1[i1 + 1],+ component2 = complex2[afterSuperselector];++ SelectorCombinatorObj combinator1 = Cast<SelectorCombinator>(component1);+ SelectorCombinatorObj combinator2 = Cast<SelectorCombinator>(component2);++ if (!combinator1.isNull()) {++ if (combinator2.isNull()) {+ return false;+ }+ // `.a ~ .b` is a superselector of `.a + .b`,+ // but otherwise the combinators must match.+ if (combinator1->isGeneralCombinator()) {+ if (combinator2->isChildCombinator()) {+ return false;+ }+ }+ else if (*combinator1 != *combinator2) {+ return false;+ }++ // `.foo > .baz` is not a superselector of `.foo > .bar > .baz` or+ // `.foo > .bar .baz`, despite the fact that `.baz` is a superselector of+ // `.bar > .baz` and `.bar .baz`. Same goes for `+` and `~`.+ if (remaining1 == 3 && remaining2 > 3) {+ return false;+ }++ i1 += 2; i2 = afterSuperselector + 1;++ }+ else if (!combinator2.isNull()) {+ if (!combinator2->isChildCombinator()) {+ return false;+ }+ i1 += 1; i2 = afterSuperselector + 1;+ }+ else {+ i1 += 1; i2 = afterSuperselector;+ }+ }++ return false;++ }+ // EO complexIsSuperselector++ // ##########################################################################+ // Like [complexIsSuperselector], but compares [complex1]+ // and [complex2] as though they shared an implicit base+ // [SimpleSelector]. For example, `B` is not normally a+ // superselector of `B A`, since it doesn't match elements+ // that match `A`. However, it *is* a parent superselector,+ // since `B X` is a superselector of `B A X`.+ // ##########################################################################+ bool complexIsParentSuperselector(+ const std::vector<SelectorComponentObj>& complex1,+ const std::vector<SelectorComponentObj>& complex2)+ {+ // Try some simple heuristics to see if we can avoid allocations.+ if (complex1.empty() && complex2.empty()) return false;+ if (Cast<SelectorCombinator>(complex1.front())) return false;+ if (Cast<SelectorCombinator>(complex2.front())) return false;+ if (complex1.size() > complex2.size()) return false;+ // TODO(nweiz): There's got to be a way to do this without a bunch of extra allocations...+ std::vector<SelectorComponentObj> cplx1(complex1);+ std::vector<SelectorComponentObj> cplx2(complex2);+ CompoundSelectorObj base = SASS_MEMORY_NEW(CompoundSelector, "[tmp]");+ cplx1.push_back(base); cplx2.push_back(base);+ return complexIsSuperselector(cplx1, cplx2);+ }+ // EO complexIsParentSuperselector++ // ##########################################################################+ // Returns whether [list] has a superselector for [complex].+ // That is, whether an item in [list] matches every element that+ // [complex] matches, as well as possibly additional elements.+ // ##########################################################################+ bool listHasSuperslectorForComplex(+ std::vector<ComplexSelectorObj> list,+ ComplexSelectorObj complex)+ {+ // Return true if every [complex] selector on [list2]+ // is a super selector of the full selector [list1].+ for (ComplexSelectorObj lhs : list) {+ if (complexIsSuperselector(lhs->elements(), complex->elements())) {+ return true;+ }+ }+ return false;+ }+ // listIsSuperslectorOfComplex++ // ##########################################################################+ // Returns whether [list1] is a superselector of [list2].+ // That is, whether [list1] matches every element that+ // [list2] matches, as well as possibly additional elements.+ // ##########################################################################+ bool listIsSuperslector(+ const std::vector<ComplexSelectorObj>& list1,+ const std::vector<ComplexSelectorObj>& list2)+ {+ // Return true if every [complex] selector on [list2]+ // is a super selector of the full selector [list1].+ for (ComplexSelectorObj complex : list2) {+ if (!listHasSuperslectorForComplex(list1, complex)) {+ return false;+ }+ }+ return true;+ }+ // EO listIsSuperslector++ // ##########################################################################+ // Implement selector methods (dispatch to functions)+ // ##########################################################################+ bool SelectorList::isSuperselectorOf(const SelectorList* sub) const+ {+ return listIsSuperslector(elements(), sub->elements());+ }+ bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const+ {+ return complexIsSuperselector(elements(), sub->elements());+ }++ // ##########################################################################+ // ##########################################################################++}
+ libsass/src/ast_sel_unify.cpp view
@@ -0,0 +1,275 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"++namespace Sass {++ // ##########################################################################+ // Returns the contents of a [SelectorList] that matches only + // elements that are matched by both [complex1] and [complex2].+ // If no such list can be produced, returns `null`.+ // ##########################################################################+ // ToDo: fine-tune API to avoid unnecessary wrapper allocations+ // ##########################################################################+ std::vector<std::vector<SelectorComponentObj>> unifyComplex(+ const std::vector<std::vector<SelectorComponentObj>>& complexes)+ {++ SASS_ASSERT(!complexes.empty(), "Can't unify empty list");+ if (complexes.size() == 1) return complexes;++ CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, ParserState("[phony]"));+ for (auto complex : complexes) {+ SelectorComponentObj base = complex.back();+ if (CompoundSelector * comp = base->getCompound()) {+ if (unifiedBase->empty()) {+ unifiedBase->concat(comp);+ }+ else {+ for (SimpleSelectorObj simple : comp->elements()) {+ unifiedBase = simple->unifyWith(unifiedBase);+ if (unifiedBase.isNull()) return {};+ }+ }+ }+ else {+ return {};+ }+ }++ std::vector<std::vector<SelectorComponentObj>> complexesWithoutBases;+ for (size_t i = 0; i < complexes.size(); i += 1) {+ std::vector<SelectorComponentObj> sel = complexes[i];+ sel.pop_back(); // remove last item (base) from the list+ complexesWithoutBases.push_back(std::move(sel));+ }++ complexesWithoutBases.back().push_back(unifiedBase);++ return weave(complexesWithoutBases);++ }+ // EO unifyComplex++ // ##########################################################################+ // Returns a [CompoundSelector] that matches only elements+ // that are matched by both [compound1] and [compound2].+ // If no such selector can be produced, returns `null`.+ // ##########################################################################+ CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs)+ {+ if (empty()) return rhs;+ CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs);+ for (const SimpleSelectorObj& sel : elements()) {+ unified = sel->unifyWith(unified);+ if (unified.isNull()) break;+ }+ return unified.detach();+ }+ // EO CompoundSelector::unifyWith(CompoundSelector*)++ // ##########################################################################+ // Returns the compoments of a [CompoundSelector] that matches only elements+ // matched by both this and [compound]. By default, this just returns a copy+ // of [compound] with this selector added to the end, or returns the original+ // array if this selector already exists in it. Returns `null` if unification+ // is impossible—for example, if there are multiple ID selectors.+ // ##########################################################################+ // This is implemented in `selector/simple.dart` as `SimpleSelector::unify`+ // ##########################################################################+ CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs)+ {++ if (rhs->length() == 1) {+ if (rhs->get(0)->is_universal()) {+ CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate());+ this_compound->append(SASS_MEMORY_COPY(this));+ CompoundSelector* unified = rhs->get(0)->unifyWith(this_compound);+ if (unified == nullptr || unified != this_compound) delete this_compound;+ return unified;+ }+ }+ for (const SimpleSelectorObj& sel : rhs->elements()) {+ if (*this == *sel) {+ return rhs;+ }+ }++ CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate());++ bool addedThis = false;+ for (auto simple : rhs->elements()) {+ // Make sure pseudo selectors always come last.+ if (!addedThis && simple->getPseudoSelector()) {+ result->append(this);+ addedThis = true;+ }+ result->append(simple);+ }++ if (!addedThis) {+ result->append(this);+ }+ return result.detach();++ }+ // EO SimpleSelector::unifyWith(CompoundSelector*)++ // ##########################################################################+ // This is implemented in `selector/type.dart` as `PseudoSelector::unify`+ // ##########################################################################+ CompoundSelector* Type_Selector::unifyWith(CompoundSelector* rhs)+ {+ if (rhs->empty()) {+ rhs->append(this);+ return rhs;+ }+ Type_Selector* type = Cast<Type_Selector>(rhs->at(0));+ if (type != nullptr) {+ SimpleSelector* unified = unifyWith(type);+ if (unified == nullptr) {+ return nullptr;+ }+ rhs->elements()[0] = unified;+ }+ else if (!is_universal() || (has_ns_ && ns_ != "*")) {+ rhs->insert(rhs->begin(), this);+ }+ return rhs;+ }++ // ##########################################################################+ // This is implemented in `selector/id.dart` as `PseudoSelector::unify`+ // ##########################################################################+ CompoundSelector* Id_Selector::unifyWith(CompoundSelector* rhs)+ {+ for (const SimpleSelector* sel : rhs->elements()) {+ if (const Id_Selector* id_sel = Cast<Id_Selector>(sel)) {+ if (id_sel->name() != name()) return nullptr;+ }+ }+ return SimpleSelector::unifyWith(rhs);+ }++ // ##########################################################################+ // This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify`+ // ##########################################################################+ CompoundSelector* Pseudo_Selector::unifyWith(CompoundSelector* compound)+ {++ if (compound->length() == 1 && compound->first()->is_universal()) {+ // std::cerr << "implement universal pseudo\n";+ }++ for (const SimpleSelectorObj& sel : compound->elements()) {+ if (*this == *sel) {+ return compound;+ }+ }++ CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate());++ bool addedThis = false;+ for (auto simple : compound->elements()) {+ // Make sure pseudo selectors always come last.+ if (Pseudo_Selector_Obj pseudo = simple->getPseudoSelector()) {+ if (pseudo->isElement()) {+ // A given compound selector may only contain one pseudo element. If+ // [compound] has a different one than [this], unification fails.+ if (isElement()) {+ return {};+ }+ // Otherwise, this is a pseudo selector and+ // should come before pseduo elements.+ result->append(this);+ addedThis = true;+ }+ }+ result->append(simple);+ }++ if (!addedThis) {+ result->append(this);+ }++ return result.detach();++ }+ // EO Pseudo_Selector::unifyWith(CompoundSelector*++ // ##########################################################################+ // This is implemented in `extend/functions.dart` as `unifyUniversalAndElement`+ // Returns a [SimpleSelector] that matches only elements that are matched by+ // both [selector1] and [selector2], which must both be either [UniversalSelector]s+ // or [TypeSelector]s. If no such selector can be produced, returns `null`.+ // Note: libsass handles universal selector directly within the type selector+ // ##########################################################################+ SimpleSelector* Type_Selector::unifyWith(const SimpleSelector* rhs)+ {+ bool rhs_ns = false;+ if (!(is_ns_eq(*rhs) || rhs->is_universal_ns())) {+ if (!is_universal_ns()) {+ return nullptr;+ }+ rhs_ns = true;+ }+ bool rhs_name = false;+ if (!(name_ == rhs->name() || rhs->is_universal())) {+ if (!(is_universal())) {+ return nullptr;+ }+ rhs_name = true;+ }+ if (rhs_ns) {+ ns(rhs->ns());+ has_ns(rhs->has_ns());+ }+ if (rhs_name) name(rhs->name());+ return this;+ }+ // EO Type_Selector::unifyWith(const SimpleSelector*)++ // ##########################################################################+ // Unify two complex selectors. Internally calls `unifyComplex`+ // and then wraps the result in newly create ComplexSelectors.+ // ##########################################################################+ SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs)+ {+ SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate());+ std::vector<std::vector<SelectorComponentObj>> rv =+ unifyComplex({ elements(), rhs->elements() });+ for (std::vector<SelectorComponentObj> items : rv) {+ ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate());+ sel->elements() = std::move(items);+ list->append(sel);+ }+ return list.detach();+ }+ // EO ComplexSelector::unifyWith(ComplexSelector*)++ // ##########################################################################+ // only called from the sass function `selector-unify`+ // ##########################################################################+ SelectorList* SelectorList::unifyWith(SelectorList* rhs)+ {+ SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate());+ // Unify all of children with RHS's children,+ // storing the results in `unified_complex_selectors`+ for (ComplexSelectorObj& seq1 : elements()) {+ for (ComplexSelectorObj& seq2 : rhs->elements()) {+ if (SelectorListObj unified = seq1->unifyWith(seq2)) {+ std::move(unified->begin(), unified->end(),+ std::inserter(slist->elements(), slist->end()));+ }+ }+ }+ return slist;+ }+ // EO SelectorList::unifyWith(SelectorList*)++ // ##########################################################################+ // ##########################################################################++}
+ libsass/src/ast_sel_weave.cpp view
@@ -0,0 +1,616 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"+#include "permutate.hpp"+#include "dart_helpers.hpp"++namespace Sass {++ // ##########################################################################+ // Returns whether or not [compound] contains a `::root` selector.+ // ##########################################################################+ bool hasRoot(const CompoundSelector* compound)+ {+ // Libsass does not yet know the root selector+ return false;+ }+ // EO hasRoot++ // ##########################################################################+ // Returns whether a [CompoundSelector] may contain only+ // one simple selector of the same type as [simple].+ // ##########################################################################+ bool isUnique(const SimpleSelector* simple)+ {+ if (Cast<Id_Selector>(simple)) return true;+ if (const Pseudo_Selector * pseudo = Cast<Pseudo_Selector>(simple)) {+ if (pseudo->is_pseudo_element()) return true;+ }+ return false;+ }+ // EO isUnique++ // ##########################################################################+ // Returns whether [complex1] and [complex2] need to be unified to+ // produce a valid combined selector. This is necessary when both+ // selectors contain the same unique simple selector, such as an ID.+ // ##########################################################################+ bool mustUnify(+ const std::vector<SelectorComponentObj>& complex1,+ const std::vector<SelectorComponentObj>& complex2)+ {++ std::vector<const SimpleSelector*> uniqueSelectors1;+ for (const SelectorComponent* component : complex1) {+ if (const CompoundSelector * compound = component->getCompound()) {+ for (const SimpleSelector* sel : compound->elements()) {+ if (isUnique(sel)) {+ uniqueSelectors1.push_back(sel);+ }+ }+ }+ }+ if (uniqueSelectors1.empty()) return false;++ // ToDo: unsure if this is correct+ for (const SelectorComponent* component : complex2) {+ if (const CompoundSelector * compound = component->getCompound()) {+ for (const SimpleSelector* sel : compound->elements()) {+ if (isUnique(sel)) {+ for (auto check : uniqueSelectors1) {+ if (*check == *sel) return true;+ }+ }+ }+ }+ }++ return false;++ }+ // EO isUnique++ // ##########################################################################+ // Helper function used by `weaveParents`+ // ##########################################################################+ bool cmpGroups(+ const std::vector<SelectorComponentObj>& group1,+ const std::vector<SelectorComponentObj>& group2,+ std::vector<SelectorComponentObj>& select)+ {++ if (group1.size() == group2.size() && std::equal(group1.begin(), group1.end(), group2.begin(), PtrObjEqualityFn<SelectorComponent>)) {+ select = group1;+ return true;+ }++ if (!Cast<CompoundSelector>(group1.front())) {+ select = {};+ return false;+ }+ if (!Cast<CompoundSelector>(group2.front())) {+ select = {};+ return false;+ }++ if (complexIsParentSuperselector(group1, group2)) {+ select = group2;+ return true;+ }+ if (complexIsParentSuperselector(group2, group1)) {+ select = group1;+ return true;+ }++ if (!mustUnify(group1, group2)) {+ select = {};+ return false;+ }++ std::vector<std::vector<SelectorComponentObj>> unified+ = unifyComplex({ group1, group2 });+ if (unified.empty()) return false;+ if (unified.size() > 1) return false;+ select = unified.front();+ return true;+ }+ // EO cmpGroups++ // ##########################################################################+ // Helper function used by `weaveParents`+ // ##########################################################################+ template <class T>+ bool checkForEmptyChild(const T& item) {+ return item.empty();+ }+ // EO checkForEmptyChild++ // ##########################################################################+ // Helper function used by `weaveParents`+ // ##########################################################################+ bool cmpChunkForEmptySequence(+ const std::vector<std::vector<SelectorComponentObj>>& seq,+ const std::vector<SelectorComponentObj>& group)+ {+ return seq.empty();+ }+ // EO cmpChunkForEmptySequence++ // ##########################################################################+ // Helper function used by `weaveParents`+ // ##########################################################################+ bool cmpChunkForParentSuperselector(+ const std::vector< std::vector<SelectorComponentObj>>& seq,+ const std::vector<SelectorComponentObj>& group)+ {+ return seq.empty() || complexIsParentSuperselector(seq.front(), group);+ }+ // EO cmpChunkForParentSuperselector++ // ##########################################################################+ // Returns all orderings of initial subseqeuences of [queue1] and [queue2].+ // The [done] callback is used to determine the extent of the initial+ // subsequences. It's called with each queue until it returns `true`.+ // Destructively removes the initial subsequences of [queue1] and [queue2].+ // For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|` denoting+ // the boundary of the initial subsequence), this would return `[(A B C 1 2),+ // (1 2 A B C)]`. The queues would then contain `(D E)` and `(3 4 5)`.+ // ##########################################################################+ template <class T>+ std::vector<std::vector<T>> getChunks(+ std::vector<T>& queue1, std::vector<T>& queue2,+ const T& group, bool(*done)(const std::vector<T>&, const T&)+ ) {++ std::vector<T> chunk1;+ while (!done(queue1, group)) {+ chunk1.push_back(queue1.front());+ queue1.erase(queue1.begin());+ }++ std::vector<T> chunk2;+ while (!done(queue2, group)) {+ chunk2.push_back(queue2.front());+ queue2.erase(queue2.begin());+ }++ if (chunk1.empty() && chunk2.empty()) return {};+ else if (chunk1.empty()) return { chunk2 };+ else if (chunk2.empty()) return { chunk1 };++ std::vector<T> choice1(chunk1), choice2(chunk2);+ std::move(std::begin(chunk2), std::end(chunk2),+ std::inserter(choice1, std::end(choice1)));+ std::move(std::begin(chunk1), std::end(chunk1),+ std::inserter(choice2, std::end(choice2)));+ return { choice1, choice2 };+ }+ // EO getChunks++ // ##########################################################################+ // If the first element of [queue] has a `::root` + // selector, removes and returns that element.+ // ##########################################################################+ CompoundSelectorObj getFirstIfRoot(std::vector<SelectorComponentObj>& queue) {+ if (queue.empty()) return {};+ SelectorComponent* first = queue.front();+ if (CompoundSelector* sel = Cast<CompoundSelector>(first)) {+ if (!hasRoot(sel)) return {};+ queue.erase(queue.begin());+ return sel;+ }+ return {};+ }+ // EO getFirstIfRoot++ // ##########################################################################+ // Returns [complex], grouped into sub-lists such that no sub-list+ // contains two adjacent [ComplexSelector]s. For example,+ // `(A B > C D + E ~ > G)` is grouped into `[(A) (B > C) (D + E ~ > G)]`.+ // ##########################################################################+ std::vector<std::vector<SelectorComponentObj>> groupSelectors(+ const std::vector<SelectorComponentObj>& components)+ {+ bool lastWasCompound = false;+ std::vector<SelectorComponentObj> group;+ std::vector<std::vector<SelectorComponentObj>> groups;+ for (size_t i = 0; i < components.size(); i += 1) {+ if (CompoundSelector* compound = components[i]->getCompound()) {+ if (lastWasCompound) {+ groups.push_back(group);+ group.clear();+ }+ group.push_back(compound);+ lastWasCompound = true;+ }+ else if (SelectorCombinator* combinator = components[i]->getCombinator()) {+ group.push_back(combinator);+ lastWasCompound = false;+ }+ }+ if (!group.empty()) {+ groups.push_back(group);+ }+ return groups;+ }+ // EO groupSelectors++ // ##########################################################################+ // Extracts leading [Combinator]s from [components1] and [components2]+ // and merges them together into a single list of combinators.+ // If there are no combinators to be merged, returns an empty list.+ // If the combinators can't be merged, returns `null`.+ // ##########################################################################+ bool mergeInitialCombinators(+ std::vector<SelectorComponentObj>& components1,+ std::vector<SelectorComponentObj>& components2,+ std::vector<SelectorComponentObj>& result)+ {++ std::vector<SelectorComponentObj> combinators1;+ while (!components1.empty() && Cast<SelectorCombinator>(components1.front())) {+ SelectorCombinatorObj front = Cast<SelectorCombinator>(components1.front());+ components1.erase(components1.begin());+ combinators1.push_back(front);+ }++ std::vector<SelectorComponentObj> combinators2;+ while (!components2.empty() && Cast<SelectorCombinator>(components2.front())) {+ SelectorCombinatorObj front = Cast<SelectorCombinator>(components2.front());+ components2.erase(components2.begin());+ combinators2.push_back(front);+ }++ // If neither sequence of combinators is a subsequence+ // of the other, they cannot be merged successfully.+ std::vector<SelectorComponentObj> LCS = lcs<SelectorComponentObj>(combinators1, combinators2);++ if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) {+ result = combinators2;+ return true;+ }+ if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) {+ result = combinators1;+ return true;+ }++ return false;++ }+ // EO mergeInitialCombinators++ // ##########################################################################+ // Extracts trailing [Combinator]s, and the selectors to which they apply,+ // from [components1] and [components2] and merges them together into a+ // single list. If there are no combinators to be merged, returns an+ // empty list. If the sequences can't be merged, returns `null`.+ // ##########################################################################+ bool mergeFinalCombinators(+ std::vector<SelectorComponentObj>& components1,+ std::vector<SelectorComponentObj>& components2,+ std::vector<std::vector<std::vector<SelectorComponentObj>>>& result)+ {++ if (components1.empty() || !Cast<SelectorCombinator>(components1.back())) {+ if (components2.empty() || !Cast<SelectorCombinator>(components2.back())) {+ return true;+ }+ }+ + std::vector<SelectorComponentObj> combinators1;+ while (!components1.empty() && Cast<SelectorCombinator>(components1.back())) {+ SelectorCombinatorObj back = Cast<SelectorCombinator>(components1.back());+ components1.erase(components1.end() - 1);+ combinators1.push_back(back);+ }++ std::vector<SelectorComponentObj> combinators2;+ while (!components2.empty() && Cast<SelectorCombinator>(components2.back())) {+ SelectorCombinatorObj back = Cast<SelectorCombinator>(components2.back());+ components2.erase(components2.end() - 1);+ combinators2.push_back(back);+ }++ // reverse now as we used push_back (faster than new alloc)+ std::reverse(combinators1.begin(), combinators1.end());+ std::reverse(combinators2.begin(), combinators2.end());++ if (combinators1.size() > 1 || combinators2.size() > 1) {+ // If there are multiple combinators, something hacky's going on. If one+ // is a supersequence of the other, use that, otherwise give up.+ auto LCS = lcs<SelectorComponentObj>(combinators1, combinators2);+ if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) {+ result.push_back({ combinators2 });+ }+ else if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) {+ result.push_back({ combinators1 });+ }+ else {+ return false;+ }+ return true;+ }++ // This code looks complicated, but it's actually just a bunch of special+ // cases for interactions between different combinators.+ SelectorCombinatorObj combinator1, combinator2;+ if (!combinators1.empty()) combinator1 = combinators1.back();+ if (!combinators2.empty()) combinator2 = combinators2.back();++ if (!combinator1.isNull() && !combinator2.isNull()) {++ CompoundSelector* compound1 = Cast<CompoundSelector>(components1.back());+ CompoundSelector* compound2 = Cast<CompoundSelector>(components2.back());++ components1.pop_back();+ components2.pop_back();++ if (combinator1->isGeneralCombinator() && combinator2->isGeneralCombinator()) {++ if (compound1->isSuperselectorOf(compound2)) {+ result.push_back({ { compound2, combinator2 } });+ }+ else if (compound2->isSuperselectorOf(compound1)) {+ result.push_back({ { compound1, combinator1 } });+ }+ else {+ std::vector<std::vector<SelectorComponentObj>> choices;+ choices.push_back({ compound1, combinator1, compound2, combinator2 });+ choices.push_back({ compound2, combinator2, compound1, combinator1 });+ if (CompoundSelector* unified = compound1->unifyWith(compound2)) {+ choices.push_back({ unified, combinator1 });+ }+ result.push_back(choices);+ }+ }+ else if ((combinator1->isGeneralCombinator() && combinator2->isAdjacentCombinator()) ||+ (combinator1->isAdjacentCombinator() && combinator2->isGeneralCombinator())) {++ CompoundSelector* followingSiblingSelector = combinator1->isGeneralCombinator() ? compound1 : compound2;+ CompoundSelector* nextSiblingSelector = combinator1->isGeneralCombinator() ? compound2 : compound1;+ SelectorCombinator* followingSiblingCombinator = combinator1->isGeneralCombinator() ? combinator1 : combinator2;+ SelectorCombinator* nextSiblingCombinator = combinator1->isGeneralCombinator() ? combinator2 : combinator1;++ if (followingSiblingSelector->isSuperselectorOf(nextSiblingSelector)) {+ result.push_back({ { nextSiblingSelector, nextSiblingCombinator } });+ }+ else {+ CompoundSelectorObj unified = compound1->unifyWith(compound2);+ std::vector<std::vector<SelectorComponentObj>> items;+ + if (!unified.isNull()) {+ items.push_back({+ unified, nextSiblingCombinator+ });+ }++ items.insert(items.begin(), {+ followingSiblingSelector,+ followingSiblingCombinator,+ nextSiblingSelector,+ nextSiblingCombinator,+ });++ result.push_back(items);+ }++ }+ else if (combinator1->isChildCombinator() && (combinator2->isAdjacentCombinator() || combinator2->isGeneralCombinator())) {+ result.push_back({ { compound2, combinator2 } });+ components1.push_back(compound1);+ components1.push_back(combinator1);+ }+ else if (combinator2->isChildCombinator() && (combinator1->isAdjacentCombinator() || combinator1->isGeneralCombinator())) {+ result.push_back({ { compound1, combinator1 } });+ components2.push_back(compound2);+ components2.push_back(combinator2);+ }+ else if (*combinator1 == *combinator2) {+ CompoundSelectorObj unified = compound1->unifyWith(compound2);+ if (unified.isNull()) return false;+ result.push_back({ { unified, combinator1 } });+ }+ else {+ return false;+ }++ return mergeFinalCombinators(components1, components2, result);++ }+ else if (!combinator1.isNull()) {++ if (combinator1->isChildCombinator() && !components2.empty()) {+ const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back());+ const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back());+ if (back1 && back2 && back2->isSuperselectorOf(back1)) {+ components2.pop_back();+ }+ }++ result.push_back({ { components1.back(), combinator1 } });++ components1.pop_back();++ return mergeFinalCombinators(components1, components2, result);++ }++ if (combinator2->isChildCombinator() && !components1.empty()) {+ const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back());+ const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back());+ if (back1 && back2 && back1->isSuperselectorOf(back2)) {+ components1.pop_back();+ }+ }++ result.push_back({ { components2.back(), combinator2 } });++ components2.pop_back();++ return mergeFinalCombinators(components1, components2, result);++ }+ // EO mergeFinalCombinators++ // ##########################################################################+ // Expands "parenthesized selectors" in [complexes]. That is, if+ // we have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually+ // expands into `.D .C, .D (.A .B)`, and this function translates+ // `.D (.A .B)` into `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B`+ // would also be required, but including merged selectors results in+ // exponential output for very little gain. The selector `.D (.A .B)`+ // is represented as the list `[[.D], [.A, .B]]`.+ // ##########################################################################+ std::vector<std::vector<SelectorComponentObj>> weave(+ const std::vector<std::vector<SelectorComponentObj>>& complexes) {++ std::vector<std::vector<SelectorComponentObj>> prefixes;++ prefixes.push_back(complexes.at(0));++ for (size_t i = 1; i < complexes.size(); i += 1) {++ if (complexes[i].empty()) {+ continue;+ }+ const std::vector<SelectorComponentObj>& complex = complexes[i];+ SelectorComponent* target = complex.back();+ if (complex.size() == 1) {+ for (auto& prefix : prefixes) {+ prefix.push_back(target);+ }+ continue;+ }++ std::vector<SelectorComponentObj> parents(complex);++ parents.pop_back();++ std::vector<std::vector<SelectorComponentObj>> newPrefixes;+ for (std::vector<SelectorComponentObj> prefix : prefixes) {+ std::vector<std::vector<SelectorComponentObj>>+ parentPrefixes = weaveParents(prefix, parents);+ if (parentPrefixes.empty()) continue;+ for (auto& parentPrefix : parentPrefixes) {+ parentPrefix.push_back(target);+ newPrefixes.push_back(parentPrefix);+ }+ }+ prefixes = newPrefixes;++ }+ return prefixes;++ }+ // EO weave++ // ##########################################################################+ // Interweaves [parents1] and [parents2] as parents of the same target+ // selector. Returns all possible orderings of the selectors in the+ // inputs (including using unification) that maintain the relative+ // ordering of the input. For example, given `.foo .bar` and `.baz .bang`,+ // this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`,+ // `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,+ // and so on until `.baz .bang .foo .bar`. Semantically, for selectors A+ // and B, this returns all selectors `AB_i` such that the union over all i+ // of elements matched by `AB_i X` is identical to the intersection of all+ // elements matched by `A X` and all elements matched by `B X`. Some `AB_i`+ // are elided to reduce the size of the output.+ // ##########################################################################+ std::vector<std::vector<SelectorComponentObj>> weaveParents(+ std::vector<SelectorComponentObj> queue1,+ std::vector<SelectorComponentObj> queue2)+ {++ std::vector<SelectorComponentObj> leads;+ std::vector<std::vector<std::vector<SelectorComponentObj>>> trails;+ if (!mergeInitialCombinators(queue1, queue2, leads)) return {};+ if (!mergeFinalCombinators(queue1, queue2, trails)) return {};+ // list comes out in reverse order for performance+ std::reverse(trails.begin(), trails.end());++ // Make sure there's at most one `:root` in the output.+ // Note: does not yet do anything in libsass (no root selector)+ CompoundSelectorObj root1 = getFirstIfRoot(queue1);+ CompoundSelectorObj root2 = getFirstIfRoot(queue2);++ if (!root1.isNull() && !root2.isNull()) {+ CompoundSelectorObj root = root1->unifyWith(root2);+ if (root.isNull()) return {}; // null+ queue1.insert(queue1.begin(), root);+ queue2.insert(queue2.begin(), root);+ }+ else if (!root1.isNull()) {+ queue2.insert(queue2.begin(), root1);+ }+ else if (!root2.isNull()) {+ queue1.insert(queue1.begin(), root2);+ }++ // group into sub-lists so no sub-list contains two adjacent ComplexSelectors.+ std::vector<std::vector<SelectorComponentObj>> groups1 = groupSelectors(queue1);+ std::vector<std::vector<SelectorComponentObj>> groups2 = groupSelectors(queue2);++ // The main array to store our choices that will be permutated+ std::vector<std::vector<std::vector<SelectorComponentObj>>> choices;++ // append initial combinators+ choices.push_back({ leads });++ std::vector<std::vector<SelectorComponentObj>> LCS =+ lcs<std::vector<SelectorComponentObj>>(groups1, groups2, cmpGroups);++ for (auto group : LCS) {++ // Create junks from groups1 and groups2+ std::vector<std::vector<std::vector<SelectorComponentObj>>>+ chunks = getChunks<std::vector<SelectorComponentObj>>(+ groups1, groups2, group, cmpChunkForParentSuperselector);++ // Create expanded array by flattening chunks2 inner+ std::vector<std::vector<SelectorComponentObj>>+ expanded = flattenInner(chunks);++ // Prepare data structures+ choices.push_back(expanded);+ choices.push_back({ group });+ if (!groups1.empty()) {+ groups1.erase(groups1.begin());+ }+ if (!groups2.empty()) {+ groups2.erase(groups2.begin());+ }++ }++ // Create junks from groups1 and groups2+ std::vector<std::vector<std::vector<SelectorComponentObj>>>+ chunks = getChunks<std::vector<SelectorComponentObj>>(+ groups1, groups2, {}, cmpChunkForEmptySequence);++ // Append chunks with inner arrays flattened+ choices.emplace_back(flattenInner(chunks));++ // append all trailing selectors to choices+ std::move(std::begin(trails), std::end(trails),+ std::inserter(choices, std::end(choices)));++ // move all non empty items to the front, then erase the trailing ones+ choices.erase(std::remove_if(choices.begin(), choices.end(), checkForEmptyChild+ <std::vector<std::vector<SelectorComponentObj>>>), choices.end());++ // permutate all possible paths through selectors+ std::vector<std::vector<SelectorComponentObj>>+ results = flattenInner(permutate(choices));++ return results;++ }+ // EO weaveParents++ // ##########################################################################+ // ##########################################################################++}
+ libsass/src/ast_selectors.cpp view
@@ -0,0 +1,1033 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast.hpp"+#include "permutate.hpp"+#include "util_string.hpp"++namespace Sass {++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Selector::Selector(ParserState pstate)+ : Expression(pstate),+ hash_(0)+ { concrete_type(SELECTOR); }++ Selector::Selector(const Selector* ptr)+ : Expression(ptr),+ hash_(ptr->hash_)+ { concrete_type(SELECTOR); }+++ bool Selector::has_real_parent_ref() const+ {+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Selector_Schema::Selector_Schema(ParserState pstate, String_Obj c)+ : AST_Node(pstate),+ contents_(c),+ connect_parent_(true),+ hash_(0)+ { }+ Selector_Schema::Selector_Schema(const Selector_Schema* ptr)+ : AST_Node(ptr),+ contents_(ptr->contents_),+ connect_parent_(ptr->connect_parent_),+ hash_(ptr->hash_)+ { }++ unsigned long Selector_Schema::specificity() const+ {+ return 0;+ }++ size_t Selector_Schema::hash() const {+ if (hash_ == 0) {+ hash_combine(hash_, contents_->hash());+ }+ return hash_;+ }++ bool Selector_Schema::has_real_parent_ref() const+ {+ // Note: disabled since it does not seem to do anything?+ // if (String_Schema_Obj schema = Cast<String_Schema>(contents())) {+ // if (schema->empty()) return false;+ // const auto first = schema->first();+ // return Cast<Parent_Reference>(first);+ // }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ SimpleSelector::SimpleSelector(ParserState pstate, std::string n)+ : Selector(pstate), ns_(""), name_(n), has_ns_(false)+ {+ size_t pos = n.find('|');+ // found some namespace+ if (pos != std::string::npos) {+ has_ns_ = true;+ ns_ = n.substr(0, pos);+ name_ = n.substr(pos + 1);+ }+ }+ SimpleSelector::SimpleSelector(const SimpleSelector* ptr)+ : Selector(ptr),+ ns_(ptr->ns_),+ name_(ptr->name_),+ has_ns_(ptr->has_ns_)+ { }++ std::string SimpleSelector::ns_name() const+ {+ if (!has_ns_) return name_;+ else return ns_ + "|" + name_;+ }++ size_t SimpleSelector::hash() const+ {+ if (hash_ == 0) {+ hash_combine(hash_, name());+ hash_combine(hash_, (int)SELECTOR);+ hash_combine(hash_, (int)simple_type());+ if (has_ns_) hash_combine(hash_, ns());+ }+ return hash_;+ }++ bool SimpleSelector::empty() const {+ return ns().empty() && name().empty();+ }++ // namespace compare functions+ bool SimpleSelector::is_ns_eq(const SimpleSelector& r) const+ {+ return has_ns_ == r.has_ns_ && ns_ == r.ns_;+ }++ // namespace query functions+ bool SimpleSelector::is_universal_ns() const+ {+ return has_ns_ && ns_ == "*";+ }++ bool SimpleSelector::is_empty_ns() const+ {+ return !has_ns_ || ns_ == "";+ }++ bool SimpleSelector::has_empty_ns() const+ {+ return has_ns_ && ns_ == "";+ }++ bool SimpleSelector::has_qualified_ns() const+ {+ return has_ns_ && ns_ != "" && ns_ != "*";+ }++ // name query functions+ bool SimpleSelector::is_universal() const+ {+ return name_ == "*";+ }++ bool SimpleSelector::has_placeholder()+ {+ return false;+ }++ bool SimpleSelector::has_real_parent_ref() const+ {+ return false;+ };++ bool SimpleSelector::is_pseudo_element() const+ {+ return false;+ }++ CompoundSelectorObj SimpleSelector::wrapInCompound()+ {+ CompoundSelectorObj selector =+ SASS_MEMORY_NEW(CompoundSelector, pstate());+ selector->append(this);+ return selector;+ }+ ComplexSelectorObj SimpleSelector::wrapInComplex()+ {+ ComplexSelectorObj selector =+ SASS_MEMORY_NEW(ComplexSelector, pstate());+ selector->append(wrapInCompound());+ return selector;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Placeholder_Selector::Placeholder_Selector(ParserState pstate, std::string n)+ : SimpleSelector(pstate, n)+ { simple_type(PLACEHOLDER_SEL); }+ Placeholder_Selector::Placeholder_Selector(const Placeholder_Selector* ptr)+ : SimpleSelector(ptr)+ { simple_type(PLACEHOLDER_SEL); }+ unsigned long Placeholder_Selector::specificity() const+ {+ return Constants::Specificity_Base;+ }+ bool Placeholder_Selector::has_placeholder() {+ return true;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Type_Selector::Type_Selector(ParserState pstate, std::string n)+ : SimpleSelector(pstate, n)+ { simple_type(TYPE_SEL); }+ Type_Selector::Type_Selector(const Type_Selector* ptr)+ : SimpleSelector(ptr)+ { simple_type(TYPE_SEL); }++ unsigned long Type_Selector::specificity() const+ {+ if (name() == "*") return 0;+ else return Constants::Specificity_Element;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Class_Selector::Class_Selector(ParserState pstate, std::string n)+ : SimpleSelector(pstate, n)+ { simple_type(CLASS_SEL); }+ Class_Selector::Class_Selector(const Class_Selector* ptr)+ : SimpleSelector(ptr)+ { simple_type(CLASS_SEL); }++ unsigned long Class_Selector::specificity() const+ {+ return Constants::Specificity_Class;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Id_Selector::Id_Selector(ParserState pstate, std::string n)+ : SimpleSelector(pstate, n)+ { simple_type(ID_SEL); }+ Id_Selector::Id_Selector(const Id_Selector* ptr)+ : SimpleSelector(ptr)+ { simple_type(ID_SEL); }++ unsigned long Id_Selector::specificity() const+ {+ return Constants::Specificity_ID;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Attribute_Selector::Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o)+ : SimpleSelector(pstate, n), matcher_(m), value_(v), modifier_(o)+ { simple_type(ATTRIBUTE_SEL); }+ Attribute_Selector::Attribute_Selector(const Attribute_Selector* ptr)+ : SimpleSelector(ptr),+ matcher_(ptr->matcher_),+ value_(ptr->value_),+ modifier_(ptr->modifier_)+ { simple_type(ATTRIBUTE_SEL); }++ size_t Attribute_Selector::hash() const+ {+ if (hash_ == 0) {+ hash_combine(hash_, SimpleSelector::hash());+ hash_combine(hash_, std::hash<std::string>()(matcher()));+ if (value_) hash_combine(hash_, value_->hash());+ }+ return hash_;+ }++ unsigned long Attribute_Selector::specificity() const+ {+ return Constants::Specificity_Attr;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Pseudo_Selector::Pseudo_Selector(ParserState pstate, std::string name, bool element)+ : SimpleSelector(pstate, name),+ normalized_(Util::unvendor(name)),+ argument_({}),+ selector_({}),+ isSyntacticClass_(!element),+ isClass_(!element && !isFakePseudoElement(normalized_))+ { simple_type(PSEUDO_SEL); }+ Pseudo_Selector::Pseudo_Selector(const Pseudo_Selector* ptr)+ : SimpleSelector(ptr),+ normalized_(ptr->normalized()),+ argument_(ptr->argument()),+ selector_(ptr->selector()),+ isSyntacticClass_(ptr->isSyntacticClass()),+ isClass_(ptr->isClass())+ { simple_type(PSEUDO_SEL); }++ // A pseudo-element is made of two colons (::) followed by the name.+ // The `::` notation is introduced by the current document in order to+ // establish a discrimination between pseudo-classes and pseudo-elements.+ // For compatibility with existing style sheets, user agents must also+ // accept the previous one-colon notation for pseudo-elements introduced+ // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and+ // :after). This compatibility is not allowed for the new pseudo-elements+ // introduced in this specification.+ bool Pseudo_Selector::is_pseudo_element() const+ {+ return isElement();+ }++ size_t Pseudo_Selector::hash() const+ {+ if (hash_ == 0) {+ hash_combine(hash_, SimpleSelector::hash());+ if (selector_) hash_combine(hash_, selector_->hash());+ if (argument_) hash_combine(hash_, argument_->hash());+ }+ return hash_;+ }++ unsigned long Pseudo_Selector::specificity() const+ {+ if (is_pseudo_element())+ return Constants::Specificity_Element;+ return Constants::Specificity_Pseudo;+ }++ Pseudo_Selector_Obj Pseudo_Selector::withSelector(SelectorListObj selector)+ {+ Pseudo_Selector_Obj pseudo = SASS_MEMORY_COPY(this);+ pseudo->selector(selector);+ return pseudo;+ }++ bool Pseudo_Selector::empty() const+ {+ // Only considered empty if selector is+ // available but has no items in it.+ return selector() && selector()->empty();+ }++ void Pseudo_Selector::cloneChildren()+ {+ if (selector().isNull()) selector({});+ else selector(SASS_MEMORY_CLONE(selector()));+ }++ bool Pseudo_Selector::has_real_parent_ref() const {+ if (!selector()) return false;+ return selector()->has_real_parent_ref();+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ SelectorList::SelectorList(ParserState pstate, size_t s)+ : Selector(pstate),+ Vectorized<ComplexSelectorObj>(s),+ is_optional_(false)+ { }+ SelectorList::SelectorList(const SelectorList* ptr)+ : Selector(ptr),+ Vectorized<ComplexSelectorObj>(*ptr),+ is_optional_(ptr->is_optional_)+ { }++ size_t SelectorList::hash() const+ {+ if (Selector::hash_ == 0) {+ hash_combine(Selector::hash_, Vectorized::hash());+ }+ return Selector::hash_;+ }++ bool SelectorList::has_real_parent_ref() const+ {+ for (ComplexSelectorObj s : elements()) {+ if (s && s->has_real_parent_ref()) return true;+ }+ return false;+ }++ void SelectorList::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ unsigned long SelectorList::specificity() const+ {+ return 0;+ }++ bool SelectorList::isInvisible() const+ {+ if (length() == 0) return true;+ for (size_t i = 0; i < length(); i += 1) {+ if (get(i)->isInvisible()) return true;+ }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ ComplexSelector::ComplexSelector(ParserState pstate)+ : Selector(pstate),+ Vectorized<SelectorComponentObj>(),+ chroots_(false),+ hasPreLineFeed_(false)+ {+ }+ ComplexSelector::ComplexSelector(const ComplexSelector* ptr)+ : Selector(ptr),+ Vectorized<SelectorComponentObj>(ptr->elements()),+ chroots_(ptr->chroots()),+ hasPreLineFeed_(ptr->hasPreLineFeed())+ {+ }++ void ComplexSelector::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ unsigned long ComplexSelector::specificity() const+ {+ int sum = 0;+ for (auto component : elements()) {+ sum += component->specificity();+ }+ return sum;+ }++ bool ComplexSelector::isInvisible() const+ {+ if (length() == 0) return true;+ for (size_t i = 0; i < length(); i += 1) {+ if (CompoundSelectorObj compound = get(i)->getCompound()) {+ if (compound->isInvisible()) return true;+ }+ }+ return false;+ }++ SelectorListObj ComplexSelector::wrapInList()+ {+ SelectorListObj selector =+ SASS_MEMORY_NEW(SelectorList, pstate());+ selector->append(this);+ return selector;+ }++ size_t ComplexSelector::hash() const+ {+ if (Selector::hash_ == 0) {+ hash_combine(Selector::hash_, Vectorized::hash());+ // ToDo: this breaks some extend lookup+ // hash_combine(Selector::hash_, chroots_);+ }+ return Selector::hash_;+ }++ bool ComplexSelector::has_placeholder() const {+ for (size_t i = 0, L = length(); i < L; ++i) {+ if (get(i)->has_placeholder()) return true;+ }+ return false;+ }++ bool ComplexSelector::has_real_parent_ref() const+ {+ for (auto item : elements()) {+ if (item->has_real_parent_ref()) return true;+ }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ SelectorComponent::SelectorComponent(ParserState pstate, bool postLineBreak)+ : Selector(pstate),+ hasPostLineBreak_(postLineBreak)+ {+ }++ SelectorComponent::SelectorComponent(const SelectorComponent* ptr)+ : Selector(ptr),+ hasPostLineBreak_(ptr->hasPostLineBreak())+ { }++ void SelectorComponent::cloneChildren()+ {+ }++ unsigned long SelectorComponent::specificity() const+ {+ return 0;+ }++ // Wrap the compound selector with a complex selector+ ComplexSelector* SelectorComponent::wrapInComplex()+ {+ auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate());+ complex->append(this);+ return complex;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ SelectorCombinator::SelectorCombinator(ParserState pstate, SelectorCombinator::Combinator combinator, bool postLineBreak)+ : SelectorComponent(pstate, postLineBreak),+ combinator_(combinator)+ {+ }+ SelectorCombinator::SelectorCombinator(const SelectorCombinator* ptr)+ : SelectorComponent(ptr->pstate(), false),+ combinator_(ptr->combinator())+ { }++ void SelectorCombinator::cloneChildren()+ {+ }++ unsigned long SelectorCombinator::specificity() const+ {+ return 0;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ CompoundSelector::CompoundSelector(ParserState pstate, bool postLineBreak)+ : SelectorComponent(pstate, postLineBreak),+ Vectorized<SimpleSelectorObj>(),+ hasRealParent_(false),+ extended_(false)+ {+ }+ CompoundSelector::CompoundSelector(const CompoundSelector* ptr)+ : SelectorComponent(ptr),+ Vectorized<SimpleSelectorObj>(*ptr),+ hasRealParent_(ptr->hasRealParent()),+ extended_(ptr->extended())+ { }++ size_t CompoundSelector::hash() const+ {+ if (Selector::hash_ == 0) {+ hash_combine(Selector::hash_, Vectorized::hash());+ hash_combine(Selector::hash_, hasRealParent_);+ }+ return Selector::hash_;+ }++ bool CompoundSelector::has_real_parent_ref() const+ {+ if (hasRealParent()) return true;+ // ToDo: dart sass has another check?+ // if (Cast<Type_Selector>(front)) {+ // if (front->ns() != "") return false;+ // }+ for (const SimpleSelector* s : elements()) {+ if (s && s->has_real_parent_ref()) return true;+ }+ return false;+ }++ bool CompoundSelector::has_placeholder() const+ {+ if (length() == 0) return false;+ for (SimpleSelectorObj ss : elements()) {+ if (ss->has_placeholder()) return true;+ }+ return false;+ }++ void CompoundSelector::cloneChildren()+ {+ for (size_t i = 0, l = length(); i < l; i++) {+ at(i) = SASS_MEMORY_CLONE(at(i));+ }+ }++ unsigned long CompoundSelector::specificity() const+ {+ int sum = 0;+ for (size_t i = 0, L = length(); i < L; ++i)+ { sum += get(i)->specificity(); }+ return sum;+ }++ bool CompoundSelector::isInvisible() const+ {+ for (size_t i = 0; i < length(); i += 1) {+ if (!get(i)->isInvisible()) return false;+ }+ return true;+ }++ bool CompoundSelector::isSuperselectorOf(const CompoundSelector* sub, std::string wrapped) const+ {+ CompoundSelector* rhs2 = const_cast<CompoundSelector*>(sub);+ CompoundSelector* lhs2 = const_cast<CompoundSelector*>(this);+ return compoundIsSuperselector(lhs2, rhs2, {});+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ MediaRule::MediaRule(ParserState pstate, Block_Obj block) :+ Has_Block(pstate, block),+ schema_({})+ {+ statement_type(MEDIA);+ }++ MediaRule::MediaRule(const MediaRule* ptr) :+ Has_Block(ptr),+ schema_(ptr->schema_)+ {+ statement_type(MEDIA);+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ CssMediaRule::CssMediaRule(ParserState pstate, Block_Obj block) :+ Has_Block(pstate, block),+ Vectorized()+ {+ statement_type(MEDIA);+ }++ CssMediaRule::CssMediaRule(const CssMediaRule* ptr) :+ Has_Block(ptr),+ Vectorized(*ptr)+ {+ statement_type(MEDIA);+ }++ CssMediaQuery::CssMediaQuery(ParserState pstate) :+ AST_Node(pstate),+ modifier_(""),+ type_(""),+ features_()+ {+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ bool CssMediaQuery::operator==(const CssMediaQuery& rhs) const+ {+ return type_ == rhs.type_+ && modifier_ == rhs.modifier_+ && features_ == rhs.features_;+ }++ // Implemented after dart-sass (maybe move to other class?)+ CssMediaQuery_Obj CssMediaQuery::merge(CssMediaQuery_Obj& other)+ {++ std::string ourType = this->type();+ Util::ascii_str_tolower(&ourType);++ std::string theirType = other->type();+ Util::ascii_str_tolower(&theirType);++ std::string ourModifier = this->modifier();+ Util::ascii_str_tolower(&ourModifier);++ std::string theirModifier = other->modifier();+ Util::ascii_str_tolower(&theirModifier);++ std::string type;+ std::string modifier;+ std::vector<std::string> features;++ if (ourType.empty() && theirType.empty()) {+ CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate());+ std::vector<std::string> f1(this->features());+ std::vector<std::string> f2(other->features());+ features.insert(features.end(), f1.begin(), f1.end());+ features.insert(features.end(), f2.begin(), f2.end());+ query->features(features);+ return query;+ }++ if ((ourModifier == "not") != (theirModifier == "not")) {+ if (ourType == theirType) {+ std::vector<std::string> negativeFeatures =+ ourModifier == "not" ? this->features() : other->features();+ std::vector<std::string> positiveFeatures =+ ourModifier == "not" ? other->features() : this->features();++ // If the negative features are a subset of the positive features, the+ // query is empty. For example, `not screen and (color)` has no+ // intersection with `screen and (color) and (grid)`.+ // However, `not screen and (color)` *does* intersect with `screen and+ // (grid)`, because it means `not (screen and (color))` and so it allows+ // a screen with no color but with a grid.+ if (listIsSubsetOrEqual(negativeFeatures, positiveFeatures)) {+ return SASS_MEMORY_NEW(CssMediaQuery, pstate());+ }+ else {+ return {};+ }+ }+ else if (this->matchesAllTypes() || other->matchesAllTypes()) {+ return {};+ }++ if (ourModifier == "not") {+ modifier = theirModifier;+ type = theirType;+ features = other->features();+ }+ else {+ modifier = ourModifier;+ type = ourType;+ features = this->features();+ }+ }+ else if (ourModifier == "not") {+ SASS_ASSERT(theirModifier == "not", "modifiers not is sync");++ // CSS has no way of representing "neither screen nor print".+ if (ourType != theirType) return {};++ auto moreFeatures = this->features().size() > other->features().size()+ ? this->features()+ : other->features();+ auto fewerFeatures = this->features().size() > other->features().size()+ ? other->features()+ : this->features();++ // If one set of features is a superset of the other,+ // use those features because they're strictly narrower.+ if (listIsSubsetOrEqual(fewerFeatures, moreFeatures)) {+ modifier = ourModifier; // "not"+ type = ourType;+ features = moreFeatures;+ }+ else {+ // Otherwise, there's no way to+ // represent the intersection.+ return {};+ }++ }+ else {+ if (this->matchesAllTypes()) {+ modifier = theirModifier;+ // Omit the type if either input query did, since that indicates that they+ // aren't targeting a browser that requires "all and".+ type = (other->matchesAllTypes() && ourType.empty()) ? "" : theirType;+ std::vector<std::string> f1(this->features());+ std::vector<std::string> f2(other->features());+ features.insert(features.end(), f1.begin(), f1.end());+ features.insert(features.end(), f2.begin(), f2.end());+ }+ else if (other->matchesAllTypes()) {+ modifier = ourModifier;+ type = ourType;+ std::vector<std::string> f1(this->features());+ std::vector<std::string> f2(other->features());+ features.insert(features.end(), f1.begin(), f1.end());+ features.insert(features.end(), f2.begin(), f2.end());+ }+ else if (ourType != theirType) {+ return SASS_MEMORY_NEW(CssMediaQuery, pstate());+ }+ else {+ modifier = ourModifier.empty() ? theirModifier : ourModifier;+ type = ourType;+ std::vector<std::string> f1(this->features());+ std::vector<std::string> f2(other->features());+ features.insert(features.end(), f1.begin(), f1.end());+ features.insert(features.end(), f2.begin(), f2.end());+ }+ }++ CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate());+ query->modifier(modifier == ourModifier ? this->modifier() : other->modifier());+ query->type(ourType.empty() ? other->type() : this->type());+ query->features(features);+ return query;+ }++ CssMediaQuery::CssMediaQuery(const CssMediaQuery* ptr) :+ AST_Node(*ptr),+ modifier_(ptr->modifier_),+ type_(ptr->type_),+ features_(ptr->features_)+ {+ }++ /////////////////////////////////////////////////////////////////////////+ // ToDo: finalize specificity implementation+ /////////////////////////////////////////////////////////////////////////++ size_t SelectorList::maxSpecificity() const+ {+ size_t specificity = 0;+ for (auto complex : elements()) {+ specificity = std::max(specificity, complex->maxSpecificity());+ }+ return specificity;+ }++ size_t SelectorList::minSpecificity() const+ {+ size_t specificity = 0;+ for (auto complex : elements()) {+ specificity = std::min(specificity, complex->minSpecificity());+ }+ return specificity;+ }++ size_t CompoundSelector::maxSpecificity() const+ {+ size_t specificity = 0;+ for (auto simple : elements()) {+ specificity += simple->maxSpecificity();+ }+ return specificity;+ }++ size_t CompoundSelector::minSpecificity() const+ {+ size_t specificity = 0;+ for (auto simple : elements()) {+ specificity += simple->minSpecificity();+ }+ return specificity;+ }++ size_t ComplexSelector::maxSpecificity() const+ {+ size_t specificity = 0;+ for (auto component : elements()) {+ specificity += component->maxSpecificity();+ }+ return specificity;+ }++ size_t ComplexSelector::minSpecificity() const+ {+ size_t specificity = 0;+ for (auto component : elements()) {+ specificity += component->minSpecificity();+ }+ return specificity;+ }++ /////////////////////////////////////////////////////////////////////////+ // ToDo: this might be done easier with new selector format+ /////////////////////////////////////////////////////////////////////////++ std::vector<ComplexSelectorObj>+ CompoundSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent)+ {++ auto parent = pstack.back();+ std::vector<ComplexSelectorObj> rv;++ for (SimpleSelectorObj simple : elements()) {+ if (Pseudo_Selector * pseudo = Cast<Pseudo_Selector>(simple)) {+ if (SelectorList* sel = Cast<SelectorList>(pseudo->selector())) {+ if (parent) {+ pseudo->selector(sel->resolve_parent_refs(+ pstack, traces, implicit_parent));+ }+ }+ }+ }++ // Mix with parents from stack+ if (hasRealParent()) {++ if (parent.isNull()) {+ return { wrapInComplex() };+ }+ else {+ for (auto complex : parent->elements()) {+ // The parent complex selector has a compound selector+ if (CompoundSelectorObj tail = Cast<CompoundSelector>(complex->last())) {+ // Create a copy to alter it+ complex = SASS_MEMORY_COPY(complex);+ tail = SASS_MEMORY_COPY(tail);++ // Check if we can merge front with back+ if (length() > 0 && tail->length() > 0) {+ SimpleSelectorObj back = tail->last();+ SimpleSelectorObj front = first();+ auto simple_back = Cast<SimpleSelector>(back);+ auto simple_front = Cast<Type_Selector>(front);+ if (simple_front && simple_back) {+ simple_back = SASS_MEMORY_COPY(simple_back);+ auto name = simple_back->name();+ name += simple_front->name();+ simple_back->name(name);+ tail->elements().back() = simple_back;+ tail->elements().insert(tail->end(),+ begin() + 1, end());+ }+ else {+ tail->concat(this);+ }+ }+ else {+ tail->concat(this);+ }++ complex->elements().back() = tail;+ // Append to results+ rv.push_back(complex);+ }+ else {+ // Can't insert parent that ends with a combinator+ // where the parent selector is followed by something+ if (parent && length() > 0) {+ throw Exception::InvalidParent(parent, traces, this);+ }+ // Create a copy to alter it+ complex = SASS_MEMORY_COPY(complex);+ // Just append ourself+ complex->append(this);+ // Append to results+ rv.push_back(complex);+ }+ }+ }+ }++ // No parents+ else {+ // Create a new wrapper to wrap ourself+ auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate());+ // Just append ourself+ complex->append(this);+ // Append to results+ rv.push_back(complex);+ }++ return rv;++ }++ /* better return std::vector? only - is empty container anyway? */+ SelectorList* ComplexSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent)+ {++ std::vector<std::vector<ComplexSelectorObj>> vars;++ auto parent = pstack.back();++ if (has_real_parent_ref() && !parent) {+ throw Exception::TopLevelParent(traces, pstate());+ }++ if (!chroots() && parent) {++ if (!has_real_parent_ref() && !implicit_parent) {+ SelectorList* retval = SASS_MEMORY_NEW(SelectorList, pstate(), 1);+ retval->append(this);+ return retval;+ }++ vars.push_back(parent->elements());+ }++ for (auto sel : elements()) {+ if (CompoundSelectorObj comp = Cast<CompoundSelector>(sel)) {+ auto asd = comp->resolve_parent_refs(pstack, traces, implicit_parent);+ if (asd.size() > 0) vars.push_back(asd);+ }+ else {+ // ToDo: merge together sequences whenever possible+ auto cont = SASS_MEMORY_NEW(ComplexSelector, pstate());+ cont->append(sel);+ vars.push_back({ cont });+ }+ }++ // Need complex selectors to preserve linefeeds+ std::vector<std::vector<ComplexSelectorObj>> res = permutateAlt(vars);++ // std::reverse(std::begin(res), std::end(res));++ auto lst = SASS_MEMORY_NEW(SelectorList, pstate());+ for (auto items : res) {+ if (items.size() > 0) {+ ComplexSelectorObj first = SASS_MEMORY_COPY(items[0]);+ first->hasPreLineFeed(first->hasPreLineFeed() || (!has_real_parent_ref() && hasPreLineFeed()));+ // ToDo: remove once we know how to handle line feeds+ // ToDo: currently a mashup between ruby and dart sass+ // if (has_real_parent_ref()) first->has_line_feed(false);+ // first->has_line_break(first->has_line_break() || has_line_break());+ first->chroots(true); // has been resolved by now+ for (size_t i = 1; i < items.size(); i += 1) {+ first->concat(items[i]);+ }+ lst->append(first);+ }+ }++ return lst;++ }++ SelectorList* SelectorList::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent)+ {+ SelectorList* rv = SASS_MEMORY_NEW(SelectorList, pstate());+ for (auto sel : elements()) {+ // Note: this one is tricky as we get back a pointer from resolve parents ...+ SelectorListObj res = sel->resolve_parent_refs(pstack, traces, implicit_parent);+ // Note: ... and concat will only append the items in elements+ // Therefore by passing it directly, the container will leak!+ rv->concat(res);+ }+ return rv;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ IMPLEMENT_AST_OPERATORS(Selector_Schema);+ IMPLEMENT_AST_OPERATORS(Placeholder_Selector);+ IMPLEMENT_AST_OPERATORS(Attribute_Selector);+ IMPLEMENT_AST_OPERATORS(Type_Selector);+ IMPLEMENT_AST_OPERATORS(Class_Selector);+ IMPLEMENT_AST_OPERATORS(Id_Selector);+ IMPLEMENT_AST_OPERATORS(Pseudo_Selector);+ IMPLEMENT_AST_OPERATORS(SelectorCombinator);+ IMPLEMENT_AST_OPERATORS(CompoundSelector);+ IMPLEMENT_AST_OPERATORS(ComplexSelector);+ IMPLEMENT_AST_OPERATORS(SelectorList);++}
+ libsass/src/ast_selectors.hpp view
@@ -0,0 +1,512 @@+#ifndef SASS_AST_SEL_H+#define SASS_AST_SEL_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++namespace Sass {++ /////////////////////////////////////////////////////////////////////////+ // Some helper functions+ /////////////////////////////////////////////////////////////////////////++ bool compoundIsSuperselector(+ const CompoundSelectorObj& compound1,+ const CompoundSelectorObj& compound2,+ const std::vector<SelectorComponentObj>& parents);++ bool complexIsParentSuperselector(+ const std::vector<SelectorComponentObj>& complex1,+ const std::vector<SelectorComponentObj>& complex2);++ std::vector<std::vector<SelectorComponentObj>> weave(+ const std::vector<std::vector<SelectorComponentObj>>& complexes);++ std::vector<std::vector<SelectorComponentObj>> weaveParents(+ std::vector<SelectorComponentObj> parents1,+ std::vector<SelectorComponentObj> parents2);++ std::vector<SimpleSelectorObj> unifyCompound(+ const std::vector<SimpleSelectorObj>& compound1,+ const std::vector<SimpleSelectorObj>& compound2);++ std::vector<std::vector<SelectorComponentObj>> unifyComplex(+ const std::vector<std::vector<SelectorComponentObj>>& complexes);++ /////////////////////////////////////////+ // Abstract base class for CSS selectors.+ /////////////////////////////////////////+ class Selector : public Expression {+ protected:+ mutable size_t hash_;+ public:+ Selector(ParserState pstate);+ virtual ~Selector() = 0;+ size_t hash() const override = 0;+ virtual bool has_real_parent_ref() const;+ // you should reset this to null on containers+ virtual unsigned long specificity() const = 0;+ // by default we return the regular specificity+ // you must override this for all containers+ virtual size_t maxSpecificity() const { return specificity(); }+ virtual size_t minSpecificity() const { return specificity(); }+ // dispatch to correct handlers+ ATTACH_VIRTUAL_CMP_OPERATIONS(Selector)+ ATTACH_VIRTUAL_AST_OPERATIONS(Selector)+ };+ inline Selector::~Selector() { }++ /////////////////////////////////////////////////////////////////////////+ // Interpolated selectors -- the interpolated String will be expanded and+ // re-parsed into a normal selector class.+ /////////////////////////////////////////////////////////////////////////+ class Selector_Schema final : public AST_Node {+ ADD_PROPERTY(String_Schema_Obj, contents)+ ADD_PROPERTY(bool, connect_parent);+ // store computed hash+ mutable size_t hash_;+ public:+ Selector_Schema(ParserState pstate, String_Obj c);++ bool has_real_parent_ref() const;+ // selector schema is not yet a final selector, so we do not+ // have a specificity for it yet. We need to+ virtual unsigned long specificity() const;+ size_t hash() const override;+ ATTACH_AST_OPERATIONS(Selector_Schema)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////+ // Abstract base class for simple selectors.+ ////////////////////////////////////////////+ class SimpleSelector : public Selector {+ public:+ enum Simple_Type {+ ID_SEL,+ TYPE_SEL,+ CLASS_SEL,+ PSEUDO_SEL,+ ATTRIBUTE_SEL,+ PLACEHOLDER_SEL,+ };+ public:+ HASH_CONSTREF(std::string, ns)+ HASH_CONSTREF(std::string, name)+ ADD_PROPERTY(Simple_Type, simple_type)+ HASH_PROPERTY(bool, has_ns)+ public:+ SimpleSelector(ParserState pstate, std::string n = "");+ virtual std::string ns_name() const;+ size_t hash() const override;+ virtual bool empty() const;+ // namespace compare functions+ bool is_ns_eq(const SimpleSelector& r) const;+ // namespace query functions+ bool is_universal_ns() const;+ bool is_empty_ns() const;+ bool has_empty_ns() const;+ bool has_qualified_ns() const;+ // name query functions+ bool is_universal() const;+ virtual bool has_placeholder();++ virtual ~SimpleSelector() = 0;+ virtual CompoundSelector* unifyWith(CompoundSelector*);++ /* helper function for syntax sugar */+ virtual Id_Selector* getIdSelector() { return NULL; }+ virtual Type_Selector* getTypeSelector() { return NULL; }+ virtual Pseudo_Selector* getPseudoSelector() { return NULL; }++ ComplexSelectorObj wrapInComplex();+ CompoundSelectorObj wrapInCompound();++ virtual bool isInvisible() const { return false; }+ virtual bool is_pseudo_element() const;+ virtual bool has_real_parent_ref() const override;++ bool operator==(const Selector& rhs) const final override;++ virtual bool operator==(const SelectorList& rhs) const;+ virtual bool operator==(const ComplexSelector& rhs) const;+ virtual bool operator==(const CompoundSelector& rhs) const;++ ATTACH_VIRTUAL_CMP_OPERATIONS(SimpleSelector);+ ATTACH_VIRTUAL_AST_OPERATIONS(SimpleSelector);+ ATTACH_CRTP_PERFORM_METHODS();++ };+ inline SimpleSelector::~SimpleSelector() { }++ /////////////////////////////////////////////////////////////////////////+ // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.+ /////////////////////////////////////////////////////////////////////////+ class Placeholder_Selector final : public SimpleSelector {+ public:+ Placeholder_Selector(ParserState pstate, std::string n);+ bool isInvisible() const override { return true; }+ virtual unsigned long specificity() const override;+ virtual bool has_placeholder() override;+ bool operator==(const SimpleSelector& rhs) const override;+ ATTACH_CMP_OPERATIONS(Placeholder_Selector)+ ATTACH_AST_OPERATIONS(Placeholder_Selector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////////////////////////+ // Type selectors (and the universal selector) -- e.g., div, span, *.+ /////////////////////////////////////////////////////////////////////+ class Type_Selector final : public SimpleSelector {+ public:+ Type_Selector(ParserState pstate, std::string n);+ virtual unsigned long specificity() const override;+ SimpleSelector* unifyWith(const SimpleSelector*);+ CompoundSelector* unifyWith(CompoundSelector*) override;+ Type_Selector* getTypeSelector() override { return this; }+ bool operator==(const SimpleSelector& rhs) const final override;+ ATTACH_CMP_OPERATIONS(Type_Selector)+ ATTACH_AST_OPERATIONS(Type_Selector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////+ // Class selectors -- i.e., .foo.+ ////////////////////////////////////////////////+ class Class_Selector final : public SimpleSelector {+ public:+ Class_Selector(ParserState pstate, std::string n);+ virtual unsigned long specificity() const override;+ bool operator==(const SimpleSelector& rhs) const final override;+ ATTACH_CMP_OPERATIONS(Class_Selector)+ ATTACH_AST_OPERATIONS(Class_Selector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////+ // ID selectors -- i.e., #foo.+ ////////////////////////////////////////////////+ class Id_Selector final : public SimpleSelector {+ public:+ Id_Selector(ParserState pstate, std::string n);+ virtual unsigned long specificity() const override;+ CompoundSelector* unifyWith(CompoundSelector*) override;+ Id_Selector* getIdSelector() final override { return this; }+ bool operator==(const SimpleSelector& rhs) const final override;+ ATTACH_CMP_OPERATIONS(Id_Selector)+ ATTACH_AST_OPERATIONS(Id_Selector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////////////+ // Attribute selectors -- e.g., [src*=".jpg"], etc.+ ///////////////////////////////////////////////////+ class Attribute_Selector final : public SimpleSelector {+ ADD_CONSTREF(std::string, matcher)+ // this cannot be changed to obj atm!!!!!!????!!!!!!!+ ADD_PROPERTY(String_Obj, value) // might be interpolated+ ADD_PROPERTY(char, modifier);+ public:+ Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o = 0);+ size_t hash() const override;+ virtual unsigned long specificity() const override;+ bool operator==(const SimpleSelector& rhs) const final override;+ ATTACH_CMP_OPERATIONS(Attribute_Selector)+ ATTACH_AST_OPERATIONS(Attribute_Selector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////////////////////////////////////////+ // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.+ //////////////////////////////////////////////////////////////////+ // Pseudo Selector cannot have any namespace?+ class Pseudo_Selector final : public SimpleSelector {+ ADD_PROPERTY(std::string, normalized)+ ADD_PROPERTY(String_Obj, argument)+ ADD_PROPERTY(SelectorListObj, selector)+ ADD_PROPERTY(bool, isSyntacticClass)+ ADD_PROPERTY(bool, isClass)+ public:+ Pseudo_Selector(ParserState pstate, std::string n, bool element = false);+ virtual bool is_pseudo_element() const override;+ size_t hash() const override;++ bool empty() const override;++ bool has_real_parent_ref() const override;++ // Whether this is a pseudo-element selector.+ // This is `true` if and only if [isClass] is `false`.+ bool isElement() const { return !isClass(); }++ // Whether this is syntactically a pseudo-element selector.+ // This is `true` if and only if [isSyntacticClass] is `false`.+ bool isSyntacticElement() const { return !isSyntacticClass(); }++ virtual unsigned long specificity() const override;+ Pseudo_Selector_Obj withSelector(SelectorListObj selector);++ CompoundSelector* unifyWith(CompoundSelector*) override;+ Pseudo_Selector* getPseudoSelector() final override { return this; }+ bool operator==(const SimpleSelector& rhs) const final override;+ ATTACH_CMP_OPERATIONS(Pseudo_Selector)+ ATTACH_AST_OPERATIONS(Pseudo_Selector)+ void cloneChildren() override;+ ATTACH_CRTP_PERFORM_METHODS()+ };+++ ////////////////////////////////////////////////////////////////////////////+ // Complex Selectors are the most important class of selectors.+ // A Selector List consists of Complex Selectors (separated by comma)+ // Complex Selectors are itself a list of Compounds and Combinators+ // Between each item there is an implicit ancestor of combinator+ ////////////////////////////////////////////////////////////////////////////+ class ComplexSelector final : public Selector, public Vectorized<SelectorComponentObj> {+ ADD_PROPERTY(bool, chroots)+ // line break before list separator+ ADD_PROPERTY(bool, hasPreLineFeed)+ public:+ ComplexSelector(ParserState pstate);++ // Returns true if the first components+ // is a compound selector and fullfills+ // a few other criteria.+ bool isInvisible() const;++ size_t hash() const override;+ void cloneChildren() override;+ bool has_placeholder() const;+ bool has_real_parent_ref() const override;++ SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);+ virtual unsigned long specificity() const override;++ SelectorList* unifyWith(ComplexSelector* rhs);++ bool isSuperselectorOf(const ComplexSelector* sub) const;++ SelectorListObj wrapInList();++ size_t maxSpecificity() const override;+ size_t minSpecificity() const override;++ bool operator==(const Selector& rhs) const override;+ bool operator==(const SelectorList& rhs) const;+ bool operator==(const CompoundSelector& rhs) const;+ bool operator==(const SimpleSelector& rhs) const;++ ATTACH_CMP_OPERATIONS(ComplexSelector)+ ATTACH_AST_OPERATIONS(ComplexSelector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Base class for complex selector components+ ////////////////////////////////////////////////////////////////////////////+ class SelectorComponent : public Selector {+ // line break after list separator+ ADD_PROPERTY(bool, hasPostLineBreak)+ public:+ SelectorComponent(ParserState pstate, bool postLineBreak = false);+ size_t hash() const override = 0;+ void cloneChildren() override;+++ // By default we consider instances not empty+ virtual bool empty() const { return false; }++ virtual bool has_placeholder() const = 0;+ bool has_real_parent_ref() const override = 0;++ ComplexSelector* wrapInComplex();++ size_t maxSpecificity() const override { return 0; }+ size_t minSpecificity() const override { return 0; }++ virtual bool isCompound() const { return false; };+ virtual bool isCombinator() const { return false; };++ /* helper function for syntax sugar */+ virtual CompoundSelector* getCompound() { return NULL; }+ virtual SelectorCombinator* getCombinator() { return NULL; }+ virtual const CompoundSelector* getCompound() const { return NULL; }+ virtual const SelectorCombinator* getCombinator() const { return NULL; }++ virtual unsigned long specificity() const override;+ bool operator==(const Selector& rhs) const override = 0;+ ATTACH_VIRTUAL_CMP_OPERATIONS(SelectorComponent);+ ATTACH_VIRTUAL_AST_OPERATIONS(SelectorComponent);+ };++ ////////////////////////////////////////////////////////////////////////////+ // A specific combinator between compound selectors+ ////////////////////////////////////////////////////////////////////////////+ class SelectorCombinator final : public SelectorComponent {+ public:++ // Enumerate all possible selector combinators. There is some+ // discrepancy with dart-sass. Opted to name them as in CSS33+ enum Combinator { CHILD /* > */, GENERAL /* ~ */, ADJACENT /* + */};++ private:++ // Store the type of this combinator+ HASH_CONSTREF(Combinator, combinator)++ public:+ SelectorCombinator(ParserState pstate, Combinator combinator, bool postLineBreak = false);++ bool has_real_parent_ref() const override { return false; }+ bool has_placeholder() const override { return false; }++ /* helper function for syntax sugar */+ SelectorCombinator* getCombinator() final override { return this; }+ const SelectorCombinator* getCombinator() const final override { return this; }++ // Query type of combinator+ bool isCombinator() const override { return true; };++ // Matches the right-hand selector if it's a direct child of the left-+ // hand selector in the DOM tree. Dart-sass also calls this `child`+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator+ bool isChildCombinator() const { return combinator_ == CHILD; } // >++ // Matches the right-hand selector if it comes after the left-hand+ // selector in the DOM tree. Dart-sass class this `followingSibling`+ // https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator+ bool isGeneralCombinator() const { return combinator_ == GENERAL; } // ~++ // Matches the right-hand selector if it's immediately adjacent to the+ // left-hand selector in the DOM tree. Dart-sass calls this `nextSibling`+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator+ bool isAdjacentCombinator() const { return combinator_ == ADJACENT; } // +++ size_t maxSpecificity() const override { return 0; }+ size_t minSpecificity() const override { return 0; }++ size_t hash() const override {+ return std::hash<int>()(combinator_);+ }+ void cloneChildren() override;+ virtual unsigned long specificity() const override;+ bool operator==(const Selector& rhs) const override;+ bool operator==(const SelectorComponent& rhs) const override;++ ATTACH_CMP_OPERATIONS(SelectorCombinator)+ ATTACH_AST_OPERATIONS(SelectorCombinator)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // A compound selector consists of multiple simple selectors+ ////////////////////////////////////////////////////////////////////////////+ class CompoundSelector final : public SelectorComponent, public Vectorized<SimpleSelectorObj> {+ ADD_PROPERTY(bool, hasRealParent)+ ADD_PROPERTY(bool, extended)+ public:+ CompoundSelector(ParserState pstate, bool postLineBreak = false);++ // Returns true if this compound selector+ // fullfills various criteria.+ bool isInvisible() const;++ bool empty() const override {+ return Vectorized::empty();+ }++ size_t hash() const override;+ CompoundSelector* unifyWith(CompoundSelector* rhs);++ /* helper function for syntax sugar */+ CompoundSelector* getCompound() final override { return this; }+ const CompoundSelector* getCompound() const final override { return this; }++ bool isSuperselectorOf(const CompoundSelector* sub, std::string wrapped = "") const;++ void cloneChildren() override;+ bool has_real_parent_ref() const override;+ bool has_placeholder() const override;+ std::vector<ComplexSelectorObj> resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);++ virtual bool isCompound() const override { return true; };+ virtual unsigned long specificity() const override;++ size_t maxSpecificity() const override;+ size_t minSpecificity() const override;++ bool operator==(const Selector& rhs) const override;++ bool operator==(const SelectorComponent& rhs) const override;++ bool operator==(const SelectorList& rhs) const;+ bool operator==(const ComplexSelector& rhs) const;+ bool operator==(const SimpleSelector& rhs) const;++ ATTACH_CMP_OPERATIONS(CompoundSelector)+ ATTACH_AST_OPERATIONS(CompoundSelector)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////+ // Comma-separated selector groups.+ ///////////////////////////////////+ class SelectorList final : public Selector, public Vectorized<ComplexSelectorObj> {+ private:+ // maybe we have optional flag+ // ToDo: should be at ExtendRule?+ ADD_PROPERTY(bool, is_optional)+ public:+ SelectorList(ParserState pstate, size_t s = 0);+ std::string type() const override { return "list"; }+ size_t hash() const override;++ SelectorList* unifyWith(SelectorList*);++ // Returns true if all complex selectors+ // can have real parents, meaning every+ // first component does allow for it+ bool isInvisible() const;++ void cloneChildren() override;+ bool has_real_parent_ref() const override;+ SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);+ virtual unsigned long specificity() const override;++ bool isSuperselectorOf(const SelectorList* sub) const;++ size_t maxSpecificity() const override;+ size_t minSpecificity() const override;++ bool operator==(const Selector& rhs) const override;+ bool operator==(const ComplexSelector& rhs) const;+ bool operator==(const CompoundSelector& rhs) const;+ bool operator==(const SimpleSelector& rhs) const;+ // Selector Lists can be compared to comma lists+ bool operator==(const Expression& rhs) const override;++ ATTACH_CMP_OPERATIONS(SelectorList)+ ATTACH_AST_OPERATIONS(SelectorList)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////+ // The Sass `@extend` directive.+ ////////////////////////////////+ class ExtendRule final : public Statement {+ ADD_PROPERTY(bool, isOptional)+ // This should be a simple selector only!+ ADD_PROPERTY(SelectorListObj, selector)+ ADD_PROPERTY(Selector_Schema_Obj, schema)+ public:+ ExtendRule(ParserState pstate, SelectorListObj s);+ ExtendRule(ParserState pstate, Selector_Schema_Obj s);+ ATTACH_AST_OPERATIONS(ExtendRule)+ ATTACH_CRTP_PERFORM_METHODS()+ };++}++#endif
+ libsass/src/ast_supports.cpp view
@@ -0,0 +1,114 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"+++namespace Sass {++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Supports_Block::Supports_Block(ParserState pstate, Supports_Condition_Obj condition, Block_Obj block)+ : Has_Block(pstate, block), condition_(condition)+ { statement_type(SUPPORTS); }+ Supports_Block::Supports_Block(const Supports_Block* ptr)+ : Has_Block(ptr), condition_(ptr->condition_)+ { statement_type(SUPPORTS); }+ bool Supports_Block::bubbles() { return true; }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Supports_Condition::Supports_Condition(ParserState pstate)+ : Expression(pstate)+ { }++ Supports_Condition::Supports_Condition(const Supports_Condition* ptr)+ : Expression(ptr)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Supports_Operator::Supports_Operator(ParserState pstate, Supports_Condition_Obj l, Supports_Condition_Obj r, Operand o)+ : Supports_Condition(pstate), left_(l), right_(r), operand_(o)+ { }+ Supports_Operator::Supports_Operator(const Supports_Operator* ptr)+ : Supports_Condition(ptr),+ left_(ptr->left_),+ right_(ptr->right_),+ operand_(ptr->operand_)+ { }++ bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const+ {+ if (Supports_Operator_Obj op = Cast<Supports_Operator>(cond)) {+ return op->operand() != operand();+ }+ return Cast<Supports_Negation>(cond) != NULL;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Supports_Negation::Supports_Negation(ParserState pstate, Supports_Condition_Obj c)+ : Supports_Condition(pstate), condition_(c)+ { }+ Supports_Negation::Supports_Negation(const Supports_Negation* ptr)+ : Supports_Condition(ptr), condition_(ptr->condition_)+ { }++ bool Supports_Negation::needs_parens(Supports_Condition_Obj cond) const+ {+ return Cast<Supports_Negation>(cond) ||+ Cast<Supports_Operator>(cond);+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Supports_Declaration::Supports_Declaration(ParserState pstate, Expression_Obj f, Expression_Obj v)+ : Supports_Condition(pstate), feature_(f), value_(v)+ { }+ Supports_Declaration::Supports_Declaration(const Supports_Declaration* ptr)+ : Supports_Condition(ptr),+ feature_(ptr->feature_),+ value_(ptr->value_)+ { }++ bool Supports_Declaration::needs_parens(Supports_Condition_Obj cond) const+ {+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Supports_Interpolation::Supports_Interpolation(ParserState pstate, Expression_Obj v)+ : Supports_Condition(pstate), value_(v)+ { }+ Supports_Interpolation::Supports_Interpolation(const Supports_Interpolation* ptr)+ : Supports_Condition(ptr),+ value_(ptr->value_)+ { }++ bool Supports_Interpolation::needs_parens(Supports_Condition_Obj cond) const+ {+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ IMPLEMENT_AST_OPERATORS(Supports_Block);+ IMPLEMENT_AST_OPERATORS(Supports_Condition);+ IMPLEMENT_AST_OPERATORS(Supports_Operator);+ IMPLEMENT_AST_OPERATORS(Supports_Negation);+ IMPLEMENT_AST_OPERATORS(Supports_Declaration);+ IMPLEMENT_AST_OPERATORS(Supports_Interpolation);++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++}
+ libsass/src/ast_supports.hpp view
@@ -0,0 +1,121 @@+#ifndef SASS_AST_SUPPORTS_H+#define SASS_AST_SUPPORTS_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <set>+#include <deque>+#include <vector>+#include <string>+#include <sstream>+#include <iostream>+#include <typeinfo>+#include <algorithm>+#include "sass/base.h"+#include "ast_fwd_decl.hpp"++#include "util.hpp"+#include "units.hpp"+#include "context.hpp"+#include "position.hpp"+#include "constants.hpp"+#include "operation.hpp"+#include "position.hpp"+#include "inspect.hpp"+#include "source_map.hpp"+#include "environment.hpp"+#include "error_handling.hpp"+#include "ast_def_macros.hpp"+#include "ast_fwd_decl.hpp"+#include "source_map.hpp"+#include "fn_utils.hpp"++#include "sass.h"++namespace Sass {++ ////////////////////+ // `@supports` rule.+ ////////////////////+ class Supports_Block : public Has_Block {+ ADD_PROPERTY(Supports_Condition_Obj, condition)+ public:+ Supports_Block(ParserState pstate, Supports_Condition_Obj condition, Block_Obj block = {});+ bool bubbles() override;+ ATTACH_AST_OPERATIONS(Supports_Block)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////////////////////////////+ // The abstract superclass of all Supports conditions.+ //////////////////////////////////////////////////////+ class Supports_Condition : public Expression {+ public:+ Supports_Condition(ParserState pstate);+ virtual bool needs_parens(Supports_Condition_Obj cond) const { return false; }+ ATTACH_AST_OPERATIONS(Supports_Condition)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////+ // An operator condition (e.g. `CONDITION1 and CONDITION2`).+ ////////////////////////////////////////////////////////////+ class Supports_Operator : public Supports_Condition {+ public:+ enum Operand { AND, OR };+ private:+ ADD_PROPERTY(Supports_Condition_Obj, left);+ ADD_PROPERTY(Supports_Condition_Obj, right);+ ADD_PROPERTY(Operand, operand);+ public:+ Supports_Operator(ParserState pstate, Supports_Condition_Obj l, Supports_Condition_Obj r, Operand o);+ virtual bool needs_parens(Supports_Condition_Obj cond) const override;+ ATTACH_AST_OPERATIONS(Supports_Operator)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////////////////+ // A negation condition (`not CONDITION`).+ //////////////////////////////////////////+ class Supports_Negation : public Supports_Condition {+ private:+ ADD_PROPERTY(Supports_Condition_Obj, condition);+ public:+ Supports_Negation(ParserState pstate, Supports_Condition_Obj c);+ virtual bool needs_parens(Supports_Condition_Obj cond) const override;+ ATTACH_AST_OPERATIONS(Supports_Negation)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ /////////////////////////////////////////////////////+ // A declaration condition (e.g. `(feature: value)`).+ /////////////////////////////////////////////////////+ class Supports_Declaration : public Supports_Condition {+ private:+ ADD_PROPERTY(Expression_Obj, feature);+ ADD_PROPERTY(Expression_Obj, value);+ public:+ Supports_Declaration(ParserState pstate, Expression_Obj f, Expression_Obj v);+ virtual bool needs_parens(Supports_Condition_Obj cond) const override;+ ATTACH_AST_OPERATIONS(Supports_Declaration)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////////+ // An interpolation condition (e.g. `#{$var}`).+ ///////////////////////////////////////////////+ class Supports_Interpolation : public Supports_Condition {+ private:+ ADD_PROPERTY(Expression_Obj, value);+ public:+ Supports_Interpolation(ParserState pstate, Expression_Obj v);+ virtual bool needs_parens(Supports_Condition_Obj cond) const override;+ ATTACH_AST_OPERATIONS(Supports_Interpolation)+ ATTACH_CRTP_PERFORM_METHODS()+ };++}++#endif
+ libsass/src/ast_values.cpp view
@@ -0,0 +1,1154 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++namespace Sass {++ void str_rtrim(std::string& str, const std::string& delimiters = " \f\n\r\t\v")+ {+ str.erase( str.find_last_not_of( delimiters ) + 1 );+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ PreValue::PreValue(ParserState pstate, bool d, bool e, bool i, Type ct)+ : Expression(pstate, d, e, i, ct)+ { }+ PreValue::PreValue(const PreValue* ptr)+ : Expression(ptr)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Value::Value(ParserState pstate, bool d, bool e, bool i, Type ct)+ : PreValue(pstate, d, e, i, ct)+ { }+ Value::Value(const Value* ptr)+ : PreValue(ptr)+ { }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ List::List(ParserState pstate, size_t size, enum Sass_Separator sep, bool argl, bool bracket)+ : Value(pstate),+ Vectorized<Expression_Obj>(size),+ separator_(sep),+ is_arglist_(argl),+ is_bracketed_(bracket),+ from_selector_(false)+ { concrete_type(LIST); }++ List::List(const List* ptr)+ : Value(ptr),+ Vectorized<Expression_Obj>(*ptr),+ separator_(ptr->separator_),+ is_arglist_(ptr->is_arglist_),+ is_bracketed_(ptr->is_bracketed_),+ from_selector_(ptr->from_selector_)+ { concrete_type(LIST); }++ size_t List::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(sep_string());+ hash_combine(hash_, std::hash<bool>()(is_bracketed()));+ for (size_t i = 0, L = length(); i < L; ++i)+ hash_combine(hash_, (elements()[i])->hash());+ }+ return hash_;+ }++ void List::set_delayed(bool delayed)+ {+ is_delayed(delayed);+ // don't set children+ }++ bool List::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<List>(&rhs)) {+ if (length() < r->length()) return true;+ if (length() > r->length()) return false;+ const auto& left = elements();+ const auto& right = r->elements();+ for (size_t i = 0; i < left.size(); i += 1) {+ if (*left[i] < *right[i]) return true;+ if (*left[i] == *right[i]) continue;+ return false;+ }+ return false;+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool List::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<List>(&rhs)) {+ if (length() != r->length()) return false;+ if (separator() != r->separator()) return false;+ if (is_bracketed() != r->is_bracketed()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ auto rv = r->at(i);+ auto lv = this->at(i);+ if (!lv && rv) return false;+ else if (!rv && lv) return false;+ else if (*lv != *rv) return false;+ }+ return true;+ }+ return false;+ }++ size_t List::size() const {+ if (!is_arglist_) return length();+ // arglist expects a list of arguments+ // so we need to break before keywords+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression_Obj obj = this->at(i);+ if (Argument* arg = Cast<Argument>(obj)) {+ if (!arg->name().empty()) return i;+ }+ }+ return length();+ }+++ Expression_Obj List::value_at_index(size_t i) {+ Expression_Obj obj = this->at(i);+ if (is_arglist_) {+ if (Argument* arg = Cast<Argument>(obj)) {+ return arg->value();+ } else {+ return obj;+ }+ } else {+ return obj;+ }+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Map::Map(ParserState pstate, size_t size)+ : Value(pstate),+ Hashed(size)+ { concrete_type(MAP); }++ Map::Map(const Map* ptr)+ : Value(ptr),+ Hashed(*ptr)+ { concrete_type(MAP); }++ bool Map::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Map>(&rhs)) {+ if (length() < r->length()) return true;+ if (length() > r->length()) return false;+ const auto& lkeys = keys();+ const auto& rkeys = r->keys();+ for (size_t i = 0; i < lkeys.size(); i += 1) {+ if (*lkeys[i] < *rkeys[i]) return true;+ if (*lkeys[i] == *rkeys[i]) continue;+ return false;+ }+ const auto& lvals = values();+ const auto& rvals = r->values();+ for (size_t i = 0; i < lvals.size(); i += 1) {+ if (*lvals[i] < *rvals[i]) return true;+ if (*lvals[i] == *rvals[i]) continue;+ return false;+ }+ return false;+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Map::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Map>(&rhs)) {+ if (length() != r->length()) return false;+ for (auto key : keys()) {+ auto rv = r->at(key);+ auto lv = this->at(key);+ if (!lv && rv) return false;+ else if (!rv && lv) return false;+ else if (*lv != *rv) return false;+ }+ return true;+ }+ return false;+ }++ List_Obj Map::to_list(ParserState& pstate) {+ List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA);++ for (auto key : keys()) {+ List_Obj l = SASS_MEMORY_NEW(List, pstate, 2);+ l->append(key);+ l->append(at(key));+ ret->append(l);+ }++ return ret;+ }++ size_t Map::hash() const+ {+ if (hash_ == 0) {+ for (auto key : keys()) {+ hash_combine(hash_, key->hash());+ hash_combine(hash_, at(key)->hash());+ }+ }++ return hash_;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Binary_Expression::Binary_Expression(ParserState pstate,+ Operand op, Expression_Obj lhs, Expression_Obj rhs)+ : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0)+ { }++ Binary_Expression::Binary_Expression(const Binary_Expression* ptr)+ : PreValue(ptr),+ op_(ptr->op_),+ left_(ptr->left_),+ right_(ptr->right_),+ hash_(ptr->hash_)+ { }++ bool Binary_Expression::is_left_interpolant(void) const+ {+ return is_interpolant() || (left() && left()->is_left_interpolant());+ }+ bool Binary_Expression::is_right_interpolant(void) const+ {+ return is_interpolant() || (right() && right()->is_right_interpolant());+ }++ const std::string Binary_Expression::type_name()+ {+ return sass_op_to_name(optype());+ }++ const std::string Binary_Expression::separator()+ {+ return sass_op_separator(optype());+ }++ bool Binary_Expression::has_interpolant() const+ {+ return is_left_interpolant() ||+ is_right_interpolant();+ }++ void Binary_Expression::set_delayed(bool delayed)+ {+ right()->set_delayed(delayed);+ left()->set_delayed(delayed);+ is_delayed(delayed);+ }++ bool Binary_Expression::operator<(const Expression& rhs) const+ {+ if (auto m = Cast<Binary_Expression>(&rhs)) {+ return type() < m->type() ||+ *left() < *m->left() ||+ *right() < *m->right();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Binary_Expression::operator==(const Expression& rhs) const+ {+ if (auto m = Cast<Binary_Expression>(&rhs)) {+ return type() == m->type() &&+ *left() == *m->left() &&+ *right() == *m->right();+ }+ return false;+ }++ size_t Binary_Expression::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<size_t>()(optype());+ hash_combine(hash_, left()->hash());+ hash_combine(hash_, right()->hash());+ }+ return hash_;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Function::Function(ParserState pstate, Definition_Obj def, bool css)+ : Value(pstate), definition_(def), is_css_(css)+ { concrete_type(FUNCTION_VAL); }++ Function::Function(const Function* ptr)+ : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_)+ { concrete_type(FUNCTION_VAL); }++ bool Function::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Function>(&rhs)) {+ auto d1 = Cast<Definition>(definition());+ auto d2 = Cast<Definition>(r->definition());+ if (d1 == nullptr) return d2 != nullptr;+ else if (d2 == nullptr) return false;+ if (is_css() == r->is_css()) {+ return d1 < d2;+ }+ return r->is_css();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Function::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Function>(&rhs)) {+ auto d1 = Cast<Definition>(definition());+ auto d2 = Cast<Definition>(r->definition());+ return d1 && d2 && d1 == d2 && is_css() == r->is_css();+ }+ return false;+ }++ std::string Function::name() {+ if (definition_) {+ return definition_->name();+ }+ return "";+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Function_Call::Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, void* cookie)+ : PreValue(pstate), sname_(n), arguments_(args), func_(), via_call_(false), cookie_(cookie), hash_(0)+ { concrete_type(FUNCTION); }+ Function_Call::Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, Function_Obj func)+ : PreValue(pstate), sname_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0)+ { concrete_type(FUNCTION); }+ Function_Call::Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args)+ : PreValue(pstate), sname_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0)+ { concrete_type(FUNCTION); }++ Function_Call::Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie)+ : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(), via_call_(false), cookie_(cookie), hash_(0)+ { concrete_type(FUNCTION); }+ Function_Call::Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func)+ : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0)+ { concrete_type(FUNCTION); }+ Function_Call::Function_Call(ParserState pstate, std::string n, Arguments_Obj args)+ : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), via_call_(false), cookie_(0), hash_(0)+ { concrete_type(FUNCTION); }++ Function_Call::Function_Call(const Function_Call* ptr)+ : PreValue(ptr),+ sname_(ptr->sname_),+ arguments_(ptr->arguments_),+ func_(ptr->func_),+ via_call_(ptr->via_call_),+ cookie_(ptr->cookie_),+ hash_(ptr->hash_)+ { concrete_type(FUNCTION); }++ bool Function_Call::operator==(const Expression& rhs) const+ {+ if (auto m = Cast<Function_Call>(&rhs)) {+ if (*sname() != *m->sname()) return false;+ if (arguments()->length() != m->arguments()->length()) return false;+ for (size_t i = 0, L = arguments()->length(); i < L; ++i)+ if (*arguments()->get(i) != *m->arguments()->get(i)) return false;+ return true;+ }+ return false;+ }++ size_t Function_Call::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(name());+ for (auto argument : arguments()->elements())+ hash_combine(hash_, argument->hash());+ }+ return hash_;+ }++ std::string Function_Call::name() const+ {+ return sname();+ }++ bool Function_Call::is_css() {+ if (func_) return func_->is_css();+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Variable::Variable(ParserState pstate, std::string n)+ : PreValue(pstate), name_(n)+ { concrete_type(VARIABLE); }++ Variable::Variable(const Variable* ptr)+ : PreValue(ptr), name_(ptr->name_)+ { concrete_type(VARIABLE); }++ bool Variable::operator==(const Expression& rhs) const+ {+ if (auto e = Cast<Variable>(&rhs)) {+ return name() == e->name();+ }+ return false;+ }++ size_t Variable::hash() const+ {+ return std::hash<std::string>()(name());+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Number::Number(ParserState pstate, double val, std::string u, bool zero)+ : Value(pstate),+ Units(),+ value_(val),+ zero_(zero),+ hash_(0)+ {+ size_t l = 0;+ size_t r;+ if (!u.empty()) {+ bool nominator = true;+ while (true) {+ r = u.find_first_of("*/", l);+ std::string unit(u.substr(l, r == std::string::npos ? r : r - l));+ if (!unit.empty()) {+ if (nominator) numerators.push_back(unit);+ else denominators.push_back(unit);+ }+ if (r == std::string::npos) break;+ // ToDo: should error for multiple slashes+ // if (!nominator && u[r] == '/') error(...)+ if (u[r] == '/')+ nominator = false;+ // strange math parsing?+ // else if (u[r] == '*')+ // nominator = true;+ l = r + 1;+ }+ }+ concrete_type(NUMBER);+ }++ Number::Number(const Number* ptr)+ : Value(ptr),+ Units(ptr),+ value_(ptr->value_), zero_(ptr->zero_),+ hash_(ptr->hash_)+ { concrete_type(NUMBER); }++ // cancel out unnecessary units+ void Number::reduce()+ {+ // apply conversion factor+ value_ *= this->Units::reduce();+ }++ void Number::normalize()+ {+ // apply conversion factor+ value_ *= this->Units::normalize();+ }++ size_t Number::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<double>()(value_);+ for (const auto numerator : numerators)+ hash_combine(hash_, std::hash<std::string>()(numerator));+ for (const auto denominator : denominators)+ hash_combine(hash_, std::hash<std::string>()(denominator));+ }+ return hash_;+ }++ bool Number::operator< (const Expression& rhs) const+ {+ if (auto n = Cast<Number>(&rhs)) {+ return *this < *n;+ }+ return false;+ }++ bool Number::operator== (const Expression& rhs) const+ {+ if (auto n = Cast<Number>(&rhs)) {+ return *this == *n;+ }+ return false;+ }++ bool Number::operator== (const Number& rhs) const+ {+ // unitless or only having one unit are equivalent (3.4)+ // therefore we need to reduce the units beforehand+ Number l(*this), r(rhs); l.reduce(); r.reduce();+ size_t lhs_units = l.numerators.size() + l.denominators.size();+ size_t rhs_units = r.numerators.size() + r.denominators.size();+ if (!lhs_units || !rhs_units) {+ return NEAR_EQUAL(l.value(), r.value());+ }+ // ensure both have same units+ l.normalize(); r.normalize();+ Units &lhs_unit = l, &rhs_unit = r;+ return lhs_unit == rhs_unit &&+ NEAR_EQUAL(l.value(), r.value());+ }++ bool Number::operator< (const Number& rhs) const+ {+ // unitless or only having one unit are equivalent (3.4)+ // therefore we need to reduce the units beforehand+ Number l(*this), r(rhs); l.reduce(); r.reduce();+ size_t lhs_units = l.numerators.size() + l.denominators.size();+ size_t rhs_units = r.numerators.size() + r.denominators.size();+ if (!lhs_units || !rhs_units) {+ return l.value() < r.value();+ }+ // ensure both have same units+ l.normalize(); r.normalize();+ Units &lhs_unit = l, &rhs_unit = r;+ if (!(lhs_unit == rhs_unit)) {+ /* ToDo: do we always get useful backtraces? */+ throw Exception::IncompatibleUnits(rhs, *this);+ }+ if (lhs_unit == rhs_unit) {+ return l.value() < r.value();+ } else {+ return lhs_unit < rhs_unit;+ }+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Color::Color(ParserState pstate, double a, const std::string disp)+ : Value(pstate),+ disp_(disp), a_(a),+ hash_(0)+ { concrete_type(COLOR); }++ Color::Color(const Color* ptr)+ : Value(ptr->pstate()),+ // reset on copy+ disp_(""),+ a_(ptr->a_),+ hash_(ptr->hash_)+ { concrete_type(COLOR); }++ bool Color::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Color_RGBA>(&rhs)) {+ return *this < *r;+ }+ else if (auto r = Cast<Color_HSLA>(&rhs)) {+ return *this < *r;+ }+ else if (auto r = Cast<Color>(&rhs)) {+ return a_ < r->a();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Color::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Color_RGBA>(&rhs)) {+ return *this == *r;+ }+ else if (auto r = Cast<Color_HSLA>(&rhs)) {+ return *this == *r;+ }+ else if (auto r = Cast<Color>(&rhs)) {+ return a_ == r->a();+ }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Color_RGBA::Color_RGBA(ParserState pstate, double r, double g, double b, double a, const std::string disp)+ : Color(pstate, a, disp),+ r_(r), g_(g), b_(b)+ { concrete_type(COLOR); }++ Color_RGBA::Color_RGBA(const Color_RGBA* ptr)+ : Color(ptr),+ r_(ptr->r_),+ g_(ptr->g_),+ b_(ptr->b_)+ { concrete_type(COLOR); }++ bool Color_RGBA::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Color_RGBA>(&rhs)) {+ if (r_ < r->r()) return true;+ if (r_ > r->r()) return false;+ if (g_ < r->g()) return true;+ if (g_ > r->g()) return false;+ if (b_ < r->b()) return true;+ if (b_ > r->b()) return false;+ if (a_ < r->a()) return true;+ if (a_ > r->a()) return false;+ return false; // is equal+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Color_RGBA::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Color_RGBA>(&rhs)) {+ return r_ == r->r() &&+ g_ == r->g() &&+ b_ == r->b() &&+ a_ == r->a();+ }+ return false;+ }++ size_t Color_RGBA::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()("RGBA");+ hash_combine(hash_, std::hash<double>()(a_));+ hash_combine(hash_, std::hash<double>()(r_));+ hash_combine(hash_, std::hash<double>()(g_));+ hash_combine(hash_, std::hash<double>()(b_));+ }+ return hash_;+ }++ Color_HSLA* Color_RGBA::copyAsHSLA() const+ {++ // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV+ double r = r_ / 255.0;+ double g = g_ / 255.0;+ double b = b_ / 255.0;++ double max = std::max(r, std::max(g, b));+ double min = std::min(r, std::min(g, b));+ double delta = max - min;++ double h = 0;+ double s;+ double l = (max + min) / 2.0;++ if (NEAR_EQUAL(max, min)) {+ h = s = 0; // achromatic+ }+ else {+ if (l < 0.5) s = delta / (max + min);+ else s = delta / (2.0 - max - min);++ if (r == max) h = (g - b) / delta + (g < b ? 6 : 0);+ else if (g == max) h = (b - r) / delta + 2;+ else if (b == max) h = (r - g) / delta + 4;+ }++ // HSL hsl_struct;+ h = h * 60;+ s = s * 100;+ l = l * 100;++ return SASS_MEMORY_NEW(Color_HSLA,+ pstate(), h, s, l, a(), ""+ );+ }++ Color_RGBA* Color_RGBA::copyAsRGBA() const+ {+ return SASS_MEMORY_COPY(this);+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Color_HSLA::Color_HSLA(ParserState pstate, double h, double s, double l, double a, const std::string disp)+ : Color(pstate, a, disp),+ h_(absmod(h, 360.0)),+ s_(clip(s, 0.0, 100.0)),+ l_(clip(l, 0.0, 100.0))+ // hash_(0)+ { concrete_type(COLOR); }++ Color_HSLA::Color_HSLA(const Color_HSLA* ptr)+ : Color(ptr),+ h_(ptr->h_),+ s_(ptr->s_),+ l_(ptr->l_)+ // hash_(ptr->hash_)+ { concrete_type(COLOR); }++ bool Color_HSLA::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Color_HSLA>(&rhs)) {+ if (h_ < r->h()) return true;+ if (h_ > r->h()) return false;+ if (s_ < r->s()) return true;+ if (s_ > r->s()) return false;+ if (l_ < r->l()) return true;+ if (l_ > r->l()) return false;+ if (a_ < r->a()) return true;+ if (a_ > r->a()) return false;+ return false; // is equal+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Color_HSLA::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Color_HSLA>(&rhs)) {+ return h_ == r->h() &&+ s_ == r->s() &&+ l_ == r->l() &&+ a_ == r->a();+ }+ return false;+ }++ size_t Color_HSLA::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()("HSLA");+ hash_combine(hash_, std::hash<double>()(a_));+ hash_combine(hash_, std::hash<double>()(h_));+ hash_combine(hash_, std::hash<double>()(s_));+ hash_combine(hash_, std::hash<double>()(l_));+ }+ return hash_;+ }++ // hue to RGB helper function+ double h_to_rgb(double m1, double m2, double h)+ {+ h = absmod(h, 1.0);+ if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;+ if (h*2.0 < 1) return m2;+ if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;+ return m1;+ }++ Color_RGBA* Color_HSLA::copyAsRGBA() const+ {++ double h = absmod(h_ / 360.0, 1.0);+ double s = clip(s_ / 100.0, 0.0, 1.0);+ double l = clip(l_ / 100.0, 0.0, 1.0);++ // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.+ double m2;+ if (l <= 0.5) m2 = l*(s+1.0);+ else m2 = (l+s)-(l*s);+ double m1 = (l*2.0)-m2;+ // round the results -- consider moving this into the Color constructor+ double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0);+ double g = (h_to_rgb(m1, m2, h) * 255.0);+ double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0);++ return SASS_MEMORY_NEW(Color_RGBA,+ pstate(), r, g, b, a(), ""+ );+ }++ Color_HSLA* Color_HSLA::copyAsHSLA() const+ {+ return SASS_MEMORY_COPY(this);+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Custom_Error::Custom_Error(ParserState pstate, std::string msg)+ : Value(pstate), message_(msg)+ { concrete_type(C_ERROR); }++ Custom_Error::Custom_Error(const Custom_Error* ptr)+ : Value(ptr), message_(ptr->message_)+ { concrete_type(C_ERROR); }++ bool Custom_Error::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Custom_Error>(&rhs)) {+ return message() < r->message();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Custom_Error::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Custom_Error>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Custom_Warning::Custom_Warning(ParserState pstate, std::string msg)+ : Value(pstate), message_(msg)+ { concrete_type(C_WARNING); }++ Custom_Warning::Custom_Warning(const Custom_Warning* ptr)+ : Value(ptr), message_(ptr->message_)+ { concrete_type(C_WARNING); }++ bool Custom_Warning::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Custom_Warning>(&rhs)) {+ return message() < r->message();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Custom_Warning::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Custom_Warning>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Boolean::Boolean(ParserState pstate, bool val)+ : Value(pstate), value_(val),+ hash_(0)+ { concrete_type(BOOLEAN); }++ Boolean::Boolean(const Boolean* ptr)+ : Value(ptr),+ value_(ptr->value_),+ hash_(ptr->hash_)+ { concrete_type(BOOLEAN); }++ bool Boolean::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<Boolean>(&rhs)) {+ return (value() < r->value());+ }+ return false;+ }++ bool Boolean::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<Boolean>(&rhs)) {+ return (value() == r->value());+ }+ return false;+ }++ size_t Boolean::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<bool>()(value_);+ }+ return hash_;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ String::String(ParserState pstate, bool delayed)+ : Value(pstate, delayed)+ { concrete_type(STRING); }+ String::String(const String* ptr)+ : Value(ptr)+ { concrete_type(STRING); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ String_Schema::String_Schema(ParserState pstate, size_t size, bool css)+ : String(pstate), Vectorized<PreValue_Obj>(size), css_(css), hash_(0)+ { concrete_type(STRING); }++ String_Schema::String_Schema(const String_Schema* ptr)+ : String(ptr),+ Vectorized<PreValue_Obj>(*ptr),+ css_(ptr->css_),+ hash_(ptr->hash_)+ { concrete_type(STRING); }++ void String_Schema::rtrim()+ {+ if (!empty()) {+ if (String* str = Cast<String>(last())) str->rtrim();+ }+ }++ bool String_Schema::is_left_interpolant(void) const+ {+ return length() && first()->is_left_interpolant();+ }+ bool String_Schema::is_right_interpolant(void) const+ {+ return length() && last()->is_right_interpolant();+ }++ bool String_Schema::operator< (const Expression& rhs) const+ {+ if (auto r = Cast<String_Schema>(&rhs)) {+ if (length() < r->length()) return true;+ if (length() > r->length()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ if (*get(i) < *r->get(i)) return true;+ if (*get(i) == *r->get(i)) continue;+ return false;+ }+ // Is equal+ return false;+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool String_Schema::operator== (const Expression& rhs) const+ {+ if (auto r = Cast<String_Schema>(&rhs)) {+ if (length() != r->length()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ auto rv = (*r)[i];+ auto lv = (*this)[i];+ if (*lv != *rv) return false;+ }+ return true;+ }+ return false;+ }++ bool String_Schema::has_interpolants()+ {+ for (auto el : elements()) {+ if (el->is_interpolant()) return true;+ }+ return false;+ }++ size_t String_Schema::hash() const+ {+ if (hash_ == 0) {+ for (auto string : elements())+ hash_combine(hash_, string->hash());+ }+ return hash_;+ }++ void String_Schema::set_delayed(bool delayed)+ {+ is_delayed(delayed);+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ String_Constant::String_Constant(ParserState pstate, std::string val, bool css)+ : String(pstate), quote_mark_(0), value_(read_css_string(val, css)), hash_(0)+ { }+ String_Constant::String_Constant(ParserState pstate, const char* beg, bool css)+ : String(pstate), quote_mark_(0), value_(read_css_string(std::string(beg), css)), hash_(0)+ { }+ String_Constant::String_Constant(ParserState pstate, const char* beg, const char* end, bool css)+ : String(pstate), quote_mark_(0), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0)+ { }+ String_Constant::String_Constant(ParserState pstate, const Token& tok, bool css)+ : String(pstate), quote_mark_(0), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0)+ { }++ String_Constant::String_Constant(const String_Constant* ptr)+ : String(ptr),+ quote_mark_(ptr->quote_mark_),+ value_(ptr->value_),+ hash_(ptr->hash_)+ { }++ bool String_Constant::is_invisible() const {+ return value_.empty() && quote_mark_ == 0;+ }++ bool String_Constant::operator< (const Expression& rhs) const+ {+ if (auto qstr = Cast<String_Quoted>(&rhs)) {+ return value() < qstr->value();+ }+ else if (auto cstr = Cast<String_Constant>(&rhs)) {+ return value() < cstr->value();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool String_Constant::operator== (const Expression& rhs) const+ {+ if (auto qstr = Cast<String_Quoted>(&rhs)) {+ return value() == qstr->value();+ }+ else if (auto cstr = Cast<String_Constant>(&rhs)) {+ return value() == cstr->value();+ }+ return false;+ }++ std::string String_Constant::inspect() const+ {+ return quote(value_, '*');+ }++ void String_Constant::rtrim()+ {+ str_rtrim(value_);+ }++ size_t String_Constant::hash() const+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(value_);+ }+ return hash_;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ String_Quoted::String_Quoted(ParserState pstate, std::string val, char q,+ bool keep_utf8_escapes, bool skip_unquoting,+ bool strict_unquoting, bool css)+ : String_Constant(pstate, val, css)+ {+ if (skip_unquoting == false) {+ value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting);+ }+ if (q && quote_mark_) quote_mark_ = q;+ }++ String_Quoted::String_Quoted(const String_Quoted* ptr)+ : String_Constant(ptr)+ { }++ bool String_Quoted::operator< (const Expression& rhs) const+ {+ if (auto qstr = Cast<String_Quoted>(&rhs)) {+ return value() < qstr->value();+ }+ else if (auto cstr = Cast<String_Constant>(&rhs)) {+ return value() < cstr->value();+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool String_Quoted::operator== (const Expression& rhs) const+ {+ if (auto qstr = Cast<String_Quoted>(&rhs)) {+ return value() == qstr->value();+ }+ else if (auto cstr = Cast<String_Constant>(&rhs)) {+ return value() == cstr->value();+ }+ return false;+ }++ std::string String_Quoted::inspect() const+ {+ return quote(value_, '*');+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Null::Null(ParserState pstate)+ : Value(pstate)+ { concrete_type(NULL_VAL); }++ Null::Null(const Null* ptr) : Value(ptr)+ { concrete_type(NULL_VAL); }++ bool Null::operator< (const Expression& rhs) const+ {+ if (Cast<Null>(&rhs)) {+ return false;+ }+ // compare/sort by type+ return type() < rhs.type();+ }++ bool Null::operator== (const Expression& rhs) const+ {+ return Cast<Null>(&rhs) != nullptr;+ }++ size_t Null::hash() const+ {+ return -1;+ }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ Parent_Reference::Parent_Reference(ParserState pstate)+ : Value(pstate)+ { concrete_type(PARENT); }++ Parent_Reference::Parent_Reference(const Parent_Reference* ptr)+ : Value(ptr)+ { concrete_type(PARENT); }++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++ IMPLEMENT_AST_OPERATORS(List);+ IMPLEMENT_AST_OPERATORS(Map);+ IMPLEMENT_AST_OPERATORS(Binary_Expression);+ IMPLEMENT_AST_OPERATORS(Function);+ IMPLEMENT_AST_OPERATORS(Function_Call);+ IMPLEMENT_AST_OPERATORS(Variable);+ IMPLEMENT_AST_OPERATORS(Number);+ IMPLEMENT_AST_OPERATORS(Color_RGBA);+ IMPLEMENT_AST_OPERATORS(Color_HSLA);+ IMPLEMENT_AST_OPERATORS(Custom_Error);+ IMPLEMENT_AST_OPERATORS(Custom_Warning);+ IMPLEMENT_AST_OPERATORS(Boolean);+ IMPLEMENT_AST_OPERATORS(String_Schema);+ IMPLEMENT_AST_OPERATORS(String_Constant);+ IMPLEMENT_AST_OPERATORS(String_Quoted);+ IMPLEMENT_AST_OPERATORS(Null);+ IMPLEMENT_AST_OPERATORS(Parent_Reference);++ /////////////////////////////////////////////////////////////////////////+ /////////////////////////////////////////////////////////////////////////++}
+ libsass/src/ast_values.hpp view
@@ -0,0 +1,498 @@+#ifndef SASS_AST_VALUES_H+#define SASS_AST_VALUES_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++namespace Sass {++ //////////////////////////////////////////////////////////////////////+ // Still just an expression, but with a to_string method+ //////////////////////////////////////////////////////////////////////+ class PreValue : public Expression {+ public:+ PreValue(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);+ ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);+ virtual ~PreValue() { }+ };++ //////////////////////////////////////////////////////////////////////+ // base class for values that support operations+ //////////////////////////////////////////////////////////////////////+ class Value : public PreValue {+ public:+ Value(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);++ // Some obects are not meant to be compared+ // ToDo: maybe fallback to pointer comparison?+ virtual bool operator< (const Expression& rhs) const override = 0;+ virtual bool operator== (const Expression& rhs) const override = 0;++ // We can give some reasonable implementations by using+ // inverst operators on the specialized implementations+ virtual bool operator> (const Expression& rhs) const {+ return rhs < *this;+ }+ virtual bool operator!= (const Expression& rhs) const {+ return !(*this == rhs);+ }++ ATTACH_VIRTUAL_AST_OPERATIONS(Value);++ };++ ///////////////////////////////////////////////////////////////////////+ // Lists of values, both comma- and space-separated (distinguished by a+ // type-tag.) Also used to represent variable-length argument lists.+ ///////////////////////////////////////////////////////////////////////+ class List : public Value, public Vectorized<Expression_Obj> {+ void adjust_after_pushing(Expression_Obj e) override { is_expanded(false); }+ private:+ ADD_PROPERTY(enum Sass_Separator, separator)+ ADD_PROPERTY(bool, is_arglist)+ ADD_PROPERTY(bool, is_bracketed)+ ADD_PROPERTY(bool, from_selector)+ public:+ List(ParserState pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false);+ std::string type() const override { return is_arglist_ ? "arglist" : "list"; }+ static std::string type_name() { return "list"; }+ const char* sep_string(bool compressed = false) const {+ return separator() == SASS_SPACE ?+ " " : (compressed ? "," : ", ");+ }+ bool is_invisible() const override { return empty() && !is_bracketed(); }+ Expression_Obj value_at_index(size_t i);++ virtual size_t hash() const override;+ virtual size_t size() const;+ virtual void set_delayed(bool delayed) override;++ virtual bool operator< (const Expression& rhs) const override;+ virtual bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(List)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Key value paris.+ ///////////////////////////////////////////////////////////////////////+ class Map : public Value, public Hashed<Expression_Obj, Expression_Obj, Map_Obj> {+ void adjust_after_pushing(std::pair<Expression_Obj, Expression_Obj> p) override { is_expanded(false); }+ public:+ Map(ParserState pstate, size_t size = 0);+ std::string type() const override { return "map"; }+ static std::string type_name() { return "map"; }+ bool is_invisible() const override { return empty(); }+ List_Obj to_list(ParserState& pstate);++ virtual size_t hash() const override;++ virtual bool operator< (const Expression& rhs) const override;+ virtual bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(Map)+ ATTACH_CRTP_PERFORM_METHODS()+ };+++ //////////////////////////////////////////////////////////////////////////+ // Binary expressions. Represents logical, relational, and arithmetic+ // operations. Templatized to avoid large switch statements and repetitive+ // subclassing.+ //////////////////////////////////////////////////////////////////////////+ class Binary_Expression : public PreValue {+ private:+ HASH_PROPERTY(Operand, op)+ HASH_PROPERTY(Expression_Obj, left)+ HASH_PROPERTY(Expression_Obj, right)+ mutable size_t hash_;+ public:+ Binary_Expression(ParserState pstate,+ Operand op, Expression_Obj lhs, Expression_Obj rhs);++ const std::string type_name();+ const std::string separator();+ bool is_left_interpolant(void) const override;+ bool is_right_interpolant(void) const override;+ bool has_interpolant() const override;++ virtual void set_delayed(bool delayed) override;++ virtual bool operator< (const Expression& rhs) const override;+ virtual bool operator==(const Expression& rhs) const override;++ virtual size_t hash() const override;+ enum Sass_OP optype() const { return op_.operand; }+ ATTACH_AST_OPERATIONS(Binary_Expression)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////+ // Function reference.+ ////////////////////////////////////////////////////+ class Function final : public Value {+ public:+ ADD_PROPERTY(Definition_Obj, definition)+ ADD_PROPERTY(bool, is_css)+ public:+ Function(ParserState pstate, Definition_Obj def, bool css);++ std::string type() const override { return "function"; }+ static std::string type_name() { return "function"; }+ bool is_invisible() const override { return true; }++ std::string name();++ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(Function)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////+ // Function calls.+ //////////////////+ class Function_Call final : public PreValue {+ HASH_CONSTREF(String_Obj, sname)+ HASH_PROPERTY(Arguments_Obj, arguments)+ HASH_PROPERTY(Function_Obj, func)+ ADD_PROPERTY(bool, via_call)+ ADD_PROPERTY(void*, cookie)+ mutable size_t hash_;+ public:+ Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie);+ Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func);+ Function_Call(ParserState pstate, std::string n, Arguments_Obj args);++ Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, void* cookie);+ Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args, Function_Obj func);+ Function_Call(ParserState pstate, String_Obj n, Arguments_Obj args);++ std::string name() const;+ bool is_css();++ bool operator==(const Expression& rhs) const override;++ size_t hash() const override;++ ATTACH_AST_OPERATIONS(Function_Call)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ///////////////////////+ // Variable references.+ ///////////////////////+ class Variable final : public PreValue {+ ADD_CONSTREF(std::string, name)+ public:+ Variable(ParserState pstate, std::string n);+ virtual bool operator==(const Expression& rhs) const override;+ virtual size_t hash() const override;+ ATTACH_AST_OPERATIONS(Variable)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////+ // Numbers, percentages, dimensions, and colors.+ ////////////////////////////////////////////////+ class Number final : public Value, public Units {+ HASH_PROPERTY(double, value)+ ADD_PROPERTY(bool, zero)+ mutable size_t hash_;+ public:+ Number(ParserState pstate, double val, std::string u = "", bool zero = true);++ bool zero() { return zero_; }++ std::string type() const override { return "number"; }+ static std::string type_name() { return "number"; }++ // cancel out unnecessary units+ // result will be in input units+ void reduce();++ // normalize units to defaults+ // needed to compare two numbers+ void normalize();++ size_t hash() const override;++ bool operator< (const Number& rhs) const;+ bool operator== (const Number& rhs) const;+ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;+ ATTACH_AST_OPERATIONS(Number)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////+ // Colors.+ //////////+ class Color : public Value {+ ADD_CONSTREF(std::string, disp)+ HASH_PROPERTY(double, a)+ protected:+ mutable size_t hash_;+ public:+ Color(ParserState pstate, double a = 1, const std::string disp = "");++ std::string type() const override { return "color"; }+ static std::string type_name() { return "color"; }++ virtual size_t hash() const override = 0;++ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;++ virtual Color_RGBA* copyAsRGBA() const = 0;+ virtual Color_RGBA* toRGBA() = 0;++ virtual Color_HSLA* copyAsHSLA() const = 0;+ virtual Color_HSLA* toHSLA() = 0;++ ATTACH_VIRTUAL_AST_OPERATIONS(Color)+ };++ //////////+ // Colors.+ //////////+ class Color_RGBA final : public Color {+ HASH_PROPERTY(double, r)+ HASH_PROPERTY(double, g)+ HASH_PROPERTY(double, b)+ public:+ Color_RGBA(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "");++ std::string type() const override { return "color"; }+ static std::string type_name() { return "color"; }++ size_t hash() const override;++ Color_RGBA* copyAsRGBA() const override;+ Color_RGBA* toRGBA() override { return this; }++ Color_HSLA* copyAsHSLA() const override;+ Color_HSLA* toHSLA() override { return copyAsHSLA(); }++ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(Color_RGBA)+ ATTACH_CRTP_PERFORM_METHODS()+ };+++ //////////+ // Colors.+ //////////+ class Color_HSLA final : public Color {+ HASH_PROPERTY(double, h)+ HASH_PROPERTY(double, s)+ HASH_PROPERTY(double, l)+ public:+ Color_HSLA(ParserState pstate, double h, double s, double l, double a = 1, const std::string disp = "");++ std::string type() const override { return "color"; }+ static std::string type_name() { return "color"; }++ size_t hash() const override;++ Color_RGBA* copyAsRGBA() const override;+ Color_RGBA* toRGBA() override { return copyAsRGBA(); }++ Color_HSLA* copyAsHSLA() const override;+ Color_HSLA* toHSLA() override { return this; }++ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(Color_HSLA)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////+ // Errors from Sass_Values.+ //////////////////////////////+ class Custom_Error final : public Value {+ ADD_CONSTREF(std::string, message)+ public:+ Custom_Error(ParserState pstate, std::string msg);+ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;+ ATTACH_AST_OPERATIONS(Custom_Error)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////+ // Warnings from Sass_Values.+ //////////////////////////////+ class Custom_Warning final : public Value {+ ADD_CONSTREF(std::string, message)+ public:+ Custom_Warning(ParserState pstate, std::string msg);+ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;+ ATTACH_AST_OPERATIONS(Custom_Warning)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////+ // Booleans.+ ////////////+ class Boolean final : public Value {+ HASH_PROPERTY(bool, value)+ mutable size_t hash_;+ public:+ Boolean(ParserState pstate, bool val);+ operator bool() override { return value_; }++ std::string type() const override { return "bool"; }+ static std::string type_name() { return "bool"; }++ size_t hash() const override;++ bool is_false() override { return !value_; }++ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(Boolean)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for Sass string values. Includes interpolated and+ // "flat" strings.+ ////////////////////////////////////////////////////////////////////////+ class String : public Value {+ public:+ String(ParserState pstate, bool delayed = false);+ static std::string type_name() { return "string"; }+ virtual ~String() = 0;+ virtual void rtrim() = 0;+ virtual bool operator<(const Expression& rhs) const override {+ return this->to_string() < rhs.to_string();+ };+ virtual bool operator==(const Expression& rhs) const override {+ return this->to_string() == rhs.to_string();+ };+ ATTACH_VIRTUAL_AST_OPERATIONS(String);+ ATTACH_CRTP_PERFORM_METHODS()+ };+ inline String::~String() { };++ ///////////////////////////////////////////////////////////////////////+ // Interpolated strings. Meant to be reduced to flat strings during the+ // evaluation phase.+ ///////////////////////////////////////////////////////////////////////+ class String_Schema final : public String, public Vectorized<PreValue_Obj> {+ ADD_PROPERTY(bool, css)+ mutable size_t hash_;+ public:+ String_Schema(ParserState pstate, size_t size = 0, bool css = true);++ std::string type() const override { return "string"; }+ static std::string type_name() { return "string"; }++ bool is_left_interpolant(void) const override;+ bool is_right_interpolant(void) const override;++ bool has_interpolants();+ void rtrim() override;+ size_t hash() const override;+ virtual void set_delayed(bool delayed) override;++ bool operator< (const Expression& rhs) const override;+ bool operator==(const Expression& rhs) const override;+ ATTACH_AST_OPERATIONS(String_Schema)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////+ // Flat strings -- the lowest level of raw textual data.+ ////////////////////////////////////////////////////////+ class String_Constant : public String {+ ADD_PROPERTY(char, quote_mark)+ HASH_CONSTREF(std::string, value)+ protected:+ mutable size_t hash_;+ public:+ String_Constant(ParserState pstate, std::string val, bool css = true);+ String_Constant(ParserState pstate, const char* beg, bool css = true);+ String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true);+ String_Constant(ParserState pstate, const Token& tok, bool css = true);+ std::string type() const override { return "string"; }+ static std::string type_name() { return "string"; }+ bool is_invisible() const override;+ virtual void rtrim() override;+ size_t hash() const override;+ bool operator< (const Expression& rhs) const override;+ bool operator==(const Expression& rhs) const override;+ // quotes are forced on inspection+ virtual std::string inspect() const override;+ ATTACH_AST_OPERATIONS(String_Constant)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ ////////////////////////////////////////////////////////+ // Possibly quoted string (unquote on instantiation)+ ////////////////////////////////////////////////////////+ class String_Quoted final : public String_Constant {+ public:+ String_Quoted(ParserState pstate, std::string val, char q = 0,+ bool keep_utf8_escapes = false, bool skip_unquoting = false,+ bool strict_unquoting = true, bool css = true);+ bool operator< (const Expression& rhs) const override;+ bool operator==(const Expression& rhs) const override;+ // quotes are forced on inspection+ std::string inspect() const override;+ ATTACH_AST_OPERATIONS(String_Quoted)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////+ // The null value.+ //////////////////+ class Null final : public Value {+ public:+ Null(ParserState pstate);+ std::string type() const override { return "null"; }+ static std::string type_name() { return "null"; }+ bool is_invisible() const override { return true; }+ operator bool() override { return false; }+ bool is_false() override { return true; }++ size_t hash() const override;++ bool operator< (const Expression& rhs) const override;+ bool operator== (const Expression& rhs) const override;++ ATTACH_AST_OPERATIONS(Null)+ ATTACH_CRTP_PERFORM_METHODS()+ };++ //////////////////////////////////+ // The Parent Reference Expression.+ //////////////////////////////////+ class Parent_Reference final : public Value {+ public:+ Parent_Reference(ParserState pstate);+ std::string type() const override { return "parent"; }+ static std::string type_name() { return "parent"; }+ bool operator< (const Expression& rhs) const override {+ return false; // they are always equal+ }+ bool operator==(const Expression& rhs) const override {+ return true; // they are always equal+ };+ ATTACH_AST_OPERATIONS(Parent_Reference)+ ATTACH_CRTP_PERFORM_METHODS()+ };++}++#endif
libsass/src/backtrace.cpp view
@@ -24,6 +24,8 @@ ss << indent; ss << "on line "; ss << trace.pstate.line + 1;+ ss << ":";+ ss << trace.pstate.column + 1; ss << " of " << rel_path; // ss << trace.caller; first = false;@@ -33,6 +35,8 @@ ss << indent; ss << "from line "; ss << trace.pstate.line + 1;+ ss << ":";+ ss << trace.pstate.column + 1; ss << " of " << rel_path; }
libsass/src/base64vlq.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "base64vlq.hpp" namespace Sass {
libsass/src/bind.cpp view
@@ -1,6 +1,7 @@ #include "sass.hpp" #include "bind.hpp" #include "ast.hpp"+#include "backtrace.hpp" #include "context.hpp" #include "expand.hpp" #include "eval.hpp"@@ -10,7 +11,7 @@ namespace Sass { - void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval)+ void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces) { std::string callee(type + " " + name); @@ -54,7 +55,7 @@ std::stringstream msg; msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << " for `" << name << "'";- return error(msg.str(), as->pstate(), eval->exp.traces);+ return error(msg.str(), as->pstate(), traces); } Parameter_Obj p = ps->at(ip); @@ -66,7 +67,7 @@ // We should always get a list for rest arguments if (List_Obj rest = Cast<List>(a->value())) { // create a new list object for wrapped items- List_Ptr arglist = SASS_MEMORY_NEW(List,+ List* arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, rest->separator(),@@ -94,7 +95,7 @@ } else if (a->is_keyword_argument()) { // expand keyword arguments into their parameters- List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true);+ List* arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); env->local_frame()[p->name()] = arglist; Map_Obj argmap = Cast<Map>(a->value()); for (auto key : argmap->keys()) {@@ -107,8 +108,8 @@ false, false)); } else {- eval->exp.traces.push_back(Backtrace(key->pstate()));- throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);+ traces.push_back(Backtrace(key->pstate()));+ throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); } } @@ -167,7 +168,7 @@ } // consumed parameter ++ip;- // no more paramaters+ // no more parameters break; } @@ -203,7 +204,7 @@ // otherwise move one of the rest args into the param, converting to argument if necessary Expression_Obj obj = arglist->at(0); if (!(a = Cast<Argument>(obj))) {- Expression_Ptr a_to_convert = obj;+ Expression* a_to_convert = obj; a = SASS_MEMORY_NEW(Argument, a_to_convert->pstate(), a_to_convert,@@ -220,17 +221,17 @@ Map_Obj argmap = Cast<Map>(a->value()); for (auto key : argmap->keys()) {- String_Constant_Ptr val = Cast<String_Constant>(key);+ String_Constant* val = Cast<String_Constant>(key); if (val == NULL) {- eval->exp.traces.push_back(Backtrace(key->pstate()));- throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);+ traces.push_back(Backtrace(key->pstate()));+ throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); } std::string param = "$" + unquote(val->value()); if (!param_map.count(param)) { std::stringstream msg; msg << callee << " has no parameter named " << param;- error(msg.str(), a->pstate(), eval->exp.traces);+ error(msg.str(), a->pstate(), traces); } env->local_frame()[param] = argmap->at(key); }@@ -245,7 +246,7 @@ std::stringstream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee;- error(msg.str(), a->pstate(), eval->exp.traces);+ error(msg.str(), a->pstate(), traces); } // ordinal arg -- bind it to the next param env->local_frame()[p->name()] = a->value();@@ -259,7 +260,7 @@ } else { std::stringstream msg; msg << callee << " has no parameter named " << a->name();- error(msg.str(), a->pstate(), eval->exp.traces);+ error(msg.str(), a->pstate(), traces); } } if (param_map[a->name()]) {@@ -267,14 +268,14 @@ std::stringstream msg; msg << "argument " << a->name() << " of " << callee << "cannot be used as named argument";- error(msg.str(), a->pstate(), eval->exp.traces);+ error(msg.str(), a->pstate(), traces); } } if (env->has_local(a->name())) { std::stringstream msg; msg << "parameter " << p->name() << "provided more than once in call to " << callee;- error(msg.str(), a->pstate(), eval->exp.traces);+ error(msg.str(), a->pstate(), traces); } env->local_frame()[a->name()] = a->value(); }@@ -294,12 +295,12 @@ env->local_frame()[leftover->name()] = varargs; } else if (leftover->default_value()) {- Expression_Ptr dv = leftover->default_value()->perform(eval);+ Expression* dv = leftover->default_value()->perform(eval); env->local_frame()[leftover->name()] = dv; } else { // param is unbound and has no default value -- error- throw Exception::MissingArgument(as->pstate(), eval->exp.traces, name, leftover->name(), type);+ throw Exception::MissingArgument(as->pstate(), traces, name, leftover->name(), type); } } }
libsass/src/bind.hpp view
@@ -2,12 +2,14 @@ #define SASS_BIND_H #include <string>+#include "backtrace.hpp" #include "environment.hpp" #include "ast_fwd_decl.hpp" namespace Sass { - void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Context*, Env*, Eval*);+ void bind(std::string type, std::string name, Parameters_Obj, Arguments_Obj, Env*, Eval*, Backtraces& traces);+ } #endif
+ libsass/src/c2ast.cpp view
@@ -0,0 +1,64 @@+#include "ast.hpp"+#include "units.hpp"+#include "position.hpp"+#include "backtrace.hpp"+#include "sass/values.h"+#include "ast_fwd_decl.hpp"+#include "error_handling.hpp"++namespace Sass {++ Value* c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate)+ {+ using std::strlen;+ using std::strcpy;+ Value* e = NULL;+ switch (sass_value_get_tag(v)) {+ case SASS_BOOLEAN: {+ e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));+ } break;+ case SASS_NUMBER: {+ e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));+ } break;+ case SASS_COLOR: {+ e = SASS_MEMORY_NEW(Color_RGBA, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));+ } break;+ case SASS_STRING: {+ if (sass_string_is_quoted(v))+ e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));+ else {+ e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));+ }+ } break;+ case SASS_LIST: {+ List* l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));+ for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {+ l->append(c2ast(sass_list_get_value(v, i), traces, pstate));+ }+ l->is_bracketed(sass_list_get_is_bracketed(v));+ e = l;+ } break;+ case SASS_MAP: {+ Map* m = SASS_MEMORY_NEW(Map, pstate);+ for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {+ *m << std::make_pair(+ c2ast(sass_map_get_key(v, i), traces, pstate),+ c2ast(sass_map_get_value(v, i), traces, pstate));+ }+ e = m;+ } break;+ case SASS_NULL: {+ e = SASS_MEMORY_NEW(Null, pstate);+ } break;+ case SASS_ERROR: {+ error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces);+ } break;+ case SASS_WARNING: {+ error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces);+ } break;+ default: break;+ }+ return e;+ }++}
+ libsass/src/c2ast.hpp view
@@ -0,0 +1,14 @@+#ifndef SASS_C2AST_H+#define SASS_C2AST_H++#include "position.hpp"+#include "backtrace.hpp"+#include "ast_fwd_decl.hpp"++namespace Sass {++ Value* c2ast(union Sass_Value* v, Backtraces traces, ParserState pstate);++}++#endif
libsass/src/cencode.c view
@@ -46,9 +46,8 @@ result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4;- #ifndef _MSC_VER- /* fall through */- #endif+ /* fall through */+ case step_B: if (plainchar == plaintextend) {@@ -60,9 +59,8 @@ result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2;- #ifndef _MSC_VER- /* fall through */- #endif+ /* fall through */+ case step_C: if (plainchar == plaintextend) {
libsass/src/check_nesting.cpp view
@@ -1,31 +1,32 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <vector>-+#include "ast.hpp" #include "check_nesting.hpp" namespace Sass { CheckNesting::CheckNesting()- : parents(std::vector<Statement_Ptr>()),+ : parents(std::vector<Statement*>()), traces(std::vector<Backtrace>()), parent(0), current_mixin_definition(0) { } - void error(AST_Node_Ptr node, Backtraces traces, std::string msg) {+ void error(AST_Node* node, Backtraces traces, std::string msg) { traces.push_back(Backtrace(node->pstate())); throw Exception::InvalidSass(node->pstate(), traces, msg); } - Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)+ Statement* CheckNesting::visit_children(Statement* parent) {- Statement_Ptr old_parent = this->parent;+ Statement* old_parent = this->parent; - if (At_Root_Block_Ptr root = Cast<At_Root_Block>(parent)) {- std::vector<Statement_Ptr> old_parents = this->parents;- std::vector<Statement_Ptr> new_parents;+ if (At_Root_Block* root = Cast<At_Root_Block>(parent)) {+ std::vector<Statement*> old_parents = this->parents;+ std::vector<Statement*> new_parents; for (size_t i = 0, L = this->parents.size(); i < L; i++) {- Statement_Ptr p = this->parents.at(i);+ Statement* p = this->parents.at(i); if (!root->exclude_node(p)) { new_parents.push_back(p); }@@ -33,8 +34,8 @@ this->parents = new_parents; for (size_t i = this->parents.size(); i > 0; i--) {- Statement_Ptr p = 0;- Statement_Ptr gp = 0;+ Statement* p = 0;+ Statement* gp = 0; if (i > 0) p = this->parents.at(i - 1); if (i > 1) gp = this->parents.at(i - 2); @@ -44,8 +45,8 @@ } } - At_Root_Block_Ptr ar = Cast<At_Root_Block>(parent);- Block_Ptr ret = ar->block();+ At_Root_Block* ar = Cast<At_Root_Block>(parent);+ Block* ret = ar->block(); if (ret != NULL) { for (auto n : ret->elements()) {@@ -65,16 +66,16 @@ this->parents.push_back(parent); - Block_Ptr b = Cast<Block>(parent);+ Block* b = Cast<Block>(parent); - if (Trace_Ptr trace = Cast<Trace>(parent)) {+ if (Trace* trace = Cast<Trace>(parent)) { if (trace->type() == 'i') { this->traces.push_back(Backtrace(trace->pstate())); } } if (!b) {- if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {+ if (Has_Block* bb = Cast<Has_Block>(parent)) { b = bb->block(); } }@@ -88,7 +89,7 @@ this->parent = old_parent; this->parents.pop_back(); - if (Trace_Ptr trace = Cast<Trace>(parent)) {+ if (Trace* trace = Cast<Trace>(parent)) { if (trace->type() == 'i') { this->traces.pop_back(); }@@ -98,12 +99,12 @@ } - Statement_Ptr CheckNesting::operator()(Block_Ptr b)+ Statement* CheckNesting::operator()(Block* b) { return this->visit_children(b); } - Statement_Ptr CheckNesting::operator()(Definition_Ptr n)+ Statement* CheckNesting::operator()(Definition* n) { if (!this->should_visit(n)) return NULL; if (!is_mixin(n)) {@@ -111,7 +112,7 @@ return n; } - Definition_Ptr old_mixin_definition = this->current_mixin_definition;+ Definition* old_mixin_definition = this->current_mixin_definition; this->current_mixin_definition = n; visit_children(n);@@ -121,25 +122,18 @@ return n; } - Statement_Ptr CheckNesting::operator()(If_Ptr i)+ Statement* CheckNesting::operator()(If* i) { this->visit_children(i); - if (Block_Ptr b = Cast<Block>(i->alternative())) {+ if (Block* b = Cast<Block>(i->alternative())) { for (auto n : b->elements()) n->perform(this); } return i; } - Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)- {- Block_Ptr b1 = Cast<Block>(s);- Has_Block_Ptr b2 = Cast<Has_Block>(s);- return b1 || b2 ? visit_children(s) : s;- }-- bool CheckNesting::should_visit(Statement_Ptr node)+ bool CheckNesting::should_visit(Statement* node) { if (!this->parent) return true; @@ -149,7 +143,7 @@ if (is_charset(node)) { this->invalid_charset_parent(this->parent, node); } - if (Cast<Extension>(node))+ if (Cast<ExtendRule>(node)) { this->invalid_extend_parent(this->parent, node); } // if (Cast<Import>(node))@@ -164,7 +158,7 @@ if (this->is_function(this->parent)) { this->invalid_function_child(node); } - if (Declaration_Ptr d = Cast<Declaration>(node))+ if (Declaration* d = Cast<Declaration>(node)) { this->invalid_prop_parent(this->parent, node); this->invalid_value_child(d->value());@@ -179,14 +173,14 @@ return true; } - void CheckNesting::invalid_content_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_content_parent(Statement* parent, AST_Node* node) { if (!this->current_mixin_definition) { error(node, traces, "@content may only be used within a mixin."); } } - void CheckNesting::invalid_charset_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_charset_parent(Statement* parent, AST_Node* node) { if (!( is_root_node(parent)@@ -195,7 +189,7 @@ } } - void CheckNesting::invalid_extend_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_extend_parent(Statement* parent, AST_Node* node) { if (!( Cast<Ruleset>(parent) ||@@ -206,7 +200,7 @@ } } - // void CheckNesting::invalid_import_parent(Statement_Ptr parent, AST_Node_Ptr node)+ // void CheckNesting::invalid_import_parent(Statement* parent, AST_Node* node) // { // for (auto pp : this->parents) { // if (@@ -231,9 +225,9 @@ // } // } - void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_mixin_definition_parent(Statement* parent, AST_Node* node) {- for (Statement_Ptr pp : this->parents) {+ for (Statement* pp : this->parents) { if ( Cast<Each>(pp) || Cast<For>(pp) ||@@ -248,9 +242,9 @@ } } - void CheckNesting::invalid_function_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_function_parent(Statement* parent, AST_Node* node) {- for (Statement_Ptr pp : this->parents) {+ for (Statement* pp : this->parents) { if ( Cast<Each>(pp) || Cast<For>(pp) ||@@ -265,7 +259,7 @@ } } - void CheckNesting::invalid_function_child(Statement_Ptr child)+ void CheckNesting::invalid_function_child(Statement* child) { if (!( Cast<Each>(child) ||@@ -286,7 +280,7 @@ } } - void CheckNesting::invalid_prop_child(Statement_Ptr child)+ void CheckNesting::invalid_prop_child(Statement* child) { if (!( Cast<Each>(child) ||@@ -302,7 +296,7 @@ } } - void CheckNesting::invalid_prop_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_prop_parent(Statement* parent, AST_Node* node) { if (!( is_mixin(parent) ||@@ -316,13 +310,13 @@ } } - void CheckNesting::invalid_value_child(AST_Node_Ptr d)+ void CheckNesting::invalid_value_child(AST_Node* d) {- if (Map_Ptr m = Cast<Map>(d)) {+ if (Map* m = Cast<Map>(d)) { traces.push_back(Backtrace(m->pstate())); throw Exception::InvalidValue(traces, *m); }- if (Number_Ptr n = Cast<Number>(d)) {+ if (Number* n = Cast<Number>(d)) { if (!n->is_valid_css_unit()) { traces.push_back(Backtrace(n->pstate())); throw Exception::InvalidValue(traces, *n);@@ -333,14 +327,14 @@ } - void CheckNesting::invalid_return_parent(Statement_Ptr parent, AST_Node_Ptr node)+ void CheckNesting::invalid_return_parent(Statement* parent, AST_Node* node) { if (!this->is_function(parent)) { error(node, traces, "@return may only be used within a function."); } } - bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)+ bool CheckNesting::is_transparent_parent(Statement* parent, Statement* grandparent) { bool parent_bubbles = parent && parent->bubbles(); @@ -357,42 +351,43 @@ valid_bubble_node; } - bool CheckNesting::is_charset(Statement_Ptr n)+ bool CheckNesting::is_charset(Statement* n) {- Directive_Ptr d = Cast<Directive>(n);+ Directive* d = Cast<Directive>(n); return d && d->keyword() == "charset"; } - bool CheckNesting::is_mixin(Statement_Ptr n)+ bool CheckNesting::is_mixin(Statement* n) {- Definition_Ptr def = Cast<Definition>(n);+ Definition* def = Cast<Definition>(n); return def && def->type() == Definition::MIXIN; } - bool CheckNesting::is_function(Statement_Ptr n)+ bool CheckNesting::is_function(Statement* n) {- Definition_Ptr def = Cast<Definition>(n);+ Definition* def = Cast<Definition>(n); return def && def->type() == Definition::FUNCTION; } - bool CheckNesting::is_root_node(Statement_Ptr n)+ bool CheckNesting::is_root_node(Statement* n) { if (Cast<Ruleset>(n)) return false; - Block_Ptr b = Cast<Block>(n);+ Block* b = Cast<Block>(n); return b && b->is_root(); } - bool CheckNesting::is_at_root_node(Statement_Ptr n)+ bool CheckNesting::is_at_root_node(Statement* n) { return Cast<At_Root_Block>(n) != NULL; } - bool CheckNesting::is_directive_node(Statement_Ptr n)+ bool CheckNesting::is_directive_node(Statement* n) { return Cast<Directive>(n) || Cast<Import>(n) ||- Cast<Media_Block>(n) ||- Cast<Supports_Block>(n);+ Cast<MediaRule>(n) ||+ Cast<CssMediaRule>(n) ||+ Cast<Supports_Block>(n); } }
libsass/src/check_nesting.hpp view
@@ -1,63 +1,68 @@ #ifndef SASS_CHECK_NESTING_H #define SASS_CHECK_NESTING_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp" #include "ast.hpp" #include "operation.hpp"+#include <vector> namespace Sass { - class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> {+ class CheckNesting : public Operation_CRTP<Statement*, CheckNesting> { - std::vector<Statement_Ptr> parents;+ std::vector<Statement*> parents; Backtraces traces;- Statement_Ptr parent;- Definition_Ptr current_mixin_definition;+ Statement* parent;+ Definition* current_mixin_definition; - Statement_Ptr fallback_impl(Statement_Ptr);- Statement_Ptr before(Statement_Ptr);- Statement_Ptr visit_children(Statement_Ptr);+ Statement* before(Statement*);+ Statement* visit_children(Statement*); public: CheckNesting(); ~CheckNesting() { } - Statement_Ptr operator()(Block_Ptr);- Statement_Ptr operator()(Definition_Ptr);- Statement_Ptr operator()(If_Ptr);+ Statement* operator()(Block*);+ Statement* operator()(Definition*);+ Statement* operator()(If*); template <typename U>- Statement_Ptr fallback(U x) {- Statement_Ptr n = Cast<Statement>(x);- if (this->should_visit(n)) {- return fallback_impl(n);+ Statement* fallback(U x) {+ Statement* s = Cast<Statement>(x);+ if (s && this->should_visit(s)) {+ Block* b1 = Cast<Block>(s);+ Has_Block* b2 = Cast<Has_Block>(s);+ if (b1 || b2) return visit_children(s); }- return NULL;+ return s; } private:- void invalid_content_parent(Statement_Ptr, AST_Node_Ptr);- void invalid_charset_parent(Statement_Ptr, AST_Node_Ptr);- void invalid_extend_parent(Statement_Ptr, AST_Node_Ptr);- // void invalid_import_parent(Statement_Ptr);- void invalid_mixin_definition_parent(Statement_Ptr, AST_Node_Ptr);- void invalid_function_parent(Statement_Ptr, AST_Node_Ptr);+ void invalid_content_parent(Statement*, AST_Node*);+ void invalid_charset_parent(Statement*, AST_Node*);+ void invalid_extend_parent(Statement*, AST_Node*);+ // void invalid_import_parent(Statement*);+ void invalid_mixin_definition_parent(Statement*, AST_Node*);+ void invalid_function_parent(Statement*, AST_Node*); - void invalid_function_child(Statement_Ptr);- void invalid_prop_child(Statement_Ptr);- void invalid_prop_parent(Statement_Ptr, AST_Node_Ptr);- void invalid_return_parent(Statement_Ptr, AST_Node_Ptr);- void invalid_value_child(AST_Node_Ptr);+ void invalid_function_child(Statement*);+ void invalid_prop_child(Statement*);+ void invalid_prop_parent(Statement*, AST_Node*);+ void invalid_return_parent(Statement*, AST_Node*);+ void invalid_value_child(AST_Node*); - bool is_transparent_parent(Statement_Ptr, Statement_Ptr);+ bool is_transparent_parent(Statement*, Statement*); - bool should_visit(Statement_Ptr);+ bool should_visit(Statement*); - bool is_charset(Statement_Ptr);- bool is_mixin(Statement_Ptr);- bool is_function(Statement_Ptr);- bool is_root_node(Statement_Ptr);- bool is_at_root_node(Statement_Ptr);- bool is_directive_node(Statement_Ptr);+ bool is_charset(Statement*);+ bool is_mixin(Statement*);+ bool is_function(Statement*);+ bool is_root_node(Statement*);+ bool is_at_root_node(Statement*);+ bool is_directive_node(Statement*); }; }
libsass/src/color_maps.cpp view
@@ -1,6 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "color_maps.hpp"+#include "util_string.hpp" namespace Sass { @@ -159,158 +163,158 @@ namespace Colors { const ParserState color_table("[COLOR TABLE]");- const Color aliceblue(color_table, 240, 248, 255, 1);- const Color antiquewhite(color_table, 250, 235, 215, 1);- const Color cyan(color_table, 0, 255, 255, 1);- const Color aqua(color_table, 0, 255, 255, 1);- const Color aquamarine(color_table, 127, 255, 212, 1);- const Color azure(color_table, 240, 255, 255, 1);- const Color beige(color_table, 245, 245, 220, 1);- const Color bisque(color_table, 255, 228, 196, 1);- const Color black(color_table, 0, 0, 0, 1);- const Color blanchedalmond(color_table, 255, 235, 205, 1);- const Color blue(color_table, 0, 0, 255, 1);- const Color blueviolet(color_table, 138, 43, 226, 1);- const Color brown(color_table, 165, 42, 42, 1);- const Color burlywood(color_table, 222, 184, 135, 1);- const Color cadetblue(color_table, 95, 158, 160, 1);- const Color chartreuse(color_table, 127, 255, 0, 1);- const Color chocolate(color_table, 210, 105, 30, 1);- const Color coral(color_table, 255, 127, 80, 1);- const Color cornflowerblue(color_table, 100, 149, 237, 1);- const Color cornsilk(color_table, 255, 248, 220, 1);- const Color crimson(color_table, 220, 20, 60, 1);- const Color darkblue(color_table, 0, 0, 139, 1);- const Color darkcyan(color_table, 0, 139, 139, 1);- const Color darkgoldenrod(color_table, 184, 134, 11, 1);- const Color darkgray(color_table, 169, 169, 169, 1);- const Color darkgrey(color_table, 169, 169, 169, 1);- const Color darkgreen(color_table, 0, 100, 0, 1);- const Color darkkhaki(color_table, 189, 183, 107, 1);- const Color darkmagenta(color_table, 139, 0, 139, 1);- const Color darkolivegreen(color_table, 85, 107, 47, 1);- const Color darkorange(color_table, 255, 140, 0, 1);- const Color darkorchid(color_table, 153, 50, 204, 1);- const Color darkred(color_table, 139, 0, 0, 1);- const Color darksalmon(color_table, 233, 150, 122, 1);- const Color darkseagreen(color_table, 143, 188, 143, 1);- const Color darkslateblue(color_table, 72, 61, 139, 1);- const Color darkslategray(color_table, 47, 79, 79, 1);- const Color darkslategrey(color_table, 47, 79, 79, 1);- const Color darkturquoise(color_table, 0, 206, 209, 1);- const Color darkviolet(color_table, 148, 0, 211, 1);- const Color deeppink(color_table, 255, 20, 147, 1);- const Color deepskyblue(color_table, 0, 191, 255, 1);- const Color dimgray(color_table, 105, 105, 105, 1);- const Color dimgrey(color_table, 105, 105, 105, 1);- const Color dodgerblue(color_table, 30, 144, 255, 1);- const Color firebrick(color_table, 178, 34, 34, 1);- const Color floralwhite(color_table, 255, 250, 240, 1);- const Color forestgreen(color_table, 34, 139, 34, 1);- const Color magenta(color_table, 255, 0, 255, 1);- const Color fuchsia(color_table, 255, 0, 255, 1);- const Color gainsboro(color_table, 220, 220, 220, 1);- const Color ghostwhite(color_table, 248, 248, 255, 1);- const Color gold(color_table, 255, 215, 0, 1);- const Color goldenrod(color_table, 218, 165, 32, 1);- const Color gray(color_table, 128, 128, 128, 1);- const Color grey(color_table, 128, 128, 128, 1);- const Color green(color_table, 0, 128, 0, 1);- const Color greenyellow(color_table, 173, 255, 47, 1);- const Color honeydew(color_table, 240, 255, 240, 1);- const Color hotpink(color_table, 255, 105, 180, 1);- const Color indianred(color_table, 205, 92, 92, 1);- const Color indigo(color_table, 75, 0, 130, 1);- const Color ivory(color_table, 255, 255, 240, 1);- const Color khaki(color_table, 240, 230, 140, 1);- const Color lavender(color_table, 230, 230, 250, 1);- const Color lavenderblush(color_table, 255, 240, 245, 1);- const Color lawngreen(color_table, 124, 252, 0, 1);- const Color lemonchiffon(color_table, 255, 250, 205, 1);- const Color lightblue(color_table, 173, 216, 230, 1);- const Color lightcoral(color_table, 240, 128, 128, 1);- const Color lightcyan(color_table, 224, 255, 255, 1);- const Color lightgoldenrodyellow(color_table, 250, 250, 210, 1);- const Color lightgray(color_table, 211, 211, 211, 1);- const Color lightgrey(color_table, 211, 211, 211, 1);- const Color lightgreen(color_table, 144, 238, 144, 1);- const Color lightpink(color_table, 255, 182, 193, 1);- const Color lightsalmon(color_table, 255, 160, 122, 1);- const Color lightseagreen(color_table, 32, 178, 170, 1);- const Color lightskyblue(color_table, 135, 206, 250, 1);- const Color lightslategray(color_table, 119, 136, 153, 1);- const Color lightslategrey(color_table, 119, 136, 153, 1);- const Color lightsteelblue(color_table, 176, 196, 222, 1);- const Color lightyellow(color_table, 255, 255, 224, 1);- const Color lime(color_table, 0, 255, 0, 1);- const Color limegreen(color_table, 50, 205, 50, 1);- const Color linen(color_table, 250, 240, 230, 1);- const Color maroon(color_table, 128, 0, 0, 1);- const Color mediumaquamarine(color_table, 102, 205, 170, 1);- const Color mediumblue(color_table, 0, 0, 205, 1);- const Color mediumorchid(color_table, 186, 85, 211, 1);- const Color mediumpurple(color_table, 147, 112, 219, 1);- const Color mediumseagreen(color_table, 60, 179, 113, 1);- const Color mediumslateblue(color_table, 123, 104, 238, 1);- const Color mediumspringgreen(color_table, 0, 250, 154, 1);- const Color mediumturquoise(color_table, 72, 209, 204, 1);- const Color mediumvioletred(color_table, 199, 21, 133, 1);- const Color midnightblue(color_table, 25, 25, 112, 1);- const Color mintcream(color_table, 245, 255, 250, 1);- const Color mistyrose(color_table, 255, 228, 225, 1);- const Color moccasin(color_table, 255, 228, 181, 1);- const Color navajowhite(color_table, 255, 222, 173, 1);- const Color navy(color_table, 0, 0, 128, 1);- const Color oldlace(color_table, 253, 245, 230, 1);- const Color olive(color_table, 128, 128, 0, 1);- const Color olivedrab(color_table, 107, 142, 35, 1);- const Color orange(color_table, 255, 165, 0, 1);- const Color orangered(color_table, 255, 69, 0, 1);- const Color orchid(color_table, 218, 112, 214, 1);- const Color palegoldenrod(color_table, 238, 232, 170, 1);- const Color palegreen(color_table, 152, 251, 152, 1);- const Color paleturquoise(color_table, 175, 238, 238, 1);- const Color palevioletred(color_table, 219, 112, 147, 1);- const Color papayawhip(color_table, 255, 239, 213, 1);- const Color peachpuff(color_table, 255, 218, 185, 1);- const Color peru(color_table, 205, 133, 63, 1);- const Color pink(color_table, 255, 192, 203, 1);- const Color plum(color_table, 221, 160, 221, 1);- const Color powderblue(color_table, 176, 224, 230, 1);- const Color purple(color_table, 128, 0, 128, 1);- const Color red(color_table, 255, 0, 0, 1);- const Color rosybrown(color_table, 188, 143, 143, 1);- const Color royalblue(color_table, 65, 105, 225, 1);- const Color saddlebrown(color_table, 139, 69, 19, 1);- const Color salmon(color_table, 250, 128, 114, 1);- const Color sandybrown(color_table, 244, 164, 96, 1);- const Color seagreen(color_table, 46, 139, 87, 1);- const Color seashell(color_table, 255, 245, 238, 1);- const Color sienna(color_table, 160, 82, 45, 1);- const Color silver(color_table, 192, 192, 192, 1);- const Color skyblue(color_table, 135, 206, 235, 1);- const Color slateblue(color_table, 106, 90, 205, 1);- const Color slategray(color_table, 112, 128, 144, 1);- const Color slategrey(color_table, 112, 128, 144, 1);- const Color snow(color_table, 255, 250, 250, 1);- const Color springgreen(color_table, 0, 255, 127, 1);- const Color steelblue(color_table, 70, 130, 180, 1);- const Color tan(color_table, 210, 180, 140, 1);- const Color teal(color_table, 0, 128, 128, 1);- const Color thistle(color_table, 216, 191, 216, 1);- const Color tomato(color_table, 255, 99, 71, 1);- const Color turquoise(color_table, 64, 224, 208, 1);- const Color violet(color_table, 238, 130, 238, 1);- const Color wheat(color_table, 245, 222, 179, 1);- const Color white(color_table, 255, 255, 255, 1);- const Color whitesmoke(color_table, 245, 245, 245, 1);- const Color yellow(color_table, 255, 255, 0, 1);- const Color yellowgreen(color_table, 154, 205, 50, 1);- const Color rebeccapurple(color_table, 102, 51, 153, 1);- const Color transparent(color_table, 0, 0, 0, 0);+ const Color_RGBA aliceblue(color_table, 240, 248, 255, 1);+ const Color_RGBA antiquewhite(color_table, 250, 235, 215, 1);+ const Color_RGBA cyan(color_table, 0, 255, 255, 1);+ const Color_RGBA aqua(color_table, 0, 255, 255, 1);+ const Color_RGBA aquamarine(color_table, 127, 255, 212, 1);+ const Color_RGBA azure(color_table, 240, 255, 255, 1);+ const Color_RGBA beige(color_table, 245, 245, 220, 1);+ const Color_RGBA bisque(color_table, 255, 228, 196, 1);+ const Color_RGBA black(color_table, 0, 0, 0, 1);+ const Color_RGBA blanchedalmond(color_table, 255, 235, 205, 1);+ const Color_RGBA blue(color_table, 0, 0, 255, 1);+ const Color_RGBA blueviolet(color_table, 138, 43, 226, 1);+ const Color_RGBA brown(color_table, 165, 42, 42, 1);+ const Color_RGBA burlywood(color_table, 222, 184, 135, 1);+ const Color_RGBA cadetblue(color_table, 95, 158, 160, 1);+ const Color_RGBA chartreuse(color_table, 127, 255, 0, 1);+ const Color_RGBA chocolate(color_table, 210, 105, 30, 1);+ const Color_RGBA coral(color_table, 255, 127, 80, 1);+ const Color_RGBA cornflowerblue(color_table, 100, 149, 237, 1);+ const Color_RGBA cornsilk(color_table, 255, 248, 220, 1);+ const Color_RGBA crimson(color_table, 220, 20, 60, 1);+ const Color_RGBA darkblue(color_table, 0, 0, 139, 1);+ const Color_RGBA darkcyan(color_table, 0, 139, 139, 1);+ const Color_RGBA darkgoldenrod(color_table, 184, 134, 11, 1);+ const Color_RGBA darkgray(color_table, 169, 169, 169, 1);+ const Color_RGBA darkgrey(color_table, 169, 169, 169, 1);+ const Color_RGBA darkgreen(color_table, 0, 100, 0, 1);+ const Color_RGBA darkkhaki(color_table, 189, 183, 107, 1);+ const Color_RGBA darkmagenta(color_table, 139, 0, 139, 1);+ const Color_RGBA darkolivegreen(color_table, 85, 107, 47, 1);+ const Color_RGBA darkorange(color_table, 255, 140, 0, 1);+ const Color_RGBA darkorchid(color_table, 153, 50, 204, 1);+ const Color_RGBA darkred(color_table, 139, 0, 0, 1);+ const Color_RGBA darksalmon(color_table, 233, 150, 122, 1);+ const Color_RGBA darkseagreen(color_table, 143, 188, 143, 1);+ const Color_RGBA darkslateblue(color_table, 72, 61, 139, 1);+ const Color_RGBA darkslategray(color_table, 47, 79, 79, 1);+ const Color_RGBA darkslategrey(color_table, 47, 79, 79, 1);+ const Color_RGBA darkturquoise(color_table, 0, 206, 209, 1);+ const Color_RGBA darkviolet(color_table, 148, 0, 211, 1);+ const Color_RGBA deeppink(color_table, 255, 20, 147, 1);+ const Color_RGBA deepskyblue(color_table, 0, 191, 255, 1);+ const Color_RGBA dimgray(color_table, 105, 105, 105, 1);+ const Color_RGBA dimgrey(color_table, 105, 105, 105, 1);+ const Color_RGBA dodgerblue(color_table, 30, 144, 255, 1);+ const Color_RGBA firebrick(color_table, 178, 34, 34, 1);+ const Color_RGBA floralwhite(color_table, 255, 250, 240, 1);+ const Color_RGBA forestgreen(color_table, 34, 139, 34, 1);+ const Color_RGBA magenta(color_table, 255, 0, 255, 1);+ const Color_RGBA fuchsia(color_table, 255, 0, 255, 1);+ const Color_RGBA gainsboro(color_table, 220, 220, 220, 1);+ const Color_RGBA ghostwhite(color_table, 248, 248, 255, 1);+ const Color_RGBA gold(color_table, 255, 215, 0, 1);+ const Color_RGBA goldenrod(color_table, 218, 165, 32, 1);+ const Color_RGBA gray(color_table, 128, 128, 128, 1);+ const Color_RGBA grey(color_table, 128, 128, 128, 1);+ const Color_RGBA green(color_table, 0, 128, 0, 1);+ const Color_RGBA greenyellow(color_table, 173, 255, 47, 1);+ const Color_RGBA honeydew(color_table, 240, 255, 240, 1);+ const Color_RGBA hotpink(color_table, 255, 105, 180, 1);+ const Color_RGBA indianred(color_table, 205, 92, 92, 1);+ const Color_RGBA indigo(color_table, 75, 0, 130, 1);+ const Color_RGBA ivory(color_table, 255, 255, 240, 1);+ const Color_RGBA khaki(color_table, 240, 230, 140, 1);+ const Color_RGBA lavender(color_table, 230, 230, 250, 1);+ const Color_RGBA lavenderblush(color_table, 255, 240, 245, 1);+ const Color_RGBA lawngreen(color_table, 124, 252, 0, 1);+ const Color_RGBA lemonchiffon(color_table, 255, 250, 205, 1);+ const Color_RGBA lightblue(color_table, 173, 216, 230, 1);+ const Color_RGBA lightcoral(color_table, 240, 128, 128, 1);+ const Color_RGBA lightcyan(color_table, 224, 255, 255, 1);+ const Color_RGBA lightgoldenrodyellow(color_table, 250, 250, 210, 1);+ const Color_RGBA lightgray(color_table, 211, 211, 211, 1);+ const Color_RGBA lightgrey(color_table, 211, 211, 211, 1);+ const Color_RGBA lightgreen(color_table, 144, 238, 144, 1);+ const Color_RGBA lightpink(color_table, 255, 182, 193, 1);+ const Color_RGBA lightsalmon(color_table, 255, 160, 122, 1);+ const Color_RGBA lightseagreen(color_table, 32, 178, 170, 1);+ const Color_RGBA lightskyblue(color_table, 135, 206, 250, 1);+ const Color_RGBA lightslategray(color_table, 119, 136, 153, 1);+ const Color_RGBA lightslategrey(color_table, 119, 136, 153, 1);+ const Color_RGBA lightsteelblue(color_table, 176, 196, 222, 1);+ const Color_RGBA lightyellow(color_table, 255, 255, 224, 1);+ const Color_RGBA lime(color_table, 0, 255, 0, 1);+ const Color_RGBA limegreen(color_table, 50, 205, 50, 1);+ const Color_RGBA linen(color_table, 250, 240, 230, 1);+ const Color_RGBA maroon(color_table, 128, 0, 0, 1);+ const Color_RGBA mediumaquamarine(color_table, 102, 205, 170, 1);+ const Color_RGBA mediumblue(color_table, 0, 0, 205, 1);+ const Color_RGBA mediumorchid(color_table, 186, 85, 211, 1);+ const Color_RGBA mediumpurple(color_table, 147, 112, 219, 1);+ const Color_RGBA mediumseagreen(color_table, 60, 179, 113, 1);+ const Color_RGBA mediumslateblue(color_table, 123, 104, 238, 1);+ const Color_RGBA mediumspringgreen(color_table, 0, 250, 154, 1);+ const Color_RGBA mediumturquoise(color_table, 72, 209, 204, 1);+ const Color_RGBA mediumvioletred(color_table, 199, 21, 133, 1);+ const Color_RGBA midnightblue(color_table, 25, 25, 112, 1);+ const Color_RGBA mintcream(color_table, 245, 255, 250, 1);+ const Color_RGBA mistyrose(color_table, 255, 228, 225, 1);+ const Color_RGBA moccasin(color_table, 255, 228, 181, 1);+ const Color_RGBA navajowhite(color_table, 255, 222, 173, 1);+ const Color_RGBA navy(color_table, 0, 0, 128, 1);+ const Color_RGBA oldlace(color_table, 253, 245, 230, 1);+ const Color_RGBA olive(color_table, 128, 128, 0, 1);+ const Color_RGBA olivedrab(color_table, 107, 142, 35, 1);+ const Color_RGBA orange(color_table, 255, 165, 0, 1);+ const Color_RGBA orangered(color_table, 255, 69, 0, 1);+ const Color_RGBA orchid(color_table, 218, 112, 214, 1);+ const Color_RGBA palegoldenrod(color_table, 238, 232, 170, 1);+ const Color_RGBA palegreen(color_table, 152, 251, 152, 1);+ const Color_RGBA paleturquoise(color_table, 175, 238, 238, 1);+ const Color_RGBA palevioletred(color_table, 219, 112, 147, 1);+ const Color_RGBA papayawhip(color_table, 255, 239, 213, 1);+ const Color_RGBA peachpuff(color_table, 255, 218, 185, 1);+ const Color_RGBA peru(color_table, 205, 133, 63, 1);+ const Color_RGBA pink(color_table, 255, 192, 203, 1);+ const Color_RGBA plum(color_table, 221, 160, 221, 1);+ const Color_RGBA powderblue(color_table, 176, 224, 230, 1);+ const Color_RGBA purple(color_table, 128, 0, 128, 1);+ const Color_RGBA red(color_table, 255, 0, 0, 1);+ const Color_RGBA rosybrown(color_table, 188, 143, 143, 1);+ const Color_RGBA royalblue(color_table, 65, 105, 225, 1);+ const Color_RGBA saddlebrown(color_table, 139, 69, 19, 1);+ const Color_RGBA salmon(color_table, 250, 128, 114, 1);+ const Color_RGBA sandybrown(color_table, 244, 164, 96, 1);+ const Color_RGBA seagreen(color_table, 46, 139, 87, 1);+ const Color_RGBA seashell(color_table, 255, 245, 238, 1);+ const Color_RGBA sienna(color_table, 160, 82, 45, 1);+ const Color_RGBA silver(color_table, 192, 192, 192, 1);+ const Color_RGBA skyblue(color_table, 135, 206, 235, 1);+ const Color_RGBA slateblue(color_table, 106, 90, 205, 1);+ const Color_RGBA slategray(color_table, 112, 128, 144, 1);+ const Color_RGBA slategrey(color_table, 112, 128, 144, 1);+ const Color_RGBA snow(color_table, 255, 250, 250, 1);+ const Color_RGBA springgreen(color_table, 0, 255, 127, 1);+ const Color_RGBA steelblue(color_table, 70, 130, 180, 1);+ const Color_RGBA tan(color_table, 210, 180, 140, 1);+ const Color_RGBA teal(color_table, 0, 128, 128, 1);+ const Color_RGBA thistle(color_table, 216, 191, 216, 1);+ const Color_RGBA tomato(color_table, 255, 99, 71, 1);+ const Color_RGBA turquoise(color_table, 64, 224, 208, 1);+ const Color_RGBA violet(color_table, 238, 130, 238, 1);+ const Color_RGBA wheat(color_table, 245, 222, 179, 1);+ const Color_RGBA white(color_table, 255, 255, 255, 1);+ const Color_RGBA whitesmoke(color_table, 245, 245, 245, 1);+ const Color_RGBA yellow(color_table, 255, 255, 0, 1);+ const Color_RGBA yellowgreen(color_table, 154, 205, 50, 1);+ const Color_RGBA rebeccapurple(color_table, 102, 51, 153, 1);+ const Color_RGBA transparent(color_table, 0, 0, 0, 0); } - const std::map<const int, const char*> colors_to_names {+ static const auto* const colors_to_names = new std::unordered_map<int, const char*> { { 240 * 0x10000 + 248 * 0x100 + 255, ColorNames::aliceblue }, { 250 * 0x10000 + 235 * 0x100 + 215, ColorNames::antiquewhite }, { 0 * 0x10000 + 255 * 0x100 + 255, ColorNames::cyan },@@ -452,7 +456,7 @@ { 102 * 0x10000 + 51 * 0x100 + 153, ColorNames::rebeccapurple } }; - const std::map<const char*, Color_Ptr_Const, map_cmp_str> names_to_colors+ static const auto *const names_to_colors = new std::unordered_map<std::string, const Color_RGBA*> { { ColorNames::aliceblue, &Colors::aliceblue }, { ColorNames::antiquewhite, &Colors::antiquewhite },@@ -605,31 +609,31 @@ { ColorNames::transparent, &Colors::transparent } }; - Color_Ptr_Const name_to_color(const char* key)+ const Color_RGBA* name_to_color(const char* key) { return name_to_color(std::string(key)); } - Color_Ptr_Const name_to_color(const std::string& key)+ const Color_RGBA* name_to_color(const std::string& key) { // case insensitive lookup. See #2462- std::string lower{key};- std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);+ std::string lower = key;+ Util::ascii_str_tolower(&lower); - auto p = names_to_colors.find(lower.c_str());- if (p != names_to_colors.end()) {+ auto p = names_to_colors->find(lower);+ if (p != names_to_colors->end()) { return p->second; }- return 0;+ return nullptr; } const char* color_to_name(const int key) {- auto p = colors_to_names.find(key);- if (p != colors_to_names.end()) {+ auto p = colors_to_names->find(key);+ if (p != colors_to_names->end()) { return p->second; }- return 0;+ return nullptr; } const char* color_to_name(const double key)@@ -637,7 +641,7 @@ return color_to_name((int)key); } - const char* color_to_name(const Color& c)+ const char* color_to_name(const Color_RGBA& c) { double key = c.r() * 0x10000 + c.g() * 0x100
libsass/src/color_maps.hpp view
@@ -7,14 +7,6 @@ namespace Sass { - struct map_cmp_str- {- bool operator()(char const *a, char const *b) const- {- return std::strcmp(a, b) < 0;- }- };- namespace ColorNames { extern const char aliceblue[];@@ -169,161 +161,161 @@ } namespace Colors {- extern const Color aliceblue;- extern const Color antiquewhite;- extern const Color cyan;- extern const Color aqua;- extern const Color aquamarine;- extern const Color azure;- extern const Color beige;- extern const Color bisque;- extern const Color black;- extern const Color blanchedalmond;- extern const Color blue;- extern const Color blueviolet;- extern const Color brown;- extern const Color burlywood;- extern const Color cadetblue;- extern const Color chartreuse;- extern const Color chocolate;- extern const Color coral;- extern const Color cornflowerblue;- extern const Color cornsilk;- extern const Color crimson;- extern const Color darkblue;- extern const Color darkcyan;- extern const Color darkgoldenrod;- extern const Color darkgray;- extern const Color darkgrey;- extern const Color darkgreen;- extern const Color darkkhaki;- extern const Color darkmagenta;- extern const Color darkolivegreen;- extern const Color darkorange;- extern const Color darkorchid;- extern const Color darkred;- extern const Color darksalmon;- extern const Color darkseagreen;- extern const Color darkslateblue;- extern const Color darkslategray;- extern const Color darkslategrey;- extern const Color darkturquoise;- extern const Color darkviolet;- extern const Color deeppink;- extern const Color deepskyblue;- extern const Color dimgray;- extern const Color dimgrey;- extern const Color dodgerblue;- extern const Color firebrick;- extern const Color floralwhite;- extern const Color forestgreen;- extern const Color magenta;- extern const Color fuchsia;- extern const Color gainsboro;- extern const Color ghostwhite;- extern const Color gold;- extern const Color goldenrod;- extern const Color gray;- extern const Color grey;- extern const Color green;- extern const Color greenyellow;- extern const Color honeydew;- extern const Color hotpink;- extern const Color indianred;- extern const Color indigo;- extern const Color ivory;- extern const Color khaki;- extern const Color lavender;- extern const Color lavenderblush;- extern const Color lawngreen;- extern const Color lemonchiffon;- extern const Color lightblue;- extern const Color lightcoral;- extern const Color lightcyan;- extern const Color lightgoldenrodyellow;- extern const Color lightgray;- extern const Color lightgrey;- extern const Color lightgreen;- extern const Color lightpink;- extern const Color lightsalmon;- extern const Color lightseagreen;- extern const Color lightskyblue;- extern const Color lightslategray;- extern const Color lightslategrey;- extern const Color lightsteelblue;- extern const Color lightyellow;- extern const Color lime;- extern const Color limegreen;- extern const Color linen;- extern const Color maroon;- extern const Color mediumaquamarine;- extern const Color mediumblue;- extern const Color mediumorchid;- extern const Color mediumpurple;- extern const Color mediumseagreen;- extern const Color mediumslateblue;- extern const Color mediumspringgreen;- extern const Color mediumturquoise;- extern const Color mediumvioletred;- extern const Color midnightblue;- extern const Color mintcream;- extern const Color mistyrose;- extern const Color moccasin;- extern const Color navajowhite;- extern const Color navy;- extern const Color oldlace;- extern const Color olive;- extern const Color olivedrab;- extern const Color orange;- extern const Color orangered;- extern const Color orchid;- extern const Color palegoldenrod;- extern const Color palegreen;- extern const Color paleturquoise;- extern const Color palevioletred;- extern const Color papayawhip;- extern const Color peachpuff;- extern const Color peru;- extern const Color pink;- extern const Color plum;- extern const Color powderblue;- extern const Color purple;- extern const Color red;- extern const Color rosybrown;- extern const Color royalblue;- extern const Color saddlebrown;- extern const Color salmon;- extern const Color sandybrown;- extern const Color seagreen;- extern const Color seashell;- extern const Color sienna;- extern const Color silver;- extern const Color skyblue;- extern const Color slateblue;- extern const Color slategray;- extern const Color slategrey;- extern const Color snow;- extern const Color springgreen;- extern const Color steelblue;- extern const Color tan;- extern const Color teal;- extern const Color thistle;- extern const Color tomato;- extern const Color turquoise;- extern const Color violet;- extern const Color wheat;- extern const Color white;- extern const Color whitesmoke;- extern const Color yellow;- extern const Color yellowgreen;- extern const Color rebeccapurple;- extern const Color transparent;+ extern const Color_RGBA aliceblue;+ extern const Color_RGBA antiquewhite;+ extern const Color_RGBA cyan;+ extern const Color_RGBA aqua;+ extern const Color_RGBA aquamarine;+ extern const Color_RGBA azure;+ extern const Color_RGBA beige;+ extern const Color_RGBA bisque;+ extern const Color_RGBA black;+ extern const Color_RGBA blanchedalmond;+ extern const Color_RGBA blue;+ extern const Color_RGBA blueviolet;+ extern const Color_RGBA brown;+ extern const Color_RGBA burlywood;+ extern const Color_RGBA cadetblue;+ extern const Color_RGBA chartreuse;+ extern const Color_RGBA chocolate;+ extern const Color_RGBA coral;+ extern const Color_RGBA cornflowerblue;+ extern const Color_RGBA cornsilk;+ extern const Color_RGBA crimson;+ extern const Color_RGBA darkblue;+ extern const Color_RGBA darkcyan;+ extern const Color_RGBA darkgoldenrod;+ extern const Color_RGBA darkgray;+ extern const Color_RGBA darkgrey;+ extern const Color_RGBA darkgreen;+ extern const Color_RGBA darkkhaki;+ extern const Color_RGBA darkmagenta;+ extern const Color_RGBA darkolivegreen;+ extern const Color_RGBA darkorange;+ extern const Color_RGBA darkorchid;+ extern const Color_RGBA darkred;+ extern const Color_RGBA darksalmon;+ extern const Color_RGBA darkseagreen;+ extern const Color_RGBA darkslateblue;+ extern const Color_RGBA darkslategray;+ extern const Color_RGBA darkslategrey;+ extern const Color_RGBA darkturquoise;+ extern const Color_RGBA darkviolet;+ extern const Color_RGBA deeppink;+ extern const Color_RGBA deepskyblue;+ extern const Color_RGBA dimgray;+ extern const Color_RGBA dimgrey;+ extern const Color_RGBA dodgerblue;+ extern const Color_RGBA firebrick;+ extern const Color_RGBA floralwhite;+ extern const Color_RGBA forestgreen;+ extern const Color_RGBA magenta;+ extern const Color_RGBA fuchsia;+ extern const Color_RGBA gainsboro;+ extern const Color_RGBA ghostwhite;+ extern const Color_RGBA gold;+ extern const Color_RGBA goldenrod;+ extern const Color_RGBA gray;+ extern const Color_RGBA grey;+ extern const Color_RGBA green;+ extern const Color_RGBA greenyellow;+ extern const Color_RGBA honeydew;+ extern const Color_RGBA hotpink;+ extern const Color_RGBA indianred;+ extern const Color_RGBA indigo;+ extern const Color_RGBA ivory;+ extern const Color_RGBA khaki;+ extern const Color_RGBA lavender;+ extern const Color_RGBA lavenderblush;+ extern const Color_RGBA lawngreen;+ extern const Color_RGBA lemonchiffon;+ extern const Color_RGBA lightblue;+ extern const Color_RGBA lightcoral;+ extern const Color_RGBA lightcyan;+ extern const Color_RGBA lightgoldenrodyellow;+ extern const Color_RGBA lightgray;+ extern const Color_RGBA lightgrey;+ extern const Color_RGBA lightgreen;+ extern const Color_RGBA lightpink;+ extern const Color_RGBA lightsalmon;+ extern const Color_RGBA lightseagreen;+ extern const Color_RGBA lightskyblue;+ extern const Color_RGBA lightslategray;+ extern const Color_RGBA lightslategrey;+ extern const Color_RGBA lightsteelblue;+ extern const Color_RGBA lightyellow;+ extern const Color_RGBA lime;+ extern const Color_RGBA limegreen;+ extern const Color_RGBA linen;+ extern const Color_RGBA maroon;+ extern const Color_RGBA mediumaquamarine;+ extern const Color_RGBA mediumblue;+ extern const Color_RGBA mediumorchid;+ extern const Color_RGBA mediumpurple;+ extern const Color_RGBA mediumseagreen;+ extern const Color_RGBA mediumslateblue;+ extern const Color_RGBA mediumspringgreen;+ extern const Color_RGBA mediumturquoise;+ extern const Color_RGBA mediumvioletred;+ extern const Color_RGBA midnightblue;+ extern const Color_RGBA mintcream;+ extern const Color_RGBA mistyrose;+ extern const Color_RGBA moccasin;+ extern const Color_RGBA navajowhite;+ extern const Color_RGBA navy;+ extern const Color_RGBA oldlace;+ extern const Color_RGBA olive;+ extern const Color_RGBA olivedrab;+ extern const Color_RGBA orange;+ extern const Color_RGBA orangered;+ extern const Color_RGBA orchid;+ extern const Color_RGBA palegoldenrod;+ extern const Color_RGBA palegreen;+ extern const Color_RGBA paleturquoise;+ extern const Color_RGBA palevioletred;+ extern const Color_RGBA papayawhip;+ extern const Color_RGBA peachpuff;+ extern const Color_RGBA peru;+ extern const Color_RGBA pink;+ extern const Color_RGBA plum;+ extern const Color_RGBA powderblue;+ extern const Color_RGBA purple;+ extern const Color_RGBA red;+ extern const Color_RGBA rosybrown;+ extern const Color_RGBA royalblue;+ extern const Color_RGBA saddlebrown;+ extern const Color_RGBA salmon;+ extern const Color_RGBA sandybrown;+ extern const Color_RGBA seagreen;+ extern const Color_RGBA seashell;+ extern const Color_RGBA sienna;+ extern const Color_RGBA silver;+ extern const Color_RGBA skyblue;+ extern const Color_RGBA slateblue;+ extern const Color_RGBA slategray;+ extern const Color_RGBA slategrey;+ extern const Color_RGBA snow;+ extern const Color_RGBA springgreen;+ extern const Color_RGBA steelblue;+ extern const Color_RGBA tan;+ extern const Color_RGBA teal;+ extern const Color_RGBA thistle;+ extern const Color_RGBA tomato;+ extern const Color_RGBA turquoise;+ extern const Color_RGBA violet;+ extern const Color_RGBA wheat;+ extern const Color_RGBA white;+ extern const Color_RGBA whitesmoke;+ extern const Color_RGBA yellow;+ extern const Color_RGBA yellowgreen;+ extern const Color_RGBA rebeccapurple;+ extern const Color_RGBA transparent; } - Color_Ptr_Const name_to_color(const char*);- Color_Ptr_Const name_to_color(const std::string&);+ const Color_RGBA* name_to_color(const char*);+ const Color_RGBA* name_to_color(const std::string&); const char* color_to_name(const int);- const char* color_to_name(const Color&);+ const char* color_to_name(const Color_RGBA&); const char* color_to_name(const double); }
libsass/src/constants.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "constants.hpp" namespace Sass {@@ -18,6 +21,15 @@ extern const unsigned long Specificity_Pseudo = 1000; extern const unsigned long Specificity_ID = 1000000; + extern const int UnificationOrder_Element = 1;+ extern const int UnificationOrder_Id = 2;+ extern const int UnificationOrder_Class = 2;+ extern const int UnificationOrder_Attribute = 3;+ extern const int UnificationOrder_PseudoClass = 4;+ extern const int UnificationOrder_Wrapped = 5;+ extern const int UnificationOrder_PseudoElement = 6;+ extern const int UnificationOrder_Placeholder = 7;+ // sass keywords extern const char at_root_kwd[] = "@at-root"; extern const char import_kwd[] = "@import";@@ -33,6 +45,7 @@ extern const char for_kwd[] = "@for"; extern const char from_kwd[] = "from"; extern const char to_kwd[] = "to";+ extern const char of_kwd[] = "of"; extern const char through_kwd[] = "through"; extern const char each_kwd[] = "@each"; extern const char in_kwd[] = "in";@@ -116,6 +129,9 @@ extern const char true_kwd[] = "true"; extern const char false_kwd[] = "false"; + // definition keywords+ extern const char using_kwd[] = "using";+ // miscellaneous punctuation and delimiters extern const char percent_str[] = "%"; extern const char empty_str[] = "";@@ -145,6 +161,10 @@ // constants for uri parsing (RFC 3986 Appendix A.) extern const char uri_chars[] = ":;/?!%&#@|[]{}'`^\"*+-.,_=~"; extern const char real_uri_chars[] = "#%&";++ extern const char selector_combinator_child[] = ">";+ extern const char selector_combinator_general[] = "~";+ extern const char selector_combinator_adjacent[] = "+"; // some specific constant character classes // they must be static to be useable by lexer
libsass/src/constants.hpp view
@@ -18,6 +18,16 @@ extern const unsigned long Specificity_Pseudo; extern const unsigned long Specificity_ID; + // Selector unification order;+ extern const int UnificationOrder_Element;+ extern const int UnificationOrder_Id;+ extern const int UnificationOrder_Class;+ extern const int UnificationOrder_Attribute;+ extern const int UnificationOrder_PseudoClass;+ extern const int UnificationOrder_Wrapped;+ extern const int UnificationOrder_PseudoElement;+ extern const int UnificationOrder_Placeholder;+ // sass keywords extern const char at_root_kwd[]; extern const char import_kwd[];@@ -33,6 +43,7 @@ extern const char for_kwd[]; extern const char from_kwd[]; extern const char to_kwd[];+ extern const char of_kwd[]; extern const char through_kwd[]; extern const char each_kwd[]; extern const char in_kwd[];@@ -117,6 +128,9 @@ extern const char true_kwd[]; extern const char false_kwd[]; + // definition keywords+ extern const char using_kwd[];+ // miscellaneous punctuation and delimiters extern const char percent_str[]; extern const char empty_str[];@@ -147,6 +161,11 @@ // constants for uri parsing (RFC 3986 Appendix A.) extern const char uri_chars[]; extern const char real_uri_chars[];++ // constants for selector combinators+ extern const char selector_combinator_child[];+ extern const char selector_combinator_general[];+ extern const char selector_combinator_adjacent[]; // some specific constant character classes // they must be static to be useable by lexer
libsass/src/context.cpp view
@@ -1,34 +1,22 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <string>-#include <cstdlib>-#include <cstring>-#include <iomanip>-#include <sstream>-#include <iostream>- #include "ast.hpp"-#include "util.hpp"-#include "sass.h"++#include "remove_placeholders.hpp"+#include "sass_functions.hpp"+#include "check_nesting.hpp"+#include "fn_selectors.hpp"+#include "fn_strings.hpp"+#include "fn_numbers.hpp"+#include "fn_colors.hpp"+#include "fn_miscs.hpp"+#include "fn_lists.hpp"+#include "fn_maps.hpp" #include "context.hpp"-#include "plugins.hpp"-#include "constants.hpp"-#include "parser.hpp"-#include "file.hpp"-#include "inspect.hpp"-#include "output.hpp" #include "expand.hpp"-#include "eval.hpp"-#include "check_nesting.hpp"+#include "parser.hpp" #include "cssize.hpp"-#include "listize.hpp"-#include "extend.hpp"-#include "remove_placeholders.hpp"-#include "functions.hpp"-#include "sass_functions.hpp"-#include "backtrace.hpp"-#include "sass2scss.h"-#include "prelexer.hpp"-#include "emitter.hpp" namespace Sass { using namespace Constants;@@ -40,23 +28,17 @@ static std::string safe_input(const char* in_path) {- // enforce some safe defaults- // used to create relative file links- std::string safe_path(in_path ? in_path : "");- return safe_path == "" ? "stdin" : safe_path;+ if (in_path == nullptr || in_path[0] == '\0') return "stdin";+ return in_path; } - static std::string safe_output(const char* out_path, const std::string& input_path = "")+ static std::string safe_output(const char* out_path, std::string input_path) {- std::string safe_path(out_path ? out_path : "");- // maybe we can extract an output path from input path- if (safe_path == "" && input_path != "") {- int lastindex = static_cast<int>(input_path.find_last_of("."));- return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";+ if (out_path == nullptr || out_path[0] == '\0') {+ if (input_path.empty()) return "stdout";+ return input_path.substr(0, input_path.find_last_of(".")) + ".css"; }- // enforce some safe defaults- // used to create relative file links- return safe_path == "" ? "stdout" : safe_path;+ return out_path; } Context::Context(struct Sass_Context& c_ctx)@@ -71,10 +53,10 @@ strings(), resources(), sheets(),- subset_map(), import_stack(), callee_stack(), traces(),+ extender(Extender::NORMAL, traces), c_compiler(NULL), c_headers (std::vector<Sass_Importer_Entry>()),@@ -150,7 +132,7 @@ } // clear inner structures (vectors) and input source resources.clear(); import_stack.clear();- subset_map.clear(), sheets.clear();+ sheets.clear(); } Data_Context::~Data_Context()@@ -382,7 +364,7 @@ } - void Context::import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path) {+ void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) { ParserState pstate(imp->pstate()); std::string imp_path(unquote(load_path));@@ -395,17 +377,17 @@ // if (protocol.compare("file") && true) { } } - // add urls (protocol other than file) and urls without procotol to `urls` member+ // add urls (protocol other than file) and urls without protocol to `urls` member // ToDo: if ctx_path is already a file resource, we should not add it here? if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") { imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path)); } else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {- String_Constant_Ptr loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));+ String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path)); Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc); Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate); loc_args->append(loc_arg);- Function_Call_Ptr new_url = SASS_MEMORY_NEW(Function_Call, pstate, "url", loc_args);+ Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, std::string("url"), loc_args); imp->urls().push_back(new_url); } else {@@ -421,7 +403,7 @@ // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet- bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one)+ bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one) { // unique counter size_t count = 0;@@ -519,7 +501,7 @@ OutputBuffer emitted = emitter.get_buffer(); // should we append a source map url? if (!c_options.omit_source_map_url) {- // generate an embeded source map+ // generate an embedded source map if (c_options.source_map_embed) { emitted.buffer += linefeed; emitted.buffer += format_embedded_source_map();@@ -556,7 +538,7 @@ { // check if entry file is given- if (input_path.empty()) return 0;+ if (input_path.empty()) return {}; // create absolute path from input filename // ToDo: this should be resolved via custom importers@@ -602,7 +584,7 @@ { // check if source string is given- if (!source_c_str) return 0;+ if (!source_c_str) return {}; // convert indented sass syntax if(c_options.is_indented_syntax_src) {@@ -639,17 +621,15 @@ return compile(); } -- // parse root block from includes Block_Obj Context::compile() { // abort if there is no data- if (resources.size() == 0) return 0;+ if (resources.size() == 0) return {}; // get root block from the first style sheet Block_Obj root = sheets.at(entry_path).root; // abort on invalid root- if (root.isNull()) return 0;+ if (root.isNull()) return {}; Env global; // create root environment // register built-in functions on env register_built_in_functions(*this, &global);@@ -668,23 +648,23 @@ } // expand and eval the tree root = expand(root);++ Extension unsatisfied;+ // check that all extends were used+ if (extender.checkForUnsatisfiedExtends(unsatisfied)) {+ throw Exception::UnsatisfiedExtend(traces, unsatisfied);+ }+ // check nesting check_nesting(root); // merge and bubble certain rules root = cssize(root);- // should we extend something?- if (!subset_map.empty()) {- // create crtp visitor object- Extend extend(subset_map);- extend.setEval(expand.eval);- // extend tree nodes- extend(root);- } // clean up by removing empty placeholders // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders; root->perform(&remove_placeholders);+ // return processed tree return root; }@@ -732,14 +712,14 @@ void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) {- Definition_Ptr def = make_native_function(sig, f, ctx);+ Definition* def = make_native_function(sig, f, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; } void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env) {- Definition_Ptr def = make_native_function(sig, f, ctx);+ Definition* def = make_native_function(sig, f, ctx); std::stringstream ss; ss << def->name() << "[f]" << arity; def->environment(env);@@ -748,11 +728,11 @@ void register_overload_stub(Context& ctx, std::string name, Env* env) {- Definition_Ptr stub = SASS_MEMORY_NEW(Definition,+ Definition* stub = SASS_MEMORY_NEW(Definition, ParserState("[built-in function]"), 0, name,- 0,+ {}, 0, true); (*env)[name + "[f]"] = stub;@@ -872,7 +852,7 @@ } void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr) {- Definition_Ptr def = make_c_function(descr, ctx);+ Definition* def = make_c_function(descr, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; }
libsass/src/context.hpp view
@@ -1,40 +1,32 @@ #ifndef SASS_CONTEXT_H #define SASS_CONTEXT_H -#include <string>-#include <vector>-#include <map>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp" + #define BUFFERSIZE 255 #include "b64/encode.h" -#include "ast_fwd_decl.hpp"-#include "kwd_arg_macros.hpp"-#include "ast_fwd_decl.hpp" #include "sass_context.hpp"-#include "environment.hpp"-#include "source_map.hpp"-#include "subset_map.hpp"-#include "backtrace.hpp"-#include "output.hpp"+#include "stylesheet.hpp" #include "plugins.hpp"-#include "file.hpp"---struct Sass_Function;+#include "output.hpp" namespace Sass { class Context { public:- void import_url (Import_Ptr imp, std::string load_path, const std::string& ctx_path);- bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp)+ void import_url (Import* imp, std::string load_path, const std::string& ctx_path);+ bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); };- bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp)+ bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); }; private:- bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import_Ptr imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);+ bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true); public: const std::string CWD;@@ -46,16 +38,16 @@ // generic ast node garbage container // used to avoid possible circular refs- std::vector<AST_Node_Obj> ast_gc;+ CallStack ast_gc; // resources add under our control // these are guaranteed to be freed std::vector<char*> strings; std::vector<Resource> resources; std::map<const std::string, StyleSheet> sheets;- Subset_Map subset_map;- std::vector<Sass_Import_Entry> import_stack;+ ImporterStack import_stack; std::vector<Sass_Callee> callee_stack; std::vector<Backtrace> traces;+ Extender extender; struct Sass_Compiler* c_compiler; @@ -67,10 +59,6 @@ std::vector<std::string> plugin_paths; // relative paths to load plugins std::vector<std::string> include_paths; // lookup paths for includes---- void apply_custom_headers(Block_Obj root, const char* path, ParserState pstate);
libsass/src/cssize.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include <typeinfo> #include <vector>@@ -9,18 +12,17 @@ namespace Sass { Cssize::Cssize(Context& ctx)- : ctx(ctx),- traces(ctx.traces),- block_stack(std::vector<Block_Ptr>()),- p_stack(std::vector<Statement_Ptr>())+ : traces(ctx.traces),+ block_stack(BlockStack()),+ p_stack(std::vector<Statement*>()) { } - Statement_Ptr Cssize::parent()+ Statement* Cssize::parent() { return p_stack.size() ? p_stack.back() : block_stack.front(); } - Block_Ptr Cssize::operator()(Block_Ptr b)+ Block* Cssize::operator()(Block* b) { Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); // bb->tabs(b->tabs());@@ -30,7 +32,7 @@ return bb.detach(); } - Statement_Ptr Cssize::operator()(Trace_Ptr t)+ Statement* Cssize::operator()(Trace* t) { traces.push_back(Backtrace(t->pstate())); auto result = t->block()->perform(this);@@ -38,11 +40,11 @@ return result; } - Statement_Ptr Cssize::operator()(Declaration_Ptr d)+ Statement* Cssize::operator()(Declaration* d) { String_Obj property = Cast<String>(d->property()); - if (Declaration_Ptr dd = Cast<Declaration>(parent())) {+ if (Declaration* dd = Cast<Declaration>(parent())) { String_Obj parent_property = Cast<String>(dd->property()); property = SASS_MEMORY_NEW(String_Constant, d->property()->pstate(),@@ -78,7 +80,7 @@ return 0; } - Statement_Ptr Cssize::operator()(Directive_Ptr r)+ Statement* Cssize::operator()(Directive* r) { if (!r->block() || !r->block()->length()) return r; @@ -110,10 +112,10 @@ } - Block_Ptr result = SASS_MEMORY_NEW(Block, rr->pstate());+ Block* result = SASS_MEMORY_NEW(Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) {- Directive_Ptr empty_node = Cast<Directive>(rr);+ Directive* empty_node = Cast<Directive>(rr); empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate())); result->append(empty_node); }@@ -128,7 +130,7 @@ return result; } - Statement_Ptr Cssize::operator()(Keyframe_Rule_Ptr r)+ Statement* Cssize::operator()(Keyframe_Rule* r) { if (!r->block() || !r->block()->length()) return r; @@ -140,14 +142,14 @@ return debubble(rr->block(), rr); } - Statement_Ptr Cssize::operator()(Ruleset_Ptr r)+ Statement* Cssize::operator()(Ruleset* r) { p_stack.push_back(r); // this can return a string schema // string schema is not a statement! // r->block() is already a string schema- // and that is comming from propset expand- Block_Ptr bb = operator()(r->block());+ // and that is coming from propset expand+ Block* bb = operator()(r->block()); // this should protect us (at least a bit) from our mess // fixing this properly is harder that it should be ... if (Cast<Statement>(bb) == NULL) {@@ -167,10 +169,10 @@ } Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());- Block_Ptr rules = SASS_MEMORY_NEW(Block, rr->block()->pstate());+ Block* rules = SASS_MEMORY_NEW(Block, rr->block()->pstate()); for (size_t i = 0, L = rr->block()->length(); i < L; i++) {- Statement_Ptr s = rr->block()->at(i);+ Statement* s = rr->block()->at(i); if (bubblable(s)) rules->append(s); if (!bubblable(s)) props->append(s); }@@ -183,14 +185,14 @@ for (size_t i = 0, L = rules->length(); i < L; i++) {- Statement_Ptr stm = rules->at(i);+ Statement* stm = rules->at(i); stm->tabs(stm->tabs() + 1); } rules->unshift(rr); } - Block_Ptr ptr = rules;+ Block* ptr = rules; rules = debubble(rules); void* lp = ptr; void* rp = rules;@@ -207,25 +209,28 @@ return rules; } - Statement_Ptr Cssize::operator()(Null_Ptr m)+ Statement* Cssize::operator()(Null* m) { return 0; } - Statement_Ptr Cssize::operator()(Media_Block_Ptr m)+ Statement* Cssize::operator()(CssMediaRule* m) { if (parent()->statement_type() == Statement::RULESET)- { return bubble(m); }+ {+ return bubble(m);+ } if (parent()->statement_type() == Statement::MEDIA)- { return SASS_MEMORY_NEW(Bubble, m->pstate(), m); }+ {+ return SASS_MEMORY_NEW(Bubble, m->pstate(), m);+ } p_stack.push_back(m); - Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block,- m->pstate(),- m->media_queries(),- operator()(m->block()));+ CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());+ mm->concat(m->elements());+ mm->block(operator()(m->block())); mm->tabs(m->tabs()); p_stack.pop_back();@@ -233,7 +238,7 @@ return debubble(mm->block(), mm); } - Statement_Ptr Cssize::operator()(Supports_Block_Ptr m)+ Statement* Cssize::operator()(Supports_Block* m) { if (!m->block()->length()) { return m; }@@ -254,17 +259,17 @@ return debubble(mm->block(), mm); } - Statement_Ptr Cssize::operator()(At_Root_Block_Ptr m)+ Statement* Cssize::operator()(At_Root_Block* m) { bool tmp = false; for (size_t i = 0, L = p_stack.size(); i < L; ++i) {- Statement_Ptr s = p_stack[i];+ Statement* s = p_stack[i]; tmp |= m->exclude_node(s); } if (!tmp && m->block()) {- Block_Ptr bb = operator()(m->block());+ Block* bb = operator()(m->block()); for (size_t i = 0, L = bb->length(); i < L; ++i) { // (bb->elements())[i]->tabs(m->tabs()); Statement_Obj stm = bb->at(i);@@ -282,9 +287,9 @@ return bubble(m); } - Statement_Ptr Cssize::bubble(Directive_Ptr m)+ Statement* Cssize::bubble(Directive* m) {- Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+ Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs());@@ -299,16 +304,16 @@ wrapper_block); if (m->value()) mm->value(m->value()); - Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);+ Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } - Statement_Ptr Cssize::bubble(At_Root_Block_Ptr m)+ Statement* Cssize::bubble(At_Root_Block* m) { if (!m || !m->block()) return NULL;- Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());+ Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent()));- Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+ Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); if (new_rule) { new_rule->block(bb); new_rule->tabs(this->parent()->tabs());@@ -316,74 +321,74 @@ wrapper_block->append(new_rule); } - At_Root_Block_Ptr mm = SASS_MEMORY_NEW(At_Root_Block,+ At_Root_Block* mm = SASS_MEMORY_NEW(At_Root_Block, m->pstate(), wrapper_block, m->expression());- Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);+ Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } - Statement_Ptr Cssize::bubble(Supports_Block_Ptr m)+ Statement* Cssize::bubble(Supports_Block* m) { Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); - Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());- Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,+ Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+ Ruleset* new_rule = SASS_MEMORY_NEW(Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); new_rule->block()->concat(m->block()); - Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+ Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); wrapper_block->append(new_rule);- Supports_Block_Ptr mm = SASS_MEMORY_NEW(Supports_Block,+ Supports_Block* mm = SASS_MEMORY_NEW(Supports_Block, m->pstate(), m->condition(), wrapper_block); mm->tabs(m->tabs()); - Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);+ Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } - Statement_Ptr Cssize::bubble(Media_Block_Ptr m)+ Statement* Cssize::bubble(CssMediaRule* m) { Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); - Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());- Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset,- parent->pstate(),- parent->selector(),- bb);+ Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());+ Ruleset* new_rule = SASS_MEMORY_NEW(Ruleset,+ parent->pstate(),+ parent->selector(),+ bb); new_rule->tabs(parent->tabs()); new_rule->block()->concat(m->block()); - Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());+ Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); wrapper_block->append(new_rule);- Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block,- m->pstate(),- m->media_queries(),- wrapper_block);+ CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule,+ m->pstate(),+ wrapper_block);+ mm->concat(m->elements()); mm->tabs(m->tabs()); return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); } - bool Cssize::bubblable(Statement_Ptr s)+ bool Cssize::bubblable(Statement* s) { return Cast<Ruleset>(s) || s->bubbles(); } - Block_Ptr Cssize::flatten(Block_Ptr b)+ Block* Cssize::flatten(const Block* b) {- Block_Ptr result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root());+ Block* result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root()); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Ptr ss = b->at(i);- if (Block_Ptr bb = Cast<Block>(ss)) {+ Statement* ss = b->at(i);+ if (const Block* bb = Cast<Block>(ss)) { Block_Obj bs = flatten(bb); for (size_t j = 0, K = bs->length(); j < K; ++j) { result->append(bs->at(j));@@ -396,7 +401,7 @@ return result; } - std::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block_Ptr b)+ std::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block* b) { std::vector<std::pair<bool, Block_Obj>> results; @@ -411,7 +416,7 @@ } else {- Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());+ Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate()); wrapper_block->append(value); results.push_back(std::make_pair(key, wrapper_block)); }@@ -419,9 +424,9 @@ return results; } - Block_Ptr Cssize::debubble(Block_Ptr children, Statement_Ptr parent)+ Block* Cssize::debubble(Block* children, Statement* parent) {- Has_Block_Obj previous_parent = 0;+ Has_Block_Obj previous_parent; std::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children); Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate()); @@ -437,7 +442,7 @@ previous_parent->block()->concat(slice); } else {- previous_parent = Cast<Has_Block>(SASS_MEMORY_COPY(parent));+ previous_parent = SASS_MEMORY_COPY(parent); previous_parent->block(slice); previous_parent->tabs(parent->tabs()); @@ -448,37 +453,25 @@ for (size_t j = 0, K = slice->length(); j < K; ++j) {- Statement_Ptr ss;+ Statement_Obj ss; Statement_Obj stm = slice->at(j); // this has to go now here (too bad) Bubble_Obj node = Cast<Bubble>(stm);- Media_Block_Ptr m1 = NULL;- Media_Block_Ptr m2 = NULL;- if (parent) m1 = Cast<Media_Block>(parent);- if (node) m2 = Cast<Media_Block>(node->node());- if (!parent ||- parent->statement_type() != Statement::MEDIA ||- node->node()->statement_type() != Statement::MEDIA ||- (m1 && m2 && *m1->media_queries() == *m2->media_queries())- )- {- ss = node->node();- }- else- {- List_Obj mq = merge_media_queries(- Cast<Media_Block>(node->node()),- Cast<Media_Block>(parent)- );- if (!mq->length()) continue;- if (Media_Block* b = Cast<Media_Block>(node->node())) {- b->media_queries(mq);- }++ CssMediaRule* rule1 = NULL;+ CssMediaRule* rule2 = NULL;+ if (parent) rule1 = Cast<CssMediaRule>(parent);+ if (node) rule2 = Cast<CssMediaRule>(node->node());+ if (rule1 || rule2) { ss = node->node(); } - if (!ss) continue;+ ss = node->node(); + if (!ss) {+ continue;+ }+ ss->tabs(ss->tabs() + node->tabs()); ss->group_end(node->group_end()); @@ -493,11 +486,11 @@ children->length(), children->is_root()); - Block_Ptr wrapper = flatten(bb);+ Block* wrapper = flatten(bb); wrapper_block->append(wrapper); if (wrapper->length()) {- previous_parent = NULL;+ previous_parent = {}; } if (wrapper_block) {@@ -509,16 +502,11 @@ return flatten(result); } - Statement_Ptr Cssize::fallback_impl(AST_Node_Ptr n)- {- return static_cast<Statement_Ptr>(n);- }-- void Cssize::append_block(Block_Ptr b, Block_Ptr cur)+ void Cssize::append_block(Block* b, Block* cur) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj ith = b->at(i)->perform(this);- if (Block_Ptr bb = Cast<Block>(ith)) {+ if (Block_Obj bb = Cast<Block>(ith)) { for (size_t j = 0, K = bb->length(); j < K; ++j) { cur->append(bb->at(j)); }@@ -529,78 +517,4 @@ } } - List_Ptr Cssize::merge_media_queries(Media_Block_Ptr m1, Media_Block_Ptr m2)- {- List_Ptr qq = SASS_MEMORY_NEW(List,- m1->media_queries()->pstate(),- m1->media_queries()->length(),- SASS_COMMA);-- for (size_t i = 0, L = m1->media_queries()->length(); i < L; i++) {- for (size_t j = 0, K = m2->media_queries()->length(); j < K; j++) {- Expression_Obj l1 = m1->media_queries()->at(i);- Expression_Obj l2 = m2->media_queries()->at(j);- Media_Query_Ptr mq1 = Cast<Media_Query>(l1);- Media_Query_Ptr mq2 = Cast<Media_Query>(l2);- Media_Query_Ptr mq = merge_media_query(mq1, mq2);- if (mq) qq->append(mq);- }- }-- return qq;- }--- Media_Query_Ptr Cssize::merge_media_query(Media_Query_Ptr mq1, Media_Query_Ptr mq2)- {-- std::string type;- std::string mod;-- std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");- std::string t1 = mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : "";- std::string m2 = std::string(mq2->is_restricted() ? "only" : mq2->is_negated() ? "not" : "");- std::string t2 = mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : "";--- if (t1.empty()) t1 = t2;- if (t2.empty()) t2 = t1;-- if ((m1 == "not") ^ (m2 == "not")) {- if (t1 == t2) {- return 0;- }- type = m1 == "not" ? t2 : t1;- mod = m1 == "not" ? m2 : m1;- }- else if (m1 == "not" && m2 == "not") {- if (t1 != t2) {- return 0;- }- type = t1;- mod = "not";- }- else if (t1 != t2) {- return 0;- } else {- type = t1;- mod = m1.empty() ? m2 : m1;- }-- Media_Query_Ptr mm = SASS_MEMORY_NEW(Media_Query,- mq1->pstate(),- 0,- mq1->length() + mq2->length(),- mod == "not",- mod == "only");-- if (!type.empty()) {- mm->media_type(SASS_MEMORY_NEW(String_Quoted, mq1->pstate(), type));- }-- mm->concat(mq2);- mm->concat(mq1);-- return mm;- } }
libsass/src/cssize.hpp view
@@ -10,66 +10,60 @@ struct Backtrace; - class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> {+ class Cssize : public Operation_CRTP<Statement*, Cssize> { - Context& ctx; Backtraces& traces;- std::vector<Block_Ptr> block_stack;- std::vector<Statement_Ptr> p_stack;-- Statement_Ptr fallback_impl(AST_Node_Ptr n);+ BlockStack block_stack;+ std::vector<Statement*> p_stack; public: Cssize(Context&); ~Cssize() { } - Selector_List_Ptr selector();-- Block_Ptr operator()(Block_Ptr);- Statement_Ptr operator()(Ruleset_Ptr);- // Statement_Ptr operator()(Bubble_Ptr);- Statement_Ptr operator()(Media_Block_Ptr);- Statement_Ptr operator()(Supports_Block_Ptr);- Statement_Ptr operator()(At_Root_Block_Ptr);- Statement_Ptr operator()(Directive_Ptr);- Statement_Ptr operator()(Keyframe_Rule_Ptr);- Statement_Ptr operator()(Trace_Ptr);- Statement_Ptr operator()(Declaration_Ptr);- // Statement_Ptr operator()(Assignment_Ptr);- // Statement_Ptr operator()(Import_Ptr);- // Statement_Ptr operator()(Import_Stub_Ptr);- // Statement_Ptr operator()(Warning_Ptr);- // Statement_Ptr operator()(Error_Ptr);- // Statement_Ptr operator()(Comment_Ptr);- // Statement_Ptr operator()(If_Ptr);- // Statement_Ptr operator()(For_Ptr);- // Statement_Ptr operator()(Each_Ptr);- // Statement_Ptr operator()(While_Ptr);- // Statement_Ptr operator()(Return_Ptr);- // Statement_Ptr operator()(Extension_Ptr);- // Statement_Ptr operator()(Definition_Ptr);- // Statement_Ptr operator()(Mixin_Call_Ptr);- // Statement_Ptr operator()(Content_Ptr);- Statement_Ptr operator()(Null_Ptr);-- Statement_Ptr parent();- std::vector<std::pair<bool, Block_Obj>> slice_by_bubble(Block_Ptr);- Statement_Ptr bubble(Directive_Ptr);- Statement_Ptr bubble(At_Root_Block_Ptr);- Statement_Ptr bubble(Media_Block_Ptr);- Statement_Ptr bubble(Supports_Block_Ptr);+ Block* operator()(Block*);+ Statement* operator()(Ruleset*);+ // Statement* operator()(Bubble*);+ Statement* operator()(CssMediaRule*);+ Statement* operator()(Supports_Block*);+ Statement* operator()(At_Root_Block*);+ Statement* operator()(Directive*);+ Statement* operator()(Keyframe_Rule*);+ Statement* operator()(Trace*);+ Statement* operator()(Declaration*);+ // Statement* operator()(Assignment*);+ // Statement* operator()(Import*);+ // Statement* operator()(Import_Stub*);+ // Statement* operator()(Warning*);+ // Statement* operator()(Error*);+ // Statement* operator()(Comment*);+ // Statement* operator()(If*);+ // Statement* operator()(For*);+ // Statement* operator()(Each*);+ // Statement* operator()(While*);+ // Statement* operator()(Return*);+ // Statement* operator()(ExtendRule*);+ // Statement* operator()(Definition*);+ // Statement* operator()(Mixin_Call*);+ // Statement* operator()(Content*);+ Statement* operator()(Null*); - Block_Ptr debubble(Block_Ptr children, Statement_Ptr parent = 0);- Block_Ptr flatten(Block_Ptr);- bool bubblable(Statement_Ptr);+ Statement* parent();+ std::vector<std::pair<bool, Block_Obj>> slice_by_bubble(Block*);+ Statement* bubble(Directive*);+ Statement* bubble(At_Root_Block*);+ Statement* bubble(CssMediaRule*);+ Statement* bubble(Supports_Block*); - List_Ptr merge_media_queries(Media_Block_Ptr, Media_Block_Ptr);- Media_Query_Ptr merge_media_query(Media_Query_Ptr, Media_Query_Ptr);+ Block* debubble(Block* children, Statement* parent = 0);+ Block* flatten(const Block*);+ bool bubblable(Statement*); + // generic fallback template <typename U>- Statement_Ptr fallback(U x) { return fallback_impl(x); }+ Statement* fallback(U x)+ { return Cast<Statement>(x); } - void append_block(Block_Ptr, Block_Ptr);+ void append_block(Block*, Block*); }; }
+ libsass/src/dart_helpers.hpp view
@@ -0,0 +1,199 @@+#ifndef SASS_DART_HELPERS_H+#define SASS_DART_HELPERS_H++#include <vector>+#include <utility>+#include <iterator>+#include <functional>++namespace Sass {++ // ##########################################################################+ // Flatten `vector<vector<T>>` to `vector<T>`+ // ##########################################################################+ template <class T>+ T flatten(const std::vector<T>& all)+ {+ T flattened;+ for (const auto& sub : all) {+ std::copy(std::begin(sub), std::end(sub),+ std::back_inserter(flattened));+ }+ return flattened;+ }++ // ##########################################################################+ // Expands each element of this Iterable into zero or more elements.+ // Calls a function on every element and ads all results to flat array+ // ##########################################################################+ // Equivalent to dart `cnt.any`+ // Pass additional closure variables to `fn`+ template <class T, class U, typename ...Args>+ T expand(const T& cnt, U fn, Args... args) {+ T flattened;+ for (const auto& sub : cnt) {+ auto rv = fn(sub, args...);+ flattened.insert(flattened.end(),+ rv.begin(), rv.end());+ }+ return flattened;+ }++ // ##########################################################################+ // ##########################################################################+ template <class T>+ T flattenInner(const std::vector<T>& vec)+ {+ T outer;+ for (const auto& sub : vec) {+ outer.emplace_back(std::move(flatten(sub)));+ }+ return outer;+ }+ // EO flattenInner++ // ##########################################################################+ // Equivalent to dart `cnt.any`+ // Pass additional closure variables to `fn`+ // ##########################################################################+ template <class T, class U, typename ...Args>+ bool hasAny(const T& cnt, U fn, Args... args) {+ for (const auto& sub : cnt) {+ if (fn(sub, args...)) {+ return true;+ }+ }+ return false;+ }+ // EO hasAny++ // ##########################################################################+ // Equivalent to dart `cnt.take(len).any`+ // Pass additional closure variables to `fn`+ // ##########################################################################+ template <class T, class U, typename ...Args>+ bool hasSubAny(const T& cnt, size_t len, U fn, Args... args) {+ for (size_t i = 0; i < len; i++) {+ if (fn(cnt[i], args...)) {+ return true;+ }+ }+ return false;+ }++ // ##########################################################################+ // Default predicate for lcs algorithm+ // ##########################################################################+ template <class T>+ inline bool lcsIdentityCmp(const T& X, const T& Y, T& result)+ {+ // Assert equality+ if (!ObjEqualityFn(X, Y)) {+ return false;+ }+ // Store in reference+ result = X;+ // Return success+ return true;+ }+ // EO lcsIdentityCmp++ // ##########################################################################+ // Longest common subsequence with predicate+ // ##########################################################################+ template <class T>+ std::vector<T> lcs(+ const std::vector<T>& X, const std::vector<T>& Y,+ bool(*select)(const T&, const T&, T&) = lcsIdentityCmp<T>)+ {++ std::size_t m = X.size(), mm = X.size() + 1;+ std::size_t n = Y.size(), nn = Y.size() + 1;++ if (m == 0) return {};+ if (n == 0) return {};++ // MSVC does not support variable-length arrays+ // To circumvent, allocate one array on the heap+ // Then use a macro to access via double index+ // e.g. `size_t L[m][n]` is supported by gcc+ size_t* len = new size_t[mm * nn + 1];+ bool* acc = new bool[mm * nn + 1];+ T* res = new T[mm * nn + 1];++ #define LEN(x, y) len[(x) * nn + (y)]+ #define ACC(x, y) acc[(x) * nn + (y)]+ #define RES(x, y) res[(x) * nn + (y)]++ /* Following steps build L[m+1][n+1] in bottom up fashion. Note+ that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1] */+ for (size_t i = 0; i <= m; i++) {+ for (size_t j = 0; j <= n; j++) {+ if (i == 0 || j == 0)+ LEN(i, j) = 0;+ else {+ ACC(i - 1, j - 1) = select(X[i - 1], Y[j - 1], RES(i - 1, j - 1));+ if (ACC(i - 1, j - 1))+ LEN(i, j) = LEN(i - 1, j - 1) + 1;+ else+ LEN(i, j) = std::max(LEN(i - 1, j), LEN(i, j - 1));+ }+ }+ }++ // Following code is used to print LCS+ std::vector<T> lcs;+ std::size_t index = LEN(m, n);+ lcs.reserve(index);++ // Start from the right-most-bottom-most corner+ // and one by one store objects in lcs[]+ std::size_t i = m, j = n;+ while (i > 0 && j > 0) {++ // If current objects in X[] and Y are same,+ // then current object is part of LCS+ if (ACC(i - 1, j - 1))+ {+ // Put the stored object in result+ // Note: we push instead of unshift+ // Note: reverse the vector later+ // ToDo: is deque more performant?+ lcs.push_back(RES(i - 1, j - 1));+ // reduce values of i, j and index+ i -= 1; j -= 1; index -= 1;+ }++ // If not same, then find the larger of two and+ // go in the direction of larger value+ else if (LEN(i - 1, j) > LEN(i, j - 1)) {+ i--;+ }+ else {+ j--;+ }++ }++ // reverse now as we used push_back+ std::reverse(lcs.begin(), lcs.end());++ // Delete temp memory on heap+ delete[] len;+ delete[] acc;+ delete[] res;++ #undef LEN+ #undef ACC+ #undef RES++ return lcs;+ }+ // EO lcs++ // ##########################################################################+ // ##########################################################################++}++#endif
libsass/src/debugger.hpp view
@@ -1,29 +1,288 @@ #ifndef SASS_DEBUGGER_H #define SASS_DEBUGGER_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <queue>+#include <vector> #include <string> #include <sstream>-#include "node.hpp"+#include "ast.hpp" #include "ast_fwd_decl.hpp"+#include "extension.hpp" +#include "ordered_map.hpp"+ using namespace Sass; -inline void debug_ast(AST_Node_Ptr node, std::string ind = "", Env* env = 0);+inline void debug_ast(AST_Node* node, std::string ind = "", Env* env = 0); -inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {- debug_ast(const_cast<AST_Node*>(node), ind, env);+inline std::string debug_vec(const AST_Node* node) {+ if (node == NULL) return "null";+ else return node->to_string(); } -inline void debug_sources_set(ComplexSelectorSet& set, std::string ind = "")-{- if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";- for(auto const &pair : set) {- debug_ast(pair, ind + "");- // debug_ast(set[pair], ind + "first: ");+inline std::string debug_dude(std::vector<std::vector<int>> vec) {+ std::stringstream out;+ out << "{";+ bool joinOut = false;+ for (auto ct : vec) {+ if (joinOut) out << ", ";+ joinOut = true;+ out << "{";+ bool joinIn = false;+ for (auto nr : ct) {+ if (joinIn) out << ", ";+ joinIn = true;+ out << nr;+ }+ out << "}"; }- if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+ out << "}";+ return out.str(); } +inline std::string debug_vec(std::string& str) {+ return str;+}++inline std::string debug_vec(Extension& ext) {+ std::stringstream out;+ out << debug_vec(ext.extender);+ out << " {@extend ";+ out << debug_vec(ext.target);+ if (ext.isOptional) {+ out << " !optional";+ }+ out << "}";+ return out.str();+}++template <class T>+inline std::string debug_vec(std::vector<T> vec) {+ std::stringstream out;+ out << "[";+ for (size_t i = 0; i < vec.size(); i += 1) {+ if (i > 0) out << ", ";+ out << debug_vec(vec[i]);+ }+ out << "]";+ return out.str();+}++template <class T>+inline std::string debug_vec(std::queue<T> vec) {+ std::stringstream out;+ out << "{";+ for (size_t i = 0; i < vec.size(); i += 1) {+ if (i > 0) out << ", ";+ out << debug_vec(vec[i]);+ }+ out << "}";+ return out.str();+}++template <class T, class U, class O>+inline std::string debug_vec(std::map<T, U, O> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(it->first) // string (key)+ << ": "+ << debug_vec(it->second); // string's value+ joinit = true;+ }+ out << "}";+ return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_vec(const ordered_map<T, U, O, V>& vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(*it); // string (key)+ // << debug_vec(it->second); // string's value+ joinit = true;+ }+ out << "}";+ return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_vec(std::unordered_map<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(it->first) // string (key)+ << ": "+ << debug_vec(it->second); // string's value+ joinit = true;+ }+ out << "}";+ return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_keys(std::unordered_map<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(it->first); // string (key)+ joinit = true;+ }+ out << "}";+ return out.str();+}++inline std::string debug_vec(ExtListSelSet& vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(*it); // string (key)+ joinit = true;+ }+ out << "}";+ return out.str();+}++/*+template <class T, class U, class O, class V>+inline std::string debug_values(tsl::ordered_map<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(const_cast<U&>(it->second)); // string's value+ joinit = true;+ }+ out << "}";+ return out.str();+}+ +template <class T, class U, class O, class V>+inline std::string debug_vec(tsl::ordered_map<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(it->first) // string (key)+ << ": "+ << debug_vec(const_cast<U&>(it->second)); // string's value+ joinit = true;+ }+ out << "}";+ return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_vals(tsl::ordered_map<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(const_cast<U&>(it->second)); // string's value+ joinit = true;+ }+ out << "}";+ return out.str();+}++template <class T, class U, class O, class V>+inline std::string debug_keys(tsl::ordered_map<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto it = vec.begin(); it != vec.end(); it++)+ {+ if (joinit) out << ", ";+ out << debug_vec(it->first);+ joinit = true;+ }+ out << "}";+ return out.str();+}+*/++template <class T, class U>+inline std::string debug_vec(std::set<T, U> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto item : vec) {+ if (joinit) out << ", ";+ out << debug_vec(item);+ joinit = true;+ }+ out << "}";+ return out.str();+}++/*+template <class T, class U, class O, class V>+inline std::string debug_vec(tsl::ordered_set<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto item : vec) {+ if (joinit) out << ", ";+ out << debug_vec(item);+ joinit = true;+ }+ out << "}";+ return out.str();+}+*/++template <class T, class U, class O, class V>+inline std::string debug_vec(std::unordered_set<T, U, O, V> vec) {+ std::stringstream out;+ out << "{";+ bool joinit = false;+ for (auto item : vec) {+ if (joinit) out << ", ";+ out << debug_vec(item);+ joinit = true;+ }+ out << "}";+ return out.str();+}++inline std::string debug_bool(bool val) {+ return val ? "true" : "false";+}+inline std::string debug_vec(ExtSmplSelSet* node) {+ if (node == NULL) return "null";+ else return debug_vec(*node);+}++inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {+ debug_ast(const_cast<AST_Node*>(node), ind, env);+}+ inline std::string str_replace(std::string str, const std::string& oldStr, const std::string& newStr) { size_t pos = 0;@@ -48,12 +307,12 @@ return is.str(); } -inline std::string pstate_source_position(AST_Node_Ptr node)+inline std::string pstate_source_position(AST_Node* node) { std::stringstream str; Position start(node->pstate()); Position end(start + node->pstate().offset);- str << (start.file == std::string::npos ? -1 : start.file)+ str << (start.file == std::string::npos ? 99999999 : start.file) << "@[" << start.line << ":" << start.column << "]" << "-[" << end.line << ":" << end.column << "]"; #ifdef DEBUG_SHARED_PTR@@ -64,203 +323,163 @@ return str.str(); } -inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)+inline void debug_ast(AST_Node* node, std::string ind, Env* env) { if (node == 0) return; if (ind == "") std::cerr << "####################################################################\n"; if (Cast<Bubble>(node)) {- Bubble_Ptr bubble = Cast<Bubble>(node);+ Bubble* bubble = Cast<Bubble>(node); std::cerr << ind << "Bubble " << bubble; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl; debug_ast(bubble->node(), ind + " ", env); } else if (Cast<Trace>(node)) {- Trace_Ptr trace = Cast<Trace>(node);+ Trace* trace = Cast<Trace>(node); std::cerr << ind << "Trace " << trace; std::cerr << " (" << pstate_source_position(node) << ")" << " [name:" << trace->name() << ", type: " << trace->type() << "]" << std::endl; debug_ast(trace->block(), ind + " ", env); } else if (Cast<At_Root_Block>(node)) {- At_Root_Block_Ptr root_block = Cast<At_Root_Block>(node);+ At_Root_Block* root_block = Cast<At_Root_Block>(node); std::cerr << ind << "At_Root_Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << root_block->tabs(); std::cerr << std::endl; debug_ast(root_block->expression(), ind + ":", env); debug_ast(root_block->block(), ind + " ", env);- } else if (Cast<Selector_List>(node)) {- Selector_List_Ptr selector = Cast<Selector_List>(node);- std::cerr << ind << "Selector_List " << selector;+ } else if (Cast<SelectorList>(node)) {+ SelectorList* selector = Cast<SelectorList>(node);+ std::cerr << ind << "SelectorList " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";- std::cerr << " [@media:" << selector->media_block() << "]";- std::cerr << (selector->is_invisible() ? " [INVISIBLE]": " -");- std::cerr << (selector->has_placeholder() ? " [PLACEHOLDER]": " -");- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -");+ std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -");+ std::cerr << (selector->has_real_parent_ref() ? " [real-parent]": " -"); std::cerr << std::endl;- debug_ast(selector->schema(), ind + "#{} "); - for(const Complex_Selector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }+ for(const ComplexSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); } -// } else if (Cast<Expression>(node)) {-// Expression_Ptr expression = Cast<Expression>(node);-// std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl;+ } else if (Cast<ComplexSelector>(node)) {+ ComplexSelector* selector = Cast<ComplexSelector>(node);+ std::cerr << ind << "ComplexSelector " << selector+ << " (" << pstate_source_position(node) << ")"+ << " <" << selector->hash() << ">"+ << " [" << (selector->chroots() ? "CHROOT" : "CONNECT") << "]"+ << " [length:" << longToHex(selector->length()) << "]"+ << " [weight:" << longToHex(selector->specificity()) << "]"+ << (selector->is_invisible() ? " [is_invisible]" : " -")+ << (selector->isInvisible() ? " [isInvisible]" : " -")+ << (selector->hasPreLineFeed() ? " [hasPreLineFeed]" : " -") - } else if (Cast<Parent_Selector>(node)) {- Parent_Selector_Ptr selector = Cast<Parent_Selector>(node);- std::cerr << ind << "Parent_Selector " << selector;-// if (selector->not_selector()) cerr << " [in_declaration]";- std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " <" << selector->hash() << ">";- std::cerr << " [" << (selector->is_real_parent_ref() ? "REAL" : "FAKE") << "]";- std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;-// debug_ast(selector->selector(), ind + "->", env);+ // << (selector->is_invisible() ? " [INVISIBLE]": " -")+ // << (selector->has_placeholder() ? " [PLACEHOLDER]": " -")+ // << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_real_parent_ref() ? " [real parent]": " -")+ // << (selector->has_line_feed() ? " [line-feed]": " -")+ // << (selector->has_line_break() ? " [line-break]": " -")+ << " -- \n"; - } else if (Cast<Complex_Selector>(node)) {- Complex_Selector_Ptr selector = Cast<Complex_Selector>(node);- std::cerr << ind << "Complex_Selector " << selector+ for(const SelectorComponentObj& i : selector->elements()) { debug_ast(i, ind + " ", env); }++ } else if (Cast<SelectorCombinator>(node)) {+ SelectorCombinator* selector = Cast<SelectorCombinator>(node);+ std::cerr << ind << "SelectorCombinator " << selector << " (" << pstate_source_position(node) << ")" << " <" << selector->hash() << ">"- << " [length:" << longToHex(selector->length()) << "]" << " [weight:" << longToHex(selector->specificity()) << "]"- << " [@media:" << selector->media_block() << "]"- << (selector->is_invisible() ? " [INVISIBLE]": " -")- << (selector->has_placeholder() ? " [PLACEHOLDER]": " -")- << (selector->is_optional() ? " [is_optional]": " -")- << (selector->has_parent_ref() ? " [has parent]": " -")- << (selector->has_line_feed() ? " [line-feed]": " -")- << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_real_parent_ref() ? " [real parent]": " -") << " -- ";+ std::string del; switch (selector->combinator()) {- case Complex_Selector::PARENT_OF: del = ">"; break;- case Complex_Selector::PRECEDES: del = "~"; break;- case Complex_Selector::ADJACENT_TO: del = "+"; break;- case Complex_Selector::ANCESTOR_OF: del = " "; break;- case Complex_Selector::REFERENCE: del = "//"; break;+ case SelectorCombinator::CHILD: del = ">"; break;+ case SelectorCombinator::GENERAL: del = "~"; break;+ case SelectorCombinator::ADJACENT: del = "+"; break; }- // if (del = "/") del += selector->reference()->perform(&to_string) + "/";- std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;- debug_ast(selector->head(), ind + " " /* + "[" + del + "]" */, env);- if (selector->tail()) {- debug_ast(selector->tail(), ind + "{" + del + "}", env);- } else if(del != " ") {- std::cerr << ind << " |" << del << "| {trailing op}" << std::endl;- }- ComplexSelectorSet set = selector->sources();- // debug_sources_set(set, ind + " @--> ");- } else if (Cast<Compound_Selector>(node)) {- Compound_Selector_Ptr selector = Cast<Compound_Selector>(node);- std::cerr << ind << "Compound_Selector " << selector;++ std::cerr << "[" << del << "]" << "\n";++ } else if (Cast<CompoundSelector>(node)) {+ CompoundSelector* selector = Cast<CompoundSelector>(node);+ std::cerr << ind << "CompoundSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";+ std::cerr << (selector->hasRealParent() ? " [REAL PARENT]" : "") << ">"; std::cerr << " [weight:" << longToHex(selector->specificity()) << "]";- std::cerr << " [@media:" << selector->media_block() << "]";- std::cerr << (selector->extended() ? " [extended]": " -");- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");- std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;- for(const Simple_Selector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }- } else if (Cast<Wrapped_Selector>(node)) {- Wrapped_Selector_Ptr selector = Cast<Wrapped_Selector>(node);- std::cerr << ind << "Wrapped_Selector " << selector;+ std::cerr << (selector->hasPostLineBreak() ? " [hasPostLineBreak]" : " -");+ std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -");+ std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -");+ std::cerr << "\n";+ for(const SimpleSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }++ } else if (Cast<Parent_Reference>(node)) {+ Parent_Reference* selector = Cast<Parent_Reference>(node);+ std::cerr << ind << "Parent_Reference " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">";- std::cerr << " <<" << selector->ns_name() << ">>";- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");- std::cerr << std::endl;- debug_ast(selector->selector(), ind + " () ", env);+ std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;+ } else if (Cast<Pseudo_Selector>(node)) {- Pseudo_Selector_Ptr selector = Cast<Pseudo_Selector>(node);+ Pseudo_Selector* selector = Cast<Pseudo_Selector>(node); std::cerr << ind << "Pseudo_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>";- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << (selector->isClass() ? " [isClass]": " -");+ std::cerr << (selector->isSyntacticClass() ? " [isSyntacticClass]": " -"); std::cerr << std::endl;- debug_ast(selector->expression(), ind + " <= ", env);+ debug_ast(selector->argument(), ind + " <= ", env);+ debug_ast(selector->selector(), ind + " || ", env); } else if (Cast<Attribute_Selector>(node)) {- Attribute_Selector_Ptr selector = Cast<Attribute_Selector>(node);+ Attribute_Selector* selector = Cast<Attribute_Selector>(node); std::cerr << ind << "Attribute_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>";- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl; debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env); } else if (Cast<Class_Selector>(node)) {- Class_Selector_Ptr selector = Cast<Class_Selector>(node);+ Class_Selector* selector = Cast<Class_Selector>(node); std::cerr << ind << "Class_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>";- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl; } else if (Cast<Id_Selector>(node)) {- Id_Selector_Ptr selector = Cast<Id_Selector>(node);+ Id_Selector* selector = Cast<Id_Selector>(node); std::cerr << ind << "Id_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>";- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl;- } else if (Cast<Element_Selector>(node)) {- Element_Selector_Ptr selector = Cast<Element_Selector>(node);- std::cerr << ind << "Element_Selector " << selector;+ } else if (Cast<Type_Selector>(node)) {+ Type_Selector* selector = Cast<Type_Selector>(node);+ std::cerr << ind << "Type_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>";- std::cerr << (selector->is_optional() ? " [is_optional]": " -");- std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -");- std::cerr << (selector->has_line_break() ? " [line-break]": " -");- std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">"; std::cerr << std::endl; } else if (Cast<Placeholder_Selector>(node)) { - Placeholder_Selector_Ptr selector = Cast<Placeholder_Selector>(node);+ Placeholder_Selector* selector = Cast<Placeholder_Selector>(node); std::cerr << ind << "Placeholder_Selector [" << selector->ns_name() << "] " << selector; std::cerr << " (" << pstate_source_position(selector) << ")" << " <" << selector->hash() << ">"- << " [@media:" << selector->media_block() << "]"- << (selector->is_optional() ? " [is_optional]": " -")- << (selector->has_line_break() ? " [line-break]": " -")- << (selector->has_line_feed() ? " [line-feed]": " -")+ << (selector->isInvisible() ? " [isInvisible]" : " -") << std::endl; - } else if (Cast<Simple_Selector>(node)) {- Simple_Selector* selector = Cast<Simple_Selector>(node);- std::cerr << ind << "Simple_Selector " << selector;+ } else if (Cast<SimpleSelector>(node)) {+ SimpleSelector* selector = Cast<SimpleSelector>(node);+ std::cerr << ind << "SimpleSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; } else if (Cast<Selector_Schema>(node)) {- Selector_Schema_Ptr selector = Cast<Selector_Schema>(node);+ Selector_Schema* selector = Cast<Selector_Schema>(node); std::cerr << ind << "Selector_Schema " << selector; std::cerr << " (" << pstate_source_position(node) << ")"- << " [@media:" << selector->media_block() << "]" << (selector->connect_parent() ? " [connect-parent]": " -") << std::endl; @@ -268,15 +487,13 @@ // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Selector>(node)) {- Selector_Ptr selector = Cast<Selector>(node);+ Selector* selector = Cast<Selector>(node); std::cerr << ind << "Selector " << selector;- std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << (selector->has_line_break() ? " [line-break]": " -")- << (selector->has_line_feed() ? " [line-feed]": " -")+ std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; } else if (Cast<Media_Query_Expression>(node)) {- Media_Query_Expression_Ptr block = Cast<Media_Query_Expression>(node);+ Media_Query_Expression* block = Cast<Media_Query_Expression>(node); std::cerr << ind << "Media_Query_Expression " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -")@@ -285,7 +502,7 @@ debug_ast(block->value(), ind + " value) "); } else if (Cast<Media_Query>(node)) {- Media_Query_Ptr block = Cast<Media_Query>(node);+ Media_Query* block = Cast<Media_Query>(node); std::cerr << ind << "Media_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_negated() ? " [is_negated]": " -")@@ -293,81 +510,101 @@ << std::endl; debug_ast(block->media_type(), ind + " "); for(const auto& i : block->elements()) { debug_ast(i, ind + " ", env); }-- } else if (Cast<Media_Block>(node)) {- Media_Block_Ptr block = Cast<Media_Block>(node);- std::cerr << ind << "Media_Block " << block;- std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " " << block->tabs() << std::endl;- debug_ast(block->media_queries(), ind + " =@ ");- if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ }+ else if (Cast<MediaRule>(node)) {+ MediaRule* rule = Cast<MediaRule>(node);+ std::cerr << ind << "MediaRule " << rule;+ std::cerr << " (" << pstate_source_position(rule) << ")";+ std::cerr << " " << rule->tabs() << std::endl;+ debug_ast(rule->schema(), ind + " =@ ");+ debug_ast(rule->block(), ind + " ");+ }+ else if (Cast<CssMediaRule>(node)) {+ CssMediaRule* rule = Cast<CssMediaRule>(node);+ std::cerr << ind << "CssMediaRule " << rule;+ std::cerr << " (" << pstate_source_position(rule) << ")";+ std::cerr << " " << rule->tabs() << std::endl;+ for (auto item : rule->elements()) {+ debug_ast(item, ind + " == ");+ }+ debug_ast(rule->block(), ind + " ");+ }+ else if (Cast<CssMediaQuery>(node)) {+ CssMediaQuery* query = Cast<CssMediaQuery>(node);+ std::cerr << ind << "CssMediaQuery " << query;+ std::cerr << " (" << pstate_source_position(query) << ")";+ std::cerr << " [" << (query->modifier()) << "] ";+ std::cerr << " [" << (query->type()) << "] ";+ std::cerr << " " << debug_vec(query->features());+ std::cerr << std::endl; } else if (Cast<Supports_Block>(node)) {- Supports_Block_Ptr block = Cast<Supports_Block>(node);+ Supports_Block* block = Cast<Supports_Block>(node); std::cerr << ind << "Supports_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->condition(), ind + " =@ "); debug_ast(block->block(), ind + " <>"); } else if (Cast<Supports_Operator>(node)) {- Supports_Operator_Ptr block = Cast<Supports_Operator>(node);+ Supports_Operator* block = Cast<Supports_Operator>(node); std::cerr << ind << "Supports_Operator " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->left(), ind + " left) "); debug_ast(block->right(), ind + " right) "); } else if (Cast<Supports_Negation>(node)) {- Supports_Negation_Ptr block = Cast<Supports_Negation>(node);+ Supports_Negation* block = Cast<Supports_Negation>(node); std::cerr << ind << "Supports_Negation " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->condition(), ind + " condition) "); } else if (Cast<At_Root_Query>(node)) {- At_Root_Query_Ptr block = Cast<At_Root_Query>(node);+ At_Root_Query* block = Cast<At_Root_Query>(node); std::cerr << ind << "At_Root_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (Cast<Supports_Declaration>(node)) {- Supports_Declaration_Ptr block = Cast<Supports_Declaration>(node);+ Supports_Declaration* block = Cast<Supports_Declaration>(node); std::cerr << ind << "Supports_Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (Cast<Block>(node)) {- Block_Ptr root_block = Cast<Block>(node);+ Block* root_block = Cast<Block>(node); std::cerr << ind << "Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; if (root_block->is_root()) std::cerr << " [root]";+ if (root_block->isInvisible()) std::cerr << " [isInvisible]"; std::cerr << " " << root_block->tabs() << std::endl; for(const Statement_Obj& i : root_block->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Warning>(node)) {- Warning_Ptr block = Cast<Warning>(node);+ Warning* block = Cast<Warning>(node); std::cerr << ind << "Warning " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->message(), ind + " : "); } else if (Cast<Error>(node)) {- Error_Ptr block = Cast<Error>(node);+ Error* block = Cast<Error>(node); std::cerr << ind << "Error " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; } else if (Cast<Debug>(node)) {- Debug_Ptr block = Cast<Debug>(node);+ Debug* block = Cast<Debug>(node); std::cerr << ind << "Debug " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->value(), ind + " "); } else if (Cast<Comment>(node)) {- Comment_Ptr block = Cast<Comment>(node);+ Comment* block = Cast<Comment>(node); std::cerr << ind << "Comment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl; debug_ast(block->text(), ind + "// ", env); } else if (Cast<If>(node)) {- If_Ptr block = Cast<If>(node);+ If* block = Cast<If>(node); std::cerr << ind << "If " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;@@ -375,30 +612,31 @@ debug_ast(block->block(), ind + " <>"); debug_ast(block->alternative(), ind + " ><"); } else if (Cast<Return>(node)) {- Return_Ptr block = Cast<Return>(node);+ Return* block = Cast<Return>(node); std::cerr << ind << "Return " << block; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " " << block->tabs() << std::endl;- } else if (Cast<Extension>(node)) {- Extension_Ptr block = Cast<Extension>(node);- std::cerr << ind << "Extension " << block;+ std::cerr << " " << block->tabs();+ std::cerr << " [" << block->value()->to_string() << "]" << std::endl;+ } else if (Cast<ExtendRule>(node)) {+ ExtendRule* block = Cast<ExtendRule>(node);+ std::cerr << ind << "ExtendRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "-> ", env); } else if (Cast<Content>(node)) {- Content_Ptr block = Cast<Content>(node);+ Content* block = Cast<Content>(node); std::cerr << ind << "Content " << block; std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " [@media:" << block->media_block() << "]"; std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->arguments(), ind + " args: ", env); } else if (Cast<Import_Stub>(node)) {- Import_Stub_Ptr block = Cast<Import_Stub>(node);+ Import_Stub* block = Cast<Import_Stub>(node); std::cerr << ind << "Import_Stub " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->imp_path() << "] "; std::cerr << " " << block->tabs() << std::endl; } else if (Cast<Import>(node)) {- Import_Ptr block = Cast<Import>(node);+ Import* block = Cast<Import>(node); std::cerr << ind << "Import " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl;@@ -406,13 +644,13 @@ for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env); debug_ast(block->import_queries(), ind + "@@ "); } else if (Cast<Assignment>(node)) {- Assignment_Ptr block = Cast<Assignment>(node);+ Assignment* block = Cast<Assignment>(node); std::cerr << ind << "Assignment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <<" << block->variable() << ">> " << block->tabs() << std::endl; debug_ast(block->value(), ind + "=", env); } else if (Cast<Declaration>(node)) {- Declaration_Ptr block = Cast<Declaration>(node);+ Declaration* block = Cast<Declaration>(node); std::cerr << ind << "Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [is_custom_property: " << block->is_custom_property() << "] ";@@ -421,14 +659,14 @@ debug_ast(block->value(), ind + " value: ", env); debug_ast(block->block(), ind + " ", env); } else if (Cast<Keyframe_Rule>(node)) {- Keyframe_Rule_Ptr has_block = Cast<Keyframe_Rule>(node);+ Keyframe_Rule* has_block = Cast<Keyframe_Rule>(node); std::cerr << ind << "Keyframe_Rule " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl; if (has_block->name()) debug_ast(has_block->name(), ind + "@"); if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Directive>(node)) {- Directive_Ptr block = Cast<Directive>(node);+ Directive* block = Cast<Directive>(node); std::cerr << ind << "Directive " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl;@@ -436,25 +674,25 @@ debug_ast(block->value(), ind + "+", env); if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Each>(node)) {- Each_Ptr block = Cast<Each>(node);+ Each* block = Cast<Each>(node); std::cerr << ind << "Each " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<For>(node)) {- For_Ptr block = Cast<For>(node);+ For* block = Cast<For>(node); std::cerr << ind << "For " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<While>(node)) {- While_Ptr block = Cast<While>(node);+ While* block = Cast<While>(node); std::cerr << ind << "While " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Definition>(node)) {- Definition_Ptr block = Cast<Definition>(node);+ Definition* block = Cast<Definition>(node); std::cerr << ind << "Definition " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << block->name() << "] ";@@ -466,14 +704,15 @@ debug_ast(block->parameters(), ind + " params: ", env); if (block->block()) debug_ast(block->block(), ind + " ", env); } else if (Cast<Mixin_Call>(node)) {- Mixin_Call_Ptr block = Cast<Mixin_Call>(node);+ Mixin_Call* block = Cast<Mixin_Call>(node); std::cerr << ind << "Mixin_Call " << block << " " << block->tabs(); std::cerr << " (" << pstate_source_position(block) << ")"; std::cerr << " [" << block->name() << "]"; std::cerr << " [has_content: " << block->has_content() << "] " << std::endl;- debug_ast(block->arguments(), ind + " args: ");+ debug_ast(block->arguments(), ind + " args: ", env);+ debug_ast(block->block_parameters(), ind + " block_params: ", env); if (block->block()) debug_ast(block->block(), ind + " ", env);- } else if (Ruleset_Ptr ruleset = Cast<Ruleset>(node)) {+ } else if (Ruleset* ruleset = Cast<Ruleset>(node)) { std::cerr << ind << "Ruleset " << ruleset; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [indent: " << ruleset->tabs() << "]";@@ -483,30 +722,22 @@ debug_ast(ruleset->selector(), ind + ">"); debug_ast(ruleset->block(), ind + " "); } else if (Cast<Block>(node)) {- Block_Ptr block = Cast<Block>(node);+ Block* block = Cast<Block>(node); std::cerr << ind << "Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_invisible() ? " [INVISIBLE]" : ""); std::cerr << " [indent: " << block->tabs() << "]" << std::endl; for(const Statement_Obj& i : block->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Variable>(node)) {- Variable_Ptr expression = Cast<Variable>(node);+ Variable* expression = Cast<Variable>(node); std::cerr << ind << "Variable " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]" << std::endl; std::string name(expression->name()); if (env && env->has(name)) debug_ast(Cast<Expression>((*env)[name]), ind + " -> ", env);- } else if (Cast<Function_Call_Schema>(node)) {- Function_Call_Schema_Ptr expression = Cast<Function_Call_Schema>(node);- std::cerr << ind << "Function_Call_Schema " << expression;- std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";- std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << "" << std::endl;- debug_ast(expression->name(), ind + "name: ", env);- debug_ast(expression->arguments(), ind + " args: ", env); } else if (Cast<Function_Call>(node)) {- Function_Call_Ptr expression = Cast<Function_Call>(node);+ Function_Call* expression = Cast<Function_Call>(node); std::cerr << ind << "Function_Call " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")";@@ -518,14 +749,14 @@ debug_ast(expression->arguments(), ind + " args: ", env); debug_ast(expression->func(), ind + " func: ", env); } else if (Cast<Function>(node)) {- Function_Ptr expression = Cast<Function>(node);+ Function* expression = Cast<Function>(node); std::cerr << ind << "Function " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_css()) std::cerr << " [css]"; std::cerr << std::endl; debug_ast(expression->definition(), ind + " definition: ", env); } else if (Cast<Arguments>(node)) {- Arguments_Ptr expression = Cast<Arguments>(node);+ Arguments* expression = Cast<Arguments>(node); std::cerr << ind << "Arguments " << expression; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << " (" << pstate_source_position(node) << ")";@@ -535,7 +766,7 @@ std::cerr << std::endl; for(const Argument_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Argument>(node)) {- Argument_Ptr expression = Cast<Argument>(node);+ Argument* expression = Cast<Argument>(node); std::cerr << ind << "Argument " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->value().ptr() << "]";@@ -544,7 +775,7 @@ std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl; debug_ast(expression->value(), ind + " value: ", env); } else if (Cast<Parameters>(node)) {- Parameters_Ptr expression = Cast<Parameters>(node);+ Parameters* expression = Cast<Parameters>(node); std::cerr << ind << "Parameters " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] ";@@ -552,14 +783,14 @@ std::cerr << std::endl; for(const Parameter_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Parameter>(node)) {- Parameter_Ptr expression = Cast<Parameter>(node);+ Parameter* expression = Cast<Parameter>(node); std::cerr << ind << "Parameter " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [default: " << expression->default_value().ptr() << "] "; std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl; } else if (Cast<Unary_Expression>(node)) {- Unary_Expression_Ptr expression = Cast<Unary_Expression>(node);+ Unary_Expression* expression = Cast<Unary_Expression>(node); std::cerr << ind << "Unary_Expression " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [delayed: " << expression->is_delayed() << "] ";@@ -567,7 +798,7 @@ std::cerr << " [" << expression->type() << "]" << std::endl; debug_ast(expression->operand(), ind + " operand: ", env); } else if (Cast<Binary_Expression>(node)) {- Binary_Expression_Ptr expression = Cast<Binary_Expression>(node);+ Binary_Expression* expression = Cast<Binary_Expression>(node); std::cerr << ind << "Binary_Expression " << expression; if (expression->is_interpolant()) std::cerr << " [is interpolant] "; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";@@ -580,7 +811,7 @@ debug_ast(expression->left(), ind + " left: ", env); debug_ast(expression->right(), ind + " right: ", env); } else if (Cast<Map>(node)) {- Map_Ptr expression = Cast<Map>(node);+ Map* expression = Cast<Map>(node); std::cerr << ind << "Map " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")";@@ -590,7 +821,7 @@ debug_ast(i.second, ind + " val: "); } } else if (Cast<List>(node)) {- List_Ptr expression = Cast<List>(node);+ List* expression = Cast<List>(node); std::cerr << ind << "List " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " <<@@ -604,27 +835,30 @@ " [hash: " << expression->hash() << "] " << std::endl; for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }- } else if (Cast<Content>(node)) {- Content_Ptr expression = Cast<Content>(node);- std::cerr << ind << "Content " << expression;- std::cerr << " (" << pstate_source_position(node) << ")";- std::cerr << " [@media:" << expression->media_block() << "]";- std::cerr << " [Statement]" << std::endl; } else if (Cast<Boolean>(node)) {- Boolean_Ptr expression = Cast<Boolean>(node);+ Boolean* expression = Cast<Boolean>(node); std::cerr << ind << "Boolean " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << "]" << std::endl;- } else if (Cast<Color>(node)) {- Color_Ptr expression = Cast<Color>(node);+ } else if (Cast<Color_RGBA>(node)) {+ Color_RGBA* expression = Cast<Color_RGBA>(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [name: " << expression->disp() << "] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";- std::cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;+ std::cerr << " rgba[" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;+ } else if (Cast<Color_HSLA>(node)) {+ Color_HSLA* expression = Cast<Color_HSLA>(node);+ std::cerr << ind << "Color " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [name: " << expression->disp() << "] ";+ std::cerr << " [delayed: " << expression->is_delayed() << "] ";+ std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";+ std::cerr << " hsla[" << expression->h() << ":" << expression->s() << ":" << expression->l() << "@" << expression->a() << "]" << std::endl; } else if (Cast<Number>(node)) {- Number_Ptr expression = Cast<Number>(node);+ Number* expression = Cast<Number>(node); std::cerr << ind << "Number " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [delayed: " << expression->is_delayed() << "] ";@@ -633,14 +867,14 @@ " [hash: " << expression->hash() << "] " << std::endl; } else if (Cast<Null>(node)) {- Null_Ptr expression = Cast<Null>(node);+ Null* expression = Cast<Null>(node); std::cerr << ind << "Null " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] " // " [hash: " << expression->hash() << "] " << std::endl; } else if (Cast<String_Quoted>(node)) {- String_Quoted_Ptr expression = Cast<String_Quoted>(node);+ String_Quoted* expression = Cast<String_Quoted>(node); std::cerr << ind << "String_Quoted " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]";@@ -649,7 +883,7 @@ if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; } else if (Cast<String_Constant>(node)) {- String_Constant_Ptr expression = Cast<String_Constant>(node);+ String_Constant* expression = Cast<String_Constant>(node); std::cerr << ind << "String_Constant " << expression; if (expression->concrete_type()) { std::cerr << " " << expression->concrete_type();@@ -660,7 +894,7 @@ if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; } else if (Cast<String_Schema>(node)) {- String_Schema_Ptr expression = Cast<String_Schema>(node);+ String_Schema* expression = Cast<String_Schema>(node); std::cerr << ind << "String_Schema " << expression; std::cerr << " (" << pstate_source_position(expression) << ")"; std::cerr << " " << expression->concrete_type();@@ -674,42 +908,43 @@ std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<String>(node)) {- String_Ptr expression = Cast<String>(node);+ String* expression = Cast<String>(node); std::cerr << ind << "String " << expression; std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; } else if (Cast<Expression>(node)) {- Expression_Ptr expression = Cast<Expression>(node);+ Expression* expression = Cast<Expression>(node); std::cerr << ind << "Expression " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; switch (expression->concrete_type()) {- case Expression::Concrete_Type::NONE: std::cerr << " [NONE]"; break;- case Expression::Concrete_Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break;- case Expression::Concrete_Type::NUMBER: std::cerr << " [NUMBER]"; break;- case Expression::Concrete_Type::COLOR: std::cerr << " [COLOR]"; break;- case Expression::Concrete_Type::STRING: std::cerr << " [STRING]"; break;- case Expression::Concrete_Type::LIST: std::cerr << " [LIST]"; break;- case Expression::Concrete_Type::MAP: std::cerr << " [MAP]"; break;- case Expression::Concrete_Type::SELECTOR: std::cerr << " [SELECTOR]"; break;- case Expression::Concrete_Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break;- case Expression::Concrete_Type::C_WARNING: std::cerr << " [C_WARNING]"; break;- case Expression::Concrete_Type::C_ERROR: std::cerr << " [C_ERROR]"; break;- case Expression::Concrete_Type::FUNCTION: std::cerr << " [FUNCTION]"; break;- case Expression::Concrete_Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;- case Expression::Concrete_Type::VARIABLE: std::cerr << " [VARIABLE]"; break;- case Expression::Concrete_Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break;+ case Expression::Type::NONE: std::cerr << " [NONE]"; break;+ case Expression::Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break;+ case Expression::Type::NUMBER: std::cerr << " [NUMBER]"; break;+ case Expression::Type::COLOR: std::cerr << " [COLOR]"; break;+ case Expression::Type::STRING: std::cerr << " [STRING]"; break;+ case Expression::Type::LIST: std::cerr << " [LIST]"; break;+ case Expression::Type::MAP: std::cerr << " [MAP]"; break;+ case Expression::Type::SELECTOR: std::cerr << " [SELECTOR]"; break;+ case Expression::Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break;+ case Expression::Type::C_WARNING: std::cerr << " [C_WARNING]"; break;+ case Expression::Type::C_ERROR: std::cerr << " [C_ERROR]"; break;+ case Expression::Type::FUNCTION: std::cerr << " [FUNCTION]"; break;+ case Expression::Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;+ case Expression::Type::VARIABLE: std::cerr << " [VARIABLE]"; break;+ case Expression::Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break;+ case Expression::Type::PARENT: std::cerr << " [PARENT]"; break; } std::cerr << std::endl; } else if (Cast<Has_Block>(node)) {- Has_Block_Ptr has_block = Cast<Has_Block>(node);+ Has_Block* has_block = Cast<Has_Block>(node); std::cerr << ind << "Has_Block " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl; if (has_block->block()) for(const Statement_Obj& i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast<Statement>(node)) {- Statement_Ptr statement = Cast<Statement>(node);+ Statement* statement = Cast<Statement>(node); std::cerr << ind << "Statement " << statement; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << statement->tabs() << std::endl;@@ -718,84 +953,12 @@ if (ind == "") std::cerr << "####################################################################\n"; } -inline void debug_node(Node* node, std::string ind = "")-{- if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";- if (node->isCombinator()) {- std::cerr << ind;- std::cerr << "Combinator ";- std::cerr << node << " ";- if (node->got_line_feed) std::cerr << "[LF] ";- switch (node->combinator()) {- case Complex_Selector::ADJACENT_TO: std::cerr << "{+} "; break;- case Complex_Selector::PARENT_OF: std::cerr << "{>} "; break;- case Complex_Selector::PRECEDES: std::cerr << "{~} "; break;- case Complex_Selector::REFERENCE: std::cerr << "{@} "; break;- case Complex_Selector::ANCESTOR_OF: std::cerr << "{ } "; break;- }- std::cerr << std::endl;- // debug_ast(node->combinator(), ind + " ");- } else if (node->isSelector()) {- std::cerr << ind;- std::cerr << "Selector ";- std::cerr << node << " ";- if (node->got_line_feed) std::cerr << "[LF] ";- std::cerr << std::endl;- debug_ast(node->selector(), ind + " ");- } else if (node->isCollection()) {- std::cerr << ind;- std::cerr << "Collection ";- std::cerr << node << " ";- if (node->got_line_feed) std::cerr << "[LF] ";- std::cerr << std::endl;- for(auto n : (*node->collection())) {- debug_node(&n, ind + " ");- }- } else if (node->isNil()) {- std::cerr << ind;- std::cerr << "Nil ";- std::cerr << node << " ";- if (node->got_line_feed) std::cerr << "[LF] ";- std::cerr << std::endl;- } else {- std::cerr << ind;- std::cerr << "OTHER ";- std::cerr << node << " ";- if (node->got_line_feed) std::cerr << "[LF] ";- std::cerr << std::endl;- }- if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";-} /*-inline void debug_ast(const AST_Node_Ptr node, std::string ind = "", Env* env = 0)+inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0) {- debug_ast(const_cast<AST_Node_Ptr>(node), ind, env);+ debug_ast(const_cast<AST_Node*>(node), ind, env); } */-inline void debug_node(const Node* node, std::string ind = "")-{- debug_node(const_cast<Node*>(node), ind);-}--inline void debug_subset_map(Sass::Subset_Map& map, std::string ind = "")-{- if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";- for(auto const &it : map.values()) {- debug_ast(it.first, ind + "first: ");- debug_ast(it.second, ind + "second: ");- }- if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-}--inline void debug_subset_entries(SubSetMapPairs* entries, std::string ind = "")-{- if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";- for(auto const &pair : *entries) {- debug_ast(pair.first, ind + "first: ");- debug_ast(pair.second, ind + "second: ");- }- if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";-} #endif // SASS_DEBUGGER
libsass/src/emitter.cpp view
@@ -1,9 +1,9 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include "util.hpp"-#include "context.hpp"-#include "output.hpp" #include "emitter.hpp"-#include "utf8_string.hpp"+#include "util_string.hpp"+#include "util.hpp" namespace Sass { @@ -47,11 +47,11 @@ void Emitter::set_filename(const std::string& str) { wbuf.smap.file = str; } - void Emitter::schedule_mapping(const AST_Node_Ptr node)+ void Emitter::schedule_mapping(const AST_Node* node) { scheduled_mapping = node; }- void Emitter::add_open_mapping(const AST_Node_Ptr node)+ void Emitter::add_open_mapping(const AST_Node* node) { wbuf.smap.add_open_mapping(node); }- void Emitter::add_close_mapping(const AST_Node_Ptr node)+ void Emitter::add_close_mapping(const AST_Node* node) { wbuf.smap.add_close_mapping(node); } ParserState Emitter::remap(const ParserState& pstate) { return wbuf.smap.remap(pstate); }@@ -134,13 +134,13 @@ // write space/lf flush_schedules(); - if (in_comment && output_style() == COMPACT) {- // unescape comment nodes- std::string out = comment_to_string(text);- // add to buffer- wbuf.buffer += out;- // account for data in source-maps+ if (in_comment) {+ std::string out = Util::normalize_newlines(text);+ if (output_style() == COMPACT) {+ out = comment_to_compact_string(out);+ } wbuf.smap.append(Offset(out));+ wbuf.buffer += std::move(out); } else { // add to buffer wbuf.buffer += text;@@ -161,7 +161,7 @@ // append some text or token to the buffer // this adds source-mappings for node start and end- void Emitter::append_token(const std::string& text, const AST_Node_Ptr node)+ void Emitter::append_token(const std::string& text, const AST_Node* node) { flush_schedules(); add_open_mapping(node);@@ -263,7 +263,7 @@ } } - void Emitter::append_scope_opener(AST_Node_Ptr node)+ void Emitter::append_scope_opener(AST_Node* node) { scheduled_linefeed = 0; append_optional_space();@@ -274,7 +274,7 @@ // append_optional_space(); ++ indentation; }- void Emitter::append_scope_closer(AST_Node_Ptr node)+ void Emitter::append_scope_closer(AST_Node* node) { -- indentation; scheduled_linefeed = 0;
libsass/src/emitter.hpp view
@@ -1,8 +1,10 @@ #ifndef SASS_EMITTER_H #define SASS_EMITTER_H -#include <string>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp"@@ -25,9 +27,9 @@ // proxy methods for source maps void add_source_index(size_t idx); void set_filename(const std::string& str);- void add_open_mapping(const AST_Node_Ptr node);- void add_close_mapping(const AST_Node_Ptr node);- void schedule_mapping(const AST_Node_Ptr node);+ void add_open_mapping(const AST_Node* node);+ void add_close_mapping(const AST_Node* node);+ void schedule_mapping(const AST_Node* node); std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate); @@ -37,8 +39,8 @@ size_t scheduled_space; size_t scheduled_linefeed; bool scheduled_delimiter;- AST_Node_Ptr scheduled_crutch;- AST_Node_Ptr scheduled_mapping;+ const AST_Node* scheduled_crutch;+ const AST_Node* scheduled_mapping; public: // output strings different in custom css properties@@ -75,7 +77,7 @@ void append_wspace(const std::string& text); // append some text or token to the buffer // this adds source-mappings for node start and end- void append_token(const std::string& text, const AST_Node_Ptr node);+ void append_token(const std::string& text, const AST_Node* node); // query last appended character char last_char(); @@ -86,8 +88,8 @@ void append_special_linefeed(void); void append_optional_linefeed(void); void append_mandatory_linefeed(void);- void append_scope_opener(AST_Node_Ptr node = 0);- void append_scope_closer(AST_Node_Ptr node = 0);+ void append_scope_opener(AST_Node* node = 0);+ void append_scope_closer(AST_Node* node = 0); void append_comma_separator(void); void append_colon_separator(void); void append_delimiter(void);
libsass/src/environment.cpp view
@@ -208,6 +208,20 @@ // use array access for getter and setter functions template <typename T>+ T& Environment<T>::get(const std::string& key)+ {+ auto cur = this;+ while (cur) {+ if (cur->has_local(key)) {+ return cur->get_local(key);+ }+ cur = cur->parent_;+ }+ return get_local(key);+ }++ // use array access for getter and setter functions+ template <typename T> T& Environment<T>::operator[](const std::string& key) { auto cur = this;@@ -230,7 +244,7 @@ for (typename environment_map<std::string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) { std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second;- if (Value_Ptr val = Cast<Value>(i->second))+ if (Value* val = Cast<Value>(i->second)) { std::cerr << " : " << val->to_string(); } std::cerr << std::endl; }
libsass/src/environment.hpp view
@@ -1,12 +1,18 @@ #ifndef SASS_ENVIRONMENT_H #define SASS_ENVIRONMENT_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <map> #include <string> #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" namespace Sass { + // this defeats the whole purpose of environment being templatable!! typedef environment_map<std::string, AST_Node_Obj>::iterator EnvIter; class EnvResult {@@ -94,6 +100,10 @@ // look on the full stack for key // include all scopes available+ T& get(const std::string& key);++ // look on the full stack for key+ // include all scopes available EnvResult find(const std::string& key); // use array access for getter and setter functions@@ -107,6 +117,7 @@ // define typedef for our use case typedef Environment<AST_Node_Obj> Env;+ typedef std::vector<Env*> EnvStack; }
libsass/src/error_handling.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp"@@ -15,43 +18,38 @@ prefix("Error"), pstate(pstate), traces(traces) { } - InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg)- : Base(pstate, msg, traces)+ InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg, char* owned_src)+ : Base(pstate, msg, traces), owned_src(owned_src) { } - InvalidParent::InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector)+ InvalidParent::InvalidParent(Selector* parent, Backtraces traces, Selector* selector) : Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector) {- msg = "Invalid parent selector for \"";- msg += selector->to_string(Sass_Inspect_Options());- msg += "\": \"";- msg += parent->to_string(Sass_Inspect_Options());- msg += "\"";+ msg = "Invalid parent selector for "+ "\"" + selector->to_string(Sass_Inspect_Options()) + "\": "+ "\"" + parent->to_string(Sass_Inspect_Options()) + "\""; } - InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg)+ InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument* arg) : Base(pstate, def_msg, traces), name(name), arg(arg) {- msg = "Variable keyword argument map must have string keys.\n";- msg += name + " is not a string in " + arg->to_string() + ".";+ msg = "Variable keyword argument map must have string keys.\n" ++ name + " is not a string in " + arg->to_string() + "."; } - InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value)+ InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value* value) : Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value) {- msg = arg + ": \"";+ msg = arg + ": \""; if (value) msg += value->to_string(Sass_Inspect_Options());- msg += "\" is not a " + type;- msg += " for `" + fn + "'";+ msg += "\" is not a " + type + " for `" + fn + "'"; } MissingArgument::MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype) : Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype) {- msg = fntype + " " + fn;- msg += " is missing argument ";- msg += arg + ".";+ msg = fntype + " " + fn + " is missing argument " + arg + "."; } InvalidSyntax::InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg)@@ -65,87 +63,66 @@ DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org) : Base(org.pstate(), def_msg, traces), dup(dup), org(org) {- msg = "Duplicate key ";- msg += dup.get_duplicate_key()->inspect();- msg += " in map (";- msg += org.inspect();- msg += ").";+ msg = "Duplicate key " + dup.get_duplicate_key()->inspect() + " in map (" + org.inspect() + ")."; } TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const std::string type) : Base(var.pstate(), def_msg, traces), var(var), type(type) {- msg = var.to_string();- msg += " is not an ";- msg += type;- msg += ".";+ msg = var.to_string() + " is not an " + type + "."; } InvalidValue::InvalidValue(Backtraces traces, const Expression& val) : Base(val.pstate(), def_msg, traces), val(val) {- msg = val.to_string();- msg += " isn't a valid CSS value.";+ msg = val.to_string() + " isn't a valid CSS value."; } StackError::StackError(Backtraces traces, const AST_Node& node) : Base(node.pstate(), def_msg, traces), node(node) {- msg = "stack level too deep";+ msg = "stack level too deep"; } IncompatibleUnits::IncompatibleUnits(const Units& lhs, const Units& rhs) {- msg = "Incompatible units: '";- msg += rhs.unit();- msg += "' and '";- msg += lhs.unit();- msg += "'.";+ msg = "Incompatible units: '" + rhs.unit() + "' and '" + lhs.unit() + "'."; } IncompatibleUnits::IncompatibleUnits(const UnitType lhs, const UnitType rhs) {- msg = "Incompatible units: '";- msg += unit_to_string(rhs);- msg += "' and '";- msg += unit_to_string(lhs);- msg += "'.";+ msg = std::string("Incompatible units: '") + unit_to_string(rhs) + "' and '" + unit_to_string(lhs) + "'."; } - AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+ AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op) : OperationError(), lhs(lhs), rhs(rhs), op(op) {- msg = "Alpha channels must be equal: ";- msg += lhs->to_string({ NESTED, 5 });- msg += " " + sass_op_to_name(op) + " ";- msg += rhs->to_string({ NESTED, 5 });- msg += ".";+ msg = "Alpha channels must be equal: " ++ lhs->to_string({ NESTED, 5 }) ++ " " + sass_op_to_name(op) + " " ++ rhs->to_string({ NESTED, 5 }) + "."; } ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs) : OperationError(), lhs(lhs), rhs(rhs) {- msg = "divided by 0";+ msg = "divided by 0"; } - UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+ UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op) : OperationError(), lhs(lhs), rhs(rhs), op(op) {- msg = def_op_msg + ": \"";- msg += lhs->to_string({ NESTED, 5 });- msg += " " + sass_op_to_name(op) + " ";- msg += rhs->to_string({ TO_SASS, 5 });- msg += "\".";+ msg = def_op_msg + ": \"" ++ lhs->to_string({ NESTED, 5 }) ++ " " + sass_op_to_name(op) + " " ++ rhs->to_string({ TO_SASS, 5 }) ++ "\"."; } - InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)+ InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op) : UndefinedOperation(lhs, rhs, op) {- msg = def_op_null_msg + ": \"";- msg += lhs->inspect();- msg += " " + sass_op_to_name(op) + " ";- msg += rhs->inspect();- msg += "\".";+ msg = def_op_null_msg + ": \"" + lhs->inspect() + " " + sass_op_to_name(op) + " " + rhs->inspect() + "\"."; } SassValueError::SassValueError(Backtraces traces, ParserState pstate, OperationError& err)@@ -154,6 +131,27 @@ msg = err.what(); prefix = err.errtype(); }++ TopLevelParent::TopLevelParent(Backtraces traces, ParserState pstate)+ : Base(pstate, "Top-level selectors may not contain the parent selector \"&\".", traces)+ {++ }++ UnsatisfiedExtend::UnsatisfiedExtend(Backtraces traces, Extension extension)+ : Base(extension.target->pstate(), "The target selector was not found.\n"+ "Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces)+ {++ }++ ExtendAcrossMedia::ExtendAcrossMedia(Backtraces traces, Extension extension)+ : Base(extension.target->pstate(), "You may not @extend selectors across media queries.\n"+ "Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces)+ {++ }+ }
libsass/src/error_handling.hpp view
@@ -1,9 +1,14 @@ #ifndef SASS_ERROR_HANDLING_H #define SASS_ERROR_HANDLING_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include <string> #include <sstream> #include <stdexcept>+#include "units.hpp" #include "position.hpp" #include "backtrace.hpp" #include "ast_fwd_decl.hpp"@@ -36,16 +41,28 @@ class InvalidSass : public Base { public:- InvalidSass(ParserState pstate, Backtraces traces, std::string msg);- virtual ~InvalidSass() throw() {};+ InvalidSass(InvalidSass& other) : Base(other), owned_src(other.owned_src) {+ // Assumes that `this` will outlive `other`.+ other.owned_src = nullptr;+ }++ // Required because the copy constructor's argument is not const.+ // Can't use `std::move` here because we build on Visual Studio 2013.+ InvalidSass(InvalidSass &&other) : Base(other), owned_src(other.owned_src) {+ other.owned_src = nullptr;+ }++ InvalidSass(ParserState pstate, Backtraces traces, std::string msg, char* owned_src = nullptr);+ virtual ~InvalidSass() throw() { sass_free_memory(owned_src); };+ char *owned_src; }; class InvalidParent : public Base { protected:- Selector_Ptr parent;- Selector_Ptr selector;+ Selector* parent;+ Selector* selector; public:- InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector);+ InvalidParent(Selector* parent, Backtraces traces, Selector* selector); virtual ~InvalidParent() throw() {}; }; @@ -64,18 +81,18 @@ std::string fn; std::string arg; std::string type;- const Value_Ptr value;+ const Value* value; public:- InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0);+ InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value* value = 0); virtual ~InvalidArgumentType() throw() {}; }; class InvalidVarKwdType : public Base { protected: std::string name;- const Argument_Ptr arg;+ const Argument* arg; public:- InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg = 0);+ InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument* arg = 0); virtual ~InvalidVarKwdType() throw() {}; }; @@ -165,36 +182,54 @@ class UndefinedOperation : public OperationError { protected:- Expression_Ptr_Const lhs;- Expression_Ptr_Const rhs;+ const Expression* lhs;+ const Expression* rhs; const Sass_OP op; public:- UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);+ UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op); // virtual const char* errtype() const { return "Error"; } virtual ~UndefinedOperation() throw() {}; }; class InvalidNullOperation : public UndefinedOperation { public:- InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);+ InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op); virtual ~InvalidNullOperation() throw() {}; }; class AlphaChannelsNotEqual : public OperationError { protected:- Expression_Ptr_Const lhs;- Expression_Ptr_Const rhs;+ const Expression* lhs;+ const Expression* rhs; const Sass_OP op; public:- AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);+ AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op); // virtual const char* errtype() const { return "Error"; } virtual ~AlphaChannelsNotEqual() throw() {}; }; class SassValueError : public Base {- public:- SassValueError(Backtraces traces, ParserState pstate, OperationError& err);- virtual ~SassValueError() throw() {};+ public:+ SassValueError(Backtraces traces, ParserState pstate, OperationError& err);+ virtual ~SassValueError() throw() {};+ };++ class TopLevelParent : public Base {+ public:+ TopLevelParent(Backtraces traces, ParserState pstate);+ virtual ~TopLevelParent() throw() {};+ };++ class UnsatisfiedExtend : public Base {+ public:+ UnsatisfiedExtend(Backtraces traces, Extension extension);+ virtual ~UnsatisfiedExtend() throw() {};+ };++ class ExtendAcrossMedia : public Base {+ public:+ ExtendAcrossMedia(Backtraces traces, Extension extension);+ virtual ~ExtendAcrossMedia() throw() {}; }; }
libsass/src/eval.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstdlib> #include <cmath> #include <iostream>@@ -17,7 +20,8 @@ #include "position.hpp" #include "sass/values.h" #include "to_value.hpp"-#include "to_c.hpp"+#include "ast2c.hpp"+#include "c2ast.hpp" #include "context.hpp" #include "backtrace.hpp" #include "lexer.hpp"@@ -26,6 +30,7 @@ #include "expand.hpp" #include "color_maps.hpp" #include "sass_functions.hpp"+#include "util_string.hpp" namespace Sass { @@ -47,14 +52,34 @@ return exp.environment(); } - Selector_List_Obj Eval::selector()+ const std::string Eval::cwd() {- return exp.selector();+ return ctx.cwd(); } - Expression_Ptr Eval::operator()(Block_Ptr b)+ struct Sass_Inspect_Options& Eval::options() {- Expression_Ptr val = 0;+ return ctx.c_options;+ }++ struct Sass_Compiler* Eval::compiler()+ {+ return ctx.c_compiler;+ }++ EnvStack& Eval::env_stack()+ {+ return exp.env_stack;+ }++ std::vector<Sass_Callee>& Eval::callee_stack()+ {+ return ctx.callee_stack;+ }++ Expression* Eval::operator()(Block* b)+ {+ Expression* val = 0; for (size_t i = 0, L = b->length(); i < L; ++i) { val = b->at(i)->perform(this); if (val) return val;@@ -62,14 +87,14 @@ return val; } - Expression_Ptr Eval::operator()(Assignment_Ptr a)+ Expression* Eval::operator()(Assignment* a) {- Env* env = exp.environment();+ Env* env = environment(); std::string var(a->variable()); if (a->is_global()) { if (a->is_default()) { if (env->has_global(var)) {- Expression_Ptr e = Cast<Expression>(env->get_global(var));+ Expression* e = Cast<Expression>(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(this)); }@@ -88,7 +113,7 @@ while (cur && cur->is_lexical()) { if (cur->has_local(var)) { if (AST_Node_Obj node = cur->get_local(var)) {- Expression_Ptr e = Cast<Expression>(node);+ Expression* e = Cast<Expression>(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(this)); }@@ -104,7 +129,7 @@ } else if (env->has_global(var)) { if (AST_Node_Obj node = env->get_global(var)) {- Expression_Ptr e = Cast<Expression>(node);+ Expression* e = Cast<Expression>(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(this)); }@@ -123,11 +148,11 @@ return 0; } - Expression_Ptr Eval::operator()(If_Ptr i)+ Expression* Eval::operator()(If* i) {- Expression_Obj rv = 0;- Env env(exp.environment());- exp.env_stack.push_back(&env);+ Expression_Obj rv;+ Env env(environment());+ env_stack().push_back(&env); Expression_Obj cond = i->predicate()->perform(this); if (!cond->is_false()) { rv = i->block()->perform(this);@@ -136,13 +161,13 @@ Block_Obj alt = i->alternative(); if (alt) rv = alt->perform(this); }- exp.env_stack.pop_back();+ env_stack().pop_back(); return rv.detach(); } // For does not create a new env scope // But iteration vars are reset afterwards- Expression_Ptr Eval::operator()(For_Ptr f)+ Expression* Eval::operator()(For* f) { std::string variable(f->variable()); Expression_Obj low = f->lower_bound()->perform(this);@@ -168,9 +193,9 @@ double end = sass_end->value(); // only create iterator once in this environment Env env(environment(), true);- exp.env_stack.push_back(&env);+ env_stack().push_back(&env); Block_Obj body = f->block();- Expression_Ptr val = 0;+ Expression* val = 0; if (start < end) { if (f->is_inclusive()) ++end; for (double i = start;@@ -192,26 +217,25 @@ if (val) break; } }- exp.env_stack.pop_back();+ env_stack().pop_back(); return val; } // Eval does not create a new env scope // But iteration vars are reset afterwards- Expression_Ptr Eval::operator()(Each_Ptr e)+ Expression* Eval::operator()(Each* e) { std::vector<std::string> variables(e->variables()); Expression_Obj expr = e->list()->perform(this); Env env(environment(), true);- exp.env_stack.push_back(&env);- List_Obj list = 0;- Map_Ptr map = 0;+ env_stack().push_back(&env);+ List_Obj list;+ Map* map = nullptr; if (expr->concrete_type() == Expression::MAP) { map = Cast<Map>(expr); }- else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {- Listize listize;- Expression_Obj rv = ls->perform(&listize);+ else if (SelectorList * ls = Cast<SelectorList>(expr)) {+ Expression_Obj rv = Listize::perform(ls); list = Cast<List>(rv); } else if (expr->concrete_type() != Expression::LIST) {@@ -223,14 +247,14 @@ } Block_Obj body = e->block();- Expression_Obj val = 0;+ Expression_Obj val; if (map) { for (Expression_Obj key : map->keys()) { Expression_Obj value = map->at(key); if (variables.size() == 1) {- List_Ptr variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);+ List* variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE); variable->append(key); variable->append(value); env.set_local(variables[0], variable);@@ -244,22 +268,22 @@ } } else {- if (list->length() == 1 && Cast<Selector_List>(list)) {+ if (list->length() == 1 && Cast<SelectorList>(list)) { list = Cast<List>(list); } for (size_t i = 0, L = list->length(); i < L; ++i) {- Expression_Ptr item = list->at(i);+ Expression* item = list->at(i); // unwrap value if the expression is an argument- if (Argument_Ptr arg = Cast<Argument>(item)) item = arg->value();+ if (Argument* arg = Cast<Argument>(item)) item = arg->value(); // check if we got passed a list of args (investigate)- if (List_Ptr scalars = Cast<List>(item)) {+ if (List* scalars = Cast<List>(item)) { if (variables.size() == 1) {- Expression_Ptr var = scalars;+ Expression* var = scalars; env.set_local(variables[0], var); } else { // XXX: this is never hit via spec tests for (size_t j = 0, K = variables.size(); j < K; ++j) {- Expression_Ptr res = j >= scalars->length()+ Expression* res = j >= scalars->length() ? SASS_MEMORY_NEW(Null, expr->pstate()) : scalars->at(j); env.set_local(variables[j], res);@@ -270,7 +294,7 @@ env.set_local(variables.at(0), item); for (size_t j = 1, K = variables.size(); j < K; ++j) { // XXX: this is never hit via spec tests- Expression_Ptr res = SASS_MEMORY_NEW(Null, expr->pstate());+ Expression* res = SASS_MEMORY_NEW(Null, expr->pstate()); env.set_local(variables[j], res); } }@@ -279,46 +303,46 @@ if (val) break; } }- exp.env_stack.pop_back();+ env_stack().pop_back(); return val.detach(); } - Expression_Ptr Eval::operator()(While_Ptr w)+ Expression* Eval::operator()(While* w) { Expression_Obj pred = w->predicate(); Block_Obj body = w->block(); Env env(environment(), true);- exp.env_stack.push_back(&env);+ env_stack().push_back(&env); Expression_Obj cond = pred->perform(this); while (!cond->is_false()) { Expression_Obj val = body->perform(this); if (val) {- exp.env_stack.pop_back();+ env_stack().pop_back(); return val.detach(); } cond = pred->perform(this); }- exp.env_stack.pop_back();+ env_stack().pop_back(); return 0; } - Expression_Ptr Eval::operator()(Return_Ptr r)+ Expression* Eval::operator()(Return* r) { return r->value()->perform(this); } - Expression_Ptr Eval::operator()(Warning_Ptr w)+ Expression* Eval::operator()(Warning* w) {- Sass_Output_Style outstyle = ctx.c_options.output_style;- ctx.c_options.output_style = NESTED;+ Sass_Output_Style outstyle = options().output_style;+ options().output_style = NESTED; Expression_Obj message = w->message()->perform(this);- Env* env = exp.environment();+ Env* env = environment(); // try to use generic function if (env->has("@warn[f]")) { // add call stack entry- ctx.callee_stack.push_back({+ callee_stack().push_back({ "@warn", w->pstate().path, w->pstate().line + 1,@@ -327,18 +351,18 @@ { env } }); - Definition_Ptr def = Cast<Definition>((*env)["@warn[f]"]);+ Definition* def = Cast<Definition>((*env)["@warn[f]"]); // Block_Obj body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); - To_C to_c;+ AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);- sass_list_set_value(c_args, 0, message->perform(&to_c));- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- ctx.c_options.output_style = outstyle;- ctx.callee_stack.pop_back();+ sass_list_set_value(c_args, 0, message->perform(&ast2c));+ union Sass_Value* c_val = c_func(c_args, c_function, compiler());+ options().output_style = outstyle;+ callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0;@@ -350,23 +374,23 @@ traces.push_back(Backtrace(w->pstate())); std::cerr << traces_to_string(traces, " "); std::cerr << std::endl;- ctx.c_options.output_style = outstyle;+ options().output_style = outstyle; traces.pop_back(); return 0; } - Expression_Ptr Eval::operator()(Error_Ptr e)+ Expression* Eval::operator()(Error* e) {- Sass_Output_Style outstyle = ctx.c_options.output_style;- ctx.c_options.output_style = NESTED;+ Sass_Output_Style outstyle = options().output_style;+ options().output_style = NESTED; Expression_Obj message = e->message()->perform(this);- Env* env = exp.environment();+ Env* env = environment(); // try to use generic function if (env->has("@error[f]")) { // add call stack entry- ctx.callee_stack.push_back({+ callee_stack().push_back({ "@error", e->pstate().path, e->pstate().line + 1,@@ -375,18 +399,18 @@ { env } }); - Definition_Ptr def = Cast<Definition>((*env)["@error[f]"]);+ Definition* def = Cast<Definition>((*env)["@error[f]"]); // Block_Obj body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); - To_C to_c;+ AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);- sass_list_set_value(c_args, 0, message->perform(&to_c));- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- ctx.c_options.output_style = outstyle;- ctx.callee_stack.pop_back();+ sass_list_set_value(c_args, 0, message->perform(&ast2c));+ union Sass_Value* c_val = c_func(c_args, c_function, compiler());+ options().output_style = outstyle;+ callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0;@@ -394,23 +418,23 @@ } std::string result(unquote(message->to_sass()));- ctx.c_options.output_style = outstyle;+ options().output_style = outstyle; error(result, e->pstate(), traces); return 0; } - Expression_Ptr Eval::operator()(Debug_Ptr d)+ Expression* Eval::operator()(Debug* d) {- Sass_Output_Style outstyle = ctx.c_options.output_style;- ctx.c_options.output_style = NESTED;+ Sass_Output_Style outstyle = options().output_style;+ options().output_style = NESTED; Expression_Obj message = d->value()->perform(this);- Env* env = exp.environment();+ Env* env = environment(); // try to use generic function if (env->has("@debug[f]")) { // add call stack entry- ctx.callee_stack.push_back({+ callee_stack().push_back({ "@debug", d->pstate().path, d->pstate().line + 1,@@ -419,37 +443,37 @@ { env } }); - Definition_Ptr def = Cast<Definition>((*env)["@debug[f]"]);+ Definition* def = Cast<Definition>((*env)["@debug[f]"]); // Block_Obj body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); - To_C to_c;+ AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false);- sass_list_set_value(c_args, 0, message->perform(&to_c));- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);- ctx.c_options.output_style = outstyle;- ctx.callee_stack.pop_back();+ sass_list_set_value(c_args, 0, message->perform(&ast2c));+ union Sass_Value* c_val = c_func(c_args, c_function, compiler());+ options().output_style = outstyle;+ callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; } - std::string cwd(ctx.cwd()); std::string result(unquote(message->to_sass()));- std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));- std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));+ std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd(), cwd()));+ std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd(), cwd())); std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));- ctx.c_options.output_style = outstyle;+ options().output_style = outstyle; std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result; std::cerr << std::endl; return 0; } - Expression_Ptr Eval::operator()(List_Ptr l)++ Expression* Eval::operator()(List* l) { // special case for unevaluated map if (l->separator() == SASS_HASH) {@@ -490,7 +514,7 @@ return ll.detach(); } - Expression_Ptr Eval::operator()(Map_Ptr m)+ Expression* Eval::operator()(Map* m) { if (m->is_expanded()) return m; @@ -505,8 +529,8 @@ m->pstate(), m->length()); for (auto key : m->keys()) {- Expression_Ptr ex_key = key->perform(this);- Expression_Ptr ex_val = m->at(key);+ Expression* ex_key = key->perform(this);+ Expression* ex_val = m->at(key); if (ex_val == NULL) continue; ex_val = ex_val->perform(this); *mm << std::make_pair(ex_key, ex_val);@@ -522,7 +546,7 @@ return mm.detach(); } - Expression_Ptr Eval::operator()(Binary_Expression_Ptr b_in)+ Expression* Eval::operator()(Binary_Expression* b_in) { Expression_Obj lhs = b_in->left();@@ -543,21 +567,21 @@ } // Evaluate variables as early o- while (Variable_Ptr l_v = Cast<Variable>(lhs)) {+ while (Variable* l_v = Cast<Variable>(lhs)) { lhs = operator()(l_v); }- while (Variable_Ptr r_v = Cast<Variable>(rhs)) {+ while (Variable* r_v = Cast<Variable>(rhs)) { rhs = operator()(r_v); } Binary_Expression_Obj b = b_in; // Evaluate sub-expressions early on- while (Binary_Expression_Ptr l_b = Cast<Binary_Expression>(lhs)) {+ while (Binary_Expression* l_b = Cast<Binary_Expression>(lhs)) { if (!force && l_b->is_delayed()) break; lhs = operator()(l_b); }- while (Binary_Expression_Ptr r_b = Cast<Binary_Expression>(rhs)) {+ while (Binary_Expression* r_b = Cast<Binary_Expression>(rhs)) { if (!force && r_b->is_delayed()) break; rhs = operator()(r_b); }@@ -571,9 +595,9 @@ // specific types we know are final // handle them early to avoid overhead- if (Number_Ptr l_n = Cast<Number>(lhs)) {+ if (Number* l_n = Cast<Number>(lhs)) { // lhs is number and rhs is number- if (Number_Ptr r_n = Cast<Number>(rhs)) {+ if (Number* r_n = Cast<Number>(rhs)) { try { switch (op_type) { case Sass_OP::EQ: return *l_n == *r_n ? bool_true : bool_false;@@ -583,7 +607,7 @@ case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false; case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:- return Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, b_in->pstate());+ return Operators::op_numbers(op_type, *l_n, *r_n, options(), b_in->pstate()); default: break; } }@@ -594,13 +618,15 @@ } } // lhs is number and rhs is color- else if (Color_Ptr r_c = Cast<Color>(rhs)) {+ // Todo: allow to work with HSLA colors+ else if (Color* r_col = Cast<Color>(rhs)) {+ Color_RGBA_Obj r_c = r_col->toRGBA(); try { switch (op_type) { case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false; case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:- return Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate());+ return Operators::op_number_color(op_type, *l_n, *r_c, options(), b_in->pstate()); default: break; } }@@ -611,9 +637,11 @@ } } }- else if (Color_Ptr l_c = Cast<Color>(lhs)) {+ else if (Color* l_col = Cast<Color>(lhs)) {+ Color_RGBA_Obj l_c = l_col->toRGBA(); // lhs is color and rhs is color- if (Color_Ptr r_c = Cast<Color>(rhs)) {+ if (Color* r_col = Cast<Color>(rhs)) {+ Color_RGBA_Obj r_c = r_col->toRGBA(); try { switch (op_type) { case Sass_OP::EQ: return *l_c == *r_c ? bool_true : bool_false;@@ -623,7 +651,7 @@ case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false; case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:- return Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, b_in->pstate());+ return Operators::op_colors(op_type, *l_c, *r_c, options(), b_in->pstate()); default: break; } }@@ -634,13 +662,13 @@ } } // lhs is color and rhs is number- else if (Number_Ptr r_n = Cast<Number>(rhs)) {+ else if (Number* r_n = Cast<Number>(rhs)) { try { switch (op_type) { case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false; case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:- return Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate());+ return Operators::op_color_number(op_type, *l_c, *r_n, options(), b_in->pstate()); default: break; } }@@ -655,7 +683,7 @@ String_Schema_Obj ret_schema; // only the last item will be used to eval the binary expression- if (String_Schema_Ptr s_l = Cast<String_Schema>(b->left())) {+ if (String_Schema* s_l = Cast<String_Schema>(b->left())) { if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) { ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate()); Binary_Expression_Obj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(),@@ -668,7 +696,7 @@ return ret_schema->perform(this); } }- if (String_Schema_Ptr s_r = Cast<String_Schema>(b->right())) {+ if (String_Schema* s_r = Cast<String_Schema>(b->right())) { if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) { ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate());@@ -708,8 +736,8 @@ AST_Node_Obj lu = lhs; AST_Node_Obj ru = rhs; - Expression::Concrete_Type l_type;- Expression::Concrete_Type r_type;+ Expression::Type l_type;+ Expression::Type r_type; // Is one of the operands an interpolant? String_Schema_Obj s1 = Cast<String_Schema>(b->left());@@ -729,7 +757,7 @@ if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB || op_type == Sass_OP::EQ) { // If possible upgrade LHS to a number (for number to string compare)- if (String_Constant_Ptr str = Cast<String_Constant>(lhs)) {+ if (String_Constant* str = Cast<String_Constant>(lhs)) { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) {@@ -737,7 +765,7 @@ } } // If possible upgrade RHS to a number (for string to number compare)- if (String_Constant_Ptr str = Cast<String_Constant>(rhs)) {+ if (String_Constant* str = Cast<String_Constant>(rhs)) { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) {@@ -752,12 +780,12 @@ if (force_delay) { std::string str("");- str += v_l->to_string(ctx.c_options);+ str += v_l->to_string(options()); if (b->op().ws_before) str += " "; str += b->separator(); if (b->op().ws_after) str += " ";- str += v_r->to_string(ctx.c_options);- String_Constant_Ptr val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str);+ str += v_r->to_string(options());+ String_Constant* val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str); val->is_interpolant(b->left()->has_interpolant()); return val; }@@ -777,7 +805,6 @@ } catch (Exception::OperationError& err) {- // throw Exception::Base(b->pstate(), err.what()); traces.push_back(Backtrace(b->pstate())); throw Exception::SassValueError(traces, b->pstate(), err); }@@ -791,25 +818,25 @@ try { ParserState pstate(b->pstate()); if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {- Number_Ptr l_n = Cast<Number>(lhs);- Number_Ptr r_n = Cast<Number>(rhs);+ Number* l_n = Cast<Number>(lhs);+ Number* r_n = Cast<Number>(rhs); l_n->reduce(); r_n->reduce();- rv = Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, pstate);+ rv = Operators::op_numbers(op_type, *l_n, *r_n, options(), pstate); } else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {- Number_Ptr l_n = Cast<Number>(lhs);- Color_Ptr r_c = Cast<Color>(rhs);- rv = Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, pstate);+ Number* l_n = Cast<Number>(lhs);+ Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA();+ rv = Operators::op_number_color(op_type, *l_n, *r_c, options(), pstate); } else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {- Color_Ptr l_c = Cast<Color>(lhs);- Number_Ptr r_n = Cast<Number>(rhs);- rv = Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, pstate);+ Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+ Number* r_n = Cast<Number>(rhs);+ rv = Operators::op_color_number(op_type, *l_c, *r_n, options(), pstate); } else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {- Color_Ptr l_c = Cast<Color>(lhs);- Color_Ptr r_c = Cast<Color>(rhs);- rv = Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, pstate);+ Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+ Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA();+ rv = Operators::op_colors(op_type, *l_c, *r_c, options(), pstate); } else { To_Value to_value(ctx);@@ -830,15 +857,15 @@ traces.push_back(Backtrace(v_r->pstate())); throw Exception::InvalidValue(traces, *v_r); }- Value_Ptr ex = Operators::op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress- if (String_Constant_Ptr str = Cast<String_Constant>(ex))+ Value* ex = Operators::op_strings(b->op(), *v_l, *v_r, options(), pstate, !interpolant); // pass true to compress+ if (String_Constant* str = Cast<String_Constant>(ex)) { if (str->concrete_type() == Expression::STRING) {- String_Constant_Ptr lstr = Cast<String_Constant>(lhs);- String_Constant_Ptr rstr = Cast<String_Constant>(rhs);+ String_Constant* lstr = Cast<String_Constant>(lhs);+ String_Constant* rstr = Cast<String_Constant>(rhs); if (op_type != Sass_OP::SUB) {- if (String_Constant_Ptr org = lstr ? lstr : rstr)+ if (String_Constant* org = lstr ? lstr : rstr) { str->quote_mark(org->quote_mark()); } } }@@ -866,11 +893,11 @@ } - Expression_Ptr Eval::operator()(Unary_Expression_Ptr u)+ Expression* Eval::operator()(Unary_Expression* u) { Expression_Obj operand = u->operand()->perform(this); if (u->optype() == Unary_Expression::NOT) {- Boolean_Ptr result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand);+ Boolean* result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand); result->value(!result->value()); return result; }@@ -882,28 +909,35 @@ return cpy.detach(); // return the copy } else if (u->optype() == Unary_Expression::SLASH) {- std::string str = '/' + nr->to_string(ctx.c_options);+ std::string str = '/' + nr->to_string(options()); return SASS_MEMORY_NEW(String_Constant, u->pstate(), str); } // nothing for positive return nr.detach(); } else {- // Special cases: +/- variables which evaluate to null ouput just +/-,+ // Special cases: +/- variables which evaluate to null output just +/-, // but +/- null itself outputs the string if (operand->concrete_type() == Expression::NULL_VAL && Cast<Variable>(u->operand())) { u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), "")); } // Never apply unary opertions on colors @see #2140- else if (Color_Ptr color = Cast<Color>(operand)) {+ else if (Color* color = Cast<Color>(operand)) { // Use the color name if this was eval with one if (color->disp().length() > 0) {- operand = SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp());- u->operand(operand);+ Unary_Expression_Obj cpy = SASS_MEMORY_COPY(u);+ cpy->operand(SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp()));+ return SASS_MEMORY_NEW(String_Quoted,+ cpy->pstate(),+ cpy->inspect()); } } else {- u->operand(operand);+ Unary_Expression_Obj cpy = SASS_MEMORY_COPY(u);+ cpy->operand(operand);+ return SASS_MEMORY_NEW(String_Quoted,+ cpy->pstate(),+ cpy->inspect()); } return SASS_MEMORY_NEW(String_Quoted,@@ -914,7 +948,7 @@ return u; } - Expression_Ptr Eval::operator()(Function_Call_Ptr c)+ Expression* Eval::operator()(Function_Call* c) { if (traces.size() > Constants::MaxCallStack) { // XXX: this is never hit via spec tests@@ -922,8 +956,18 @@ stm << "Stack depth exceeded max of " << Constants::MaxCallStack; error(stm.str(), c->pstate(), traces); }++ if (Cast<String_Schema>(c->sname())) {+ Expression_Obj evaluated_name = c->sname()->perform(this);+ Expression_Obj evaluated_args = c->arguments()->perform(this);+ std::string str(evaluated_name->to_string());+ str += evaluated_args->to_string();+ return SASS_MEMORY_NEW(String_Constant, c->pstate(), str);+ }+ std::string name(Util::normalize_underscores(c->name())); std::string full_name(name + "[f]");+ // we make a clone here, need to implement that further Arguments_Obj args = c->arguments(); @@ -941,11 +985,11 @@ c->name(), args); if (args->has_named_arguments()) {- error("Function " + c->name() + " doesn't support keyword arguments", c->pstate(), traces);+ error("Plain CSS function " + c->name() + " doesn't support keyword arguments", c->pstate(), traces); }- String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted,+ String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, c->pstate(),- lit->to_string(ctx.c_options));+ lit->to_string(options())); str->is_interpolant(c->is_interpolant()); return str; } else {@@ -961,7 +1005,7 @@ if (full_name != "if[f]") { args = Cast<Arguments>(args->perform(this)); }- Definition_Ptr def = Cast<Definition>((*env)[full_name]);+ Definition* def = Cast<Definition>((*env)[full_name]); if (c->func()) def = c->func()->definition(); @@ -971,7 +1015,7 @@ // account for rest arguments if (args->has_rest_argument() && args->length() > 0) { // get the rest arguments list- List_Ptr rest = Cast<List>(args->last()->value());+ List* rest = Cast<List>(args->last()->value()); // arguments before rest argument plus rest if (rest) L += rest->length() - 1; }@@ -991,13 +1035,13 @@ Parameters_Obj params = def->parameters(); Env fn_env(def->environment());- exp.env_stack.push_back(&fn_env);+ env_stack().push_back(&fn_env); if (func || body) {- bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+ bind(std::string("Function"), c->name(), params, args, &fn_env, this, traces); std::string msg(", in function `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg));- ctx.callee_stack.push_back({+ callee_stack().push_back({ c->name().c_str(), c->pstate().path, c->pstate().line + 1,@@ -1011,12 +1055,12 @@ result = body->perform(this); } else if (func) {- result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.selector_stack);+ result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.getSelectorStack(), exp.originalStack); } if (!result) { error(std::string("Function ") + c->name() + " finished without @return", c->pstate(), traces); }- ctx.callee_stack.pop_back();+ callee_stack().pop_back(); traces.pop_back(); } @@ -1034,10 +1078,10 @@ // populates env with default values for params std::string ff(c->name());- bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+ bind(std::string("Function"), c->name(), params, args, &fn_env, this, traces); std::string msg(", in function `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg));- ctx.callee_stack.push_back({+ callee_stack().push_back({ c->name().c_str(), c->pstate().path, c->pstate().line + 1,@@ -1046,24 +1090,30 @@ { env } }); - To_C to_c;+ AST2C ast2c; union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA, false); for(size_t i = 0; i < params->length(); i++) { Parameter_Obj param = params->at(i); std::string key = param->name(); AST_Node_Obj node = fn_env.get_local(key); Expression_Obj arg = Cast<Expression>(node);- sass_list_set_value(c_args, i, arg->perform(&to_c));+ sass_list_set_value(c_args, i, arg->perform(&ast2c)); }- union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ union Sass_Value* c_val = c_func(c_args, c_function, compiler()); if (sass_value_get_tag(c_val) == SASS_ERROR) {- error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), traces);+ std::string message("error in C function " + c->name() + ": " + sass_error_get_message(c_val));+ sass_delete_value(c_val);+ sass_delete_value(c_args);+ error(message, c->pstate(), traces); } else if (sass_value_get_tag(c_val) == SASS_WARNING) {- error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), traces);+ std::string message("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val));+ sass_delete_value(c_val);+ sass_delete_value(c_args);+ error(message, c->pstate(), traces); }- result = cval_to_astnode(c_val, traces, c->pstate());+ result = c2ast(c_val, traces, c->pstate()); - ctx.callee_stack.pop_back();+ callee_stack().pop_back(); traces.pop_back(); sass_delete_value(c_args); if (c_val != c_args)@@ -1077,30 +1127,20 @@ result = result->perform(this); result->is_interpolant(c->is_interpolant());- exp.env_stack.pop_back();+ env_stack().pop_back(); return result.detach(); } - Expression_Ptr Eval::operator()(Function_Call_Schema_Ptr s)- {- Expression_Ptr evaluated_name = s->name()->perform(this);- Expression_Ptr evaluated_args = s->arguments()->perform(this);- String_Schema_Obj ss = SASS_MEMORY_NEW(String_Schema, s->pstate(), 2);- ss->append(evaluated_name);- ss->append(evaluated_args);- return ss->perform(this);- }-- Expression_Ptr Eval::operator()(Variable_Ptr v)+ Expression* Eval::operator()(Variable* v) {- Expression_Obj value = 0;+ Expression_Obj value; Env* env = environment(); const std::string& name(v->name()); EnvResult rv(env->find(name)); if (rv.found) value = static_cast<Expression*>(rv.it->second.ptr()); else error("Undefined variable: \"" + v->name() + "\".", v->pstate(), traces);- if (Argument_Ptr arg = Cast<Argument>(value)) value = arg->value();- if (Number_Ptr nr = Cast<Number>(value)) nr->zero(true); // force flag+ if (Argument* arg = Cast<Argument>(value)) value = arg->value();+ if (Number* nr = Cast<Number>(value)) nr->zero(true); // force flag value->is_interpolant(v->is_interpolant()); if (force) value->is_expanded(false); value->set_delayed(false); // verified@@ -1109,17 +1149,22 @@ return value.detach(); } - Expression_Ptr Eval::operator()(Color_Ptr c)+ Expression* Eval::operator()(Color_RGBA* c) { return c; } - Expression_Ptr Eval::operator()(Number_Ptr n)+ Expression* Eval::operator()(Color_HSLA* c) {+ return c;+ }++ Expression* Eval::operator()(Number* n)+ { return n; } - Expression_Ptr Eval::operator()(Boolean_Ptr b)+ Expression* Eval::operator()(Boolean* b) { return b; }@@ -1128,8 +1173,8 @@ bool needs_closing_brace = false; - if (Arguments_Ptr args = Cast<Arguments>(ex)) {- List_Ptr ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA);+ if (Arguments* args = Cast<Arguments>(ex)) {+ List* ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA); for(auto arg : args->elements()) { ll->append(arg->value()); }@@ -1138,7 +1183,7 @@ res += "("; ex = ll; }- if (Number_Ptr nr = Cast<Number>(ex)) {+ if (Number* nr = Cast<Number>(ex)) { Number reduced(nr); reduced.reduce(); if (!reduced.is_valid_css_unit()) {@@ -1146,10 +1191,10 @@ throw Exception::InvalidValue(traces, *nr); } }- if (Argument_Ptr arg = Cast<Argument>(ex)) {+ if (Argument* arg = Cast<Argument>(ex)) { ex = arg->value(); }- if (String_Quoted_Ptr sq = Cast<String_Quoted>(ex)) {+ if (String_Quoted* sq = Cast<String_Quoted>(ex)) { if (was_itpl) { bool was_interpolant = ex->is_interpolant(); ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value());@@ -1160,12 +1205,12 @@ if (Cast<Null>(ex)) { return; } // parent selector needs another go- if (Cast<Parent_Selector>(ex)) {+ if (Cast<Parent_Reference>(ex)) { // XXX: this is never hit via spec tests ex = ex->perform(this); } - if (List_Ptr l = Cast<List>(ex)) {+ if (List* l = Cast<List>(ex)) { List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator()); // this fixes an issue with bourbon sample, not really sure why // if (l->size() && Cast<Null>((*l)[0])) { res += ""; }@@ -1179,12 +1224,12 @@ // here. Normally single list items are already unwrapped. if (l->size() > 1) { // string_to_output would fail "#{'_\a' '_\a'}";- std::string str(ll->to_string(ctx.c_options));+ std::string str(ll->to_string(options())); str = read_hex_escapes(str); // read escapes newline_to_space(str); // replace directly res += str; // append to result string } else {- res += (ll->to_string(ctx.c_options));+ res += (ll->to_string(options())); } ll->is_interpolant(l->is_interpolant()); }@@ -1194,14 +1239,13 @@ // Selector_List // String_Quoted // String_Constant- // Parent_Selector // Binary_Expression else { // ex = ex->perform(this); if (into_quotes && ex->is_interpolant()) {- res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : "");+ res += evacuate_escapes(ex ? ex->to_string(options()) : ""); } else {- std::string str(ex ? ex->to_string(ctx.c_options) : "");+ std::string str(ex ? ex->to_string(options()) : ""); if (into_quotes) str = read_hex_escapes(str); res += str; // append to result string }@@ -1211,14 +1255,14 @@ } - Expression_Ptr Eval::operator()(String_Schema_Ptr s)+ Expression* Eval::operator()(String_Schema* s) { size_t L = s->length(); bool into_quotes = false; if (L > 1) { if (!Cast<String_Quoted>((*s)[0]) && !Cast<String_Quoted>((*s)[L - 1])) {- if (String_Constant_Ptr l = Cast<String_Constant>((*s)[0])) {- if (String_Constant_Ptr r = Cast<String_Constant>((*s)[L - 1])) {+ if (String_Constant* l = Cast<String_Constant>((*s)[0])) {+ if (String_Constant* r = Cast<String_Constant>((*s)[L - 1])) { if (r->value().size() > 0) { if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;@@ -1247,7 +1291,7 @@ // string schema seems to have a special unquoting behavior (also handles "nested" quotes) String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false, s->css()); // if (s->is_interpolant()) str->quote_mark(0);- // String_Constant_Ptr str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);+ // String_Constant* str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res); if (str->quote_mark()) str->quote_mark('*'); else if (!is_in_comment) str->value(string_to_output(str->value())); str->is_interpolant(s->is_interpolant());@@ -1255,25 +1299,25 @@ } - Expression_Ptr Eval::operator()(String_Constant_Ptr s)+ Expression* Eval::operator()(String_Constant* s) { return s; } - Expression_Ptr Eval::operator()(String_Quoted_Ptr s)+ Expression* Eval::operator()(String_Quoted* s) {- String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), "");+ String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), ""); str->value(s->value()); str->quote_mark(s->quote_mark()); str->is_interpolant(s->is_interpolant()); return str; } - Expression_Ptr Eval::operator()(Supports_Operator_Ptr c)+ Expression* Eval::operator()(Supports_Operator* c) {- Expression_Ptr left = c->left()->perform(this);- Expression_Ptr right = c->right()->perform(this);- Supports_Operator_Ptr cc = SASS_MEMORY_NEW(Supports_Operator,+ Expression* left = c->left()->perform(this);+ Expression* right = c->right()->perform(this);+ Supports_Operator* cc = SASS_MEMORY_NEW(Supports_Operator, c->pstate(), Cast<Supports_Condition>(left), Cast<Supports_Condition>(right),@@ -1281,52 +1325,52 @@ return cc; } - Expression_Ptr Eval::operator()(Supports_Negation_Ptr c)+ Expression* Eval::operator()(Supports_Negation* c) {- Expression_Ptr condition = c->condition()->perform(this);- Supports_Negation_Ptr cc = SASS_MEMORY_NEW(Supports_Negation,+ Expression* condition = c->condition()->perform(this);+ Supports_Negation* cc = SASS_MEMORY_NEW(Supports_Negation, c->pstate(), Cast<Supports_Condition>(condition)); return cc; } - Expression_Ptr Eval::operator()(Supports_Declaration_Ptr c)+ Expression* Eval::operator()(Supports_Declaration* c) {- Expression_Ptr feature = c->feature()->perform(this);- Expression_Ptr value = c->value()->perform(this);- Supports_Declaration_Ptr cc = SASS_MEMORY_NEW(Supports_Declaration,+ Expression* feature = c->feature()->perform(this);+ Expression* value = c->value()->perform(this);+ Supports_Declaration* cc = SASS_MEMORY_NEW(Supports_Declaration, c->pstate(), feature, value); return cc; } - Expression_Ptr Eval::operator()(Supports_Interpolation_Ptr c)+ Expression* Eval::operator()(Supports_Interpolation* c) {- Expression_Ptr value = c->value()->perform(this);- Supports_Interpolation_Ptr cc = SASS_MEMORY_NEW(Supports_Interpolation,+ Expression* value = c->value()->perform(this);+ Supports_Interpolation* cc = SASS_MEMORY_NEW(Supports_Interpolation, c->pstate(), value); return cc; } - Expression_Ptr Eval::operator()(At_Root_Query_Ptr e)+ Expression* Eval::operator()(At_Root_Query* e) { Expression_Obj feature = e->feature(); feature = (feature ? feature->perform(this) : 0); Expression_Obj value = e->value(); value = (value ? value->perform(this) : 0);- Expression_Ptr ee = SASS_MEMORY_NEW(At_Root_Query,+ Expression* ee = SASS_MEMORY_NEW(At_Root_Query, e->pstate(), Cast<String>(feature), value); return ee; } - Media_Query_Ptr Eval::operator()(Media_Query_Ptr q)+ Media_Query* Eval::operator()(Media_Query* q) { String_Obj t = q->media_type();- t = static_cast<String_Ptr>(t.isNull() ? 0 : t->perform(this));+ t = static_cast<String*>(t.isNull() ? 0 : t->perform(this)); Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query, q->pstate(), t,@@ -1334,12 +1378,12 @@ q->is_negated(), q->is_restricted()); for (size_t i = 0, L = q->length(); i < L; ++i) {- qq->append(static_cast<Media_Query_Expression_Ptr>((*q)[i]->perform(this)));+ qq->append(static_cast<Media_Query_Expression*>((*q)[i]->perform(this))); } return qq.detach(); } - Expression_Ptr Eval::operator()(Media_Query_Expression_Ptr e)+ Expression* Eval::operator()(Media_Query_Expression* e) { Expression_Obj feature = e->feature(); feature = (feature ? feature->perform(this) : 0);@@ -1363,12 +1407,12 @@ e->is_interpolated()); } - Expression_Ptr Eval::operator()(Null_Ptr n)+ Expression* Eval::operator()(Null* n) { return n; } - Expression_Ptr Eval::operator()(Argument_Ptr a)+ Expression* Eval::operator()(Argument* a) { Expression_Obj val = a->value()->perform(this); bool is_rest_argument = a->is_rest_argument();@@ -1397,13 +1441,13 @@ is_keyword_argument); } - Expression_Ptr Eval::operator()(Arguments_Ptr a)+ Expression* Eval::operator()(Arguments* a) { Arguments_Obj aa = SASS_MEMORY_NEW(Arguments, a->pstate()); if (a->length() == 0) return aa.detach(); for (size_t i = 0, L = a->length(); i < L; ++i) { Expression_Obj rv = (*a)[i]->perform(this);- Argument_Ptr arg = Cast<Argument>(rv);+ Argument* arg = Cast<Argument>(rv); if (!(arg->is_rest_argument() || arg->is_keyword_argument())) { aa->append(arg); }@@ -1414,8 +1458,8 @@ Expression_Obj splat = Cast<Argument>(rest)->value()->perform(this); Sass_Separator separator = SASS_COMMA;- List_Ptr ls = Cast<List>(splat);- Map_Ptr ms = Cast<Map>(splat);+ List* ls = Cast<List>(splat);+ Map* ms = Cast<Map>(splat); List_Obj arglist = SASS_MEMORY_NEW(List, splat->pstate(),@@ -1439,7 +1483,7 @@ if (a->has_keyword_argument()) { Expression_Obj rv = a->get_keyword_argument()->perform(this);- Argument_Ptr rvarg = Cast<Argument>(rv);+ Argument* rvarg = Cast<Argument>(rv); Expression_Obj kwarg = rvarg->value()->perform(this); aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "", false, true));@@ -1447,217 +1491,47 @@ return aa.detach(); } - Expression_Ptr Eval::operator()(Comment_Ptr c)+ Expression* Eval::operator()(Comment* c) { return 0; } - inline Expression_Ptr Eval::fallback_impl(AST_Node_Ptr n)- {- return static_cast<Expression_Ptr>(n);- }-- // All the binary helpers.-- Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate)- {- using std::strlen;- using std::strcpy;- Expression_Ptr e = NULL;- switch (sass_value_get_tag(v)) {- case SASS_BOOLEAN: {- e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));- } break;- case SASS_NUMBER: {- e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));- } break;- case SASS_COLOR: {- e = SASS_MEMORY_NEW(Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));- } break;- case SASS_STRING: {- if (sass_string_is_quoted(v))- e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v));- else {- e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));- }- } break;- case SASS_LIST: {- List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));- for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {- l->append(cval_to_astnode(sass_list_get_value(v, i), traces, pstate));- }- l->is_bracketed(sass_list_get_is_bracketed(v));- e = l;- } break;- case SASS_MAP: {- Map_Ptr m = SASS_MEMORY_NEW(Map, pstate);- for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {- *m << std::make_pair(- cval_to_astnode(sass_map_get_key(v, i), traces, pstate),- cval_to_astnode(sass_map_get_value(v, i), traces, pstate));- }- e = m;- } break;- case SASS_NULL: {- e = SASS_MEMORY_NEW(Null, pstate);- } break;- case SASS_ERROR: {- error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces);- } break;- case SASS_WARNING: {- error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces);- } break;- default: break;- }- return e;- }-- Selector_List_Ptr Eval::operator()(Selector_List_Ptr s)- {- std::vector<Selector_List_Obj> rv;- Selector_List_Obj sl = SASS_MEMORY_NEW(Selector_List, s->pstate());- sl->is_optional(s->is_optional());- sl->media_block(s->media_block());- sl->is_optional(s->is_optional());- for (size_t i = 0, iL = s->length(); i < iL; ++i) {- rv.push_back(operator()((*s)[i]));- }-- // we should actually permutate parent first- // but here we have permutated the selector first- size_t round = 0;- while (round != std::string::npos) {- bool abort = true;- for (size_t i = 0, iL = rv.size(); i < iL; ++i) {- if (rv[i]->length() > round) {- sl->append((*rv[i])[round]);- abort = false;- }- }- if (abort) {- round = std::string::npos;- } else {- ++ round;- }-- }- return sl.detach();- }--- Selector_List_Ptr Eval::operator()(Complex_Selector_Ptr s)- {- bool implicit_parent = !exp.old_at_root_without_rule;- if (is_in_selector_schema) exp.selector_stack.push_back(0);- Selector_List_Obj resolved = s->resolve_parent_refs(exp.selector_stack, traces, implicit_parent);- if (is_in_selector_schema) exp.selector_stack.pop_back();- for (size_t i = 0; i < resolved->length(); i++) {- Complex_Selector_Ptr is = resolved->at(i)->first();- while (is) {- if (is->head()) {- is->head(operator()(is->head()));- }- is = is->tail();- }- }- return resolved.detach();- }-- Compound_Selector_Ptr Eval::operator()(Compound_Selector_Ptr s)- {- for (size_t i = 0; i < s->length(); i++) {- Simple_Selector_Ptr ss = s->at(i);- // skip parents here (called via resolve_parent_refs)- if (ss == NULL || Cast<Parent_Selector>(ss)) continue;- s->at(i) = Cast<Simple_Selector>(ss->perform(this));- }- return s;- }-- Selector_List_Ptr Eval::operator()(Selector_Schema_Ptr s)+ SelectorList* Eval::operator()(Selector_Schema* s) { LOCAL_FLAG(is_in_selector_schema, true); // the parser will look for a brace to end the selector- ctx.c_options.in_selector = true; // do not compress colors Expression_Obj sel = s->contents()->perform(this);- std::string result_str(sel->to_string(ctx.c_options));- ctx.c_options.in_selector = false; // flag temporary only+ std::string result_str(sel->to_string(options())); result_str = unquote(Util::rtrim(result_str)); char* temp_cstr = sass_copy_c_string(result_str.c_str()); ctx.strings.push_back(temp_cstr); // attach to context Parser p = Parser::from_c_str(temp_cstr, ctx, traces, s->pstate());- p.last_media_block = s->media_block();- // a selector schema may or may not connect to parent?- bool chroot = s->connect_parent() == false;- Selector_List_Obj sl = p.parse_selector_list(chroot);- auto vec_str_rend = ctx.strings.rend();- auto vec_str_rbegin = ctx.strings.rbegin();- // remove the first item searching from the back- // we cannot assume our item is still the last one- // order is not important, so we can optimize this- auto it = std::find(vec_str_rbegin, vec_str_rend, temp_cstr);- // undefined behavior if not found!- if (it != vec_str_rend) {- // overwrite with last item- *it = ctx.strings.back();- // remove last one from vector- ctx.strings.pop_back();- // free temporary copy- free(temp_cstr);- }++ // If a schema contains a reference to parent it is already+ // connected to it, so don't connect implicitly anymore+ SelectorListObj parsed = p.parseSelectorList(true); flag_is_in_selector_schema.reset();- return operator()(sl);+ return parsed.detach(); } - Expression_Ptr Eval::operator()(Parent_Selector_Ptr p)+ Expression* Eval::operator()(Parent_Reference* p) {- if (Selector_List_Obj pr = selector()) {- exp.selector_stack.pop_back();- Selector_List_Obj rv = operator()(pr);- exp.selector_stack.push_back(rv);- return rv.detach();+ if (SelectorListObj pr = exp.original()) {+ return operator()(pr); } else { return SASS_MEMORY_NEW(Null, p->pstate()); } } - Simple_Selector_Ptr Eval::operator()(Simple_Selector_Ptr s)+ SimpleSelector* Eval::operator()(SimpleSelector* s) { return s; } - // hotfix to avoid invalid nested `:not` selectors- // probably the wrong place, but this should ultimately- // be fixed by implement superselector correctly for `:not`- // first use of "find" (ATM only implemented for selectors)- bool hasNotSelector(AST_Node_Obj obj) {- if (Wrapped_Selector_Ptr w = Cast<Wrapped_Selector>(obj)) {- return w->name() == ":not";- }- return false;- }-- Wrapped_Selector_Ptr Eval::operator()(Wrapped_Selector_Ptr s)+ Pseudo_Selector* Eval::operator()(Pseudo_Selector* pseudo) {-- if (s->name() == ":not") {- if (exp.selector_stack.back()) {- if (s->selector()->find(hasNotSelector)) {- s->selector()->clear();- s->name(" ");- } else if (s->selector()->length() == 1) {- Complex_Selector_Ptr cs = s->selector()->at(0);- if (cs->tail()) {- s->selector()->clear();- s->name(" ");- }- } else if (s->selector()->length() > 1) {- s->selector()->clear();- s->name(" ");- }- }- }- return s;+ // ToDo: should we eval selector?+ return pseudo; }; }
libsass/src/eval.hpp view
@@ -1,7 +1,11 @@ #ifndef SASS_EVAL_H #define SASS_EVAL_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp" #include "ast.hpp"+ #include "context.hpp" #include "listize.hpp" #include "operation.hpp"@@ -12,10 +16,7 @@ class Expand; class Context; - class Eval : public Operation_CRTP<Expression_Ptr, Eval> {-- private:- Expression_Ptr fallback_impl(AST_Node_Ptr n);+ class Eval : public Operation_CRTP<Expression*, Eval> { public: Expand& exp;@@ -32,71 +33,77 @@ Boolean_Obj bool_false; Env* environment();- Selector_List_Obj selector();+ EnvStack& env_stack();+ const std::string cwd();+ CalleeStack& callee_stack();+ struct Sass_Inspect_Options& options();+ struct Sass_Compiler* compiler(); // for evaluating function bodies- Expression_Ptr operator()(Block_Ptr);- Expression_Ptr operator()(Assignment_Ptr);- Expression_Ptr operator()(If_Ptr);- Expression_Ptr operator()(For_Ptr);- Expression_Ptr operator()(Each_Ptr);- Expression_Ptr operator()(While_Ptr);- Expression_Ptr operator()(Return_Ptr);- Expression_Ptr operator()(Warning_Ptr);- Expression_Ptr operator()(Error_Ptr);- Expression_Ptr operator()(Debug_Ptr);+ Expression* operator()(Block*);+ Expression* operator()(Assignment*);+ Expression* operator()(If*);+ Expression* operator()(For*);+ Expression* operator()(Each*);+ Expression* operator()(While*);+ Expression* operator()(Return*);+ Expression* operator()(Warning*);+ Expression* operator()(Error*);+ Expression* operator()(Debug*); - Expression_Ptr operator()(List_Ptr);- Expression_Ptr operator()(Map_Ptr);- Expression_Ptr operator()(Binary_Expression_Ptr);- Expression_Ptr operator()(Unary_Expression_Ptr);- Expression_Ptr operator()(Function_Call_Ptr);- Expression_Ptr operator()(Function_Call_Schema_Ptr);- Expression_Ptr operator()(Variable_Ptr);- Expression_Ptr operator()(Number_Ptr);- Expression_Ptr operator()(Color_Ptr);- Expression_Ptr operator()(Boolean_Ptr);- Expression_Ptr operator()(String_Schema_Ptr);- Expression_Ptr operator()(String_Quoted_Ptr);- Expression_Ptr operator()(String_Constant_Ptr);- // Expression_Ptr operator()(Selector_List_Ptr);- Media_Query_Ptr operator()(Media_Query_Ptr);- Expression_Ptr operator()(Media_Query_Expression_Ptr);- Expression_Ptr operator()(At_Root_Query_Ptr);- Expression_Ptr operator()(Supports_Operator_Ptr);- Expression_Ptr operator()(Supports_Negation_Ptr);- Expression_Ptr operator()(Supports_Declaration_Ptr);- Expression_Ptr operator()(Supports_Interpolation_Ptr);- Expression_Ptr operator()(Null_Ptr);- Expression_Ptr operator()(Argument_Ptr);- Expression_Ptr operator()(Arguments_Ptr);- Expression_Ptr operator()(Comment_Ptr);+ Expression* operator()(List*);+ Expression* operator()(Map*);+ Expression* operator()(Binary_Expression*);+ Expression* operator()(Unary_Expression*);+ Expression* operator()(Function_Call*);+ Expression* operator()(Variable*);+ Expression* operator()(Number*);+ Expression* operator()(Color_RGBA*);+ Expression* operator()(Color_HSLA*);+ Expression* operator()(Boolean*);+ Expression* operator()(String_Schema*);+ Expression* operator()(String_Quoted*);+ Expression* operator()(String_Constant*);+ Media_Query* operator()(Media_Query*);+ Expression* operator()(Media_Query_Expression*);+ Expression* operator()(At_Root_Query*);+ Expression* operator()(Supports_Operator*);+ Expression* operator()(Supports_Negation*);+ Expression* operator()(Supports_Declaration*);+ Expression* operator()(Supports_Interpolation*);+ Expression* operator()(Null*);+ Expression* operator()(Argument*);+ Expression* operator()(Arguments*);+ Expression* operator()(Comment*); // these will return selectors- Selector_List_Ptr operator()(Selector_List_Ptr);- Selector_List_Ptr operator()(Complex_Selector_Ptr);- Compound_Selector_Ptr operator()(Compound_Selector_Ptr);- Simple_Selector_Ptr operator()(Simple_Selector_Ptr s);- Wrapped_Selector_Ptr operator()(Wrapped_Selector_Ptr s);+ SelectorList* operator()(SelectorList*);+ SelectorList* operator()(ComplexSelector*);+ CompoundSelector* operator()(CompoundSelector*);+ SelectorComponent* operator()(SelectorComponent*);+ SimpleSelector* operator()(SimpleSelector* s);+ Pseudo_Selector* operator()(Pseudo_Selector* s);+ // they don't have any specific implementation (yet)- // Element_Selector_Ptr operator()(Element_Selector_Ptr s) { return s; };- // Pseudo_Selector_Ptr operator()(Pseudo_Selector_Ptr s) { return s; };- // Class_Selector_Ptr operator()(Class_Selector_Ptr s) { return s; };- // Id_Selector_Ptr operator()(Id_Selector_Ptr s) { return s; };- // Placeholder_Selector_Ptr operator()(Placeholder_Selector_Ptr s) { return s; };+ Id_Selector* operator()(Id_Selector* s) { return s; };+ Class_Selector* operator()(Class_Selector* s) { return s; };+ Type_Selector* operator()(Type_Selector* s) { return s; };+ Attribute_Selector* operator()(Attribute_Selector* s) { return s; };+ Placeholder_Selector* operator()(Placeholder_Selector* s) { return s; };+ // actual evaluated selectors- Selector_List_Ptr operator()(Selector_Schema_Ptr);- Expression_Ptr operator()(Parent_Selector_Ptr);+ SelectorList* operator()(Selector_Schema*);+ Expression* operator()(Parent_Reference*); + // generic fallback template <typename U>- Expression_Ptr fallback(U x) { return fallback_impl(x); }+ Expression* fallback(U x)+ { return Cast<Expression>(x); } private: void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false); };-- Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate = ParserState("[AST]")); }
+ libsass/src/eval_selectors.cpp view
@@ -0,0 +1,75 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "expand.hpp"+#include "eval.hpp"+#include "ast.hpp"+++namespace Sass {++ SelectorList* Eval::operator()(SelectorList* s)+ {+ std::vector<SelectorListObj> rv;+ SelectorListObj sl = SASS_MEMORY_NEW(SelectorList, s->pstate());+ for (size_t i = 0, iL = s->length(); i < iL; ++i) {+ rv.push_back(operator()(s->get(i)));+ }++ // we should actually permutate parent first+ // but here we have permutated the selector first+ size_t round = 0;+ while (round != std::string::npos) {+ bool abort = true;+ for (size_t i = 0, iL = rv.size(); i < iL; ++i) {+ if (rv[i]->length() > round) {+ sl->append((*rv[i])[round]);+ abort = false;+ }+ }+ if (abort) {+ round = std::string::npos;+ }+ else {+ ++round;+ }++ }+ return sl.detach();+ }++ SelectorComponent* Eval::operator()(SelectorComponent* s)+ {+ return {};+ }++ SelectorList* Eval::operator()(ComplexSelector* s)+ {+ bool implicit_parent = !exp.old_at_root_without_rule;+ if (is_in_selector_schema) exp.pushNullSelector();+ SelectorListObj other = s->resolve_parent_refs(+ exp.getOriginalStack(), traces, implicit_parent);+ if (is_in_selector_schema) exp.popNullSelector();++ for (size_t i = 0; i < other->length(); i++) {+ ComplexSelectorObj sel = other->at(i);+ for (size_t n = 0; n < sel->length(); n++) {+ if (CompoundSelectorObj comp = Cast<CompoundSelector>(sel->at(n))) {+ sel->at(n) = operator()(comp);+ }+ }+ }++ return other.detach();+ }++ CompoundSelector* Eval::operator()(CompoundSelector* s)+ {+ for (size_t i = 0; i < s->length(); i++) {+ SimpleSelector* ss = s->at(i);+ // skip parents here (called via resolve_parent_refs)+ s->at(i) = Cast<SimpleSelector>(ss->perform(this));+ }+ return s;+ }+}
libsass/src/expand.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include <typeinfo> @@ -16,7 +19,7 @@ // simple endless recursion protection const size_t maxRecursion = 500; - Expand::Expand(Context& ctx, Env* env, std::vector<Selector_List_Obj>* stack)+ Expand::Expand(Context& ctx, Env* env, SelectorStack* stack, SelectorStack* originals) : ctx(ctx), traces(ctx.traces), eval(Eval(*this)),@@ -24,19 +27,32 @@ in_keyframes(false), at_root_without_rule(false), old_at_root_without_rule(false),- env_stack(std::vector<Env*>()),- block_stack(std::vector<Block_Ptr>()),- call_stack(std::vector<AST_Node_Obj>()),- selector_stack(std::vector<Selector_List_Obj>()),- media_block_stack(std::vector<Media_Block_Ptr>())+ env_stack(),+ block_stack(),+ call_stack(),+ selector_stack(),+ originalStack(),+ mediaStack() {- env_stack.push_back(0);+ env_stack.push_back(nullptr); env_stack.push_back(env);- block_stack.push_back(0);- call_stack.push_back(0);- if (stack == NULL) { selector_stack.push_back(0); }- else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); }- media_block_stack.push_back(0);+ block_stack.push_back(nullptr);+ call_stack.push_back({});+ if (stack == NULL) { pushToSelectorStack({}); }+ else {+ for (auto item : *stack) {+ if (item.isNull()) pushToSelectorStack({});+ else pushToSelectorStack(item);+ }+ }+ if (originals == NULL) { pushToOriginalStack({}); }+ else {+ for (auto item : *stack) {+ if (item.isNull()) pushToOriginalStack({});+ else pushToOriginalStack(item);+ }+ }+ mediaStack.push_back({}); } Env* Expand::environment()@@ -46,15 +62,84 @@ return 0; } - Selector_List_Obj Expand::selector()+ void Expand::pushNullSelector() {- if (selector_stack.size() > 0)- return selector_stack.back();- return 0;+ pushToSelectorStack({});+ pushToOriginalStack({}); } + void Expand::popNullSelector()+ {+ popFromOriginalStack();+ popFromSelectorStack();+ }++ SelectorStack Expand::getOriginalStack()+ {+ return originalStack;+ }++ SelectorStack Expand::getSelectorStack()+ {+ return selector_stack;+ }++ SelectorListObj& Expand::selector()+ {+ if (selector_stack.size() > 0) {+ auto& sel = selector_stack.back();+ if (sel.isNull()) return sel;+ return sel;+ }+ // Avoid the need to return copies+ // We always want an empty first item+ selector_stack.push_back({});+ return selector_stack.back();;+ }++ SelectorListObj& Expand::original()+ {+ if (originalStack.size() > 0) {+ auto& sel = originalStack.back();+ if (sel.isNull()) return sel;+ return sel;+ }+ // Avoid the need to return copies+ // We always want an empty first item+ originalStack.push_back({});+ return originalStack.back();+ }++ SelectorListObj Expand::popFromSelectorStack()+ {+ SelectorListObj last = selector_stack.back();+ if (selector_stack.size() > 0) + selector_stack.pop_back();+ if (last.isNull()) return {};+ return last;+ }++ void Expand::pushToSelectorStack(SelectorListObj selector)+ {+ selector_stack.push_back(selector);+ }++ SelectorListObj Expand::popFromOriginalStack()+ {+ SelectorListObj last = originalStack.back();+ if (originalStack.size() > 0)+ originalStack.pop_back();+ if (last.isNull()) return {};+ return last;+ }++ void Expand::pushToOriginalStack(SelectorListObj selector)+ {+ originalStack.push_back(selector);+ }+ // blocks create new variable scopes- Block_Ptr Expand::operator()(Block_Ptr b)+ Block* Expand::operator()(Block* b) { // create new local environment // set the current env as parent@@ -77,76 +162,62 @@ return bb.detach(); } - Statement_Ptr Expand::operator()(Ruleset_Ptr r)+ Statement* Expand::operator()(Ruleset* r) { LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule); if (in_keyframes) {- Block_Ptr bb = operator()(r->block());+ Block* bb = operator()(r->block()); Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);- if (r->selector()) {- if (Selector_List_Ptr s = r->selector()) {- selector_stack.push_back(0);- k->name(s->eval(eval));- selector_stack.pop_back();+ if (r->schema()) {+ pushNullSelector();+ k->name(eval(r->schema()));+ popNullSelector();+ }+ else if (r->selector()) {+ if (SelectorListObj s = r->selector()) {+ pushNullSelector();+ k->name(eval(s));+ popNullSelector(); } }+ return k.detach(); } - // reset when leaving scope- LOCAL_FLAG(at_root_without_rule, false);+ if (r->schema()) {+ SelectorListObj sel = eval(r->schema());+ r->selector(sel);+ bool chroot = sel->has_real_parent_ref();+ for (auto complex : sel->elements()) {+ complex->chroots(chroot);+ } - // `&` is allowed in `@at-root`!- bool has_parent_selector = false;- for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) {- Selector_List_Obj ll = selector_stack.at(i);- has_parent_selector = ll != 0 && ll->length() > 0; } - Selector_List_Obj sel = r->selector();- if (sel) sel = sel->eval(eval);-- // check for parent selectors in base level rules- if (r->is_root() || (block_stack.back() && block_stack.back()->is_root())) {- if (Selector_List_Ptr selector_list = Cast<Selector_List>(r->selector())) {- for (Complex_Selector_Obj complex_selector : selector_list->elements()) {- Complex_Selector_Ptr tail = complex_selector;- while (tail) {- if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {- Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header);- if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue;- std::string sel_str(complex_selector->to_string(ctx.c_options));- error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), traces);- }- tail = tail->tail();- }- }- }- }- else {- if (sel->length() == 0 || sel->has_parent_ref()) {- if (sel->has_real_parent_ref() && !has_parent_selector) {- error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), traces);- }- }- }+ // reset when leaving scope+ LOCAL_FLAG(at_root_without_rule, false); + SelectorListObj evaled = eval(r->selector()); // do not connect parent again- sel->remove_parent_selectors();- selector_stack.push_back(sel); Env env(environment()); if (block_stack.back()->is_root()) { env_stack.push_back(&env); }- sel->set_media_block(media_block_stack.back());- Block_Obj blk = 0;+ Block_Obj blk;+ pushToSelectorStack(evaled);+ // The copy is needed for parent reference evaluation+ // dart-sass stores it as `originalSelector` member+ pushToOriginalStack(SASS_MEMORY_COPY(evaled));+ ctx.extender.addSelector(evaled, mediaStack.back()); if (r->block()) blk = operator()(r->block());- Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset,+ popFromOriginalStack();+ popFromSelectorStack();+ Ruleset* rr = SASS_MEMORY_NEW(Ruleset, r->pstate(),- sel,+ evaled, blk);- selector_stack.pop_back();+ if (block_stack.back()->is_root()) { env_stack.pop_back(); }@@ -157,7 +228,7 @@ return rr; } - Statement_Ptr Expand::operator()(Supports_Block_Ptr f)+ Statement* Expand::operator()(Supports_Block* f) { Expression_Obj condition = f->condition()->perform(&eval); Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block,@@ -167,34 +238,47 @@ return ff.detach(); } - Statement_Ptr Expand::operator()(Media_Block_Ptr m)+ std::vector<CssMediaQuery_Obj> Expand::mergeMediaQueries(+ const std::vector<CssMediaQuery_Obj>& lhs,+ const std::vector<CssMediaQuery_Obj>& rhs) {- Media_Block_Obj cpy = SASS_MEMORY_COPY(m);- // Media_Blocks are prone to have circular references- // Copy could leak memory if it does not get picked up- // Looks like we are able to reset block reference for copy- // Good as it will ensure a low memory overhead for this fix- // So this is a cheap solution with a minimal price- ctx.ast_gc.push_back(cpy); cpy->block(0);- Expression_Obj mq = eval(m->media_queries());- std::string str_mq(mq->to_string(ctx.c_options));+ std::vector<CssMediaQuery_Obj> queries;+ for (CssMediaQuery_Obj query1 : lhs) {+ for (CssMediaQuery_Obj query2 : rhs) {+ CssMediaQuery_Obj result = query1->merge(query2);+ if (result && !result->empty()) {+ queries.push_back(result);+ }+ }+ }+ return queries;+ }++ Statement* Expand::operator()(MediaRule* m)+ {+ Expression_Obj mq = eval(m->schema());+ std::string str_mq(mq->to_css(ctx.c_options)); char* str = sass_copy_c_string(str_mq.c_str()); ctx.strings.push_back(str);- Parser p(Parser::from_c_str(str, ctx, traces, mq->pstate()));- mq = p.parse_media_queries(); // re-assign now- cpy->media_queries(mq);- media_block_stack.push_back(cpy);- Block_Obj blk = operator()(m->block());- Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block,- m->pstate(),- mq,- blk);- media_block_stack.pop_back();- mm->tabs(m->tabs());- return mm;+ Parser parser(Parser::from_c_str(str, ctx, traces, mq->pstate()));+ // Create a new CSS only representation of the media rule+ CssMediaRuleObj css = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());+ std::vector<CssMediaQuery_Obj> parsed = parser.parseCssMediaQueries();+ if (mediaStack.size() && mediaStack.back()) {+ auto& parent = mediaStack.back()->elements();+ css->concat(mergeMediaQueries(parent, parsed));+ }+ else {+ css->concat(parsed);+ }+ mediaStack.push_back(css);+ css->block(operator()(m->block()));+ mediaStack.pop_back();+ return css.detach();+ } - Statement_Ptr Expand::operator()(At_Root_Block_Ptr a)+ Statement* Expand::operator()(At_Root_Block* a) { Block_Obj ab = a->block(); Expression_Obj ae = a->expression();@@ -202,7 +286,7 @@ if (ae) ae = ae->perform(&eval); else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate()); - LOCAL_FLAG(at_root_without_rule, true);+ LOCAL_FLAG(at_root_without_rule, Cast<At_Root_Query>(ae)->exclude("rule")); LOCAL_FLAG(in_keyframes, false); ;@@ -215,18 +299,18 @@ return aa.detach(); } - Statement_Ptr Expand::operator()(Directive_Ptr a)+ Statement* Expand::operator()(Directive* a) { LOCAL_FLAG(in_keyframes, a->is_keyframes());- Block_Ptr ab = a->block();- Selector_List_Ptr as = a->selector();- Expression_Ptr av = a->value();- selector_stack.push_back(0);+ Block* ab = a->block();+ SelectorList* as = a->selector();+ Expression* av = a->value();+ pushNullSelector(); if (av) av = av->perform(&eval); if (as) as = eval(as);- selector_stack.pop_back();- Block_Ptr bb = ab ? operator()(ab) : NULL;- Directive_Ptr aa = SASS_MEMORY_NEW(Directive,+ popNullSelector();+ Block* bb = ab ? operator()(ab) : NULL;+ Directive* aa = SASS_MEMORY_NEW(Directive, a->pstate(), a->keyword(), as,@@ -235,7 +319,7 @@ return aa; } - Statement_Ptr Expand::operator()(Declaration_Ptr d)+ Statement* Expand::operator()(Declaration* d) { Block_Obj ab = d->block(); String_Obj old_p = d->property();@@ -250,9 +334,15 @@ if (value) value = value->perform(&eval); Block_Obj bb = ab ? operator()(ab) : NULL; if (!bb) {- if (!value || (value->is_invisible() && !d->is_important())) return 0;+ if (!value || (value->is_invisible() && !d->is_important())) {+ if (d->is_custom_property()) {+ error("Custom property values may not be empty.", d->value()->pstate(), traces);+ } else {+ return nullptr;+ }+ } }- Declaration_Ptr decl = SASS_MEMORY_NEW(Declaration,+ Declaration* decl = SASS_MEMORY_NEW(Declaration, d->pstate(), new_p, value,@@ -263,7 +353,7 @@ return decl; } - Statement_Ptr Expand::operator()(Assignment_Ptr a)+ Statement* Expand::operator()(Assignment* a) { Env* env = environment(); const std::string& var(a->variable());@@ -324,7 +414,7 @@ return 0; } - Statement_Ptr Expand::operator()(Import_Ptr imp)+ Statement* Expand::operator()(Import* imp) { Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate()); if (imp->import_queries() && imp->import_queries()->size()) {@@ -339,7 +429,7 @@ return result.detach(); } - Statement_Ptr Expand::operator()(Import_Stub_Ptr i)+ Statement* Expand::operator()(Import_Stub* i) { traces.push_back(Backtrace(i->pstate())); // get parent node from call stack@@ -369,28 +459,28 @@ return 0; } - Statement_Ptr Expand::operator()(Warning_Ptr w)+ Statement* Expand::operator()(Warning* w) { // eval handles this too, because warnings may occur in functions w->perform(&eval); return 0; } - Statement_Ptr Expand::operator()(Error_Ptr e)+ Statement* Expand::operator()(Error* e) { // eval handles this too, because errors may occur in functions e->perform(&eval); return 0; } - Statement_Ptr Expand::operator()(Debug_Ptr d)+ Statement* Expand::operator()(Debug* d) { // eval handles this too, because warnings may occur in functions d->perform(&eval); return 0; } - Statement_Ptr Expand::operator()(Comment_Ptr c)+ Statement* Expand::operator()(Comment* c) { if (ctx.output_style() == COMPRESSED) { // comments should not be evaluated in compact@@ -398,13 +488,13 @@ if (!c->is_important()) return NULL; } eval.is_in_comment = true;- Comment_Ptr rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important());+ Comment* rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important()); eval.is_in_comment = false; // TODO: eval the text, once we're parsing/storing it as a String_Schema return rv; } - Statement_Ptr Expand::operator()(If_Ptr i)+ Statement* Expand::operator()(If* i) { Env env(environment(), true); env_stack.push_back(&env);@@ -414,7 +504,7 @@ append_block(i->block()); } else {- Block_Ptr alt = i->alternative();+ Block* alt = i->alternative(); if (alt) append_block(alt); } call_stack.pop_back();@@ -424,7 +514,7 @@ // For does not create a new env scope // But iteration vars are reset afterwards- Statement_Ptr Expand::operator()(For_Ptr f)+ Statement* Expand::operator()(For* f) { std::string variable(f->variable()); Expression_Obj low = f->lower_bound()->perform(&eval);@@ -452,7 +542,7 @@ Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(f);- Block_Ptr body = f->block();+ Block* body = f->block(); if (start < end) { if (f->is_inclusive()) ++end; for (double i = start;@@ -479,18 +569,17 @@ // Eval does not create a new env scope // But iteration vars are reset afterwards- Statement_Ptr Expand::operator()(Each_Ptr e)+ Statement* Expand::operator()(Each* e) { std::vector<std::string> variables(e->variables()); Expression_Obj expr = e->list()->perform(&eval);- List_Obj list = 0;+ List_Obj list; Map_Obj map; if (expr->concrete_type() == Expression::MAP) { map = Cast<Map>(expr); }- else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {- Listize listize;- Expression_Obj rv = ls->perform(&listize);+ else if (SelectorList * ls = Cast<SelectorList>(expr)) {+ Expression_Obj rv = Listize::perform(ls); list = Cast<List>(rv); } else if (expr->concrete_type() != Expression::LIST) {@@ -504,7 +593,7 @@ Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(e);- Block_Ptr body = e->block();+ Block* body = e->block(); if (map) { for (auto key : map->keys()) {@@ -525,7 +614,7 @@ } else { // bool arglist = list->is_arglist();- if (list->length() == 1 && Cast<Selector_List>(list)) {+ if (list->length() == 1 && Cast<SelectorList>(list)) { list = Cast<List>(list); } for (size_t i = 0, L = list->length(); i < L; ++i) {@@ -563,10 +652,10 @@ return 0; } - Statement_Ptr Expand::operator()(While_Ptr w)+ Statement* Expand::operator()(While* w) { Expression_Obj pred = w->predicate();- Block_Ptr body = w->block();+ Block* body = w->block(); Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(w);@@ -580,96 +669,72 @@ return 0; } - Statement_Ptr Expand::operator()(Return_Ptr r)+ Statement* Expand::operator()(Return* r) { error("@return may only be used within a function", r->pstate(), traces); return 0; } -- void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {+ Statement* Expand::operator()(ExtendRule* e)+ { - if (Selector_List_Obj sl = Cast<Selector_List>(s)) {- for (Complex_Selector_Obj complex_selector : sl->elements()) {- Complex_Selector_Obj tail = complex_selector;- while (tail) {- if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {- if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others- std::string sel_str(complex_selector->to_string(ctx.c_options));- error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), traces);- }- tail = tail->tail();- }- }+ // evaluate schema first+ if (e->schema()) {+ e->selector(eval(e->schema()));+ e->isOptional(e->selector()->is_optional()); }+ // evaluate the selector+ e->selector(eval(e->selector())); + if (e->selector()) { - Selector_List_Obj contextualized = Cast<Selector_List>(s->perform(&eval));- if (contextualized == false) return;- for (auto complex_sel : contextualized->elements()) {- Complex_Selector_Obj c = complex_sel;- if (!c->head() || c->tail()) {- std::string sel_str(contextualized->to_string(ctx.c_options));- error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), traces);- }- Compound_Selector_Obj target = c->head();- if (contextualized->is_optional()) target->is_optional(true);- for (size_t i = 0, L = extender->length(); i < L; ++i) {- Complex_Selector_Obj sel = (*extender)[i];- if (!(sel->head() && sel->head()->length() > 0 &&- Cast<Parent_Selector>((*sel->head())[0])))- {- Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate());- hh->media_block((*extender)[i]->media_block());- Complex_Selector_Obj ssel = SASS_MEMORY_NEW(Complex_Selector, (*extender)[i]->pstate());- ssel->media_block((*extender)[i]->media_block());- if (sel->has_line_feed()) ssel->has_line_feed(true);- Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate());- ps->media_block((*extender)[i]->media_block());- hh->append(ps);- ssel->tail(sel);- ssel->head(hh);- sel = ssel;+ for (auto complex : e->selector()->elements()) {++ if (complex->length() != 1) {+ error("complex selectors may not be extended.", complex->pstate(), traces); }- // if (c->has_line_feed()) sel->has_line_feed(true);- ctx.subset_map.put(target, std::make_pair(sel, target));- }- } - }+ if (const CompoundSelector* compound = complex->first()->getCompound()) { - Statement* Expand::operator()(Extension_Ptr e)- {- if (Selector_List_Ptr extender = selector()) {- Selector_List_Ptr sl = e->selector();- // abort on invalid selector- if (sl == NULL) return NULL;- if (Selector_Schema_Ptr schema = sl->schema()) {- if (schema->has_real_parent_ref()) {- // put root block on stack again (ignore parents)- // selector schema must not connect in eval!- block_stack.push_back(block_stack.at(1));- sl = eval(sl->schema());- block_stack.pop_back();- } else {- selector_stack.push_back(0);- sl = eval(sl->schema());- selector_stack.pop_back();+ if (compound->length() != 1) {++ std::stringstream sels; bool addComma = false;+ sels << "Compound selectors may no longer be extended.\n";+ sels << "Consider `@extend ";+ for (auto sel : compound->elements()) {+ if (addComma) sels << ", ";+ sels << sel->to_sass();+ addComma = true;+ }+ sels << "` instead.\n";+ sels << "See http://bit.ly/ExtendCompound for details.";++ warning(sels.str(), compound->pstate());++ // Make this an error once deprecation is over+ for (SimpleSelectorObj simple : compound->elements()) {+ // Pass every selector we ever see to extender (to make them findable for extend)+ ctx.extender.addExtension(selector(), simple, mediaStack.back(), e->isOptional());+ }++ }+ else {+ // Pass every selector we ever see to extender (to make them findable for extend)+ ctx.extender.addExtension(selector(), compound->first(), mediaStack.back(), e->isOptional());+ }+ }- }- for (Complex_Selector_Obj cs : sl->elements()) {- if (!cs.isNull() && !cs->head().isNull()) {- cs->head()->media_block(media_block_stack.back());+ else {+ error("complex selectors may not be extended.", complex->pstate(), traces); } }- selector_stack.push_back(0);- expand_selector_list(sl, extender);- selector_stack.pop_back(); }- return 0;++ return nullptr;+ } - Statement_Ptr Expand::operator()(Definition_Ptr d)+ Statement* Expand::operator()(Definition* d) { Env* env = environment(); Definition_Obj dd = SASS_MEMORY_COPY(d);@@ -694,8 +759,9 @@ return 0; } - Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)+ Statement* Expand::operator()(Mixin_Call* c) {+ if (recursions > maxRecursion) { throw Exception::StackError(traces, *c); }@@ -730,29 +796,31 @@ Env new_env(def->environment()); env_stack.push_back(&new_env); if (c->block()) {+ Parameters_Obj params = c->block_parameters();+ if (!params) params = SASS_MEMORY_NEW(Parameters, c->pstate()); // represent mixin content blocks as thunks/closures Definition_Obj thunk = SASS_MEMORY_NEW(Definition, c->pstate(), "@content",- SASS_MEMORY_NEW(Parameters, c->pstate()),+ params, c->block(), Definition::MIXIN); thunk->environment(env); new_env.local_frame()["@content[m]"] = thunk; } - bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);+ bind(std::string("Mixin"), c->name(), params, args, &new_env, &eval, traces); Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate()); Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block); env->set_global("is_in_mixin", bool_true);- if (Block_Ptr pr = block_stack.back()) {+ if (Block* pr = block_stack.back()) { trace_block->is_root(pr->is_root()); } block_stack.push_back(trace_block); for (auto bb : body->elements()) {- if (Ruleset_Ptr r = Cast<Ruleset>(bb)) {+ if (Ruleset* r = Cast<Ruleset>(bb)) { r->is_root(trace_block->is_root()); } Statement_Obj ith = bb->perform(this);@@ -769,45 +837,29 @@ return trace.detach(); } - Statement_Ptr Expand::operator()(Content_Ptr c)+ Statement* Expand::operator()(Content* c) { Env* env = environment(); // convert @content directives into mixin calls to the underlying thunk if (!env->has("@content[m]")) return 0;-- if (block_stack.back()->is_root()) {- selector_stack.push_back(0);- }+ Arguments_Obj args = c->arguments();+ if (!args) args = SASS_MEMORY_NEW(Arguments, c->pstate()); Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, c->pstate(), "@content",- SASS_MEMORY_NEW(Arguments, c->pstate()));+ args); Trace_Obj trace = Cast<Trace>(call->perform(this));-- if (block_stack.back()->is_root()) {- selector_stack.pop_back();- }- return trace.detach(); } - // produce an error if something is not implemented- inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n)- {- std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();- String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err);- error("unknown internal error; please contact the LibSass maintainers", n->pstate(), traces);- return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg);- }- // process and add to last block on stack- inline void Expand::append_block(Block_Ptr b)+ inline void Expand::append_block(Block* b) { if (b->is_root()) call_stack.push_back(b); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Ptr stm = b->at(i);+ Statement* stm = b->at(i); Statement_Obj ith = stm->perform(this); if (ith) block_stack.back()->append(ith); }
libsass/src/expand.hpp view
@@ -15,12 +15,23 @@ class Eval; struct Backtrace; - class Expand : public Operation_CRTP<Statement_Ptr, Expand> {+ class Expand : public Operation_CRTP<Statement*, Expand> { public: Env* environment();- Selector_List_Obj selector();+ SelectorListObj& selector();+ SelectorListObj& original();+ SelectorListObj popFromSelectorStack();+ SelectorStack getOriginalStack();+ SelectorStack getSelectorStack();+ void pushNullSelector();+ void popNullSelector();+ void pushToSelectorStack(SelectorListObj selector); + SelectorListObj popFromOriginalStack();++ void pushToOriginalStack(SelectorListObj selector);+ Context& ctx; Backtraces& traces; Eval eval;@@ -30,51 +41,56 @@ bool old_at_root_without_rule; // it's easier to work with vectors- std::vector<Env*> env_stack;- std::vector<Block_Ptr> block_stack;- std::vector<AST_Node_Obj> call_stack;- std::vector<Selector_List_Obj> selector_stack;- std::vector<Media_Block_Ptr> media_block_stack;+ EnvStack env_stack;+ BlockStack block_stack;+ CallStack call_stack;+ private:+ SelectorStack selector_stack;+ public:+ SelectorStack originalStack;+ MediaStack mediaStack; Boolean_Obj bool_true; - Statement_Ptr fallback_impl(AST_Node_Ptr n);- private:- void expand_selector_list(Selector_Obj, Selector_List_Obj extender); + std::vector<CssMediaQuery_Obj> mergeMediaQueries(const std::vector<CssMediaQuery_Obj>& lhs, const std::vector<CssMediaQuery_Obj>& rhs);+ public:- Expand(Context&, Env*, std::vector<Selector_List_Obj>* stack = NULL);+ Expand(Context&, Env*, SelectorStack* stack = nullptr, SelectorStack* original = nullptr); ~Expand() { } - Block_Ptr operator()(Block_Ptr);- Statement_Ptr operator()(Ruleset_Ptr);- Statement_Ptr operator()(Media_Block_Ptr);- Statement_Ptr operator()(Supports_Block_Ptr);- Statement_Ptr operator()(At_Root_Block_Ptr);- Statement_Ptr operator()(Directive_Ptr);- Statement_Ptr operator()(Declaration_Ptr);- Statement_Ptr operator()(Assignment_Ptr);- Statement_Ptr operator()(Import_Ptr);- Statement_Ptr operator()(Import_Stub_Ptr);- Statement_Ptr operator()(Warning_Ptr);- Statement_Ptr operator()(Error_Ptr);- Statement_Ptr operator()(Debug_Ptr);- Statement_Ptr operator()(Comment_Ptr);- Statement_Ptr operator()(If_Ptr);- Statement_Ptr operator()(For_Ptr);- Statement_Ptr operator()(Each_Ptr);- Statement_Ptr operator()(While_Ptr);- Statement_Ptr operator()(Return_Ptr);- Statement_Ptr operator()(Extension_Ptr);- Statement_Ptr operator()(Definition_Ptr);- Statement_Ptr operator()(Mixin_Call_Ptr);- Statement_Ptr operator()(Content_Ptr);+ Block* operator()(Block*);+ Statement* operator()(Ruleset*); - template <typename U>- Statement_Ptr fallback(U x) { return fallback_impl(x); }+ Statement* operator()(MediaRule*); - void append_block(Block_Ptr);+ // Css Ruleset is already static+ // Statement* operator()(CssMediaRule*);++ Statement* operator()(Supports_Block*);+ Statement* operator()(At_Root_Block*);+ Statement* operator()(Directive*);+ Statement* operator()(Declaration*);+ Statement* operator()(Assignment*);+ Statement* operator()(Import*);+ Statement* operator()(Import_Stub*);+ Statement* operator()(Warning*);+ Statement* operator()(Error*);+ Statement* operator()(Debug*);+ Statement* operator()(Comment*);+ Statement* operator()(If*);+ Statement* operator()(For*);+ Statement* operator()(Each*);+ Statement* operator()(While*);+ Statement* operator()(Return*);+ Statement* operator()(ExtendRule*);+ Statement* operator()(Definition*);+ Statement* operator()(Mixin_Call*);+ Statement* operator()(Content*);++ void append_block(Block*);+ }; }
− libsass/src/extend.cpp
@@ -1,2130 +0,0 @@-#include "sass.hpp"-#include "extend.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "paths.hpp"-#include "parser.hpp"-#include "expand.hpp"-#include "node.hpp"-#include "sass_util.hpp"-#include "remove_placeholders.hpp"-#include "debug.hpp"-#include <iostream>-#include <deque>-#include <set>--/*- NOTES:-- - The print* functions print to cerr. This allows our testing frameworks (like sass-spec) to ignore the output, which- is very helpful when debugging. The format of the output is mainly to wrap things in square brackets to match what- ruby already outputs (to make comparisons easier).-- - For the direct porting effort, we're trying to port method-for-method until we get all the tests passing.- Where applicable, I've tried to include the ruby code above the function for reference until all our tests pass.- The ruby code isn't always directly portable, so I've tried to include any modified ruby code that was actually- used for the porting.-- - DO NOT try to optimize yet. We get a tremendous benefit out of comparing the output of each stage of the extend to the ruby- output at the same stage. This makes it much easier to determine where problems are. Try to keep as close to- the ruby code as you can until we have all the sass-spec tests passing. Then, we should optimize. However, if you see- something that could probably be optimized, let's not forget it. Add a // TODO: or // IMPROVEMENT: comment.-- - Coding conventions in this file (these may need to be changed before merging back into master)- - Very basic hungarian notation:- p prefix for pointers (pSelector)- no prefix for value types and references (selector)- - Use STL iterators where possible- - prefer verbose naming over terse naming- - use typedefs for STL container types for make maintenance easier-- - You may see a lot of comments that say "// TODO: is this the correct combinator?". See the comment referring to combinators- in extendCompoundSelector for a more extensive explanation of my confusion. I think our divergence in data model from ruby- sass causes this to be necessary.--- GLOBAL TODOS:-- - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode.-- - consider making the extend* functions member functions to avoid passing around ctx and subset_map map around. This has the- drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and- can cause additional compile time dependencies.-- - mark the helper methods in this file static to given them compilation unit linkage.-- - implement parent directive matching-- - fix compilation warnings for unused Extend members if we really don't need those references anymore.- */---namespace Sass {----#ifdef DEBUG-- // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp- std::ostream& operator<<(std::ostream& os, const Complex_Selector::Combinator combinator) {- switch (combinator) {- case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;- case Complex_Selector::PARENT_OF: os << "\">\""; break;- case Complex_Selector::PRECEDES: os << "\"~\""; break;- case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;- case Complex_Selector::REFERENCE: os << "\"/\""; break;- }-- return os;- }--- std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) {- for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) {- if (i > 0) os << ", ";- os << compoundSelector[i]->to_string();- }- return os;- }-- std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) {- os << simpleSelector.to_string();- return os;- }-- // Print a string representation of a Compound_Selector- static void printSimpleSelector(Simple_Selector* pSimpleSelector, const char* message=NULL, bool newline=true) {-- if (message) {- std::cerr << message;- }-- if (pSimpleSelector) {- std::cerr << "[" << *pSimpleSelector << "]";- } else {- std::cerr << "NULL";- }-- if (newline) {- std::cerr << std::endl;- }- }-- // Print a string representation of a Compound_Selector- static void printCompoundSelector(Compound_Selector_Ptr pCompoundSelector, const char* message=NULL, bool newline=true) {-- if (message) {- std::cerr << message;- }-- if (pCompoundSelector) {- std::cerr << "[" << *pCompoundSelector << "]";- } else {- std::cerr << "NULL";- }-- if (newline) {- std::cerr << std::endl;- }- }--- std::ostream& operator<<(std::ostream& os, Complex_Selector& complexSelector) {-- os << "[";- Complex_Selector_Ptr pIter = &complexSelector;- bool first = true;- while (pIter) {- if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {- if (!first) {- os << ", ";- }- first = false;- os << pIter->combinator();- }-- if (!first) {- os << ", ";- }- first = false;-- if (pIter->head()) {- os << pIter->head()->to_string();- } else {- os << "NULL_HEAD";- }-- pIter = pIter->tail();- }- os << "]";-- return os;- }--- // Print a string representation of a Complex_Selector- static void printComplexSelector(Complex_Selector_Ptr pComplexSelector, const char* message=NULL, bool newline=true) {-- if (message) {- std::cerr << message;- }-- if (pComplexSelector) {- std::cerr << *pComplexSelector;- } else {- std::cerr << "NULL";- }-- if (newline) {- std::cerr << std::endl;- }- }-- static void printSelsNewSeqPairCollection(SubSetMapLookups& collection, const char* message=NULL, bool newline=true) {-- if (message) {- std::cerr << message;- }- bool first = true;- std::cerr << "[";- for(SubSetMapLookup& pair : collection) {- if (first) {- first = false;- } else {- std::cerr << ", ";- }- std::cerr << "[";- Compound_Selector_Ptr pSels = pair.first;- Complex_Selector_Ptr pNewSelector = pair.second;- std::cerr << "[" << *pSels << "], ";- printComplexSelector(pNewSelector, NULL, false);- }- std::cerr << "]";-- if (newline) {- std::cerr << std::endl;- }- }-- // Print a string representation of a ComplexSelectorSet- static void printSourcesSet(ComplexSelectorSet& sources, const char* message=NULL, bool newline=true) {-- if (message) {- std::cerr << message;- }-- // Convert to a deque of strings so we can sort since order doesn't matter in a set. This should cut down on- // the differences we see when debug printing.- typedef std::deque<std::string> SourceStrings;- SourceStrings sourceStrings;- for (ComplexSelectorSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {- Complex_Selector_Ptr pSource = *iterator;- std::stringstream sstream;- sstream << complexSelectorToNode(pSource);- sourceStrings.push_back(sstream.str());- }-- // Sort to get consistent output- std::sort(sourceStrings.begin(), sourceStrings.end());-- std::cerr << "ComplexSelectorSet[";- for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) {- std::string source = *iterator;- if (iterator != sourceStrings.begin()) {- std::cerr << ", ";- }- std::cerr << source;- }- std::cerr << "]";-- if (newline) {- std::cerr << std::endl;- }- }--- std::ostream& operator<<(std::ostream& os, SubSetMapPairs& entries) {- os << "SUBSET_MAP_ENTRIES[";-- for (SubSetMapPairs::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {- Complex_Selector_Obj pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge)- Compound_Selector_Obj pExtCompoundSelector = iterator->second; // The stuff after the @extend-- if (iterator != entries.begin()) {- os << ", ";- }-- os << "(";-- if (pExtComplexSelector) {- std::cerr << *pExtComplexSelector;- } else {- std::cerr << "NULL";- }-- os << " -> ";-- if (pExtCompoundSelector) {- std::cerr << *pExtCompoundSelector;- } else {- std::cerr << "NULL";- }-- os << ")";-- }-- os << "]";-- return os;- }-#endif-- static bool parentSuperselector(Complex_Selector_Ptr pOne, Complex_Selector_Ptr pTwo) {- // TODO: figure out a better way to create a Complex_Selector from scratch- // TODO: There's got to be a better way. This got ugly quick...- Element_Selector_Obj fakeParent = SASS_MEMORY_NEW(Element_Selector, ParserState("[FAKE]"), "temp");- Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);- fakeHead->elements().push_back(fakeParent);- Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, NULL /*tail*/);-- pOne->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);- pTwo->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);-- bool isSuperselector = pOne->is_superselector_of(pTwo);-- pOne->clear_innermost();- pTwo->clear_innermost();-- return isSuperselector;- }-- void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out) {- for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {- Node& child = *iter;- out.push_back(nodeToComplexSelector(child));- }- }-- Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque) {- Node result = Node::createCollection();-- for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {- Complex_Selector_Obj pChild = *iter;- result.collection()->push_back(complexSelectorToNode(pChild));- }-- return result;- }-- class LcsCollectionComparator {- public:- LcsCollectionComparator() {}-- bool operator()(Complex_Selector_Obj pOne, Complex_Selector_Obj pTwo, Complex_Selector_Obj& pOut) const {- /*- This code is based on the following block from ruby sass' subweave- do |s1, s2|- next s1 if s1 == s2- next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)- next s2 if parent_superselector?(s1, s2)- next s1 if parent_superselector?(s2, s1)- end- */-- if (*pOne == *pTwo) {- pOut = pOne;- return true;- }-- if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) {- return false;- }-- if (parentSuperselector(pOne, pTwo)) {- pOut = pTwo;- return true;- }-- if (parentSuperselector(pTwo, pOne)) {- pOut = pOne;- return true;- }-- return false;- }- };--- /*- This is the equivalent of ruby's Sass::Util.lcs_backtrace.-- # Computes a single longest common subsequence for arrays x and y.- # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS- */- void lcs_backtrace(const LCSTable& c, ComplexSelectorDeque& x, ComplexSelectorDeque& y, int i, int j, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {- //DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)- // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output-- if (i == 0 || j == 0) {- DEBUG_PRINTLN(LCS, "RETURNING EMPTY")- return;- }--- Complex_Selector_Obj pCompareOut;- if (comparator(x[i], y[j], pCompareOut)) {- DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")- lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out);- out.push_back(pCompareOut);- return;- }-- if (c[i][j - 1] > c[i - 1][j]) {- DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")- lcs_backtrace(c, x, y, i, j - 1, comparator, out);- return;- }-- DEBUG_PRINTLN(LCS, "FINAL RETURN")- lcs_backtrace(c, x, y, i - 1, j, comparator, out);- return;- }-- /*- This is the equivalent of ruby's Sass::Util.lcs_table.-- # Calculates the memoization table for the Least Common Subsequence algorithm.- # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS- */- void lcs_table(const ComplexSelectorDeque& x, const ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, LCSTable& out) {- //DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)- // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output-- LCSTable c(x.size(), std::vector<int>(y.size()));-- // These shouldn't be necessary since the vector will be initialized to 0 already.- // x.size.times {|i| c[i][0] = 0}- // y.size.times {|j| c[0][j] = 0}-- for (size_t i = 1; i < x.size(); i++) {- for (size_t j = 1; j < y.size(); j++) {- Complex_Selector_Obj pCompareOut;-- if (comparator(x[i], y[j], pCompareOut)) {- c[i][j] = c[i - 1][j - 1] + 1;- } else {- c[i][j] = std::max(c[i][j - 1], c[i - 1][j]);- }- }- }-- out = c;- }-- /*- This is the equivalent of ruby's Sass::Util.lcs.-- # Computes a single longest common subsequence for `x` and `y`.- # If there are more than one longest common subsequences,- # the one returned is that which starts first in `x`.-- # @param x [NodeCollection]- # @param y [NodeCollection]- # @comparator An equality check between elements of `x` and `y`.- # @return [NodeCollection] The LCS-- http://en.wikipedia.org/wiki/Longest_common_subsequence_problem- */- void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {- //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)- // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output-- x.push_front(NULL);- y.push_front(NULL);-- LCSTable table;- lcs_table(x, y, comparator, table);-- return lcs_backtrace(table, x, y, static_cast<int>(x.size()) - 1, static_cast<int>(y.size()) - 1, comparator, out);- }--- /*- This is the equivalent of ruby's Sequence.trim.-- The following is the modified version of the ruby code that was more portable to C++. You- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.-- # Avoid truly horrific quadratic behavior. TODO: I think there- # may be a way to get perfect trimming without going quadratic.- return seqses if seqses.size > 100-- # Keep the results in a separate array so we can be sure we aren't- # comparing against an already-trimmed selector. This ensures that two- # identical selectors don't mutually trim one another.- result = seqses.dup-- # This is n^2 on the sequences, but only comparing between- # separate sequences should limit the quadratic behavior.- seqses.each_with_index do |seqs1, i|- tempResult = []-- for seq1 in seqs1 do- max_spec = 0- for seq in _sources(seq1) do- max_spec = [max_spec, seq.specificity].max- end--- isMoreSpecificOuter = false- for seqs2 in result do- if seqs1.equal?(seqs2) then- next- end-- # Second Law of Extend: the specificity of a generated selector- # should never be less than the specificity of the extending- # selector.- #- # See https://github.com/nex3/sass/issues/324.- isMoreSpecificInner = false- for seq2 in seqs2 do- isMoreSpecificInner = _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)- if isMoreSpecificInner then- break- end- end-- if isMoreSpecificInner then- isMoreSpecificOuter = true- break- end- end-- if !isMoreSpecificOuter then- tempResult.push(seq1)- end- end-- result[i] = tempResult-- end-- result- */- /*- - IMPROVEMENT: We could probably work directly in the output trimmed deque.- */- Node Extend::trim(Node& seqses, bool isReplace) {- // See the comments in the above ruby code before embarking on understanding this function.-- // Avoid poor performance in extreme cases.- if (seqses.collection()->size() > 100) {- return seqses;- }--- DEBUG_PRINTLN(TRIM, "TRIM: " << seqses)--- Node result = Node::createCollection();- result.plus(seqses);-- DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result)-- // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're- // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track- // of the index manually.- int toTrimIndex = 0;-- for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) {- Node& seqs1 = *seqsesIter;-- DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex)-- Node tempResult = Node::createCollection();- tempResult.got_line_feed = seqs1.got_line_feed;-- for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) {- Node& seq1 = *seqs1Iter;-- Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1);-- // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code- // for a good description of sources.- //- // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test.- // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We- // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My- // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely- // a guess though.- unsigned long maxSpecificity = isReplace ? pSeq1->specificity() : 0;- ComplexSelectorSet sources = pSeq1->sources();-- DEBUG_PRINTLN(TRIM, "TRIM SEQ1: " << seq1)- DEBUG_EXEC(TRIM, printSourcesSet(sources, "TRIM SOURCES: "))-- for (ComplexSelectorSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {- const Complex_Selector_Obj& pCurrentSelector = *sourcesSetIterator;- maxSpecificity = std::max(maxSpecificity, pCurrentSelector->specificity());- }-- DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity)-- bool isMoreSpecificOuter = false;-- int resultIndex = 0;-- for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) {- Node& seqs2 = *resultIter;-- DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1)- DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2)-- // Do not compare the same sequence to itself. The ruby call we're trying to- // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision.- // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is- // derived from seqses and seqs2 is derived from result.- if (seqs1.collection() == seqs2.collection()) {- DEBUG_PRINTLN(TRIM, "CONTINUE")- continue;- }-- bool isMoreSpecificInner = false;-- for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) {- Node& seq2 = *seqs2Iter;-- Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2);-- DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity())- DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))- DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false"))-- isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1);-- if (isMoreSpecificInner) {- DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC")- break;- }- }-- // If we found something more specific, we're done. Let the outer loop know and stop iterating.- if (isMoreSpecificInner) {- isMoreSpecificOuter = true;- break;- }-- resultIndex++;- }-- if (!isMoreSpecificOuter) {- DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1)- tempResult.collection()->push_back(seq1);- }-- }-- DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result)- DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult)- (*result.collection())[toTrimIndex] = tempResult;-- toTrimIndex++;-- DEBUG_PRINTLN(TRIM, "RESULT: " << result)- }-- return result;- }---- static bool parentSuperselector(const Node& one, const Node& two) {- // TODO: figure out a better way to create a Complex_Selector from scratch- // TODO: There's got to be a better way. This got ugly quick...- Element_Selector_Obj fakeParent = SASS_MEMORY_NEW(Element_Selector, ParserState("[FAKE]"), "temp");- Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);- fakeHead->elements().push_back(fakeParent);- Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, NULL /*tail*/);-- Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one);- pOneWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);- Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two);- pTwoWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);-- return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);- }--- class ParentSuperselectorChunker {- public:- ParentSuperselectorChunker(Node& lcs) : mLcs(lcs) {}- Node& mLcs;-- bool operator()(const Node& seq) const {- // {|s| parent_superselector?(s.first, lcs.first)}- if (seq.collection()->size() == 0) return false;- return parentSuperselector(seq.collection()->front(), mLcs.collection()->front());- }- };-- class SubweaveEmptyChunker {- public:- bool operator()(const Node& seq) const {- // {|s| s.empty?}-- return seq.collection()->empty();- }- };-- /*- # Takes initial subsequences of `seq1` and `seq2` and returns all- # orderings of those subsequences. The initial subsequences are determined- # by a block.- #- # Destructively removes the initial subsequences of `seq1` and `seq2`.- #- # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`- # denoting the boundary of the initial subsequence), this would return- # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and- # `(3 4 5)`.- #- # @param seq1 [Array]- # @param seq2 [Array]- # @yield [a] Used to determine when to cut off the initial subsequences.- # Called repeatedly for each sequence until it returns true.- # @yieldparam a [Array] A final subsequence of one input sequence after- # cutting off some initial subsequence.- # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence- # here.- # @return [Array<Array>] All possible orderings of the initial subsequences.- def chunks(seq1, seq2)- chunk1 = []- chunk1 << seq1.shift until yield seq1- chunk2 = []- chunk2 << seq2.shift until yield seq2- return [] if chunk1.empty? && chunk2.empty?- return [chunk2] if chunk1.empty?- return [chunk1] if chunk2.empty?- [chunk1 + chunk2, chunk2 + chunk1]- end- */- template<typename ChunkerType>- static Node chunks(Node& seq1, Node& seq2, const ChunkerType& chunker) {- Node chunk1 = Node::createCollection();- while (seq1.collection()->size() && !chunker(seq1)) {- chunk1.collection()->push_back(seq1.collection()->front());- seq1.collection()->pop_front();- }-- Node chunk2 = Node::createCollection();- while (!seq2.collection()->empty() && !chunker(seq2)) {- chunk2.collection()->push_back(seq2.collection()->front());- seq2.collection()->pop_front();- }-- if (chunk1.collection()->empty() && chunk2.collection()->empty()) {- DEBUG_PRINTLN(CHUNKS, "RETURNING BOTH EMPTY")- return Node::createCollection();- }-- if (chunk1.collection()->empty()) {- Node chunk2Wrapper = Node::createCollection();- chunk2Wrapper.collection()->push_back(chunk2);- DEBUG_PRINTLN(CHUNKS, "RETURNING ONE EMPTY")- return chunk2Wrapper;- }-- if (chunk2.collection()->empty()) {- Node chunk1Wrapper = Node::createCollection();- chunk1Wrapper.collection()->push_back(chunk1);- DEBUG_PRINTLN(CHUNKS, "RETURNING TWO EMPTY")- return chunk1Wrapper;- }-- Node perms = Node::createCollection();-- Node firstPermutation = Node::createCollection();- firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());- firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());- perms.collection()->push_back(firstPermutation);-- Node secondPermutation = Node::createCollection();- secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());- secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());- perms.collection()->push_back(secondPermutation);-- DEBUG_PRINTLN(CHUNKS, "RETURNING PERM")-- return perms;- }--- static Node groupSelectors(Node& seq) {- Node newSeq = Node::createCollection();-- Node tail = Node::createCollection();- tail.plus(seq);-- while (!tail.collection()->empty()) {- Node head = Node::createCollection();-- do {- head.collection()->push_back(tail.collection()->front());- tail.collection()->pop_front();- } while (!tail.collection()->empty() && (head.collection()->back().isCombinator() || tail.collection()->front().isCombinator()));-- newSeq.collection()->push_back(head);- }-- return newSeq;- }--- static void getAndRemoveInitialOps(Node& seq, Node& ops) {- NodeDeque& seqCollection = *(seq.collection());- NodeDeque& opsCollection = *(ops.collection());-- while (seqCollection.size() > 0 && seqCollection.front().isCombinator()) {- opsCollection.push_back(seqCollection.front());- seqCollection.pop_front();- }- }--- static void getAndRemoveFinalOps(Node& seq, Node& ops) {- NodeDeque& seqCollection = *(seq.collection());- NodeDeque& opsCollection = *(ops.collection());-- while (seqCollection.size() > 0 && seqCollection.back().isCombinator()) {- opsCollection.push_back(seqCollection.back()); // Purposefully reversed to match ruby code- seqCollection.pop_back();- }- }--- /*- def merge_initial_ops(seq1, seq2)- ops1, ops2 = [], []- ops1 << seq1.shift while seq1.first.is_a?(String)- ops2 << seq2.shift while seq2.first.is_a?(String)-- newline = false- newline ||= !!ops1.shift if ops1.first == "\n"- newline ||= !!ops2.shift if ops2.first == "\n"-- # If neither sequence is a subsequence of the other, they cannot be- # merged successfully- lcs = Sass::Util.lcs(ops1, ops2)- return unless lcs == ops1 || lcs == ops2- return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)- end- */- static Node mergeInitialOps(Node& seq1, Node& seq2) {- Node ops1 = Node::createCollection();- Node ops2 = Node::createCollection();-- getAndRemoveInitialOps(seq1, ops1);- getAndRemoveInitialOps(seq2, ops2);-- // TODO: Do we have this information available to us?- // newline = false- // newline ||= !!ops1.shift if ops1.first == "\n"- // newline ||= !!ops2.shift if ops2.first == "\n"-- // If neither sequence is a subsequence of the other, they cannot be merged successfully- DefaultLcsComparator lcsDefaultComparator;- Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator);-- if (!(opsLcs == ops1 || opsLcs == ops2)) {- return Node::createNil();- }-- // TODO: more newline logic- // return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)-- return (ops1.collection()->size() > ops2.collection()->size() ? ops1 : ops2);- }--- /*- def merge_final_ops(seq1, seq2, res = [])--- # This code looks complicated, but it's actually just a bunch of special- # cases for interactions between different combinators.- op1, op2 = ops1.first, ops2.first- if op1 && op2- sel1 = seq1.pop- sel2 = seq2.pop- if op1 == '~' && op2 == '~'- if sel1.superselector?(sel2)- res.unshift sel2, '~'- elsif sel2.superselector?(sel1)- res.unshift sel1, '~'- else- merged = sel1.unify(sel2.members, sel2.subject?)- res.unshift [- [sel1, '~', sel2, '~'],- [sel2, '~', sel1, '~'],- ([merged, '~'] if merged)- ].compact- end- elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')- if op1 == '~'- tilde_sel, plus_sel = sel1, sel2- else- tilde_sel, plus_sel = sel2, sel1- end-- if tilde_sel.superselector?(plus_sel)- res.unshift plus_sel, '+'- else- merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)- res.unshift [- [tilde_sel, '~', plus_sel, '+'],- ([merged, '+'] if merged)- ].compact- end- elsif op1 == '>' && %w[~ +].include?(op2)- res.unshift sel2, op2- seq1.push sel1, op1- elsif op2 == '>' && %w[~ +].include?(op1)- res.unshift sel1, op1- seq2.push sel2, op2- elsif op1 == op2- return unless merged = sel1.unify(sel2.members, sel2.subject?)- res.unshift merged, op1- else- # Unknown selector combinators can't be unified- return- end- return merge_final_ops(seq1, seq2, res)- elsif op1- seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last)- res.unshift seq1.pop, op1- return merge_final_ops(seq1, seq2, res)- else # op2- seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last)- res.unshift seq2.pop, op2- return merge_final_ops(seq1, seq2, res)- end- end- */- static Node mergeFinalOps(Node& seq1, Node& seq2, Node& res) {-- Node ops1 = Node::createCollection();- Node ops2 = Node::createCollection();-- getAndRemoveFinalOps(seq1, ops1);- getAndRemoveFinalOps(seq2, ops2);-- // TODO: do we have newlines to remove?- // ops1.reject! {|o| o == "\n"}- // ops2.reject! {|o| o == "\n"}-- if (ops1.collection()->empty() && ops2.collection()->empty()) {- return res;- }-- if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) {- DefaultLcsComparator lcsDefaultComparator;- Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator);-- // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up.-- if (!(opsLcs == ops1 || opsLcs == ops2)) {- return Node::createNil();- }-- if (ops1.collection()->size() > ops2.collection()->size()) {- res.collection()->insert(res.collection()->begin(), ops1.collection()->rbegin(), ops1.collection()->rend());- } else {- res.collection()->insert(res.collection()->begin(), ops2.collection()->rbegin(), ops2.collection()->rend());- }-- return res;- }-- if (!ops1.collection()->empty() && !ops2.collection()->empty()) {-- Node op1 = ops1.collection()->front();- Node op2 = ops2.collection()->front();-- Node sel1 = seq1.collection()->back();- seq1.collection()->pop_back();-- Node sel2 = seq2.collection()->back();- seq2.collection()->pop_back();-- if (op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::PRECEDES) {-- if (sel1.selector()->is_superselector_of(sel2.selector())) {-- res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);- res.collection()->push_front(sel2);-- } else if (sel2.selector()->is_superselector_of(sel1.selector())) {-- res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);- res.collection()->push_front(sel1);-- } else {-- DEBUG_PRINTLN(ALL, "sel1: " << sel1)- DEBUG_PRINTLN(ALL, "sel2: " << sel2)-- Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result- // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)- Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head());- pMergedWrapper->head(pMerged);-- DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))-- Node newRes = Node::createCollection();-- Node firstPerm = Node::createCollection();- firstPerm.collection()->push_back(sel1);- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));- firstPerm.collection()->push_back(sel2);- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));- newRes.collection()->push_back(firstPerm);-- Node secondPerm = Node::createCollection();- secondPerm.collection()->push_back(sel2);- secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));- secondPerm.collection()->push_back(sel1);- secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));- newRes.collection()->push_back(secondPerm);-- if (pMerged) {- Node mergedPerm = Node::createCollection();- mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper));- mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));- newRes.collection()->push_back(mergedPerm);- }-- res.collection()->push_front(newRes);-- DEBUG_PRINTLN(ALL, "RESULT: " << res)-- }-- } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) {-- Node tildeSel = sel1;- Node plusSel = sel2;- Node plusOp = op2;- if (op1.combinator() != Complex_Selector::PRECEDES) {- tildeSel = sel2;- plusSel = sel1;- plusOp = op1;- }-- if (tildeSel.selector()->is_superselector_of(plusSel.selector())) {-- res.collection()->push_front(plusOp);- res.collection()->push_front(plusSel);-- } else {-- DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel)- DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel)-- Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(plusSel.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result- // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)- Compound_Selector_Ptr pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head());- pMergedWrapper->head(pMerged);-- DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))-- Node newRes = Node::createCollection();-- Node firstPerm = Node::createCollection();- firstPerm.collection()->push_back(tildeSel);- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));- firstPerm.collection()->push_back(plusSel);- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));- newRes.collection()->push_back(firstPerm);-- if (pMerged) {- Node mergedPerm = Node::createCollection();- mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper));- mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));- newRes.collection()->push_back(mergedPerm);- }-- res.collection()->push_front(newRes);-- DEBUG_PRINTLN(ALL, "RESULT: " << res)-- }- } else if (op1.combinator() == Complex_Selector::PARENT_OF && (op2.combinator() == Complex_Selector::PRECEDES || op2.combinator() == Complex_Selector::ADJACENT_TO)) {-- res.collection()->push_front(op2);- res.collection()->push_front(sel2);-- seq1.collection()->push_back(sel1);- seq1.collection()->push_back(op1);-- } else if (op2.combinator() == Complex_Selector::PARENT_OF && (op1.combinator() == Complex_Selector::PRECEDES || op1.combinator() == Complex_Selector::ADJACENT_TO)) {-- res.collection()->push_front(op1);- res.collection()->push_front(sel1);-- seq2.collection()->push_back(sel2);- seq2.collection()->push_back(op2);-- } else if (op1.combinator() == op2.combinator()) {-- DEBUG_PRINTLN(ALL, "sel1: " << sel1)- DEBUG_PRINTLN(ALL, "sel2: " << sel2)-- Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result- // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)- Compound_Selector_Ptr pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head());- pMergedWrapper->head(pMerged);-- DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))-- if (!pMerged) {- return Node::createNil();- }-- res.collection()->push_front(op1);- res.collection()->push_front(Node::createSelector(pMergedWrapper));-- DEBUG_PRINTLN(ALL, "RESULT: " << res)-- } else {- return Node::createNil();- }-- return mergeFinalOps(seq1, seq2, res);-- } else if (!ops1.collection()->empty()) {-- Node op1 = ops1.collection()->front();-- if (op1.combinator() == Complex_Selector::PARENT_OF && !seq2.collection()->empty() && seq2.collection()->back().selector()->is_superselector_of(seq1.collection()->back().selector())) {- seq2.collection()->pop_back();- }-- // TODO: consider unshift(NodeCollection, Node)- res.collection()->push_front(op1);- res.collection()->push_front(seq1.collection()->back());- seq1.collection()->pop_back();-- return mergeFinalOps(seq1, seq2, res);-- } else { // !ops2.collection()->empty()-- Node op2 = ops2.collection()->front();-- if (op2.combinator() == Complex_Selector::PARENT_OF && !seq1.collection()->empty() && seq1.collection()->back().selector()->is_superselector_of(seq2.collection()->back().selector())) {- seq1.collection()->pop_back();- }-- res.collection()->push_front(op2);- res.collection()->push_front(seq2.collection()->back());- seq2.collection()->pop_back();-- return mergeFinalOps(seq1, seq2, res);-- }-- }--- /*- This is the equivalent of ruby's Sequence.subweave.-- Here is the original subweave code for reference during porting.-- def subweave(seq1, seq2)- return [seq2] if seq1.empty?- return [seq1] if seq2.empty?-- seq1, seq2 = seq1.dup, seq2.dup- return unless init = merge_initial_ops(seq1, seq2)- return unless fin = merge_final_ops(seq1, seq2)- seq1 = group_selectors(seq1)- seq2 = group_selectors(seq2)- lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|- next s1 if s1 == s2- next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)- next s2 if parent_superselector?(s1, s2)- next s1 if parent_superselector?(s2, s1)- end-- diff = [[init]]- until lcs.empty?- diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]- seq1.shift- seq2.shift- end- diff << chunks(seq1, seq2) {|s| s.empty?}- diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}- diff.reject! {|c| c.empty?}-- result = Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}-- result- end- */- Node subweave(Node& one, Node& two) {- // Check for the simple cases- if (one.collection()->size() == 0) {- Node out = Node::createCollection();- out.collection()->push_back(two);- return out;- }- if (two.collection()->size() == 0) {- Node out = Node::createCollection();- out.collection()->push_back(one);- return out;- }-- Node seq1 = Node::createCollection();- seq1.plus(one);- Node seq2 = Node::createCollection();- seq2.plus(two);-- DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1)- DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2)-- Node init = mergeInitialOps(seq1, seq2);- if (init.isNil()) {- return Node::createNil();- }-- DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init)-- Node res = Node::createCollection();- Node fin = mergeFinalOps(seq1, seq2, res);- if (fin.isNil()) {- return Node::createNil();- }-- DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin)--- // Moving this line up since fin isn't modified between now and when it happened before- // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}-- for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end();- finIter != finEndIter; ++finIter) {-- Node& childNode = *finIter;-- if (!childNode.isCollection()) {- Node wrapper = Node::createCollection();- wrapper.collection()->push_back(childNode);- childNode = wrapper;- }-- }-- DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin)---- Node groupSeq1 = groupSelectors(seq1);- DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1)-- Node groupSeq2 = groupSelectors(seq2);- DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2)--- ComplexSelectorDeque groupSeq1Converted;- nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted);-- ComplexSelectorDeque groupSeq2Converted;- nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted);-- ComplexSelectorDeque out;- LcsCollectionComparator collectionComparator;- lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, out);- Node seqLcs = complexSelectorDequeToNode(out);-- DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs)--- Node initWrapper = Node::createCollection();- initWrapper.collection()->push_back(init);- Node diff = Node::createCollection();- diff.collection()->push_back(initWrapper);-- DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff)--- while (!seqLcs.collection()->empty()) {- ParentSuperselectorChunker superselectorChunker(seqLcs);- Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker);- diff.collection()->push_back(chunksResult);-- Node lcsWrapper = Node::createCollection();- lcsWrapper.collection()->push_back(seqLcs.collection()->front());- seqLcs.collection()->pop_front();- diff.collection()->push_back(lcsWrapper);-- if (groupSeq1.collection()->size()) groupSeq1.collection()->pop_front();- if (groupSeq2.collection()->size()) groupSeq2.collection()->pop_front();- }-- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff)--- DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2)--- SubweaveEmptyChunker emptyChunker;- Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker);- diff.collection()->push_back(chunksResult);--- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff)--- diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end());-- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff)-- // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection)- Node diffFiltered = Node::createCollection();- for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end();- diffIter != diffEndIter; ++diffIter) {- Node& node = *diffIter;- if (node.collection() && !node.collection()->empty()) {- diffFiltered.collection()->push_back(node);- }- }- diff = diffFiltered;-- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff)--- Node pathsResult = paths(diff);-- DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult)--- // We're flattening in place- for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end();- pathsIter != pathsEndIter; ++pathsIter) {-- Node& child = *pathsIter;- child = flatten(child);- }-- DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult)--- /*- TODO: implement- rejected = mapped.reject {|p| path_has_two_subjects?(p)}- $stderr.puts "REJECTED: #{rejected}"- */--- return pathsResult;-- }- /*- // disabled to avoid clang warning [-Wunused-function]- static Node subweaveNaive(const Node& one, const Node& two) {- Node out = Node::createCollection();-- // Check for the simple cases- if (one.isNil()) {- out.collection()->push_back(two.klone());- } else if (two.isNil()) {- out.collection()->push_back(one.klone());- } else {- // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B- // See https://gist.github.com/nex3/7609394 for details.-- Node firstPerm = one.klone();- Node twoCloned = two.klone();- firstPerm.plus(twoCloned);- out.collection()->push_back(firstPerm);-- Node secondPerm = two.klone();- Node oneCloned = one.klone();- secondPerm.plus(oneCloned );- out.collection()->push_back(secondPerm);- }-- return out;- }- */--- /*- This is the equivalent of ruby's Sequence.weave.-- The following is the modified version of the ruby code that was more portable to C++. You- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.-- def weave(path)- # This function works by moving through the selector path left-to-right,- # building all possible prefixes simultaneously. These prefixes are- # `befores`, while the remaining parenthesized suffixes is `afters`.- befores = [[]]- afters = path.dup-- until afters.empty?- current = afters.shift.dup- last_current = [current.pop]-- tempResult = []-- for before in befores do- sub = subweave(before, current)- if sub.nil?- next- end-- for seqs in sub do- tempResult.push(seqs + last_current)- end- end-- befores = tempResult-- end-- return befores- end- */- /*- def weave(path)- befores = [[]]- afters = path.dup-- until afters.empty?- current = afters.shift.dup-- last_current = [current.pop]--- tempResult = []-- for before in befores do- sub = subweave(before, current)-- if sub.nil?- next []- end--- for seqs in sub do- toPush = seqs + last_current-- tempResult.push(seqs + last_current)- end-- end-- befores = tempResult-- end-- return befores- end- */- Node Extend::weave(Node& path) {-- DEBUG_PRINTLN(WEAVE, "WEAVE: " << path)-- Node befores = Node::createCollection();- befores.collection()->push_back(Node::createCollection());-- Node afters = Node::createCollection();- afters.plus(path);-- while (!afters.collection()->empty()) {- Node current = afters.collection()->front().klone();- afters.collection()->pop_front();- DEBUG_PRINTLN(WEAVE, "CURRENT: " << current)- if (current.collection()->size() == 0) continue;-- Node last_current = Node::createCollection();- last_current.collection()->push_back(current.collection()->back());- current.collection()->pop_back();- DEBUG_PRINTLN(WEAVE, "CURRENT POST POP: " << current)- DEBUG_PRINTLN(WEAVE, "LAST CURRENT: " << last_current)-- Node tempResult = Node::createCollection();-- for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) {- Node& before = *beforesIter;-- Node sub = subweave(before, current);-- DEBUG_PRINTLN(WEAVE, "SUB: " << sub)-- if (sub.isNil()) {- return Node::createCollection();- }-- for (NodeDeque::iterator subIter = sub.collection()->begin(), subEndIter = sub.collection()->end(); subIter != subEndIter; subIter++) {- Node& seqs = *subIter;-- Node toPush = Node::createCollection();- toPush.plus(seqs);- toPush.plus(last_current);-- // move line feed from inner to outer selector (very hacky indeed)- if (last_current.collection() && last_current.collection()->front().selector()) {- toPush.got_line_feed = last_current.collection()->front().got_line_feed;- last_current.collection()->front().selector()->has_line_feed(false);- last_current.collection()->front().got_line_feed = false;- }-- tempResult.collection()->push_back(toPush);-- }- }-- befores = tempResult;-- }-- return befores;- }---- /*- This is the equivalent of ruby's SimpleSequence.do_extend.-- // TODO: I think I have some modified ruby code to put here. Check.- */- /*- ISSUES:- - Previous TODO: Do we need to group the results by extender?- - What does subject do in?: next unless unified = seq.members.last.unify(self_without_sel, subject?)- - IMPROVEMENT: The search for uniqueness at the end is not ideal since it's has to loop over everything...- - IMPROVEMENT: Check if the final search for uniqueness is doing anything that extendComplexSelector isn't already doing...- */- template<typename KeyType>- class GroupByToAFunctor {- public:- KeyType operator()(SubSetMapPair& extPair) const {- Complex_Selector_Obj pSelector = extPair.first;- return pSelector;- }- };- Node Extend::extendCompoundSelector(Compound_Selector_Ptr pSelector, CompoundSelectorSet& seen, bool isReplace) {-- /* this turned out to be too much overhead- probably due to holding a "Node" object- // check if we already extended this selector- // we can do this since subset_map is "static"- auto memoized = memoizeCompound.find(pSelector);- if (memoized != memoizeCompound.end()) {- return memoized->second.klone();- }- */-- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))- // TODO: Ruby has another loop here to skip certain members?-- // let RESULTS be an empty list of complex selectors- Node results = Node::createCollection();- // extendedSelectors.got_line_feed = true;-- SubSetMapPairs entries = subset_map.get_v(pSelector);-- GroupByToAFunctor<Complex_Selector_Obj> extPairKeyFunctor;- SubSetMapResults arr;- group_by_to_a(entries, extPairKeyFunctor, arr);-- SubSetMapLookups holder;-- // for each (EXTENDER, TARGET) in MAP.get(COMPOUND):- for (SubSetMapResult& groupedPair : arr) {-- Complex_Selector_Obj seq = groupedPair.first;- SubSetMapPairs& group = groupedPair.second;-- DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(seq, "SEQ: "))-- Compound_Selector_Obj pSels = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());- for (SubSetMapPair& pair : group) {- pair.second->extended(true);- pSels->concat(pair.second);- }-- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))-- // The selector up to where the @extend is (ie, the thing to merge)- Complex_Selector_Ptr pExtComplexSelector = seq;-- // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL?- // RUBY: self_without_sel = Sass::Util.array_minus(members, sels)- Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(pSels);-- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: "))- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: "))-- Compound_Selector_Obj pInnermostCompoundSelector = pExtComplexSelector->last()->head();-- if (!pInnermostCompoundSelector) {- pInnermostCompoundSelector = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());- }- Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors);-- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: "))- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: "))-- // RUBY: next unless unified- if (!pUnifiedSelector || pUnifiedSelector->length() == 0) {- continue;- }-- // TODO: implement the parent directive match (if necessary based on test failures)- // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?-- // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just- // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more- // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered- // out and aren't operated on.- Complex_Selector_Obj pNewSelector = SASS_MEMORY_CLONE(pExtComplexSelector); // ->first();-- Complex_Selector_Obj pNewInnerMost = SASS_MEMORY_NEW(Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL);-- Complex_Selector::Combinator combinator = pNewSelector->clear_innermost();- pNewSelector->set_innermost(pNewInnerMost, combinator);--#ifdef DEBUG- ComplexSelectorSet debugSet;- debugSet = pNewSelector->sources();- if (debugSet.size() > 0) {- throw std::runtime_error("The new selector should start with no sources. Something needs to be cloned to fix this.");- }- debugSet = pExtComplexSelector->sources();- if (debugSet.size() > 0) {- throw std::runtime_error("The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this.");- }-#endif--- // if (pSelector && pSelector->has_line_feed()) pNewInnerMost->has_line_feed(true);- // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending.- DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector))-- DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, "SOURCES NEW SEQ BEGIN: "))-- // I actually want to create a copy here (performance!)- ComplexSelectorSet newSourcesSet = pSelector->sources(); // XXX- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES THIS EXTEND: "))-- newSourcesSet.insert(pExtComplexSelector);- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES WITH NEW SOURCE: "))-- // RUBY: new_seq.add_sources!(sources + [seq])- pNewSelector->addSources(newSourcesSet);-- DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet newSet = pNewSelector->sources(); printSourcesSet(newSet, "SOURCES ON NEW SELECTOR AFTER ADD: "))- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))--- if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);-- holder.push_back(std::make_pair(pSels, pNewSelector));- }--- for (SubSetMapLookup& pair : holder) {-- Compound_Selector_Obj pSels = pair.first;- Complex_Selector_Obj pNewSelector = pair.second;--- // RUBY??: next [] if seen.include?(sels)- if (seen.find(pSels) != seen.end()) {- continue;- }--- CompoundSelectorSet recurseSeen(seen);- recurseSeen.insert(pSels);--- DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector))- Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, recurseSeen, isReplace, false); // !:isOriginal-- DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors)-- for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end();- iterator != endIterator; ++iterator) {- Node newSelector = *iterator;--// DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << results)-// DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << results.contains(newSelector, false /*simpleSelectorOrderDependent*/));-- if (!results.contains(newSelector)) {-// DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR")- results.collection()->push_back(newSelector);- }- }- }-- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: "))-- // this turned out to be too much overhead- // memory results in a map table - since extending is very expensive- // memoizeCompound.insert(std::pair<Compound_Selector_Obj, Node>(pSelector, results));-- return results;- }--- // check if selector has something to be extended by subset_map- bool Extend::complexSelectorHasExtension(Complex_Selector_Ptr selector, CompoundSelectorSet& seen) {-- bool hasExtension = false;-- Complex_Selector_Obj pIter = selector;-- while (!hasExtension && pIter) {- Compound_Selector_Obj pHead = pIter->head();-- if (pHead) {- SubSetMapPairs entries = subset_map.get_v(pHead);- for (SubSetMapPair ext : entries) {- // check if both selectors have the same media block parent- // if (ext.first->media_block() == pComplexSelector->media_block()) continue;- if (ext.second->media_block() == 0) continue;- if (pHead->media_block() &&- ext.second->media_block()->media_queries() &&- pHead->media_block()->media_queries()- ) {- std::string query_left(ext.second->media_block()->media_queries()->to_string());- std::string query_right(pHead->media_block()->media_queries()->to_string());- if (query_left == query_right) continue;- }-- // fail if one goes across media block boundaries- std::stringstream err;- std::string cwd(Sass::File::get_cwd());- ParserState pstate(ext.second->pstate());- std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));- err << "You may not @extend an outer selector from within @media.\n";- err << "You may only @extend selectors within the same directive.\n";- err << "From \"@extend " << ext.second->to_string() << "\"";- err << " on line " << pstate.line+1 << " of " << rel_path << "\n";- error(err.str(), selector->pstate(), eval->exp.traces);- }- if (entries.size() > 0) hasExtension = true;- }-- pIter = pIter->tail();- }-- return hasExtension;- }--- /*- This is the equivalent of ruby's Sequence.do_extend.-- // TODO: I think I have some modified ruby code to put here. Check.- */- /*- ISSUES:- - check to automatically include combinators doesn't transfer over to libsass' data model where- the combinator and compound selector are one unit- next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)- */- Node Extend::extendComplexSelector(Complex_Selector_Ptr selector, CompoundSelectorSet& seen, bool isReplace, bool isOriginal) {-- // check if we already extended this selector- // we can do this since subset_map is "static"- auto memoized = memoizeComplex.find(selector);- if (memoized != memoizeComplex.end()) {- return memoized->second;- }-- // convert the input selector to extend node format- Node complexSelector = complexSelectorToNode(selector);- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector)-- // let CHOICES be an empty list of selector-lists- // create new collection to hold the results- Node choices = Node::createCollection();-- // for each compound selector COMPOUND in COMPLEX:- for (Node& sseqOrOp : *complexSelector.collection()) {-- DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp)-- // If it's not a selector (meaning it's a combinator), just include it automatically- // RUBY: next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)- if (!sseqOrOp.isSelector()) {- // Wrap our Combinator in two collections to match ruby. This is essentially making a collection Node- // with one collection child. The collection child represents a Complex_Selector that is only a combinator.- Node outer = Node::createCollection();- Node inner = Node::createCollection();- outer.collection()->push_back(inner);- inner.collection()->push_back(sseqOrOp);- choices.collection()->push_back(outer);- continue;- }-- // verified now that node is a valid selector- Complex_Selector_Obj sseqSel = sseqOrOp.selector();- Compound_Selector_Obj sseqHead = sseqSel->head();-- // let EXTENDED be extend_compound(COMPOUND, SEEN)- // extend the compound selector against the given subset_map- // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)- Node extended = extendCompoundSelector(sseqHead, seen, isReplace); // slow(17%)!- if (sseqOrOp.got_line_feed) extended.got_line_feed = true;- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended)-- // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with a ruby- // Array instead of a Sequence due to the member mapping: choices = extended.map {|seq| seq.members}- // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder?- if (isOriginal && !selector->has_placeholder()) {- ComplexSelectorSet srcset;- srcset.insert(selector);- sseqSel->addSources(srcset);- // DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector)- }-- bool isSuperselector = false;- // if no complex selector in EXTENDED is a superselector of COMPOUND:- for (Node& childNode : *extended.collection()) {- Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode);- if (pExtensionSelector->is_superselector_of(sseqSel)) {- isSuperselector = true;- break;- }- }-- if (!isSuperselector) {- // add a complex selector composed only of COMPOUND to EXTENDED- if (sseqOrOp.got_line_feed) sseqSel->has_line_feed(sseqOrOp.got_line_feed);- extended.collection()->push_front(complexSelectorToNode(sseqSel));- }-- DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)-- // add EXTENDED to CHOICES- // Aggregate our current extensions- choices.collection()->push_back(extended);- }--- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << choices)---- // Ruby Equivalent: paths- Node paths = Sass::paths(choices);-- DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths)-- // let WEAVES be an empty list of selector lists- Node weaves = Node::createCollection();-- // for each list of complex selectors PATH in paths(CHOICES):- for (Node& path : *paths.collection()) {- // add weave(PATH) to WEAVES- Node weaved = weave(path); // slow(12%)!- weaved.got_line_feed = path.got_line_feed;- weaves.collection()->push_back(weaved);- }-- DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves)-- // Ruby Equivalent: trim- Node trimmed(trim(weaves, isReplace)); // slow(19%)!-- DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed)-- // Ruby Equivalent: flatten- Node flattened(flatten(trimmed, 1));-- DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors)- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector)-- // memory results in a map table - since extending is very expensive- memoizeComplex.insert(std::pair<Complex_Selector_Obj, Node>(selector, flattened));-- // return trim(WEAVES)- return flattened;- }---- /*- This is the equivalent of ruby's CommaSequence.do_extend.- */- // We get a selector list with has something to extend and a subset_map with- // all extenders. Pick the ones that match our selectors in the list.- Selector_List_Ptr Extend::extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen) {-- Selector_List_Obj pNewSelectors = SASS_MEMORY_NEW(Selector_List, pSelectorList->pstate(), pSelectorList->length());-- // check if we already extended this selector- // we can do this since subset_map is "static"- auto memoized = memoizeList.find(pSelectorList);- if (memoized != memoizeList.end()) {- extendedSomething = true;- return memoized->second;- }-- extendedSomething = false;- // process each comlplex selector in the selector list.- // Find the ones that can be extended by given subset_map.- for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {- Complex_Selector_Obj pSelector = (*pSelectorList)[index];-- // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that.- // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to- // run through the extend code (which does a data model transformation), check if there is anything to extend before doing- // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps- // when debugging).- if (!complexSelectorHasExtension(pSelector, seen)) {- pNewSelectors->append(pSelector);- continue;- }-- // complexSelectorHasExtension was true!- extendedSomething = true;-- // now do the actual extension of the complex selector- Node extendedSelectors = extendComplexSelector(pSelector, seen, isReplace, true);-- if (!pSelector->has_placeholder()) {- Node nSelector(complexSelectorToNode(pSelector));- if (!extendedSelectors.contains(nSelector)) {- pNewSelectors->append(pSelector);- continue;- }- }-- bool doReplace = isReplace;- for (Node& childNode : *extendedSelectors.collection()) {- // When it is a replace, skip the first one, unless there is only one- if(doReplace && extendedSelectors.collection()->size() > 1 ) {- doReplace = false;- continue;- }- pNewSelectors->append(nodeToComplexSelector(childNode));- }- }-- Remove_Placeholders remove_placeholders;- // it seems that we have to remove the place holders early here- // normally we do this as the very last step (compare to ruby sass)- pNewSelectors = remove_placeholders.remove_placeholders(pNewSelectors);-- // unwrap all wrapped selectors with inner lists- for (Complex_Selector_Obj cur : pNewSelectors->elements()) {- // process tails- while (cur) {- // process header- if (cur->head() && seen.find(cur->head()) == seen.end()) {- CompoundSelectorSet recseen(seen);- recseen.insert(cur->head());- // create a copy since we add multiple items if stuff get unwrapped- Compound_Selector_Obj cpy_head = SASS_MEMORY_NEW(Compound_Selector, cur->pstate());- for (Simple_Selector_Obj hs : *cur->head()) {- if (Wrapped_Selector_Obj ws = Cast<Wrapped_Selector>(hs)) {- ws->selector(SASS_MEMORY_CLONE(ws->selector()));- if (Selector_List_Obj sl = Cast<Selector_List>(ws->selector())) {- // special case for ruby ass- if (sl->empty()) {- // this seems inconsistent but it is how ruby sass seems to remove parentheses- cpy_head->append(SASS_MEMORY_NEW(Element_Selector, hs->pstate(), ws->name()));- }- // has wrapped not selectors- else if (ws->name() == ":not") {- // extend the inner list of wrapped selector- bool extended = false;- Selector_List_Obj ext_sl = extendSelectorList(sl, false, extended, recseen);- for (size_t i = 0; i < ext_sl->length(); i += 1) {- if (Complex_Selector_Obj ext_cs = ext_sl->at(i)) {- // create clones for wrapped selector and the inner list- Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);- Selector_List_Obj cpy_ws_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());- // remove parent selectors from inner selector- Compound_Selector_Obj ext_head = NULL;- if (ext_cs->first()) ext_head = ext_cs->first()->head();- if (ext_head && ext_head && ext_head->length() > 0) {- cpy_ws_sl->append(ext_cs->first());- }- // assign list to clone- cpy_ws->selector(cpy_ws_sl);- // append the clone- cpy_head->append(cpy_ws);- }- }- if (eval && extended) {- eval->exp.selector_stack.push_back(pNewSelectors);- cpy_head->perform(eval);- eval->exp.selector_stack.pop_back();- }- }- // has wrapped selectors- else {- Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);- Selector_List_Obj ext_sl = extendSelectorList(sl, recseen);- cpy_ws->selector(ext_sl);- cpy_head->append(cpy_ws);- }- } else {- cpy_head->append(hs);- }- } else {- cpy_head->append(hs);- }- }- // replace header- cur->head(cpy_head);- }- // process tail- cur = cur->tail();- }- }-- // memory results in a map table - since extending is very expensive- memoizeList.insert(std::pair<Selector_List_Obj, Selector_List_Obj>(pSelectorList, pNewSelectors));-- return pNewSelectors.detach();-- }--- bool shouldExtendBlock(Block_Obj b) {-- // If a block is empty, there's no reason to extend it since any rules placed on this block- // won't have any output. The main benefit of this is for structures like:- //- // .a {- // .b {- // x: y;- // }- // }- //- // We end up visiting two rulesets (one with the selector .a and the other with the selector .a .b).- // In this case, we don't want to try to pull rules onto .a since they won't get output anyway since- // there are no child statements. However .a .b should have extensions applied.-- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);-- if (Cast<Ruleset>(stm)) {- // Do nothing. This doesn't count as a statement that causes extension since we'll- // iterate over this rule set in a future visit and try to extend it.- }- else {- return true;- }- }-- return false;-- }--- // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change.- // Every Ruleset in the whole tree is calling this function. We decide if there- // was is @extend that matches our selector. If we find one, we will go further- // and call the extend magic for our selector. The subset_map contains all blocks- // where @extend was found. Pick the ones that match our selector!- void Extend::extendObjectWithSelectorAndBlock(Ruleset_Ptr pObject) {-- DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << Cast<Selector_List>(pObject->selector())->to_string())-- // Ruby sass seems to filter nodes that don't have any content well before we get here.- // I'm not sure the repercussions of doing so, so for now, let's just not extend things- // that won't be output later. Profiling shows this may us 0.2% or so.- if (!shouldExtendBlock(pObject->block())) {- DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT")- return;- }-- bool extendedSomething = false;-- CompoundSelectorSet seen;- Selector_List_Obj pNewSelectorList = extendSelectorList(pObject->selector(), false, extendedSomething, seen);-- if (extendedSomething && pNewSelectorList) {- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << pObject->selector()->to_string())- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string())- pNewSelectorList->remove_parent_selectors();- pObject->selector(pNewSelectorList);- } else {- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING")- }- }-- Extend::Extend(Subset_Map& ssm)- : subset_map(ssm), eval(NULL)- { }-- void Extend::setEval(Eval& e) {- eval = &e;- }-- void Extend::operator()(Block_Ptr b)- {- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);- stm->perform(this);- }- // do final check if everything was extended- // we set `extended` flag on extended selectors- if (b->is_root()) {- // debug_subset_map(subset_map);- for(auto const &it : subset_map.values()) {- Complex_Selector_Ptr sel = NULL;- Compound_Selector_Ptr ext = NULL;- if (it.first) sel = it.first->first();- if (it.second) ext = it.second;- if (ext && (ext->extended() || ext->is_optional())) continue;- std::string str_sel(sel ? sel->to_string({ NESTED, 5 }) : "NULL");- std::string str_ext(ext ? ext->to_string({ NESTED, 5 }) : "NULL");- // debug_ast(sel, "sel: ");- // debug_ast(ext, "ext: ");- error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n"- "The selector \"" + str_ext + "\" was not found.\n"- "Use \"@extend " + str_ext + " !optional\" if the"- " extend should be able to fail.", (ext ? ext->pstate() : NULL), eval->exp.traces);- }- }-- }-- void Extend::operator()(Ruleset_Ptr pRuleset)- {- extendObjectWithSelectorAndBlock( pRuleset );- pRuleset->block()->perform(this);- }-- void Extend::operator()(Supports_Block_Ptr pFeatureBlock)- {- pFeatureBlock->block()->perform(this);- }-- void Extend::operator()(Media_Block_Ptr pMediaBlock)- {- pMediaBlock->block()->perform(this);- }-- void Extend::operator()(Directive_Ptr a)- {- // Selector_List_Ptr ls = Cast<Selector_List>(a->selector());- // selector_stack.push_back(ls);- if (a->block()) a->block()->perform(this);- // exp.selector_stack.pop_back();- }-}
− libsass/src/extend.hpp
@@ -1,86 +0,0 @@-#ifndef SASS_EXTEND_H-#define SASS_EXTEND_H--#include <string>-#include <set>--#include "ast.hpp"-#include "node.hpp"-#include "eval.hpp"-#include "operation.hpp"-#include "subset_map.hpp"-#include "ast_fwd_decl.hpp"--namespace Sass {-- Node subweave(Node& one, Node& two);-- class Extend : public Operation_CRTP<void, Extend> {-- Subset_Map& subset_map;- Eval* eval;-- void fallback_impl(AST_Node_Ptr n) { }-- private:-- std::unordered_map<- Selector_List_Obj, // key- Selector_List_Obj, // value- HashNodes, // hasher- CompareNodes // compare- > memoizeList;-- std::unordered_map<- Complex_Selector_Obj, // key- Node, // value- HashNodes, // hasher- CompareNodes // compare- > memoizeComplex;-- /* this turned out to be too much overhead- re-evaluate once we store an ast selector- std::unordered_map<- Compound_Selector_Obj, // key- Node, // value- HashNodes, // hasher- CompareNodes // compare- > memoizeCompound;- */-- void extendObjectWithSelectorAndBlock(Ruleset_Ptr pObject);- Node extendComplexSelector(Complex_Selector_Ptr sel, CompoundSelectorSet& seen, bool isReplace, bool isOriginal);- Node extendCompoundSelector(Compound_Selector_Ptr sel, CompoundSelectorSet& seen, bool isReplace);- bool complexSelectorHasExtension(Complex_Selector_Ptr selector, CompoundSelectorSet& seen);- Node trim(Node& seqses, bool isReplace);- Node weave(Node& path);-- public:- void setEval(Eval& eval);- Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen);- Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace = false) {- bool extendedSomething = false;- CompoundSelectorSet seen;- return extendSelectorList(pSelectorList, isReplace, extendedSomething, seen);- }- Selector_List_Ptr extendSelectorList(Selector_List_Obj pSelectorList, CompoundSelectorSet& seen) {- bool isReplace = false;- bool extendedSomething = false;- return extendSelectorList(pSelectorList, isReplace, extendedSomething, seen);- }- Extend(Subset_Map&);- ~Extend() { }-- void operator()(Block_Ptr);- void operator()(Ruleset_Ptr);- void operator()(Supports_Block_Ptr);- void operator()(Media_Block_Ptr);- void operator()(Directive_Ptr);-- template <typename U>- void fallback(U x) { return fallback_impl(x); }- };--}--#endif
+ libsass/src/extender.cpp view
@@ -0,0 +1,1186 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+#include "ast.hpp"++#include "extender.hpp"+#include "permutate.hpp"+#include "dart_helpers.hpp"++namespace Sass {++ // ##########################################################################+ // Constructor without default [mode].+ // [traces] are needed to throw errors.+ // ##########################################################################+ Extender::Extender(Backtraces& traces) :+ mode(NORMAL),+ traces(traces),+ selectors(),+ extensions(),+ extensionsByExtender(),+ mediaContexts(),+ sourceSpecificity(),+ originals()+ {}++ // ##########################################################################+ // Constructor with specific [mode].+ // [traces] are needed to throw errors.+ // ##########################################################################+ Extender::Extender(ExtendMode mode, Backtraces& traces) :+ mode(mode),+ traces(traces),+ selectors(),+ extensions(),+ extensionsByExtender(),+ mediaContexts(),+ sourceSpecificity(),+ originals()+ {}++ // ##########################################################################+ // Extends [selector] with [source] extender and [targets] extendees.+ // This works as though `source {@extend target}` were written in the+ // stylesheet, with the exception that [target] can contain compound+ // selectors which must be extended as a unit.+ // ##########################################################################+ SelectorListObj Extender::extend(+ SelectorListObj& selector,+ const SelectorListObj& source,+ const SelectorListObj& targets,+ Backtraces& traces)+ {+ return extendOrReplace(selector, source, targets, ExtendMode::TARGETS, traces);+ }+ // EO Extender::extend++ // ##########################################################################+ // Returns a copy of [selector] with [targets] replaced by [source].+ // ##########################################################################+ SelectorListObj Extender::replace(+ SelectorListObj& selector,+ const SelectorListObj& source,+ const SelectorListObj& targets,+ Backtraces& traces)+ {+ return extendOrReplace(selector, source, targets, ExtendMode::REPLACE, traces);+ }+ // EO Extender::replace++ // ##########################################################################+ // A helper function for [extend] and [replace].+ // ##########################################################################+ SelectorListObj Extender::extendOrReplace(+ SelectorListObj& selector,+ const SelectorListObj& source,+ const SelectorListObj& targets,+ const ExtendMode mode,+ Backtraces& traces)+ {+ ExtSelExtMapEntry extenders;++ for (auto complex : source->elements()) {+ // Extension.oneOff(complex as ComplexSelector)+ extenders.insert(complex, Extension(complex));+ }++ for (auto complex : targets->elements()) {++ // This seems superfluous, check is done before!?+ // if (complex->length() != 1) {+ // error("complex selectors may not be extended.", complex->pstate(), traces);+ // }++ if (const CompoundSelector* compound = complex->first()->getCompound()) {++ ExtSelExtMap extensions;++ for (const SimpleSelectorObj& simple : compound->elements()) {+ extensions.insert(std::make_pair(simple, extenders));+ }++ Extender extender(mode, traces);++ if (!selector->is_invisible()) {+ for (auto sel : selector->elements()) {+ extender.originals.insert(sel);+ }+ }++ selector = extender.extendList(selector, extensions, {});++ }++ }++ return selector;++ }+ // EO extendOrReplace++ // ##########################################################################+ // The set of all simple selectors in style rules handled+ // by this extender. This includes simple selectors that+ // were added because of downstream extensions.+ // ##########################################################################+ ExtSmplSelSet Extender::getSimpleSelectors() const+ {+ ExtSmplSelSet set;+ for (auto& entry : selectors) {+ set.insert(entry.first);+ }+ return set;+ }+ // EO getSimpleSelectors++ // ##########################################################################+ // Check for extends that have not been satisfied.+ // Returns true if any non-optional extension did not+ // extend any selector. Updates the passed reference+ // to point to that Extension for further analysis.+ // ##########################################################################+ bool Extender::checkForUnsatisfiedExtends(Extension& unsatisfied) const+ {+ ExtSmplSelSet originals = getSimpleSelectors();+ for (auto target : extensions) {+ SimpleSelector* key = target.first;+ ExtSelExtMapEntry& val = target.second;+ if (originals.find(key) == originals.end()) {+ const Extension& extension = val.front().second;+ if (extension.isOptional) continue;+ unsatisfied = extension;+ return true;+ }+ }+ return false;+ }+ // EO checkUnsatisfiedExtends++ // ##########################################################################+ // Adds [selector] to this extender, with [selectorSpan] as the span covering+ // the selector and [ruleSpan] as the span covering the entire style rule.+ // Extends [selector] using any registered extensions, then returns an empty+ // [ModifiableCssStyleRule] with the resulting selector. If any more relevant+ // extensions are added, the returned rule is automatically updated.+ // The [mediaContext] is the media query context in which the selector was+ // defined, or `null` if it was defined at the top level of the document.+ // ##########################################################################+ void Extender::addSelector(+ const SelectorListObj& selector,+ const CssMediaRuleObj& mediaContext)+ {++ // Note: dart-sass makes a copy here AFAICT+ // Note: probably why we have originalStack+ // SelectorListObj original = selector;++ if (!selector->isInvisible()) {+ for (auto complex : selector->elements()) {+ originals.insert(complex);+ }+ }++ if (!extensions.empty()) {++ SelectorListObj res = extendList(selector, extensions, mediaContext);++ selector->elements(res->elements());++ }++ if (!mediaContext.isNull()) {+ mediaContexts.insert(selector, mediaContext);+ }++ registerSelector(selector, selector);++ }+ // EO addSelector++ // ##########################################################################+ // Registers the [SimpleSelector]s in [list]+ // to point to [rule] in [selectors].+ // ##########################################################################+ void Extender::registerSelector(+ const SelectorListObj& list,+ const SelectorListObj& rule)+ {+ if (list.isNull() || list->empty()) return;+ for (auto complex : list->elements()) {+ for (auto component : complex->elements()) {+ if (auto compound = component->getCompound()) {+ for (SimpleSelector* simple : compound->elements()) {+ selectors[simple].insert(rule);+ if (auto pseudo = simple->getPseudoSelector()) {+ if (pseudo->selector()) {+ auto sel = pseudo->selector();+ registerSelector(sel, rule);+ }+ }+ }+ }+ }+ }+ }+ // EO registerSelector++ // ##########################################################################+ // Returns an extension that combines [left] and [right]. Throws + // a [SassException] if [left] and [right] have incompatible + // media contexts. Throws an [ArgumentError] if [left]+ // and [right] don't have the same extender and target.+ // ##########################################################################+ Extension Extender::mergeExtension(+ const Extension& lhs,+ const Extension& rhs)+ {+ // If one extension is optional and doesn't add a+ // special media context, it doesn't need to be merged.+ if (rhs.isOptional && rhs.mediaContext.isNull()) return lhs;+ if (lhs.isOptional && lhs.mediaContext.isNull()) return rhs;++ Extension rv(lhs);+ // ToDo: is this right?+ rv.isOptional = true;+ rv.isOriginal = false;+ return rv;+ }+ // EO mergeExtension++ // ##########################################################################+ // Helper function to copy extension between maps+ // ##########################################################################+ // Seems only relevant for sass 4.0 modules+ // ##########################################################################+ /* void mapCopyExts(+ ExtSelExtMap& dest,+ const ExtSelExtMap& source)+ {+ for (auto it : source) {+ SimpleSelectorObj key = it.first;+ ExtSelExtMapEntry& inner = it.second;+ ExtSelExtMap::iterator dmap = dest.find(key);+ if (dmap == dest.end()) {+ dest.insert(std::make_pair(key, inner));+ }+ else {+ ExtSelExtMapEntry& imap = dmap->second;+ // ToDo: optimize ordered_map API!+ // ToDo: we iterate and fetch the value+ for (ComplexSelectorObj& it2 : inner) {+ imap.insert(it2, inner.get(it2));+ }+ }+ }+ } */+ // EO mapCopyExts++ // ##########################################################################+ // Adds an extension to this extender. The [extender] is the selector for the+ // style rule in which the extension is defined, and [target] is the selector+ // passed to `@extend`. The [extend] provides the extend span and indicates + // whether the extension is optional. The [mediaContext] defines the media query+ // context in which the extension is defined. It can only extend selectors+ // within the same context. A `null` context indicates no media queries.+ // ##########################################################################+ // ToDo: rename extender to parent, since it is not involved in extending stuff+ // ToDo: check why dart sass passes the ExtendRule around (is this the main selector?)+ // ##########################################################################+ // Note: this function could need some logic cleanup+ // ##########################################################################+ void Extender::addExtension(+ const SelectorListObj& extender,+ const SimpleSelectorObj& target,+ const CssMediaRuleObj& mediaQueryContext,+ bool is_optional)+ {++ auto rules = selectors.find(target);+ bool hasRule = rules != selectors.end();++ ExtSelExtMapEntry newExtensions;++ // ToDo: we check this here first and fetch the same? item again after the loop!?+ bool hasExistingExtensions = extensionsByExtender.find(target) != extensionsByExtender.end();++ ExtSelExtMapEntry& sources = extensions[target];++ for (auto& complex : extender->elements()) {++ Extension state(complex);+ // ToDo: fine-tune public API+ state.target = target;+ state.isOptional = is_optional;+ state.mediaContext = mediaQueryContext;++ if (sources.hasKey(complex)) {+ // If there's already an extend from [extender] to [target],+ // we don't need to re-run the extension. We may need to+ // mark the extension as mandatory, though.+ // sources.insert(complex, mergeExtension(existingState->second, state);+ // ToDo: implement behavior once use case is found!?+ continue;+ }++ sources.insert(complex, state);++ for (auto& component : complex->elements()) {+ if (auto compound = component->getCompound()) {+ for (auto& simple : compound->elements()) {+ extensionsByExtender[simple].push_back(state);+ if (sourceSpecificity.find(simple) == sourceSpecificity.end()) {+ // Only source specificity for the original selector is relevant.+ // Selectors generated by `@extend` don't get new specificity.+ sourceSpecificity[simple] = complex->maxSpecificity();+ }+ }+ }+ }++ if (hasRule || hasExistingExtensions) {+ newExtensions.insert(complex, state);+ }++ }+ // EO foreach complex++ if (newExtensions.empty()) {+ return;+ }++ ExtSelExtMap newExtensionsByTarget;+ newExtensionsByTarget.insert(std::make_pair(target, newExtensions));+ // ToDo: do we really need to fetch again (see top off fn) + auto existingExtensions = extensionsByExtender.find(target);+ if (existingExtensions != extensionsByExtender.end()) {+ if (hasExistingExtensions && !existingExtensions->second.empty()) {+ // Seems only relevant for sass 4.0 modules+ // auto additionalExtensions =+ extendExistingExtensions(existingExtensions->second, newExtensionsByTarget);+ // Seems only relevant for sass 4.0 modules+ /* if (!additionalExtensions.empty()) {+ mapCopyExts(newExtensionsByTarget, additionalExtensions);+ } */+ }+ }++ if (hasRule) {+ extendExistingStyleRules(selectors[target], newExtensionsByTarget);+ }++ }+ // EO addExtension+ + // ##########################################################################+ // Extend [extensions] using [newExtensions].+ // ##########################################################################+ // Note: dart-sass throws an error in here+ // ##########################################################################+ void Extender::extendExistingStyleRules(+ const ExtListSelSet& rules,+ const ExtSelExtMap& newExtensions)+ {+ // Is a modifyableCssStyleRUle in dart sass+ for (const SelectorListObj& rule : rules) {+ const SelectorListObj& oldValue = SASS_MEMORY_COPY(rule);+ CssMediaRuleObj mediaContext;+ if (mediaContexts.hasKey(rule)) mediaContext = mediaContexts.get(rule);+ SelectorListObj ext = extendList(rule, newExtensions, mediaContext);+ // If no extends actually happenedit (for example becaues unification+ // failed), we don't need to re-register the selector.+ if (ObjEqualityFn(oldValue, ext)) continue;+ rule->elements(ext->elements());+ registerSelector(rule, rule);++ }+ }+ // EO extendExistingStyleRules++ // ##########################################################################+ // Extend [extensions] using [newExtensions]. Note that this does duplicate+ // some work done by [_extendExistingStyleRules], but it's necessary to+ // expand each extension's extender separately without reference to the full+ // selector list, so that relevant results don't get trimmed too early.+ //+ // Returns extensions that should be added to [newExtensions] before+ // extending selectors in order to properly handle extension loops such as:+ //+ // .c {x: y; @extend .a}+ // .x.y.a {@extend .b}+ // .z.b {@extend .c}+ //+ // Returns `null` (Note: empty map) if there are no extensions to add.+ // ##########################################################################+ // Note: maybe refactor to return `bool` (and pass reference)+ // Note: dart-sass throws an error in here+ // ##########################################################################+ ExtSelExtMap Extender::extendExistingExtensions(+ // Taking in a reference here makes MSVC debug stuck!?+ const std::vector<Extension>& oldExtensions,+ const ExtSelExtMap& newExtensions)+ {++ ExtSelExtMap additionalExtensions;++ // During the loop `oldExtensions` vector might be changed.+ // Callers normally pass this from `extensionsByExtender` and+ // that points back to the `sources` vector from `extensions`.+ for (size_t i = 0, iL = oldExtensions.size(); i < iL; i += 1) {+ const Extension& extension = oldExtensions[i];+ ExtSelExtMapEntry& sources = extensions[extension.target];+ std::vector<ComplexSelectorObj> selectors(extendComplex(+ extension.extender,+ newExtensions,+ extension.mediaContext+ ));++ if (selectors.empty()) {+ continue;+ }++ // ToDo: "catch" error from extend++ bool first = false, containsExtension =+ ObjEqualityFn(selectors.front(), extension.extender);+ for (const ComplexSelectorObj& complex : selectors) {+ // If the output contains the original complex + // selector, there's no need to recreate it.+ if (containsExtension && first) {+ first = false;+ continue;+ }++ const Extension withExtender =+ extension.withExtender(complex);+ if (sources.hasKey(complex)) {+ sources.insert(complex, mergeExtension(+ sources.get(complex), withExtender));+ }+ else {+ sources.insert(complex, withExtender);+ /*+ // Seems only relevant for sass 4.0 modules+ for (auto& component : complex->elements()) {+ if (auto compound = component->getCompound()) {+ for (auto& simple : compound->elements()) {+ extensionsByExtender[simple].push_back(withExtender);+ }+ }+ }+ if (newExtensions.find(extension.target) != newExtensions.end()) {+ additionalExtensions[extension.target].insert(complex, withExtender);+ }+ */+ }+ }++ // If [selectors] doesn't contain [extension.extender],+ // for example if it was replaced due to :not() expansion,+ // we must get rid of the old version.+ /*+ // Seems only relevant for sass 4.0 modules+ if (!containsExtension) {+ sources.erase(extension.extender);+ }+ */++ }++ return additionalExtensions;++ }+ // EO extendExistingExtensions++ // ##########################################################################+ // Extends [list] using [extensions].+ // ##########################################################################+ SelectorListObj Extender::extendList(+ const SelectorListObj& list,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext)+ {++ // This could be written more simply using [List.map], but we want to+ // avoid any allocations in the common case where no extends apply.+ std::vector<ComplexSelectorObj> extended;+ for (size_t i = 0; i < list->length(); i++) {+ const ComplexSelectorObj& complex = list->get(i);+ std::vector<ComplexSelectorObj> result =+ extendComplex(complex, extensions, mediaQueryContext);+ if (result.empty()) {+ if (!extended.empty()) {+ extended.push_back(complex);+ }+ }+ else {+ if (extended.empty()) {+ for (size_t n = 0; n < i; n += 1) {+ extended.push_back(list->get(n));+ }+ }+ for (auto sel : result) {+ extended.push_back(sel);+ }+ }+ }++ if (extended.empty()) {+ return list;+ }++ SelectorListObj rv = SASS_MEMORY_NEW(SelectorList, list->pstate());+ rv->concat(trim(extended, originals));+ return rv;++ }+ // EO extendList++ // ##########################################################################+ // Extends [complex] using [extensions], and+ // returns the contents of a [SelectorList].+ // ##########################################################################+ std::vector<ComplexSelectorObj> Extender::extendComplex(+ // Taking in a reference here makes MSVC debug stuck!?+ const ComplexSelectorObj& complex,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext)+ {++ // The complex selectors that each compound selector in [complex.components]+ // can expand to.+ //+ // For example, given+ //+ // .a .b {...}+ // .x .y {@extend .b}+ //+ // this will contain+ //+ // [+ // [.a],+ // [.b, .x .y]+ // ]+ //+ // This could be written more simply using [List.map], but we want to avoid+ // any allocations in the common case where no extends apply.++ std::vector<ComplexSelectorObj> result;+ std::vector<std::vector<ComplexSelectorObj>> extendedNotExpanded;+ bool isOriginal = originals.find(complex) != originals.end();+ for (size_t i = 0; i < complex->length(); i += 1) {+ const SelectorComponentObj& component = complex->get(i);+ if (CompoundSelector* compound = Cast<CompoundSelector>(component)) {+ std::vector<ComplexSelectorObj> extended = extendCompound(+ compound, extensions, mediaQueryContext, isOriginal);+ if (extended.empty()) {+ if (!extendedNotExpanded.empty()) {+ extendedNotExpanded.push_back({+ compound->wrapInComplex()+ });+ }+ }+ else {+ // Note: dart-sass checks for null!?+ if (extendedNotExpanded.empty()) {+ for (size_t n = 0; n < i; n++) {+ extendedNotExpanded.push_back({+ complex->at(n)->wrapInComplex()+ });+ }+ }+ extendedNotExpanded.push_back(extended);+ }+ }+ else {+ // Note: dart-sass checks for null!?+ if (!extendedNotExpanded.empty()) {+ extendedNotExpanded.push_back({+ component->wrapInComplex()+ });+ }+ }+ }++ // Note: dart-sass checks for null!?+ if (extendedNotExpanded.empty()) {+ return {};+ }++ bool first = true;++ // ToDo: either change weave or paths to work with the same data?+ std::vector<std::vector<ComplexSelectorObj>>+ paths = permutate(extendedNotExpanded);+ + for (const std::vector<ComplexSelectorObj>& path : paths) {+ // Unpack the inner complex selector to component list+ std::vector<std::vector<SelectorComponentObj>> _paths;+ for (const ComplexSelectorObj& sel : path) {+ _paths.insert(_paths.end(), sel->elements());+ }++ std::vector<std::vector<SelectorComponentObj>> weaved = weave(_paths);++ for (std::vector<SelectorComponentObj>& components : weaved) {++ ComplexSelectorObj cplx = SASS_MEMORY_NEW(ComplexSelector, "[phony]");+ cplx->hasPreLineFeed(complex->hasPreLineFeed());+ for (auto& pp : path) {+ if (pp->hasPreLineFeed()) {+ cplx->hasPreLineFeed(true);+ }+ }+ cplx->elements(components);++ // Make sure that copies of [complex] retain their status+ // as "original" selectors. This includes selectors that+ // are modified because a :not() was extended into.+ if (first && originals.find(complex) != originals.end()) {+ originals.insert(cplx);+ }+ first = false;++ result.push_back(cplx);++ }++ }++ return result;+ }+ // EO extendComplex++ // ##########################################################################+ // Returns a one-off [Extension] whose+ // extender is composed solely of [simple].+ // ##########################################################################+ Extension Extender::extensionForSimple(+ const SimpleSelectorObj& simple) const+ {+ Extension extension(simple->wrapInComplex());+ extension.specificity = maxSourceSpecificity(simple);+ extension.isOriginal = true;+ return extension;+ }+ // Extender::extensionForSimple++ // ##########################################################################+ // Returns a one-off [Extension] whose extender is composed+ // solely of a compound selector containing [simples].+ // ##########################################################################+ Extension Extender::extensionForCompound(+ // Taking in a reference here makes MSVC debug stuck!?+ const std::vector<SimpleSelectorObj>& simples) const+ {+ CompoundSelectorObj compound = SASS_MEMORY_NEW(CompoundSelector, ParserState("[ext]"));+ compound->concat(simples);+ Extension extension(compound->wrapInComplex());+ // extension.specificity = sourceSpecificity[simple];+ extension.isOriginal = true;+ return extension;+ }+ // EO extensionForCompound++ // ##########################################################################+ // Extends [compound] using [extensions], and returns the+ // contents of a [SelectorList]. The [inOriginal] parameter+ // indicates whether this is in an original complex selector,+ // meaning that [compound] should not be trimmed out.+ // ##########################################################################+ std::vector<ComplexSelectorObj> Extender::extendCompound(+ const CompoundSelectorObj& compound,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext,+ bool inOriginal)+ {++ // If there's more than one target and they all need to+ // match, we track which targets are actually extended.+ ExtSmplSelSet targetsUsed2;++ ExtSmplSelSet* targetsUsed = nullptr;++ if (mode != ExtendMode::NORMAL && extensions.size() > 1) {+ targetsUsed = &targetsUsed2;+ }++ std::vector<ComplexSelectorObj> result;+ // The complex selectors produced from each component of [compound].+ std::vector<std::vector<Extension>> options;++ for (size_t i = 0; i < compound->length(); i++) {+ const SimpleSelectorObj& simple = compound->get(i);+ auto extended = extendSimple(simple, extensions, mediaQueryContext, targetsUsed);+ if (extended.empty()) {+ if (!options.empty()) {+ options.push_back({ extensionForSimple(simple) });+ }+ }+ else {+ if (options.empty()) {+ if (i != 0) {+ std::vector<SimpleSelectorObj> in;+ for (size_t n = 0; n < i; n += 1) {+ in.push_back(compound->get(n));+ }+ options.push_back({ extensionForCompound(in) });+ }+ }+ options.insert(options.end(),+ extended.begin(), extended.end());+ }+ }++ if (options.empty()) {+ return {};+ }++ // If [_mode] isn't [ExtendMode.normal] and we didn't use all+ // the targets in [extensions], extension fails for [compound].+ if (targetsUsed != nullptr) {++ if (targetsUsed->size() != extensions.size()) {+ if (!targetsUsed->empty()) {+ return {};+ }+ }+ }++ // Optimize for the simple case of a single simple+ // selector that doesn't need any unification.+ if (options.size() == 1) {+ std::vector<Extension> exts = options[0];+ for (size_t n = 0; n < exts.size(); n += 1) {+ exts[n].assertCompatibleMediaContext(mediaQueryContext, traces);+ result.push_back(exts[n].extender);+ }+ return result;+ }++ // Find all paths through [options]. In this case, each path represents a+ // different unification of the base selector. For example, if we have:+ //+ // .a.b {...}+ // .w .x {@extend .a}+ // .y .z {@extend .b}+ //+ // then [options] is `[[.a, .w .x], [.b, .y .z]]` and `paths(options)` is+ //+ // [+ // [.a, .b],+ // [.a, .y .z],+ // [.w .x, .b],+ // [.w .x, .y .z]+ // ]+ //+ // We then unify each path to get a list of complex selectors:+ //+ // [+ // [.a.b],+ // [.y .a.z],+ // [.w .x.b],+ // [.w .y .x.z, .y .w .x.z]+ // ]++ bool first = mode != ExtendMode::REPLACE;+ std::vector<ComplexSelectorObj> unifiedPaths;+ std::vector<std::vector<Extension>> prePaths = permutate(options);++ for (size_t i = 0; i < prePaths.size(); i += 1) {+ std::vector<std::vector<SelectorComponentObj>> complexes;+ const std::vector<Extension>& path = prePaths[i];+ if (first) {+ // The first path is always the original selector. We can't just+ // return [compound] directly because pseudo selectors may be+ // modified, but we don't have to do any unification.+ first = false;+ CompoundSelectorObj mergedSelector =+ SASS_MEMORY_NEW(CompoundSelector, "[ext]");+ for (size_t n = 0; n < path.size(); n += 1) {+ const ComplexSelectorObj& sel = path[n].extender;+ if (CompoundSelectorObj compound = Cast<CompoundSelector>(sel->last())) {+ mergedSelector->concat(compound->elements());+ }+ }+ complexes.push_back({ mergedSelector });+ }+ else {+ std::vector<SimpleSelectorObj> originals;+ std::vector<std::vector<SelectorComponentObj>> toUnify;++ for (auto& state : path) {+ if (state.isOriginal) {+ const ComplexSelectorObj& sel = state.extender;+ if (const CompoundSelector* compound = Cast<CompoundSelector>(sel->last())) {+ originals.insert(originals.end(), compound->last());+ }+ }+ else {+ toUnify.push_back(state.extender->elements());+ }+ }+ if (!originals.empty()) {+ CompoundSelectorObj merged =+ SASS_MEMORY_NEW(CompoundSelector, "[phony]");+ merged->concat(originals);+ toUnify.insert(toUnify.begin(), { merged });+ }+ complexes = unifyComplex(toUnify);+ if (complexes.empty()) {+ return {};+ }++ }++ bool lineBreak = false;+ // var specificity = _sourceSpecificityFor(compound);+ for (const Extension& state : path) {+ state.assertCompatibleMediaContext(mediaQueryContext, traces);+ lineBreak = lineBreak || state.extender->hasPreLineFeed();+ // specificity = math.max(specificity, state.specificity);+ }++ for (std::vector<SelectorComponentObj>& components : complexes) {+ auto sel = SASS_MEMORY_NEW(ComplexSelector, "[ext]");+ sel->hasPreLineFeed(lineBreak);+ sel->elements(components);+ unifiedPaths.push_back(sel);+ }++ }++ return unifiedPaths;+ }+ // EO extendCompound++ // ##########################################################################+ // Extends [simple] without extending the+ // contents of any selector pseudos it contains.+ // ##########################################################################+ std::vector<Extension> Extender::extendWithoutPseudo(+ const SimpleSelectorObj& simple,+ const ExtSelExtMap& extensions,+ ExtSmplSelSet* targetsUsed) const+ {++ auto extension = extensions.find(simple);+ if (extension == extensions.end()) return {};+ const ExtSelExtMapEntry& extenders = extension->second;++ if (targetsUsed != nullptr) {+ targetsUsed->insert(simple);+ }+ if (mode == ExtendMode::REPLACE) {+ return extenders.values();+ }++ const std::vector<Extension>&+ values = extenders.values();+ std::vector<Extension> result;+ result.reserve(values.size() + 1);+ result.push_back(extensionForSimple(simple));+ result.insert(result.end(), values.begin(), values.end());+ return result;+ }+ // EO extendWithoutPseudo++ // ##########################################################################+ // Extends [simple] and also extending the+ // contents of any selector pseudos it contains.+ // ##########################################################################+ std::vector<std::vector<Extension>> Extender::extendSimple(+ const SimpleSelectorObj& simple,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext,+ ExtSmplSelSet* targetsUsed)+ {+ if (Pseudo_Selector* pseudo = Cast<Pseudo_Selector>(simple)) {+ if (pseudo->selector()) {+ std::vector<std::vector<Extension>> merged;+ std::vector<Pseudo_Selector_Obj> extended =+ extendPseudo(pseudo, extensions, mediaQueryContext);+ for (Pseudo_Selector_Obj& extend : extended) {+ SimpleSelectorObj simple = extend;+ std::vector<Extension> result =+ extendWithoutPseudo(simple, extensions, targetsUsed);+ if (result.empty()) result = { extensionForSimple(extend) };+ merged.push_back(result);+ }+ if (!extended.empty()) {+ return merged;+ }+ }+ }+ std::vector<Extension> result =+ extendWithoutPseudo(simple, extensions, targetsUsed);+ if (result.empty()) return {};+ return { result };+ }+ // extendSimple++ // ##########################################################################+ // Inner loop helper for [extendPseudo] function+ // ##########################################################################+ std::vector<ComplexSelectorObj> Extender::extendPseudoComplex(+ const ComplexSelectorObj& complex,+ const Pseudo_Selector_Obj& pseudo,+ const CssMediaRuleObj& mediaQueryContext)+ {++ if (complex->length() != 1) { return { complex }; }+ auto compound = Cast<CompoundSelector>(complex->get(0));+ if (compound == nullptr) { return { complex }; }+ if (compound->length() != 1) { return { complex }; }+ auto innerPseudo = Cast<Pseudo_Selector>(compound->get(0));+ if (innerPseudo == nullptr) { return { complex }; }+ if (!innerPseudo->selector()) { return { complex }; }++ std::string name(pseudo->normalized());++ if (name == "not") {+ // In theory, if there's a `:not` nested within another `:not`, the+ // inner `:not`'s contents should be unified with the return value.+ // For example, if `:not(.foo)` extends `.bar`, `:not(.bar)` should+ // become `.foo:not(.bar)`. However, this is a narrow edge case and+ // supporting it properly would make this code and the code calling it+ // a lot more complicated, so it's not supported for now.+ if (innerPseudo->normalized() != "matches") return {};+ return innerPseudo->selector()->elements();+ }+ else if (name == "matches" && name == "any" && name == "current" && name == "nth-child" && name == "nth-last-child") {+ // As above, we could theoretically support :not within :matches, but+ // doing so would require this method and its callers to handle much+ // more complex cases that likely aren't worth the pain.+ if (innerPseudo->name() != pseudo->name()) return {};+ if (!ObjEquality()(innerPseudo->argument(), pseudo->argument())) return {};+ return innerPseudo->selector()->elements();+ }+ else if (name == "has" && name == "host" && name == "host-context" && name == "slotted") {+ // We can't expand nested selectors here, because each layer adds an+ // additional layer of semantics. For example, `:has(:has(img))`+ // doesn't match `<div><img></div>` but `:has(img)` does.+ return { complex };+ }++ return {};++ }+ // EO extendPseudoComplex++ // ##########################################################################+ // Extends [pseudo] using [extensions], and returns+ // a list of resulting pseudo selectors.+ // ##########################################################################+ std::vector<Pseudo_Selector_Obj> Extender::extendPseudo(+ const Pseudo_Selector_Obj& pseudo,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext)+ {+ auto selector = pseudo->selector();+ SelectorListObj extended = extendList(+ selector, extensions, mediaQueryContext);+ if (!extended || !pseudo || !pseudo->selector()) { return {}; }+ if (ObjEqualityFn(pseudo->selector(), extended)) { return {}; }++ // For `:not()`, we usually want to get rid of any complex selectors because+ // that will cause the selector to fail to parse on all browsers at time of+ // writing. We can keep them if either the original selector had a complex+ // selector, or the result of extending has only complex selectors, because+ // either way we aren't breaking anything that isn't already broken.+ std::vector<ComplexSelectorObj> complexes = extended->elements();++ if (pseudo->normalized() == "not") {+ if (!hasAny(pseudo->selector()->elements(), hasMoreThanOne)) {+ if (hasAny(extended->elements(), hasExactlyOne)) {+ complexes.clear();+ for (auto& complex : extended->elements()) {+ if (complex->length() <= 1) {+ complexes.push_back(complex);+ }+ }+ }+ }+ }+ + std::vector<ComplexSelectorObj> expanded = expand(+ complexes, extendPseudoComplex, pseudo, mediaQueryContext);++ // Older browsers support `:not`, but only with a single complex selector.+ // In order to support those browsers, we break up the contents of a `:not`+ // unless it originally contained a selector list.+ if (pseudo->normalized() == "not") {+ if (pseudo->selector()->length() == 1) {+ std::vector<Pseudo_Selector_Obj> pseudos;+ for (size_t i = 0; i < expanded.size(); i += 1) {+ pseudos.push_back(pseudo->withSelector(+ expanded[i]->wrapInList()+ ));+ }+ return pseudos;+ }+ }++ SelectorListObj list = SASS_MEMORY_NEW(SelectorList, "[phony]");+ list->concat(complexes);+ return { pseudo->withSelector(list) };++ }+ // EO extendPseudo++ // ##########################################################################+ // Rotates the element in list from [start] (inclusive) to [end] (exclusive)+ // one index higher, looping the final element back to [start].+ // ##########################################################################+ void Extender::rotateSlice(+ std::vector<ComplexSelectorObj>& list,+ size_t start, size_t end)+ {+ auto element = list[end - 1];+ for (size_t i = start; i < end; i++) {+ auto next = list[i];+ list[i] = element;+ element = next;+ }+ }+ // EO rotateSlice++ // ##########################################################################+ // Removes elements from [selectors] if they're subselectors of other+ // elements. The [isOriginal] callback indicates which selectors are+ // original to the document, and thus should never be trimmed.+ // ##########################################################################+ // Note: for adaption I pass in the set directly, there is some+ // code path in selector-trim that might need this special callback+ // ##########################################################################+ std::vector<ComplexSelectorObj> Extender::trim(+ const std::vector<ComplexSelectorObj>& selectors,+ const ExtCplxSelSet& existing) const+ {++ // Avoid truly horrific quadratic behavior.+ // TODO(nweiz): I think there may be a way to get perfect trimming + // without going quadratic by building some sort of trie-like+ // data structure that can be used to look up superselectors.+ // TODO(mgreter): Check how this perfoms in C++ (up the limit)+ if (selectors.size() > 100) return selectors;++ // This is n² on the sequences, but only comparing between separate sequences+ // should limit the quadratic behavior. We iterate from last to first and reverse+ // the result so that, if two selectors are identical, we keep the first one.+ std::vector<ComplexSelectorObj> result; size_t numOriginals = 0;++ size_t i = selectors.size();+ outer: // Use label to continue loop+ while (--i != std::string::npos) {++ const ComplexSelectorObj& complex1 = selectors[i];+ // Check if selector in known in existing "originals"+ // For custom behavior dart-sass had `isOriginal(complex1)`+ if (existing.find(complex1) != existing.end()) {+ // Make sure we don't include duplicate originals, which could+ // happen if a style rule extends a component of its own selector.+ for (size_t j = 0; j < numOriginals; j++) {+ if (ObjEqualityFn(result[j], complex1)) {+ rotateSlice(result, 0, j + 1);+ goto outer;+ }+ }+ result.insert(result.begin(), complex1);+ numOriginals++;+ continue;+ }++ // The maximum specificity of the sources that caused [complex1]+ // to be generated. In order for [complex1] to be removed, there+ // must be another selector that's a superselector of it *and*+ // that has specificity greater or equal to this.+ size_t maxSpecificity = 0;+ for (const SelectorComponentObj& component : complex1->elements()) {+ if (const CompoundSelectorObj compound = Cast<CompoundSelector>(component)) {+ maxSpecificity = std::max(maxSpecificity, maxSourceSpecificity(compound));+ }+ }+ ++ // Look in [result] rather than [selectors] for selectors after [i]. This+ // ensures we aren't comparing against a selector that's already been trimmed,+ // and thus that if there are two identical selectors only one is trimmed.+ if (hasAny(result, dontTrimComplex, complex1, maxSpecificity)) {+ continue;+ }++ // Check if any element (up to [i]) from [selector] returns true+ // when passed to [dontTrimComplex]. The arguments [complex1] and+ // [maxSepcificity] will be passed to the invoked function.+ if (hasSubAny(selectors, i, dontTrimComplex, complex1, maxSpecificity)) {+ continue;+ }++ // ToDo: Maybe use deque for front insert?+ result.insert(result.begin(), complex1);++ }++ return result;++ }+ // EO trim++ // ##########################################################################+ // Returns the maximum specificity of the given [simple] source selector.+ // ##########################################################################+ size_t Extender::maxSourceSpecificity(const SimpleSelectorObj& simple) const+ {+ auto it = sourceSpecificity.find(simple);+ if (it == sourceSpecificity.end()) return 0;+ return it->second;+ }+ // EO maxSourceSpecificity(SimpleSelectorObj)++ // ##########################################################################+ // Returns the maximum specificity for sources that went into producing [compound].+ // ##########################################################################+ size_t Extender::maxSourceSpecificity(const CompoundSelectorObj& compound) const+ {+ size_t specificity = 0;+ for (auto simple : compound->elements()) {+ size_t src = maxSourceSpecificity(simple);+ specificity = std::max(specificity, src);+ }+ return specificity;+ }+ // EO maxSourceSpecificity(CompoundSelectorObj)++ // ##########################################################################+ // Helper function used as callbacks on lists+ // ##########################################################################+ bool Extender::dontTrimComplex(+ const ComplexSelector* complex2,+ const ComplexSelector* complex1,+ const size_t maxSpecificity)+ {+ if (complex2->minSpecificity() < maxSpecificity) return false;+ return complex2->isSuperselectorOf(complex1);+ }+ // EO dontTrimComplex++ // ##########################################################################+ // Helper function used as callbacks on lists+ // ##########################################################################+ bool Extender::hasExactlyOne(const ComplexSelectorObj& vec)+ {+ return vec->length() == 1;+ }+ // EO hasExactlyOne++ // ##########################################################################+ // Helper function used as callbacks on lists+ // ##########################################################################+ bool Extender::hasMoreThanOne(const ComplexSelectorObj& vec)+ {+ return vec->length() > 1;+ }+ // hasMoreThanOne++}
+ libsass/src/extender.hpp view
@@ -0,0 +1,399 @@+#ifndef SASS_EXTENDER_H+#define SASS_EXTENDER_H++#include <set>+#include <map>+#include <string>++#include "ast_helpers.hpp"+#include "ast_fwd_decl.hpp"+#include "operation.hpp"+#include "extension.hpp"+#include "backtrace.hpp"+#include "ordered_map.hpp"++namespace Sass {++ // ##########################################################################+ // Different hash map types used by extender+ // ##########################################################################++ // This is special (ptrs!)+ typedef std::unordered_set<+ ComplexSelectorObj,+ ObjPtrHash,+ ObjPtrEquality+ > ExtCplxSelSet;++ typedef std::unordered_set<+ SimpleSelectorObj,+ ObjHash,+ ObjEquality+ > ExtSmplSelSet;++ typedef std::unordered_set<+ SelectorListObj,+ ObjPtrHash,+ ObjPtrEquality+ > ExtListSelSet;++ typedef std::unordered_map<+ SimpleSelectorObj,+ ExtListSelSet,+ ObjHash,+ ObjEquality+ > ExtSelMap;++ typedef ordered_map<+ ComplexSelectorObj,+ Extension,+ ObjHash,+ ObjEquality+ > ExtSelExtMapEntry;++ typedef std::unordered_map<+ SimpleSelectorObj,+ ExtSelExtMapEntry,+ ObjHash,+ ObjEquality+ > ExtSelExtMap;++ typedef std::unordered_map <+ SimpleSelectorObj,+ std::vector<+ Extension+ >,+ ObjHash,+ ObjEquality+ > ExtByExtMap;++ class Extender : public Operation_CRTP<void, Extender> {++ public:++ enum ExtendMode { TARGETS, REPLACE, NORMAL, };++ private:++ // ##########################################################################+ // The mode that controls this extender's behavior.+ // ##########################################################################+ ExtendMode mode;++ // ##########################################################################+ // Shared backtraces with context and expander. Needed the throw+ // errors when e.g. extending across media query boundaries.+ // ##########################################################################+ Backtraces& traces;++ // ##########################################################################+ // A map from all simple selectors in the stylesheet to the rules that+ // contain them.This is used to find which rules an `@extend` applies to.+ // ##########################################################################+ ExtSelMap selectors;++ // ##########################################################################+ // A map from all extended simple selectors+ // to the sources of those extensions.+ // ##########################################################################+ ExtSelExtMap extensions;++ // ##########################################################################+ // A map from all simple selectors in extenders to+ // the extensions that those extenders define.+ // ##########################################################################+ ExtByExtMap extensionsByExtender;++ // ##########################################################################+ // A map from CSS rules to the media query contexts they're defined in.+ // This tracks the contexts in which each style rule is defined.+ // If a rule is defined at the top level, it doesn't have an entry.+ // ##########################################################################+ ordered_map<+ SelectorListObj,+ CssMediaRuleObj,+ ObjPtrHash,+ ObjPtrEquality+ > mediaContexts;+ + // ##########################################################################+ // A map from [SimpleSelector]s to the specificity of their source selectors.+ // This tracks the maximum specificity of the [ComplexSelector] that originally + // contained each [SimpleSelector]. This allows us to ensure we don't trim any+ // selectors that need to exist to satisfy the [second law that of extend][].+ // [second law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184+ // ##########################################################################+ std::unordered_map<+ SimpleSelectorObj,+ size_t,+ ObjPtrHash,+ ObjPtrEquality+ > sourceSpecificity;++ // ##########################################################################+ // A set of [ComplexSelector]s that were originally part of their+ // component [SelectorList]s, as opposed to being added by `@extend`.+ // This allows us to ensure that we don't trim any selectors+ // that need to exist to satisfy the [first law of extend][].+ // ##########################################################################+ ExtCplxSelSet originals;++ public:++ // Constructor without default [mode].+ // [traces] are needed to throw errors.+ Extender(Backtraces& traces);++ // ##########################################################################+ // Constructor with specific [mode].+ // [traces] are needed to throw errors.+ // ##########################################################################+ Extender(ExtendMode mode, Backtraces& traces);++ // ##########################################################################+ // Empty desctructor+ // ##########################################################################+ ~Extender() {};++ // ##########################################################################+ // Extends [selector] with [source] extender and [targets] extendees.+ // This works as though `source {@extend target}` were written in the+ // stylesheet, with the exception that [target] can contain compound+ // selectors which must be extended as a unit.+ // ##########################################################################+ static SelectorListObj extend(+ SelectorListObj& selector,+ const SelectorListObj& source,+ const SelectorListObj& target,+ Backtraces& traces);++ // ##########################################################################+ // Returns a copy of [selector] with [targets] replaced by [source].+ // ##########################################################################+ static SelectorListObj replace(+ SelectorListObj& selector,+ const SelectorListObj& source,+ const SelectorListObj& target,+ Backtraces& traces);++ // ##########################################################################+ // Adds [selector] to this extender, with [selectorSpan] as the span covering+ // the selector and [ruleSpan] as the span covering the entire style rule.+ // Extends [selector] using any registered extensions, then returns an empty+ // [ModifiableCssStyleRule] with the resulting selector. If any more relevant+ // extensions are added, the returned rule is automatically updated.+ // The [mediaContext] is the media query context in which the selector was+ // defined, or `null` if it was defined at the top level of the document.+ // ##########################################################################+ void addSelector(+ const SelectorListObj& selector,+ const CssMediaRuleObj& mediaContext);++ // ##########################################################################+ // Registers the [SimpleSelector]s in [list]+ // to point to [rule] in [selectors].+ // ##########################################################################+ void registerSelector(+ const SelectorListObj& list,+ const SelectorListObj& rule);++ // ##########################################################################+ // Adds an extension to this extender. The [extender] is the selector for the+ // style rule in which the extension is defined, and [target] is the selector+ // passed to `@extend`. The [extend] provides the extend span and indicates + // whether the extension is optional. The [mediaContext] defines the media query+ // context in which the extension is defined. It can only extend selectors+ // within the same context. A `null` context indicates no media queries.+ // ##########################################################################+ void addExtension(+ const SelectorListObj& extender,+ const SimpleSelectorObj& target,+ const CssMediaRuleObj& mediaQueryContext,+ bool is_optional = false);++ // ##########################################################################+ // The set of all simple selectors in style rules handled+ // by this extender. This includes simple selectors that+ // were added because of downstream extensions.+ // ##########################################################################+ ExtSmplSelSet getSimpleSelectors() const;++ // ##########################################################################+ // Check for extends that have not been satisfied.+ // Returns true if any non-optional extension did not+ // extend any selector. Updates the passed reference+ // to point to that Extension for further analysis.+ // ##########################################################################+ bool checkForUnsatisfiedExtends(+ Extension& unsatisfied) const;++ private:++ // ##########################################################################+ // A helper function for [extend] and [replace].+ // ##########################################################################+ static SelectorListObj extendOrReplace(+ SelectorListObj& selector,+ const SelectorListObj& source,+ const SelectorListObj& target,+ const ExtendMode mode,+ Backtraces& traces);++ // ##########################################################################+ // Returns an extension that combines [left] and [right]. Throws + // a [SassException] if [left] and [right] have incompatible + // media contexts. Throws an [ArgumentError] if [left]+ // and [right] don't have the same extender and target.+ // ##########################################################################+ static Extension mergeExtension(+ const Extension& lhs,+ const Extension& rhs);++ // ##########################################################################+ // Extend [extensions] using [newExtensions].+ // ##########################################################################+ // Note: dart-sass throws an error in here+ // ##########################################################################+ void extendExistingStyleRules(+ const ExtListSelSet& rules,+ const ExtSelExtMap& newExtensions);++ // ##########################################################################+ // Extend [extensions] using [newExtensions]. Note that this does duplicate+ // some work done by [_extendExistingStyleRules], but it's necessary to+ // expand each extension's extender separately without reference to the full+ // selector list, so that relevant results don't get trimmed too early.+ // Returns `null` (Note: empty map) if there are no extensions to add.+ // ##########################################################################+ ExtSelExtMap extendExistingExtensions(+ // Taking in a reference here makes MSVC debug stuck!?+ const std::vector<Extension>& extensions,+ const ExtSelExtMap& newExtensions);++ // ##########################################################################+ // Extends [list] using [extensions].+ // ##########################################################################+ SelectorListObj extendList(+ const SelectorListObj& list,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaContext);++ // ##########################################################################+ // Extends [complex] using [extensions], and+ // returns the contents of a [SelectorList].+ // ##########################################################################+ std::vector<ComplexSelectorObj> extendComplex(+ // Taking in a reference here makes MSVC debug stuck!?+ const ComplexSelectorObj& list,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext);++ // ##########################################################################+ // Returns a one-off [Extension] whose+ // extender is composed solely of [simple].+ // ##########################################################################+ Extension extensionForSimple(+ const SimpleSelectorObj& simple) const;++ // ##########################################################################+ // Returns a one-off [Extension] whose extender is composed+ // solely of a compound selector containing [simples].+ // ##########################################################################+ Extension extensionForCompound(+ // Taking in a reference here makes MSVC debug stuck!?+ const std::vector<SimpleSelectorObj>& simples) const;++ // ##########################################################################+ // Extends [compound] using [extensions], and returns the+ // contents of a [SelectorList]. The [inOriginal] parameter+ // indicates whether this is in an original complex selector,+ // meaning that [compound] should not be trimmed out.+ // ##########################################################################+ std::vector<ComplexSelectorObj> extendCompound(+ const CompoundSelectorObj& compound,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext,+ bool inOriginal = false);++ // ##########################################################################+ // Extends [simple] without extending the+ // contents of any selector pseudos it contains.+ // ##########################################################################+ std::vector<Extension> extendWithoutPseudo(+ const SimpleSelectorObj& simple,+ const ExtSelExtMap& extensions,+ ExtSmplSelSet* targetsUsed) const;++ // ##########################################################################+ // Extends [simple] and also extending the+ // contents of any selector pseudos it contains.+ // ##########################################################################+ std::vector<std::vector<Extension>> extendSimple(+ const SimpleSelectorObj& simple,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext,+ ExtSmplSelSet* targetsUsed);++ // ##########################################################################+ // Inner loop helper for [extendPseudo] function+ // ##########################################################################+ static std::vector<ComplexSelectorObj> extendPseudoComplex(+ const ComplexSelectorObj& complex,+ const Pseudo_Selector_Obj& pseudo,+ const CssMediaRuleObj& mediaQueryContext);++ // ##########################################################################+ // Extends [pseudo] using [extensions], and returns+ // a list of resulting pseudo selectors.+ // ##########################################################################+ std::vector<Pseudo_Selector_Obj> extendPseudo(+ const Pseudo_Selector_Obj& pseudo,+ const ExtSelExtMap& extensions,+ const CssMediaRuleObj& mediaQueryContext);++ // ##########################################################################+ // Rotates the element in list from [start] (inclusive) to [end] (exclusive)+ // one index higher, looping the final element back to [start].+ // ##########################################################################+ static void rotateSlice(+ std::vector<ComplexSelectorObj>& list,+ size_t start, size_t end);++ // ##########################################################################+ // Removes elements from [selectors] if they're subselectors of other+ // elements. The [isOriginal] callback indicates which selectors are+ // original to the document, and thus should never be trimmed.+ // ##########################################################################+ std::vector<ComplexSelectorObj> trim(+ const std::vector<ComplexSelectorObj>& selectors,+ const ExtCplxSelSet& set) const;++ // ##########################################################################+ // Returns the maximum specificity of the given [simple] source selector.+ // ##########################################################################+ size_t maxSourceSpecificity(const SimpleSelectorObj& simple) const;++ // ##########################################################################+ // Returns the maximum specificity for sources that went into producing [compound].+ // ##########################################################################+ size_t maxSourceSpecificity(const CompoundSelectorObj& compound) const;++ // ##########################################################################+ // Helper function used as callbacks on lists+ // ##########################################################################+ static bool dontTrimComplex(+ const ComplexSelector* complex2,+ const ComplexSelector* complex1,+ const size_t maxSpecificity);++ // ##########################################################################+ // Helper function used as callbacks on lists+ // ##########################################################################+ static bool hasExactlyOne(const ComplexSelectorObj& vec);+ static bool hasMoreThanOne(const ComplexSelectorObj& vec);++ };++}++#endif
+ libsass/src/extension.cpp view
@@ -0,0 +1,43 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast_helpers.hpp"+#include "extension.hpp"+#include "ast.hpp"++namespace Sass {++ // ##########################################################################+ // Static function to create a copy with a new extender+ // ##########################################################################+ Extension Extension::withExtender(const ComplexSelectorObj& newExtender) const+ {+ Extension extension(newExtender);+ extension.specificity = specificity;+ extension.isOptional = isOptional;+ extension.target = target;+ return extension;+ }++ // ##########################################################################+ // Asserts that the [mediaContext] for a selector is+ // compatible with the query context for this extender.+ // ##########################################################################+ void Extension::assertCompatibleMediaContext(CssMediaRuleObj mediaQueryContext, Backtraces& traces) const+ {++ if (this->mediaContext.isNull()) return;++ if (mediaQueryContext && ObjPtrEqualityFn(mediaContext->block(), mediaQueryContext->block())) return;++ if (ObjEqualityFn<CssMediaRuleObj>(mediaQueryContext, mediaContext)) return;++ throw Exception::ExtendAcrossMedia(traces, *this);++ }++ // ##########################################################################+ // ##########################################################################++}
+ libsass/src/extension.hpp view
@@ -0,0 +1,89 @@+#ifndef SASS_EXTENSION_H+#define SASS_EXTENSION_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <unordered_map>+#include <unordered_set>+#include "ast_fwd_decl.hpp"+#include "backtrace.hpp"++namespace Sass {++ class Extension {++ public:++ // The selector in which the `@extend` appeared.+ ComplexSelectorObj extender;++ // The selector that's being extended.+ // `null` for one-off extensions.+ SimpleSelectorObj target;++ // The minimum specificity required for any+ // selector generated from this extender.+ size_t specificity;++ // Whether this extension is optional.+ bool isOptional;++ // Whether this is a one-off extender representing a selector that was+ // originally in the document, rather than one defined with `@extend`.+ bool isOriginal;++ bool isSatisfied;++ // The media query context to which this extend is restricted,+ // or `null` if it can apply within any context.+ CssMediaRuleObj mediaContext;++ // Creates a one-off extension that's not intended to be modified over time.+ // If [specificity] isn't passed, it defaults to `extender.maxSpecificity`.+ Extension(ComplexSelectorObj extender) :+ extender(extender),+ target({}),+ specificity(0),+ isOptional(true),+ isOriginal(false),+ isSatisfied(false),+ mediaContext({}) {++ }++ // Copy constructor+ Extension(const Extension& extension) :+ extender(extension.extender),+ target(extension.target),+ specificity(extension.specificity),+ isOptional(extension.isOptional),+ isOriginal(extension.isOriginal),+ isSatisfied(extension.isSatisfied),+ mediaContext(extension.mediaContext) {++ }++ // Default constructor+ Extension() :+ extender({}),+ target({}),+ specificity(0),+ isOptional(false),+ isOriginal(false),+ isSatisfied(false),+ mediaContext({}) {+ }++ // Asserts that the [mediaContext] for a selector is + // compatible with the query context for this extender.+ void assertCompatibleMediaContext(CssMediaRuleObj mediaContext, Backtraces& traces) const;++ Extension withExtender(const ComplexSelectorObj& newExtender) const;++ };++}++#endif
libsass/src/file.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #ifdef _WIN32 # ifdef __MINGW32__ # ifndef off64_t@@ -10,9 +13,7 @@ #else # include <unistd.h> #endif-#include <iostream>-#include <fstream>-#include <cctype>+#include <cstdio> #include <vector> #include <algorithm> #include <sys/stat.h>@@ -21,6 +22,9 @@ #include "prelexer.hpp" #include "utf8_string.hpp" #include "sass_functions.hpp"+#include "error_handling.hpp"+#include "util.hpp"+#include "util_string.hpp" #include "sass2scss.h" #ifdef _WIN32@@ -79,7 +83,10 @@ wchar_t resolved[32768]; // windows unicode filepaths are encoded in utf16 std::string abspath(join_paths(get_cwd(), path));- std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));+ if (!(abspath[0] == '/' && abspath[1] == '/')) {+ abspath = "//?/" + abspath;+ }+ std::wstring wpath(UTF_8::convert_to_utf16(abspath)); std::replace(wpath.begin(), wpath.end(), '/', '\\'); DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL); if (rv > 32767) throw Exception::OperationError("Path is too long");@@ -99,19 +106,19 @@ bool is_absolute_path(const std::string& path) { #ifdef _WIN32- if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;+ if (path.length() >= 2 && Util::ascii_isalpha(path[0]) && path[1] == ':') return true; #endif size_t i = 0; // check if we have a protocol- if (path[i] && Prelexer::is_alpha(path[i])) {+ if (path[i] && Util::ascii_isalpha(static_cast<unsigned char>(path[i]))) { // skip over all alphanumeric characters- while (path[i] && Prelexer::is_alnum(path[i])) ++i;+ while (path[i] && Util::ascii_isalnum(static_cast<unsigned char>(path[i]))) ++i; i = i && path[i] == ':' ? i + 1 : 0; } return path[i] == '/'; } - // helper function to find the last directory seperator+ // helper function to find the last directory separator inline size_t find_last_folder_separator(const std::string& path, size_t limit = std::string::npos) { size_t pos;@@ -166,15 +173,15 @@ while((pos = path.find("/./", pos)) != std::string::npos) path.erase(pos, 2); // remove all leading and trailing self references- while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);- while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);+ while(path.size() >= 2 && path[0] == '.' && path[1] == '/') path.erase(0, 2);+ while((pos = path.length()) > 1 && path[pos - 2] == '/' && path[pos - 1] == '.') path.erase(pos - 2); size_t proto = 0; // check if we have a protocol- if (path[proto] && Prelexer::is_alpha(path[proto])) {+ if (path[proto] && Util::ascii_isalpha(static_cast<unsigned char>(path[proto]))) { // skip over all alphanumeric characters- while (path[proto] && Prelexer::is_alnum(path[proto++])) {}+ while (path[proto] && Util::ascii_isalnum(static_cast<unsigned char>(path[proto++]))) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; }@@ -253,9 +260,9 @@ size_t proto = 0; // check if we have a protocol- if (path[proto] && Prelexer::is_alpha(path[proto])) {+ if (path[proto] && Util::ascii_isalpha(static_cast<unsigned char>(path[proto]))) { // skip over all alphanumeric characters- while (path[proto] && Prelexer::is_alnum(path[proto++])) {}+ while (path[proto] && Util::ascii_isalnum(static_cast<unsigned char>(path[proto++]))) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; }@@ -281,7 +288,8 @@ #else // compare the charactes in a case insensitive manner // windows fs is only case insensitive in ascii ranges- if (tolower(abs_path[i]) != tolower(abs_base[i])) break;+ if (Util::ascii_tolower(static_cast<unsigned char>(abs_path[i])) !=+ Util::ascii_tolower(static_cast<unsigned char>(abs_base[i]))) break; #endif if (abs_path[i] == '/') index = i + 1; }@@ -323,6 +331,8 @@ // (2) underscore + given // (3) underscore + given + extension // (4) given + extension+ // (5) given + _index.scss+ // (6) given + _index.sass std::vector<Include> resolve_includes(const std::string& root, const std::string& file, const std::vector<std::string>& exts) { std::string filename = join_paths(root, file);@@ -350,6 +360,25 @@ abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); }+ // index files+ if (includes.size() == 0) {+ // ignore directories that look like @import'able filename+ for(auto ext : exts) {+ if (ends_with(name, ext)) return includes;+ }+ // next test underscore index exts+ for(auto ext : exts) {+ rel_path = join_paths(base, join_paths(name, "_index" + ext));+ abs_path = join_paths(root, rel_path);+ if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+ }+ // next test plain index exts+ for(auto ext : exts) {+ rel_path = join_paths(base, join_paths(name, "index" + ext));+ abs_path = join_paths(root, rel_path);+ if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+ }+ } // nothing found return includes; }@@ -411,7 +440,10 @@ wchar_t resolved[32768]; // windows unicode filepaths are encoded in utf16 std::string abspath(join_paths(get_cwd(), path));- std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));+ if (!(abspath[0] == '/' && abspath[1] == '/')) {+ abspath = "//?/" + abspath;+ }+ std::wstring wpath(UTF_8::convert_to_utf16(abspath)); std::replace(wpath.begin(), wpath.end(), '/', '\\'); DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL); if (rv > 32767) throw Exception::OperationError("Path is too long");@@ -430,28 +462,34 @@ // just convert from unsigned char* char* contents = (char*) pBuffer; #else+ // Read the file using `<cstdio>` instead of `<fstream>` for better portability.+ // The `<fstream>` header initializes `<locale>` and this buggy in GCC4/5 with static linking.+ // See:+ // https://www.spinics.net/lists/gcchelp/msg46851.html+ // https://github.com/sass/sassc-ruby/issues/128 struct stat st; if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;- std::ifstream file(path.c_str(), std::ios::in | std::ios::binary | std::ios::ate);- char* contents = 0;- if (file.is_open()) {- size_t size = file.tellg();- // allocate an extra byte for the null char- // and another one for edge-cases in lexer- contents = (char*) malloc((size+2)*sizeof(char));- file.seekg(0, std::ios::beg);- file.read(contents, size);- contents[size+0] = '\0';- contents[size+1] = '\0';- file.close();+ FILE* fd = std::fopen(path.c_str(), "rb");+ if (fd == nullptr) return nullptr;+ const std::size_t size = st.st_size;+ char* contents = static_cast<char*>(malloc(st.st_size + 2 * sizeof(char)));+ if (std::fread(static_cast<void*>(contents), 1, size, fd) != size) {+ free(contents);+ std::fclose(fd);+ return nullptr; }+ if (std::fclose(fd) != 0) {+ free(contents);+ return nullptr;+ }+ contents[size] = '\0';+ contents[size + 1] = '\0'; #endif std::string extension; if (path.length() > 5) { extension = path.substr(path.length() - 5, 5); }- for(size_t i=0; i<extension.size();++i)- extension[i] = tolower(extension[i]);+ Util::ascii_str_tolower(&extension); if (extension == ".sass" && contents != 0) { char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); free(contents); // free the indented contents
libsass/src/file.hpp view
@@ -1,6 +1,10 @@ #ifndef SASS_FILE_H #define SASS_FILE_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include <string> #include <vector> @@ -100,22 +104,11 @@ public: // the file contents char* contents;- // conected sourcemap+ // connected sourcemap char* srcmap; public: Resource(char* contents, char* srcmap) : contents(contents), srcmap(srcmap)- { }- };-- // parsed stylesheet from loaded resource- class StyleSheet : public Resource {- public:- // parsed root block- Block_Obj root;- public:- StyleSheet(const Resource& res, Block_Obj root)- : Resource(res), root(root) { } };
+ libsass/src/fn_colors.cpp view
@@ -0,0 +1,596 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <iomanip>+#include "ast.hpp"+#include "fn_utils.hpp"+#include "fn_colors.hpp"+#include "util.hpp"+#include "util_string.hpp"++namespace Sass {++ namespace Functions {++ bool string_argument(AST_Node_Obj obj) {+ String_Constant* s = Cast<String_Constant>(obj);+ if (s == nullptr) return false;+ const std::string& str = s->value();+ return starts_with(str, "calc(") ||+ starts_with(str, "var(");+ }++ void hsla_alpha_percent_deprecation(const ParserState& pstate, const std::string val)+ {++ std::string msg("Passing a percentage as the alpha value to hsla() will be interpreted");+ std::string tail("differently in future versions of Sass. For now, use " + val + " instead.");++ deprecated(msg, tail, false, pstate);++ }++ Signature rgb_sig = "rgb($red, $green, $blue)";+ BUILT_IN(rgb)+ {+ if (+ string_argument(env["$red"]) ||+ string_argument(env["$green"]) ||+ string_argument(env["$blue"])+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("+ + env["$red"]->to_string()+ + ", "+ + env["$green"]->to_string()+ + ", "+ + env["$blue"]->to_string()+ + ")"+ );+ }++ return SASS_MEMORY_NEW(Color_RGBA,+ pstate,+ COLOR_NUM("$red"),+ COLOR_NUM("$green"),+ COLOR_NUM("$blue"));+ }++ Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";+ BUILT_IN(rgba_4)+ {+ if (+ string_argument(env["$red"]) ||+ string_argument(env["$green"]) ||+ string_argument(env["$blue"]) ||+ string_argument(env["$alpha"])+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("+ + env["$red"]->to_string()+ + ", "+ + env["$green"]->to_string()+ + ", "+ + env["$blue"]->to_string()+ + ", "+ + env["$alpha"]->to_string()+ + ")"+ );+ }++ return SASS_MEMORY_NEW(Color_RGBA,+ pstate,+ COLOR_NUM("$red"),+ COLOR_NUM("$green"),+ COLOR_NUM("$blue"),+ ALPHA_NUM("$alpha"));+ }++ Signature rgba_2_sig = "rgba($color, $alpha)";+ BUILT_IN(rgba_2)+ {+ if (+ string_argument(env["$color"])+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("+ + env["$color"]->to_string()+ + ", "+ + env["$alpha"]->to_string()+ + ")"+ );+ }++ Color_RGBA_Obj c_arg = ARG("$color", Color)->toRGBA();++ if (+ string_argument(env["$alpha"])+ ) {+ std::stringstream strm;+ strm << "rgba("+ << (int)c_arg->r() << ", "+ << (int)c_arg->g() << ", "+ << (int)c_arg->b() << ", "+ << env["$alpha"]->to_string()+ << ")";+ return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());+ }++ Color_RGBA_Obj new_c = SASS_MEMORY_COPY(c_arg);+ new_c->a(ALPHA_NUM("$alpha"));+ new_c->disp("");+ return new_c.detach();+ }++ ////////////////+ // RGB FUNCTIONS+ ////////////////++ Signature red_sig = "red($color)";+ BUILT_IN(red)+ {+ Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();+ return SASS_MEMORY_NEW(Number, pstate, color->r());+ }++ Signature green_sig = "green($color)";+ BUILT_IN(green)+ {+ Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();+ return SASS_MEMORY_NEW(Number, pstate, color->g());+ }++ Signature blue_sig = "blue($color)";+ BUILT_IN(blue)+ {+ Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();+ return SASS_MEMORY_NEW(Number, pstate, color->b());+ }++ Color_RGBA* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) {+ Color_RGBA_Obj c1 = color1->toRGBA();+ Color_RGBA_Obj c2 = color2->toRGBA();+ double p = weight/100;+ double w = 2*p - 1;+ double a = c1->a() - c2->a();++ double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;+ double w2 = 1 - w1;++ return SASS_MEMORY_NEW(Color_RGBA,+ pstate,+ Sass::round(w1*c1->r() + w2*c2->r(), ctx.c_options.precision),+ Sass::round(w1*c1->g() + w2*c2->g(), ctx.c_options.precision),+ Sass::round(w1*c1->b() + w2*c2->b(), ctx.c_options.precision),+ c1->a()*p + c2->a()*(1-p));+ }++ Signature mix_sig = "mix($color1, $color2, $weight: 50%)";+ BUILT_IN(mix)+ {+ Color_Obj color1 = ARG("$color1", Color);+ Color_Obj color2 = ARG("$color2", Color);+ double weight = DARG_U_PRCT("$weight");+ return colormix(ctx, pstate, color1, color2, weight);++ }++ ////////////////+ // HSL FUNCTIONS+ ////////////////++ Signature hsl_sig = "hsl($hue, $saturation, $lightness)";+ BUILT_IN(hsl)+ {+ if (+ string_argument(env["$hue"]) ||+ string_argument(env["$saturation"]) ||+ string_argument(env["$lightness"])+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("+ + env["$hue"]->to_string()+ + ", "+ + env["$saturation"]->to_string()+ + ", "+ + env["$lightness"]->to_string()+ + ")"+ );+ }++ return SASS_MEMORY_NEW(Color_HSLA,+ pstate,+ ARGVAL("$hue"),+ ARGVAL("$saturation"),+ ARGVAL("$lightness"),+ 1.0);++ }++ Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";+ BUILT_IN(hsla)+ {+ if (+ string_argument(env["$hue"]) ||+ string_argument(env["$saturation"]) ||+ string_argument(env["$lightness"]) ||+ string_argument(env["$alpha"])+ ) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("+ + env["$hue"]->to_string()+ + ", "+ + env["$saturation"]->to_string()+ + ", "+ + env["$lightness"]->to_string()+ + ", "+ + env["$alpha"]->to_string()+ + ")"+ );+ }++ Number* alpha = ARG("$alpha", Number);+ if (alpha && alpha->unit() == "%") {+ Number_Obj val = SASS_MEMORY_COPY(alpha);+ val->numerators.clear(); // convert+ val->value(val->value() / 100.0);+ std::string nr(val->to_string(ctx.c_options));+ hsla_alpha_percent_deprecation(pstate, nr);+ }++ return SASS_MEMORY_NEW(Color_HSLA,+ pstate,+ ARGVAL("$hue"),+ ARGVAL("$saturation"),+ ARGVAL("$lightness"),+ ARGVAL("$alpha"));++ }++ /////////////////////////////////////////////////////////////////////////+ // Query functions+ /////////////////////////////////////////////////////////////////////////++ Signature hue_sig = "hue($color)";+ BUILT_IN(hue)+ {+ Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();+ return SASS_MEMORY_NEW(Number, pstate, col->h(), "deg");+ }++ Signature saturation_sig = "saturation($color)";+ BUILT_IN(saturation)+ {+ Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();+ return SASS_MEMORY_NEW(Number, pstate, col->s(), "%");+ }++ Signature lightness_sig = "lightness($color)";+ BUILT_IN(lightness)+ {+ Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();+ return SASS_MEMORY_NEW(Number, pstate, col->l(), "%");+ }++ /////////////////////////////////////////////////////////////////////////+ // HSL manipulation functions+ /////////////////////////////////////////////////////////////////////////++ Signature adjust_hue_sig = "adjust-hue($color, $degrees)";+ BUILT_IN(adjust_hue)+ {+ Color* col = ARG("$color", Color);+ double degrees = ARGVAL("$degrees");+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->h(absmod(copy->h() + degrees, 360.0));+ return copy.detach();+ }++ Signature lighten_sig = "lighten($color, $amount)";+ BUILT_IN(lighten)+ {+ Color* col = ARG("$color", Color);+ double amount = DARG_U_PRCT("$amount");+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->l(clip(copy->l() + amount, 0.0, 100.0));+ return copy.detach();++ }++ Signature darken_sig = "darken($color, $amount)";+ BUILT_IN(darken)+ {+ Color* col = ARG("$color", Color);+ double amount = DARG_U_PRCT("$amount");+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->l(clip(copy->l() - amount, 0.0, 100.0));+ return copy.detach();+ }++ Signature saturate_sig = "saturate($color, $amount: false)";+ BUILT_IN(saturate)+ {+ // CSS3 filter function overload: pass literal through directly+ if (!Cast<Number>(env["$amount"])) {+ return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");+ }++ Color* col = ARG("$color", Color);+ double amount = DARG_U_PRCT("$amount");+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->s(clip(copy->s() + amount, 0.0, 100.0));+ return copy.detach();+ }++ Signature desaturate_sig = "desaturate($color, $amount)";+ BUILT_IN(desaturate)+ {+ Color* col = ARG("$color", Color);+ double amount = DARG_U_PRCT("$amount");+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->s(clip(copy->s() - amount, 0.0, 100.0));+ return copy.detach();+ }++ Signature grayscale_sig = "grayscale($color)";+ BUILT_IN(grayscale)+ {+ // CSS3 filter function overload: pass literal through directly+ Number* amount = Cast<Number>(env["$color"]);+ if (amount) {+ return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");+ }++ Color* col = ARG("$color", Color);+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->s(0.0); // just reset saturation+ return copy.detach();+ }++ /////////////////////////////////////////////////////////////////////////+ // Misc manipulation functions+ /////////////////////////////////////////////////////////////////////////++ Signature complement_sig = "complement($color)";+ BUILT_IN(complement)+ {+ Color* col = ARG("$color", Color);+ Color_HSLA_Obj copy = col->copyAsHSLA();+ copy->h(absmod(copy->h() - 180.0, 360.0));+ return copy.detach();+ }++ Signature invert_sig = "invert($color, $weight: 100%)";+ BUILT_IN(invert)+ {+ // CSS3 filter function overload: pass literal through directly+ Number* amount = Cast<Number>(env["$color"]);+ double weight = DARG_U_PRCT("$weight");+ if (amount) {+ // TODO: does not throw on 100% manually passed as value+ if (weight < 100.0) {+ error("Only one argument may be passed to the plain-CSS invert() function.", pstate, traces);+ }+ return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");+ }++ Color* col = ARG("$color", Color);+ Color_RGBA_Obj inv = col->copyAsRGBA();+ inv->r(clip(255.0 - inv->r(), 0.0, 255.0));+ inv->g(clip(255.0 - inv->g(), 0.0, 255.0));+ inv->b(clip(255.0 - inv->b(), 0.0, 255.0));+ return colormix(ctx, pstate, inv, col, weight);+ }++ /////////////////////////////////////////////////////////////////////////+ // Opacity functions+ /////////////////////////////////////////////////////////////////////////++ Signature alpha_sig = "alpha($color)";+ Signature opacity_sig = "opacity($color)";+ BUILT_IN(alpha)+ {+ String_Constant* ie_kwd = Cast<String_Constant>(env["$color"]);+ if (ie_kwd) {+ return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");+ }++ // CSS3 filter function overload: pass literal through directly+ Number* amount = Cast<Number>(env["$color"]);+ if (amount) {+ return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");+ }++ return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a());+ }++ Signature opacify_sig = "opacify($color, $amount)";+ Signature fade_in_sig = "fade-in($color, $amount)";+ BUILT_IN(opacify)+ {+ Color* col = ARG("$color", Color);+ double amount = DARG_U_FACT("$amount");+ Color_Obj copy = SASS_MEMORY_COPY(col);+ copy->a(clip(col->a() + amount, 0.0, 1.0));+ return copy.detach();+ }++ Signature transparentize_sig = "transparentize($color, $amount)";+ Signature fade_out_sig = "fade-out($color, $amount)";+ BUILT_IN(transparentize)+ {+ Color* col = ARG("$color", Color);+ double amount = DARG_U_FACT("$amount");+ Color_Obj copy = SASS_MEMORY_COPY(col);+ copy->a(std::max(col->a() - amount, 0.0));+ return copy.detach();+ }++ ////////////////////////+ // OTHER COLOR FUNCTIONS+ ////////////////////////++ Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+ BUILT_IN(adjust_color)+ {+ Color* col = ARG("$color", Color);+ Number* r = Cast<Number>(env["$red"]);+ Number* g = Cast<Number>(env["$green"]);+ Number* b = Cast<Number>(env["$blue"]);+ Number* h = Cast<Number>(env["$hue"]);+ Number* s = Cast<Number>(env["$saturation"]);+ Number* l = Cast<Number>(env["$lightness"]);+ Number* a = Cast<Number>(env["$alpha"]);++ bool rgb = r || g || b;+ bool hsl = h || s || l;++ if (rgb && hsl) {+ error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);+ }+ else if (rgb) {+ Color_RGBA_Obj c = col->copyAsRGBA();+ if (r) c->r(c->r() + DARG_R_BYTE("$red"));+ if (g) c->g(c->g() + DARG_R_BYTE("$green"));+ if (b) c->b(c->b() + DARG_R_BYTE("$blue"));+ if (a) c->a(c->a() + DARG_R_FACT("$alpha"));+ return c.detach();+ }+ else if (hsl) {+ Color_HSLA_Obj c = col->copyAsHSLA();+ if (h) c->h(c->h() + absmod(h->value(), 360.0));+ if (s) c->s(c->s() + DARG_R_PRCT("$saturation"));+ if (l) c->l(c->l() + DARG_R_PRCT("$lightness"));+ if (a) c->a(c->a() + DARG_R_FACT("$alpha"));+ return c.detach();+ }+ else if (a) {+ Color_Obj c = SASS_MEMORY_COPY(col);+ c->a(c->a() + DARG_R_FACT("$alpha"));+ c->a(clip(c->a(), 0.0, 1.0));+ return c.detach();+ }+ error("not enough arguments for `adjust-color'", pstate, traces);+ // unreachable+ return col;+ }++ Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+ BUILT_IN(scale_color)+ {+ Color* col = ARG("$color", Color);+ Number* r = Cast<Number>(env["$red"]);+ Number* g = Cast<Number>(env["$green"]);+ Number* b = Cast<Number>(env["$blue"]);+ Number* h = Cast<Number>(env["$hue"]);+ Number* s = Cast<Number>(env["$saturation"]);+ Number* l = Cast<Number>(env["$lightness"]);+ Number* a = Cast<Number>(env["$alpha"]);++ bool rgb = r || g || b;+ bool hsl = h || s || l;++ if (rgb && hsl) {+ error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);+ }+ else if (rgb) {+ Color_RGBA_Obj c = col->copyAsRGBA();+ double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;+ double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;+ double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;+ double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;+ if (rscale) c->r(c->r() + rscale * (rscale > 0.0 ? 255.0 - c->r() : c->r()));+ if (gscale) c->g(c->g() + gscale * (gscale > 0.0 ? 255.0 - c->g() : c->g()));+ if (bscale) c->b(c->b() + bscale * (bscale > 0.0 ? 255.0 - c->b() : c->b()));+ if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));+ return c.detach();+ }+ else if (hsl) {+ Color_HSLA_Obj c = col->copyAsHSLA();+ double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;+ double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;+ double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;+ double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;+ if (hscale) c->h(c->h() + hscale * (hscale > 0.0 ? 360.0 - c->h() : c->h()));+ if (sscale) c->s(c->s() + sscale * (sscale > 0.0 ? 100.0 - c->s() : c->s()));+ if (lscale) c->l(c->l() + lscale * (lscale > 0.0 ? 100.0 - c->l() : c->l()));+ if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));+ return c.detach();+ }+ else if (a) {+ Color_Obj c = SASS_MEMORY_COPY(col);+ double ascale = DARG_R_PRCT("$alpha") / 100.0;+ c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));+ c->a(clip(c->a(), 0.0, 1.0));+ return c.detach();+ }+ error("not enough arguments for `scale-color'", pstate, traces);+ // unreachable+ return col;+ }++ Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+ BUILT_IN(change_color)+ {+ Color* col = ARG("$color", Color);+ Number* r = Cast<Number>(env["$red"]);+ Number* g = Cast<Number>(env["$green"]);+ Number* b = Cast<Number>(env["$blue"]);+ Number* h = Cast<Number>(env["$hue"]);+ Number* s = Cast<Number>(env["$saturation"]);+ Number* l = Cast<Number>(env["$lightness"]);+ Number* a = Cast<Number>(env["$alpha"]);++ bool rgb = r || g || b;+ bool hsl = h || s || l;++ if (rgb && hsl) {+ error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);+ }+ else if (rgb) {+ Color_RGBA_Obj c = col->copyAsRGBA();+ if (r) c->r(DARG_U_BYTE("$red"));+ if (g) c->g(DARG_U_BYTE("$green"));+ if (b) c->b(DARG_U_BYTE("$blue"));+ if (a) c->a(DARG_U_FACT("$alpha"));+ return c.detach();+ }+ else if (hsl) {+ Color_HSLA_Obj c = col->copyAsHSLA();+ if (h) c->h(absmod(h->value(), 360.0));+ if (s) c->s(DARG_U_PRCT("$saturation"));+ if (l) c->l(DARG_U_PRCT("$lightness"));+ if (a) c->a(DARG_U_FACT("$alpha"));+ return c.detach();+ }+ else if (a) {+ Color_Obj c = SASS_MEMORY_COPY(col);+ c->a(clip(DARG_U_FACT("$alpha"), 0.0, 1.0));+ return c.detach();+ }+ error("not enough arguments for `change-color'", pstate, traces);+ // unreachable+ return col;+ }++ Signature ie_hex_str_sig = "ie-hex-str($color)";+ BUILT_IN(ie_hex_str)+ {+ Color* col = ARG("$color", Color);+ Color_RGBA_Obj c = col->toRGBA();+ double r = clip(c->r(), 0.0, 255.0);+ double g = clip(c->g(), 0.0, 255.0);+ double b = clip(c->b(), 0.0, 255.0);+ double a = clip(c->a(), 0.0, 1.0) * 255.0;++ std::stringstream ss;+ ss << '#' << std::setw(2) << std::setfill('0');+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision));++ std::string result = ss.str();+ Util::ascii_str_toupper(&result);+ return SASS_MEMORY_NEW(String_Quoted, pstate, result);+ }++ }++}
+ libsass/src/fn_colors.hpp view
@@ -0,0 +1,85 @@+#ifndef SASS_FN_COLORS_H+#define SASS_FN_COLORS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ // macros for common ranges (u mean unsigned or upper, r for full range)+ #define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double+ #define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double+ #define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double+ #define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double+ #define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double+ #define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double++ // macros for color related inputs (rbg and alpha/opacity values)+ #define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double+ #define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double++ extern Signature rgb_sig;+ extern Signature rgba_4_sig;+ extern Signature rgba_2_sig;+ extern Signature red_sig;+ extern Signature green_sig;+ extern Signature blue_sig;+ extern Signature mix_sig;+ extern Signature hsl_sig;+ extern Signature hsla_sig;+ extern Signature hue_sig;+ extern Signature saturation_sig;+ extern Signature lightness_sig;+ extern Signature adjust_hue_sig;+ extern Signature lighten_sig;+ extern Signature darken_sig;+ extern Signature saturate_sig;+ extern Signature desaturate_sig;+ extern Signature grayscale_sig;+ extern Signature complement_sig;+ extern Signature invert_sig;+ extern Signature alpha_sig;+ extern Signature opacity_sig;+ extern Signature opacify_sig;+ extern Signature fade_in_sig;+ extern Signature transparentize_sig;+ extern Signature fade_out_sig;+ extern Signature adjust_color_sig;+ extern Signature scale_color_sig;+ extern Signature change_color_sig;+ extern Signature ie_hex_str_sig;++ BUILT_IN(rgb);+ BUILT_IN(rgba_4);+ BUILT_IN(rgba_2);+ BUILT_IN(red);+ BUILT_IN(green);+ BUILT_IN(blue);+ BUILT_IN(mix);+ BUILT_IN(hsl);+ BUILT_IN(hsla);+ BUILT_IN(hue);+ BUILT_IN(saturation);+ BUILT_IN(lightness);+ BUILT_IN(adjust_hue);+ BUILT_IN(lighten);+ BUILT_IN(darken);+ BUILT_IN(saturate);+ BUILT_IN(desaturate);+ BUILT_IN(grayscale);+ BUILT_IN(complement);+ BUILT_IN(invert);+ BUILT_IN(alpha);+ BUILT_IN(opacify);+ BUILT_IN(transparentize);+ BUILT_IN(adjust_color);+ BUILT_IN(scale_color);+ BUILT_IN(change_color);+ BUILT_IN(ie_hex_str);++ }++}++#endif
+ libsass/src/fn_lists.cpp view
@@ -0,0 +1,285 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "listize.hpp"+#include "operators.hpp"+#include "fn_utils.hpp"+#include "fn_lists.hpp"++namespace Sass {++ namespace Functions {++ /////////////////+ // LIST FUNCTIONS+ /////////////////++ Signature keywords_sig = "keywords($args)";+ BUILT_IN(keywords)+ {+ List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy+ Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1);+ for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {+ Expression_Obj obj = arglist->at(i);+ Argument_Obj arg = (Argument*) obj.ptr(); // XXX+ std::string name = std::string(arg->name());+ name = name.erase(0, 1); // sanitize name (remove dollar sign)+ *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,+ pstate, name),+ arg->value());+ }+ return result.detach();+ }++ Signature length_sig = "length($list)";+ BUILT_IN(length)+ {+ if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {+ return SASS_MEMORY_NEW(Number, pstate, (double) sl->length());+ }+ Expression* v = ARG("$list", Expression);+ if (v->concrete_type() == Expression::MAP) {+ Map* map = Cast<Map>(env["$list"]);+ return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1));+ }+ if (v->concrete_type() == Expression::SELECTOR) {+ if (CompoundSelector * h = Cast<CompoundSelector>(v)) {+ return SASS_MEMORY_NEW(Number, pstate, (double)h->length());+ } else if (SelectorList * ls = Cast<SelectorList>(v)) {+ return SASS_MEMORY_NEW(Number, pstate, (double)ls->length());+ } else {+ return SASS_MEMORY_NEW(Number, pstate, 1);+ }+ }++ List* list = Cast<List>(env["$list"]);+ return SASS_MEMORY_NEW(Number,+ pstate,+ (double)(list ? list->size() : 1));+ }++ Signature nth_sig = "nth($list, $n)";+ BUILT_IN(nth)+ {+ double nr = ARGVAL("$n");+ Map* m = Cast<Map>(env["$list"]);+ if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {+ size_t len = m ? m->length() : sl->length();+ bool empty = m ? m->empty() : sl->empty();+ if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+ double index = std::floor(nr < 0 ? len + nr : nr - 1);+ if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);+ return Cast<Value>(Listize::perform(sl->get(static_cast<int>(index))));+ }+ List_Obj l = Cast<List>(env["$list"]);+ if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces);+ // if the argument isn't a list, then wrap it in a singleton list+ if (!m && !l) {+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression));+ }+ size_t len = m ? m->length() : l->length();+ bool empty = m ? m->empty() : l->empty();+ if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+ double index = std::floor(nr < 0 ? len + nr : nr - 1);+ if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);++ if (m) {+ l = SASS_MEMORY_NEW(List, pstate, 2);+ l->append(m->keys()[static_cast<unsigned int>(index)]);+ l->append(m->at(m->keys()[static_cast<unsigned int>(index)]));+ return l.detach();+ }+ else {+ Value_Obj rv = l->value_at_index(static_cast<int>(index));+ rv->set_delayed(false);+ return rv.detach();+ }+ }++ Signature set_nth_sig = "set-nth($list, $n, $value)";+ BUILT_IN(set_nth)+ {+ Map_Obj m = Cast<Map>(env["$list"]);+ List_Obj l = Cast<List>(env["$list"]);+ Number_Obj n = ARG("$n", Number);+ Expression_Obj v = ARG("$value", Expression);+ if (!l) {+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression));+ }+ if (m) {+ l = m->to_list(pstate);+ }+ if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);+ double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);+ if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);+ List* result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ result->append(((i == index) ? v : (*l)[i]));+ }+ return result;+ }++ Signature index_sig = "index($list, $value)";+ BUILT_IN(index)+ {+ Map_Obj m = Cast<Map>(env["$list"]);+ List_Obj l = Cast<List>(env["$list"]);+ Expression_Obj v = ARG("$value", Expression);+ if (!l) {+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression));+ }+ if (m) {+ l = m->to_list(pstate);+ }+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));+ }+ return SASS_MEMORY_NEW(Null, pstate);+ }++ Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)";+ BUILT_IN(join)+ {+ Map_Obj m1 = Cast<Map>(env["$list1"]);+ Map_Obj m2 = Cast<Map>(env["$list2"]);+ List_Obj l1 = Cast<List>(env["$list1"]);+ List_Obj l2 = Cast<List>(env["$list2"]);+ String_Constant_Obj sep = ARG("$separator", String_Constant);+ enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);+ Value* bracketed = ARG("$bracketed", Value);+ bool is_bracketed = (l1 ? l1->is_bracketed() : false);+ if (!l1) {+ l1 = SASS_MEMORY_NEW(List, pstate, 1);+ l1->append(ARG("$list1", Expression));+ sep_val = (l2 ? l2->separator() : SASS_SPACE);+ is_bracketed = (l2 ? l2->is_bracketed() : false);+ }+ if (!l2) {+ l2 = SASS_MEMORY_NEW(List, pstate, 1);+ l2->append(ARG("$list2", Expression));+ }+ if (m1) {+ l1 = m1->to_list(pstate);+ sep_val = SASS_COMMA;+ }+ if (m2) {+ l2 = m2->to_list(pstate);+ }+ size_t len = l1->length() + l2->length();+ std::string sep_str = unquote(sep->value());+ if (sep_str == "space") sep_val = SASS_SPACE;+ else if (sep_str == "comma") sep_val = SASS_COMMA;+ else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);+ String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);+ bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";+ if (!bracketed_is_auto) {+ is_bracketed = !bracketed->is_false();+ }+ List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed);+ result->concat(l1);+ result->concat(l2);+ return result.detach();+ }++ Signature append_sig = "append($list, $val, $separator: auto)";+ BUILT_IN(append)+ {+ Map_Obj m = Cast<Map>(env["$list"]);+ List_Obj l = Cast<List>(env["$list"]);+ Expression_Obj v = ARG("$val", Expression);+ if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {+ l = Cast<List>(Listize::perform(sl));+ }+ String_Constant_Obj sep = ARG("$separator", String_Constant);+ if (!l) {+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression));+ }+ if (m) {+ l = m->to_list(pstate);+ }+ List* result = SASS_MEMORY_COPY(l);+ std::string sep_str(unquote(sep->value()));+ if (sep_str != "auto") { // check default first+ if (sep_str == "space") result->separator(SASS_SPACE);+ else if (sep_str == "comma") result->separator(SASS_COMMA);+ else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);+ }+ if (l->is_arglist()) {+ result->append(SASS_MEMORY_NEW(Argument,+ v->pstate(),+ v,+ "",+ false,+ false));++ } else {+ result->append(v);+ }+ return result;+ }++ Signature zip_sig = "zip($lists...)";+ BUILT_IN(zip)+ {+ List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List));+ size_t shortest = 0;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ List_Obj ith = Cast<List>(arglist->value_at_index(i));+ Map_Obj mith = Cast<Map>(arglist->value_at_index(i));+ if (!ith) {+ if (mith) {+ ith = mith->to_list(pstate);+ } else {+ ith = SASS_MEMORY_NEW(List, pstate, 1);+ ith->append(arglist->value_at_index(i));+ }+ if (arglist->is_arglist()) {+ Argument_Obj arg = (Argument*)(arglist->at(i).ptr()); // XXX+ arg->value(ith);+ } else {+ (*arglist)[i] = ith;+ }+ }+ shortest = (i ? std::min(shortest, ith->length()) : ith->length());+ }+ List* zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA);+ size_t L = arglist->length();+ for (size_t i = 0; i < shortest; ++i) {+ List* zipper = SASS_MEMORY_NEW(List, pstate, L);+ for (size_t j = 0; j < L; ++j) {+ zipper->append(Cast<List>(arglist->value_at_index(j))->at(i));+ }+ zippers->append(zipper);+ }+ return zippers;+ }++ Signature list_separator_sig = "list_separator($list)";+ BUILT_IN(list_separator)+ {+ List_Obj l = Cast<List>(env["$list"]);+ if (!l) {+ l = SASS_MEMORY_NEW(List, pstate, 1);+ l->append(ARG("$list", Expression));+ }+ return SASS_MEMORY_NEW(String_Quoted,+ pstate,+ l->separator() == SASS_COMMA ? "comma" : "space");+ }++ Signature is_bracketed_sig = "is-bracketed($list)";+ BUILT_IN(is_bracketed)+ {+ Value_Obj value = ARG("$list", Value);+ List_Obj list = Cast<List>(value);+ return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed());+ }++ }++}
+ libsass/src/fn_lists.hpp view
@@ -0,0 +1,34 @@+#ifndef SASS_FN_LISTS_H+#define SASS_FN_LISTS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ extern Signature length_sig;+ extern Signature nth_sig;+ extern Signature index_sig;+ extern Signature join_sig;+ extern Signature append_sig;+ extern Signature zip_sig;+ extern Signature list_separator_sig;+ extern Signature is_bracketed_sig;+ extern Signature keywords_sig;++ BUILT_IN(length);+ BUILT_IN(nth);+ BUILT_IN(index);+ BUILT_IN(join);+ BUILT_IN(append);+ BUILT_IN(zip);+ BUILT_IN(list_separator);+ BUILT_IN(is_bracketed);+ BUILT_IN(keywords);++ }++}++#endif
+ libsass/src/fn_maps.cpp view
@@ -0,0 +1,94 @@+#include "operators.hpp"+#include "fn_utils.hpp"+#include "fn_maps.hpp"++namespace Sass {++ namespace Functions {++ /////////////////+ // MAP FUNCTIONS+ /////////////////++ Signature map_get_sig = "map-get($map, $key)";+ BUILT_IN(map_get)+ {+ // leaks for "map-get((), foo)" if not Obj+ // investigate why this is (unexpected)+ Map_Obj m = ARGM("$map", Map);+ Expression_Obj v = ARG("$key", Expression);+ try {+ Value_Obj val = m->at(v);+ if (!val) return SASS_MEMORY_NEW(Null, pstate);+ val->set_delayed(false);+ return val.detach();+ } catch (const std::out_of_range&) {+ return SASS_MEMORY_NEW(Null, pstate);+ }+ catch (...) { throw; }+ }++ Signature map_has_key_sig = "map-has-key($map, $key)";+ BUILT_IN(map_has_key)+ {+ Map_Obj m = ARGM("$map", Map);+ Expression_Obj v = ARG("$key", Expression);+ return SASS_MEMORY_NEW(Boolean, pstate, m->has(v));+ }++ Signature map_keys_sig = "map-keys($map)";+ BUILT_IN(map_keys)+ {+ Map_Obj m = ARGM("$map", Map);+ List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);+ for ( auto key : m->keys()) {+ result->append(key);+ }+ return result;+ }++ Signature map_values_sig = "map-values($map)";+ BUILT_IN(map_values)+ {+ Map_Obj m = ARGM("$map", Map);+ List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);+ for ( auto key : m->keys()) {+ result->append(m->at(key));+ }+ return result;+ }++ Signature map_merge_sig = "map-merge($map1, $map2)";+ BUILT_IN(map_merge)+ {+ Map_Obj m1 = ARGM("$map1", Map);+ Map_Obj m2 = ARGM("$map2", Map);++ size_t len = m1->length() + m2->length();+ Map* result = SASS_MEMORY_NEW(Map, pstate, len);+ // concat not implemented for maps+ *result += m1;+ *result += m2;+ return result;+ }++ Signature map_remove_sig = "map-remove($map, $keys...)";+ BUILT_IN(map_remove)+ {+ bool remove;+ Map_Obj m = ARGM("$map", Map);+ List_Obj arglist = ARG("$keys", List);+ Map* result = SASS_MEMORY_NEW(Map, pstate, 1);+ for (auto key : m->keys()) {+ remove = false;+ for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {+ remove = Operators::eq(key, arglist->value_at_index(j));+ }+ if (!remove) *result << std::make_pair(key, m->at(key));+ }+ return result;+ }++ }++}
+ libsass/src/fn_maps.hpp view
@@ -0,0 +1,30 @@+#ifndef SASS_FN_MAPS_H+#define SASS_FN_MAPS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ #define ARGM(argname, argtype) get_arg_m(argname, env, sig, pstate, traces)++ extern Signature map_get_sig;+ extern Signature map_merge_sig;+ extern Signature map_remove_sig;+ extern Signature map_keys_sig;+ extern Signature map_values_sig;+ extern Signature map_has_key_sig;++ BUILT_IN(map_get);+ BUILT_IN(map_merge);+ BUILT_IN(map_remove);+ BUILT_IN(map_keys);+ BUILT_IN(map_values);+ BUILT_IN(map_has_key);++ }++}++#endif
+ libsass/src/fn_miscs.cpp view
@@ -0,0 +1,244 @@+#include "ast.hpp"+#include "expand.hpp"+#include "fn_utils.hpp"+#include "fn_miscs.hpp"+#include "util_string.hpp"++namespace Sass {++ namespace Functions {++ //////////////////////////+ // INTROSPECTION FUNCTIONS+ //////////////////////////++ Signature type_of_sig = "type-of($value)";+ BUILT_IN(type_of)+ {+ Expression* v = ARG("$value", Expression);+ return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());+ }++ Signature variable_exists_sig = "variable-exists($name)";+ BUILT_IN(variable_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has("$"+s)) {+ return SASS_MEMORY_NEW(Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(Boolean, pstate, false);+ }+ }++ Signature global_variable_exists_sig = "global-variable-exists($name)";+ BUILT_IN(global_variable_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global("$"+s)) {+ return SASS_MEMORY_NEW(Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(Boolean, pstate, false);+ }+ }++ Signature function_exists_sig = "function-exists($name)";+ BUILT_IN(function_exists)+ {+ String_Constant* ss = Cast<String_Constant>(env["$name"]);+ if (!ss) {+ error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);+ }++ std::string name = Util::normalize_underscores(unquote(ss->value()));++ if(d_env.has(name+"[f]")) {+ return SASS_MEMORY_NEW(Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(Boolean, pstate, false);+ }+ }++ Signature mixin_exists_sig = "mixin-exists($name)";+ BUILT_IN(mixin_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has(s+"[m]")) {+ return SASS_MEMORY_NEW(Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(Boolean, pstate, false);+ }+ }++ Signature feature_exists_sig = "feature-exists($feature)";+ BUILT_IN(feature_exists)+ {+ std::string s = unquote(ARG("$feature", String_Constant)->value());++ static const auto *const features = new std::unordered_set<std::string> {+ "global-variable-shadowing",+ "extend-selector-pseudoclass",+ "at-error",+ "units-level-3",+ "custom-property"+ };+ return SASS_MEMORY_NEW(Boolean, pstate, features->find(s) != features->end());+ }++ Signature call_sig = "call($function, $args...)";+ BUILT_IN(call)+ {+ std::string function;+ Function* ff = Cast<Function>(env["$function"]);+ String_Constant* ss = Cast<String_Constant>(env["$function"]);++ if (ss) {+ function = Util::normalize_underscores(unquote(ss->value()));+ std::cerr << "DEPRECATION WARNING: ";+ std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;+ std::cerr << "in Sass 4.0. Use call(get-function(" + quote(function) + ")) instead." << std::endl;+ std::cerr << std::endl;+ } else if (ff) {+ function = ff->name();+ }++ List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));++ Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);+ // std::string full_name(name + "[f]");+ // Definition* def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0;+ // Parameters* params = def ? def->parameters() : 0;+ // size_t param_size = params ? params->length() : 0;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression_Obj expr = arglist->value_at_index(i);+ // if (params && params->has_rest_parameter()) {+ // Parameter_Obj p = param_size > i ? (*params)[i] : 0;+ // List* list = Cast<List>(expr);+ // if (list && p && !p->is_rest_parameter()) expr = (*list)[0];+ // }+ if (arglist->is_arglist()) {+ Expression_Obj obj = arglist->at(i);+ Argument_Obj arg = (Argument*) obj.ptr(); // XXX+ args->append(SASS_MEMORY_NEW(Argument,+ pstate,+ expr,+ arg ? arg->name() : "",+ arg ? arg->is_rest_argument() : false,+ arg ? arg->is_keyword_argument() : false));+ } else {+ args->append(SASS_MEMORY_NEW(Argument, pstate, expr));+ }+ }+ Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, function, args);++ Expand expand(ctx, &d_env, &selector_stack, &original_stack);+ func->via_call(true); // calc invoke is allowed+ if (ff) func->func(ff);+ return Cast<PreValue>(func->perform(&expand.eval));+ }++ ////////////////////+ // BOOLEAN FUNCTIONS+ ////////////////////++ Signature not_sig = "not($value)";+ BUILT_IN(sass_not)+ {+ return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());+ }++ Signature if_sig = "if($condition, $if-true, $if-false)";+ BUILT_IN(sass_if)+ {+ Expand expand(ctx, &d_env, &selector_stack, &original_stack);+ Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);+ bool is_true = !cond->is_false();+ Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression);+ Value_Obj qwe = Cast<Value>(res->perform(&expand.eval));+ // res = res->perform(&expand.eval.val_eval);+ qwe->set_delayed(false); // clone?+ return qwe.detach();+ }++ //////////////////////////+ // MISCELLANEOUS FUNCTIONS+ //////////////////////////++ Signature inspect_sig = "inspect($value)";+ BUILT_IN(inspect)+ {+ Expression* v = ARG("$value", Expression);+ if (v->concrete_type() == Expression::NULL_VAL) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "null");+ } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {+ return SASS_MEMORY_NEW(String_Constant, pstate, "false");+ } else if (v->concrete_type() == Expression::STRING) {+ String_Constant *s = Cast<String_Constant>(v);+ if (s->quote_mark()) {+ return SASS_MEMORY_NEW(String_Constant, pstate, quote(s->value(), s->quote_mark()));+ } else {+ return s;+ }+ } else {+ // ToDo: fix to_sass for nested parentheses+ Sass_Output_Style old_style;+ old_style = ctx.c_options.output_style;+ ctx.c_options.output_style = TO_SASS;+ Emitter emitter(ctx.c_options);+ Inspect i(emitter);+ i.in_declaration = false;+ v->perform(&i);+ ctx.c_options.output_style = old_style;+ return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer());+ }+ }++ Signature content_exists_sig = "content-exists()";+ BUILT_IN(content_exists)+ {+ if (!d_env.has_global("is_in_mixin")) {+ error("Cannot call content-exists() except within a mixin.", pstate, traces);+ }+ return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));+ }++ Signature get_function_sig = "get-function($name, $css: false)";+ BUILT_IN(get_function)+ {+ String_Constant* ss = Cast<String_Constant>(env["$name"]);+ if (!ss) {+ error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);+ }++ std::string name = Util::normalize_underscores(unquote(ss->value()));+ std::string full_name = name + "[f]";++ Boolean_Obj css = ARG("$css", Boolean);+ if (!css->is_false()) {+ Definition* def = SASS_MEMORY_NEW(Definition,+ pstate,+ name,+ SASS_MEMORY_NEW(Parameters, pstate),+ SASS_MEMORY_NEW(Block, pstate, 0, false),+ Definition::FUNCTION);+ return SASS_MEMORY_NEW(Function, pstate, def, true);+ }+++ if (!d_env.has_global(full_name)) {+ error("Function not found: " + name, pstate, traces);+ }++ Definition* def = Cast<Definition>(d_env[full_name]);+ return SASS_MEMORY_NEW(Function, pstate, def, false);+ }++ }++}
+ libsass/src/fn_miscs.hpp view
@@ -0,0 +1,40 @@+#ifndef SASS_FN_MISCS_H+#define SASS_FN_MISCS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ extern Signature type_of_sig;+ extern Signature variable_exists_sig;+ extern Signature global_variable_exists_sig;+ extern Signature function_exists_sig;+ extern Signature mixin_exists_sig;+ extern Signature feature_exists_sig;+ extern Signature call_sig;+ extern Signature not_sig;+ extern Signature if_sig;+ extern Signature set_nth_sig;+ extern Signature content_exists_sig;+ extern Signature get_function_sig;++ BUILT_IN(type_of);+ BUILT_IN(variable_exists);+ BUILT_IN(global_variable_exists);+ BUILT_IN(function_exists);+ BUILT_IN(mixin_exists);+ BUILT_IN(feature_exists);+ BUILT_IN(call);+ BUILT_IN(sass_not);+ BUILT_IN(sass_if);+ BUILT_IN(set_nth);+ BUILT_IN(content_exists);+ BUILT_IN(get_function);++ }++}++#endif
+ libsass/src/fn_numbers.cpp view
@@ -0,0 +1,227 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include <cstdint>+#include <cstdlib>+#include <cmath>+#include <random>+#include <sstream>+#include <iomanip>+#include <algorithm>++#include "ast.hpp"+#include "units.hpp"+#include "fn_utils.hpp"+#include "fn_numbers.hpp"++#ifdef __MINGW32__+#include "windows.h"+#include "wincrypt.h"+#endif++namespace Sass {++ namespace Functions {++ #ifdef __MINGW32__+ uint64_t GetSeed()+ {+ HCRYPTPROV hp = 0;+ BYTE rb[8];+ CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);+ CryptGenRandom(hp, sizeof(rb), rb);+ CryptReleaseContext(hp, 0);++ uint64_t seed;+ memcpy(&seed, &rb[0], sizeof(seed));++ return seed;+ }+ #else+ uint64_t GetSeed()+ {+ std::random_device rd;+ return rd();+ }+ #endif++ // note: the performance of many implementations of+ // random_device degrades sharply once the entropy pool+ // is exhausted. For practical use, random_device is+ // generally only used to seed a PRNG such as mt19937.+ static std::mt19937 rand(static_cast<unsigned int>(GetSeed()));++ ///////////////////+ // NUMBER FUNCTIONS+ ///////////////////++ Signature percentage_sig = "percentage($number)";+ BUILT_IN(percentage)+ {+ Number_Obj n = ARGN("$number");+ if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces);+ return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");+ }++ Signature round_sig = "round($number)";+ BUILT_IN(round)+ {+ Number_Obj r = ARGN("$number");+ r->value(Sass::round(r->value(), ctx.c_options.precision));+ r->pstate(pstate);+ return r.detach();+ }++ Signature ceil_sig = "ceil($number)";+ BUILT_IN(ceil)+ {+ Number_Obj r = ARGN("$number");+ r->value(std::ceil(r->value()));+ r->pstate(pstate);+ return r.detach();+ }++ Signature floor_sig = "floor($number)";+ BUILT_IN(floor)+ {+ Number_Obj r = ARGN("$number");+ r->value(std::floor(r->value()));+ r->pstate(pstate);+ return r.detach();+ }++ Signature abs_sig = "abs($number)";+ BUILT_IN(abs)+ {+ Number_Obj r = ARGN("$number");+ r->value(std::abs(r->value()));+ r->pstate(pstate);+ return r.detach();+ }++ Signature min_sig = "min($numbers...)";+ BUILT_IN(min)+ {+ List* arglist = ARG("$numbers", List);+ Number_Obj least;+ size_t L = arglist->length();+ if (L == 0) {+ error("At least one argument must be passed.", pstate, traces);+ }+ for (size_t i = 0; i < L; ++i) {+ Expression_Obj val = arglist->value_at_index(i);+ Number_Obj xi = Cast<Number>(val);+ if (!xi) {+ error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces);+ }+ if (least) {+ if (*xi < *least) least = xi;+ } else least = xi;+ }+ return least.detach();+ }++ Signature max_sig = "max($numbers...)";+ BUILT_IN(max)+ {+ List* arglist = ARG("$numbers", List);+ Number_Obj greatest;+ size_t L = arglist->length();+ if (L == 0) {+ error("At least one argument must be passed.", pstate, traces);+ }+ for (size_t i = 0; i < L; ++i) {+ Expression_Obj val = arglist->value_at_index(i);+ Number_Obj xi = Cast<Number>(val);+ if (!xi) {+ error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces);+ }+ if (greatest) {+ if (*greatest < *xi) greatest = xi;+ } else greatest = xi;+ }+ return greatest.detach();+ }++ Signature random_sig = "random($limit:false)";+ BUILT_IN(random)+ {+ AST_Node_Obj arg = env["$limit"];+ Value* v = Cast<Value>(arg);+ Number* l = Cast<Number>(arg);+ Boolean* b = Cast<Boolean>(arg);+ if (l) {+ double lv = l->value();+ if (lv < 1) {+ std::stringstream err;+ err << "$limit " << lv << " must be greater than or equal to 1 for `random'";+ error(err.str(), pstate, traces);+ }+ bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON;+ if (!eq_int) {+ std::stringstream err;+ err << "Expected $limit to be an integer but got " << lv << " for `random'";+ error(err.str(), pstate, traces);+ }+ std::uniform_real_distribution<> distributor(1, lv + 1);+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ return SASS_MEMORY_NEW(Number, pstate, (double)distributed);+ }+ else if (b) {+ std::uniform_real_distribution<> distributor(0, 1);+ double distributed = static_cast<double>(distributor(rand));+ return SASS_MEMORY_NEW(Number, pstate, distributed);+ } else if (v) {+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);+ } else {+ traces.push_back(Backtrace(pstate));+ throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");+ }+ }++ Signature unique_id_sig = "unique-id()";+ BUILT_IN(unique_id)+ {+ std::stringstream ss;+ std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;+ return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str());+ }++ Signature unit_sig = "unit($number)";+ BUILT_IN(unit)+ {+ Number_Obj arg = ARGN("$number");+ std::string str(quote(arg->unit(), '"'));+ return SASS_MEMORY_NEW(String_Quoted, pstate, str);+ }++ Signature unitless_sig = "unitless($number)";+ BUILT_IN(unitless)+ {+ Number_Obj arg = ARGN("$number");+ bool unitless = arg->is_unitless();+ return SASS_MEMORY_NEW(Boolean, pstate, unitless);+ }++ Signature comparable_sig = "comparable($number1, $number2)";+ BUILT_IN(comparable)+ {+ Number_Obj n1 = ARGN("$number1");+ Number_Obj n2 = ARGN("$number2");+ if (n1->is_unitless() || n2->is_unitless()) {+ return SASS_MEMORY_NEW(Boolean, pstate, true);+ }+ // normalize into main units+ n1->normalize(); n2->normalize();+ Units &lhs_unit = *n1, &rhs_unit = *n2;+ bool is_comparable = (lhs_unit == rhs_unit);+ return SASS_MEMORY_NEW(Boolean, pstate, is_comparable);+ }++ }++}
+ libsass/src/fn_numbers.hpp view
@@ -0,0 +1,45 @@+#ifndef SASS_FN_NUMBERS_H+#define SASS_FN_NUMBERS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ // return a number object (copied since we want to have reduced units)+ #define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy++ extern Signature percentage_sig;+ extern Signature round_sig;+ extern Signature ceil_sig;+ extern Signature floor_sig;+ extern Signature abs_sig;+ extern Signature min_sig;+ extern Signature max_sig;+ extern Signature inspect_sig;+ extern Signature random_sig;+ extern Signature unique_id_sig;+ extern Signature unit_sig;+ extern Signature unitless_sig;+ extern Signature comparable_sig;++ BUILT_IN(percentage);+ BUILT_IN(round);+ BUILT_IN(ceil);+ BUILT_IN(floor);+ BUILT_IN(abs);+ BUILT_IN(min);+ BUILT_IN(max);+ BUILT_IN(inspect);+ BUILT_IN(random);+ BUILT_IN(unique_id);+ BUILT_IN(unit);+ BUILT_IN(unitless);+ BUILT_IN(comparable);++ }++}++#endif
+ libsass/src/fn_selectors.cpp view
@@ -0,0 +1,205 @@+#include <numeric>++#include "parser.hpp"+#include "extender.hpp"+#include "listize.hpp"+#include "fn_utils.hpp"+#include "fn_selectors.hpp"++namespace Sass {++ namespace Functions {++ Signature selector_nest_sig = "selector-nest($selectors...)";+ BUILT_IN(selector_nest)+ {+ List* arglist = ARG("$selectors", List);++ // Not enough parameters+ if (arglist->length() == 0) {+ error(+ "$selectors: At least one selector must be passed for `selector-nest'",+ pstate, traces);+ }++ // Parse args into vector of selectors+ SelectorStack parsedSelectors;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i));+ if (exp->concrete_type() == Expression::NULL_VAL) {+ error(+ "$selectors: null is not a valid selector: it must be a string,\n"+ "a list of strings, or a list of lists of strings for 'selector-nest'",+ pstate, traces);+ }+ if (String_Constant_Obj str = Cast<String_Constant>(exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string(ctx.c_options);+ SelectorListObj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);+ parsedSelectors.push_back(sel);+ }++ // Nothing to do+ if( parsedSelectors.empty() ) {+ return SASS_MEMORY_NEW(Null, pstate);+ }++ // Set the first element as the `result`, keep+ // appending to as we go down the parsedSelector vector.+ SelectorStack::iterator itr = parsedSelectors.begin();+ SelectorListObj& result = *itr;+ ++itr;++ for(;itr != parsedSelectors.end(); ++itr) {+ SelectorListObj& child = *itr;+ original_stack.push_back(result);+ SelectorListObj rv = child->resolve_parent_refs(original_stack, traces);+ result->elements(rv->elements());+ original_stack.pop_back();+ }++ return Cast<Value>(Listize::perform(result));+ }++ Signature selector_append_sig = "selector-append($selectors...)";+ BUILT_IN(selector_append)+ {+ List* arglist = ARG("$selectors", List);++ // Not enough parameters+ if (arglist->empty()) {+ error(+ "$selectors: At least one selector must be "+ "passed for `selector-append'",+ pstate, traces);+ }++ // Parse args into vector of selectors+ SelectorStack parsedSelectors;+ parsedSelectors.push_back({});+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression* exp = Cast<Expression>(arglist->value_at_index(i));+ if (exp->concrete_type() == Expression::NULL_VAL) {+ error(+ "$selectors: null is not a valid selector: it must be a string,\n"+ "a list of strings, or a list of lists of strings for 'selector-append'",+ pstate, traces);+ }+ if (String_Constant* str = Cast<String_Constant>(exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string();+ SelectorListObj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces,+ exp->pstate(), pstate.src,+ /*allow_parent=*/true);++ for (auto& complex : sel->elements()) {+ if (complex->empty()) {+ complex->append(SASS_MEMORY_NEW(CompoundSelector, "[phony]"));+ }+ if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {+ comp->hasRealParent(true);+ complex->chroots(true);+ }+ }++ if (parsedSelectors.size() > 1) {++ if (!sel->has_real_parent_ref()) {+ auto parent = parsedSelectors.back();+ for (auto& complex : parent->elements()) {+ if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {+ comp->hasRealParent(false);+ }+ }+ error("Can't append \"" + sel->to_string() + "\" to \"" ++ parent->to_string() + "\" for `selector-append'",+ pstate, traces);+ }++ // Build the resolved stack from the left. It's cheaper to directly+ // calculate and update each resolved selcted from the left, than to+ // recursively calculate them from the right side, as we would need+ // to go through the whole stack depth to build the final results.+ // E.g. 'a', 'b', 'x, y' => 'a' => 'a b' => 'a b x, a b y'+ // vs 'a', 'b', 'x, y' => 'x' => 'b x' => 'a b x', 'y' ...+ parsedSelectors.push_back(sel->resolve_parent_refs(parsedSelectors, traces, true));+ }+ else {+ parsedSelectors.push_back(sel);+ }+ }++ // Nothing to do+ if( parsedSelectors.empty() ) {+ return SASS_MEMORY_NEW(Null, pstate);+ }++ return Cast<Value>(Listize::perform(parsedSelectors.back()));+ }++ Signature selector_unify_sig = "selector-unify($selector1, $selector2)";+ BUILT_IN(selector_unify)+ {+ SelectorListObj selector1 = ARGSELS("$selector1");+ SelectorListObj selector2 = ARGSELS("$selector2");+ SelectorListObj result = selector1->unifyWith(selector2);+ return Cast<Value>(Listize::perform(result));+ }++ Signature simple_selectors_sig = "simple-selectors($selector)";+ BUILT_IN(simple_selectors)+ {+ CompoundSelectorObj sel = ARGSEL("$selector");++ List* l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);++ for (size_t i = 0, L = sel->length(); i < L; ++i) {+ const SimpleSelectorObj& ss = sel->get(i);+ std::string ss_string = ss->to_string() ;+ l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));+ }++ return l;+ }++ Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";+ BUILT_IN(selector_extend)+ {+ SelectorListObj selector = ARGSELS("$selector");+ SelectorListObj target = ARGSELS("$extendee");+ SelectorListObj source = ARGSELS("$extender");+ SelectorListObj result = Extender::extend(selector, source, target, traces);+ return Cast<Value>(Listize::perform(result));+ }++ Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";+ BUILT_IN(selector_replace)+ {+ SelectorListObj selector = ARGSELS("$selector");+ SelectorListObj target = ARGSELS("$original");+ SelectorListObj source = ARGSELS("$replacement");+ SelectorListObj result = Extender::replace(selector, source, target, traces);+ return Cast<Value>(Listize::perform(result));+ }++ Signature selector_parse_sig = "selector-parse($selector)";+ BUILT_IN(selector_parse)+ {+ SelectorListObj selector = ARGSELS("$selector");+ return Cast<Value>(Listize::perform(selector));+ }++ Signature is_superselector_sig = "is-superselector($super, $sub)";+ BUILT_IN(is_superselector)+ {+ SelectorListObj sel_sup = ARGSELS("$super");+ SelectorListObj sel_sub = ARGSELS("$sub");+ bool result = sel_sup->isSuperselectorOf(sel_sub);+ return SASS_MEMORY_NEW(Boolean, pstate, result);+ }++ }++}
+ libsass/src/fn_selectors.hpp view
@@ -0,0 +1,35 @@+#ifndef SASS_FN_SELECTORS_H+#define SASS_FN_SELECTORS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ #define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, traces, ctx)+ #define ARGSELS(argname) get_arg_sels(argname, env, sig, pstate, traces, ctx)++ BUILT_IN(selector_nest);+ BUILT_IN(selector_append);+ BUILT_IN(selector_extend);+ BUILT_IN(selector_replace);+ BUILT_IN(selector_unify);+ BUILT_IN(is_superselector);+ BUILT_IN(simple_selectors);+ BUILT_IN(selector_parse);++ extern Signature selector_nest_sig;+ extern Signature selector_append_sig;+ extern Signature selector_extend_sig;+ extern Signature selector_replace_sig;+ extern Signature selector_unify_sig;+ extern Signature is_superselector_sig;+ extern Signature simple_selectors_sig;+ extern Signature selector_parse_sig;++ }++}++#endif
+ libsass/src/fn_strings.cpp view
@@ -0,0 +1,256 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "utf8.h"+#include "ast.hpp"+#include "fn_utils.hpp"+#include "fn_strings.hpp"+#include "util_string.hpp"++namespace Sass {++ namespace Functions {++ void handle_utf8_error (const ParserState& pstate, Backtraces traces)+ {+ try {+ throw;+ }+ catch (utf8::invalid_code_point&) {+ std::string msg("utf8::invalid_code_point");+ error(msg, pstate, traces);+ }+ catch (utf8::not_enough_room&) {+ std::string msg("utf8::not_enough_room");+ error(msg, pstate, traces);+ }+ catch (utf8::invalid_utf8&) {+ std::string msg("utf8::invalid_utf8");+ error(msg, pstate, traces);+ }+ catch (...) { throw; }+ }++ ///////////////////+ // STRING FUNCTIONS+ ///////////////////++ Signature unquote_sig = "unquote($string)";+ BUILT_IN(sass_unquote)+ {+ AST_Node_Obj arg = env["$string"];+ if (String_Quoted* string_quoted = Cast<String_Quoted>(arg)) {+ String_Constant* result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value());+ // remember if the string was quoted (color tokens)+ result->is_delayed(true); // delay colors+ return result;+ }+ else if (String_Constant* str = Cast<String_Constant>(arg)) {+ return str;+ }+ else if (Value* ex = Cast<Value>(arg)) {+ Sass_Output_Style oldstyle = ctx.c_options.output_style;+ ctx.c_options.output_style = SASS_STYLE_NESTED;+ std::string val(arg->to_string(ctx.c_options));+ val = Cast<Null>(arg) ? "null" : val;+ ctx.c_options.output_style = oldstyle;++ deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);+ return ex;+ }+ throw std::runtime_error("Invalid Data Type for unquote");+ }++ Signature quote_sig = "quote($string)";+ BUILT_IN(sass_quote)+ {+ const String_Constant* s = ARG("$string", String_Constant);+ String_Quoted *result = SASS_MEMORY_NEW(+ String_Quoted, pstate, s->value(),+ /*q=*/'\0', /*keep_utf8_escapes=*/false, /*skip_unquoting=*/true);+ result->quote_mark('*');+ return result;+ }++ Signature str_length_sig = "str-length($string)";+ BUILT_IN(str_length)+ {+ size_t len = std::string::npos;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ len = UTF_8::code_point_count(s->value(), 0, s->value().size());++ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, traces); }+ // return something even if we had an error (-1)+ return SASS_MEMORY_NEW(Number, pstate, (double)len);+ }++ Signature str_insert_sig = "str-insert($string, $insert, $index)";+ BUILT_IN(str_insert)+ {+ std::string str;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ str = s->value();+ String_Constant* i = ARG("$insert", String_Constant);+ std::string ins = i->value();+ double index = ARGVAL("$index");+ if (index != (int)index) {+ error("$index: " + std::to_string(index) + " is not an int", pstate, traces);+ }+ size_t len = UTF_8::code_point_count(str, 0, str.size());++ if (index > 0 && index <= len) {+ // positive and within string length+ str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins);+ }+ else if (index > len) {+ // positive and past string length+ str += ins;+ }+ else if (index == 0) {+ str = ins + str;+ }+ else if (std::abs(index) <= len) {+ // negative and within string length+ index += len + 1;+ str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins);+ }+ else {+ // negative and past string length+ str = ins + str;+ }++ if (String_Quoted* ss = Cast<String_Quoted>(s)) {+ if (ss->quote_mark()) str = quote(str);+ }+ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, traces); }+ return SASS_MEMORY_NEW(String_Quoted, pstate, str);+ }++ Signature str_index_sig = "str-index($string, $substring)";+ BUILT_IN(str_index)+ {+ size_t index = std::string::npos;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ String_Constant* t = ARG("$substring", String_Constant);+ std::string str = s->value();+ std::string substr = t->value();++ size_t c_index = str.find(substr);+ if(c_index == std::string::npos) {+ return SASS_MEMORY_NEW(Null, pstate);+ }+ index = UTF_8::code_point_count(str, 0, c_index) + 1;+ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, traces); }+ // return something even if we had an error (-1)+ return SASS_MEMORY_NEW(Number, pstate, (double)index);+ }++ Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";+ BUILT_IN(str_slice)+ {+ std::string newstr;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ double start_at = ARGVAL("$start-at");+ double end_at = ARGVAL("$end-at");++ if (start_at != (int)start_at) {+ error("$start-at: " + std::to_string(start_at) + " is not an int", pstate, traces);+ }++ String_Quoted* ss = Cast<String_Quoted>(s);++ std::string str(s->value());++ size_t size = utf8::distance(str.begin(), str.end());++ if (!Cast<Number>(env["$end-at"])) {+ end_at = -1;+ }++ if (end_at != (int)end_at) {+ error("$end-at: " + std::to_string(end_at) + " is not an int", pstate, traces);+ }++ if (end_at == 0 || (end_at + size) < 0) {+ if (ss && ss->quote_mark()) newstr = quote("");+ return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);+ }++ if (end_at < 0) {+ end_at += size + 1;+ if (end_at == 0) end_at = 1;+ }+ if (end_at > size) { end_at = (double)size; }+ if (start_at < 0) {+ start_at += size + 1;+ if (start_at <= 0) start_at = 1;+ }+ else if (start_at == 0) { ++ start_at; }++ if (start_at <= end_at)+ {+ std::string::iterator start = str.begin();+ utf8::advance(start, start_at - 1, str.end());+ std::string::iterator end = start;+ utf8::advance(end, end_at - start_at + 1, str.end());+ newstr = std::string(start, end);+ }+ if (ss) {+ if(ss->quote_mark()) newstr = quote(newstr);+ }+ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, traces); }+ return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);+ }++ Signature to_upper_case_sig = "to-upper-case($string)";+ BUILT_IN(to_upper_case)+ {+ String_Constant* s = ARG("$string", String_Constant);+ std::string str = s->value();+ Util::ascii_str_toupper(&str);++ if (String_Quoted* ss = Cast<String_Quoted>(s)) {+ String_Quoted* cpy = SASS_MEMORY_COPY(ss);+ cpy->value(str);+ return cpy;+ } else {+ return SASS_MEMORY_NEW(String_Quoted, pstate, str);+ }+ }++ Signature to_lower_case_sig = "to-lower-case($string)";+ BUILT_IN(to_lower_case)+ {+ String_Constant* s = ARG("$string", String_Constant);+ std::string str = s->value();+ Util::ascii_str_tolower(&str);++ if (String_Quoted* ss = Cast<String_Quoted>(s)) {+ String_Quoted* cpy = SASS_MEMORY_COPY(ss);+ cpy->value(str);+ return cpy;+ } else {+ return SASS_MEMORY_NEW(String_Quoted, pstate, str);+ }+ }++ }++}
+ libsass/src/fn_strings.hpp view
@@ -0,0 +1,34 @@+#ifndef SASS_FN_STRINGS_H+#define SASS_FN_STRINGS_H++#include "fn_utils.hpp"++namespace Sass {++ namespace Functions {++ extern Signature unquote_sig;+ extern Signature quote_sig;+ extern Signature str_length_sig;+ extern Signature str_insert_sig;+ extern Signature str_index_sig;+ extern Signature str_slice_sig;+ extern Signature to_upper_case_sig;+ extern Signature to_lower_case_sig;+ extern Signature length_sig;++ BUILT_IN(sass_unquote);+ BUILT_IN(sass_quote);+ BUILT_IN(str_length);+ BUILT_IN(str_insert);+ BUILT_IN(str_index);+ BUILT_IN(str_slice);+ BUILT_IN(to_upper_case);+ BUILT_IN(to_lower_case);+ BUILT_IN(length);++ }++}++#endif
+ libsass/src/fn_utils.cpp view
@@ -0,0 +1,155 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "parser.hpp"+#include "fn_utils.hpp"+#include "util_string.hpp"++namespace Sass {++ Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)+ {+ Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]"));+ sig_parser.lex<Prelexer::identifier>();+ std::string name(Util::normalize_underscores(sig_parser.lexed));+ Parameters_Obj params = sig_parser.parse_parameters();+ return SASS_MEMORY_NEW(Definition,+ ParserState("[built-in function]"),+ sig,+ name,+ params,+ func,+ false);+ }++ Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)+ {+ using namespace Prelexer;++ const char* sig = sass_function_get_signature(c_func);+ Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]"));+ // allow to overload generic callback plus @warn, @error and @debug with custom functions+ sig_parser.lex < alternatives < identifier, exactly <'*'>,+ exactly < Constants::warn_kwd >,+ exactly < Constants::error_kwd >,+ exactly < Constants::debug_kwd >+ > >();+ std::string name(Util::normalize_underscores(sig_parser.lexed));+ Parameters_Obj params = sig_parser.parse_parameters();+ return SASS_MEMORY_NEW(Definition,+ ParserState("[c function]"),+ sig,+ name,+ params,+ c_func);+ }++ namespace Functions {++ std::string function_name(Signature sig)+ {+ std::string str(sig);+ return str.substr(0, str.find('('));+ }++ Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ AST_Node* value = env[argname];+ if (Map* map = Cast<Map>(value)) return map;+ List* list = Cast<List>(value);+ if (list && list->length() == 0) {+ return SASS_MEMORY_NEW(Map, pstate, 0);+ }+ return get_arg<Map>(argname, env, sig, pstate, traces);+ }++ double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi)+ {+ Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ double v = tmpnr.value();+ if (!(lo <= v && v <= hi)) {+ std::stringstream msg;+ msg << "argument `" << argname << "` of `" << sig << "` must be between ";+ msg << lo << " and " << hi;+ error(msg.str(), pstate, traces);+ }+ return v;+ }++ Number* get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+ val = SASS_MEMORY_COPY(val);+ val->reduce();+ return val;+ }++ double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ return tmpnr.value();+ }++ double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ if (tmpnr.unit() == "%") {+ return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0);+ } else {+ return std::min(std::max(tmpnr.value(), 0.0), 255.0);+ }+ }++ double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) {+ Number* val = get_arg<Number>(argname, env, sig, pstate, traces);+ Number tmpnr(val);+ tmpnr.reduce();+ if (tmpnr.unit() == "%") {+ return std::min(std::max(tmpnr.value(), 0.0), 100.0);+ } else {+ return std::min(std::max(tmpnr.value(), 0.0), 1.0);+ }+ }++ SelectorListObj get_arg_sels(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {+ Expression_Obj exp = ARG(argname, Expression);+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << argname << ": null is not a valid selector: it must be a string,\n";+ msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";+ error(msg.str(), exp->pstate(), traces);+ }+ if (String_Constant* str = Cast<String_Constant>(exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string(ctx.c_options);+ return Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src, /*allow_parent=*/false);+ }++ CompoundSelectorObj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {+ Expression_Obj exp = ARG(argname, Expression);+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << argname << ": null is not a string for `" << function_name(sig) << "'";+ error(msg.str(), exp->pstate(), traces);+ }+ if (String_Constant* str = Cast<String_Constant>(exp)) {+ str->quote_mark(0);+ }+ std::string exp_src = exp->to_string(ctx.c_options);+ SelectorListObj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src, /*allow_parent=*/false);+ if (sel_list->length() == 0) return {};+ return sel_list->first()->first();+ }+++ }++}
+ libsass/src/fn_utils.hpp view
@@ -0,0 +1,62 @@+#ifndef SASS_FN_UTILS_H+#define SASS_FN_UTILS_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "units.hpp"+#include "backtrace.hpp"+#include "environment.hpp"+#include "ast_fwd_decl.hpp"+#include "error_handling.hpp"++namespace Sass {++ #define FN_PROTOTYPE \+ Env& env, \+ Env& d_env, \+ Context& ctx, \+ Signature sig, \+ ParserState pstate, \+ Backtraces& traces, \+ SelectorStack selector_stack, \+ SelectorStack original_stack \++ typedef const char* Signature;+ typedef PreValue* (*Native_Function)(FN_PROTOTYPE);+ #define BUILT_IN(name) PreValue* name(FN_PROTOTYPE)++ #define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)+ // special function for weird hsla percent (10px == 10% == 10 != 0.1)+ #define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double++ Definition* make_native_function(Signature, Native_Function, Context& ctx);+ Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);++ namespace Functions {++ template <typename T>+ T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)+ {+ T* val = Cast<T>(env[argname]);+ if (!val) {+ error("argument `" + argname + "` of `" + sig + "` must be a " + T::type_name(), pstate, traces);+ }+ return val;+ }++ Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // maps only+ Number* get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // numbers only+ double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // colors only+ double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // colors only+ double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi); // colors only+ double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces); // shared+ SelectorListObj get_arg_sels(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); // selectors only+ CompoundSelectorObj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx); // selectors only++ }++}++#endif
− libsass/src/functions.cpp
@@ -1,2234 +0,0 @@-#include "sass.hpp"-#include "functions.hpp"-#include "ast.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "parser.hpp"-#include "constants.hpp"-#include "inspect.hpp"-#include "extend.hpp"-#include "eval.hpp"-#include "util.hpp"-#include "expand.hpp"-#include "operators.hpp"-#include "utf8_string.hpp"-#include "sass/base.h"-#include "utf8.h"--#include <cstdint>-#include <cstdlib>-#include <cmath>-#include <cctype>-#include <sstream>-#include <string>-#include <iomanip>-#include <iostream>-#include <random>-#include <set>--#ifdef __MINGW32__-#include "windows.h"-#include "wincrypt.h"-#endif--#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)-#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, traces, ctx)--// return a number object (copied since we want to have reduced units)-#define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy--// special function for weird hsla percent (10px == 10% == 10 != 0.1)-#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double--// macros for common ranges (u mean unsigned or upper, r for full range)-#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double-#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double-#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double-#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double-#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double-#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double--// macros for color related inputs (rbg and alpha/opacity values)-#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double-#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double--namespace Sass {- using std::stringstream;- using std::endl;-- Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx)- {- Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]"));- sig_parser.lex<Prelexer::identifier>();- std::string name(Util::normalize_underscores(sig_parser.lexed));- Parameters_Obj params = sig_parser.parse_parameters();- return SASS_MEMORY_NEW(Definition,- ParserState("[built-in function]"),- sig,- name,- params,- func,- false);- }-- Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx)- {- using namespace Prelexer;-- const char* sig = sass_function_get_signature(c_func);- Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]"));- // allow to overload generic callback plus @warn, @error and @debug with custom functions- sig_parser.lex < alternatives < identifier, exactly <'*'>,- exactly < Constants::warn_kwd >,- exactly < Constants::error_kwd >,- exactly < Constants::debug_kwd >- > >();- std::string name(Util::normalize_underscores(sig_parser.lexed));- Parameters_Obj params = sig_parser.parse_parameters();- return SASS_MEMORY_NEW(Definition,- ParserState("[c function]"),- sig,- name,- params,- c_func,- false, true);- }-- std::string function_name(Signature sig)- {- std::string str(sig);- return str.substr(0, str.find('('));- }-- namespace Functions {-- inline void handle_utf8_error (const ParserState& pstate, Backtraces traces)- {- try {- throw;- }- catch (utf8::invalid_code_point) {- std::string msg("utf8::invalid_code_point");- error(msg, pstate, traces);- }- catch (utf8::not_enough_room) {- std::string msg("utf8::not_enough_room");- error(msg, pstate, traces);- }- catch (utf8::invalid_utf8) {- std::string msg("utf8::invalid_utf8");- error(msg, pstate, traces);- }- catch (...) { throw; }- }-- template <typename T>- T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)- {- // Minimal error handling -- the expectation is that built-ins will be written correctly!- T* val = Cast<T>(env[argname]);- if (!val) {- std::string msg("argument `");- msg += argname;- msg += "` of `";- msg += sig;- msg += "` must be a ";- msg += T::type_name();- error(msg, pstate, traces);- }- return val;- }-- Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx)- {- // Minimal error handling -- the expectation is that built-ins will be written correctly!- Map_Ptr val = Cast<Map>(env[argname]);- if (val) return val;-- List_Ptr lval = Cast<List>(env[argname]);- if (lval && lval->length() == 0) return SASS_MEMORY_NEW(Map, pstate, 0);-- // fallback on get_arg for error handling- val = get_arg<Map>(argname, env, sig, pstate, traces);- return val;- }-- double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi)- {- // Minimal error handling -- the expectation is that built-ins will be written correctly!- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);- Number tmpnr(val);- tmpnr.reduce();- double v = tmpnr.value();- if (!(lo <= v && v <= hi)) {- std::stringstream msg;- msg << "argument `" << argname << "` of `" << sig << "` must be between ";- msg << lo << " and " << hi;- error(msg.str(), pstate, traces);- }- return v;- }-- Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)- {- // Minimal error handling -- the expectation is that built-ins will be written correctly!- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);- val = SASS_MEMORY_COPY(val);- val->reduce();- return val;- }-- double get_arg_v(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)- {- // Minimal error handling -- the expectation is that built-ins will be written correctly!- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);- Number tmpnr(val);- tmpnr.reduce();- /*- if (tmpnr.unit() == "%") {- tmpnr.value(tmpnr.value() / 100);- tmpnr.numerators.clear();- } else {- if (!tmpnr.is_unitless()) error("argument " + argname + " of `" + std::string(sig) + "` must be unitless", pstate);- }- */- return tmpnr.value();- }-- double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)- {- // Minimal error handling -- the expectation is that built-ins will be written correctly!- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);- Number tmpnr(val);- tmpnr.reduce();- return tmpnr.value();- }-- double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)- {- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);- Number tmpnr(val);- tmpnr.reduce();- if (tmpnr.unit() == "%") {- return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0);- } else {- return std::min(std::max(tmpnr.value(), 0.0), 255.0);- }- }--- inline double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) {- Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);- Number tmpnr(val);- tmpnr.reduce();- if (tmpnr.unit() == "%") {- return std::min(std::max(tmpnr.value(), 0.0), 100.0);- } else {- return std::min(std::max(tmpnr.value(), 0.0), 1.0);- }- }-- #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, traces, ctx)-- template <typename T>- T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx);-- template <>- Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {- Expression_Obj exp = ARG(argname, Expression);- if (exp->concrete_type() == Expression::NULL_VAL) {- std::stringstream msg;- msg << argname << ": null is not a valid selector: it must be a string,\n";- msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";- error(msg.str(), pstate, traces);- }- if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {- str->quote_mark(0);- }- std::string exp_src = exp->to_string(ctx.c_options);- return Parser::parse_selector(exp_src.c_str(), ctx, traces);- }-- template <>- Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {- Expression_Obj exp = ARG(argname, Expression);- if (exp->concrete_type() == Expression::NULL_VAL) {- std::stringstream msg;- msg << argname << ": null is not a string for `" << function_name(sig) << "'";- error(msg.str(), pstate, traces);- }- if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {- str->quote_mark(0);- }- std::string exp_src = exp->to_string(ctx.c_options);- Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces);- if (sel_list->length() == 0) return NULL;- Complex_Selector_Obj first = sel_list->first();- if (!first->tail()) return first->head();- return first->tail()->head();- }-- #ifdef __MINGW32__- uint64_t GetSeed()- {- HCRYPTPROV hp = 0;- BYTE rb[8];- CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);- CryptGenRandom(hp, sizeof(rb), rb);- CryptReleaseContext(hp, 0);-- uint64_t seed;- memcpy(&seed, &rb[0], sizeof(seed));-- return seed;- }- #else- uint64_t GetSeed()- {- std::random_device rd;- return rd();- }- #endif-- // note: the performance of many implementations of- // random_device degrades sharply once the entropy pool- // is exhausted. For practical use, random_device is- // generally only used to seed a PRNG such as mt19937.- static std::mt19937 rand(static_cast<unsigned int>(GetSeed()));-- // features- static std::set<std::string> features {- "global-variable-shadowing",- "extend-selector-pseudoclass",- "at-error",- "units-level-3",- "custom-property"- };-- ////////////////- // RGB FUNCTIONS- ////////////////-- inline bool special_number(String_Constant_Ptr s) {- if (s) {- std::string calc("calc(");- std::string var("var(");- std::string ss(s->value());- return std::equal(calc.begin(), calc.end(), ss.begin()) ||- std::equal(var.begin(), var.end(), ss.begin());- }- return false;- }-- Signature rgb_sig = "rgb($red, $green, $blue)";- BUILT_IN(rgb)- {- if (- special_number(Cast<String_Constant>(env["$red"])) ||- special_number(Cast<String_Constant>(env["$green"])) ||- special_number(Cast<String_Constant>(env["$blue"]))- ) {- return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("- + env["$red"]->to_string()- + ", "- + env["$green"]->to_string()- + ", "- + env["$blue"]->to_string()- + ")"- );- }-- return SASS_MEMORY_NEW(Color,- pstate,- COLOR_NUM("$red"),- COLOR_NUM("$green"),- COLOR_NUM("$blue"));- }-- Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";- BUILT_IN(rgba_4)- {- if (- special_number(Cast<String_Constant>(env["$red"])) ||- special_number(Cast<String_Constant>(env["$green"])) ||- special_number(Cast<String_Constant>(env["$blue"])) ||- special_number(Cast<String_Constant>(env["$alpha"]))- ) {- return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("- + env["$red"]->to_string()- + ", "- + env["$green"]->to_string()- + ", "- + env["$blue"]->to_string()- + ", "- + env["$alpha"]->to_string()- + ")"- );- }-- return SASS_MEMORY_NEW(Color,- pstate,- COLOR_NUM("$red"),- COLOR_NUM("$green"),- COLOR_NUM("$blue"),- ALPHA_NUM("$alpha"));- }-- Signature rgba_2_sig = "rgba($color, $alpha)";- BUILT_IN(rgba_2)- {- if (- special_number(Cast<String_Constant>(env["$color"]))- ) {- return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("- + env["$color"]->to_string()- + ", "- + env["$alpha"]->to_string()- + ")"- );- }-- Color_Ptr c_arg = ARG("$color", Color);-- if (- special_number(Cast<String_Constant>(env["$alpha"]))- ) {- std::stringstream strm;- strm << "rgba("- << (int)c_arg->r() << ", "- << (int)c_arg->g() << ", "- << (int)c_arg->b() << ", "- << env["$alpha"]->to_string()- << ")";- return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());- }-- Color_Ptr new_c = SASS_MEMORY_COPY(c_arg);- new_c->a(ALPHA_NUM("$alpha"));- new_c->disp("");- return new_c;- }-- Signature red_sig = "red($color)";- BUILT_IN(red)- { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->r()); }-- Signature green_sig = "green($color)";- BUILT_IN(green)- { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->g()); }-- Signature blue_sig = "blue($color)";- BUILT_IN(blue)- { return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->b()); }-- Color* colormix(Context& ctx, ParserState& pstate, Color* color1, Color* color2, double weight) {- double p = weight/100;- double w = 2*p - 1;- double a = color1->a() - color2->a();-- double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;- double w2 = 1 - w1;-- return SASS_MEMORY_NEW(Color,- pstate,- Sass::round(w1*color1->r() + w2*color2->r(), ctx.c_options.precision),- Sass::round(w1*color1->g() + w2*color2->g(), ctx.c_options.precision),- Sass::round(w1*color1->b() + w2*color2->b(), ctx.c_options.precision),- color1->a()*p + color2->a()*(1-p));- }-- Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";- BUILT_IN(mix)- {- Color_Obj color1 = ARG("$color-1", Color);- Color_Obj color2 = ARG("$color-2", Color);- double weight = DARG_U_PRCT("$weight");- return colormix(ctx, pstate, color1, color2, weight);-- }-- ////////////////- // HSL FUNCTIONS- ////////////////-- // RGB to HSL helper function- struct HSL { double h; double s; double l; };- HSL rgb_to_hsl(double r, double g, double b)- {-- // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV- r /= 255.0; g /= 255.0; b /= 255.0;-- double max = std::max(r, std::max(g, b));- double min = std::min(r, std::min(g, b));- double delta = max - min;-- double h = 0;- double s;- double l = (max + min) / 2.0;-- if (NEAR_EQUAL(max, min)) {- h = s = 0; // achromatic- }- else {- if (l < 0.5) s = delta / (max + min);- else s = delta / (2.0 - max - min);-- if (r == max) h = (g - b) / delta + (g < b ? 6 : 0);- else if (g == max) h = (b - r) / delta + 2;- else if (b == max) h = (r - g) / delta + 4;- }-- HSL hsl_struct;- hsl_struct.h = h / 6 * 360;- hsl_struct.s = s * 100;- hsl_struct.l = l * 100;-- return hsl_struct;- }-- // hue to RGB helper function- double h_to_rgb(double m1, double m2, double h) {- while (h < 0) h += 1;- while (h > 1) h -= 1;- if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;- if (h*2.0 < 1) return m2;- if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;- return m1;- }-- Color_Ptr hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate)- {- h /= 360.0;- s /= 100.0;- l /= 100.0;-- if (l < 0) l = 0;- if (s < 0) s = 0;- if (l > 1) l = 1;- if (s > 1) s = 1;- while (h < 0) h += 1;- while (h > 1) h -= 1;-- // if saturation is exacly zero, we loose- // information for hue, since it will evaluate- // to zero if converted back from rgb. Setting- // saturation to a very tiny number solves this.- if (s == 0) s = 1e-10;-- // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.- double m2;- if (l <= 0.5) m2 = l*(s+1.0);- else m2 = (l+s)-(l*s);- double m1 = (l*2.0)-m2;- // round the results -- consider moving this into the Color constructor- double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0);- double g = (h_to_rgb(m1, m2, h) * 255.0);- double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0);-- return SASS_MEMORY_NEW(Color, pstate, r, g, b, a);- }-- Signature hsl_sig = "hsl($hue, $saturation, $lightness)";- BUILT_IN(hsl)- {- if (- special_number(Cast<String_Constant>(env["$hue"])) ||- special_number(Cast<String_Constant>(env["$saturation"])) ||- special_number(Cast<String_Constant>(env["$lightness"]))- ) {- return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("- + env["$hue"]->to_string()- + ", "- + env["$saturation"]->to_string()- + ", "- + env["$lightness"]->to_string()- + ")"- );- }-- return hsla_impl(ARGVAL("$hue"),- ARGVAL("$saturation"),- ARGVAL("$lightness"),- 1.0,- ctx,- pstate);- }-- Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";- BUILT_IN(hsla)- {- if (- special_number(Cast<String_Constant>(env["$hue"])) ||- special_number(Cast<String_Constant>(env["$saturation"])) ||- special_number(Cast<String_Constant>(env["$lightness"])) ||- special_number(Cast<String_Constant>(env["$alpha"]))- ) {- return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("- + env["$hue"]->to_string()- + ", "- + env["$saturation"]->to_string()- + ", "- + env["$lightness"]->to_string()- + ", "- + env["$alpha"]->to_string()- + ")"- );- }-- return hsla_impl(ARGVAL("$hue"),- ARGVAL("$saturation"),- ARGVAL("$lightness"),- ARGVAL("$alpha"),- ctx,- pstate);- }-- Signature hue_sig = "hue($color)";- BUILT_IN(hue)- {- Color_Ptr rgb_color = ARG("$color", Color);- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- return SASS_MEMORY_NEW(Number, pstate, hsl_color.h, "deg");- }-- Signature saturation_sig = "saturation($color)";- BUILT_IN(saturation)- {- Color_Ptr rgb_color = ARG("$color", Color);- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- return SASS_MEMORY_NEW(Number, pstate, hsl_color.s, "%");- }-- Signature lightness_sig = "lightness($color)";- BUILT_IN(lightness)- {- Color_Ptr rgb_color = ARG("$color", Color);- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- return SASS_MEMORY_NEW(Number, pstate, hsl_color.l, "%");- }-- Signature adjust_hue_sig = "adjust-hue($color, $degrees)";- BUILT_IN(adjust_hue)- {- Color_Ptr rgb_color = ARG("$color", Color);- double degrees = ARGVAL("$degrees");- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- return hsla_impl(hsl_color.h + degrees,- hsl_color.s,- hsl_color.l,- rgb_color->a(),- ctx,- pstate);- }-- Signature lighten_sig = "lighten($color, $amount)";- BUILT_IN(lighten)- {- Color_Ptr rgb_color = ARG("$color", Color);- double amount = DARG_U_PRCT("$amount");- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- //Check lightness is not negative before lighten it- double hslcolorL = hsl_color.l;- if (hslcolorL < 0) {- hslcolorL = 0;- }-- return hsla_impl(hsl_color.h,- hsl_color.s,- hslcolorL + amount,- rgb_color->a(),- ctx,- pstate);- }-- Signature darken_sig = "darken($color, $amount)";- BUILT_IN(darken)- {- Color_Ptr rgb_color = ARG("$color", Color);- double amount = DARG_U_PRCT("$amount");- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());-- //Check lightness if not over 100, before darken it- double hslcolorL = hsl_color.l;- if (hslcolorL > 100) {- hslcolorL = 100;- }-- return hsla_impl(hsl_color.h,- hsl_color.s,- hslcolorL - amount,- rgb_color->a(),- ctx,- pstate);- }-- Signature saturate_sig = "saturate($color, $amount: false)";- BUILT_IN(saturate)- {- // CSS3 filter function overload: pass literal through directly- if (!Cast<Number>(env["$amount"])) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");- }-- double amount = DARG_U_PRCT("$amount");- Color_Ptr rgb_color = ARG("$color", Color);- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());-- double hslcolorS = hsl_color.s + amount;-- // Saturation cannot be below 0 or above 100- if (hslcolorS < 0) {- hslcolorS = 0;- }- if (hslcolorS > 100) {- hslcolorS = 100;- }-- return hsla_impl(hsl_color.h,- hslcolorS,- hsl_color.l,- rgb_color->a(),- ctx,- pstate);- }-- Signature desaturate_sig = "desaturate($color, $amount)";- BUILT_IN(desaturate)- {- Color_Ptr rgb_color = ARG("$color", Color);- double amount = DARG_U_PRCT("$amount");- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());-- double hslcolorS = hsl_color.s - amount;-- // Saturation cannot be below 0 or above 100- if (hslcolorS <= 0) {- hslcolorS = 0;- }- if (hslcolorS > 100) {- hslcolorS = 100;- }-- return hsla_impl(hsl_color.h,- hslcolorS,- hsl_color.l,- rgb_color->a(),- ctx,- pstate);- }-- Signature grayscale_sig = "grayscale($color)";- BUILT_IN(grayscale)- {- // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = Cast<Number>(env["$color"]);- if (amount) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");- }-- Color_Ptr rgb_color = ARG("$color", Color);- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- return hsla_impl(hsl_color.h,- 0.0,- hsl_color.l,- rgb_color->a(),- ctx,- pstate);- }-- Signature complement_sig = "complement($color)";- BUILT_IN(complement)- {- Color_Ptr rgb_color = ARG("$color", Color);- HSL hsl_color = rgb_to_hsl(rgb_color->r(),- rgb_color->g(),- rgb_color->b());- return hsla_impl(hsl_color.h - 180.0,- hsl_color.s,- hsl_color.l,- rgb_color->a(),- ctx,- pstate);- }-- Signature invert_sig = "invert($color, $weight: 100%)";- BUILT_IN(invert)- {- // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = Cast<Number>(env["$color"]);- if (amount) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");- }-- double weight = DARG_U_PRCT("$weight");- Color_Ptr rgb_color = ARG("$color", Color);- Color_Obj inv = SASS_MEMORY_NEW(Color,- pstate,- 255 - rgb_color->r(),- 255 - rgb_color->g(),- 255 - rgb_color->b(),- rgb_color->a());- return colormix(ctx, pstate, inv, rgb_color, weight);- }-- ////////////////////- // OPACITY FUNCTIONS- ////////////////////- Signature alpha_sig = "alpha($color)";- Signature opacity_sig = "opacity($color)";- BUILT_IN(alpha)- {- String_Constant_Ptr ie_kwd = Cast<String_Constant>(env["$color"]);- if (ie_kwd) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");- }-- // CSS3 filter function overload: pass literal through directly- Number_Ptr amount = Cast<Number>(env["$color"]);- if (amount) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");- }-- return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a());- }-- Signature opacify_sig = "opacify($color, $amount)";- Signature fade_in_sig = "fade-in($color, $amount)";- BUILT_IN(opacify)- {- Color_Ptr color = ARG("$color", Color);- double amount = DARG_U_FACT("$amount");- double alpha = std::min(color->a() + amount, 1.0);- return SASS_MEMORY_NEW(Color,- pstate,- color->r(),- color->g(),- color->b(),- alpha);- }-- Signature transparentize_sig = "transparentize($color, $amount)";- Signature fade_out_sig = "fade-out($color, $amount)";- BUILT_IN(transparentize)- {- Color_Ptr color = ARG("$color", Color);- double amount = DARG_U_FACT("$amount");- double alpha = std::max(color->a() - amount, 0.0);- return SASS_MEMORY_NEW(Color,- pstate,- color->r(),- color->g(),- color->b(),- alpha);- }-- ////////////////////////- // OTHER COLOR FUNCTIONS- ////////////////////////-- Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";- BUILT_IN(adjust_color)- {- Color_Ptr color = ARG("$color", Color);- Number_Ptr r = Cast<Number>(env["$red"]);- Number_Ptr g = Cast<Number>(env["$green"]);- Number_Ptr b = Cast<Number>(env["$blue"]);- Number_Ptr h = Cast<Number>(env["$hue"]);- Number_Ptr s = Cast<Number>(env["$saturation"]);- Number_Ptr l = Cast<Number>(env["$lightness"]);- Number_Ptr a = Cast<Number>(env["$alpha"]);-- bool rgb = r || g || b;- bool hsl = h || s || l;-- if (rgb && hsl) {- error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);- }- if (rgb) {- double rr = r ? DARG_R_BYTE("$red") : 0;- double gg = g ? DARG_R_BYTE("$green") : 0;- double bb = b ? DARG_R_BYTE("$blue") : 0;- double aa = a ? DARG_R_FACT("$alpha") : 0;- return SASS_MEMORY_NEW(Color,- pstate,- color->r() + rr,- color->g() + gg,- color->b() + bb,- color->a() + aa);- }- if (hsl) {- HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());- double ss = s ? DARG_R_PRCT("$saturation") : 0;- double ll = l ? DARG_R_PRCT("$lightness") : 0;- double aa = a ? DARG_R_FACT("$alpha") : 0;- return hsla_impl(hsl_struct.h + (h ? h->value() : 0),- hsl_struct.s + ss,- hsl_struct.l + ll,- color->a() + aa,- ctx,- pstate);- }- if (a) {- return SASS_MEMORY_NEW(Color,- pstate,- color->r(),- color->g(),- color->b(),- color->a() + (a ? a->value() : 0));- }- error("not enough arguments for `adjust-color'", pstate, traces);- // unreachable- return color;- }-- Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";- BUILT_IN(scale_color)- {- Color_Ptr color = ARG("$color", Color);- Number_Ptr r = Cast<Number>(env["$red"]);- Number_Ptr g = Cast<Number>(env["$green"]);- Number_Ptr b = Cast<Number>(env["$blue"]);- Number_Ptr h = Cast<Number>(env["$hue"]);- Number_Ptr s = Cast<Number>(env["$saturation"]);- Number_Ptr l = Cast<Number>(env["$lightness"]);- Number_Ptr a = Cast<Number>(env["$alpha"]);-- bool rgb = r || g || b;- bool hsl = h || s || l;-- if (rgb && hsl) {- error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);- }- if (rgb) {- double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;- double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;- double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;- double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;- return SASS_MEMORY_NEW(Color,- pstate,- color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()),- color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()),- color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()),- color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));- }- if (hsl) {- double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;- double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;- double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;- double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;- HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());- hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h);- hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s);- hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l);- double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a());- return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate);- }- if (a) {- double ascale = (DARG_R_PRCT("$alpha")) / 100.0;- return SASS_MEMORY_NEW(Color,- pstate,- color->r(),- color->g(),- color->b(),- color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));- }- error("not enough arguments for `scale-color'", pstate, traces);- // unreachable- return color;- }-- Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";- BUILT_IN(change_color)- {- Color_Ptr color = ARG("$color", Color);- Number_Ptr r = Cast<Number>(env["$red"]);- Number_Ptr g = Cast<Number>(env["$green"]);- Number_Ptr b = Cast<Number>(env["$blue"]);- Number_Ptr h = Cast<Number>(env["$hue"]);- Number_Ptr s = Cast<Number>(env["$saturation"]);- Number_Ptr l = Cast<Number>(env["$lightness"]);- Number_Ptr a = Cast<Number>(env["$alpha"]);-- bool rgb = r || g || b;- bool hsl = h || s || l;-- if (rgb && hsl) {- error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);- }- if (rgb) {- return SASS_MEMORY_NEW(Color,- pstate,- r ? DARG_U_BYTE("$red") : color->r(),- g ? DARG_U_BYTE("$green") : color->g(),- b ? DARG_U_BYTE("$blue") : color->b(),- a ? DARG_U_BYTE("$alpha") : color->a());- }- if (hsl) {- HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());- if (h) hsl_struct.h = std::fmod(h->value(), 360.0);- if (s) hsl_struct.s = DARG_U_PRCT("$saturation");- if (l) hsl_struct.l = DARG_U_PRCT("$lightness");- double alpha = a ? DARG_U_FACT("$alpha") : color->a();- return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate);- }- if (a) {- double alpha = DARG_U_FACT("$alpha");- return SASS_MEMORY_NEW(Color,- pstate,- color->r(),- color->g(),- color->b(),- alpha);- }- error("not enough arguments for `change-color'", pstate, traces);- // unreachable- return color;- }-- template <size_t range>- static double cap_channel(double c) {- if (c > range) return range;- else if (c < 0) return 0;- else return c;- }-- Signature ie_hex_str_sig = "ie-hex-str($color)";- BUILT_IN(ie_hex_str)- {- Color_Ptr c = ARG("$color", Color);- double r = cap_channel<0xff>(c->r());- double g = cap_channel<0xff>(c->g());- double b = cap_channel<0xff>(c->b());- double a = cap_channel<1> (c->a()) * 255;-- std::stringstream ss;- ss << '#' << std::setw(2) << std::setfill('0');- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));- ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision));-- std::string result(ss.str());- for (size_t i = 0, L = result.length(); i < L; ++i) {- result[i] = std::toupper(result[i]);- }- return SASS_MEMORY_NEW(String_Quoted, pstate, result);- }-- ///////////////////- // STRING FUNCTIONS- ///////////////////-- Signature unquote_sig = "unquote($string)";- BUILT_IN(sass_unquote)- {- AST_Node_Obj arg = env["$string"];- if (String_Quoted_Ptr string_quoted = Cast<String_Quoted>(arg)) {- String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value());- // remember if the string was quoted (color tokens)- result->is_delayed(true); // delay colors- return result;- }- else if (String_Constant_Ptr str = Cast<String_Constant>(arg)) {- return str;- }- else if (Expression_Ptr ex = Cast<Expression>(arg)) {- Sass_Output_Style oldstyle = ctx.c_options.output_style;- ctx.c_options.output_style = SASS_STYLE_NESTED;- std::string val(arg->to_string(ctx.c_options));- val = Cast<Null>(arg) ? "null" : val;- ctx.c_options.output_style = oldstyle;-- deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);- return ex;- }- throw std::runtime_error("Invalid Data Type for unquote");- }-- Signature quote_sig = "quote($string)";- BUILT_IN(sass_quote)- {- AST_Node_Obj arg = env["$string"];- // only set quote mark to true if already a string- if (String_Quoted_Ptr qstr = Cast<String_Quoted>(arg)) {- qstr->quote_mark('*');- return qstr;- }- // all other nodes must be converted to a string node- std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote()));- String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str);- result->quote_mark('*');- return result;- }--- Signature str_length_sig = "str-length($string)";- BUILT_IN(str_length)- {- size_t len = std::string::npos;- try {- String_Constant_Ptr s = ARG("$string", String_Constant);- len = UTF_8::code_point_count(s->value(), 0, s->value().size());-- }- // handle any invalid utf8 errors- // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, traces); }- // return something even if we had an error (-1)- return SASS_MEMORY_NEW(Number, pstate, (double)len);- }-- Signature str_insert_sig = "str-insert($string, $insert, $index)";- BUILT_IN(str_insert)- {- std::string str;- try {- String_Constant_Ptr s = ARG("$string", String_Constant);- str = s->value();- str = unquote(str);- String_Constant_Ptr i = ARG("$insert", String_Constant);- std::string ins = i->value();- ins = unquote(ins);- double index = ARGVAL("$index");- size_t len = UTF_8::code_point_count(str, 0, str.size());-- if (index > 0 && index <= len) {- // positive and within string length- str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins);- }- else if (index > len) {- // positive and past string length- str += ins;- }- else if (index == 0) {- str = ins + str;- }- else if (std::abs(index) <= len) {- // negative and within string length- index += len + 1;- str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins);- }- else {- // negative and past string length- str = ins + str;- }-- if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) {- if (ss->quote_mark()) str = quote(str);- }- }- // handle any invalid utf8 errors- // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, traces); }- return SASS_MEMORY_NEW(String_Quoted, pstate, str);- }-- Signature str_index_sig = "str-index($string, $substring)";- BUILT_IN(str_index)- {- size_t index = std::string::npos;- try {- String_Constant_Ptr s = ARG("$string", String_Constant);- String_Constant_Ptr t = ARG("$substring", String_Constant);- std::string str = s->value();- str = unquote(str);- std::string substr = t->value();- substr = unquote(substr);-- size_t c_index = str.find(substr);- if(c_index == std::string::npos) {- return SASS_MEMORY_NEW(Null, pstate);- }- index = UTF_8::code_point_count(str, 0, c_index) + 1;- }- // handle any invalid utf8 errors- // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, traces); }- // return something even if we had an error (-1)- return SASS_MEMORY_NEW(Number, pstate, (double)index);- }-- Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";- BUILT_IN(str_slice)- {- std::string newstr;- try {- String_Constant_Ptr s = ARG("$string", String_Constant);- double start_at = ARGVAL("$start-at");- double end_at = ARGVAL("$end-at");- String_Quoted_Ptr ss = Cast<String_Quoted>(s);-- std::string str = unquote(s->value());-- size_t size = utf8::distance(str.begin(), str.end());-- if (!Cast<Number>(env["$end-at"])) {- end_at = -1;- }-- if (end_at == 0 || (end_at + size) < 0) {- if (ss && ss->quote_mark()) newstr = quote("");- return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);- }-- if (end_at < 0) {- end_at += size + 1;- if (end_at == 0) end_at = 1;- }- if (end_at > size) { end_at = (double)size; }- if (start_at < 0) {- start_at += size + 1;- if (start_at < 0) start_at = 0;- }- else if (start_at == 0) { ++ start_at; }-- if (start_at <= end_at)- {- std::string::iterator start = str.begin();- utf8::advance(start, start_at - 1, str.end());- std::string::iterator end = start;- utf8::advance(end, end_at - start_at + 1, str.end());- newstr = std::string(start, end);- }- if (ss) {- if(ss->quote_mark()) newstr = quote(newstr);- }- }- // handle any invalid utf8 errors- // other errors will be re-thrown- catch (...) { handle_utf8_error(pstate, traces); }- return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);- }-- Signature to_upper_case_sig = "to-upper-case($string)";- BUILT_IN(to_upper_case)- {- String_Constant_Ptr s = ARG("$string", String_Constant);- std::string str = s->value();-- for (size_t i = 0, L = str.length(); i < L; ++i) {- if (Sass::Util::isAscii(str[i])) {- str[i] = std::toupper(str[i]);- }- }-- if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) {- String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss);- cpy->value(str);- return cpy;- } else {- return SASS_MEMORY_NEW(String_Quoted, pstate, str);- }- }-- Signature to_lower_case_sig = "to-lower-case($string)";- BUILT_IN(to_lower_case)- {- String_Constant_Ptr s = ARG("$string", String_Constant);- std::string str = s->value();-- for (size_t i = 0, L = str.length(); i < L; ++i) {- if (Sass::Util::isAscii(str[i])) {- str[i] = std::tolower(str[i]);- }- }-- if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) {- String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss);- cpy->value(str);- return cpy;- } else {- return SASS_MEMORY_NEW(String_Quoted, pstate, str);- }- }-- ///////////////////- // NUMBER FUNCTIONS- ///////////////////-- Signature percentage_sig = "percentage($number)";- BUILT_IN(percentage)- {- Number_Obj n = ARGN("$number");- if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces);- return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");- }-- Signature round_sig = "round($number)";- BUILT_IN(round)- {- Number_Obj r = ARGN("$number");- r->value(Sass::round(r->value(), ctx.c_options.precision));- r->pstate(pstate);- return r.detach();- }-- Signature ceil_sig = "ceil($number)";- BUILT_IN(ceil)- {- Number_Obj r = ARGN("$number");- r->value(std::ceil(r->value()));- r->pstate(pstate);- return r.detach();- }-- Signature floor_sig = "floor($number)";- BUILT_IN(floor)- {- Number_Obj r = ARGN("$number");- r->value(std::floor(r->value()));- r->pstate(pstate);- return r.detach();- }-- Signature abs_sig = "abs($number)";- BUILT_IN(abs)- {- Number_Obj r = ARGN("$number");- r->value(std::abs(r->value()));- r->pstate(pstate);- return r.detach();- }-- Signature min_sig = "min($numbers...)";- BUILT_IN(min)- {- List_Ptr arglist = ARG("$numbers", List);- Number_Obj least = NULL;- for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj val = arglist->value_at_index(i);- Number_Obj xi = Cast<Number>(val);- if (!xi) {- error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces);- }- if (least) {- if (*xi < *least) least = xi;- } else least = xi;- }- return least.detach();- }-- Signature max_sig = "max($numbers...)";- BUILT_IN(max)- {- List_Ptr arglist = ARG("$numbers", List);- Number_Obj greatest = NULL;- for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj val = arglist->value_at_index(i);- Number_Obj xi = Cast<Number>(val);- if (!xi) {- error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces);- }- if (greatest) {- if (*greatest < *xi) greatest = xi;- } else greatest = xi;- }- return greatest.detach();- }-- Signature random_sig = "random($limit:false)";- BUILT_IN(random)- {- AST_Node_Obj arg = env["$limit"];- Value_Ptr v = Cast<Value>(arg);- Number_Ptr l = Cast<Number>(arg);- Boolean_Ptr b = Cast<Boolean>(arg);- if (l) {- double lv = l->value();- if (lv < 1) {- stringstream err;- err << "$limit " << lv << " must be greater than or equal to 1 for `random'";- error(err.str(), pstate, traces);- }- bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON;- if (!eq_int) {- stringstream err;- err << "Expected $limit to be an integer but got " << lv << " for `random'";- error(err.str(), pstate, traces);- }- std::uniform_real_distribution<> distributor(1, lv + 1);- uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));- return SASS_MEMORY_NEW(Number, pstate, (double)distributed);- }- else if (b) {- std::uniform_real_distribution<> distributor(0, 1);- double distributed = static_cast<double>(distributor(rand));- return SASS_MEMORY_NEW(Number, pstate, distributed);- } else if (v) {- traces.push_back(Backtrace(pstate));- throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);- } else {- traces.push_back(Backtrace(pstate));- throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");- }- }-- /////////////////- // LIST FUNCTIONS- /////////////////-- Signature length_sig = "length($list)";- BUILT_IN(length)- {- if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {- return SASS_MEMORY_NEW(Number, pstate, (double)sl->length());- }- Expression_Ptr v = ARG("$list", Expression);- if (v->concrete_type() == Expression::MAP) {- Map_Ptr map = Cast<Map>(env["$list"]);- return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1));- }- if (v->concrete_type() == Expression::SELECTOR) {- if (Compound_Selector_Ptr h = Cast<Compound_Selector>(v)) {- return SASS_MEMORY_NEW(Number, pstate, (double)h->length());- } else if (Selector_List_Ptr ls = Cast<Selector_List>(v)) {- return SASS_MEMORY_NEW(Number, pstate, (double)ls->length());- } else {- return SASS_MEMORY_NEW(Number, pstate, 1);- }- }-- List_Ptr list = Cast<List>(env["$list"]);- return SASS_MEMORY_NEW(Number,- pstate,- (double)(list ? list->size() : 1));- }-- Signature nth_sig = "nth($list, $n)";- BUILT_IN(nth)- {- double nr = ARGVAL("$n");- Map_Ptr m = Cast<Map>(env["$list"]);- if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {- size_t len = m ? m->length() : sl->length();- bool empty = m ? m->empty() : sl->empty();- if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);- double index = std::floor(nr < 0 ? len + nr : nr - 1);- if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);- // return (*sl)[static_cast<int>(index)];- Listize listize;- return (*sl)[static_cast<int>(index)]->perform(&listize);- }- List_Obj l = Cast<List>(env["$list"]);- if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces);- // if the argument isn't a list, then wrap it in a singleton list- if (!m && !l) {- l = SASS_MEMORY_NEW(List, pstate, 1);- l->append(ARG("$list", Expression));- }- size_t len = m ? m->length() : l->length();- bool empty = m ? m->empty() : l->empty();- if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);- double index = std::floor(nr < 0 ? len + nr : nr - 1);- if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);-- if (m) {- l = SASS_MEMORY_NEW(List, pstate, 1);- l->append(m->keys()[static_cast<unsigned int>(index)]);- l->append(m->at(m->keys()[static_cast<unsigned int>(index)]));- return l.detach();- }- else {- Expression_Obj rv = l->value_at_index(static_cast<int>(index));- rv->set_delayed(false);- return rv.detach();- }- }-- Signature set_nth_sig = "set-nth($list, $n, $value)";- BUILT_IN(set_nth)- {- Map_Obj m = Cast<Map>(env["$list"]);- List_Obj l = Cast<List>(env["$list"]);- Number_Obj n = ARG("$n", Number);- Expression_Obj v = ARG("$value", Expression);- if (!l) {- l = SASS_MEMORY_NEW(List, pstate, 1);- l->append(ARG("$list", Expression));- }- if (m) {- l = m->to_list(pstate);- }- if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);- double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);- if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);- List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed());- for (size_t i = 0, L = l->length(); i < L; ++i) {- result->append(((i == index) ? v : (*l)[i]));- }- return result;- }-- Signature index_sig = "index($list, $value)";- BUILT_IN(index)- {- Map_Obj m = Cast<Map>(env["$list"]);- List_Obj l = Cast<List>(env["$list"]);- Expression_Obj v = ARG("$value", Expression);- if (!l) {- l = SASS_MEMORY_NEW(List, pstate, 1);- l->append(ARG("$list", Expression));- }- if (m) {- l = m->to_list(pstate);- }- for (size_t i = 0, L = l->length(); i < L; ++i) {- if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));- }- return SASS_MEMORY_NEW(Null, pstate);- }-- Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)";- BUILT_IN(join)- {- Map_Obj m1 = Cast<Map>(env["$list1"]);- Map_Obj m2 = Cast<Map>(env["$list2"]);- List_Obj l1 = Cast<List>(env["$list1"]);- List_Obj l2 = Cast<List>(env["$list2"]);- String_Constant_Obj sep = ARG("$separator", String_Constant);- enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);- Value* bracketed = ARG("$bracketed", Value);- bool is_bracketed = (l1 ? l1->is_bracketed() : false);- if (!l1) {- l1 = SASS_MEMORY_NEW(List, pstate, 1);- l1->append(ARG("$list1", Expression));- sep_val = (l2 ? l2->separator() : SASS_SPACE);- is_bracketed = (l2 ? l2->is_bracketed() : false);- }- if (!l2) {- l2 = SASS_MEMORY_NEW(List, pstate, 1);- l2->append(ARG("$list2", Expression));- }- if (m1) {- l1 = m1->to_list(pstate);- sep_val = SASS_COMMA;- }- if (m2) {- l2 = m2->to_list(pstate);- }- size_t len = l1->length() + l2->length();- std::string sep_str = unquote(sep->value());- if (sep_str == "space") sep_val = SASS_SPACE;- else if (sep_str == "comma") sep_val = SASS_COMMA;- else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);- String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);- bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";- if (!bracketed_is_auto) {- is_bracketed = !bracketed->is_false();- }- List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed);- result->concat(l1);- result->concat(l2);- return result.detach();- }-- Signature append_sig = "append($list, $val, $separator: auto)";- BUILT_IN(append)- {- Map_Obj m = Cast<Map>(env["$list"]);- List_Obj l = Cast<List>(env["$list"]);- Expression_Obj v = ARG("$val", Expression);- if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {- Listize listize;- l = Cast<List>(sl->perform(&listize));- }- String_Constant_Obj sep = ARG("$separator", String_Constant);- if (!l) {- l = SASS_MEMORY_NEW(List, pstate, 1);- l->append(ARG("$list", Expression));- }- if (m) {- l = m->to_list(pstate);- }- List_Ptr result = SASS_MEMORY_COPY(l);- std::string sep_str(unquote(sep->value()));- if (sep_str != "auto") { // check default first- if (sep_str == "space") result->separator(SASS_SPACE);- else if (sep_str == "comma") result->separator(SASS_COMMA);- else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);- }- if (l->is_arglist()) {- result->append(SASS_MEMORY_NEW(Argument,- v->pstate(),- v,- "",- false,- false));-- } else {- result->append(v);- }- return result;- }-- Signature zip_sig = "zip($lists...)";- BUILT_IN(zip)- {- List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List));- size_t shortest = 0;- for (size_t i = 0, L = arglist->length(); i < L; ++i) {- List_Obj ith = Cast<List>(arglist->value_at_index(i));- Map_Obj mith = Cast<Map>(arglist->value_at_index(i));- if (!ith) {- if (mith) {- ith = mith->to_list(pstate);- } else {- ith = SASS_MEMORY_NEW(List, pstate, 1);- ith->append(arglist->value_at_index(i));- }- if (arglist->is_arglist()) {- Argument_Obj arg = (Argument_Ptr)(arglist->at(i).ptr()); // XXX- arg->value(ith);- } else {- (*arglist)[i] = ith;- }- }- shortest = (i ? std::min(shortest, ith->length()) : ith->length());- }- List_Ptr zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA);- size_t L = arglist->length();- for (size_t i = 0; i < shortest; ++i) {- List_Ptr zipper = SASS_MEMORY_NEW(List, pstate, L);- for (size_t j = 0; j < L; ++j) {- zipper->append(Cast<List>(arglist->value_at_index(j))->at(i));- }- zippers->append(zipper);- }- return zippers;- }-- Signature list_separator_sig = "list_separator($list)";- BUILT_IN(list_separator)- {- List_Obj l = Cast<List>(env["$list"]);- if (!l) {- l = SASS_MEMORY_NEW(List, pstate, 1);- l->append(ARG("$list", Expression));- }- return SASS_MEMORY_NEW(String_Quoted,- pstate,- l->separator() == SASS_COMMA ? "comma" : "space");- }-- /////////////////- // MAP FUNCTIONS- /////////////////-- Signature map_get_sig = "map-get($map, $key)";- BUILT_IN(map_get)- {- // leaks for "map-get((), foo)" if not Obj- // investigate why this is (unexpected)- Map_Obj m = ARGM("$map", Map, ctx);- Expression_Obj v = ARG("$key", Expression);- try {- Expression_Obj val = m->at(v);- if (!val) return SASS_MEMORY_NEW(Null, pstate);- val->set_delayed(false);- return val.detach();- } catch (const std::out_of_range&) {- return SASS_MEMORY_NEW(Null, pstate);- }- catch (...) { throw; }- }-- Signature map_has_key_sig = "map-has-key($map, $key)";- BUILT_IN(map_has_key)- {- Map_Obj m = ARGM("$map", Map, ctx);- Expression_Obj v = ARG("$key", Expression);- return SASS_MEMORY_NEW(Boolean, pstate, m->has(v));- }-- Signature map_keys_sig = "map-keys($map)";- BUILT_IN(map_keys)- {- Map_Obj m = ARGM("$map", Map, ctx);- List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);- for ( auto key : m->keys()) {- result->append(key);- }- return result;- }-- Signature map_values_sig = "map-values($map)";- BUILT_IN(map_values)- {- Map_Obj m = ARGM("$map", Map, ctx);- List_Ptr result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);- for ( auto key : m->keys()) {- result->append(m->at(key));- }- return result;- }-- Signature map_merge_sig = "map-merge($map1, $map2)";- BUILT_IN(map_merge)- {- Map_Obj m1 = ARGM("$map1", Map, ctx);- Map_Obj m2 = ARGM("$map2", Map, ctx);-- size_t len = m1->length() + m2->length();- Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, len);- // concat not implemented for maps- *result += m1;- *result += m2;- return result;- }-- Signature map_remove_sig = "map-remove($map, $keys...)";- BUILT_IN(map_remove)- {- bool remove;- Map_Obj m = ARGM("$map", Map, ctx);- List_Obj arglist = ARG("$keys", List);- Map_Ptr result = SASS_MEMORY_NEW(Map, pstate, 1);- for (auto key : m->keys()) {- remove = false;- for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {- remove = Operators::eq(key, arglist->value_at_index(j));- }- if (!remove) *result << std::make_pair(key, m->at(key));- }- return result;- }-- Signature keywords_sig = "keywords($args)";- BUILT_IN(keywords)- {- List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy- Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1);- for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {- Expression_Obj obj = arglist->at(i);- Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX- std::string name = std::string(arg->name());- name = name.erase(0, 1); // sanitize name (remove dollar sign)- *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,- pstate, name),- arg->value());- }- return result.detach();- }-- //////////////////////////- // INTROSPECTION FUNCTIONS- //////////////////////////-- Signature type_of_sig = "type-of($value)";- BUILT_IN(type_of)- {- Expression_Ptr v = ARG("$value", Expression);- return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());- }-- Signature unit_sig = "unit($number)";- BUILT_IN(unit)- {- Number_Obj arg = ARGN("$number");- std::string str(quote(arg->unit(), '"'));- return SASS_MEMORY_NEW(String_Quoted, pstate, str);- }-- Signature unitless_sig = "unitless($number)";- BUILT_IN(unitless)- {- Number_Obj arg = ARGN("$number");- bool unitless = arg->is_unitless();- return SASS_MEMORY_NEW(Boolean, pstate, unitless);- }-- Signature comparable_sig = "comparable($number-1, $number-2)";- BUILT_IN(comparable)- {- Number_Obj n1 = ARGN("$number-1");- Number_Obj n2 = ARGN("$number-2");- if (n1->is_unitless() || n2->is_unitless()) {- return SASS_MEMORY_NEW(Boolean, pstate, true);- }- // normalize into main units- n1->normalize(); n2->normalize();- Units &lhs_unit = *n1, &rhs_unit = *n2;- bool is_comparable = (lhs_unit == rhs_unit);- return SASS_MEMORY_NEW(Boolean, pstate, is_comparable);- }-- Signature variable_exists_sig = "variable-exists($name)";- BUILT_IN(variable_exists)- {- std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));-- if(d_env.has("$"+s)) {- return SASS_MEMORY_NEW(Boolean, pstate, true);- }- else {- return SASS_MEMORY_NEW(Boolean, pstate, false);- }- }-- Signature global_variable_exists_sig = "global-variable-exists($name)";- BUILT_IN(global_variable_exists)- {- std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));-- if(d_env.has_global("$"+s)) {- return SASS_MEMORY_NEW(Boolean, pstate, true);- }- else {- return SASS_MEMORY_NEW(Boolean, pstate, false);- }- }-- Signature function_exists_sig = "function-exists($name)";- BUILT_IN(function_exists)- {- String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);- if (!ss) {- error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);- }-- std::string name = Util::normalize_underscores(unquote(ss->value()));-- if(d_env.has_global(name+"[f]")) {- return SASS_MEMORY_NEW(Boolean, pstate, true);- }- else {- return SASS_MEMORY_NEW(Boolean, pstate, false);- }- }-- Signature mixin_exists_sig = "mixin-exists($name)";- BUILT_IN(mixin_exists)- {- std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));-- if(d_env.has_global(s+"[m]")) {- return SASS_MEMORY_NEW(Boolean, pstate, true);- }- else {- return SASS_MEMORY_NEW(Boolean, pstate, false);- }- }-- Signature feature_exists_sig = "feature-exists($name)";- BUILT_IN(feature_exists)- {- std::string s = unquote(ARG("$name", String_Constant)->value());-- if(features.find(s) == features.end()) {- return SASS_MEMORY_NEW(Boolean, pstate, false);- }- else {- return SASS_MEMORY_NEW(Boolean, pstate, true);- }- }-- Signature call_sig = "call($name, $args...)";- BUILT_IN(call)- {- std::string name;- Function_Ptr ff = Cast<Function>(env["$name"]);- String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);-- if (ss) {- name = Util::normalize_underscores(unquote(ss->value()));- std::cerr << "DEPRECATION WARNING: ";- std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;- std::cerr << "in Sass 4.0. Use call(get-function(" + quote(name) + ")) instead." << std::endl;- std::cerr << std::endl;- } else if (ff) {- name = ff->name();- }-- List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));-- Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);- // std::string full_name(name + "[f]");- // Definition_Ptr def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0;- // Parameters_Ptr params = def ? def->parameters() : 0;- // size_t param_size = params ? params->length() : 0;- for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj expr = arglist->value_at_index(i);- // if (params && params->has_rest_parameter()) {- // Parameter_Obj p = param_size > i ? (*params)[i] : 0;- // List_Ptr list = Cast<List>(expr);- // if (list && p && !p->is_rest_parameter()) expr = (*list)[0];- // }- if (arglist->is_arglist()) {- Expression_Obj obj = arglist->at(i);- Argument_Obj arg = (Argument_Ptr) obj.ptr(); // XXX- args->append(SASS_MEMORY_NEW(Argument,- pstate,- expr,- arg ? arg->name() : "",- arg ? arg->is_rest_argument() : false,- arg ? arg->is_keyword_argument() : false));- } else {- args->append(SASS_MEMORY_NEW(Argument, pstate, expr));- }- }- Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args);- Expand expand(ctx, &d_env, &selector_stack);- func->via_call(true); // calc invoke is allowed- if (ff) func->func(ff);- return func->perform(&expand.eval);- }-- ////////////////////- // BOOLEAN FUNCTIONS- ////////////////////-- Signature not_sig = "not($value)";- BUILT_IN(sass_not)- {- return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());- }-- Signature if_sig = "if($condition, $if-true, $if-false)";- // BUILT_IN(sass_if)- // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }- BUILT_IN(sass_if)- {- Expand expand(ctx, &d_env, &selector_stack);- Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);- bool is_true = !cond->is_false();- Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression);- res = res->perform(&expand.eval);- res->set_delayed(false); // clone?- return res.detach();- }-- //////////////////////////- // MISCELLANEOUS FUNCTIONS- //////////////////////////-- // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String)- // unquoted_string(value.to_sass)-- Signature inspect_sig = "inspect($value)";- BUILT_IN(inspect)- {- Expression_Ptr v = ARG("$value", Expression);- if (v->concrete_type() == Expression::NULL_VAL) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "null");- } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {- return SASS_MEMORY_NEW(String_Quoted, pstate, "false");- } else if (v->concrete_type() == Expression::STRING) {- return v;- } else {- // ToDo: fix to_sass for nested parentheses- Sass_Output_Style old_style;- old_style = ctx.c_options.output_style;- ctx.c_options.output_style = TO_SASS;- Emitter emitter(ctx.c_options);- Inspect i(emitter);- i.in_declaration = false;- v->perform(&i);- ctx.c_options.output_style = old_style;- return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer());- }- // return v;- }- Signature selector_nest_sig = "selector-nest($selectors...)";- BUILT_IN(selector_nest)- {- List_Ptr arglist = ARG("$selectors", List);-- // Not enough parameters- if( arglist->length() == 0 )- error("$selectors: At least one selector must be passed for `selector-nest'", pstate, traces);-- // Parse args into vector of selectors- std::vector<Selector_List_Obj> parsedSelectors;- for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i));- if (exp->concrete_type() == Expression::NULL_VAL) {- std::stringstream msg;- msg << "$selectors: null is not a valid selector: it must be a string,\n";- msg << "a list of strings, or a list of lists of strings for 'selector-nest'";- error(msg.str(), pstate, traces);- }- if (String_Constant_Obj str = Cast<String_Constant>(exp)) {- str->quote_mark(0);- }- std::string exp_src = exp->to_string(ctx.c_options);- Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);- parsedSelectors.push_back(sel);- }-- // Nothing to do- if( parsedSelectors.empty() ) {- return SASS_MEMORY_NEW(Null, pstate);- }-- // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.- std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();- Selector_List_Obj result = *itr;- ++itr;-- for(;itr != parsedSelectors.end(); ++itr) {- Selector_List_Obj child = *itr;- std::vector<Complex_Selector_Obj> exploded;- selector_stack.push_back(result);- Selector_List_Obj rv = child->resolve_parent_refs(selector_stack, traces);- selector_stack.pop_back();- for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) {- exploded.push_back((*rv)[m]);- }- result->elements(exploded);- }-- Listize listize;- return result->perform(&listize);- }-- Signature selector_append_sig = "selector-append($selectors...)";- BUILT_IN(selector_append)- {- List_Ptr arglist = ARG("$selectors", List);-- // Not enough parameters- if( arglist->length() == 0 )- error("$selectors: At least one selector must be passed for `selector-append'", pstate, traces);-- // Parse args into vector of selectors- std::vector<Selector_List_Obj> parsedSelectors;- for (size_t i = 0, L = arglist->length(); i < L; ++i) {- Expression_Obj exp = Cast<Expression>(arglist->value_at_index(i));- if (exp->concrete_type() == Expression::NULL_VAL) {- std::stringstream msg;- msg << "$selectors: null is not a valid selector: it must be a string,\n";- msg << "a list of strings, or a list of lists of strings for 'selector-append'";- error(msg.str(), pstate, traces);- }- if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {- str->quote_mark(0);- }- std::string exp_src = exp->to_string();- Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);- parsedSelectors.push_back(sel);- }-- // Nothing to do- if( parsedSelectors.empty() ) {- return SASS_MEMORY_NEW(Null, pstate);- }-- // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.- std::vector<Selector_List_Obj>::iterator itr = parsedSelectors.begin();- Selector_List_Obj result = *itr;- ++itr;-- for(;itr != parsedSelectors.end(); ++itr) {- Selector_List_Obj child = *itr;- std::vector<Complex_Selector_Obj> newElements;-- // For every COMPLEX_SELECTOR in `result`- // For every COMPLEX_SELECTOR in `child`- // let parentSeqClone equal a copy of result->elements[i]- // let childSeq equal child->elements[j]- // Append all of childSeq head elements into parentSeqClone- // Set the innermost tail of parentSeqClone, to childSeq's tail- // Replace result->elements with newElements- for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) {- for (size_t j = 0, childLen = child->length(); j < childLen; ++j) {- Complex_Selector_Obj parentSeqClone = SASS_MEMORY_CLONE((*result)[i]);- Complex_Selector_Obj childSeq = (*child)[j];- Complex_Selector_Obj base = childSeq->tail();-- // Must be a simple sequence- if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) {- std::string msg("Can't append \"");- msg += childSeq->to_string();- msg += "\" to \"";- msg += parentSeqClone->to_string();- msg += "\" for `selector-append'";- error(msg, pstate, traces);- }-- // Cannot be a Universal selector- Element_Selector_Obj pType = Cast<Element_Selector>(childSeq->head()->first());- if(pType && pType->name() == "*") {- std::string msg("Can't append \"");- msg += childSeq->to_string();- msg += "\" to \"";- msg += parentSeqClone->to_string();- msg += "\" for `selector-append'";- error(msg, pstate, traces);- }-- // TODO: Add check for namespace stuff-- // append any selectors in childSeq's head- parentSeqClone->innermost()->head()->concat(base->head());-- // Set parentSeqClone new tail- parentSeqClone->innermost()->tail( base->tail() );-- newElements.push_back(parentSeqClone);- }- }-- result->elements(newElements);- }-- Listize listize;- return result->perform(&listize);- }-- Signature selector_unify_sig = "selector-unify($selector1, $selector2)";- BUILT_IN(selector_unify)- {- Selector_List_Obj selector1 = ARGSEL("$selector1", Selector_List_Obj, p_contextualize);- Selector_List_Obj selector2 = ARGSEL("$selector2", Selector_List_Obj, p_contextualize);-- Selector_List_Obj result = selector1->unify_with(selector2);- Listize listize;- return result->perform(&listize);- }-- Signature simple_selectors_sig = "simple-selectors($selector)";- BUILT_IN(simple_selectors)- {- Compound_Selector_Obj sel = ARGSEL("$selector", Compound_Selector_Obj, p_contextualize);-- List_Ptr l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);-- for (size_t i = 0, L = sel->length(); i < L; ++i) {- Simple_Selector_Obj ss = (*sel)[i];- std::string ss_string = ss->to_string() ;-- l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));- }-- return l;- }-- Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";- BUILT_IN(selector_extend)- {- Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize);- Selector_List_Obj extendee = ARGSEL("$extendee", Selector_List_Obj, p_contextualize);- Selector_List_Obj extender = ARGSEL("$extender", Selector_List_Obj, p_contextualize);-- Subset_Map subset_map;- extender->populate_extends(extendee, subset_map);- Extend extend(subset_map);-- Selector_List_Obj result = extend.extendSelectorList(selector, false);-- Listize listize;- return result->perform(&listize);- }-- Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";- BUILT_IN(selector_replace)- {- Selector_List_Obj selector = ARGSEL("$selector", Selector_List_Obj, p_contextualize);- Selector_List_Obj original = ARGSEL("$original", Selector_List_Obj, p_contextualize);- Selector_List_Obj replacement = ARGSEL("$replacement", Selector_List_Obj, p_contextualize);- Subset_Map subset_map;- replacement->populate_extends(original, subset_map);- Extend extend(subset_map);-- Selector_List_Obj result = extend.extendSelectorList(selector, true);-- Listize listize;- return result->perform(&listize);- }-- Signature selector_parse_sig = "selector-parse($selector)";- BUILT_IN(selector_parse)- {- Selector_List_Obj sel = ARGSEL("$selector", Selector_List_Obj, p_contextualize);-- Listize listize;- return sel->perform(&listize);- }-- Signature is_superselector_sig = "is-superselector($super, $sub)";- BUILT_IN(is_superselector)- {- Selector_List_Obj sel_sup = ARGSEL("$super", Selector_List_Obj, p_contextualize);- Selector_List_Obj sel_sub = ARGSEL("$sub", Selector_List_Obj, p_contextualize);- bool result = sel_sup->is_superselector_of(sel_sub);- return SASS_MEMORY_NEW(Boolean, pstate, result);- }-- Signature unique_id_sig = "unique-id()";- BUILT_IN(unique_id)- {- std::stringstream ss;- std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8- uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));- ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;- return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str());- }-- Signature is_bracketed_sig = "is-bracketed($list)";- BUILT_IN(is_bracketed)- {- Value_Obj value = ARG("$list", Value);- List_Obj list = Cast<List>(value);- return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed());- }-- Signature content_exists_sig = "content-exists()";- BUILT_IN(content_exists)- {- if (!d_env.has_global("is_in_mixin")) {- error("Cannot call content-exists() except within a mixin.", pstate, traces);- }- return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));- }-- Signature get_function_sig = "get-function($name, $css: false)";- BUILT_IN(get_function)- {- String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);- if (!ss) {- error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);- }-- std::string name = Util::normalize_underscores(unquote(ss->value()));- std::string full_name = name + "[f]";-- Boolean_Obj css = ARG("$css", Boolean);- if (!css->is_false()) {- Definition_Ptr def = SASS_MEMORY_NEW(Definition,- pstate,- name,- SASS_MEMORY_NEW(Parameters, pstate),- SASS_MEMORY_NEW(Block, pstate, 0, false),- Definition::FUNCTION);- return SASS_MEMORY_NEW(Function, pstate, def, true);- }--- if (!d_env.has_global(full_name)) {- error("Function not found: " + name, pstate, traces);- }-- Definition_Ptr def = Cast<Definition>(d_env[full_name]);- return SASS_MEMORY_NEW(Function, pstate, def, false);- }- }-}
− libsass/src/functions.hpp
@@ -1,198 +0,0 @@-#ifndef SASS_FUNCTIONS_H-#define SASS_FUNCTIONS_H--#include "listize.hpp"-#include "position.hpp"-#include "environment.hpp"-#include "ast_fwd_decl.hpp"-#include "sass/functions.h"--#define BUILT_IN(name) Expression_Ptr \-name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtraces traces, std::vector<Selector_List_Obj> selector_stack)--namespace Sass {- struct Backtrace;- typedef const char* Signature;- typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>);-- Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx);- Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx);-- std::string function_name(Signature);-- namespace Functions {-- extern Signature rgb_sig;- extern Signature rgba_4_sig;- extern Signature rgba_2_sig;- extern Signature red_sig;- extern Signature green_sig;- extern Signature blue_sig;- extern Signature mix_sig;- extern Signature hsl_sig;- extern Signature hsla_sig;- extern Signature hue_sig;- extern Signature saturation_sig;- extern Signature lightness_sig;- extern Signature adjust_hue_sig;- extern Signature lighten_sig;- extern Signature darken_sig;- extern Signature saturate_sig;- extern Signature desaturate_sig;- extern Signature grayscale_sig;- extern Signature complement_sig;- extern Signature invert_sig;- extern Signature alpha_sig;- extern Signature opacity_sig;- extern Signature opacify_sig;- extern Signature fade_in_sig;- extern Signature transparentize_sig;- extern Signature fade_out_sig;- extern Signature adjust_color_sig;- extern Signature scale_color_sig;- extern Signature change_color_sig;- extern Signature ie_hex_str_sig;- extern Signature unquote_sig;- extern Signature quote_sig;- extern Signature str_length_sig;- extern Signature str_insert_sig;- extern Signature str_index_sig;- extern Signature str_slice_sig;- extern Signature to_upper_case_sig;- extern Signature to_lower_case_sig;- extern Signature percentage_sig;- extern Signature round_sig;- extern Signature ceil_sig;- extern Signature floor_sig;- extern Signature abs_sig;- extern Signature min_sig;- extern Signature max_sig;- extern Signature inspect_sig;- extern Signature random_sig;- extern Signature length_sig;- extern Signature nth_sig;- extern Signature index_sig;- extern Signature join_sig;- extern Signature append_sig;- extern Signature zip_sig;- extern Signature list_separator_sig;- extern Signature type_of_sig;- extern Signature unit_sig;- extern Signature unitless_sig;- extern Signature comparable_sig;- extern Signature variable_exists_sig;- extern Signature global_variable_exists_sig;- extern Signature function_exists_sig;- extern Signature mixin_exists_sig;- extern Signature feature_exists_sig;- extern Signature call_sig;- extern Signature not_sig;- extern Signature if_sig;- extern Signature map_get_sig;- extern Signature map_merge_sig;- extern Signature map_remove_sig;- extern Signature map_keys_sig;- extern Signature map_values_sig;- extern Signature map_has_key_sig;- extern Signature keywords_sig;- extern Signature set_nth_sig;- extern Signature unique_id_sig;- extern Signature selector_nest_sig;- extern Signature selector_append_sig;- extern Signature selector_extend_sig;- extern Signature selector_replace_sig;- extern Signature selector_unify_sig;- extern Signature is_superselector_sig;- extern Signature simple_selectors_sig;- extern Signature selector_parse_sig;- extern Signature is_bracketed_sig;- extern Signature content_exists_sig;- extern Signature get_function_sig;-- BUILT_IN(rgb);- BUILT_IN(rgba_4);- BUILT_IN(rgba_2);- BUILT_IN(red);- BUILT_IN(green);- BUILT_IN(blue);- BUILT_IN(mix);- BUILT_IN(hsl);- BUILT_IN(hsla);- BUILT_IN(hue);- BUILT_IN(saturation);- BUILT_IN(lightness);- BUILT_IN(adjust_hue);- BUILT_IN(lighten);- BUILT_IN(darken);- BUILT_IN(saturate);- BUILT_IN(desaturate);- BUILT_IN(grayscale);- BUILT_IN(complement);- BUILT_IN(invert);- BUILT_IN(alpha);- BUILT_IN(opacify);- BUILT_IN(transparentize);- BUILT_IN(adjust_color);- BUILT_IN(scale_color);- BUILT_IN(change_color);- BUILT_IN(ie_hex_str);- BUILT_IN(sass_unquote);- BUILT_IN(sass_quote);- BUILT_IN(str_length);- BUILT_IN(str_insert);- BUILT_IN(str_index);- BUILT_IN(str_slice);- BUILT_IN(to_upper_case);- BUILT_IN(to_lower_case);- BUILT_IN(percentage);- BUILT_IN(round);- BUILT_IN(ceil);- BUILT_IN(floor);- BUILT_IN(abs);- BUILT_IN(min);- BUILT_IN(max);- BUILT_IN(inspect);- BUILT_IN(random);- BUILT_IN(length);- BUILT_IN(nth);- BUILT_IN(index);- BUILT_IN(join);- BUILT_IN(append);- BUILT_IN(zip);- BUILT_IN(list_separator);- BUILT_IN(type_of);- BUILT_IN(unit);- BUILT_IN(unitless);- BUILT_IN(comparable);- BUILT_IN(variable_exists);- BUILT_IN(global_variable_exists);- BUILT_IN(function_exists);- BUILT_IN(mixin_exists);- BUILT_IN(feature_exists);- BUILT_IN(call);- BUILT_IN(sass_not);- BUILT_IN(sass_if);- BUILT_IN(map_get);- BUILT_IN(map_merge);- BUILT_IN(map_remove);- BUILT_IN(map_keys);- BUILT_IN(map_values);- BUILT_IN(map_has_key);- BUILT_IN(keywords);- BUILT_IN(set_nth);- BUILT_IN(unique_id);- BUILT_IN(selector_nest);- BUILT_IN(selector_append);- BUILT_IN(selector_extend);- BUILT_IN(selector_replace);- BUILT_IN(selector_unify);- BUILT_IN(is_superselector);- BUILT_IN(simple_selectors);- BUILT_IN(selector_parse);- BUILT_IN(is_bracketed);- BUILT_IN(content_exists);- BUILT_IN(get_function);- }-}--#endif
libsass/src/inspect.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cmath> #include <string> #include <iostream>@@ -21,7 +24,7 @@ Inspect::~Inspect() { } // statements- void Inspect::operator()(Block_Ptr block)+ void Inspect::operator()(Block* block) { if (!block->is_root()) { add_open_mapping(block);@@ -39,25 +42,23 @@ } - void Inspect::operator()(Ruleset_Ptr ruleset)+ void Inspect::operator()(Ruleset* ruleset) { if (ruleset->selector()) {- opt.in_selector = true; ruleset->selector()->perform(this);- opt.in_selector = false; } if (ruleset->block()) { ruleset->block()->perform(this); } } - void Inspect::operator()(Keyframe_Rule_Ptr rule)+ void Inspect::operator()(Keyframe_Rule* rule) { if (rule->name()) rule->name()->perform(this); if (rule->block()) rule->block()->perform(this); } - void Inspect::operator()(Bubble_Ptr bubble)+ void Inspect::operator()(Bubble* bubble) { append_indentation(); append_token("::BUBBLE", bubble);@@ -66,19 +67,65 @@ append_scope_closer(); } - void Inspect::operator()(Media_Block_Ptr media_block)+ void Inspect::operator()(MediaRule* rule) { append_indentation();- append_token("@media", media_block);+ append_token("@media", rule); append_mandatory_space();+ if (rule->block()) {+ rule->block()->perform(this);+ }+ }++ void Inspect::operator()(CssMediaRule* rule)+ {+ if (output_style() == NESTED)+ indentation += rule->tabs();+ append_indentation();+ append_token("@media", rule);+ append_mandatory_space(); in_media_block = true;- media_block->media_queries()->perform(this);+ bool joinIt = false;+ for (auto query : rule->elements()) {+ if (joinIt) {+ append_comma_separator();+ append_optional_space();+ }+ operator()(query);+ joinIt = true;+ }+ if (rule->block()) {+ rule->block()->perform(this);+ } in_media_block = false;- media_block->block()->perform(this);+ if (output_style() == NESTED)+ indentation -= rule->tabs(); } - void Inspect::operator()(Supports_Block_Ptr feature_block)+ void Inspect::operator()(CssMediaQuery* query) {+ bool joinIt = false;+ if (!query->modifier().empty()) {+ append_string(query->modifier());+ append_mandatory_space();+ }+ if (!query->type().empty()) {+ append_string(query->type());+ joinIt = true;+ }+ for (auto feature : query->features()) {+ if (joinIt) {+ append_mandatory_space();+ append_string("and");+ append_mandatory_space();+ }+ append_string(feature);+ joinIt = true;+ }+ }++ void Inspect::operator()(Supports_Block* feature_block)+ { append_indentation(); append_token("@supports", feature_block); append_mandatory_space();@@ -86,7 +133,7 @@ feature_block->block()->perform(this); } - void Inspect::operator()(At_Root_Block_Ptr at_root_block)+ void Inspect::operator()(At_Root_Block* at_root_block) { append_indentation(); append_token("@at-root ", at_root_block);@@ -95,7 +142,7 @@ if(at_root_block->block()) at_root_block->block()->perform(this); } - void Inspect::operator()(Directive_Ptr at_rule)+ void Inspect::operator()(Directive* at_rule) { append_indentation(); append_token(at_rule->keyword(), at_rule);@@ -118,7 +165,7 @@ } } - void Inspect::operator()(Declaration_Ptr dec)+ void Inspect::operator()(Declaration* dec) { if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration;@@ -133,8 +180,7 @@ append_colon_separator(); if (dec->value()->concrete_type() == Expression::SELECTOR) {- Listize listize;- Expression_Obj ls = dec->value()->perform(&listize);+ Expression_Obj ls = Listize::perform(dec->value()); ls->perform(this); } else { dec->value()->perform(this);@@ -150,7 +196,7 @@ in_declaration = was_decl; } - void Inspect::operator()(Assignment_Ptr assn)+ void Inspect::operator()(Assignment* assn) { append_token(assn->variable(), assn); append_colon_separator();@@ -162,7 +208,7 @@ append_delimiter(); } - void Inspect::operator()(Import_Ptr import)+ void Inspect::operator()(Import* import) { if (!import->urls().empty()) { append_token("@import", import);@@ -193,7 +239,7 @@ } } - void Inspect::operator()(Import_Stub_Ptr import)+ void Inspect::operator()(Import_Stub* import) { append_indentation(); append_token("@import", import);@@ -202,7 +248,7 @@ append_delimiter(); } - void Inspect::operator()(Warning_Ptr warning)+ void Inspect::operator()(Warning* warning) { append_indentation(); append_token("@warn", warning);@@ -211,7 +257,7 @@ append_delimiter(); } - void Inspect::operator()(Error_Ptr error)+ void Inspect::operator()(Error* error) { append_indentation(); append_token("@error", error);@@ -220,7 +266,7 @@ append_delimiter(); } - void Inspect::operator()(Debug_Ptr debug)+ void Inspect::operator()(Debug* debug) { append_indentation(); append_token("@debug", debug);@@ -229,14 +275,14 @@ append_delimiter(); } - void Inspect::operator()(Comment_Ptr comment)+ void Inspect::operator()(Comment* comment) { in_comment = true; comment->text()->perform(this); in_comment = false; } - void Inspect::operator()(If_Ptr cond)+ void Inspect::operator()(If* cond) { append_indentation(); append_token("@if", cond);@@ -251,7 +297,7 @@ } } - void Inspect::operator()(For_Ptr loop)+ void Inspect::operator()(For* loop) { append_indentation(); append_token("@for", loop);@@ -264,7 +310,7 @@ loop->block()->perform(this); } - void Inspect::operator()(Each_Ptr loop)+ void Inspect::operator()(Each* loop) { append_indentation(); append_token("@each", loop);@@ -279,7 +325,7 @@ loop->block()->perform(this); } - void Inspect::operator()(While_Ptr loop)+ void Inspect::operator()(While* loop) { append_indentation(); append_token("@while", loop);@@ -288,7 +334,7 @@ loop->block()->perform(this); } - void Inspect::operator()(Return_Ptr ret)+ void Inspect::operator()(Return* ret) { append_indentation(); append_token("@return", ret);@@ -297,7 +343,7 @@ append_delimiter(); } - void Inspect::operator()(Extension_Ptr extend)+ void Inspect::operator()(ExtendRule* extend) { append_indentation(); append_token("@extend", extend);@@ -306,7 +352,7 @@ append_delimiter(); } - void Inspect::operator()(Definition_Ptr def)+ void Inspect::operator()(Definition* def) { append_indentation(); if (def->type() == Definition::MIXIN) {@@ -321,7 +367,7 @@ def->block()->perform(this); } - void Inspect::operator()(Mixin_Call_Ptr call)+ void Inspect::operator()(Mixin_Call* call) { append_indentation(); append_token("@include", call);@@ -337,14 +383,14 @@ if (!call->block()) append_delimiter(); } - void Inspect::operator()(Content_Ptr content)+ void Inspect::operator()(Content* content) { append_indentation(); append_token("@content", content); append_delimiter(); } - void Inspect::operator()(Map_Ptr map)+ void Inspect::operator()(Map* map) { if (output_style() == TO_SASS && map->empty()) { append_string("()");@@ -366,15 +412,15 @@ append_string(")"); } - std::string Inspect::lbracket(List_Ptr list) {+ std::string Inspect::lbracket(List* list) { return list->is_bracketed() ? "[" : "("; } - std::string Inspect::rbracket(List_Ptr list) {+ std::string Inspect::rbracket(List* list) { return list->is_bracketed() ? "]" : ")"; } - void Inspect::operator()(List_Ptr list)+ void Inspect::operator()(List* list) { if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) { append_string(lbracket(list));@@ -398,7 +444,7 @@ list->length() == 1 && !list->from_selector() && !Cast<List>(list->at(0)) &&- !Cast<Selector_List>(list->at(0))+ !Cast<SelectorList>(list->at(0)) ) { append_string(lbracket(list)); }@@ -448,7 +494,7 @@ list->length() == 1 && !list->from_selector() && !Cast<List>(list->at(0)) &&- !Cast<Selector_List>(list->at(0))+ !Cast<SelectorList>(list->at(0)) ) { append_string(","); append_string(rbracket(list));@@ -462,7 +508,7 @@ } - void Inspect::operator()(Binary_Expression_Ptr expr)+ void Inspect::operator()(Binary_Expression* expr) { expr->left()->perform(this); if ( in_media_block ||@@ -499,7 +545,7 @@ expr->right()->perform(this); } - void Inspect::operator()(Unary_Expression_Ptr expr)+ void Inspect::operator()(Unary_Expression* expr) { if (expr->optype() == Unary_Expression::PLUS) append_string("+"); else if (expr->optype() == Unary_Expression::SLASH) append_string("/");@@ -507,91 +553,42 @@ expr->operand()->perform(this); } - void Inspect::operator()(Function_Call_Ptr call)+ void Inspect::operator()(Function_Call* call) { append_token(call->name(), call); call->arguments()->perform(this); } - void Inspect::operator()(Function_Call_Schema_Ptr call)- {- call->name()->perform(this);- call->arguments()->perform(this);- }-- void Inspect::operator()(Variable_Ptr var)+ void Inspect::operator()(Variable* var) { append_token(var->name(), var); } - void Inspect::operator()(Number_Ptr n)+ void Inspect::operator()(Number* n) { - std::string res;- // reduce units n->reduce(); - // check if the fractional part of the value equals to zero- // neat trick from http://stackoverflow.com/a/1521682/1550314- // double int_part; bool is_int = modf(value, &int_part) == 0.0;-- // this all cannot be done with one run only, since fixed- // output differs from normal output and regular output- // can contain scientific notation which we do not want!-- // first sample std::stringstream ss;- ss.precision(12);- ss << n->value();-- // check if we got scientific notation in result- if (ss.str().find_first_of("e") != std::string::npos) {- ss.clear(); ss.str(std::string());- ss.precision(std::max(12, opt.precision));- ss << std::fixed << n->value();- }-- std::string tmp = ss.str();- size_t pos_point = tmp.find_first_of(".,");- size_t pos_fract = tmp.find_last_not_of("0");- bool is_int = pos_point == pos_fract ||- pos_point == std::string::npos;+ ss.precision(opt.precision);+ ss << std::fixed << n->value(); - // reset stream for another run- ss.clear(); ss.str(std::string());+ std::string res = ss.str();+ size_t s = res.length(); - // take a shortcut for integers- if (is_int)- {- ss.precision(0);- ss << std::fixed << n->value();- res = std::string(ss.str());- }- // process floats- else+ // delete trailing zeros+ for(s = s - 1; s > 0; --s) {- // do we have have too much precision?- if (pos_fract < opt.precision + pos_point)- { ss.precision((int)(pos_fract - pos_point)); }- else { ss.precision(opt.precision); }- // round value again- ss << std::fixed << n->value();- res = std::string(ss.str());- // maybe we truncated up to decimal point- size_t pos = res.find_last_not_of("0");- // handle case where we have a "0"- if (pos == std::string::npos) {- res = "0.0";- } else {- bool at_dec_point = res[pos] == '.' ||- res[pos] == ',';- // don't leave a blank point- if (at_dec_point) ++ pos;- res.resize (pos + 1);- }+ if(res[s] == '0') {+ res.erase(s, 1);+ }+ else break; } + // delete trailing decimal separator+ if(res[s] == '.') res.erase(s, 1);+ // some final cosmetics if (res == "0.0") res = "0"; else if (res == "") res = "0";@@ -599,15 +596,22 @@ else if (res == "-0.0") res = "0"; else if (opt.output_style == COMPRESSED) {- // check if handling negative nr- size_t off = res[0] == '-' ? 1 : 0;- // remove leading zero from floating point in compressed mode- if (n->zero() && res[off] == '0' && res[off+1] == '.') res.erase(off, 1);+ if (n->zero()) {+ // check if handling negative nr+ size_t off = res[0] == '-' ? 1 : 0;+ // remove leading zero from floating point in compressed mode+ if (res[off] == '0' && res[off+1] == '.') res.erase(off, 1);+ } } // add unit now res += n->unit(); + if (opt.output_style == TO_CSS && !n->is_valid_css_unit()) {+ // traces.push_back(Backtrace(nr->pstate()));+ throw Exception::InvalidValue({}, *n);+ }+ // output the final token append_token(res, n); }@@ -620,7 +624,7 @@ else return c; } - void Inspect::operator()(Color_Ptr c)+ void Inspect::operator()(Color_RGBA* c) { // output the final token std::stringstream ss;@@ -629,11 +633,6 @@ // maybe an unknown token std::string name = c->disp(); - if (opt.in_selector && name != "") {- append_token(name, c);- return;- }- // resolved color std::string res_name = name; @@ -644,7 +643,7 @@ // get color from given name (if one was given at all) if (name != "" && name_to_color(name)) {- Color_Ptr_Const n = name_to_color(name);+ const Color_RGBA* n = name_to_color(name); r = Sass::round(cap_channel<0xff>(n->r()), opt.precision); g = Sass::round(cap_channel<0xff>(n->g()), opt.precision); b = Sass::round(cap_channel<0xff>(n->b()), opt.precision);@@ -711,13 +710,19 @@ } - void Inspect::operator()(Boolean_Ptr b)+ void Inspect::operator()(Color_HSLA* c) {+ Color_RGBA_Obj rgba = c->toRGBA();+ operator()(rgba);+ }++ void Inspect::operator()(Boolean* b)+ { // output the final token append_token(b->value() ? "true" : "false", b); } - void Inspect::operator()(String_Schema_Ptr ss)+ void Inspect::operator()(String_Schema* ss) { // Evaluation should turn these into String_Constants, // so this method is only for inspection purposes.@@ -728,12 +733,12 @@ } } - void Inspect::operator()(String_Constant_Ptr s)+ void Inspect::operator()(String_Constant* s) { append_token(s->value(), s); } - void Inspect::operator()(String_Quoted_Ptr s)+ void Inspect::operator()(String_Quoted* s) { if (const char q = s->quote_mark()) { append_token(quote(s->value(), q), s);@@ -742,17 +747,17 @@ } } - void Inspect::operator()(Custom_Error_Ptr e)+ void Inspect::operator()(Custom_Error* e) { append_token(e->message(), e); } - void Inspect::operator()(Custom_Warning_Ptr w)+ void Inspect::operator()(Custom_Warning* w) { append_token(w->message(), w); } - void Inspect::operator()(Supports_Operator_Ptr so)+ void Inspect::operator()(Supports_Operator* so) { if (so->needs_parens(so->left())) append_string("(");@@ -774,7 +779,7 @@ if (so->needs_parens(so->right())) append_string(")"); } - void Inspect::operator()(Supports_Negation_Ptr sn)+ void Inspect::operator()(Supports_Negation* sn) { append_token("not", sn); append_mandatory_space();@@ -783,7 +788,7 @@ if (sn->needs_parens(sn->condition())) append_string(")"); } - void Inspect::operator()(Supports_Declaration_Ptr sd)+ void Inspect::operator()(Supports_Declaration* sd) { append_string("("); sd->feature()->perform(this);@@ -792,12 +797,12 @@ append_string(")"); } - void Inspect::operator()(Supports_Interpolation_Ptr sd)+ void Inspect::operator()(Supports_Interpolation* sd) { sd->value()->perform(this); } - void Inspect::operator()(Media_Query_Ptr mq)+ void Inspect::operator()(Media_Query* mq) { size_t i = 0; if (mq->media_type()) {@@ -814,7 +819,7 @@ } } - void Inspect::operator()(Media_Query_Expression_Ptr mqe)+ void Inspect::operator()(Media_Query_Expression* mqe) { if (mqe->is_interpolated()) { mqe->feature()->perform(this);@@ -830,7 +835,7 @@ } } - void Inspect::operator()(At_Root_Query_Ptr ae)+ void Inspect::operator()(At_Root_Query* ae) { if (ae->feature()) { append_string("(");@@ -843,7 +848,7 @@ } } - void Inspect::operator()(Function_Ptr f)+ void Inspect::operator()(Function* f) { append_token("get-function", f); append_string("(");@@ -851,14 +856,14 @@ append_string(")"); } - void Inspect::operator()(Null_Ptr n)+ void Inspect::operator()(Null* n) { // output the final token append_token("null", n); } // parameters and arguments- void Inspect::operator()(Parameter_Ptr p)+ void Inspect::operator()(Parameter* p) { append_token(p->name(), p); if (p->default_value()) {@@ -870,7 +875,7 @@ } } - void Inspect::operator()(Parameters_Ptr p)+ void Inspect::operator()(Parameters* p) { append_string("("); if (!p->empty()) {@@ -883,7 +888,7 @@ append_string(")"); } - void Inspect::operator()(Argument_Ptr a)+ void Inspect::operator()(Argument* a) { if (!a->name().empty()) { append_token(a->name(), a);@@ -895,7 +900,7 @@ return; } if (a->value()->concrete_type() == Expression::STRING) {- String_Constant_Ptr s = Cast<String_Constant>(a->value());+ String_Constant* s = Cast<String_Constant>(a->value()); if (s) s->perform(this); } else { a->value()->perform(this);@@ -905,7 +910,7 @@ } } - void Inspect::operator()(Arguments_Ptr a)+ void Inspect::operator()(Arguments* a) { append_string("("); if (!a->empty()) {@@ -919,46 +924,38 @@ append_string(")"); } - void Inspect::operator()(Selector_Schema_Ptr s)+ void Inspect::operator()(Selector_Schema* s) {- opt.in_selector = true; s->contents()->perform(this);- opt.in_selector = false; } - void Inspect::operator()(Parent_Selector_Ptr p)+ void Inspect::operator()(Parent_Reference* p) {- if (p->is_real_parent_ref()) append_string("&");+ append_string("&"); } - void Inspect::operator()(Placeholder_Selector_Ptr s)+ void Inspect::operator()(Placeholder_Selector* s) { append_token(s->name(), s);- if (s->has_line_break()) append_optional_linefeed();- if (s->has_line_break()) append_indentation(); } - void Inspect::operator()(Element_Selector_Ptr s)+ void Inspect::operator()(Type_Selector* s) { append_token(s->ns_name(), s); } - void Inspect::operator()(Class_Selector_Ptr s)+ void Inspect::operator()(Class_Selector* s) { append_token(s->ns_name(), s);- if (s->has_line_break()) append_optional_linefeed();- if (s->has_line_break()) append_indentation(); } - void Inspect::operator()(Id_Selector_Ptr s)+ void Inspect::operator()(Id_Selector* s) { append_token(s->ns_name(), s);- if (s->has_line_break()) append_optional_linefeed();- if (s->has_line_break()) append_indentation(); } - void Inspect::operator()(Attribute_Selector_Ptr s)+ void Inspect::operator()(Attribute_Selector* s) { append_string("["); add_open_mapping(s);@@ -977,111 +974,38 @@ append_string("]"); } - void Inspect::operator()(Pseudo_Selector_Ptr s)+ void Inspect::operator()(Pseudo_Selector* s) {- append_token(s->ns_name(), s);- if (s->expression()) {- append_string("(");- s->expression()->perform(this);- append_string(")");- }- } - void Inspect::operator()(Wrapped_Selector_Ptr s)- {- if (s->name() == " ") {- append_string("");- } else {- bool was = in_wrapped;- in_wrapped = true;- append_token(s->name(), s);- append_string("(");- bool was_comma_array = in_comma_array;- in_comma_array = false;- s->selector()->perform(this);- in_comma_array = was_comma_array;- append_string(")");- in_wrapped = was;- }- }-- void Inspect::operator()(Compound_Selector_Ptr s)- {- for (size_t i = 0, L = s->length(); i < L; ++i) {- (*s)[i]->perform(this);- }- if (s->has_line_break()) {- if (output_style() != COMPACT) {- append_optional_linefeed();- }- }- }-- void Inspect::operator()(Complex_Selector_Ptr c)- {- Compound_Selector_Obj head = c->head();- Complex_Selector_Obj tail = c->tail();- Complex_Selector::Combinator comb = c->combinator();-- if (comb == Complex_Selector::ANCESTOR_OF && (!head || head->empty())) {- if (tail) tail->perform(this);- return;- }-- if (c->has_line_feed()) {- if (!(c->has_parent_ref())) {- append_optional_linefeed();- append_indentation();+ if (s->name() != "") {+ append_string(":");+ if (s->isSyntacticElement()) {+ append_string(":"); }- }-- if (head && head->length() != 0) head->perform(this);- bool is_empty = !head || head->length() == 0 || head->is_empty_reference();- bool is_tail = head && !head->is_empty_reference() && tail;- if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;-- switch (comb) {- case Complex_Selector::ANCESTOR_OF:- if (is_tail) append_mandatory_space();- break;- case Complex_Selector::PARENT_OF:- append_optional_space();- append_string(">");- append_optional_space();- break;- case Complex_Selector::ADJACENT_TO:- append_optional_space();- append_string("+");- append_optional_space();- break;- case Complex_Selector::REFERENCE:- append_mandatory_space();- append_string("/");- if (c->reference()) c->reference()->perform(this);- append_string("/");- append_mandatory_space();- break;- case Complex_Selector::PRECEDES:- if (is_empty) append_optional_space();- else append_mandatory_space();- append_string("~");- if (tail) append_mandatory_space();- else append_optional_space();- break;- default: break;- }- if (tail && comb != Complex_Selector::ANCESTOR_OF) {- if (c->has_line_break()) append_optional_linefeed();- }- if (tail) tail->perform(this);- if (!tail && c->has_line_break()) {- if (output_style() == COMPACT) {- append_mandatory_space();+ append_token(s->ns_name(), s);+ if (s->selector() || s->argument()) {+ bool was = in_wrapped;+ in_wrapped = true;+ append_string("(");+ if (s->argument()) {+ s->argument()->perform(this);+ }+ if (s->selector() && s->argument()) {+ append_mandatory_space();+ }+ bool was_comma_array = in_comma_array;+ in_comma_array = false;+ if (s->selector()) {+ s->selector()->perform(this);+ }+ in_comma_array = was_comma_array;+ append_string(")");+ in_wrapped = was; } } } - void Inspect::operator()(Selector_List_Ptr g)+ void Inspect::operator()(SelectorList* g) { if (g->empty()) {@@ -1096,7 +1020,7 @@ // probably ruby sass eqivalent of element_needs_parens if (output_style() == TO_SASS && g->length() == 1 && (!Cast<List>((*g)[0]) &&- !Cast<Selector_List>((*g)[0]))) {+ !Cast<SelectorList>((*g)[0]))) { append_string("("); } else if (!in_declaration && in_comma_array) {@@ -1106,8 +1030,9 @@ if (in_declaration) in_comma_array = true; for (size_t i = 0, L = g->length(); i < L; ++i) {+ if (!in_wrapped && i == 0) append_indentation();- if ((*g)[i] == 0) continue;+ if ((*g)[i] == nullptr) continue; schedule_mapping(g->at(i)->last()); // add_open_mapping((*g)[i]->last()); (*g)[i]->perform(this);@@ -1122,7 +1047,7 @@ // probably ruby sass eqivalent of element_needs_parens if (output_style() == TO_SASS && g->length() == 1 && (!Cast<List>((*g)[0]) &&- !Cast<Selector_List>((*g)[0]))) {+ !Cast<SelectorList>((*g)[0]))) { append_string(",)"); } else if (!in_declaration && in_comma_array) {@@ -1130,9 +1055,70 @@ } }+ void Inspect::operator()(ComplexSelector* sel)+ {+ if (sel->hasPreLineFeed()) {+ append_optional_linefeed();+ if (!in_wrapped && output_style() == NESTED) {+ append_indentation();+ }+ }+ const SelectorComponent* prev = nullptr;+ for (auto& item : sel->elements()) {+ if (prev != nullptr) {+ if (typeid(*item) == typeid(SelectorCombinator) ||+ typeid(*prev) == typeid(SelectorCombinator)) {+ append_optional_space();+ } else {+ append_mandatory_space();+ }+ }+ item->perform(this);+ prev = item.ptr();+ }+ } - void Inspect::fallback_impl(AST_Node_Ptr n)+ void Inspect::operator()(SelectorComponent* sel) {+ // You should probably never call this method directly+ // But in case anyone does, we will do the upcasting+ if (auto comp = Cast<CompoundSelector>(sel)) operator()(comp);+ if (auto comb = Cast<SelectorCombinator>(sel)) operator()(comb);+ }++ void Inspect::operator()(CompoundSelector* sel)+ {+ if (sel->hasRealParent()) {+ append_string("&");+ }+ for (auto& item : sel->elements()) {+ item->perform(this);+ }+ // Add the post line break (from ruby sass)+ // Dart sass uses another logic for newlines+ if (sel->hasPostLineBreak()) {+ if (output_style() != COMPACT) {+ append_optional_linefeed();+ }+ }+ }++ void Inspect::operator()(SelectorCombinator* sel)+ {+ append_optional_space();+ switch (sel->combinator()) {+ case SelectorCombinator::Combinator::CHILD: append_string(">"); break;+ case SelectorCombinator::Combinator::GENERAL: append_string("~"); break;+ case SelectorCombinator::Combinator::ADJACENT: append_string("+"); break;+ }+ append_optional_space();+ // Add the post line break (from ruby sass)+ // Dart sass uses another logic for newlines+ if (sel->hasPostLineBreak()) {+ if (output_style() != COMPACT) {+ // append_optional_linefeed();+ }+ } } }
libsass/src/inspect.hpp view
@@ -13,90 +13,88 @@ // import all the class-specific methods and override as desired using Operation_CRTP<void, Inspect>::operator(); - void fallback_impl(AST_Node_Ptr n);- public: Inspect(const Emitter& emi); virtual ~Inspect(); // statements- virtual void operator()(Block_Ptr);- virtual void operator()(Ruleset_Ptr);- virtual void operator()(Bubble_Ptr);- virtual void operator()(Supports_Block_Ptr);- virtual void operator()(Media_Block_Ptr);- virtual void operator()(At_Root_Block_Ptr);- virtual void operator()(Directive_Ptr);- virtual void operator()(Keyframe_Rule_Ptr);- virtual void operator()(Declaration_Ptr);- virtual void operator()(Assignment_Ptr);- virtual void operator()(Import_Ptr);- virtual void operator()(Import_Stub_Ptr);- virtual void operator()(Warning_Ptr);- virtual void operator()(Error_Ptr);- virtual void operator()(Debug_Ptr);- virtual void operator()(Comment_Ptr);- virtual void operator()(If_Ptr);- virtual void operator()(For_Ptr);- virtual void operator()(Each_Ptr);- virtual void operator()(While_Ptr);- virtual void operator()(Return_Ptr);- virtual void operator()(Extension_Ptr);- virtual void operator()(Definition_Ptr);- virtual void operator()(Mixin_Call_Ptr);- virtual void operator()(Content_Ptr);+ virtual void operator()(Block*);+ virtual void operator()(Ruleset*);+ virtual void operator()(Bubble*);+ virtual void operator()(Supports_Block*);+ virtual void operator()(At_Root_Block*);+ virtual void operator()(Directive*);+ virtual void operator()(Keyframe_Rule*);+ virtual void operator()(Declaration*);+ virtual void operator()(Assignment*);+ virtual void operator()(Import*);+ virtual void operator()(Import_Stub*);+ virtual void operator()(Warning*);+ virtual void operator()(Error*);+ virtual void operator()(Debug*);+ virtual void operator()(Comment*);+ virtual void operator()(If*);+ virtual void operator()(For*);+ virtual void operator()(Each*);+ virtual void operator()(While*);+ virtual void operator()(Return*);+ virtual void operator()(ExtendRule*);+ virtual void operator()(Definition*);+ virtual void operator()(Mixin_Call*);+ virtual void operator()(Content*); // expressions- virtual void operator()(Map_Ptr);- virtual void operator()(Function_Ptr);- virtual void operator()(List_Ptr);- virtual void operator()(Binary_Expression_Ptr);- virtual void operator()(Unary_Expression_Ptr);- virtual void operator()(Function_Call_Ptr);- virtual void operator()(Function_Call_Schema_Ptr);- // virtual void operator()(Custom_Warning_Ptr);- // virtual void operator()(Custom_Error_Ptr);- virtual void operator()(Variable_Ptr);- virtual void operator()(Number_Ptr);- virtual void operator()(Color_Ptr);- virtual void operator()(Boolean_Ptr);- virtual void operator()(String_Schema_Ptr);- virtual void operator()(String_Constant_Ptr);- virtual void operator()(String_Quoted_Ptr);- virtual void operator()(Custom_Error_Ptr);- virtual void operator()(Custom_Warning_Ptr);- virtual void operator()(Supports_Operator_Ptr);- virtual void operator()(Supports_Negation_Ptr);- virtual void operator()(Supports_Declaration_Ptr);- virtual void operator()(Supports_Interpolation_Ptr);- virtual void operator()(Media_Query_Ptr);- virtual void operator()(Media_Query_Expression_Ptr);- virtual void operator()(At_Root_Query_Ptr);- virtual void operator()(Null_Ptr);- virtual void operator()(Parent_Selector_Ptr p);+ virtual void operator()(Map*);+ virtual void operator()(Function*);+ virtual void operator()(List*);+ virtual void operator()(Binary_Expression*);+ virtual void operator()(Unary_Expression*);+ virtual void operator()(Function_Call*);+ // virtual void operator()(Custom_Warning*);+ // virtual void operator()(Custom_Error*);+ virtual void operator()(Variable*);+ virtual void operator()(Number*);+ virtual void operator()(Color_RGBA*);+ virtual void operator()(Color_HSLA*);+ virtual void operator()(Boolean*);+ virtual void operator()(String_Schema*);+ virtual void operator()(String_Constant*);+ virtual void operator()(String_Quoted*);+ virtual void operator()(Custom_Error*);+ virtual void operator()(Custom_Warning*);+ virtual void operator()(Supports_Operator*);+ virtual void operator()(Supports_Negation*);+ virtual void operator()(Supports_Declaration*);+ virtual void operator()(Supports_Interpolation*);+ virtual void operator()(MediaRule*);+ virtual void operator()(CssMediaRule*);+ virtual void operator()(CssMediaQuery*);+ virtual void operator()(Media_Query*);+ virtual void operator()(Media_Query_Expression*);+ virtual void operator()(At_Root_Query*);+ virtual void operator()(Null*);+ virtual void operator()(Parent_Reference* p); // parameters and arguments- virtual void operator()(Parameter_Ptr);- virtual void operator()(Parameters_Ptr);- virtual void operator()(Argument_Ptr);- virtual void operator()(Arguments_Ptr);+ virtual void operator()(Parameter*);+ virtual void operator()(Parameters*);+ virtual void operator()(Argument*);+ virtual void operator()(Arguments*); // selectors- virtual void operator()(Selector_Schema_Ptr);- virtual void operator()(Placeholder_Selector_Ptr);- virtual void operator()(Element_Selector_Ptr);- virtual void operator()(Class_Selector_Ptr);- virtual void operator()(Id_Selector_Ptr);- virtual void operator()(Attribute_Selector_Ptr);- virtual void operator()(Pseudo_Selector_Ptr);- virtual void operator()(Wrapped_Selector_Ptr);- virtual void operator()(Compound_Selector_Ptr);- virtual void operator()(Complex_Selector_Ptr);- virtual void operator()(Selector_List_Ptr);-- virtual std::string lbracket(List_Ptr);- virtual std::string rbracket(List_Ptr);+ virtual void operator()(Selector_Schema*);+ virtual void operator()(Placeholder_Selector*);+ virtual void operator()(Type_Selector*);+ virtual void operator()(Class_Selector*);+ virtual void operator()(Id_Selector*);+ virtual void operator()(Attribute_Selector*);+ virtual void operator()(Pseudo_Selector*);+ virtual void operator()(SelectorComponent*);+ virtual void operator()(SelectorCombinator*);+ virtual void operator()(CompoundSelector*);+ virtual void operator()(ComplexSelector*);+ virtual void operator()(SelectorList*);+ virtual std::string lbracket(List*);+ virtual std::string rbracket(List*); - // template <typename U>- // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node_Ptr>(x)); } }; }
libsass/src/json.cpp view
@@ -402,7 +402,7 @@ try { emit_string(&sb, str); }- catch (std::exception) {+ catch (std::exception&) { sb_free(&sb); throw; }@@ -421,7 +421,7 @@ else emit_value(&sb, node); }- catch (std::exception) {+ catch (std::exception&) { sb_free(&sb); throw; }
libsass/src/lexer.cpp view
@@ -1,9 +1,12 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <cctype>+ #include <iostream> #include <iomanip> #include "lexer.hpp" #include "constants.hpp"+#include "util_string.hpp" namespace Sass {@@ -25,77 +28,14 @@ const char* kwd_minus(const char* src) { return exactly<'-'>(src); }; const char* kwd_slash(const char* src) { return exactly<'/'>(src); }; - //####################################- // implement some function that do exist in the standard- // but those are locale aware which brought some trouble- // this even seems to improve performance by quite a bit- //####################################-- bool is_alpha(const char& chr)- {- return unsigned(chr - 'A') <= 'Z' - 'A' ||- unsigned(chr - 'a') <= 'z' - 'a';- }-- bool is_space(const char& chr)- {- // adapted the technique from is_alpha- return chr == ' ' || unsigned(chr - '\t') <= '\r' - '\t';- }-- bool is_digit(const char& chr)- {- // adapted the technique from is_alpha- return unsigned(chr - '0') <= '9' - '0';- }-- bool is_number(const char& chr)- {- // adapted the technique from is_alpha- return is_digit(chr) || chr == '-' || chr == '+';- }-- bool is_xdigit(const char& chr)- {- // adapted the technique from is_alpha- return unsigned(chr - '0') <= '9' - '0' ||- unsigned(chr - 'a') <= 'f' - 'a' ||- unsigned(chr - 'A') <= 'F' - 'A';- }-- bool is_punct(const char& chr)- {- // locale independent- return chr == '.';- }-- bool is_alnum(const char& chr)- {- return is_alpha(chr) || is_digit(chr);- }-- // check if char is outside ascii range- bool is_unicode(const char& chr)- {- // check for unicode range- return unsigned(chr) > 127;- }-- // check if char is outside ascii range- // but with specific ranges (copied from Ruby Sass)- bool is_nonascii(const char& chr)- {- unsigned int cmp = unsigned(chr);- return (- (cmp >= 128 && cmp <= 15572911) ||- (cmp >= 15630464 && cmp <= 15712189) ||- (cmp >= 4036001920)- );+ bool is_number(char chr) {+ return Util::ascii_isdigit(static_cast<unsigned char>(chr)) ||+ chr == '-' || chr == '+'; } // check if char is within a reduced ascii range // valid in a uri (copied from Ruby Sass)- bool is_uri_character(const char& chr)+ bool is_uri_character(char chr) { unsigned int cmp = unsigned(chr); return (cmp > 41 && cmp < 127) ||@@ -104,17 +44,19 @@ // check if char is within a reduced ascii range // valid for escaping (copied from Ruby Sass)- bool is_escapable_character(const char& chr)+ bool is_escapable_character(char chr) { unsigned int cmp = unsigned(chr); return cmp > 31 && cmp < 127; } // Match word character (look ahead)- bool is_character(const char& chr)+ bool is_character(char chr) { // valid alpha, numeric or unicode char (plus hyphen)- return is_alnum(chr) || is_unicode(chr) || chr == '-';+ return Util::ascii_isalnum(static_cast<unsigned char>(chr)) ||+ !Util::ascii_isascii(static_cast<unsigned char>(chr)) ||+ chr == '-'; } //####################################@@ -122,16 +64,13 @@ //#################################### // create matchers that advance the position- const char* space(const char* src) { return is_space(*src) ? src + 1 : 0; }- const char* alpha(const char* src) { return is_alpha(*src) ? src + 1 : 0; }- const char* unicode(const char* src) { return is_unicode(*src) ? src + 1 : 0; }- const char* nonascii(const char* src) { return is_nonascii(*src) ? src + 1 : 0; }- const char* digit(const char* src) { return is_digit(*src) ? src + 1 : 0; }- const char* xdigit(const char* src) { return is_xdigit(*src) ? src + 1 : 0; }- const char* alnum(const char* src) { return is_alnum(*src) ? src + 1 : 0; }- const char* punct(const char* src) { return is_punct(*src) ? src + 1 : 0; }- const char* hyphen(const char* src) { return *src && *src == '-' ? src + 1 : 0; }- const char* character(const char* src) { return is_character(*src) ? src + 1 : 0; }+ const char* space(const char* src) { return Util::ascii_isspace(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+ const char* alpha(const char* src) { return Util::ascii_isalpha(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+ const char* nonascii(const char* src) { return Util::ascii_isascii(static_cast<unsigned char>(*src)) ? nullptr : src + 1; }+ const char* digit(const char* src) { return Util::ascii_isdigit(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+ const char* xdigit(const char* src) { return Util::ascii_isxdigit(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+ const char* alnum(const char* src) { return Util::ascii_isalnum(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }+ const char* hyphen(const char* src) { return *src == '-' ? src + 1 : 0; } const char* uri_character(const char* src) { return is_uri_character(*src) ? src + 1 : 0; } const char* escapable_character(const char* src) { return is_escapable_character(*src) ? src + 1 : 0; } @@ -150,11 +89,11 @@ // Match word boundary (zero-width lookahead). const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; } - // Match linefeed /(?:\n|\r\n?)/+ // Match linefeed /(?:\n|\r\n?|\f)/ const char* re_linebreak(const char* src) { // end of file or unix linefeed return here- if (*src == 0 || *src == '\n') return src + 1;+ if (*src == 0 || *src == '\n' || *src == '\f') return src + 1; // a carriage return may optionally be followed by a linefeed if (*src == '\r') return *(src + 1) == '\n' ? src + 2 : src + 1; // no linefeed@@ -166,7 +105,7 @@ const char* end_of_line(const char* src) { // end of file or unix linefeed return here- return *src == 0 || *src == '\n' || *src == '\r' ? src : 0;+ return *src == 0 || *src == '\n' || *src == '\r' || *src == '\f' ? src : 0; } // Assert end_of_file boundary (/\z/)
libsass/src/lexer.hpp view
@@ -24,31 +24,20 @@ // BASIC CLASS MATCHERS //#################################### - // These are locale independant- bool is_space(const char& src);- bool is_alpha(const char& src);- bool is_punct(const char& src);- bool is_digit(const char& src);- bool is_number(const char& src);- bool is_alnum(const char& src);- bool is_xdigit(const char& src);- bool is_unicode(const char& src);- bool is_nonascii(const char& src);- bool is_character(const char& src);- bool is_uri_character(const char& src);- bool escapable_character(const char& src);+ // Matches ASCII digits, +, and -.+ bool is_number(char src); + bool is_uri_character(char src);+ bool escapable_character(char src);+ // Match a single ctype predicate. const char* space(const char* src); const char* alpha(const char* src); const char* digit(const char* src); const char* xdigit(const char* src); const char* alnum(const char* src);- const char* punct(const char* src); const char* hyphen(const char* src);- const char* unicode(const char* src); const char* nonascii(const char* src);- const char* character(const char* src); const char* uri_character(const char* src); const char* escapable_character(const char* src);
libsass/src/listize.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include <typeinfo> #include <string>@@ -13,8 +16,14 @@ Listize::Listize() { } - Expression_Ptr Listize::operator()(Selector_List_Ptr sel)+ Expression* Listize::perform(AST_Node* node) {+ Listize listize;+ return node->perform(&listize);+ }++ Expression* Listize::operator()(SelectorList* sel)+ { List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); l->from_selector(true); for (size_t i = 0, L = sel->length(); i < L; ++i) {@@ -25,62 +34,37 @@ return SASS_MEMORY_NEW(Null, l->pstate()); } - Expression_Ptr Listize::operator()(Compound_Selector_Ptr sel)+ Expression* Listize::operator()(CompoundSelector* sel) { std::string str; for (size_t i = 0, L = sel->length(); i < L; ++i) {- Expression_Ptr e = (*sel)[i]->perform(this);+ Expression* e = (*sel)[i]->perform(this); if (e) str += e->to_string(); } return SASS_MEMORY_NEW(String_Quoted, sel->pstate(), str); } - Expression_Ptr Listize::operator()(Complex_Selector_Ptr sel)+ Expression* Listize::operator()(ComplexSelector* sel) {- List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), 2);+ List_Obj l = SASS_MEMORY_NEW(List, sel->pstate());+ // ToDo: investigate what this does+ // Note: seems reated to parent ref l->from_selector(true);- Compound_Selector_Obj head = sel->head();- if (head && !head->is_empty_reference())- {- Expression_Ptr hh = head->perform(this);- if (hh) l->append(hh);- } - std::string reference = ! sel->reference() ? ""- : sel->reference()->to_string();- switch(sel->combinator())- {- case Complex_Selector::PARENT_OF:- l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), ">"));- break;- case Complex_Selector::ADJACENT_TO:- l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "+"));- break;- case Complex_Selector::REFERENCE:- l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "/" + reference + "/"));- break;- case Complex_Selector::PRECEDES:- l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "~"));- break;- case Complex_Selector::ANCESTOR_OF:- break;- default: break;+ for (auto component : sel->elements()) {+ if (CompoundSelectorObj compound = Cast<CompoundSelector>(component)) {+ if (!compound->empty()) {+ Expression_Obj hh = compound->perform(this);+ if (hh) l->append(hh);+ }+ }+ else if (component) {+ l->append(SASS_MEMORY_NEW(String_Quoted, component->pstate(), component->to_string()));+ } } - Complex_Selector_Obj tail = sel->tail();- if (tail)- {- Expression_Obj tt = tail->perform(this);- if (List_Ptr ls = Cast<List>(tt))- { l->concat(ls); }- } if (l->length() == 0) return 0; return l.detach();- }-- Expression_Ptr Listize::fallback_impl(AST_Node_Ptr n)- {- return Cast<Expression>(n); } }
libsass/src/listize.hpp view
@@ -1,32 +1,35 @@ #ifndef SASS_LISTIZE_H #define SASS_LISTIZE_H -#include <vector>-#include <iostream>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp" -#include "ast.hpp"-#include "context.hpp"+#include "ast_fwd_decl.hpp" #include "operation.hpp"-#include "environment.hpp" namespace Sass { struct Backtrace; - class Listize : public Operation_CRTP<Expression_Ptr, Listize> {+ class Listize : public Operation_CRTP<Expression*, Listize> { - Expression_Ptr fallback_impl(AST_Node_Ptr n);+ public: + static Expression* perform(AST_Node* node);+ public: Listize(); ~Listize() { } - Expression_Ptr operator()(Selector_List_Ptr);- Expression_Ptr operator()(Complex_Selector_Ptr);- Expression_Ptr operator()(Compound_Selector_Ptr);+ Expression* operator()(SelectorList*);+ Expression* operator()(ComplexSelector*);+ Expression* operator()(CompoundSelector*); + // generic fallback template <typename U>- Expression_Ptr fallback(U x) { return fallback_impl(x); }+ Expression* fallback(U x)+ { return Cast<Expression>(x); } }; }
@@ -18,7 +18,7 @@ std::cerr << "# REPORTING MISSING DEALLOCATIONS #\n"; std::cerr << "###################################\n"; for (SharedObj* var : all) {- if (AST_Node_Ptr ast = dynamic_cast<AST_Node*>(var)) {+ if (AST_Node* ast = dynamic_cast<AST_Node*>(var)) { debug_ast(ast); } else { std::cerr << "LEAKED " << var << "\n";@@ -30,85 +30,4 @@ #endif bool SharedObj::taint = false;-- SharedObj::SharedObj()- : detached(false)- #ifdef DEBUG_SHARED_PTR- , dbg(false)- #endif- {- refcounter = 0;- #ifdef DEBUG_SHARED_PTR- if (taint) all.push_back(this);- #endif- };-- SharedObj::~SharedObj() {- #ifdef DEBUG_SHARED_PTR- if (dbg) std::cerr << "Destruct " << this << "\n";- if(!all.empty()) { // check needed for MSVC (no clue why?)- all.erase(std::remove(all.begin(), all.end(), this), all.end());- }- #endif- };-- void SharedPtr::decRefCount() {- if (node) {- -- node->refcounter;- #ifdef DEBUG_SHARED_PTR- if (node->dbg) std::cerr << "- " << node << " X " << node->refcounter << " (" << this << ") " << "\n";- #endif- if (node->refcounter == 0) {- #ifdef DEBUG_SHARED_PTR- // AST_Node_Ptr ast = dynamic_cast<AST_Node*>(node);- if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";- #endif- if (!node->detached) {- delete(node);- }- }- }- }-- void SharedPtr::incRefCount() {- if (node) {- ++ node->refcounter;- node->detached = false;- #ifdef DEBUG_SHARED_PTR- if (node->dbg) {- std::cerr << "+ " << node << " X " << node->refcounter << " (" << this << ") " << "\n";- }- #endif- }- }-- SharedPtr::~SharedPtr() {- decRefCount();- }--- // the create constructor- SharedPtr::SharedPtr(SharedObj* ptr)- : node(ptr) {- incRefCount();- }- // copy assignment operator- SharedPtr& SharedPtr::operator=(const SharedPtr& rhs) {- void* cur_ptr = (void*) node;- void* rhs_ptr = (void*) rhs.node;- if (cur_ptr == rhs_ptr) {- return *this;- }- decRefCount();- node = rhs.node;- incRefCount();- return *this;- }-- // the copy constructor- SharedPtr::SharedPtr(const SharedPtr& obj)- : node(obj.node) {- incRefCount();- }- }
@@ -3,8 +3,16 @@ #include "sass/base.h" +#include <cstddef>+#include <iostream>+#include <string>+#include <type_traits> #include <vector> +// https://lokiastari.com/blog/2014/12/30/c-plus-plus-by-example-smart-pointer/index.html+// https://lokiastari.com/blog/2015/01/15/c-plus-plus-by-example-smart-pointer-part-ii/index.html+// https://lokiastari.com/blog/2015/01/23/c-plus-plus-by-example-smart-pointer-part-iii/index.html+ namespace Sass { class SharedPtr;@@ -38,169 +46,275 @@ #endif + // SharedObj is the base class for all objects that can be stored as a shared object+ // It adds the reference counter and other values directly to the objects+ // This gives a slight overhead when directly used as a stack object, but has some+ // advantages for our code. It is safe to create two shared pointers from the same+ // objects, as the "control block" is directly attached to it. This would lead+ // to undefined behavior with std::shared_ptr. This also avoids the need to+ // allocate additional control blocks and/or the need to dereference two+ // pointers on each operation. This can be optimized in `std::shared_ptr`+ // too by using `std::make_shared` (where the control block and the actual+ // object are allocated in one continuous memory block via one single call). class SharedObj {- protected:- friend class SharedPtr;- friend class Memory_Manager;+ public:+ SharedObj() : refcount(0), detached(false) {+ #ifdef DEBUG_SHARED_PTR+ if (taint) all.push_back(this);+ #endif+ }+ virtual ~SharedObj() {+ #ifdef DEBUG_SHARED_PTR+ for (size_t i = 0; i < all.size(); i++) {+ if (all[i] == this) {+ all.erase(all.begin() + i);+ break;+ }+ }+ #endif+ }+ #ifdef DEBUG_SHARED_PTR- static std::vector<SharedObj*> all;- std::string file;- size_t line;+ static void dumpMemLeaks();+ SharedObj* trace(std::string file, size_t line) {+ this->file = file;+ this->line = line;+ return this;+ }+ std::string getDbgFile() { return file; }+ size_t getDbgLine() { return line; }+ void setDbg(bool dbg) { this->dbg = dbg; }+ size_t getRefCount() const { return refcount; } #endif- static bool taint;- long refcounter;- // long refcount;++ static void setTaint(bool val) { taint = val; }++ virtual std::string to_string() const = 0;+ protected:+ friend class SharedPtr;+ friend class Memory_Manager;+ size_t refcount; bool detached;+ static bool taint; #ifdef DEBUG_SHARED_PTR- bool dbg;+ std::string file;+ size_t line;+ bool dbg = false;+ static std::vector<SharedObj*> all; #endif- public:- #ifdef DEBUG_SHARED_PTR- static void dumpMemLeaks();- SharedObj* trace(std::string file, size_t line) {- this->file = file;- this->line = line;- return this;+ };++ // SharedPtr is a intermediate (template-less) base class for SharedImpl.+ // ToDo: there should be a way to include this in SharedImpl and to get+ // ToDo: rid of all the static_cast that are now needed in SharedImpl.+ class SharedPtr {+ public:+ SharedPtr() : node(nullptr) {}+ SharedPtr(SharedObj* ptr) : node(ptr) {+ incRefCount();+ }+ SharedPtr(const SharedPtr& obj) : SharedPtr(obj.node) {}+ ~SharedPtr() {+ decRefCount();+ }++ SharedPtr& operator=(SharedObj* other_node) {+ if (node != other_node) {+ decRefCount();+ node = other_node;+ incRefCount();+ } else if (node != nullptr) {+ node->detached = false; }- #endif- SharedObj();- #ifdef DEBUG_SHARED_PTR- std::string getDbgFile() {- return file;+ return *this;+ }++ SharedPtr& operator=(const SharedPtr& obj) {+ return *this = obj.node;+ }++ // Prevents all SharedPtrs from freeing this node until it is assigned to another SharedPtr.+ SharedObj* detach() {+ if (node != nullptr) node->detached = true;+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) {+ std::cerr << "DETACHING NODE\n"; }- size_t getDbgLine() {- return line;+ #endif + return node;+ }++ SharedObj* obj() const { return node; }+ SharedObj* operator->() const { return node; }+ bool isNull() const { return node == nullptr; }+ operator bool() const { return node != nullptr; }++ protected:+ SharedObj* node;+ void decRefCount() {+ if (node == nullptr) return;+ --node->refcount;+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) std::cerr << "- " << node << " X " << node->refcount << " (" << this << ") " << "\n";+ #endif+ if (node->refcount == 0 && !node->detached) {+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) std::cerr << "DELETE NODE " << node << "\n";+ #endif+ delete node; }- void setDbg(bool dbg) {- this->dbg = dbg;+ else if (node->refcount == 0) {+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) std::cerr << "NODE EVAEDED DELETE " << node << "\n";+ #endif }- #endif- static void setTaint(bool val) {- taint = val; }- virtual ~SharedObj();- long getRefCount() {- return refcounter;+ void incRefCount() {+ if (node == nullptr) return;+ node->detached = false;+ ++node->refcount;+ #ifdef DEBUG_SHARED_PTR+ if (node->dbg) std::cerr << "+ " << node << " X " << node->refcount << " (" << this << ") " << "\n";+ #endif } }; + template <class T>+ class SharedImpl : private SharedPtr { - class SharedPtr {- protected:- SharedObj* node;- protected:- void decRefCount();- void incRefCount(); public:- // the empty constructor- SharedPtr()- : node(NULL) {};- // the create constructor- SharedPtr(SharedObj* ptr);- // the copy constructor- SharedPtr(const SharedPtr& obj);- // the move constructor- SharedPtr(SharedPtr&& obj);- // copy assignment operator- SharedPtr& operator=(const SharedPtr& obj);- // move assignment operator- SharedPtr& operator=(SharedPtr&& obj);- // pure virtual destructor- virtual ~SharedPtr() = 0;- public:- SharedObj* obj () const {- return node;- };- SharedObj* operator-> () const {- return node;- };- bool isNull () {- return node == NULL;- };- bool isNull () const {- return node == NULL;- };- SharedObj* detach() const {- if (node) {- node->detached = true;- }- return node;- };- operator bool() const {- return node != NULL;- };+ SharedImpl() : SharedPtr(nullptr) {} - };+ template <class U>+ SharedImpl(U* node) :+ SharedPtr(static_cast<T*>(node)) {} - template < class T >- class SharedImpl : private SharedPtr {- public:- SharedImpl()- : SharedPtr(NULL) {};- SharedImpl(T* node)- : SharedPtr(node) {};- template < class U >- SharedImpl(SharedImpl<U> obj)- : SharedPtr(static_cast<T*>(obj.ptr())) {}- SharedImpl(T&& node)- : SharedPtr(node) {};- SharedImpl(const T& node)- : SharedPtr(node) {};- // the copy constructor- SharedImpl(const SharedImpl<T>& impl)- : SharedPtr(impl.node) {};- // the move constructor- SharedImpl(SharedImpl<T>&& impl)- : SharedPtr(impl.node) {};- // copy assignment operator- SharedImpl<T>& operator=(const SharedImpl<T>& rhs) {- if (node) decRefCount();- node = rhs.node;- incRefCount();- return *this;- }- // move assignment operator- SharedImpl<T>& operator=(SharedImpl<T>&& rhs) {- // don't move our self- if (this != &rhs) {- if (node) decRefCount();- node = std::move(rhs.node);- rhs.node = NULL;- }- return *this;- }- ~SharedImpl() {};- public:- operator T*() const {- return static_cast<T*>(this->obj());- }- operator T&() const {- return *static_cast<T*>(this->obj());+ template <class U>+ SharedImpl(const SharedImpl<U>& impl) :+ SharedImpl(impl.ptr()) {}++ template <class U>+ SharedImpl<T>& operator=(U *rhs) {+ return static_cast<SharedImpl<T>&>(+ SharedPtr::operator=(static_cast<T*>(rhs))); }- T& operator* () const {- return *static_cast<T*>(this->obj());- };- T* operator-> () const {- return static_cast<T*>(this->obj());- };- T* ptr () const {- return static_cast<T*>(this->obj());- };- T* detach() const {- if (this->obj() == NULL) return NULL;- return static_cast<T*>(SharedPtr::detach());++ template <class U>+ SharedImpl<T>& operator=(const SharedImpl<U>& rhs) {+ return static_cast<SharedImpl<T>&>(+ SharedPtr::operator=(static_cast<const SharedImpl<T>&>(rhs))); }- bool isNull() const {- return this->obj() == NULL;++ operator std::string() const {+ if (node) return node->to_string();+ return "null"; }- bool operator<(const T& rhs) const {- return *this->ptr() < rhs;- };- operator bool() const {- return this->obj() != NULL;- };++ using SharedPtr::isNull;+ using SharedPtr::operator bool;+ operator T*() const { return static_cast<T*>(this->obj()); }+ operator T&() const { return *static_cast<T*>(this->obj()); }+ T& operator* () const { return *static_cast<T*>(this->obj()); };+ T* operator-> () const { return static_cast<T*>(this->obj()); };+ T* ptr () const { return static_cast<T*>(this->obj()); };+ T* detach() { return static_cast<T*>(SharedPtr::detach()); }+ }; -}+ // Comparison operators, based on:+ // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp++ template<class T1, class T2>+ bool operator==(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+ return x.ptr() == y.ptr();+ }++ template<class T1, class T2>+ bool operator!=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+ return x.ptr() != y.ptr();+ }++ template<class T1, class T2>+ bool operator<(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+ using CT = typename std::common_type<T1*, T2*>::type;+ return std::less<CT>()(x.get(), y.get());+ }++ template<class T1, class T2>+ bool operator<=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+ return !(y < x);+ }++ template<class T1, class T2>+ bool operator>(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+ return y < x;+ }++ template<class T1, class T2>+ bool operator>=(const SharedImpl<T1>& x, const SharedImpl<T2>& y) {+ return !(x < y);+ }++ template <class T>+ bool operator==(const SharedImpl<T>& x, std::nullptr_t) noexcept {+ return x.isNull();+ }++ template <class T>+ bool operator==(std::nullptr_t, const SharedImpl<T>& x) noexcept {+ return x.isNull();+ }++ template <class T>+ bool operator!=(const SharedImpl<T>& x, std::nullptr_t) noexcept {+ return !x.isNull();+ }++ template <class T>+ bool operator!=(std::nullptr_t, const SharedImpl<T>& x) noexcept {+ return !x.isNull();+ }++ template <class T>+ bool operator<(const SharedImpl<T>& x, std::nullptr_t) {+ return std::less<T*>()(x.get(), nullptr);+ }++ template <class T>+ bool operator<(std::nullptr_t, const SharedImpl<T>& y) {+ return std::less<T*>()(nullptr, y.get());+ }++ template <class T>+ bool operator<=(const SharedImpl<T>& x, std::nullptr_t) {+ return !(nullptr < x);+ }++ template <class T>+ bool operator<=(std::nullptr_t, const SharedImpl<T>& y) {+ return !(y < nullptr);+ }++ template <class T>+ bool operator>(const SharedImpl<T>& x, std::nullptr_t) {+ return nullptr < x;+ }++ template <class T>+ bool operator>(std::nullptr_t, const SharedImpl<T>& y) {+ return y < nullptr;+ }++ template <class T>+ bool operator>=(const SharedImpl<T>& x, std::nullptr_t) {+ return !(x < nullptr);+ }++ template <class T>+ bool operator>=(std::nullptr_t, const SharedImpl<T>& y) {+ return !(nullptr < y);+ }++} // namespace Sass #endif
− libsass/src/node.cpp
@@ -1,319 +0,0 @@-#include "sass.hpp"-#include <vector>--#include "node.hpp"-#include "context.hpp"-#include "parser.hpp"--namespace Sass {--- Node Node::createCombinator(const Complex_Selector::Combinator& combinator) {- NodeDequePtr null;- return Node(COMBINATOR, combinator, NULL /*pSelector*/, null /*pCollection*/);- }--- Node Node::createSelector(const Complex_Selector& pSelector) {- NodeDequePtr null;-- Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(&pSelector);- pStripped->tail(NULL);- pStripped->combinator(Complex_Selector::ANCESTOR_OF);-- Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/);- n.got_line_feed = pSelector.has_line_feed();- return n;- }--- Node Node::createCollection() {- NodeDequePtr pEmptyCollection = std::make_shared<NodeDeque>();- return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection);- }--- Node Node::createCollection(const NodeDeque& values) {- NodeDequePtr pShallowCopiedCollection = std::make_shared<NodeDeque>(values);- return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pShallowCopiedCollection);- }--- Node Node::createNil() {- NodeDequePtr null;- return Node(NIL, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, null /*pCollection*/);- }--- Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector_Ptr pSelector, NodeDequePtr& pCollection)- : got_line_feed(false), mType(type), mCombinator(combinator), mpSelector(pSelector), mpCollection(pCollection)- { if (pSelector) got_line_feed = pSelector->has_line_feed(); }--- Node Node::klone() const {- NodeDequePtr pNewCollection = std::make_shared<NodeDeque>();- if (mpCollection) {- for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) {- Node& toClone = *iter;- pNewCollection->push_back(toClone.klone());- }- }-- Node n(mType, mCombinator, mpSelector ? SASS_MEMORY_COPY(mpSelector) : NULL, pNewCollection);- n.got_line_feed = got_line_feed;- return n;- }--- bool Node::contains(const Node& potentialChild) const {- bool found = false;-- for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) {- Node& toTest = *iter;-- if (toTest == potentialChild) {- found = true;- break;- }- }-- return found;- }--- bool Node::operator==(const Node& rhs) const {- if (this->type() != rhs.type()) {- return false;- }-- if (this->isCombinator()) {-- return this->combinator() == rhs.combinator();-- } else if (this->isNil()) {-- return true; // no state to check-- } else if (this->isSelector()){-- return *this->selector() == *rhs.selector();-- } else if (this->isCollection()) {-- if (this->collection()->size() != rhs.collection()->size()) {- return false;- }-- for (NodeDeque::iterator lhsIter = this->collection()->begin(), lhsIterEnd = this->collection()->end(),- rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) {-- if (*lhsIter != *rhsIter) {- return false;- }-- }-- return true;-- }-- // We shouldn't get here.- throw "Comparing unknown node types. A new type was probably added and this method wasn't implemented for it.";- }--- void Node::plus(Node& rhs) {- if (!this->isCollection() || !rhs.isCollection()) {- throw "Both the current node and rhs must be collections.";- }- this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end());- }--#ifdef DEBUG- std::ostream& operator<<(std::ostream& os, const Node& node) {-- if (node.isCombinator()) {-- switch (node.combinator()) {- case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;- case Complex_Selector::PARENT_OF: os << "\">\""; break;- case Complex_Selector::PRECEDES: os << "\"~\""; break;- case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;- case Complex_Selector::REFERENCE: os << "\"/\""; break;- }-- } else if (node.isNil()) {-- os << "nil";-- } else if (node.isSelector()){-- os << node.selector()->head()->to_string();-- } else if (node.isCollection()) {-- os << "[";-- for (NodeDeque::iterator iter = node.collection()->begin(), iterBegin = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {- if (iter != iterBegin) {- os << ", ";- }-- os << (*iter);- }-- os << "]";-- }-- return os;-- }-#endif--- Node complexSelectorToNode(Complex_Selector_Ptr pToConvert) {- if (pToConvert == NULL) {- return Node::createNil();- }- Node node = Node::createCollection();- node.got_line_feed = pToConvert->has_line_feed();- bool has_lf = pToConvert->has_line_feed();-- // unwrap the selector from parent ref- if (pToConvert->head() && pToConvert->head()->has_parent_ref()) {- Complex_Selector_Obj tail = pToConvert->tail();- if (tail) tail->has_line_feed(pToConvert->has_line_feed());- pToConvert = tail;- }-- while (pToConvert) {-- bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference();-- // the first Complex_Selector may contain a dummy head pointer, skip it.- if (pToConvert->head() && !empty_parent_ref) {- node.collection()->push_back(Node::createSelector(*pToConvert));- if (has_lf) node.collection()->back().got_line_feed = has_lf;- if (pToConvert->head() || empty_parent_ref) {- if (pToConvert->tail()) {- pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());- }- }- has_lf = false;- }-- if (pToConvert->combinator() != Complex_Selector::ANCESTOR_OF) {- node.collection()->push_back(Node::createCombinator(pToConvert->combinator()));- if (has_lf) node.collection()->back().got_line_feed = has_lf;- has_lf = false;- }-- if (pToConvert && empty_parent_ref && pToConvert->tail()) {- // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());- }-- pToConvert = pToConvert->tail();- }-- return node;- }--- Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert) {- if (toConvert.isNil()) {- return NULL;- }--- if (!toConvert.isCollection()) {- throw "The node to convert to a Complex_Selector_Ptr must be a collection type or nil.";- }--- NodeDeque& childNodes = *toConvert.collection();-- std::string noPath("");- Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL);-- Complex_Selector_Obj pCurrent = pFirst;-- if (toConvert.isSelector()) pFirst->has_line_feed(toConvert.got_line_feed);- if (toConvert.isCombinator()) pFirst->has_line_feed(toConvert.got_line_feed);-- for (NodeDeque::iterator childIter = childNodes.begin(), childIterEnd = childNodes.end(); childIter != childIterEnd; childIter++) {-- Node& child = *childIter;-- if (child.isSelector()) {- // JMA - need to clone the selector, because they can end up getting shared across Node- // collections, and can result in an infinite loop during the call to parentSuperselector()- pCurrent->tail(SASS_MEMORY_COPY(child.selector()));- // if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);- pCurrent = pCurrent->tail();- } else if (child.isCombinator()) {- pCurrent->combinator(child.combinator());- if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);-- // if the next node is also a combinator, create another Complex_Selector to hold it so it doesn't replace the current combinator- if (childIter+1 != childIterEnd) {- Node& nextNode = *(childIter+1);- if (nextNode.isCombinator()) {- pCurrent->tail(SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL));- if (nextNode.got_line_feed) pCurrent->tail()->has_line_feed(nextNode.got_line_feed);- pCurrent = pCurrent->tail();- }- }- } else {- throw "The node to convert's children must be only combinators or selectors.";- }- }-- // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass- Compound_Selector_Ptr fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[NODE]"), 1);- Parent_Selector_Ptr selectorRef = SASS_MEMORY_NEW(Parent_Selector, ParserState("[NODE]"));- fakeHead->elements().push_back(selectorRef);- if (toConvert.got_line_feed) pFirst->has_line_feed(toConvert.got_line_feed);- // pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed);- pFirst->head(fakeHead);- return SASS_MEMORY_COPY(pFirst);- }-- // A very naive trim function, which removes duplicates in a node- // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs- Node Node::naiveTrim(Node& seqses) {-- std::vector<Node*> res;- std::vector<Complex_Selector_Obj> known;-- NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(),- seqsesIterEnd = seqses.collection()->rend();-- for (; seqsesIter != seqsesIterEnd; ++seqsesIter)- {- Node& seqs1 = *seqsesIter;- if( seqs1.isSelector() ) {- Complex_Selector_Obj sel = seqs1.selector();- std::vector<Complex_Selector_Obj>::iterator it;- bool found = false;- for (it = known.begin(); it != known.end(); ++it) {- if (**it == *sel) { found = true; break; }- }- if( !found ) {- known.push_back(seqs1.selector());- res.push_back(&seqs1);- }- } else {- res.push_back(&seqs1);- }- }-- Node result = Node::createCollection();-- for (size_t i = res.size() - 1; i != std::string::npos; --i) {- result.collection()->push_back(*res[i]);- }-- return result;- }-}
− libsass/src/node.hpp
@@ -1,118 +0,0 @@-#ifndef SASS_NODE_H-#define SASS_NODE_H--#include <deque>-#include <memory>--#include "ast.hpp"---namespace Sass {----- class Context;-- /*- There are a lot of stumbling blocks when trying to port the ruby extend code to C++. The biggest is the choice of- data type. The ruby code will pretty seamlessly switch types between an Array<SimpleSequence or Op> (libsass'- equivalent is the Complex_Selector) to a Sequence, which contains more metadata about the sequence than just the- selector info. They also have the ability to have arbitrary nestings of arrays like [1, [2]], which is hard to- implement using Array equivalents in C++ (like the deque or vector). They also have the ability to include nil- in the arrays, like [1, nil, 3], which has potential semantic differences than an empty array [1, [], 3]. To be- able to represent all of these as unique cases, we need to create a tree of variant objects. The tree nature allows- the inconsistent nesting levels. The variant nature (while making some of the C++ code uglier) allows the code to- more closely match the ruby code, which is a huge benefit when attempting to implement an complex algorithm like- the Extend operator.-- Note that the current libsass data model also pairs the combinator with the Complex_Selector that follows it, but- ruby sass has no such restriction, so we attempt to create a data structure that can handle them split apart.- */-- class Node;- typedef std::deque<Node> NodeDeque;- typedef std::shared_ptr<NodeDeque> NodeDequePtr;-- class Node {- public:- enum TYPE {- SELECTOR,- COMBINATOR,- COLLECTION,- NIL- };-- TYPE type() const { return mType; }- bool isCombinator() const { return mType == COMBINATOR; }- bool isSelector() const { return mType == SELECTOR; }- bool isCollection() const { return mType == COLLECTION; }- bool isNil() const { return mType == NIL; }- bool got_line_feed;-- Complex_Selector::Combinator combinator() const { return mCombinator; }-- Complex_Selector_Obj selector() { return mpSelector; }- Complex_Selector_Obj selector() const { return mpSelector; }-- NodeDequePtr collection() { return mpCollection; }- const NodeDequePtr collection() const { return mpCollection; }-- static Node createCombinator(const Complex_Selector::Combinator& combinator);-- // This method will klone the selector, stripping off the tail and combinator- static Node createSelector(const Complex_Selector& pSelector);-- static Node createCollection();- static Node createCollection(const NodeDeque& values);-- static Node createNil();- static Node naiveTrim(Node& seqses);-- Node klone() const;-- bool operator==(const Node& rhs) const;- inline bool operator!=(const Node& rhs) const { return !(*this == rhs); }--- /*- COLLECTION FUNCTIONS-- Most types don't need any helper methods (nil and combinator due to their simplicity and- selector due to the fact that we leverage the non-node selector code on the Complex_Selector- whereever possible). The following methods are intended to be called on Node objects whose- type is COLLECTION only.- */-- // rhs and this must be node collections. Shallow copy the nodes from rhs to the end of this.- // This function DOES NOT remove the nodes from rhs.- void plus(Node& rhs);-- // potentialChild must be a node collection of selectors/combinators. this must be a collection- // of collections of nodes/combinators. This method checks if potentialChild is a child of this- // Node.- bool contains(const Node& potentialChild) const;-- private:- // Private constructor; Use the static methods (like createCombinator and createSelector)- // to instantiate this object. This is more expressive, and it allows us to break apart each- // case into separate functions.- Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector_Ptr pSelector, NodeDequePtr& pCollection);-- TYPE mType;-- // TODO: can we union these to save on memory?- Complex_Selector::Combinator mCombinator;- Complex_Selector_Obj mpSelector;- NodeDequePtr mpCollection;- };--#ifdef DEBUG- std::ostream& operator<<(std::ostream& os, const Node& node);-#endif- Node complexSelectorToNode(Complex_Selector_Ptr pToConvert);- Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert);--}--#endif
libsass/src/operation.hpp view
@@ -1,171 +1,221 @@ #ifndef SASS_OPERATION_H #define SASS_OPERATION_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++// base classes to implement curiously recurring template pattern (CRTP)+// https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern++#include <typeinfo>+#include <stdexcept>+ #include "ast_fwd_decl.hpp"+#include "ast_def_macros.hpp" namespace Sass { + #define ATTACH_ABSTRACT_CRTP_PERFORM_METHODS()\+ virtual void perform(Operation<void>* op) = 0; \+ virtual Value* perform(Operation<Value*>* op) = 0; \+ virtual std::string perform(Operation<std::string>* op) = 0; \+ virtual AST_Node* perform(Operation<AST_Node*>* op) = 0; \+ virtual Selector* perform(Operation<Selector*>* op) = 0; \+ virtual Statement* perform(Operation<Statement*>* op) = 0; \+ virtual Expression* perform(Operation<Expression*>* op) = 0; \+ virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) = 0; \+ virtual Supports_Condition* perform(Operation<Supports_Condition*>* op) = 0; \++ // you must add operators to every class+ // ensures `this` of actual instance type+ // we therefore call the specific operator+ // they are virtual so most specific is used+ #define ATTACH_CRTP_PERFORM_METHODS()\+ virtual void perform(Operation<void>* op) override { return (*op)(this); } \+ virtual Value* perform(Operation<Value*>* op) override { return (*op)(this); } \+ virtual std::string perform(Operation<std::string>* op) override { return (*op)(this); } \+ virtual AST_Node* perform(Operation<AST_Node*>* op) override { return (*op)(this); } \+ virtual Selector* perform(Operation<Selector*>* op) override { return (*op)(this); } \+ virtual Statement* perform(Operation<Statement*>* op) override { return (*op)(this); } \+ virtual Expression* perform(Operation<Expression*>* op) override { return (*op)(this); } \+ virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) override { return (*op)(this); } \+ virtual Supports_Condition* perform(Operation<Supports_Condition*>* op) override { return (*op)(this); } \+ template<typename T> class Operation { public:- virtual T operator()(AST_Node_Ptr x) = 0;- virtual ~Operation() { }+ virtual T operator()(AST_Node* x) = 0; // statements- virtual T operator()(Block_Ptr x) = 0;- virtual T operator()(Ruleset_Ptr x) = 0;- virtual T operator()(Bubble_Ptr x) = 0;- virtual T operator()(Trace_Ptr x) = 0;- virtual T operator()(Supports_Block_Ptr x) = 0;- virtual T operator()(Media_Block_Ptr x) = 0;- virtual T operator()(At_Root_Block_Ptr x) = 0;- virtual T operator()(Directive_Ptr x) = 0;- virtual T operator()(Keyframe_Rule_Ptr x) = 0;- virtual T operator()(Declaration_Ptr x) = 0;- virtual T operator()(Assignment_Ptr x) = 0;- virtual T operator()(Import_Ptr x) = 0;- virtual T operator()(Import_Stub_Ptr x) = 0;- virtual T operator()(Warning_Ptr x) = 0;- virtual T operator()(Error_Ptr x) = 0;- virtual T operator()(Debug_Ptr x) = 0;- virtual T operator()(Comment_Ptr x) = 0;- virtual T operator()(If_Ptr x) = 0;- virtual T operator()(For_Ptr x) = 0;- virtual T operator()(Each_Ptr x) = 0;- virtual T operator()(While_Ptr x) = 0;- virtual T operator()(Return_Ptr x) = 0;- virtual T operator()(Content_Ptr x) = 0;- virtual T operator()(Extension_Ptr x) = 0;- virtual T operator()(Definition_Ptr x) = 0;- virtual T operator()(Mixin_Call_Ptr x) = 0;+ virtual T operator()(Block* x) = 0;+ virtual T operator()(Ruleset* x) = 0;+ virtual T operator()(Bubble* x) = 0;+ virtual T operator()(Trace* x) = 0;+ virtual T operator()(Supports_Block* x) = 0;+ virtual T operator()(MediaRule* x) = 0;+ virtual T operator()(CssMediaRule* x) = 0;+ virtual T operator()(CssMediaQuery* x) = 0;+ virtual T operator()(At_Root_Block* x) = 0;+ virtual T operator()(Directive* x) = 0;+ virtual T operator()(Keyframe_Rule* x) = 0;+ virtual T operator()(Declaration* x) = 0;+ virtual T operator()(Assignment* x) = 0;+ virtual T operator()(Import* x) = 0;+ virtual T operator()(Import_Stub* x) = 0;+ virtual T operator()(Warning* x) = 0;+ virtual T operator()(Error* x) = 0;+ virtual T operator()(Debug* x) = 0;+ virtual T operator()(Comment* x) = 0;+ virtual T operator()(If* x) = 0;+ virtual T operator()(For* x) = 0;+ virtual T operator()(Each* x) = 0;+ virtual T operator()(While* x) = 0;+ virtual T operator()(Return* x) = 0;+ virtual T operator()(Content* x) = 0;+ virtual T operator()(ExtendRule* x) = 0;+ virtual T operator()(Definition* x) = 0;+ virtual T operator()(Mixin_Call* x) = 0; // expressions- virtual T operator()(List_Ptr x) = 0;- virtual T operator()(Map_Ptr x) = 0;- virtual T operator()(Function_Ptr x) = 0;- virtual T operator()(Binary_Expression_Ptr x) = 0;- virtual T operator()(Unary_Expression_Ptr x) = 0;- virtual T operator()(Function_Call_Ptr x) = 0;- virtual T operator()(Function_Call_Schema_Ptr x) = 0;- virtual T operator()(Custom_Warning_Ptr x) = 0;- virtual T operator()(Custom_Error_Ptr x) = 0;- virtual T operator()(Variable_Ptr x) = 0;- virtual T operator()(Number_Ptr x) = 0;- virtual T operator()(Color_Ptr x) = 0;- virtual T operator()(Boolean_Ptr x) = 0;- virtual T operator()(String_Schema_Ptr x) = 0;- virtual T operator()(String_Quoted_Ptr x) = 0;- virtual T operator()(String_Constant_Ptr x) = 0;- virtual T operator()(Supports_Condition_Ptr x) = 0;- virtual T operator()(Supports_Operator_Ptr x) = 0;- virtual T operator()(Supports_Negation_Ptr x) = 0;- virtual T operator()(Supports_Declaration_Ptr x) = 0;- virtual T operator()(Supports_Interpolation_Ptr x) = 0;- virtual T operator()(Media_Query_Ptr x) = 0;- virtual T operator()(Media_Query_Expression_Ptr x) = 0;- virtual T operator()(At_Root_Query_Ptr x) = 0;- virtual T operator()(Null_Ptr x) = 0;- virtual T operator()(Parent_Selector_Ptr x) = 0;+ virtual T operator()(Null* x) = 0;+ virtual T operator()(List* x) = 0;+ virtual T operator()(Map* x) = 0;+ virtual T operator()(Function* x) = 0;+ virtual T operator()(Binary_Expression* x) = 0;+ virtual T operator()(Unary_Expression* x) = 0;+ virtual T operator()(Function_Call* x) = 0;+ virtual T operator()(Custom_Warning* x) = 0;+ virtual T operator()(Custom_Error* x) = 0;+ virtual T operator()(Variable* x) = 0;+ virtual T operator()(Number* x) = 0;+ virtual T operator()(Color* x) = 0;+ virtual T operator()(Color_RGBA* x) = 0;+ virtual T operator()(Color_HSLA* x) = 0;+ virtual T operator()(Boolean* x) = 0;+ virtual T operator()(String_Schema* x) = 0;+ virtual T operator()(String_Quoted* x) = 0;+ virtual T operator()(String_Constant* x) = 0;+ virtual T operator()(Supports_Condition* x) = 0;+ virtual T operator()(Supports_Operator* x) = 0;+ virtual T operator()(Supports_Negation* x) = 0;+ virtual T operator()(Supports_Declaration* x) = 0;+ virtual T operator()(Supports_Interpolation* x) = 0;+ virtual T operator()(Media_Query* x) = 0;+ virtual T operator()(Media_Query_Expression* x) = 0;+ virtual T operator()(At_Root_Query* x) = 0;+ virtual T operator()(Parent_Reference* x) = 0; // parameters and arguments- virtual T operator()(Parameter_Ptr x) = 0;- virtual T operator()(Parameters_Ptr x) = 0;- virtual T operator()(Argument_Ptr x) = 0;- virtual T operator()(Arguments_Ptr x) = 0;+ virtual T operator()(Parameter* x) = 0;+ virtual T operator()(Parameters* x) = 0;+ virtual T operator()(Argument* x) = 0;+ virtual T operator()(Arguments* x) = 0; // selectors- virtual T operator()(Selector_Schema_Ptr x) = 0;- virtual T operator()(Placeholder_Selector_Ptr x) = 0;- virtual T operator()(Element_Selector_Ptr x) = 0;- virtual T operator()(Class_Selector_Ptr x) = 0;- virtual T operator()(Id_Selector_Ptr x) = 0;- virtual T operator()(Attribute_Selector_Ptr x) = 0;- virtual T operator()(Pseudo_Selector_Ptr x) = 0;- virtual T operator()(Wrapped_Selector_Ptr x) = 0;- virtual T operator()(Compound_Selector_Ptr x)= 0;- virtual T operator()(Complex_Selector_Ptr x) = 0;- virtual T operator()(Selector_List_Ptr x) = 0;+ virtual T operator()(Selector_Schema* x) = 0;+ virtual T operator()(Placeholder_Selector* x) = 0;+ virtual T operator()(Type_Selector* x) = 0;+ virtual T operator()(Class_Selector* x) = 0;+ virtual T operator()(Id_Selector* x) = 0;+ virtual T operator()(Attribute_Selector* x) = 0;+ virtual T operator()(Pseudo_Selector* x) = 0;+ virtual T operator()(SelectorComponent* x) = 0;+ virtual T operator()(SelectorCombinator* x) = 0;+ virtual T operator()(CompoundSelector* x) = 0;+ virtual T operator()(ComplexSelector* x) = 0;+ virtual T operator()(SelectorList* x) = 0; - template <typename U>- T fallback(U x) { return T(); } }; + // example: Operation_CRTP<Expression*, Eval>+ // T is the base return type of all visitors+ // D is the class derived visitor class+ // normally you want to implement all operators template <typename T, typename D> class Operation_CRTP : public Operation<T> { public:- D& impl() { return static_cast<D&>(*this); }- public:- T operator()(AST_Node_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(AST_Node* x) { return static_cast<D*>(this)->fallback(x); } // statements- T operator()(Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Ruleset_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Bubble_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Trace_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Supports_Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Media_Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(At_Root_Block_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Directive_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Keyframe_Rule_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Declaration_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Assignment_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Import_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Import_Stub_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Warning_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Error_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Debug_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Comment_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(If_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(For_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Each_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(While_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Return_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Content_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Extension_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Definition_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Mixin_Call_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Block* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Ruleset* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Bubble* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Trace* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Block* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(MediaRule* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(CssMediaRule* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(CssMediaQuery* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(At_Root_Block* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Directive* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Keyframe_Rule* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Declaration* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Assignment* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Import* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Import_Stub* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Warning* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Error* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Debug* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Comment* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(If* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(For* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Each* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(While* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Return* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Content* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(ExtendRule* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Definition* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Mixin_Call* x) { return static_cast<D*>(this)->fallback(x); } // expressions- T operator()(List_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Map_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Function_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Binary_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Unary_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Function_Call_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Function_Call_Schema_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Custom_Warning_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Custom_Error_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Variable_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Number_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Color_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Boolean_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(String_Schema_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(String_Constant_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(String_Quoted_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Supports_Condition_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Supports_Operator_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Supports_Negation_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Supports_Declaration_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Supports_Interpolation_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Media_Query_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Media_Query_Expression_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(At_Root_Query_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Null_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Parent_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Null* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(List* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Map* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Function* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Binary_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Unary_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Function_Call* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Custom_Warning* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Custom_Error* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Variable* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Number* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Color* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Color_RGBA* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Color_HSLA* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Boolean* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(String_Schema* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(String_Constant* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(String_Quoted* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Condition* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Operator* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Negation* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Declaration* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Supports_Interpolation* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Media_Query* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Media_Query_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(At_Root_Query* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Parent_Reference* x) { return static_cast<D*>(this)->fallback(x); } // parameters and arguments- T operator()(Parameter_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Parameters_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Argument_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Arguments_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Parameter* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Parameters* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Argument* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Arguments* x) { return static_cast<D*>(this)->fallback(x); } // selectors- T operator()(Selector_Schema_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Placeholder_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Element_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Class_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Id_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Attribute_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Pseudo_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Wrapped_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Compound_Selector_Ptr x){ return static_cast<D*>(this)->fallback(x); }- T operator()(Complex_Selector_Ptr x) { return static_cast<D*>(this)->fallback(x); }- T operator()(Selector_List_Ptr x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Selector_Schema* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Placeholder_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Type_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Class_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Id_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Attribute_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(Pseudo_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(SelectorComponent* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(SelectorCombinator* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(CompoundSelector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(ComplexSelector* x) { return static_cast<D*>(this)->fallback(x); }+ T operator()(SelectorList* x) { return static_cast<D*>(this)->fallback(x); } - template <typename U>- T fallback(U x) { return T(); }+ // fallback with specific type U+ // will be called if not overloaded+ template <typename U> inline T fallback(U x)+ {+ throw std::runtime_error(+ std::string(typeid(*this).name()) + ": CRTP not implemented for " + typeid(x).name());+ }+ }; }
libsass/src/operators.cpp view
@@ -1,4 +1,8 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"++#include <cmath> #include "operators.hpp" namespace Sass {@@ -57,26 +61,21 @@ /* colour math deprecation warning */ void op_color_deprecation(enum Sass_OP op, std::string lsh, std::string rhs, const ParserState& pstate) {- std::string op_str(- op == Sass_OP::ADD ? "plus" :- op == Sass_OP::DIV ? "div" :- op == Sass_OP::SUB ? "minus" :- op == Sass_OP::MUL ? "times" : ""- );-- std::string msg("The operation `" + lsh + " " + op_str + " " + rhs + "` is deprecated and will be an error in future versions.");- std::string tail("Consider using Sass's color functions instead.\nhttp://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions");-- deprecated(msg, tail, false, pstate);+ deprecated(+ "The operation `" + lsh + " " + sass_op_to_name(op) + " " + rhs ++ "` is deprecated and will be an error in future versions.",+ "Consider using Sass's color functions instead.\n"+ "https://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions",+ /*with_column=*/false, pstate); } /* static function, throws OperationError, has no traces but optional pstate for returned value */- Value_Ptr op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ Value* op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed) { enum Sass_OP op = operand.operand; - String_Quoted_Ptr lqstr = Cast<String_Quoted>(&lhs);- String_Quoted_Ptr rqstr = Cast<String_Quoted>(&rhs);+ String_Quoted* lqstr = Cast<String_Quoted>(&lhs);+ String_Quoted* rqstr = Cast<String_Quoted>(&rhs); std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt)); std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));@@ -120,8 +119,9 @@ return SASS_MEMORY_NEW(String_Constant, pstate, lstr + sep + rstr); } + /* ToDo: allow to operate also with hsla colors */ /* static function, throws OperationError, has no traces but optional pstate for returned value */- Value_Ptr op_colors(enum Sass_OP op, const Color& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ Value* op_colors(enum Sass_OP op, const Color_RGBA& lhs, const Color_RGBA& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed) { if (lhs.a() != rhs.a()) {@@ -133,7 +133,7 @@ op_color_deprecation(op, lhs.to_string(), rhs.to_string(), pstate); - return SASS_MEMORY_NEW(Color,+ return SASS_MEMORY_NEW(Color_RGBA, pstate, ops[op](lhs.r(), rhs.r()), ops[op](lhs.g(), rhs.g()),@@ -142,7 +142,7 @@ } /* static function, throws OperationError, has no traces but optional pstate for returned value */- Value_Ptr op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ Value* op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed) { double lval = lhs.value(); double rval = rhs.value();@@ -165,7 +165,7 @@ if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) { if (lhs.numerators == rhs.numerators) { if (lhs.denominators == rhs.denominators) {- Number_Ptr v = SASS_MEMORY_COPY(&lhs);+ Number* v = SASS_MEMORY_COPY(&lhs); v->value(ops[op](lval, rval)); return v; }@@ -212,7 +212,7 @@ } /* static function, throws OperationError, has no traces but optional pstate for returned value */- Value_Ptr op_number_color(enum Sass_OP op, const Number& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ Value* op_number_color(enum Sass_OP op, const Number& lhs, const Color_RGBA& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed) { double lval = lhs.value(); @@ -220,7 +220,7 @@ case Sass_OP::ADD: case Sass_OP::MUL: { op_color_deprecation(op, lhs.to_string(), rhs.to_string(opt), pstate);- return SASS_MEMORY_NEW(Color,+ return SASS_MEMORY_NEW(Color_RGBA, pstate, ops[op](lval, rhs.r()), ops[op](lval, rhs.g()),@@ -243,7 +243,7 @@ } /* static function, throws OperationError, has no traces but optional pstate for returned value */- Value_Ptr op_color_number(enum Sass_OP op, const Color& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)+ Value* op_color_number(enum Sass_OP op, const Color_RGBA& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed) { double rval = rhs.value(); @@ -254,7 +254,7 @@ op_color_deprecation(op, lhs.to_string(), rhs.to_string(), pstate); - return SASS_MEMORY_NEW(Color,+ return SASS_MEMORY_NEW(Color_RGBA, pstate, ops[op](lhs.r(), rval), ops[op](lhs.g(), rval),
libsass/src/operators.hpp view
@@ -17,11 +17,11 @@ bool lte(Expression_Obj, Expression_Obj); bool gte(Expression_Obj, Expression_Obj); // arithmetic for all the combinations that matter- Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);- Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);- Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);- Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);- Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value* op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value* op_colors(enum Sass_OP, const Color_RGBA&, const Color_RGBA&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value* op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value* op_number_color(enum Sass_OP, const Number&, const Color_RGBA&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);+ Value* op_color_number(enum Sass_OP, const Color_RGBA&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false); };
+ libsass/src/ordered_map.hpp view
@@ -0,0 +1,112 @@+#ifndef SASS_ORDERED_MAP_H+#define SASS_ORDERED_MAP_H++namespace Sass {++ // ##########################################################################+ // Very simple and limited container for insert ordered hash map.+ // Piggy-back implementation on std::unordered_map and std::vector+ // ##########################################################################+ template<+ class Key,+ class T,+ class Hash = std::hash<Key>,+ class KeyEqual = std::equal_to<Key>,+ class Allocator = std::allocator<std::pair<const Key, T>>+ >+ class ordered_map {++ private:++ using map_type = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>;+ using map_iterator = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>::iterator;+ using map_const_iterator = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>::const_iterator;++ // The main unordered map+ map_type _map;++ // Keep insertion order+ std::vector<Key> _keys;+ std::vector<T> _values;++ const KeyEqual _keyEqual;++ public:++ ordered_map() :+ _keyEqual(KeyEqual())+ {+ }++ ordered_map& operator= (const ordered_map& other) {+ _map = other._map;+ _keys = other._keys;+ _values = other._values;+ return *this;+ }++ std::pair<Key, T> front() {+ return std::make_pair(+ _keys.front(),+ _values.front()+ );+ }++ bool empty() const {+ return _keys.empty();+ }++ void insert(const Key& key, const T& val) {+ if (!hasKey(key)) {+ _values.push_back(val);+ _keys.push_back(key);+ }+ _map[key] = val;+ }++ bool hasKey(const Key& key) const {+ return _map.find(key) != _map.end();+ }++ bool erase(const Key& key) {+ _map.erase(key);+ // find the position in the array+ for (size_t i = 0; i < _keys.size(); i += 1) {+ if (_keyEqual(key, _keys[i])) {+ _keys.erase(_keys.begin() + i);+ _values.erase(_values.begin() + i);+ return true;+ }+ }+ return false;+ }++ const std::vector<Key>& keys() const { return _keys; }+ const std::vector<T>& values() const { return _values; }++ const T& get(const Key& key) {+ if (hasKey(key)) {+ return _map[key];+ }+ throw std::runtime_error("Key does not exist");+ }++ using iterator = typename std::vector<Key>::iterator;+ using const_iterator = typename std::vector<Key>::const_iterator;+ using reverse_iterator = typename std::vector<Key>::reverse_iterator;+ using const_reverse_iterator = typename std::vector<Key>::const_reverse_iterator;++ typename std::vector<Key>::iterator end() { return _keys.end(); }+ typename std::vector<Key>::iterator begin() { return _keys.begin(); }+ typename std::vector<Key>::reverse_iterator rend() { return _keys.rend(); }+ typename std::vector<Key>::reverse_iterator rbegin() { return _keys.rbegin(); }+ typename std::vector<Key>::const_iterator end() const { return _keys.end(); }+ typename std::vector<Key>::const_iterator begin() const { return _keys.begin(); }+ typename std::vector<Key>::const_reverse_iterator rend() const { return _keys.rend(); }+ typename std::vector<Key>::const_reverse_iterator rbegin() const { return _keys.rbegin(); }++ };++}++#endif
libsass/src/output.cpp view
@@ -1,6 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "output.hpp"+#include "util.hpp" namespace Sass { @@ -12,12 +16,12 @@ Output::~Output() { } - void Output::fallback_impl(AST_Node_Ptr n)+ void Output::fallback_impl(AST_Node* n) { return n->perform(this); } - void Output::operator()(Number_Ptr n)+ void Output::operator()(Number* n) { // check for a valid unit here // includes result for reporting@@ -31,12 +35,12 @@ append_token(res, n); } - void Output::operator()(Import_Ptr imp)+ void Output::operator()(Import* imp) { top_nodes.push_back(imp); } - void Output::operator()(Map_Ptr m)+ void Output::operator()(Map* m) { // should be handle in check_expression throw Exception::InvalidValue({}, *m);@@ -86,9 +90,8 @@ } - void Output::operator()(Comment_Ptr c)+ void Output::operator()(Comment* c) {- std::string txt = c->text()->to_string(opt); // if (indentation && txt == "/**/") return; bool important = c->is_important(); if (output_style() != COMPRESSED || important) {@@ -108,15 +111,17 @@ } } - void Output::operator()(Ruleset_Ptr r)+ void Output::operator()(Ruleset* r) {- Selector_Obj s = r->selector();- Block_Obj b = r->block();+ Block_Obj b = r->block();+ SelectorListObj s = r->selector(); + if (!s || s->empty()) return;+ // Filter out rulesets that aren't printable (process its children though) if (!Util::isPrintable(r, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) {- const Statement_Obj& stm = b->at(i);+ const Statement_Obj& stm = b->get(i); if (Cast<Has_Block>(stm)) { if (!Cast<Declaration>(stm)) { stm->perform(this);@@ -126,7 +131,9 @@ return; } - if (output_style() == NESTED) indentation += r->tabs();+ if (output_style() == NESTED) {+ indentation += r->tabs();+ } if (opt.source_comments) { std::stringstream ss; append_indentation();@@ -139,22 +146,22 @@ if (s) s->perform(this); append_scope_opener(b); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);+ Statement_Obj stm = b->get(i); bool bPrintExpression = true; // Check print conditions- if (Declaration_Ptr dec = Cast<Declaration>(stm)) {- if (String_Constant_Ptr valConst = Cast<String_Constant>(dec->value())) {- std::string val(valConst->value());- if (String_Quoted_Ptr qstr = Cast<String_Quoted>(valConst)) {+ if (Declaration* dec = Cast<Declaration>(stm)) {+ if (const String_Constant* valConst = Cast<String_Constant>(dec->value())) {+ const std::string& val = valConst->value();+ if (const String_Quoted* qstr = Cast<const String_Quoted>(valConst)) { if (!qstr->quote_mark() && val.empty()) { bPrintExpression = false; } } }- else if (List_Ptr list = Cast<List>(dec->value())) {+ else if (List* list = Cast<List>(dec->value())) { bool all_invisible = true; for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {- Expression_Ptr item = list->at(list_i);+ Expression* item = list->get(list_i); if (!item->is_invisible()) all_invisible = false; } if (all_invisible && !list->is_bracketed()) bPrintExpression = false;@@ -169,7 +176,7 @@ append_scope_closer(b); }- void Output::operator()(Keyframe_Rule_Ptr r)+ void Output::operator()(Keyframe_Rule* r) { Block_Obj b = r->block(); Selector_Obj v = r->name();@@ -185,14 +192,14 @@ append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);+ Statement_Obj stm = b->get(i); stm->perform(this); if (i < L - 1) append_special_linefeed(); } append_scope_closer(); } - void Output::operator()(Supports_Block_Ptr f)+ void Output::operator()(Supports_Block* f) { if (f->is_invisible()) return; @@ -202,7 +209,7 @@ // Filter out feature blocks that aren't printable (process its children though) if (!Util::isPrintable(f, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);+ Statement_Obj stm = b->get(i); if (Cast<Has_Block>(stm)) { stm->perform(this); }@@ -218,7 +225,7 @@ append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);+ Statement_Obj stm = b->get(i); stm->perform(this); if (i < L - 1) append_special_linefeed(); }@@ -229,44 +236,24 @@ } - void Output::operator()(Media_Block_Ptr m)+ void Output::operator()(CssMediaRule* rule) {- if (m->is_invisible()) return;-- Block_Obj b = m->block();-- // Filter out media blocks that aren't printable (process its children though)- if (!Util::isPrintable(m, output_style())) {- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);- if (Cast<Has_Block>(stm)) {- stm->perform(this);- }- }- return;- }- if (output_style() == NESTED) indentation += m->tabs();- append_indentation();- append_token("@media", m);- append_mandatory_space();- in_media_block = true;- m->media_queries()->perform(this);- in_media_block = false;- append_scope_opener();-- for (size_t i = 0, L = b->length(); i < L; ++i) {- if (b->at(i)) {- Statement_Obj stm = b->at(i);- stm->perform(this);- }- if (i < L - 1) append_special_linefeed();+ // Avoid null pointer exception+ if (rule == nullptr) return;+ // Skip empty/invisible rule+ if (rule->isInvisible()) return;+ // Avoid null pointer exception+ if (rule->block() == nullptr) return;+ // Skip empty/invisible rule+ if (rule->block()->isInvisible()) return;+ // Skip if block is empty/invisible+ if (Util::isPrintable(rule, output_style())) {+ // Let inspect do its magic+ Inspect::operator()(rule); }-- if (output_style() == NESTED) indentation -= m->tabs();- append_scope_closer(); } - void Output::operator()(Directive_Ptr a)+ void Output::operator()(Directive* a) { std::string kwd = a->keyword(); Selector_Obj s = a->selector();@@ -301,7 +288,7 @@ bool format = kwd != "@font-face";; for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Obj stm = b->at(i);+ Statement_Obj stm = b->get(i); stm->perform(this); if (i < L - 1 && format) append_special_linefeed(); }@@ -309,7 +296,7 @@ append_scope_closer(); } - void Output::operator()(String_Quoted_Ptr s)+ void Output::operator()(String_Quoted* s) { if (s->quote_mark()) { append_token(quote(s->value(), s->quote_mark()), s);@@ -320,12 +307,9 @@ } } - void Output::operator()(String_Constant_Ptr s)+ void Output::operator()(String_Constant* s) { std::string value(s->value());- if (s->can_compress_whitespace() && output_style() == COMPRESSED) {- value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());- } if (!in_comment && !in_custom_property) { append_token(string_to_output(value), s); } else {
libsass/src/output.hpp view
@@ -11,13 +11,6 @@ namespace Sass { class Context; - // Refactor to make it generic to find linefeed (look behind)- inline bool ends_with(std::string const & value, std::string const & ending)- {- if (ending.size() > value.size()) return false;- return std::equal(ending.rbegin(), ending.rend(), value.rbegin());- }- class Output : public Inspect { protected: using Inspect::operator();@@ -28,24 +21,24 @@ protected: std::string charset;- std::vector<AST_Node_Ptr> top_nodes;+ std::vector<AST_Node*> top_nodes; public: OutputBuffer get_buffer(void); - virtual void operator()(Map_Ptr);- virtual void operator()(Ruleset_Ptr);- virtual void operator()(Supports_Block_Ptr);- virtual void operator()(Media_Block_Ptr);- virtual void operator()(Directive_Ptr);- virtual void operator()(Keyframe_Rule_Ptr);- virtual void operator()(Import_Ptr);- virtual void operator()(Comment_Ptr);- virtual void operator()(Number_Ptr);- virtual void operator()(String_Quoted_Ptr);- virtual void operator()(String_Constant_Ptr);+ virtual void operator()(Map*);+ virtual void operator()(Ruleset*);+ virtual void operator()(Supports_Block*);+ virtual void operator()(CssMediaRule*);+ virtual void operator()(Directive*);+ virtual void operator()(Keyframe_Rule*);+ virtual void operator()(Import*);+ virtual void operator()(Comment*);+ virtual void operator()(Number*);+ virtual void operator()(String_Quoted*);+ virtual void operator()(String_Constant*); - void fallback_impl(AST_Node_Ptr n);+ void fallback_impl(AST_Node* n); };
libsass/src/parser.cpp view
@@ -1,13 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "parser.hpp"-#include "file.hpp"-#include "inspect.hpp"-#include "constants.hpp"-#include "util.hpp"-#include "prelexer.hpp" #include "color_maps.hpp"-#include "sass/functions.h"-#include "error_handling.hpp"+#include "util_string.hpp" // Notes about delayed: some ast nodes can have delayed evaluation so // they can preserve their original semantics if needed. This is most@@ -21,20 +18,16 @@ // Another case with delayed values are colors. In compressed mode // only processed values get compressed (other are left as written). -#include <cstdlib>-#include <iostream>-#include <vector>-#include <typeinfo> namespace Sass { using namespace Constants; using namespace Prelexer; - Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+ Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent) { pstate.offset.column = 0; pstate.offset.line = 0;- Parser p(ctx, pstate, traces);+ Parser p(ctx, pstate, traces, allow_parent); p.source = source ? source : beg; p.position = beg ? beg : p.source; p.end = p.position + strlen(p.position);@@ -44,11 +37,11 @@ return p; } - Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+ Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent) { pstate.offset.column = 0; pstate.offset.line = 0;- Parser p(ctx, pstate, traces);+ Parser p(ctx, pstate, traces, allow_parent); p.source = source ? source : beg; p.position = beg ? beg : p.source; p.end = end ? end : p.position + strlen(p.position);@@ -66,12 +59,11 @@ pstate.offset.line = 0; } - Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)+ SelectorListObj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent) {- Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source);- // ToDo: ruby sass errors on parent references+ Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source, allow_parent); // ToDo: remap the source-map entries somehow- return p.parse_selector_list(false);+ return p.parseSelectorList(false); } bool Parser::peek_newline(const char* start)@@ -258,16 +250,23 @@ else if (lex < kwd_extend >(true)) { Lookahead lookahead = lookahead_for_include(position); if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");- Selector_List_Obj target;+ SelectorListObj target; if (!lookahead.has_interpolants) {- target = parse_selector_list(true);+ LOCAL_FLAG(allow_parent, false);+ auto selector = parseSelectorList(true);+ auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector);+ extender->isOptional(selector && selector->is_optional());+ block->append(extender); } else {- target = SASS_MEMORY_NEW(Selector_List, pstate);- target->schema(parse_selector_schema(lookahead.found, true));+ LOCAL_FLAG(allow_parent, false);+ auto selector = parse_selector_schema(lookahead.found, true);+ auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector);+ // A schema is not optional yet, check once it is evaluated+ // extender->isOptional(selector && selector->is_optional());+ block->append(extender); } - block->append(SASS_MEMORY_NEW(Extension, pstate, target)); } // selector may contain interpolations which need delayed evaluation@@ -280,7 +279,7 @@ } // parse multiple specific keyword directives- else if (lex < kwd_media >(true)) { block->append(parse_media_block()); }+ else if (lex < kwd_media >(true)) { block->append(parseMediaRule()); } else if (lex < kwd_at_root >(true)) { block->append(parse_at_root_block()); } else if (lex < kwd_include_directive >(true)) { block->append(parse_include_directive()); } else if (lex < kwd_content_directive >(true)) { block->append(parse_content_directive()); }@@ -291,9 +290,9 @@ // ignore the @charset directive for now else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } + else if (lex < exactly < else_kwd >>(true)) { error("Invalid CSS: @else must come after @if"); }+ // generic at keyword (keep last)- else if (lex< re_special_directive >(true)) { block->append(parse_special_directive()); }- else if (lex< re_prefixed_directive >(true)) { block->append(parse_prefixed_directive()); } else if (lex< at_keyword >(true)) { block->append(parse_directive()); } else if (is_root && stack.back() != Scope::AtRoot /* && block->is_root() */) {@@ -333,11 +332,11 @@ do { while (lex< block_comment >()); if (lex< quoted_string >()) {- to_import.push_back(std::pair<std::string,Function_Call_Obj>(std::string(lexed), 0));+ to_import.push_back(std::pair<std::string,Function_Call_Obj>(std::string(lexed), {})); } else if (lex< uri_prefix >()) { Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);- Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, "url", args);+ Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, std::string("url"), args); if (lex< quoted_string >()) { Expression_Obj quoted_url = parse_string();@@ -479,7 +478,7 @@ bool is_arglist = false; bool is_keyword = false; Expression_Obj val = parse_space_list();- List_Ptr l = Cast<List>(val);+ List* l = Cast<List>(val); if (lex_css< exactly< ellipsis > >()) { if (val->concrete_type() == Expression::MAP || ( (l != NULL && l->separator() == SASS_HASH)@@ -527,10 +526,13 @@ // create the connector object (add parts later) Ruleset_Obj ruleset = SASS_MEMORY_NEW(Ruleset, pstate); // parse selector static or as schema to be evaluated later- if (lookahead.parsable) ruleset->selector(parse_selector_list(false));+ if (lookahead.parsable) {+ ruleset->selector(parseSelectorList(false));+ } else {- Selector_List_Obj list = SASS_MEMORY_NEW(Selector_List, pstate);- list->schema(parse_selector_schema(lookahead.position, false));+ SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate);+ auto sc = parse_selector_schema(lookahead.position, false);+ ruleset->schema(sc); ruleset->selector(list); } // then parse the inner block@@ -556,15 +558,14 @@ lex< optional_spaces >(); const char* i = position; // selector schema re-uses string schema implementation- String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+ String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); // the selector schema is pretty much just a wrapper for the string schema Selector_Schema_Obj selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema); selector_schema->connect_parent(chroot == false);- selector_schema->media_block(last_media_block); // process until end while (i < end_of_selector) {- // try to parse mutliple interpolants+ // try to parse multiple interpolants if (const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, end_of_selector)) { // accumulate the preceding segment if the position has advanced if (i < p) {@@ -645,224 +646,34 @@ // normalize underscores to hyphens std::string name(Util::normalize_underscores(lexed)); // create the initial mixin call object- Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, 0, 0);+ Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, {}, {}, {}); // parse mandatory arguments call->arguments(parse_arguments());+ // parse using and optional block parameters+ bool has_parameters = lex< kwd_using >() != nullptr;++ if (has_parameters) {+ if (!peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \"(\", was ");+ } else {+ if (peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \";\", was ");+ }++ if (has_parameters) call->block_parameters(parse_parameters());+ // parse optional block if (peek < exactly <'{'> >()) { call->block(parse_block()); }+ else if (has_parameters) {+ css_error("Invalid CSS", " after ", ": expected \"{\", was ");+ } // return ast node return call.detach(); } // EO parse_include_directive - // parse a list of complex selectors- // this is the main entry point for most- Selector_List_Obj Parser::parse_selector_list(bool chroot)- {- bool reloop;- bool had_linefeed = false;- NESTING_GUARD(nestings);- Complex_Selector_Obj sel;- Selector_List_Obj group = SASS_MEMORY_NEW(Selector_List, pstate);- group->media_block(last_media_block); - if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) {- css_error("Invalid CSS", " after ", ": expected selector, was ");- }-- do {- reloop = false;-- had_linefeed = had_linefeed || peek_newline();-- if (peek_css< alternatives < class_char < selector_list_delims > > >())- break; // in case there are superfluous commas at the end-- // now parse the complex selector- sel = parse_complex_selector(chroot);-- if (!sel) return group.detach();-- sel->has_line_feed(had_linefeed);-- had_linefeed = false;-- while (peek_css< exactly<','> >())- {- lex< css_comments >(false);- // consume everything up and including the comma separator- reloop = lex< exactly<','> >() != 0;- // remember line break (also between some commas)- had_linefeed = had_linefeed || peek_newline();- // remember line break (also between some commas)- }- group->append(sel);- }- while (reloop);- while (lex_css< kwd_optional >()) {- group->is_optional(true);- }- // update for end position- group->update_pstate(pstate);- if (sel) sel->last()->has_line_break(false);- return group.detach();- }- // EO parse_selector_list-- // a complex selector combines a compound selector with another- // complex selector, with one of four combinator operations.- // the compound selector (head) is optional, since the combinator- // can come first in the whole selector sequence (like `> DIV').- Complex_Selector_Obj Parser::parse_complex_selector(bool chroot)- {-- NESTING_GUARD(nestings);- String_Obj reference = 0;- lex < block_comment >();- advanceToNextToken();- Complex_Selector_Obj sel = SASS_MEMORY_NEW(Complex_Selector, pstate);-- if (peek < end_of_file >()) return 0;-- // parse the left hand side- Compound_Selector_Obj lhs;- // special case if it starts with combinator ([+~>])- if (!peek_css< class_char < selector_combinator_ops > >()) {- // parse the left hand side- lhs = parse_compound_selector();- }--- // parse combinator between lhs and rhs- Complex_Selector::Combinator combinator = Complex_Selector::ANCESTOR_OF;- if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO;- else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES;- else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF;- else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) {- // comments are allowed, but not spaces?- combinator = Complex_Selector::REFERENCE;- if (!lex < re_reference_combinator >()) return 0;- reference = SASS_MEMORY_NEW(String_Constant, pstate, lexed);- if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?- }-- if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;-- // lex < block_comment >();- sel->head(lhs);- sel->combinator(combinator);- sel->media_block(last_media_block);-- if (combinator == Complex_Selector::REFERENCE) sel->reference(reference);- // has linfeed after combinator?- sel->has_line_break(peek_newline());- // sel->has_line_feed(has_line_feed);-- // check if we got the abort condition (ToDo: optimize)- if (!peek_css< class_char < complex_selector_delims > >()) {- // parse next selector in sequence- sel->tail(parse_complex_selector(true));- }-- // add a parent selector if we are not in a root- // also skip adding parent ref if we only have refs- if (!sel->has_parent_ref() && !chroot) {- // create the objects to wrap parent selector reference- Compound_Selector_Obj head = SASS_MEMORY_NEW(Compound_Selector, pstate);- Parent_Selector_Ptr parent = SASS_MEMORY_NEW(Parent_Selector, pstate, false);- parent->media_block(last_media_block);- head->media_block(last_media_block);- // add simple selector- head->append(parent);- // selector may not have any head yet- if (!sel->head()) { sel->head(head); }- // otherwise we need to create a new complex selector and set the old one as its tail- else {- sel = SASS_MEMORY_NEW(Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel);- sel->media_block(last_media_block);- }- // peek for linefeed and remember result on head- // if (peek_newline()) head->has_line_break(true);- }-- sel->update_pstate(pstate);- // complex selector- return sel;- }- // EO parse_complex_selector-- // parse one compound selector, which is basically- // a list of simple selectors (directly adjacent)- // lex them exactly (without skipping white-space)- Compound_Selector_Obj Parser::parse_compound_selector()- {- // init an empty compound selector wrapper- Compound_Selector_Obj seq = SASS_MEMORY_NEW(Compound_Selector, pstate);- seq->media_block(last_media_block);-- // skip initial white-space- lex< css_whitespace >();-- // parse list- while (true)- {- // remove all block comments (don't skip white-space)- lex< delimited_by< slash_star, star_slash, false > >(false);- // parse functional- if (match < re_pseudo_selector >())- {- seq->append(parse_simple_selector());- }- // parse parent selector- else if (lex< exactly<'&'> >(false))- {- // this produces a linefeed!?- seq->has_parent_reference(true);- seq->append(SASS_MEMORY_NEW(Parent_Selector, pstate));- // parent selector only allowed at start- // upcoming Sass may allow also trailing- if (seq->length() > 1) {- ParserState state(pstate);- Simple_Selector_Obj cur = (*seq)[seq->length()-1];- Simple_Selector_Obj prev = (*seq)[seq->length()-2];- std::string sel(prev->to_string({ NESTED, 5 }));- std::string found(cur->to_string({ NESTED, 5 }));- if (lex < identifier >()) { found += std::string(lexed); }- error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n"- "\"" + found + "\" may only be used at the beginning of a compound selector.", state);- }- }- // parse type selector- else if (lex< re_type_selector >(false))- {- seq->append(SASS_MEMORY_NEW(Element_Selector, pstate, lexed));- }- // peek for abort conditions- else if (peek< spaces >()) break;- else if (peek< end_of_file >()) { break; }- else if (peek_css < class_char < selector_combinator_ops > >()) break;- else if (peek_css < class_char < complex_selector_delims > >()) break;- // otherwise parse another simple selector- else {- Simple_Selector_Obj sel = parse_simple_selector();- if (!sel) return 0;- seq->append(sel);- }- }-- if (seq && !peek_css<alternatives<end_of_file,exactly<'{'>>>()) {- seq->has_line_break(peek_newline());- }-- // EO while true- return seq;-- }- // EO parse_compound_selector-- Simple_Selector_Obj Parser::parse_simple_selector()+ SimpleSelectorObj Parser::parse_simple_selector() { lex < css_comments >(false); if (lex< class_name >()) {@@ -872,10 +683,10 @@ return SASS_MEMORY_NEW(Id_Selector, pstate, lexed); } else if (lex< alternatives < variable, number, static_reference_combinator > >()) {- return SASS_MEMORY_NEW(Element_Selector, pstate, lexed);+ return SASS_MEMORY_NEW(Type_Selector, pstate, lexed); } else if (peek< pseudo_not >()) {- return parse_negated_selector();+ return parse_negated_selector2(); } else if (peek< re_pseudo_selector >()) { return parse_pseudo_selector();@@ -887,86 +698,117 @@ return parse_attribute_selector(); } else if (lex< placeholder >()) {- Placeholder_Selector_Ptr sel = SASS_MEMORY_NEW(Placeholder_Selector, pstate, lexed);- sel->media_block(last_media_block);- return sel;+ return SASS_MEMORY_NEW(Placeholder_Selector, pstate, lexed); } else { css_error("Invalid CSS", " after ", ": expected selector, was "); } // failed- return 0;+ return {}; } - Wrapped_Selector_Obj Parser::parse_negated_selector()+ Pseudo_Selector_Obj Parser::parse_negated_selector2() { lex< pseudo_not >(); std::string name(lexed); ParserState nsource_position = pstate;- Selector_List_Obj negated = parse_selector_list(true);+ SelectorListObj negated = parseSelectorList(true); if (!lex< exactly<')'> >()) { error("negated selector is missing ')'"); } name.erase(name.size() - 1);- return SASS_MEMORY_NEW(Wrapped_Selector, nsource_position, name, negated);++ Pseudo_Selector* sel = SASS_MEMORY_NEW(Pseudo_Selector, nsource_position, name.substr(1));+ sel->selector(negated);+ return sel; } + // Helper to clean binominal string+ bool BothAreSpaces(char lhs, char rhs) { return isspace(lhs) && isspace(rhs); }+ // a pseudo selector often starts with one or two colons // it can contain more selectors inside parentheses- Simple_Selector_Obj Parser::parse_pseudo_selector() {- if (lex< sequence<- optional < pseudo_prefix >,- // we keep the space within the name, strange enough- // ToDo: refactor output to schedule the space for it- // or do we really want to keep the real white-space?- sequence< identifier, optional < block_comment >, exactly<'('> >- > >())- {+ SimpleSelectorObj Parser::parse_pseudo_selector() { - std::string name(lexed);- name.erase(name.size() - 1);- ParserState p = pstate;+ // Lex one or two colon characters+ if (lex<pseudo_prefix>()) {+ std::string colons(lexed);+ // Check if it is a pseudo element+ bool element = colons.size() == 2; - // specially parse static stuff- // ToDo: really everything static?- if (peek_css <- sequence <- alternatives <- static_value,- binomial- >,- optional_css_whitespace,- exactly<')'>- >- >()- ) {- lex_css< alternatives < static_value, binomial > >();- String_Constant_Obj expr = SASS_MEMORY_NEW(String_Constant, pstate, lexed);- if (lex_css< exactly<')'> >()) {- expr->can_compress_whitespace(true);- return SASS_MEMORY_NEW(Pseudo_Selector, p, name, expr);+ if (lex< sequence<+ // we keep the space within the name, strange enough+ // ToDo: refactor output to schedule the space for it+ // or do we really want to keep the real white-space?+ sequence< identifier, optional < block_comment >, exactly<'('> >+ > >())+ {++ std::string name(lexed);+ name.erase(name.size() - 1);+ ParserState p = pstate;++ // specially parse nth-child pseudo selectors+ if (lex_css < sequence < binomial, word_boundary >>()) {+ std::string parsed(lexed); // always compacting binominals (as dart-sass)+ parsed.erase(std::unique(parsed.begin(), parsed.end(), BothAreSpaces), parsed.end());+ String_Constant_Obj arg = SASS_MEMORY_NEW(String_Constant, pstate, parsed);+ Pseudo_Selector* pseudo = SASS_MEMORY_NEW(Pseudo_Selector, p, name, element);+ if (lex < sequence < css_whitespace, insensitive < of_kwd >>>(false)) {+ pseudo->selector(parseSelectorList(true));+ }+ pseudo->argument(arg);+ if (lex_css< exactly<')'> >()) {+ return pseudo;+ } }- }- else if (Selector_List_Obj wrapped = parse_selector_list(true)) {- if (wrapped && lex_css< exactly<')'> >()) {- return SASS_MEMORY_NEW(Wrapped_Selector, p, name, wrapped);+ else {+ if (peek_css< exactly<')'>>() && Util::equalsLiteral("nth-", name.substr(0, 4))) {+ css_error("Invalid CSS", " after ", ": expected An+B expression, was ");+ }++ std::string unvendored = Util::unvendor(name);++ if (unvendored == "not" || unvendored == "matches" || unvendored == "current" || unvendored == "any" || unvendored == "has" || unvendored == "host" || unvendored == "host-context" || unvendored == "slotted") {+ if (SelectorListObj wrapped = parseSelectorList(true)) {+ if (wrapped && lex_css< exactly<')'> >()) {+ Pseudo_Selector* pseudo = SASS_MEMORY_NEW(Pseudo_Selector, p, name, element);+ pseudo->selector(wrapped);+ return pseudo;+ }+ }+ } else {+ String_Schema_Obj arg = parse_css_variable_value();+ Pseudo_Selector* pseudo = SASS_MEMORY_NEW(Pseudo_Selector, p, name, element);+ pseudo->argument(arg);++ if (lex_css< exactly<')'> >()) {+ return pseudo;+ }+ } }+ }+ // EO if pseudo selector - }- // EO if pseudo selector+ else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {+ return SASS_MEMORY_NEW(Pseudo_Selector, pstate, lexed, element);+ }+ else if (lex < pseudo_prefix >()) {+ css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");+ } - else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {- return SASS_MEMORY_NEW(Pseudo_Selector, pstate, lexed); }- else if(lex < pseudo_prefix >()) {- css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");+ else {+ lex < identifier >(); // needed for error message?+ css_error("Invalid CSS", " after ", ": expected selector, was "); } + css_error("Invalid CSS", " after ", ": expected \")\", was "); // unreachable statement- return 0;+ return {}; } const char* Parser::re_attr_sensitive_close(const char* src)@@ -985,11 +827,11 @@ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector"); std::string name(lexed); if (lex_css< re_attr_sensitive_close >()) {- return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, 0);+ return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", {}, {}); } else if (lex_css< re_attr_insensitive_close >()) { char modifier = lexed.begin[0];- return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, modifier);+ return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", {}, modifier); } if (!lex_css< alternatives< exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match > >()) {@@ -997,7 +839,7 @@ } std::string matcher(lexed); - String_Obj value = 0;+ String_Obj value; if (lex_css< identifier >()) { value = SASS_MEMORY_NEW(String_Constant, p, lexed); }@@ -1016,11 +858,11 @@ return SASS_MEMORY_NEW(Attribute_Selector, p, name, matcher, value, modifier); } error("unterminated attribute selector for " + name);- return NULL; // to satisfy compilers (error must not return)+ return {}; // to satisfy compilers (error must not return) } /* parse block comment and add to block */- void Parser::parse_block_comments()+ void Parser::parse_block_comments(bool store) { Block_Obj block = block_stack.back(); @@ -1028,7 +870,7 @@ bool is_important = lexed.begin[2] == '!'; // flag on second param is to skip loosely over comments String_Obj contents = parse_interpolated_chunk(lexed, true, false);- block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));+ if (store) block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important)); } } @@ -1072,7 +914,7 @@ } else { value = parse_list(DELAYED);- if (List_Ptr list = Cast<List>(value)) {+ if (List* list = Cast<List>(value)) { if (!list->is_bracketed() && list->length() == 0 && !peek< exactly <'{'> >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }@@ -1086,23 +928,6 @@ } } - // parse +/- and return false if negative- // this is never hit via spec tests- bool Parser::parse_number_prefix()- {- bool positive = true;- while(true) {- if (lex < block_comment >()) continue;- if (lex < number_prefix >()) continue;- if (lex < exactly < '-' > >()) {- positive = !positive;- continue;- }- break;- }- return positive;- }- Expression_Obj Parser::parse_map() { NESTING_GUARD(nestings);@@ -1375,7 +1200,7 @@ Expression_Obj lhs = parse_operators(); // if it's a singleton, return it (don't wrap it) if (!(peek_css< exactly<'+'> >(position) ||- // condition is a bit misterious, but some combinations should not be counted as operations+ // condition is a bit mysterious, but some combinations should not be counted as operations (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) || peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position))@@ -1478,7 +1303,7 @@ } else if (lex< identifier_schema >()) { String_Obj string = parse_identifier_schema();- if (String_Schema_Ptr schema = Cast<String_Schema>(string)) {+ if (String_Schema* schema = Cast<String_Schema>(string)) { if (lex < exactly < '(' > >()) { schema->append(parse_list()); lex < exactly < ')' > >();@@ -1493,32 +1318,25 @@ return parse_function_call(); } else if (lex< exactly<'+'> >()) {- Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else if (lex< exactly<'-'> >()) {- Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else if (lex< exactly<'/'> >()) {- Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor());+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else if (lex< sequence< kwd_not > >()) {- Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; }- // this whole branch is never hit via spec tests- else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {- if (parse_number_prefix()) return parse_value(); // prefix is positive- Unary_Expression_Ptr ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());- if (ex->operand()) ex->is_delayed(ex->operand()->is_delayed());- return ex;- } else { return parse_value(); }@@ -1533,9 +1351,9 @@ (L > 2 && parsed.substr(0, 3) == "-0.") ); } - Number_Ptr Parser::lexed_number(const ParserState& pstate, const std::string& parsed)+ Number* Parser::lexed_number(const ParserState& pstate, const std::string& parsed) {- Number_Ptr nr = SASS_MEMORY_NEW(Number,+ Number* nr = SASS_MEMORY_NEW(Number, pstate, sass_strtod(parsed.c_str()), "",@@ -1545,9 +1363,9 @@ return nr; } - Number_Ptr Parser::lexed_percentage(const ParserState& pstate, const std::string& parsed)+ Number* Parser::lexed_percentage(const ParserState& pstate, const std::string& parsed) {- Number_Ptr nr = SASS_MEMORY_NEW(Number,+ Number* nr = SASS_MEMORY_NEW(Number, pstate, sass_strtod(parsed.c_str()), "%",@@ -1557,7 +1375,7 @@ return nr; } - Number_Ptr Parser::lexed_dimension(const ParserState& pstate, const std::string& parsed)+ Number* Parser::lexed_dimension(const ParserState& pstate, const std::string& parsed) { size_t L = parsed.length(); size_t num_pos = parsed.find_first_not_of(" \n\r\t");@@ -1568,7 +1386,7 @@ } if (unit_pos == std::string::npos) unit_pos = L; const std::string& num = parsed.substr(num_pos, unit_pos - num_pos);- Number_Ptr nr = SASS_MEMORY_NEW(Number,+ Number* nr = SASS_MEMORY_NEW(Number, pstate, sass_strtod(num.c_str()), Token(number(parsed.c_str())),@@ -1578,9 +1396,9 @@ return nr; } - Value_Ptr Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed)+ Value* Parser::lexed_hex_color(const ParserState& pstate, const std::string& parsed) {- Color_Ptr color = NULL;+ Color_RGBA* color = NULL; if (parsed[0] != '#') { return SASS_MEMORY_NEW(String_Quoted, pstate, parsed); }@@ -1590,7 +1408,7 @@ std::string r(2, parsed[1]); std::string g(2, parsed[2]); std::string b(2, parsed[3]);- color = SASS_MEMORY_NEW(Color,+ color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast<double>(strtol(r.c_str(), NULL, 16)), static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1603,7 +1421,7 @@ std::string g(2, parsed[2]); std::string b(2, parsed[3]); std::string a(2, parsed[4]);- color = SASS_MEMORY_NEW(Color,+ color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast<double>(strtol(r.c_str(), NULL, 16)), static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1615,7 +1433,7 @@ std::string r(parsed.substr(1,2)); std::string g(parsed.substr(3,2)); std::string b(parsed.substr(5,2));- color = SASS_MEMORY_NEW(Color,+ color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast<double>(strtol(r.c_str(), NULL, 16)), static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1628,7 +1446,7 @@ std::string g(parsed.substr(3,2)); std::string b(parsed.substr(5,2)); std::string a(parsed.substr(7,2));- color = SASS_MEMORY_NEW(Color,+ color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast<double>(strtol(r.c_str(), NULL, 16)), static_cast<double>(strtol(g.c_str(), NULL, 16)),@@ -1641,10 +1459,10 @@ return color; } - Value_Ptr Parser::color_or_string(const std::string& lexed) const+ Value* Parser::color_or_string(const std::string& lexed) const { if (auto color = name_to_color(lexed)) {- auto c = SASS_MEMORY_NEW(Color, color);+ auto c = SASS_MEMORY_NEW(Color_RGBA, color); c->is_delayed(true); c->pstate(pstate); c->disp(lexed);@@ -1663,7 +1481,7 @@ if (match< ampersand >()) { warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate); }- return SASS_MEMORY_NEW(Parent_Selector, pstate); }+ return SASS_MEMORY_NEW(Parent_Reference, pstate); } if (lex< kwd_important >()) { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); }@@ -1729,7 +1547,7 @@ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); // unreachable statement- return 0;+ return {}; } // this parses interpolation inside other strings@@ -1742,7 +1560,7 @@ find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end); if (!p) {- String_Quoted_Ptr str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, std::string(i, chunk.end), 0, false, false, true, css);+ String_Quoted* str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, std::string(i, chunk.end), 0, false, false, true, css); if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*'); return str_quoted; }@@ -1786,71 +1604,63 @@ return schema.detach(); } - String_Schema_Obj Parser::parse_css_variable_value(bool top_level)- {- String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);- String_Schema_Obj tok;- if (!(tok = parse_css_variable_value_token(top_level))) {- return NULL;- }-- schema->concat(tok);- while ((tok = parse_css_variable_value_token(top_level))) {- schema->concat(tok);- }-- return schema.detach();- }-- String_Schema_Obj Parser::parse_css_variable_value_token(bool top_level)+ String_Schema_Obj Parser::parse_css_variable_value() { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);- if (- (top_level && lex< css_variable_top_level_value >(false)) ||- (!top_level && lex< css_variable_value >(false))- ) {- Token str(lexed);- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));- }- else if (Expression_Obj tok = lex_interpolation()) {- if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {- schema->concat(s);- } else {- schema->append(tok);- }- }- else if (lex< quoted_string >()) {- Expression_Obj tok = parse_string();- if (String_Schema_Ptr s = Cast<String_Schema>(tok)) {- schema->concat(s);- } else {- schema->append(tok);- }- }- else {- if (peek< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {- if (lex< exactly<'('> >()) {- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("(")));- if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);- if (!lex< exactly<')'> >()) css_error("Invalid CSS", " after ", ": expected \")\", was ");- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(")")));+ std::vector<char> brackets;+ while (true) {+ if (+ (brackets.empty() && lex< css_variable_top_level_value >(false)) ||+ (!brackets.empty() && lex< css_variable_value >(false))+ ) {+ Token str(lexed);+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));+ } else if (Expression_Obj tok = lex_interpolation()) {+ if (String_Schema* s = Cast<String_Schema>(tok)) {+ if (s->empty()) break;+ schema->concat(s);+ } else {+ schema->append(tok); }- else if (lex< exactly<'['> >()) {- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("[")));- if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);- if (!lex< exactly<']'> >()) css_error("Invalid CSS", " after ", ": expected \"]\", was ");- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("]")));+ } else if (lex< quoted_string >()) {+ Expression_Obj tok = parse_string();+ if (tok.isNull()) break;+ if (String_Schema* s = Cast<String_Schema>(tok)) {+ if (s->empty()) break;+ schema->concat(s);+ } else {+ schema->append(tok); }- else if (lex< exactly<'{'> >()) {- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("{")));- if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);- if (!lex< exactly<'}'> >()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");- schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("}")));+ } else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {+ const char opening_bracket = *(position - 1);+ brackets.push_back(opening_bracket);+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, opening_bracket)));+ } else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) {+ if (brackets.empty()) break;+ const char closing_bracket = *(match - 1);+ if (brackets.back() != Util::opening_bracket_for(closing_bracket)) {+ std::string message = ": expected \"";+ message += Util::closing_bracket_for(brackets.back());+ message += "\", was ";+ css_error("Invalid CSS", " after ", message); }+ lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >();+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, closing_bracket)));+ brackets.pop_back();+ } else {+ break; } } - return schema->length() > 0 ? schema.detach() : NULL;+ if (!brackets.empty()) {+ std::string message = ": expected \"";+ message += Util::closing_bracket_for(brackets.back());+ message += "\", was ";+ css_error("Invalid CSS", " after ", message);+ }++ if (schema->empty()) error("Custom property values may not be empty.");+ return schema.detach(); } Value_Obj Parser::parse_static_value()@@ -1883,7 +1693,7 @@ return SASS_MEMORY_NEW(String_Quoted, pstate, std::string(str.begin, str.end)); } - String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+ String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); while (i < str.end) { p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, str.end); if (p) {@@ -1918,7 +1728,7 @@ String_Obj Parser::parse_ie_keyword_arg() {- String_Schema_Ptr kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3);+ String_Schema_Obj kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3); if (lex< variable >()) { kwd_arg->append(SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed))); } else {@@ -2139,7 +1949,7 @@ uri = url_string->to_string({ NESTED, 5 }); } - if (String_Schema_Ptr schema = Cast<String_Schema>(url_string)) {+ if (String_Schema* schema = Cast<String_Schema>(url_string)) { String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate); res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix)); res->append(schema);@@ -2166,7 +1976,7 @@ while (pp && peek< exactly< hash_lbrace > >(pp)) { pp = sequence< interpolant, real_uri_value >(pp); }- if (!pp) return 0;+ if (!pp) return {}; position = pp; return parse_interpolated_chunk(Token(p, position)); }@@ -2175,7 +1985,7 @@ return SASS_MEMORY_NEW(String_Constant, pstate, res); } - return 0;+ return {}; } Function_Call_Obj Parser::parse_function_call()@@ -2191,18 +2001,21 @@ return SASS_MEMORY_NEW(Function_Call, call_pos, name, args); } - Function_Call_Schema_Obj Parser::parse_function_call_schema()+ Function_Call_Obj Parser::parse_function_call_schema() { String_Obj name = parse_identifier_schema(); ParserState source_position_of_call = pstate; Arguments_Obj args = parse_arguments(); - return SASS_MEMORY_NEW(Function_Call_Schema, source_position_of_call, name, args);+ return SASS_MEMORY_NEW(Function_Call, source_position_of_call, name, args); } Content_Obj Parser::parse_content_directive() {- return SASS_MEMORY_NEW(Content, pstate);+ ParserState call_pos = pstate;+ Arguments_Obj args = parse_arguments();++ return SASS_MEMORY_NEW(Content, call_pos, args); } If_Obj Parser::parse_if_directive(bool else_if)@@ -2212,7 +2025,7 @@ bool root = block_stack.back()->is_root(); Expression_Obj predicate = parse_list(); Block_Obj block = parse_block(root);- Block_Obj alternative = NULL;+ Block_Obj alternative; // only throw away comment if we parse a case // we want all other comments to be parsed@@ -2297,7 +2110,7 @@ stack.push_back(Scope::Control); bool root = block_stack.back()->is_root(); // create the initial while call object- While_Obj call = SASS_MEMORY_NEW(While, pstate, 0, 0);+ While_Obj call = SASS_MEMORY_NEW(While, pstate, {}, {}); // parse mandatory predicate Expression_Obj predicate = parse_list(); List_Obj l = Cast<List>(predicate);@@ -2313,20 +2126,107 @@ return call.detach(); } - // EO parse_while_directive- Media_Block_Obj Parser::parse_media_block()++ std::vector<CssMediaQuery_Obj> Parser::parseCssMediaQueries() {- stack.push_back(Scope::Media);- Media_Block_Obj media_block = SASS_MEMORY_NEW(Media_Block, pstate, 0, 0);+ std::vector<CssMediaQuery_Obj> result;+ do {+ if (auto query = parseCssMediaQuery()) {+ result.push_back(query);+ }+ } while (lex<exactly<','>>());+ return result;+ } - media_block->media_queries(parse_media_queries());+ std::string Parser::parseIdentifier()+ {+ if (lex < identifier >(false)) {+ return std::string(lexed);+ }+ return std::string();+ } - Media_Block_Obj prev_media_block = last_media_block;- last_media_block = media_block;- media_block->block(parse_css_block());- last_media_block = prev_media_block;+ CssMediaQuery_Obj Parser::parseCssMediaQuery()+ {+ CssMediaQuery_Obj result = SASS_MEMORY_NEW(CssMediaQuery, pstate);+ lex<css_comments>(false);++ // Check if any tokens are to parse+ if (!peek_css<exactly<'('>>()) {++ std::string token1(parseIdentifier());+ lex<css_comments>(false);++ if (token1.empty()) {+ return {};+ }++ std::string token2(parseIdentifier());+ lex<css_comments>(false);++ if (Util::equalsLiteral("and", token2)) {+ result->type(token1);+ }+ else {+ if (token2.empty()) {+ result->type(token1);+ }+ else {+ result->modifier(token1);+ result->type(token2);+ }++ if (lex < kwd_and >()) {+ lex<css_comments>(false);+ }+ else {+ return result;+ }++ }++ }++ std::vector<std::string> queries;++ do {+ lex<css_comments>(false);++ if (lex<exactly<'('>>()) {+ // In dart sass parser returns a pure string+ if (lex < skip_over_scopes < exactly < '(' >, exactly < ')' > > >()) {+ std::string decl("(" + std::string(lexed));+ queries.push_back(decl);+ }+ // Should be: parseDeclarationValue;+ if (!lex<exactly<')'>>()) {+ // Should we throw an error here?+ }+ }+ } while (lex < kwd_and >());++ result->features(queries);++ if (result->features().empty()) {+ if (result->type().empty()) {+ return {};+ }+ }++ return result;+ }+++ // EO parse_while_directive+ MediaRule_Obj Parser::parseMediaRule()+ {+ MediaRule_Obj rule = SASS_MEMORY_NEW(MediaRule, pstate);+ stack.push_back(Scope::Media);+ rule->schema(parse_media_queries());+ parse_block_comments(false);+ rule->block(parse_css_block()); stack.pop_back();- return media_block.detach();+ return rule; } List_Obj Parser::parse_media_queries()@@ -2339,7 +2239,7 @@ return queries.detach(); } - // Expression_Ptr Parser::parse_media_query()+ // Expression* Parser::parse_media_query() Media_Query_Obj Parser::parse_media_query() { advanceToNextToken();@@ -2353,7 +2253,7 @@ while (lex_css < kwd_and >()) media_query->append(parse_media_expression()); if (lex < identifier_schema >()) {- String_Schema_Ptr schema = SASS_MEMORY_NEW(String_Schema, pstate);+ String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); schema->append(media_query->media_type()); schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); schema->append(parse_identifier_schema());@@ -2370,7 +2270,7 @@ { if (lex < identifier_schema >()) { String_Obj ss = parse_identifier_schema();- return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, 0, true);+ return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, {}, true); } if (!lex_css< exactly<'('> >()) { error("media query expression must begin with '('");@@ -2380,7 +2280,7 @@ error("media feature required in media query expression"); } feature = parse_expression();- Expression_Obj expression = 0;+ Expression_Obj expression; if (lex_css< exactly<':'> >()) { expression = parse_list(DELAYED); }@@ -2394,10 +2294,7 @@ // these are very similar to media blocks Supports_Block_Obj Parser::parse_supports_directive() {- Supports_Condition_Obj cond = parse_supports_condition();- if (!cond) {- css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", false);- }+ Supports_Condition_Obj cond = parse_supports_condition(/*top_level=*/true); // create the ast node object for the support queries Supports_Block_Obj query = SASS_MEMORY_NEW(Supports_Block, pstate, cond); // additional block is mandatory@@ -2409,27 +2306,27 @@ // parse one query operation // may encounter nested queries- Supports_Condition_Obj Parser::parse_supports_condition()+ Supports_Condition_Obj Parser::parse_supports_condition(bool top_level) { lex < css_whitespace >(); Supports_Condition_Obj cond; if ((cond = parse_supports_negation())) return cond;- if ((cond = parse_supports_operator())) return cond;+ if ((cond = parse_supports_operator(top_level))) return cond; if ((cond = parse_supports_interpolation())) return cond; return cond; } Supports_Condition_Obj Parser::parse_supports_negation() {- if (!lex < kwd_not >()) return 0;- Supports_Condition_Obj cond = parse_supports_condition_in_parens();+ if (!lex < kwd_not >()) return {};+ Supports_Condition_Obj cond = parse_supports_condition_in_parens(/*parens_required=*/true); return SASS_MEMORY_NEW(Supports_Negation, pstate, cond); } - Supports_Condition_Obj Parser::parse_supports_operator()+ Supports_Condition_Obj Parser::parse_supports_operator(bool top_level) {- Supports_Condition_Obj cond = parse_supports_condition_in_parens();- if (cond.isNull()) return 0;+ Supports_Condition_Obj cond = parse_supports_condition_in_parens(/*parens_required=*/top_level);+ if (cond.isNull()) return {}; while (true) { Supports_Operator::Operand op = Supports_Operator::OR;@@ -2437,9 +2334,9 @@ else if(!lex < kwd_or >()) { break; } lex < css_whitespace >();- Supports_Condition_Obj right = parse_supports_condition_in_parens();+ Supports_Condition_Obj right = parse_supports_condition_in_parens(/*parens_required=*/true); - // Supports_Condition_Ptr cc = SASS_MEMORY_NEW(Supports_Condition, *static_cast<Supports_Condition_Ptr>(cond));+ // Supports_Condition* cc = SASS_MEMORY_NEW(Supports_Condition, *static_cast<Supports_Condition*>(cond)); cond = SASS_MEMORY_NEW(Supports_Operator, pstate, cond, right, op); } return cond;@@ -2447,10 +2344,10 @@ Supports_Condition_Obj Parser::parse_supports_interpolation() {- if (!lex < interpolant >()) return 0;+ if (!lex < interpolant >()) return {}; String_Obj interp = parse_interpolated_chunk(lexed);- if (!interp) return 0;+ if (!interp) return {}; return SASS_MEMORY_NEW(Supports_Interpolation, pstate, interp); }@@ -2459,10 +2356,10 @@ // look like declarations their semantics differ significantly Supports_Condition_Obj Parser::parse_supports_declaration() {- Supports_Condition_Ptr cond;+ Supports_Condition* cond; // parse something declaration like Expression_Obj feature = parse_expression();- Expression_Obj expression = 0;+ Expression_Obj expression; if (lex_css< exactly<':'> >()) { expression = parse_list(DELAYED); }@@ -2475,21 +2372,24 @@ return cond; } - Supports_Condition_Obj Parser::parse_supports_condition_in_parens()+ Supports_Condition_Obj Parser::parse_supports_condition_in_parens(bool parens_required) { Supports_Condition_Obj interp = parse_supports_interpolation();- if (interp != 0) return interp;+ if (interp != nullptr) return interp; - if (!lex < exactly <'('> >()) return 0;+ if (!lex < exactly <'('> >()) {+ if (parens_required) {+ css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", /*trim=*/false);+ } else {+ return {};+ }+ } lex < css_whitespace >(); - Supports_Condition_Obj cond = parse_supports_condition();- if (cond != 0) {- if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");- } else {- cond = parse_supports_declaration();- if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");- }+ Supports_Condition_Obj cond = parse_supports_condition(/*top_level=*/false);+ if (cond.isNull()) cond = parse_supports_declaration();+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");+ lex < css_whitespace >(); return cond; }@@ -2498,7 +2398,7 @@ { stack.push_back(Scope::AtRoot); ParserState at_source_position = pstate;- Block_Obj body = 0;+ Block_Obj body; At_Root_Query_Obj expr; Lookahead lookahead_result; if (lex_css< exactly<'('> >()) {@@ -2545,68 +2445,6 @@ return cond; } - Directive_Obj Parser::parse_special_directive()- {- std::string kwd(lexed);-- if (lexed == "@else") error("Invalid CSS: @else must come after @if");-- // this whole branch is never hit via spec tests-- Directive_Ptr at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);- Lookahead lookahead = lookahead_for_include(position);- if (lookahead.found && !lookahead.has_interpolants) {- at_rule->selector(parse_selector_list(false));- }-- lex < css_comments >(false);-- if (lex < static_property >()) {- at_rule->value(parse_interpolated_chunk(Token(lexed)));- } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {- at_rule->value(parse_list());- }-- lex < css_comments >(false);-- if (peek< exactly<'{'> >()) {- at_rule->block(parse_block());- }-- return at_rule;- }-- // this whole branch is never hit via spec tests- Directive_Obj Parser::parse_prefixed_directive()- {- std::string kwd(lexed);-- if (lexed == "@else") error("Invalid CSS: @else must come after @if");-- Directive_Obj at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);- Lookahead lookahead = lookahead_for_include(position);- if (lookahead.found && !lookahead.has_interpolants) {- at_rule->selector(parse_selector_list(false));- }-- lex < css_comments >(false);-- if (lex < static_property >()) {- at_rule->value(parse_interpolated_chunk(Token(lexed)));- } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {- at_rule->value(parse_list());- }-- lex < css_comments >(false);-- if (peek< exactly<'{'> >()) {- at_rule->block(parse_block());- }-- return at_rule;- }-- Directive_Obj Parser::parse_directive() { Directive_Obj directive = SASS_MEMORY_NEW(Directive, pstate, lexed);@@ -2624,7 +2462,7 @@ if (lex < interpolant >(true) != NULL) { return parse_interpolated_chunk(lexed, true); }- return 0;+ return {}; } Expression_Obj Parser::lex_interp_uri()@@ -2647,6 +2485,7 @@ lex < one_plus < alternatives <+ exactly <'>'>, sequence < exactly <'\\'>, any_char@@ -2690,13 +2529,13 @@ if (match) { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); }- return NULL;+ return {}; } Expression_Obj Parser::lex_almost_any_value_token() { Expression_Obj rv;- if (*position == 0) return 0;+ if (*position == 0) return {}; if ((rv = lex_almost_any_value_chars())) return rv; // if ((rv = lex_block_comment())) return rv; // if ((rv = lex_single_line_comment())) return rv;@@ -2712,10 +2551,10 @@ { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);- if (*position == 0) return 0;+ if (*position == 0) return {}; lex < spaces >(false); Expression_Obj token = lex_almost_any_value_token();- if (!token) return 0;+ if (!token) return {}; schema->append(token); if (*position == 0) { schema->rtrim();@@ -2813,7 +2652,7 @@ rv.error = q; rv.position = q; // check expected opening bracket- // only after successfull matching+ // only after successful matching if (peek < exactly<'{'> >(q)) rv.found = q; // else if (peek < end_of_file >(q)) rv.found = q; else if (peek < exactly<'('> >(q)) rv.found = q;@@ -2997,7 +2836,7 @@ Expression_Obj Parser::fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, std::vector<Operand>& ops, size_t i) {- if (String_Schema_Ptr schema = Cast<String_Schema>(base)) {+ if (String_Schema* schema = Cast<String_Schema>(base)) { // return schema; if (schema->has_interpolants()) { if (i + 1 < operands.size() && (@@ -3020,7 +2859,7 @@ } for (size_t S = operands.size(); i < S; ++i) {- if (String_Schema_Ptr schema = Cast<String_Schema>(operands[i])) {+ if (String_Schema* schema = Cast<String_Schema>(operands[i])) { if (schema->has_interpolants()) { if (i + 1 < S) { // this whole branch is never hit via spec tests@@ -3037,13 +2876,13 @@ } else { base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); }- Binary_Expression_Ptr b = Cast<Binary_Expression>(base.ptr());+ Binary_Expression* b = Cast<Binary_Expression>(base.ptr()); if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); } } // nested binary expression are never to be delayed- if (Binary_Expression_Ptr b = Cast<Binary_Expression>(base)) {+ if (Binary_Expression* b = Cast<Binary_Expression>(base)) { if (Cast<Binary_Expression>(b->left())) base->set_delayed(false); if (Cast<Binary_Expression>(b->right())) base->set_delayed(false); }@@ -3054,8 +2893,15 @@ { Position p(pos.line ? pos : before_token); ParserState pstate(path, source, p, Offset(0, 0));+ // `pstate.src` may not outlive stack unwind so we must copy it.+ // This is needed since we often parse dynamically generated code,+ // e.g. for interpolations, and we normally don't want to keep this+ // memory around after we parsed the AST tree successfully. Only on+ // errors we want to preserve them for better error reporting.+ char *src_copy = sass_copy_c_string(pstate.src);+ pstate.src = src_copy; traces.push_back(Backtrace(pstate));- throw Exception::InvalidSass(pstate, traces, msg);+ throw Exception::InvalidSass(pstate, traces, msg, src_copy); } void Parser::error(std::string msg)@@ -3078,7 +2924,7 @@ } // backup position to last significant char while (trim && last_pos > source && last_pos < end) {- if (!Prelexer::is_space(*last_pos)) break;+ if (!Util::ascii_isspace(static_cast<unsigned char>(*last_pos))) break; utf8::prior(last_pos, source); }
libsass/src/parser.hpp view
@@ -1,6 +1,10 @@ #ifndef SASS_PARSER_H #define SASS_PARSER_H +// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"+ #include <string> #include <vector> @@ -38,7 +42,6 @@ Context& ctx; std::vector<Block_Obj> block_stack; std::vector<Scope> stack;- Media_Block_Ptr last_media_block; const char* source; const char* position; const char* end;@@ -48,23 +51,24 @@ Backtraces traces; size_t indentation; size_t nestings;+ bool allow_parent; Token lexed; - Parser(Context& ctx, const ParserState& pstate, Backtraces traces)- : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),+ Parser(Context& ctx, const ParserState& pstate, Backtraces traces, bool allow_parent = true)+ : ParserState(pstate), ctx(ctx), block_stack(), stack(0), source(0), position(0), end(0), before_token(pstate), after_token(pstate),- pstate(pstate), traces(traces), indentation(0), nestings(0)- { + pstate(pstate), traces(traces), indentation(0), nestings(0), allow_parent(allow_parent)+ { stack.push_back(Scope::Root); } // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));- static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);- static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);- static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);+ static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = nullptr, bool allow_parent = true);+ static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = nullptr, bool allow_parent = true);+ static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = nullptr); // special static parsers to convert strings into certain selectors- static Selector_List_Obj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);+ static SelectorListObj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = nullptr, bool allow_parent = true); #ifdef __clang__ @@ -184,7 +188,7 @@ // update after_token position for current token after_token.add(it_before_token, it_after_token); - // ToDo: could probably do this incremetal on original object (API wants offset?)+ // ToDo: could probably do this incremental on original object (API wants offset?) pstate = ParserState(path, source, lexed, before_token, after_token - before_token); // advance internal char iterator@@ -256,20 +260,20 @@ Argument_Obj parse_argument(); Assignment_Obj parse_assignment(); Ruleset_Obj parse_ruleset(Lookahead lookahead);- Selector_List_Obj parse_selector_list(bool chroot);- Complex_Selector_Obj parse_complex_selector(bool chroot);+ SelectorListObj parseSelectorList(bool chroot);+ ComplexSelectorObj parseComplexSelector(bool chroot); Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot);- Compound_Selector_Obj parse_compound_selector();- Simple_Selector_Obj parse_simple_selector();- Wrapped_Selector_Obj parse_negated_selector();- Simple_Selector_Obj parse_pseudo_selector();+ CompoundSelectorObj parseCompoundSelector();+ SimpleSelectorObj parse_simple_selector();+ Pseudo_Selector_Obj parse_negated_selector2();+ Expression* parse_binominal();+ SimpleSelectorObj parse_pseudo_selector(); Attribute_Selector_Obj parse_attribute_selector(); Block_Obj parse_block(bool is_root = false); Block_Obj parse_css_block(bool is_root = false); bool parse_block_nodes(bool is_root = false); bool parse_block_node(bool is_root = false); - bool parse_number_prefix(); Declaration_Obj parse_declaration(); Expression_Obj parse_map(); Expression_Obj parse_bracket_list();@@ -285,14 +289,13 @@ Expression_Obj parse_value(); Function_Call_Obj parse_calc_function(); Function_Call_Obj parse_function_call();- Function_Call_Schema_Obj parse_function_call_schema();+ Function_Call_Obj parse_function_call_schema(); String_Obj parse_url_function_string(); String_Obj parse_url_function_argument(); String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true); String_Obj parse_string(); Value_Obj parse_static_value();- String_Schema_Obj parse_css_variable_value(bool top_level = true);- String_Schema_Obj parse_css_variable_value_token(bool top_level = true);+ String_Schema_Obj parse_css_variable_value(); String_Obj parse_ie_property(); String_Obj parse_ie_keyword_arg(); String_Schema_Obj parse_value_schema(const char* stop);@@ -301,31 +304,32 @@ For_Obj parse_for_directive(); Each_Obj parse_each_directive(); While_Obj parse_while_directive();+ MediaRule_Obj parseMediaRule();+ std::vector<CssMediaQuery_Obj> parseCssMediaQueries();+ std::string parseIdentifier();+ CssMediaQuery_Obj parseCssMediaQuery(); Return_Obj parse_return_directive(); Content_Obj parse_content_directive(); void parse_charset_directive();- Media_Block_Obj parse_media_block(); List_Obj parse_media_queries(); Media_Query_Obj parse_media_query(); Media_Query_Expression_Obj parse_media_expression(); Supports_Block_Obj parse_supports_directive();- Supports_Condition_Obj parse_supports_condition();+ Supports_Condition_Obj parse_supports_condition(bool top_level); Supports_Condition_Obj parse_supports_negation();- Supports_Condition_Obj parse_supports_operator();+ Supports_Condition_Obj parse_supports_operator(bool top_level); Supports_Condition_Obj parse_supports_interpolation(); Supports_Condition_Obj parse_supports_declaration();- Supports_Condition_Obj parse_supports_condition_in_parens();+ Supports_Condition_Obj parse_supports_condition_in_parens(bool parens_required); At_Root_Block_Obj parse_at_root_block(); At_Root_Query_Obj parse_at_root_query(); String_Schema_Obj parse_almost_any_value();- Directive_Obj parse_special_directive();- Directive_Obj parse_prefixed_directive(); Directive_Obj parse_directive(); Warning_Obj parse_warning(); Error_Obj parse_error(); Debug_Obj parse_debug(); - Value_Ptr color_or_string(const std::string& lexed) const;+ Value* color_or_string(const std::string& lexed) const; // be more like ruby sass Expression_Obj lex_almost_any_value_token();@@ -338,7 +342,7 @@ Token lex_variable(); Token lex_identifier(); - void parse_block_comments();+ void parse_block_comments(bool store = true); Lookahead lookahead_for_value(const char* start = 0); Lookahead lookahead_for_selector(const char* start = 0);@@ -375,19 +379,19 @@ return SASS_MEMORY_NEW(String_Constant, pstate, lexed); } }- return 0;+ return {}; } public:- static Number_Ptr lexed_number(const ParserState& pstate, const std::string& parsed);- static Number_Ptr lexed_dimension(const ParserState& pstate, const std::string& parsed);- static Number_Ptr lexed_percentage(const ParserState& pstate, const std::string& parsed);- static Value_Ptr lexed_hex_color(const ParserState& pstate, const std::string& parsed);+ static Number* lexed_number(const ParserState& pstate, const std::string& parsed);+ static Number* lexed_dimension(const ParserState& pstate, const std::string& parsed);+ static Number* lexed_percentage(const ParserState& pstate, const std::string& parsed);+ static Value* lexed_hex_color(const ParserState& pstate, const std::string& parsed); private:- Number_Ptr lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); };- Number_Ptr lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); };- Number_Ptr lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); };- Value_Ptr lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); };+ Number* lexed_number(const std::string& parsed) { return lexed_number(pstate, parsed); };+ Number* lexed_dimension(const std::string& parsed) { return lexed_dimension(pstate, parsed); };+ Number* lexed_percentage(const std::string& parsed) { return lexed_percentage(pstate, parsed); };+ Value* lexed_hex_color(const std::string& parsed) { return lexed_hex_color(pstate, parsed); }; static const char* re_attr_sensitive_close(const char* src); static const char* re_attr_insensitive_close(const char* src);
+ libsass/src/parser_selectors.cpp view
@@ -0,0 +1,189 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "parser.hpp"++namespace Sass {++ using namespace Prelexer;+ using namespace Constants;++ ComplexSelectorObj Parser::parseComplexSelector(bool chroot)+ {++ NESTING_GUARD(nestings);++ lex < block_comment >();+ advanceToNextToken();++ ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate);++ if (peek < end_of_file >()) return sel;++ while (true) {++ lex < block_comment >();+ advanceToNextToken();++ // check for child (+) combinator+ if (lex < exactly < selector_combinator_child > >()) {+ sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::CHILD, peek_newline()));+ }+ // check for general sibling (~) combinator+ else if (lex < exactly < selector_combinator_general > >()) {+ sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::GENERAL, peek_newline()));+ }+ // check for adjecant sibling (+) combinator+ else if (lex < exactly < selector_combinator_adjacent > >()) {+ sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::ADJACENT, peek_newline()));+ }+ // check if we can parse a compound selector+ else if (CompoundSelectorObj compound = parseCompoundSelector()) {+ sel->append(compound);+ }+ else {+ break;+ }+ }++ if (sel->empty()) return {};++ // check if we parsed any parent references+ sel->chroots(sel->has_real_parent_ref() || chroot);++ sel->update_pstate(pstate);++ return sel;++ }++ SelectorListObj Parser::parseSelectorList(bool chroot)+ {++ bool reloop;+ bool had_linefeed = false;+ NESTING_GUARD(nestings);+ SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate);++ if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) {+ css_error("Invalid CSS", " after ", ": expected selector, was ");+ }++ do {+ reloop = false;++ had_linefeed = had_linefeed || peek_newline();++ if (peek_css< alternatives < class_char < selector_list_delims > > >())+ break; // in case there are superfluous commas at the end++ // now parse the complex selector+ ComplexSelectorObj complex = parseComplexSelector(chroot);+ if (complex.isNull()) return list.detach();+ complex->hasPreLineFeed(had_linefeed);++ had_linefeed = false;++ while (peek_css< exactly<','> >())+ {+ lex< css_comments >(false);+ // consume everything up and including the comma separator+ reloop = lex< exactly<','> >() != 0;+ // remember line break (also between some commas)+ had_linefeed = had_linefeed || peek_newline();+ // remember line break (also between some commas)+ }+ list->append(complex);++ } while (reloop);++ while (lex_css< kwd_optional >()) {+ list->is_optional(true);+ }++ // update for end position+ list->update_pstate(pstate);++ return list.detach();+ }++ // parse one compound selector, which is basically+ // a list of simple selectors (directly adjacent)+ // lex them exactly (without skipping white-space)+ CompoundSelectorObj Parser::parseCompoundSelector()+ {+ // init an empty compound selector wrapper+ CompoundSelectorObj seq = SASS_MEMORY_NEW(CompoundSelector, pstate);++ // skip initial white-space+ lex < block_comment >();+ advanceToNextToken();++ if (lex< exactly<'&'> >(false))+ {+ // ToDo: check the conditions and try to simplify flag passing+ if (!allow_parent) error("Parent selectors aren't allowed here.");+ // Create and append a new parent selector object+ seq->hasRealParent(true);+ }++ // parse list+ while (true)+ {+ // remove all block comments+ // leaves trailing white-space+ lex < block_comment >();+ // parse parent selector+ if (lex< exactly<'&'> >(false))+ {+ // parent selector only allowed at start+ // upcoming Sass may allow also trailing+ ParserState state(pstate);+ std::string found("&");+ if (lex < identifier >()) {+ found += std::string(lexed);+ }+ std::string sel(seq->hasRealParent() ? "&" : "");+ if (!seq->empty()) { sel = seq->last()->to_string({ NESTED, 5 }); }+ // ToDo: parser should throw parser exceptions+ error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n"+ "\"" + found + "\" may only be used at the beginning of a compound selector.", state);+ }+ // parse functional+ else if (match < re_functional >())+ {+ seq->append(parse_simple_selector());+ }++ // parse type selector+ else if (lex< re_type_selector >(false))+ {+ seq->append(SASS_MEMORY_NEW(Type_Selector, pstate, lexed));+ }+ // peek for abort conditions+ else if (peek< spaces >()) break;+ else if (peek< end_of_file >()) { break; }+ else if (peek_css < class_char < selector_combinator_ops > >()) break;+ else if (peek_css < class_char < complex_selector_delims > >()) break;+ // otherwise parse another simple selector+ else {+ SimpleSelectorObj sel = parse_simple_selector();+ if (!sel) return {};+ seq->append(sel);+ }+ }+ // EO while true++ if (seq && !peek_css<alternatives<end_of_file,exactly<'{'>>>()) {+ seq->hasPostLineBreak(peek_newline());+ }++ // We may have set hasRealParent+ if (seq && seq->empty() && !seq->hasRealParent()) return {};++ return seq;+ }+++}
− libsass/src/paths.hpp
@@ -1,71 +0,0 @@-#ifndef SASS_PATHS_H-#define SASS_PATHS_H--#include <string>-#include <vector>-#include <sstream>---template<typename T>-std::string vector_to_string(std::vector<T> v)-{- std::stringstream buffer;- buffer << "[";-- if (!v.empty())- { buffer << v[0]; }- else- { buffer << "]"; }-- if (v.size() == 1)- { buffer << "]"; }- else- {- for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i];- buffer << "]";- }-- return buffer.str();-}--namespace Sass {--- template<typename T>- std::vector<std::vector<T> > paths(std::vector<std::vector<T> > strata, size_t from_end = 0)- {- if (strata.empty()) {- return std::vector<std::vector<T> >();- }-- size_t end = strata.size() - from_end;- if (end <= 1) {- std::vector<std::vector<T> > starting_points;- starting_points.reserve(strata[0].size());- for (size_t i = 0, S = strata[0].size(); i < S; ++i) {- std::vector<T> starting_point;- starting_point.push_back(strata[0][i]);- starting_points.push_back(starting_point);- }- return starting_points;- }-- std::vector<std::vector<T> > up_to_here = paths(strata, from_end + 1);- std::vector<T> here = strata[end-1];-- std::vector<std::vector<T> > branches;- branches.reserve(up_to_here.size() * here.size());- for (size_t i = 0, S1 = up_to_here.size(); i < S1; ++i) {- for (size_t j = 0, S2 = here.size(); j < S2; ++j) {- std::vector<T> branch = up_to_here[i];- branch.push_back(here[j]);- branches.push_back(branch);- }- }-- return branches;- }--}--#endif
+ libsass/src/permutate.hpp view
@@ -0,0 +1,140 @@+#ifndef SASS_PATHS_H+#define SASS_PATHS_H++#include <vector>++namespace Sass {++ // Returns a list of all possible paths through the given lists.+ //+ // For example, given `[[1, 2], [3, 4], [5]]`, this returns:+ //+ // ```+ // [[1, 3, 5],+ // [2, 3, 5],+ // [1, 4, 5],+ // [2, 4, 5]]+ // ```+ // + // Note: called `paths` in dart-sass+ template <class T>+ std::vector<std::vector<T>> permutate(+ const std::vector<std::vector<T>>& in)+ {++ size_t L = in.size(), n = 0;++ // Exit early if any entry is empty+ for (size_t i = 0; i < L; i += 1) {+ if (in[i].size() == 0) return {};+ }++ size_t* state = new size_t[L + 1];+ std::vector<std::vector<T>> out;++ // First initialize all states for every permutation group+ for (size_t i = 0; i < L; i += 1) {+ state[i] = in[i].size() - 1;+ }+ while (true) {+ std::vector<T> perm;+ // Create one permutation for state+ for (size_t i = 0; i < L; i += 1) {+ perm.push_back(in.at(i).at(in[i].size() - state[i] - 1));+ }+ // Current group finished+ if (state[n] == 0) {+ // Find position of next decrement+ while (n < L && state[++n] == 0) {}++ if (n == L) {+ out.push_back(perm);+ break;+ }++ state[n] -= 1;++ for (size_t p = 0; p < n; p += 1) {+ state[p] = in[p].size() - 1;+ }++ // Restart from front+ n = 0;++ }+ else {+ state[n] -= 1;+ }+ out.push_back(perm);+ }++ delete[] state;+ return out;+ }+ // EO permutate++ // ToDo: this variant is used in resolve_parent_refs+ template <class T>+ std::vector<std::vector<T>>+ permutateAlt(const std::vector<std::vector<T>>& in) {++ size_t L = in.size();+ size_t n = in.size() - 1;+ size_t* state = new size_t[L];+ std::vector< std::vector<T>> out;++ // First initialize all states for every permutation group+ for (size_t i = 0; i < L; i += 1) {+ if (in[i].size() == 0) return {};+ state[i] = in[i].size() - 1;+ }++ while (true) {+ /*+ // std::cerr << "PERM: ";+ for (size_t p = 0; p < L; p++)+ { // std::cerr << state[p] << " "; }+ // std::cerr << "\n";+ */+ std::vector<T> perm;+ // Create one permutation for state+ for (size_t i = 0; i < L; i += 1) {+ perm.push_back(in.at(i).at(in[i].size() - state[i] - 1));+ }+ // Current group finished+ if (state[n] == 0) {+ // Find position of next decrement+ while (n > 0 && state[--n] == 0)+ {++ }+ // Check for end condition+ if (state[n] != 0) {+ // Decrease next on the left side+ state[n] -= 1;+ // Reset all counters to the right+ for (size_t p = n + 1; p < L; p += 1) {+ state[p] = in[p].size() - 1;+ }+ // Restart from end+ n = L - 1;+ }+ else {+ out.push_back(perm);+ break;+ }+ }+ else {+ state[n] -= 1;+ }+ out.push_back(perm);+ }++ delete[] state;+ return out;+ }+ // EO permutateAlt++}++#endif
libsass/src/plugins.cpp view
@@ -1,7 +1,11 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <iostream> #include "output.hpp" #include "plugins.hpp"+#include "util.hpp" #ifdef _WIN32 #include <windows.h>@@ -154,7 +158,7 @@ } } }- catch (utf8::invalid_utf8)+ catch (utf8::invalid_utf8&) { // report the error to the console (should not happen) // implementors should make sure to provide valid utf8
libsass/src/position.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "position.hpp" namespace Sass {
libsass/src/position.hpp view
@@ -86,7 +86,7 @@ size_t length() const { return end - begin; } std::string ws_before() const { return std::string(prefix, begin); }- const std::string to_string() const { return std::string(begin, end); }+ std::string to_string() const { return std::string(begin, end); } std::string time_wspace() const { std::string str(to_string()); std::string whitespaces(" \t\f\v\n\r");
libsass/src/prelexer.cpp view
@@ -1,8 +1,11 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <cctype>+ #include <iostream> #include <iomanip> #include "util.hpp"+#include "util_string.hpp" #include "position.hpp" #include "prelexer.hpp" #include "constants.hpp"@@ -260,7 +263,7 @@ >(src); } - // Match a line comment (/.*?(?=\n|\r\n?|\Z)/.+ // Match a line comment (/.*?(?=\n|\r\n?|\f|\Z)/. const char* line_comment(const char* src) { return sequence<@@ -333,7 +336,7 @@ return alternatives< unicode_seq, alpha,- unicode,+ nonascii, exactly<'-'>, exactly<'_'>, NONASCII,@@ -348,7 +351,7 @@ return alternatives< unicode_seq, alnum,- unicode,+ nonascii, exactly<'-'>, exactly<'_'>, NONASCII,@@ -382,7 +385,7 @@ { return alternatives < alpha,- unicode,+ nonascii, escape_seq, exactly<'_'> >(src);@@ -392,7 +395,7 @@ { return alternatives < alnum,- unicode,+ nonascii, escape_seq, exactly<'_'> >(src);@@ -1260,6 +1263,9 @@ const char* kwd_lte(const char* src) { return exactly<lte>(src); }+ const char* kwd_using(const char* src) {+ return keyword<using_kwd>(src);+ } // match specific IE syntax const char* ie_progid(const char* src) {@@ -1394,7 +1400,7 @@ }*/ const char* H(const char* src) {- return std::isxdigit(*src) ? src+1 : 0;+ return Util::ascii_isxdigit(static_cast<unsigned char>(*src)) ? src+1 : 0; } const char* W(const char* src) {
libsass/src/prelexer.hpp view
@@ -25,6 +25,7 @@ const char* kwd_gte(const char* src); const char* kwd_lt(const char* src); const char* kwd_lte(const char* src);+ const char* kwd_using(const char* src); // Match standard control chars const char* kwd_at(const char* src);@@ -64,17 +65,16 @@ size_t level = 0; bool in_squote = false; bool in_dquote = false;- // bool in_braces = false;-- while (*src) {-- // check for abort condition- if (end && src >= end) break;+ bool in_backslash_escape = false; + while ((end == nullptr || src < end) && *src != '\0') { // has escaped sequence?- if (*src == '\\') {- ++ src; // skip this (and next)+ if (in_backslash_escape) {+ in_backslash_escape = false; }+ else if (*src == '\\') {+ in_backslash_escape = true;+ } else if (*src == '"') { in_dquote = ! in_dquote; }@@ -119,7 +119,7 @@ // first start/opener must be consumed already! template<prelexer start, prelexer stop> const char* skip_over_scopes(const char* src) {- return skip_over_scopes<start, stop>(src, 0);+ return skip_over_scopes<start, stop>(src, nullptr); } // Match a sequence of characters delimited by the supplied chars.
libsass/src/remove_placeholders.cpp view
@@ -1,84 +1,86 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+#include "ast.hpp"+ #include "remove_placeholders.hpp"-#include "context.hpp"-#include "inspect.hpp"-#include <iostream> namespace Sass { Remove_Placeholders::Remove_Placeholders() { } - void Remove_Placeholders::operator()(Block_Ptr b) {- for (size_t i = 0, L = b->length(); i < L; ++i) {- Statement_Ptr st = b->at(i);- st->perform(this);- }+ void Remove_Placeholders::operator()(Block* b) {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ if (b->get(i)) b->get(i)->perform(this);+ } } - Selector_List_Ptr Remove_Placeholders::remove_placeholders(Selector_List_Ptr sl)+ void Remove_Placeholders::remove_placeholders(SimpleSelector* simple) {- Selector_List_Ptr new_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());-- for (size_t i = 0, L = sl->length(); i < L; ++i) {- if (!sl->at(i)->contains_placeholder()) {- new_sl->append(sl->at(i));- }+ if (Pseudo_Selector * pseudo = simple->getPseudoSelector()) {+ if (pseudo->selector()) remove_placeholders(pseudo->selector()); }-- return new_sl;- } -- void Remove_Placeholders::operator()(Ruleset_Ptr r) {- // Create a new selector group without placeholders- Selector_List_Obj sl = Cast<Selector_List>(r->selector());+ void Remove_Placeholders::remove_placeholders(CompoundSelector* compound)+ {+ for (size_t i = 0, L = compound->length(); i < L; ++i) {+ if (compound->get(i)) remove_placeholders(compound->get(i));+ }+ listEraseItemIf(compound->elements(), listIsEmpty<SimpleSelector>);+ } - if (sl) {- // Set the new placeholder selector list- r->selector(remove_placeholders(sl));- // Remove placeholders in wrapped selectors- for (Complex_Selector_Obj cs : sl->elements()) {- while (cs) {- if (cs->head()) {- for (Simple_Selector_Obj& ss : cs->head()->elements()) {- if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {- if (Selector_List_Ptr wsl = Cast<Selector_List>(ws->selector())) {- Selector_List_Ptr clean = remove_placeholders(wsl);- // also clean superflous parent selectors- // probably not really the correct place- clean->remove_parent_selectors();- ws->selector(clean);- }- }- }- }- cs = cs->tail();- }+ void Remove_Placeholders::remove_placeholders(ComplexSelector* complex)+ {+ if (complex->has_placeholder()) {+ complex->clear(); // remove all+ }+ else {+ for (size_t i = 0, L = complex->length(); i < L; ++i) {+ if (CompoundSelector * compound = complex->get(i)->getCompound()) {+ if (compound) remove_placeholders(compound); } }+ listEraseItemIf(complex->elements(), listIsEmpty<SelectorComponent>);+ }+ } - // Iterate into child blocks- Block_Obj b = r->block();+ SelectorList* Remove_Placeholders::remove_placeholders(SelectorList* sl)+ {+ for (size_t i = 0, L = sl->length(); i < L; ++i) {+ if (sl->get(i)) remove_placeholders(sl->get(i));+ }+ listEraseItemIf(sl->elements(), listIsEmpty<ComplexSelector>);+ return sl;+ } - for (size_t i = 0, L = b->length(); i < L; ++i) {- if (b->at(i)) {- Statement_Obj st = b->at(i);- st->perform(this);- }- }+ void Remove_Placeholders::operator()(CssMediaRule* rule)+ {+ if (rule->block()) operator()(rule->block()); } - void Remove_Placeholders::operator()(Media_Block_Ptr m) {- operator()(m->block());+ void Remove_Placeholders::operator()(Ruleset* r)+ {+ if (SelectorListObj sl = r->selector()) {+ // Set the new placeholder selector list+ r->selector((remove_placeholders(sl)));+ }+ // Iterate into child blocks+ Block_Obj b = r->block();+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ if (b->get(i)) { b->get(i)->perform(this); }+ } }- void Remove_Placeholders::operator()(Supports_Block_Ptr m) {- operator()(m->block());++ void Remove_Placeholders::operator()(Supports_Block* m)+ {+ if (m->block()) operator()(m->block()); } - void Remove_Placeholders::operator()(Directive_Ptr a) {- if (a->block()) a->block()->perform(this);+ void Remove_Placeholders::operator()(Directive* a)+ {+ if (a->block()) a->block()->perform(this); } }
libsass/src/remove_placeholders.hpp view
@@ -1,34 +1,36 @@ #ifndef SASS_REMOVE_PLACEHOLDERS_H #define SASS_REMOVE_PLACEHOLDERS_H -#pragma once--#include "ast.hpp"+#include "ast_fwd_decl.hpp" #include "operation.hpp" namespace Sass { + class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> { - class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> {+ public: - void fallback_impl(AST_Node_Ptr n) {}+ SelectorList* remove_placeholders(SelectorList*);+ void remove_placeholders(SimpleSelector* simple);+ void remove_placeholders(CompoundSelector* complex);+ void remove_placeholders(ComplexSelector* complex); - public:- Selector_List_Ptr remove_placeholders(Selector_List_Ptr); - public:- Remove_Placeholders();- ~Remove_Placeholders() { }+ public:+ Remove_Placeholders();+ ~Remove_Placeholders() { } - void operator()(Block_Ptr);- void operator()(Ruleset_Ptr);- void operator()(Media_Block_Ptr);- void operator()(Supports_Block_Ptr);- void operator()(Directive_Ptr);+ void operator()(Block*);+ void operator()(Ruleset*);+ void operator()(CssMediaRule*);+ void operator()(Supports_Block*);+ void operator()(Directive*); - template <typename U>- void fallback(U x) { return fallback_impl(x); }- };+ // ignore missed types+ template <typename U>+ void fallback(U x) {}++ }; }
libsass/src/sass.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstdlib> #include <cstring> #include <vector>@@ -7,6 +10,7 @@ #include "sass.h" #include "file.hpp" #include "util.hpp"+#include "context.hpp" #include "sass_context.hpp" #include "sass_functions.hpp"
libsass/src/sass.hpp view
@@ -14,11 +14,17 @@ // aplies to MSVC and MinGW #ifdef _WIN32 // we do not want the ERROR macro-# define NOGDI+# ifndef NOGDI+# define NOGDI+# endif // we do not want the min/max macro-# define NOMINMAX+# ifndef NOMINMAX+# define NOMINMAX+# endif // we do not want the IN/OUT macro-# define _NO_W32_PSEUDO_MODIFIERS+# ifndef _NO_W32_PSEUDO_MODIFIERS+# define _NO_W32_PSEUDO_MODIFIERS+# endif #endif @@ -59,6 +65,7 @@ // only used internal to trigger ruby inspect behavior const static Sass_Output_Style INSPECT = SASS_STYLE_INSPECT; const static Sass_Output_Style TO_SASS = SASS_STYLE_TO_SASS;+ const static Sass_Output_Style TO_CSS = SASS_STYLE_TO_CSS; // helper to aid dreaded MSVC debug mode // see implementation for more details@@ -90,13 +97,10 @@ // Precision for fractional numbers int precision; - // Do not compress colors in selectors- bool in_selector;- // initialization list (constructor with defaults) Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED,- int precision = 5, bool in_selector = false)- : output_style(style), precision(precision), in_selector(in_selector)+ int precision = 10)+ : output_style(style), precision(precision) { } };@@ -125,7 +129,7 @@ // initialization list (constructor with defaults) Sass_Output_Options(Sass_Output_Style style = Sass::NESTED,- int precision = 5,+ int precision = 10, const char* indent = " ", const char* linefeed = "\n", bool source_comments = false)
libsass/src/sass2scss.cpp view
@@ -154,6 +154,21 @@ } + static size_t findFirstCharacter (std::string& sass, size_t pos)+ {+ return sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos);+ }++ static size_t findLastCharacter (std::string& sass, size_t pos)+ {+ return sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, pos);+ }++ static bool isUrl (std::string& sass, size_t pos)+ {+ return sass[pos] == 'u' && sass[pos+1] == 'r' && sass[pos+2] == 'l' && sass[pos+3] == '(';+ }+ // check if there is some char data // will ignore everything in comments static bool hasCharData (std::string& sass)@@ -452,7 +467,7 @@ // right trim input sass = rtrim(sass); - // get postion of first meaningfull character in string+ // get position of first meaningfull character in string size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE); // special case for final run@@ -542,7 +557,7 @@ // default to a selector // change back if property found converter.selector = true;- // get postion of first whitespace char+ // get position of first whitespace char size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left); // assertion check for valid result if (pos_wspace != std::string::npos)@@ -565,7 +580,7 @@ // assertion for valid result if (pos_colon != std::string::npos) {- // ... after the first word (skip begining colons)+ // ... after the first word (skip beginning colons) pos_colon = sass.find_first_of(":", pos_colon); // it is a selector if there was no colon found converter.selector = pos_colon == std::string::npos;@@ -587,6 +602,7 @@ sass.substr(pos_left, 5) == "@warn" || sass.substr(pos_left, 6) == "@debug" || sass.substr(pos_left, 6) == "@error" ||+ sass.substr(pos_left, 6) == "@value" || sass.substr(pos_left, 8) == "@charset" || sass.substr(pos_left, 10) == "@namespace" ) { sass = indent + sass.substr(pos_left); }@@ -606,23 +622,38 @@ { // get positions for the actual import url size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7);- size_t pos_quote = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);- // leave proper urls untouched- if (sass.substr(pos_quote, 4) != "url(")- {- // check if the url appears to be already quoted- if (sass.substr(pos_quote, 1) != "\"" && sass.substr(pos_quote, 1) != "\'")- {- // get position of the last char on the line- size_t pos_end = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);- // assertion check for valid result- if (pos_end != std::string::npos)- {- // add quotes around the full line after the import statement- sass = sass.substr(0, pos_quote) + "\"" + sass.substr(pos_quote, pos_end - pos_quote + 1) + "\"";+ size_t pos = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);+ size_t start = pos;+ bool in_dqstr = false;+ bool in_sqstr = false;+ bool is_escaped = false;+ do {+ if (is_escaped) {+ is_escaped = false;+ }+ else if (sass[pos] == '\\') {+ is_escaped = true;+ }+ else if (sass[pos] == '"') {+ if (!in_sqstr) in_dqstr = ! in_dqstr;+ }+ else if (sass[pos] == '\'') {+ if (!in_dqstr) in_sqstr = ! in_sqstr;+ }+ else if (in_dqstr || in_sqstr) {+ // skip over quoted stuff+ }+ else if (sass[pos] == ',' || sass[pos] == 0) {+ if (sass[start] != '"' && sass[start] != '\'' && !isUrl(sass, start)) {+ size_t end = findLastCharacter(sass, pos - 1) + 1;+ sass = sass.replace(end, 0, "\"");+ sass = sass.replace(start, 0, "\"");+ pos += 2; }+ start = findFirstCharacter(sass, pos + 1); } }+ while (sass[pos++] != 0); } else if (@@ -727,7 +758,7 @@ scss += flush(sass, converter); } - // get postion of last meaningfull char+ // get position of last meaningfull char size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE); // check for invalid result
libsass/src/sass_context.cpp view
@@ -1,20 +1,10 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"-#include <cstring>-#include <stdexcept>-#include <sstream>-#include <string>-#include <vector>--#include "sass.h" #include "ast.hpp"-#include "file.hpp"-#include "json.hpp"-#include "util.hpp"-#include "context.hpp"-#include "sass_context.hpp"+ #include "sass_functions.hpp"-#include "ast_fwd_decl.hpp"-#include "error_handling.hpp"+#include "json.hpp" #define LFEED "\n" @@ -28,6 +18,24 @@ return json_mkstring(str.c_str()); } + static void handle_string_error(Sass_Context* c_ctx, const std::string& msg, int severety)+ {+ std::stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Internal Error: " << msg << std::endl;+ json_append_member(json_err, "status", json_mknumber(severety));+ json_append_member(json_err, "message", json_mkstring(msg.c_str()));+ json_append_member(json_err, "formatted", json_mkstream(msg_stream));+ try { c_ctx->error_json = json_stringify(json_err, " "); }+ catch (...) {}+ c_ctx->error_message = sass_copy_string(msg_stream.str());+ c_ctx->error_text = sass_copy_c_string(msg.c_str());+ c_ctx->error_status = severety;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ static int handle_error(Sass_Context* c_ctx) { try { throw;@@ -67,23 +75,23 @@ } // now create the code trace (ToDo: maybe have util functions?)- if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) {+ if (e.pstate.line != std::string::npos &&+ e.pstate.column != std::string::npos &&+ e.pstate.src != nullptr) { size_t lines = e.pstate.line;- const char* line_beg = e.pstate.src; // scan through src until target line // move line_beg pointer to line start- while (line_beg && *line_beg && lines != 0) {+ const char* line_beg;+ for (line_beg = e.pstate.src; *line_beg != '\0'; ++line_beg) {+ if (lines == 0) break; if (*line_beg == '\n') --lines;- utf8::unchecked::next(line_beg); }- const char* line_end = line_beg; // move line_end before next newline character- while (line_end && *line_end && *line_end != '\n') {- if (*line_end == '\n') break;- if (*line_end == '\r') break;- utf8::unchecked::next(line_end); + const char* line_end;+ for (line_end = line_beg; *line_end != '\0'; ++line_end) {+ if (*line_end == '\n' || *line_end == '\r') break; }- if (line_end && *line_end != 0) ++ line_end;+ if (*line_end != '\0') ++line_end; size_t line_len = line_end - line_beg; size_t move_in = 0; size_t shorten = 0; size_t left_chars = 42; size_t max_chars = 76;@@ -107,96 +115,34 @@ json_append_member(json_err, "message", json_mkstring(e.what())); json_append_member(json_err, "formatted", json_mkstream(msg_stream)); try { c_ctx->error_json = json_stringify(json_err, " "); }- catch (...) {}+ catch (...) {} // silently ignore this error? c_ctx->error_message = sass_copy_string(msg_stream.str()); c_ctx->error_text = sass_copy_c_string(e.what()); c_ctx->error_status = 1; c_ctx->error_file = sass_copy_c_string(e.pstate.path); c_ctx->error_line = e.pstate.line + 1; c_ctx->error_column = e.pstate.column + 1;- c_ctx->error_src = e.pstate.src;+ c_ctx->error_src = sass_copy_c_string(e.pstate.src); c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } catch (std::bad_alloc& ba) { std::stringstream msg_stream;- JsonNode* json_err = json_mkobject();- msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;- json_append_member(json_err, "status", json_mknumber(2));- json_append_member(json_err, "message", json_mkstring(ba.what()));- json_append_member(json_err, "formatted", json_mkstream(msg_stream));- try { c_ctx->error_json = json_stringify(json_err, " "); }- catch (...) {}- c_ctx->error_message = sass_copy_string(msg_stream.str());- c_ctx->error_text = sass_copy_c_string(ba.what());- c_ctx->error_status = 2;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- json_delete(json_err);+ msg_stream << "Unable to allocate memory: " << ba.what();+ handle_string_error(c_ctx, msg_stream.str(), 2); } catch (std::exception& e) {- std::stringstream msg_stream;- JsonNode* json_err = json_mkobject();- msg_stream << "Internal Error: " << e.what() << std::endl;- json_append_member(json_err, "status", json_mknumber(3));- json_append_member(json_err, "message", json_mkstring(e.what()));- json_append_member(json_err, "formatted", json_mkstream(msg_stream));- try { c_ctx->error_json = json_stringify(json_err, " "); }- catch (...) {}- c_ctx->error_message = sass_copy_string(msg_stream.str());- c_ctx->error_text = sass_copy_c_string(e.what());- c_ctx->error_status = 3;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- json_delete(json_err);+ handle_string_error(c_ctx, e.what(), 3); } catch (std::string& e) {- std::stringstream msg_stream;- JsonNode* json_err = json_mkobject();- msg_stream << "Internal Error: " << e << std::endl;- json_append_member(json_err, "status", json_mknumber(4));- json_append_member(json_err, "message", json_mkstring(e.c_str()));- json_append_member(json_err, "formatted", json_mkstream(msg_stream));- try { c_ctx->error_json = json_stringify(json_err, " "); }- catch (...) {}- c_ctx->error_message = sass_copy_string(msg_stream.str());- c_ctx->error_text = sass_copy_c_string(e.c_str());- c_ctx->error_status = 4;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- json_delete(json_err);+ handle_string_error(c_ctx, e, 4); } catch (const char* e) {- std::stringstream msg_stream;- JsonNode* json_err = json_mkobject();- msg_stream << "Internal Error: " << e << std::endl;- json_append_member(json_err, "status", json_mknumber(4));- json_append_member(json_err, "message", json_mkstring(e));- json_append_member(json_err, "formatted", json_mkstream(msg_stream));- try { c_ctx->error_json = json_stringify(json_err, " "); }- catch (...) {}- c_ctx->error_message = sass_copy_string(msg_stream.str());- c_ctx->error_text = sass_copy_c_string(e);- c_ctx->error_status = 4;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- json_delete(json_err);+ handle_string_error(c_ctx, e, 4); } catch (...) {- std::stringstream msg_stream;- JsonNode* json_err = json_mkobject();- msg_stream << "Unknown error occurred" << std::endl;- json_append_member(json_err, "status", json_mknumber(5));- json_append_member(json_err, "message", json_mkstring("unknown"));- try { c_ctx->error_json = json_stringify(json_err, " "); }- catch (...) {}- c_ctx->error_message = sass_copy_string(msg_stream.str());- c_ctx->error_text = sass_copy_c_string("unknown");- c_ctx->error_status = 5;- c_ctx->output_string = 0;- c_ctx->source_map_string = 0;- json_delete(json_err);+ handle_string_error(c_ctx, "unknown", 5); } return c_ctx->error_status; }@@ -212,7 +158,7 @@ { // assert valid pointer- if (compiler == 0) return 0;+ if (compiler == 0) return {}; // The cpp context must be set by now Context* cpp_ctx = compiler->cpp_ctx; Sass_Context* c_ctx = compiler->c_ctx;@@ -233,7 +179,7 @@ // dispatch parse call Block_Obj root(cpp_ctx->parse()); // abort on errors- if (!root) return 0;+ if (!root) return {}; // skip all prefixed files? (ToDo: check srcmap) // IMO source-maps should point to headers already@@ -253,7 +199,7 @@ catch (...) { handle_errors(c_ctx); } // error- return 0;+ return {}; } @@ -331,8 +277,8 @@ c_ctx->error_message = 0; c_ctx->error_status = 0; // reset error position- c_ctx->error_src = 0; c_ctx->error_file = 0;+ c_ctx->error_src = 0; c_ctx->error_line = std::string::npos; c_ctx->error_column = std::string::npos; @@ -381,7 +327,7 @@ inline void init_options (struct Sass_Options* options) {- options->precision = 5;+ options->precision = 10; options->indent = " "; options->linefeed = LFEED; }@@ -396,7 +342,9 @@ Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path) {- SharedObj::setTaint(true); // needed for static colors+ #ifdef DEBUG_SHARED_PTR+ SharedObj::setTaint(true);+ #endif struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(1, sizeof(struct Sass_File_Context)); if (ctx == 0) { std::cerr << "Error allocating memory for file context" << std::endl; return 0; } ctx->type = SASS_CONTEXT_FILE;@@ -413,6 +361,9 @@ Sass_Data_Context* ADDCALL sass_make_data_context(char* source_string) {+ #ifdef DEBUG_SHARED_PTR+ SharedObj::setTaint(true);+ #endif struct Sass_Data_Context* ctx = (struct Sass_Data_Context*) calloc(1, sizeof(struct Sass_Data_Context)); if (ctx == 0) { std::cerr << "Error allocating memory for data context" << std::endl; return 0; } ctx->type = SASS_CONTEXT_DATA;@@ -448,7 +399,7 @@ return data_ctx->error_status; try { if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); }- // empty source string is a valid case, even if not really usefull (different than with file context)+ // empty source string is a valid case, even if not really useful (different than with file context) // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } } catch (...) { return handle_errors(data_ctx) | 1; }@@ -530,7 +481,7 @@ static void sass_clear_options (struct Sass_Options* options) { if (options == 0) return;- // Deallocate custom functions, headers and importes+ // Deallocate custom functions, headers and imports sass_delete_function_list(options->c_functions); sass_delete_importer_list(options->c_importers); sass_delete_importer_list(options->c_headers);@@ -591,6 +542,7 @@ if (ctx->error_text) free(ctx->error_text); if (ctx->error_json) free(ctx->error_json); if (ctx->error_file) free(ctx->error_file);+ if (ctx->error_src) free(ctx->error_src); free_string_array(ctx->included_files); // play safe and reset properties ctx->output_string = 0;@@ -599,6 +551,7 @@ ctx->error_text = 0; ctx->error_json = 0; ctx->error_file = 0;+ ctx->error_src = 0; ctx->included_files = 0; // debug leaked memory #ifdef DEBUG_SHARED_PTR@@ -617,7 +570,7 @@ if (cpp_ctx) delete(cpp_ctx); compiler->cpp_ctx = NULL; compiler->c_ctx = NULL;- compiler->root = NULL;+ compiler->root = {}; free(compiler); } @@ -653,7 +606,7 @@ void ADDCALL sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); } void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); } - // Getters for Sass_Compiler options (get conected sass context)+ // Getters for Sass_Compiler options (get connected sass context) enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler) { return compiler->state; } struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; } struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler) { return compiler->c_ctx; }@@ -697,9 +650,9 @@ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_message); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_text); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_file);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src); IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_line); IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_column);- IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, output_string); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, source_map_string); IMPLEMENT_SASS_CONTEXT_GETTER(char**, included_files);@@ -709,6 +662,7 @@ IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_message); IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_text); IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_file);+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_src); IMPLEMENT_SASS_CONTEXT_TAKER(char*, output_string); IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string); IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files);
libsass/src/sass_context.hpp view
@@ -90,7 +90,7 @@ char* error_file; size_t error_line; size_t error_column;- const char* error_src;+ char* error_src; // report imported files char** included_files;
libsass/src/sass_functions.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstring> #include "util.hpp" #include "context.hpp"@@ -98,7 +101,7 @@ } // Creator for a single import entry returned by the custom importer inside the list- // We take ownership of the memory for source and srcmap (freed when context is destroyd)+ // We take ownership of the memory for source and srcmap (freed when context is destroyed) Sass_Import_Entry ADDCALL sass_make_import(const char* imp_path, const char* abs_path, char* source, char* srcmap) { Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import));@@ -167,21 +170,21 @@ // Getters and Setters for environments (lexical, local and global) union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame env, const char* name) {- Expression_Ptr ex = Cast<Expression>((*env->frame)[name]);+ Expression* ex = Cast<Expression>((*env->frame)[name]); return ex != NULL ? ast_node_to_sass_value(ex) : NULL; } void ADDCALL sass_env_set_lexical (Sass_Env_Frame env, const char* name, union Sass_Value* val) { (*env->frame)[name] = sass_value_to_ast_node(val); } union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame env, const char* name) {- Expression_Ptr ex = Cast<Expression>(env->frame->get_local(name));+ Expression* ex = Cast<Expression>(env->frame->get_local(name)); return ex != NULL ? ast_node_to_sass_value(ex) : NULL; } void ADDCALL sass_env_set_local (Sass_Env_Frame env, const char* name, union Sass_Value* val) { env->frame->set_local(name, sass_value_to_ast_node(val)); } union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame env, const char* name) {- Expression_Ptr ex = Cast<Expression>(env->frame->get_global(name));+ Expression* ex = Cast<Expression>(env->frame->get_global(name)); return ex != NULL ? ast_node_to_sass_value(ex) : NULL; } void ADDCALL sass_env_set_global (Sass_Env_Frame env, const char* name, union Sass_Value* val) {
libsass/src/sass_functions.hpp view
@@ -3,7 +3,7 @@ #include "sass.h" #include "environment.hpp"-#include "functions.hpp"+#include "fn_utils.hpp" // Struct to hold custom function callback struct Sass_Function {
− libsass/src/sass_util.cpp
@@ -1,149 +0,0 @@-#include "sass.hpp"-#include "node.hpp"--namespace Sass {--- /*- # This is the equivalent of ruby's Sass::Util.paths.- #- # Return an array of all possible paths through the given arrays.- #- # @param arrs [NodeCollection<NodeCollection<Node>>]- # @return [NodeCollection<NodeCollection<Node>>]- #- # @example- # paths([[1, 2], [3, 4], [5]]) #=>- # # [[1, 3, 5],- # # [2, 3, 5],- # # [1, 4, 5],- # # [2, 4, 5]]-- The following is the modified version of the ruby code that was more portable to C++. You- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.-- def paths(arrs)- // I changed the inject and maps to an iterative approach to make it easier to implement in C++- loopStart = [[]]-- for arr in arrs do- permutations = []- for e in arr do- for path in loopStart do- permutations.push(path + [e])- end- end- loopStart = permutations- end- end- */- Node paths(const Node& arrs) {-- Node loopStart = Node::createCollection();- loopStart.collection()->push_back(Node::createCollection());-- for (NodeDeque::iterator arrsIter = arrs.collection()->begin(), arrsEndIter = arrs.collection()->end();- arrsIter != arrsEndIter; ++arrsIter) {-- Node& arr = *arrsIter;-- Node permutations = Node::createCollection();-- for (NodeDeque::iterator arrIter = arr.collection()->begin(), arrIterEnd = arr.collection()->end();- arrIter != arrIterEnd; ++arrIter) {-- Node& e = *arrIter;-- for (NodeDeque::iterator loopStartIter = loopStart.collection()->begin(), loopStartIterEnd = loopStart.collection()->end();- loopStartIter != loopStartIterEnd; ++loopStartIter) {-- Node& path = *loopStartIter;-- Node newPermutation = Node::createCollection();- newPermutation.got_line_feed = arr.got_line_feed;- newPermutation.plus(path);- newPermutation.collection()->push_back(e);-- permutations.collection()->push_back(newPermutation);- }- }-- loopStart = permutations;- }-- return loopStart;- }--- /*- This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten.- Sass::Util.flatten requires the number of levels to flatten, while- [].flatten doesn't and will flatten the entire array. This function- supports both.-- # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened- #- # @param arr [NodeCollection] The array to flatten- # @param n [int] The number of levels to flatten- # @return [NodeCollection] The flattened array-- The following is the modified version of the ruby code that was more portable to C++. You- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.-- def flatten(arr, n = -1)- if n != -1 and n == 0 then- return arr- end-- flattened = []-- for e in arr do- if e.is_a?(Array) then- flattened.concat(flatten(e, n - 1))- else- flattened << e- end- end-- return flattened- end- */- Node flatten(Node& arr, int n) {- if (n != -1 && n == 0) {- return arr;- }-- Node flattened = Node::createCollection();- if (arr.got_line_feed) flattened.got_line_feed = true;-- for (NodeDeque::iterator iter = arr.collection()->begin(), iterEnd = arr.collection()->end();- iter != iterEnd; iter++) {- Node& e = *iter;-- // e has the lf set- if (e.isCollection()) {-- // e.collection().got_line_feed = e.got_line_feed;- Node recurseFlattened = flatten(e, n - 1);-- if(e.got_line_feed) {- flattened.got_line_feed = e.got_line_feed;- recurseFlattened.got_line_feed = e.got_line_feed;- }-- for(auto i : (*recurseFlattened.collection())) {- if (recurseFlattened.got_line_feed) {-- i.got_line_feed = true;- }- flattened.collection()->push_back(i);- }-- } else {- flattened.collection()->push_back(e);- }- }-- return flattened;- }-}
− libsass/src/sass_util.hpp
@@ -1,256 +0,0 @@-#ifndef SASS_SASS_UTIL_H-#define SASS_SASS_UTIL_H--#include "ast.hpp"-#include "node.hpp"-#include "debug.hpp"--namespace Sass {----- /*- This is for ports of functions in the Sass:Util module.- */--- /*- # Return a Node collection of all possible paths through the given Node collection of Node collections.- #- # @param arrs [NodeCollection<NodeCollection<Node>>]- # @return [NodeCollection<NodeCollection<Node>>]- #- # @example- # paths([[1, 2], [3, 4], [5]]) #=>- # # [[1, 3, 5],- # # [2, 3, 5],- # # [1, 4, 5],- # # [2, 4, 5]]- */- Node paths(const Node& arrs);--- /*- This class is a default implementation of a Node comparator that can be passed to the lcs function below.- It uses operator== for equality comparision. It then returns one if the Nodes are equal.- */- class DefaultLcsComparator {- public:- bool operator()(const Node& one, const Node& two, Node& out) const {- // TODO: Is this the correct C++ interpretation?- // block ||= proc {|a, b| a == b && a}- if (one == two) {- out = one;- return true;- }-- return false;- }- };--- typedef std::vector<std::vector<int> > LCSTable;--- /*- This is the equivalent of ruby's Sass::Util.lcs_backtrace.-- # Computes a single longest common subsequence for arrays x and y.- # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS- */- template<typename ComparatorType>- Node lcs_backtrace(const LCSTable& c, const Node& x, const Node& y, int i, int j, const ComparatorType& comparator) {- DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)-- if (i == 0 || j == 0) {- DEBUG_PRINTLN(LCS, "RETURNING EMPTY")- return Node::createCollection();- }-- NodeDeque& xChildren = *(x.collection());- NodeDeque& yChildren = *(y.collection());-- Node compareOut = Node::createNil();- if (comparator(xChildren[i], yChildren[j], compareOut)) {- DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")- Node result = lcs_backtrace(c, x, y, i - 1, j - 1, comparator);- result.collection()->push_back(compareOut);- return result;- }-- if (c[i][j - 1] > c[i - 1][j]) {- DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")- return lcs_backtrace(c, x, y, i, j - 1, comparator);- }-- DEBUG_PRINTLN(LCS, "FINAL RETURN")- return lcs_backtrace(c, x, y, i - 1, j, comparator);- }--- /*- This is the equivalent of ruby's Sass::Util.lcs_table.-- # Calculates the memoization table for the Least Common Subsequence algorithm.- # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS- */- template<typename ComparatorType>- void lcs_table(const Node& x, const Node& y, const ComparatorType& comparator, LCSTable& out) {- DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)-- NodeDeque& xChildren = *(x.collection());- NodeDeque& yChildren = *(y.collection());-- LCSTable c(xChildren.size(), std::vector<int>(yChildren.size()));-- // These shouldn't be necessary since the vector will be initialized to 0 already.- // x.size.times {|i| c[i][0] = 0}- // y.size.times {|j| c[0][j] = 0}-- for (size_t i = 1; i < xChildren.size(); i++) {- for (size_t j = 1; j < yChildren.size(); j++) {- Node compareOut = Node::createNil();-- if (comparator(xChildren[i], yChildren[j], compareOut)) {- c[i][j] = c[i - 1][j - 1] + 1;- } else {- c[i][j] = std::max(c[i][j - 1], c[i - 1][j]);- }- }- }-- out = c;- }--- /*- This is the equivalent of ruby's Sass::Util.lcs.-- # Computes a single longest common subsequence for `x` and `y`.- # If there are more than one longest common subsequences,- # the one returned is that which starts first in `x`.-- # @param x [NodeCollection]- # @param y [NodeCollection]- # @comparator An equality check between elements of `x` and `y`.- # @return [NodeCollection] The LCS-- http://en.wikipedia.org/wiki/Longest_common_subsequence_problem- */- template<typename ComparatorType>- Node lcs(Node& x, Node& y, const ComparatorType& comparator) {- DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)-- Node newX = Node::createCollection();- newX.collection()->push_back(Node::createNil());- newX.plus(x);-- Node newY = Node::createCollection();- newY.collection()->push_back(Node::createNil());- newY.plus(y);-- LCSTable table;- lcs_table(newX, newY, comparator, table);-- return lcs_backtrace(table, newX, newY, static_cast<int>(newX.collection()->size()) - 1, static_cast<int>(newY.collection()->size()) - 1, comparator);- }--- /*- This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten.- Sass::Util.flatten requires the number of levels to flatten, while- [].flatten doesn't and will flatten the entire array. This function- supports both.-- # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened- #- # @param arr [NodeCollection] The array to flatten- # @param n [int] The number of levels to flatten- # @return [NodeCollection] The flattened array- */- Node flatten(Node& arr, int n = -1);--- /*- This is the equivalent of ruby's Sass::Util.group_by_to_a.-- # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed- # order. Unlike [#hash_to_a], the resulting order isn't sorted key order;- # instead, it's the same order as `#group_by` has under Ruby 1.9 (key- # appearance order).- #- # @param enum [Enumerable]- # @return [Array<[Object, Array]>] An array of pairs.-- TODO: update @param and @return once I know what those are.-- The following is the modified version of the ruby code that was more portable to C++. You- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.-- def group_by_to_a(enum, &block)- order = {}-- arr = []-- grouped = {}-- for e in enum do- key = block[e]- unless order.include?(key)- order[key] = order.size- end-- if not grouped.has_key?(key) then- grouped[key] = [e]- else- grouped[key].push(e)- end- end-- grouped.each do |key, vals|- arr[order[key]] = [key, vals]- end-- arr- end-- */- template<typename EnumType, typename KeyType, typename KeyFunctorType>- void group_by_to_a(std::vector<EnumType>& enumeration, KeyFunctorType& keyFunc, std::vector<std::pair<KeyType, std::vector<EnumType> > >& arr /*out*/) {-- std::map<unsigned int, KeyType> order;-- std::map<size_t, std::vector<EnumType> > grouped;-- for (typename std::vector<EnumType>::iterator enumIter = enumeration.begin(), enumIterEnd = enumeration.end(); enumIter != enumIterEnd; enumIter++) {- EnumType& e = *enumIter;-- KeyType key = keyFunc(e);-- if (grouped.find(key->hash()) == grouped.end()) {- order.insert(std::make_pair((unsigned int)order.size(), key));-- std::vector<EnumType> newCollection;- newCollection.push_back(e);- grouped.insert(std::make_pair(key->hash(), newCollection));- } else {- std::vector<EnumType>& collection = grouped.at(key->hash());- collection.push_back(e);- }- }-- for (unsigned int index = 0; index < order.size(); index++) {- KeyType& key = order.at(index);- std::vector<EnumType>& values = grouped.at(key->hash());-- std::pair<KeyType, std::vector<EnumType> > grouping = std::make_pair(key, values);-- arr.push_back(grouping);- }- }---}--#endif
libsass/src/sass_values.cpp view
@@ -1,9 +1,11 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <cstdlib> #include <cstring> #include "util.hpp" #include "eval.hpp"-#include "values.hpp" #include "operators.hpp" #include "sass/values.h" #include "sass_values.hpp"@@ -290,7 +292,7 @@ union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b) { - Sass::Value_Ptr rv;+ Sass::Value_Obj rv; try { @@ -312,28 +314,34 @@ } if (sass_value_is_number(a) && sass_value_is_number(b)) {- Number_Ptr_Const l_n = Cast<Number>(lhs);- Number_Ptr_Const r_n = Cast<Number>(rhs);+ const Number* l_n = Cast<Number>(lhs);+ const Number* r_n = Cast<Number>(rhs); rv = Operators::op_numbers(op, *l_n, *r_n, options, l_n->pstate()); } else if (sass_value_is_number(a) && sass_value_is_color(a)) {- Number_Ptr_Const l_n = Cast<Number>(lhs);- Color_Ptr_Const r_c = Cast<Color>(rhs);+ const Number* l_n = Cast<Number>(lhs);+ // Direct HSLA operations are not supported+ // All color maths will be deprecated anyway+ Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA(); rv = Operators::op_number_color(op, *l_n, *r_c, options, l_n->pstate()); } else if (sass_value_is_color(a) && sass_value_is_number(b)) {- Color_Ptr_Const l_c = Cast<Color>(lhs);- Number_Ptr_Const r_n = Cast<Number>(rhs);+ // Direct HSLA operations are not supported+ // All color maths will be deprecated anyway+ Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+ const Number* r_n = Cast<Number>(rhs); rv = Operators::op_color_number(op, *l_c, *r_n, options, l_c->pstate()); } else if (sass_value_is_color(a) && sass_value_is_color(b)) {- Color_Ptr_Const l_c = Cast<Color>(lhs);- Color_Ptr_Const r_c = Cast<Color>(rhs);+ // Direct HSLA operations are not supported+ // All color maths will be deprecated anyway+ Color_RGBA_Obj l_c = Cast<Color>(lhs)->toRGBA();+ Color_RGBA_Obj r_c = Cast<Color>(rhs)->toRGBA(); rv = Operators::op_colors(op, *l_c, *r_c, options, l_c->pstate()); } else /* convert other stuff to string and apply operation */ {- Value_Ptr l_v = Cast<Value>(lhs);- Value_Ptr r_v = Cast<Value>(rhs);+ Value* l_v = Cast<Value>(lhs);+ Value* r_v = Cast<Value>(rhs); rv = Operators::op_strings(op, *l_v, *r_v, options, l_v->pstate()); } @@ -341,8 +349,7 @@ if (!rv) return sass_make_error("invalid return value"); // convert result back to ast node- return ast_node_to_sass_value(rv);-+ return ast_node_to_sass_value(rv.ptr()); } // simply pass the error message back to the caller for now
libsass/src/source_map.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <string> #include <sstream> #include <iostream>@@ -170,12 +173,12 @@ current_position += offset; } - void SourceMap::add_open_mapping(const AST_Node_Ptr node)+ void SourceMap::add_open_mapping(const AST_Node* node) { mappings.push_back(Mapping(node->pstate(), current_position)); } - void SourceMap::add_close_mapping(const AST_Node_Ptr node)+ void SourceMap::add_close_mapping(const AST_Node* node) { mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position)); }
libsass/src/source_map.hpp view
@@ -28,8 +28,8 @@ void prepend(const Offset& offset); void append(const OutputBuffer& out); void prepend(const OutputBuffer& out);- void add_open_mapping(const AST_Node_Ptr node);- void add_close_mapping(const AST_Node_Ptr node);+ void add_open_mapping(const AST_Node* node);+ void add_close_mapping(const AST_Node* node); std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate);
+ libsass/src/stylesheet.cpp view
@@ -0,0 +1,22 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "stylesheet.hpp"++namespace Sass {++ // Constructor+ Sass::StyleSheet::StyleSheet(const Resource& res, Block_Obj root) :+ Resource(res),+ root(root)+ {+ }++ StyleSheet::StyleSheet(const StyleSheet& sheet) :+ Resource(sheet),+ root(sheet.root)+ {+ }++}
+ libsass/src/stylesheet.hpp view
@@ -0,0 +1,57 @@+#ifndef SASS_STYLESHEET_H+#define SASS_STYLESHEET_H++// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris.+#include "sass.hpp"++#include "ast_fwd_decl.hpp"+#include "extender.hpp"+#include "file.hpp"++namespace Sass {++ // parsed stylesheet from loaded resource+ // this should be a `Module` for sass 4.0+ class StyleSheet : public Resource {+ public:++ // The canonical URL for this module's source file. This may be `null`+ // if the module was loaded from a string without a URL provided.+ // Uri get url;++ // Modules that this module uses.+ // List<Module> get upstream;++ // The module's variables.+ // Map<String, Value> get variables;++ // The module's functions. Implementations must ensure+ // that each [Callable] is stored under its own name.+ // Map<String, Callable> get functions;++ // The module's mixins. Implementations must ensure that+ // each [Callable] is stored under its own name.+ // Map<String, Callable> get mixins;++ // The extensions defined in this module, which is also able to update+ // [css]'s style rules in-place based on downstream extensions.+ // Extender extender;++ // The module's CSS tree.+ Block_Obj root;++ public:++ // default argument constructor+ StyleSheet(const Resource& res, Block_Obj root);++ // Copy constructor+ StyleSheet(const StyleSheet& res);++ };+++}++#endif
− libsass/src/subset_map.cpp
@@ -1,55 +0,0 @@-#include "sass.hpp"-#include "ast.hpp"-#include "subset_map.hpp"--namespace Sass {-- void Subset_Map::put(const Compound_Selector_Obj& sel, const SubSetMapPair& value)- {- if (sel->empty()) throw std::runtime_error("internal error: subset map keys may not be empty");- size_t index = values_.size();- values_.push_back(value);- for (size_t i = 0, S = sel->length(); i < S; ++i)- {- hash_[(*sel)[i]].push_back(std::make_pair(sel, index));- }- }-- std::vector<SubSetMapPair> Subset_Map::get_kv(const Compound_Selector_Obj& sel)- {- SimpleSelectorDict dict(sel->begin(), sel->end()); // XXX Set- std::vector<size_t> indices;- for (size_t i = 0, S = sel->length(); i < S; ++i) {- if (!hash_.count((*sel)[i])) {- continue;- }- const std::vector<std::pair<Compound_Selector_Obj, size_t> >& subsets = hash_[(*sel)[i]];- for (const std::pair<Compound_Selector_Obj, size_t>& item : subsets) {- bool include = true;- for (const Simple_Selector_Obj& it : item.first->elements()) {- auto found = dict.find(it);- if (found == dict.end()) {- include = false;- break;- }- }- if (include) indices.push_back(item.second);- }- }- sort(indices.begin(), indices.end());- std::vector<size_t>::iterator indices_end = unique(indices.begin(), indices.end());- indices.resize(distance(indices.begin(), indices_end));-- std::vector<SubSetMapPair> results;- for (size_t i = 0, S = indices.size(); i < S; ++i) {- results.push_back(values_[indices[i]]);- }- return results;- }-- std::vector<SubSetMapPair> Subset_Map::get_v(const Compound_Selector_Obj& sel)- {- return get_kv(sel);- }--}
− libsass/src/subset_map.hpp
@@ -1,76 +0,0 @@-#ifndef SASS_SUBSET_MAP_H-#define SASS_SUBSET_MAP_H--#include <map>-#include <set>-#include <vector>-#include <algorithm>-#include <iterator>--#include "ast_fwd_decl.hpp"---// #include <iostream>-// #include <sstream>-// template<typename T>-// std::string vector_to_string(std::vector<T> v)-// {-// std::stringstream buffer;-// buffer << "[";--// if (!v.empty())-// { buffer << v[0]; }-// else-// { buffer << "]"; }--// if (v.size() == 1)-// { buffer << "]"; }-// else-// {-// for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i];-// buffer << "]";-// }--// return buffer.str();-// }--// template<typename T>-// std::string set_to_string(set<T> v)-// {-// std::stringstream buffer;-// buffer << "[";-// typename std::set<T>::iterator i = v.begin();-// if (!v.empty())-// { buffer << *i; }-// else-// { buffer << "]"; }--// if (v.size() == 1)-// { buffer << "]"; }-// else-// {-// for (++i; i != v.end(); ++i) buffer << ", " << *i;-// buffer << "]";-// }--// return buffer.str();-// }--namespace Sass {-- class Subset_Map {- private:- std::vector<SubSetMapPair> values_;- std::map<Simple_Selector_Obj, std::vector<std::pair<Compound_Selector_Obj, size_t> >, OrderNodes > hash_;- public:- void put(const Compound_Selector_Obj& sel, const SubSetMapPair& value);- std::vector<SubSetMapPair> get_kv(const Compound_Selector_Obj& s);- std::vector<SubSetMapPair> get_v(const Compound_Selector_Obj& s);- bool empty() { return values_.empty(); }- void clear() { values_.clear(); hash_.clear(); }- const std::vector<SubSetMapPair> values(void) { return values_; }- };--}--#endif
− libsass/src/to_c.cpp
@@ -1,74 +0,0 @@-#include "sass.hpp"-#include "to_c.hpp"-#include "ast.hpp"--namespace Sass {-- union Sass_Value* To_C::fallback_impl(AST_Node_Ptr n)- { return sass_make_error("unknown type for C-API"); }-- union Sass_Value* To_C::operator()(Boolean_Ptr b)- { return sass_make_boolean(b->value()); }-- union Sass_Value* To_C::operator()(Number_Ptr n)- { return sass_make_number(n->value(), n->unit().c_str()); }-- union Sass_Value* To_C::operator()(Custom_Warning_Ptr w)- { return sass_make_warning(w->message().c_str()); }-- union Sass_Value* To_C::operator()(Custom_Error_Ptr e)- { return sass_make_error(e->message().c_str()); }-- union Sass_Value* To_C::operator()(Color_Ptr c)- { return sass_make_color(c->r(), c->g(), c->b(), c->a()); }-- union Sass_Value* To_C::operator()(String_Constant_Ptr s)- {- if (s->quote_mark()) {- return sass_make_qstring(s->value().c_str());- } else {- return sass_make_string(s->value().c_str());- }- }-- union Sass_Value* To_C::operator()(String_Quoted_Ptr s)- { return sass_make_qstring(s->value().c_str()); }-- union Sass_Value* To_C::operator()(List_Ptr l)- {- union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed());- for (size_t i = 0, L = l->length(); i < L; ++i) {- sass_list_set_value(v, i, (*l)[i]->perform(this));- }- return v;- }-- union Sass_Value* To_C::operator()(Map_Ptr m)- {- union Sass_Value* v = sass_make_map(m->length());- int i = 0;- for (auto key : m->keys()) {- sass_map_set_key(v, i, key->perform(this));- sass_map_set_value(v, i, m->at(key)->perform(this));- i++;- }- return v;- }-- union Sass_Value* To_C::operator()(Arguments_Ptr a)- {- union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false);- for (size_t i = 0, L = a->length(); i < L; ++i) {- sass_list_set_value(v, i, (*a)[i]->perform(this));- }- return v;- }-- union Sass_Value* To_C::operator()(Argument_Ptr a)- { return a->value()->perform(this); }-- // not strictly necessary because of the fallback- union Sass_Value* To_C::operator()(Null_Ptr n)- { return sass_make_null(); }--};
− libsass/src/to_c.hpp
@@ -1,39 +0,0 @@-#ifndef SASS_TO_C_H-#define SASS_TO_C_H--#include "ast_fwd_decl.hpp"-#include "operation.hpp"-#include "sass/values.h"--namespace Sass {-- class To_C : public Operation_CRTP<union Sass_Value*, To_C> {- // override this to define a catch-all- union Sass_Value* fallback_impl(AST_Node_Ptr n);-- public:-- To_C() { }- ~To_C() { }-- union Sass_Value* operator()(Boolean_Ptr);- union Sass_Value* operator()(Number_Ptr);- union Sass_Value* operator()(Color_Ptr);- union Sass_Value* operator()(String_Constant_Ptr);- union Sass_Value* operator()(String_Quoted_Ptr);- union Sass_Value* operator()(Custom_Warning_Ptr);- union Sass_Value* operator()(Custom_Error_Ptr);- union Sass_Value* operator()(List_Ptr);- union Sass_Value* operator()(Map_Ptr);- union Sass_Value* operator()(Null_Ptr);- union Sass_Value* operator()(Arguments_Ptr);- union Sass_Value* operator()(Argument_Ptr);-- // dispatch to fallback implementation- union Sass_Value* fallback(AST_Node_Ptr x)- { return fallback_impl(x); }- };--}--#endif
libsass/src/to_value.cpp view
@@ -1,60 +1,62 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "ast.hpp" #include "to_value.hpp" namespace Sass { - Value_Ptr To_Value::fallback_impl(AST_Node_Ptr n)- {- // throw a runtime error if this happens- // we want a well defined set of possible nodes- throw std::runtime_error("invalid node for to_value");- }- // Custom_Error is a valid value- Value_Ptr To_Value::operator()(Custom_Error_Ptr e)+ Value* To_Value::operator()(Custom_Error* e) { return e; } // Custom_Warning is a valid value- Value_Ptr To_Value::operator()(Custom_Warning_Ptr w)+ Value* To_Value::operator()(Custom_Warning* w) { return w; } // Boolean is a valid value- Value_Ptr To_Value::operator()(Boolean_Ptr b)+ Value* To_Value::operator()(Boolean* b) { return b; } // Number is a valid value- Value_Ptr To_Value::operator()(Number_Ptr n)+ Value* To_Value::operator()(Number* n) { return n; } // Color is a valid value- Value_Ptr To_Value::operator()(Color_Ptr c)+ Value* To_Value::operator()(Color_RGBA* c) { return c; } + // Color is a valid value+ Value* To_Value::operator()(Color_HSLA* c)+ {+ return c;+ }+ // String_Constant is a valid value- Value_Ptr To_Value::operator()(String_Constant_Ptr s)+ Value* To_Value::operator()(String_Constant* s) { return s; } // String_Quoted is a valid value- Value_Ptr To_Value::operator()(String_Quoted_Ptr s)+ Value* To_Value::operator()(String_Quoted* s) { return s; } // List is a valid value- Value_Ptr To_Value::operator()(List_Ptr l)+ Value* To_Value::operator()(List* l) { List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(),@@ -69,32 +71,32 @@ } // Map is a valid value- Value_Ptr To_Value::operator()(Map_Ptr m)+ Value* To_Value::operator()(Map* m) { return m; } // Null is a valid value- Value_Ptr To_Value::operator()(Null_Ptr n)+ Value* To_Value::operator()(Null* n) { return n; } // Function is a valid value- Value_Ptr To_Value::operator()(Function_Ptr n)+ Value* To_Value::operator()(Function* n) { return n; } // Argument returns its value- Value_Ptr To_Value::operator()(Argument_Ptr arg)+ Value* To_Value::operator()(Argument* arg) { if (!arg->name().empty()) return 0; return arg->value()->perform(this); } - // Selector_List is converted to a string- Value_Ptr To_Value::operator()(Selector_List_Ptr s)+ // SelectorList is converted to a string+ Value* To_Value::operator()(SelectorList* s) { return SASS_MEMORY_NEW(String_Quoted, s->pstate(),@@ -102,7 +104,7 @@ } // Binary_Expression is converted to a string- Value_Ptr To_Value::operator()(Binary_Expression_Ptr s)+ Value* To_Value::operator()(Binary_Expression* s) { return SASS_MEMORY_NEW(String_Quoted, s->pstate(),
libsass/src/to_value.hpp view
@@ -7,9 +7,7 @@ namespace Sass { - class To_Value : public Operation_CRTP<Value_Ptr, To_Value> {-- Value_Ptr fallback_impl(AST_Node_Ptr n);+ class To_Value : public Operation_CRTP<Value*, To_Value> { private: @@ -21,28 +19,26 @@ : ctx(ctx) { } ~To_Value() { }- using Operation<Value_Ptr>::operator();+ using Operation<Value*>::operator(); - Value_Ptr operator()(Argument_Ptr);- Value_Ptr operator()(Boolean_Ptr);- Value_Ptr operator()(Number_Ptr);- Value_Ptr operator()(Color_Ptr);- Value_Ptr operator()(String_Constant_Ptr);- Value_Ptr operator()(String_Quoted_Ptr);- Value_Ptr operator()(Custom_Warning_Ptr);- Value_Ptr operator()(Custom_Error_Ptr);- Value_Ptr operator()(List_Ptr);- Value_Ptr operator()(Map_Ptr);- Value_Ptr operator()(Null_Ptr);- Value_Ptr operator()(Function_Ptr);+ Value* operator()(Argument*);+ Value* operator()(Boolean*);+ Value* operator()(Number*);+ Value* operator()(Color_RGBA*);+ Value* operator()(Color_HSLA*);+ Value* operator()(String_Constant*);+ Value* operator()(String_Quoted*);+ Value* operator()(Custom_Warning*);+ Value* operator()(Custom_Error*);+ Value* operator()(List*);+ Value* operator()(Map*);+ Value* operator()(Null*);+ Value* operator()(Function*); // convert to string via `To_String`- Value_Ptr operator()(Selector_List_Ptr);- Value_Ptr operator()(Binary_Expression_Ptr);+ Value* operator()(SelectorList*);+ Value* operator()(Binary_Expression*); - // fallback throws error- template <typename U>- Value_Ptr fallback(U x) { return fallback_impl(x); } }; }
libsass/src/units.cpp view
@@ -1,5 +1,7 @@ #include "sass.hpp"+#include <map> #include <stdexcept>+#include <algorithm> #include "units.hpp" #include "error_handling.hpp" @@ -266,6 +268,10 @@ return (numerators == rhs.numerators) && (denominators == rhs.denominators); }+ bool Units::operator!= (const Units& rhs) const+ {+ return ! (*this == rhs);+ } double Units::normalize() {@@ -324,7 +330,7 @@ // has the advantage that they will be pre-sorted std::map<std::string, int> exponents; - // initialize by summing up occurences in unit vectors+ // initialize by summing up occurrences in unit vectors // this will already cancel out equivalent units (e.q. px/px) for (size_t i = 0; i < iL; i ++) exponents[numerators[i]] += 1; for (size_t n = 0; n < nL; n ++) exponents[denominators[n]] -= 1;@@ -390,7 +396,7 @@ denominators.size() == 0; } - // this does not cover all cases (multiple prefered units)+ // this does not cover all cases (multiple preferred units) double Units::convert_factor(const Units& r) const { @@ -461,7 +467,7 @@ { // get and increment afterwards const std::string r_den = *(r_den_it);- // get possible converstion factor for units+ // get possible conversion factor for units double conversion = conversion_factor(l_den, r_den); // skip incompatible denominator if (conversion == 0) {
libsass/src/units.hpp view
@@ -83,6 +83,7 @@ // compare operations bool operator< (const Units& rhs) const; bool operator== (const Units& rhs) const;+ bool operator!= (const Units& rhs) const; // factor to convert into given units double convert_factor(const Units&) const; };
libsass/src/utf8/checked.h view
@@ -1,4 +1,4 @@-// Copyright 2006 Nemanja Trifunovic+// Copyright 2006-2016 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization@@ -41,7 +41,7 @@ class invalid_code_point : public exception { uint32_t cp; public:- invalid_code_point(uint32_t cp) : cp(cp) {}+ invalid_code_point(uint32_t codepoint) : cp(codepoint) {} virtual const char* what() const throw() { return "Invalid code point"; } uint32_t code_point() const {return cp;} };@@ -107,7 +107,9 @@ *out++ = *it; break; case internal::NOT_ENOUGH_ROOM:- throw not_enough_room();+ out = utf8::append (replacement, out);+ start = end;+ break; case internal::INVALID_LEAD: out = utf8::append (replacement, out); ++start;@@ -194,10 +196,10 @@ } template <typename octet_iterator, typename distance_type>- void retreat (octet_iterator& it, distance_type n, octet_iterator start)+ void retreat (octet_iterator& it, distance_type n, octet_iterator end) { for (distance_type i = 0; i < n; ++i)- utf8::prior(it, start);+ utf8::prior(it, end); } template <typename octet_iterator>@@ -240,7 +242,7 @@ template <typename u16bit_iterator, typename octet_iterator> u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) {- while (start != end) {+ while (start < end) { uint32_t cp = utf8::next(start, end); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);@@ -264,7 +266,7 @@ template <typename octet_iterator, typename u32bit_iterator> u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) {- while (start != end)+ while (start < end) (*result++) = utf8::next(start, end); return result;@@ -279,9 +281,9 @@ public: iterator () {} explicit iterator (const octet_iterator& octet_it,- const octet_iterator& range_start,- const octet_iterator& range_end) :- it(octet_it), range_start(range_start), range_end(range_end)+ const octet_iterator& rangestart,+ const octet_iterator& rangeend) :+ it(octet_it), range_start(rangestart), range_end(rangeend) { if (it < range_start || it > range_end) throw std::out_of_range("Invalid utf-8 iterator position");
libsass/src/utf8/core.h view
@@ -222,6 +222,9 @@ template <typename octet_iterator> utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) {+ if (it == end)+ return NOT_ENOUGH_ROOM;+ // Save the original value of it so we can go back in case of failure // Of course, it does not make much sense with i.e. stream iterators octet_iterator original_it = it;
libsass/src/utf8_string.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include <string> #include <vector> #include <cstdlib>
libsass/src/util.cpp view
@@ -2,6 +2,7 @@ #include "sass.h" #include "ast.hpp" #include "util.hpp"+#include "util_string.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "constants.hpp"@@ -26,8 +27,8 @@ #endif // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93- if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);- else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val);+ if (std::fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);+ else if (std::fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val); // work around some compiler issue // cygwin has it not defined in std using namespace std;@@ -163,42 +164,64 @@ std::replace(str.begin(), str.end(), '\n', ' '); } - // bell characters are replaced with spaces- // also eats spaces after line-feeds (ltrim)+ // 1. Removes whitespace after newlines.+ // 2. Replaces newlines with spaces.+ //+ // This method only considers LF and CRLF as newlines. std::string string_to_output(const std::string& str) {- std::string out("");- bool lf = false;- for (auto i : str) {- if (i == '\n') {- out += ' ';- lf = true;- } else if (!(lf && isspace(i))) {- out += i;- lf = false;+ std::string result;+ result.reserve(str.size());+ std::size_t pos = 0;+ while (true) {+ const std::size_t newline = str.find_first_of("\n\r", pos);+ if (newline == std::string::npos) break;+ result.append(str, pos, newline - pos);+ if (str[newline] == '\r') {+ if (str[newline + 1] == '\n') {+ pos = newline + 2;+ } else {+ // CR without LF: append as-is and continue.+ result += '\r';+ pos = newline + 1;+ continue;+ }+ } else {+ pos = newline + 1; }+ result += ' ';+ const std::size_t non_space = str.find_first_not_of(" \f\n\r\t\v", pos);+ if (non_space != std::string::npos) {+ pos = non_space;+ } }- return out;+ result.append(str, pos, std::string::npos);+ return result; } std::string escape_string(const std::string& str) {- std::string out("");- for (auto i : str) {- if (i == '\n') {- out += "\\n";- } else if (i == '\r') {- out += "\\r";- } else if (i == '\t') {- out += "\\t";- } else {- out += i;+ std::string out;+ out.reserve(str.size());+ for (char c : str) {+ switch (c) {+ case '\n':+ out.append("\\n");+ break;+ case '\r':+ out.append("\\r");+ break;+ case '\f':+ out.append("\\f");+ break;+ default:+ out += c; } } return out; } - std::string comment_to_string(const std::string& text)+ std::string comment_to_compact_string(const std::string& text) { std::string str = ""; size_t has = 0;@@ -207,7 +230,6 @@ for (auto i : text) { if (clean) { if (i == '\n') { has = 0; }- else if (i == '\r') { has = 0; } else if (i == '\t') { ++ has; } else if (i == ' ') { ++ has; } else if (i == '*') {}@@ -219,8 +241,6 @@ } } else if (i == '\n') { clean = true;- } else if (i == '\r') {- clean = true; } else { str += i; }@@ -270,7 +290,7 @@ // parse as many sequence chars as possible // ToDo: Check if ruby aborts after possible max- while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;+ while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len; if (len > 1) { @@ -356,7 +376,7 @@ // parse as many sequence chars as possible // ToDo: Check if ruby aborts after possible max- while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;+ while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len; // hex string? if (keep_utf8_sequences) {@@ -508,42 +528,15 @@ } namespace Util {- using std::string; - std::string rtrim(const std::string &str) {- std::string trimmed = str;- size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");- if (pos_ws != std::string::npos)- { trimmed.erase(pos_ws + 1); }- else { trimmed.clear(); }- return trimmed;- }-- std::string normalize_underscores(const std::string& str) {- std::string normalized = str;- for(size_t i = 0, L = normalized.length(); i < L; ++i) {- if(normalized[i] == '_') {- normalized[i] = '-';- }- }- return normalized;- }-- std::string normalize_decimals(const std::string& str) {- std::string prefix = "0";- std::string normalized = str;-- return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;- }-- bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style) {+ bool isPrintable(Ruleset* r, Sass_Output_Style style) { if (r == NULL) { return false; } Block_Obj b = r->block(); - Selector_List_Ptr sl = Cast<Selector_List>(r->selector());+ SelectorList* sl = r->selector(); bool hasSelectors = sl ? sl->length() > 0 : false; if (!hasSelectors) {@@ -556,14 +549,14 @@ Statement_Obj stm = b->at(i); if (Cast<Directive>(stm)) { return true;- } else if (Declaration_Ptr d = Cast<Declaration>(stm)) {+ } else if (Declaration* d = Cast<Declaration>(stm)) { return isPrintable(d, style);- } else if (Has_Block_Ptr p = Cast<Has_Block>(stm)) {+ } else if (Has_Block* p = Cast<Has_Block>(stm)) { Block_Obj pChildBlock = p->block(); if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; }- } else if (Comment_Ptr c = Cast<Comment>(stm)) {+ } else if (Comment* c = Cast<Comment>(stm)) { // keep for uncompressed if (style != COMPRESSED) { hasDeclarations = true;@@ -584,17 +577,17 @@ return false; } - bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style)+ bool isPrintable(String_Constant* s, Sass_Output_Style style) { return ! s->value().empty(); } - bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style)+ bool isPrintable(String_Quoted* s, Sass_Output_Style style) { return true; } - bool isPrintable(Declaration_Ptr d, Sass_Output_Style style)+ bool isPrintable(Declaration* d, Sass_Output_Style style) { Expression_Obj val = d->value(); if (String_Quoted_Obj sq = Cast<String_Quoted>(val)) return isPrintable(sq.ptr(), style);@@ -602,7 +595,7 @@ return true; } - bool isPrintable(Supports_Block_Ptr f, Sass_Output_Style style) {+ bool isPrintable(Supports_Block* f, Sass_Output_Style style) { if (f == NULL) { return false; }@@ -616,7 +609,7 @@ if (Cast<Declaration>(stm) || Cast<Directive>(stm)) { hasDeclarations = true; }- else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+ else if (Has_Block* b = Cast<Has_Block>(stm)) { Block_Obj pChildBlock = b->block(); if (!b->is_invisible()) { if (isPrintable(pChildBlock, style)) {@@ -633,36 +626,37 @@ return false; } - bool isPrintable(Media_Block_Ptr m, Sass_Output_Style style)+ bool isPrintable(CssMediaRule* m, Sass_Output_Style style) {- if (m == 0) return false;+ if (m == nullptr) return false; Block_Obj b = m->block();- if (b == 0) return false;+ if (b == nullptr) return false;+ if (m->empty()) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i); if (Cast<Directive>(stm)) return true; else if (Cast<Declaration>(stm)) return true;- else if (Comment_Ptr c = Cast<Comment>(stm)) {+ else if (Comment* c = Cast<Comment>(stm)) { if (isPrintable(c, style)) { return true; } }- else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) {+ else if (Ruleset* r = Cast<Ruleset>(stm)) { if (isPrintable(r, style)) { return true; } }- else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) {+ else if (Supports_Block* f = Cast<Supports_Block>(stm)) { if (isPrintable(f, style)) { return true; } }- else if (Media_Block_Ptr mb = Cast<Media_Block>(stm)) {+ else if (CssMediaRule* mb = Cast<CssMediaRule>(stm)) { if (isPrintable(mb, style)) { return true; } }- else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+ else if (Has_Block* b = Cast<Has_Block>(stm)) { if (isPrintable(b->block(), style)) { return true; }@@ -671,7 +665,7 @@ return false; } - bool isPrintable(Comment_Ptr c, Sass_Output_Style style)+ bool isPrintable(Comment* c, Sass_Output_Style style) { // keep for uncompressed if (style != COMPRESSED) {@@ -695,27 +689,27 @@ if (Cast<Declaration>(stm) || Cast<Directive>(stm)) { return true; }- else if (Comment_Ptr c = Cast<Comment>(stm)) {+ else if (Comment* c = Cast<Comment>(stm)) { if (isPrintable(c, style)) { return true; } }- else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) {+ else if (Ruleset* r = Cast<Ruleset>(stm)) { if (isPrintable(r, style)) { return true; } }- else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) {+ else if (Supports_Block* f = Cast<Supports_Block>(stm)) { if (isPrintable(f, style)) { return true; } }- else if (Media_Block_Ptr m = Cast<Media_Block>(stm)) {+ else if (CssMediaRule * m = Cast<CssMediaRule>(stm)) { if (isPrintable(m, style)) { return true; } }- else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {+ else if (Has_Block* b = Cast<Has_Block>(stm)) { if (isPrintable(b->block(), style)) { return true; }@@ -723,10 +717,6 @@ } return false;- }-- bool isAscii(const char chr) {- return unsigned(chr) < 128; } }
libsass/src/util.hpp view
@@ -1,17 +1,35 @@ #ifndef SASS_UTIL_H #define SASS_UTIL_H -#include <vector>-#include <string>-#include <assert.h>+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "sass/base.h" #include "ast_fwd_decl.hpp" +#include <cmath>+#include <cstring>+#include <vector>+#include <string>+#include <assert.h>+ #define SASS_ASSERT(cond, msg) assert(cond && msg) namespace Sass { + template <typename T>+ T clip(const T& n, const T& lower, const T& upper) {+ return std::max(lower, std::min(n, upper));+ }++ template <typename T>+ T absmod(const T& n, const T& r) {+ T m = std::fmod(n, r);+ if (m < 0.0) m += r;+ return m;+ }+ double round(double val, size_t precision = 0); double sass_strtod(const char* str); const char* safe_str(const char *, const char* = "");@@ -20,7 +38,7 @@ std::string read_css_string(const std::string& str, bool css = true); std::string evacuate_escapes(const std::string& str); std::string string_to_output(const std::string& str);- std::string comment_to_string(const std::string& text);+ std::string comment_to_compact_string(const std::string& text); std::string read_hex_escapes(const std::string& str); std::string escape_string(const std::string& str); void newline_to_space(std::string& str);@@ -34,22 +52,53 @@ bool peek_linefeed(const char* start); - namespace Util {+ // Returns true iff `elements` ⊆ `container`.+ template <typename C, typename T>+ bool contains_all(C container, T elements) {+ for (const auto &el : elements) {+ if (container.find(el) == container.end()) return false;+ }+ return true;+ } - std::string rtrim(const std::string& str);+ // C++20 `starts_with` equivalent.+ // See https://en.cppreference.com/w/cpp/string/basic_string/starts_with+ inline bool starts_with(const std::string& str, const char* prefix, size_t prefix_len) {+ return str.compare(0, prefix_len, prefix) == 0;+ } - std::string normalize_underscores(const std::string& str);- std::string normalize_decimals(const std::string& str);+ inline bool starts_with(const std::string& str, const char* prefix) {+ return starts_with(str, prefix, std::strlen(prefix));+ } - bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style = NESTED);- bool isPrintable(Supports_Block_Ptr r, Sass_Output_Style style = NESTED);- bool isPrintable(Media_Block_Ptr r, Sass_Output_Style style = NESTED);- bool isPrintable(Comment_Ptr b, Sass_Output_Style style = NESTED);+ // C++20 `ends_with` equivalent.+ // See https://en.cppreference.com/w/cpp/string/basic_string/ends_with+ inline bool ends_with(const std::string& str, const std::string& suffix) {+ return suffix.size() <= str.size() && std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());+ }++ inline bool ends_with(const std::string& str, const char* suffix, size_t suffix_len) {+ if (suffix_len > str.size()) return false;+ const char* suffix_it = suffix + suffix_len;+ const char* str_it = str.c_str() + str.size();+ while (suffix_it != suffix) if (*(--suffix_it) != *(--str_it)) return false;+ return true;+ }++ inline bool ends_with(const std::string& str, const char* suffix) {+ return ends_with(str, suffix, std::strlen(suffix));+ }++ namespace Util {++ bool isPrintable(Ruleset* r, Sass_Output_Style style = NESTED);+ bool isPrintable(Supports_Block* r, Sass_Output_Style style = NESTED);+ bool isPrintable(CssMediaRule* r, Sass_Output_Style style = NESTED);+ bool isPrintable(Comment* b, Sass_Output_Style style = NESTED); bool isPrintable(Block_Obj b, Sass_Output_Style style = NESTED);- bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style = NESTED);- bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style = NESTED);- bool isPrintable(Declaration_Ptr d, Sass_Output_Style style = NESTED);- bool isAscii(const char chr);+ bool isPrintable(String_Constant* s, Sass_Output_Style style = NESTED);+ bool isPrintable(String_Quoted* s, Sass_Output_Style style = NESTED);+ bool isPrintable(Declaration* d, Sass_Output_Style style = NESTED); } }
+ libsass/src/util_string.cpp view
@@ -0,0 +1,125 @@+#include "util_string.hpp"++#include <iostream>+#include <algorithm>++namespace Sass {+ namespace Util {++ // ##########################################################################+ // Special case insensitive string matcher. We can optimize+ // the more general compare case quite a bit by requiring+ // consumers to obey some rules (lowercase and no space).+ // - `literal` must only contain lower case ascii characters+ // there is one edge case where this could give false positives+ // test could contain a (non-ascii) char exactly 32 below literal+ // ##########################################################################+ bool equalsLiteral(const char* lit, const std::string& test) {+ // Work directly on characters+ const char* src = test.c_str();+ // There is a small chance that the search string+ // Is longer than the rest of the string to look at+ while (*lit && (*src == *lit || *src + 32 == *lit)) {+ ++src, ++lit;+ }+ // True if literal is at end+ // If not test was too long+ return *lit == 0;+ }++ void ascii_str_tolower(std::string* s) {+ for (auto& ch : *s) {+ ch = ascii_tolower(static_cast<unsigned char>(ch));+ }+ }++ void ascii_str_toupper(std::string* s) {+ for (auto& ch : *s) {+ ch = ascii_toupper(static_cast<unsigned char>(ch));+ }+ }++ std::string rtrim(std::string str) {+ auto it = std::find_if_not(str.rbegin(), str.rend(), ascii_isspace);+ str.erase(str.rend() - it);+ return str;+ }++ // ###########################################################################+ // Returns [name] without a vendor prefix.+ // If [name] has no vendor prefix, it's returned as-is.+ // ###########################################################################+ std::string unvendor(const std::string& name)+ {+ if (name.size() < 2) return name;+ if (name[0] != '-') return name;+ if (name[1] == '-') return name;+ for (size_t i = 2; i < name.size(); i++) {+ if (name[i] == '-') return name.substr(i + 1);+ }+ return name;+ }+ // EO unvendor++ std::string normalize_newlines(const std::string& str) {+ std::string result;+ result.reserve(str.size());+ std::size_t pos = 0;+ while (true) {+ const std::size_t newline = str.find_first_of("\n\f\r", pos);+ if (newline == std::string::npos) break;+ result.append(str, pos, newline - pos);+ result += '\n';+ if (str[newline] == '\r' && str[newline + 1] == '\n') {+ pos = newline + 2;+ }+ else {+ pos = newline + 1;+ }+ }+ result.append(str, pos, std::string::npos);+ return result;+ }++ std::string normalize_underscores(const std::string& str) {+ std::string normalized = str;+ std::replace(normalized.begin(), normalized.end(), '_', '-');+ return normalized;+ }++ std::string normalize_decimals(const std::string& str) {+ std::string normalized;+ if (!str.empty() && str[0] == '.') {+ normalized.reserve(str.size() + 1);+ normalized += '0';+ normalized += str;+ }+ else {+ normalized = str;+ }+ return normalized;+ }++ char opening_bracket_for(char closing_bracket) {+ switch (closing_bracket) {+ case ')': return '(';+ case ']': return '[';+ case '}': return '{';+ default: return '\0';+ }+ }++ char closing_bracket_for(char opening_bracket) {+ switch (opening_bracket) {+ case '(': return ')';+ case '[': return ']';+ case '{': return '}';+ default: return '\0';+ }+ }++ }+ // namespace Util++}+// namespace Sass
+ libsass/src/util_string.hpp view
@@ -0,0 +1,72 @@+#ifndef SASS_UTIL_STRING_H+#define SASS_UTIL_STRING_H++#include <string>++namespace Sass {+ namespace Util {++ // ##########################################################################+ // Special case insensitive string matcher. We can optimize+ // the more general compare case quite a bit by requiring+ // consumers to obey some rules (lowercase and no space).+ // - `literal` must only contain lower case ascii characters+ // there is one edge case where this could give false positives+ // test could contain a (non-ascii) char exactly 32 below literal+ // ##########################################################################+ bool equalsLiteral(const char* lit, const std::string& test);++ // ###########################################################################+ // Returns [name] without a vendor prefix.+ // If [name] has no vendor prefix, it's returned as-is.+ // ###########################################################################+ std::string unvendor(const std::string& name);++ std::string rtrim(std::string str);+ std::string normalize_newlines(const std::string& str);+ std::string normalize_underscores(const std::string& str);+ std::string normalize_decimals(const std::string& str);+ char opening_bracket_for(char closing_bracket);+ char closing_bracket_for(char opening_bracket);++ // Locale-independent ASCII character routines.++ inline bool ascii_isalpha(unsigned char c) {+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');+ }++ inline bool ascii_isdigit(unsigned char c) {+ return (c >= '0' && c <= '9');+ }++ inline bool ascii_isalnum(unsigned char c) {+ return ascii_isalpha(c) || ascii_isdigit(c);+ }++ inline bool ascii_isascii(unsigned char c) { return c < 128; }++ inline bool ascii_isxdigit(unsigned char c) {+ return ascii_isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');+ }++ inline bool ascii_isspace(unsigned char c) {+ return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n';+ }++ inline char ascii_tolower(unsigned char c) {+ if (c >= 'A' && c <= 'Z') return c + 32;+ return c;+ }++ void ascii_str_tolower(std::string* s);++ inline char ascii_toupper(unsigned char c) {+ if (c >= 'a' && c <= 'z') return c - 32;+ return c;+ }++ void ascii_str_toupper(std::string* s);++ } // namespace Sass+} // namespace Util+#endif // SASS_UTIL_STRING_H
libsass/src/values.cpp view
@@ -1,4 +1,7 @@+// sass.hpp must go before all system headers to get the+// __EXTENSIONS__ fix on Solaris. #include "sass.hpp"+ #include "sass.h" #include "values.hpp" @@ -7,21 +10,26 @@ namespace Sass { // convert value from C++ side to C-API- union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val)+ union Sass_Value* ast_node_to_sass_value (const Expression* val) { if (val->concrete_type() == Expression::NUMBER) {- Number_Ptr_Const res = Cast<Number>(val);+ const Number* res = Cast<Number>(val); return sass_make_number(res->value(), res->unit().c_str()); } else if (val->concrete_type() == Expression::COLOR) {- Color_Ptr_Const col = Cast<Color>(val);- return sass_make_color(col->r(), col->g(), col->b(), col->a());+ if (const Color_RGBA* rgba = Cast<Color_RGBA>(val)) {+ return sass_make_color(rgba->r(), rgba->g(), rgba->b(), rgba->a());+ } else {+ // ToDo: allow to also use HSLA colors!!+ Color_RGBA_Obj col = Cast<Color>(val)->copyAsRGBA();+ return sass_make_color(col->r(), col->g(), col->b(), col->a());+ } } else if (val->concrete_type() == Expression::LIST) {- List_Ptr_Const l = Cast<List>(val);+ const List* l = Cast<List>(val); union Sass_Value* list = sass_make_list(l->size(), l->separator(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { Expression_Obj obj = l->at(i);@@ -32,7 +40,7 @@ } else if (val->concrete_type() == Expression::MAP) {- Map_Ptr_Const m = Cast<Map>(val);+ const Map* m = Cast<Map>(val); union Sass_Value* map = sass_make_map(m->length()); size_t i = 0; for (Expression_Obj key : m->keys()) { sass_map_set_key(map, i, ast_node_to_sass_value(key));@@ -47,16 +55,16 @@ } else if (val->concrete_type() == Expression::BOOLEAN) {- Boolean_Ptr_Const res = Cast<Boolean>(val);+ const Boolean* res = Cast<Boolean>(val); return sass_make_boolean(res->value()); } else if (val->concrete_type() == Expression::STRING) {- if (String_Quoted_Ptr_Const qstr = Cast<String_Quoted>(val))+ if (const String_Quoted* qstr = Cast<String_Quoted>(val)) { return sass_make_qstring(qstr->value().c_str()); }- else if (String_Constant_Ptr_Const cstr = Cast<String_Constant>(val))+ else if (const String_Constant* cstr = Cast<String_Constant>(val)) { return sass_make_string(cstr->value().c_str()); }@@ -65,7 +73,7 @@ } // convert value from C-API to C++ side- Value_Ptr sass_value_to_ast_node (const union Sass_Value* val)+ Value* sass_value_to_ast_node (const union Sass_Value* val) { switch (sass_value_get_tag(val)) { case SASS_NUMBER:@@ -78,7 +86,8 @@ ParserState("[C-VALUE]"), sass_boolean_get_value(val)); case SASS_COLOR:- return SASS_MEMORY_NEW(Color,+ // ToDo: allow to also use HSLA colors!!+ return SASS_MEMORY_NEW(Color_RGBA, ParserState("[C-VALUE]"), sass_color_get_r(val), sass_color_get_g(val),@@ -94,7 +103,7 @@ ParserState("[C-VALUE]"), sass_string_get_value(val)); case SASS_LIST: {- List_Ptr l = SASS_MEMORY_NEW(List,+ List* l = SASS_MEMORY_NEW(List, ParserState("[C-VALUE]"), sass_list_get_length(val), sass_list_get_separator(val));@@ -105,7 +114,7 @@ return l; } case SASS_MAP: {- Map_Ptr m = SASS_MEMORY_NEW(Map, ParserState("[C-VALUE]"));+ Map* m = SASS_MEMORY_NEW(Map, ParserState("[C-VALUE]")); for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) { *m << std::make_pair( sass_value_to_ast_node(sass_map_get_key(val, i)),
libsass/src/values.hpp view
@@ -5,8 +5,8 @@ namespace Sass { - union Sass_Value* ast_node_to_sass_value (const Expression_Ptr val);- Value_Ptr sass_value_to_ast_node (const union Sass_Value* val);+ union Sass_Value* ast_node_to_sass_value (const Expression* val);+ Value* sass_value_to_ast_node (const union Sass_Value* val); } #endif
tests/Main.hs view
@@ -35,7 +35,7 @@ #ifndef EXTERNAL_LIBSASS it "should report correct version" $ do str <- peekCString libsass_version- str `shouldBe` "3.5.5"+ str `shouldBe` "3.6.3" #endif it "should support quoted strings" $ withCString "sample" $ \cstr -> do